Implementation Plan: Per-Project Orchestrator Lifecycle Hooks (v1)
- [ ] Step 0: Create Cucumber BDD suite skeleton + failing AC scenarios first (`AC-01`..`AC-18`) - [ ] Step 1: Add hooks config schema + semantic validation in `ralph-core` - [ ] Step 2: Add `ralph hooks validate` CLI command (human + JSON output) - [ ] Step 3: Implement `HookExecutor` (JSON stdin, timeout, output truncation) - [ ] Step 4: Add hook telemetry logging and diagnostics integration - [
Overview
Implementation Plan: Per-Project Orchestrator Lifecycle Hooks (v1)
Checklist
- Step 0: Create Cucumber BDD suite skeleton + failing AC scenarios first (
AC-01..AC-18) - Step 1: Add hooks config schema + semantic validation in
ralph-core - Step 2: Add
ralph hooks validateCLI command (human + JSON output) - Step 3: Implement
HookExecutor(JSON stdin, timeout, output truncation) - Step 4: Add hook telemetry logging and diagnostics integration
- Step 5: Implement
HookEnginedispatch forloop.start+iteration.start - Step 6: Implement
on_errordispositions (warn,block) end-to-end - Step 7: Implement suspend core (
wait_for_resume) +ralph loops resume <id> - Step 8: Add suspend modes
retry_backoffandwait_then_retry - Step 9: Add remaining lifecycle mappings (
plan.created,human.interact,loop.complete,loop.error) - Step 10: Add opt-in metadata mutation (JSON-only, metadata namespace only)
- Step 11: Integrate hooks validation into preflight + update docs/examples
- Step 12: Add mutation testing gates for hooks-critical modules
- Step 13: Drive BDD suite to green + finalize traceability matrix + CI gate
Sequencing rule (TDD/BDD-first): Step 0 is mandatory before implementation steps. Build code incrementally to satisfy failing BDD scenarios.
Step 0: Create Cucumber BDD suite skeleton + failing AC scenarios first (AC-01..AC-18)
Objective Establish a BDD-first baseline so implementation is explicitly driven by requirements.
Subtasks
- 0a. Draft
AC-01..AC-18→ scenario mapping table (initial traceability skeleton). - 0b. Create hook feature files and scenario placeholders per AC.
- 0c. Wire minimal step definitions into the existing e2e harness.
- 0d. Run and record a CI-safe red baseline.
Implementation Guidance
- Create Cucumber feature files under
crates/ralph-e2e/features/hooks/. - Add scenario/scenario-outline placeholders for all AC IDs (
AC-01..AC-18). - Implement minimal step definitions to execute scenarios against the existing harness.
- Mark scenarios as expected-failing/red for capabilities not yet implemented.
Test Requirements
- BDD runner executes all hook feature scenarios.
- Scenario naming includes AC IDs for traceability.
- Red baseline is reproducible in CI-safe mode.
Integration Notes
- Keep fixtures deterministic and avoid network-dependent steps.
- Do not block on full implementation here; this is scaffolding + red baseline.
Demo
- Run BDD suite and show AC-labeled scenarios are discovered and currently failing for unimplemented behavior.
Step 1: Add hooks config schema + semantic validation in ralph-core
Objective Create first-class config types for hooks and enforce v1 guardrails at config-validation time.
Implementation Guidance
- Add
HooksConfig,HookDefaults,HookSpec, and hook enums (phase-event keys,on_error,suspend_mode) incrates/ralph-core/src/config.rs(or a new hooks config module re-exported from config). - Add
hookstoRalphConfigwith safe defaults (enabled: false). - Add semantic validation:
- valid phase-event names only
- required
name+command - positive
timeout_seconds/max_output_bytes - mutation shape is opt-in and JSON-only contract
- reject non-v1 fields (global scope, parallel execution options)
- Keep behavior inert (no runtime dispatch yet).
Test Requirements
- Add serde parse tests for valid/invalid hooks YAML.
- Add validation tests for enum errors, missing fields, invalid limits, and non-goal fields.
- Ensure existing config tests still pass.
Integration Notes
- Must not alter current runtime behavior when hooks are absent/disabled.
- Keep backward compatibility with existing configs that omit
hooks.
Demo
cargo test -p ralph-core configpasses.- A sample config with
hooks.enabled: trueparses and validates.
Step 2: Add ralph hooks validate CLI command (human + JSON output)
Objective Provide a dedicated pre-run validation surface for hook configuration and command wiring.
Implementation Guidance
- Add a new CLI namespace in
crates/ralph-cli/src/main.rs(e.g.,Hooks(HooksArgs)) withvalidatesubcommand. - Implement
crates/ralph-cli/src/hooks.rssimilar to existing preflight command patterns. - Validation output modes:
- human-readable summary
- JSON report for automation
- Validation should include config semantics and command resolvability checks (without executing hook scripts).
Test Requirements
- CLI unit/integration tests for:
- success case
- malformed config case
- unknown phase-event case
- JSON output shape
Integration Notes
- Reuse existing config source loading (
preflight::load_config_for_preflight) for consistent behavior. - Keep exit codes deterministic (
0pass, non-zero fail).
Demo
ralph hooks validate -c ralph.ymlreturns PASS/FAIL with actionable diagnostics.
Step 3: Implement HookExecutor (JSON stdin, timeout, output truncation)
Objective Build the core execution primitive for one hook invocation with v1 safety guarantees.
Subtasks
- 3a. Implement process spawn + command/env/cwd resolution.
- 3b. Implement JSON payload stdin write path.
- 3c. Implement timeout enforcement and termination handling.
- 3d. Implement stdout/stderr capture with
max_output_bytestruncation. - 3e. Finalize
HookRunResultmodel and conversion helpers.
Implementation Guidance
- Add a new module (e.g.,
crates/ralph-core/src/hooks/executor.rs). - Execution behavior:
- spawn command with configured cwd/env
- write lifecycle payload JSON to stdin
- enforce timeout
- capture stdout/stderr
- truncate each stream to
max_output_bytes
- Return structured
HookRunResultwith:- start/end timestamps
- duration
- exit code
- timed_out flag
- truncated stdout/stderr
Test Requirements
- Unit tests with fixture scripts for:
- successful run
- non-zero exit
- timeout
- oversized stdout/stderr truncation
- stdin payload delivered
Integration Notes
- Keep executor pure and reusable by CLI validation and runtime dispatch.
- No lifecycle wiring yet.
Demo
- Executor tests show expected timeout and truncation behavior deterministically.
Step 4: Add hook telemetry logging and diagnostics integration
Objective Persist required hook observability data for each invocation.
Implementation Guidance
- Add hook telemetry log model and writer (new diagnostics file, e.g.,
hook-runs.jsonl, or extend orchestration event with hook entries). - Required fields:
- phase-event
- hook name
- start/end/duration
- exit code
- timeout flag
- stdout/stderr (truncated)
- disposition
- Ensure telemetry logging works regardless of pass/fail/suspend outcomes.
Test Requirements
- Serialization tests for telemetry entries.
- Diagnostics integration tests verifying file creation and required fields.
Integration Notes
- Respect
RALPH_DIAGNOSTICSbehavior and existing diagnostics conventions.
Demo
- Run with diagnostics enabled and confirm hook run entries are persisted in structured JSONL.
Step 5: Implement HookEngine dispatch for loop.start + iteration.start
Objective Ship first end-to-end lifecycle hook dispatch path with deterministic sequencing.
Subtasks
- 5a. Build hook resolver for phase-event lookup + declaration-order sequencing.
- 5b. Implement payload builder for loop/iteration context.
- 5c. Wire
pre/post.loop.startdispatch boundaries. - 5d. Wire
pre/post.iteration.startdispatch boundaries. - 5e. Add disabled/empty-config fast-path (no-op dispatch).
Implementation Guidance
- Add
HookEngineorchestrator module to resolve configured hooks by phase-event. - Wire dispatch into runtime boundaries:
pre.loop.start/post.loop.startpre.iteration.start/post.iteration.start
- Build payload with context fields (
active_hat,selected_hat,selected_task, loop IDs, iteration data). - Execute hooks sequentially in declaration order.
Test Requirements
- Integration tests validating exact dispatch order and event-phase selection.
- Validate no dispatch occurs when hooks disabled.
Integration Notes
- Keep behavior additive; no blocking policy yet beyond telemetry collection.
Demo
- Configure simple echo hooks for loop/iteration events and observe ordered invocations + telemetry.
Step 6: Implement on_error dispositions (warn, block) end-to-end
Objective Enable policy outcomes from hook failures without suspend complexity yet.
Implementation Guidance
- Implement disposition resolver:
warn: continueblock: fail lifecycle action with clear user-facing reason
- Add consistent error messages in CLI output and diagnostics.
- Ensure block behavior is deterministic and non-ambiguous per phase.
Test Requirements
- Integration tests for warn-vs-block behavior at both loop.start and iteration.start phases.
- Verify blocked runs terminate with expected reason/exit behavior.
Integration Notes
blockshould not leave partial internal state transitions for the current boundary.
Demo
- A failing hook with
on_error: warncontinues; withon_error: blockstops with explicit reason.
Step 7: Implement suspend core (wait_for_resume) + ralph loops resume <id>
Objective Deliver operator-controlled suspension with durable state and explicit CLI resume.
Subtasks
- 7a. Define suspend-state schema and atomic file I/O (
suspend-state,resume-requested). - 7b. Implement runtime suspended-wait state machine with signal precedence.
- 7c. Add
ralph loops resume <id>CLI plumbing and loop resolution. - 7d. Add idempotency and invalid-state behavior for resume operations.
- 7e. Add end-to-end suspend → resume → continue tests.
Implementation Guidance
- Add suspension persistence:
.ralph/suspend-state.json.ralph/resume-requested
- On
on_error: suspend(default mode), enter suspended state and wait for resume/stop/restart signals. - Add
Resumesubcommand tocrates/ralph-cli/src/loops.rs:ralph loops resume <id>- resolve loop via existing
resolve_loop - validate suspended state
- write resume signal atomically
- idempotent no-op messaging when already resumed/not suspended
Test Requirements
- Unit tests for suspend state transitions and signal precedence.
- CLI tests for
loops resumesuccess, missing loop, non-suspended loop, idempotent retry. - Integration test for suspend -> resume -> continue path.
Integration Notes
- Preserve precedence: stop/restart should outrank resume while suspended.
Demo
- Run with a suspending hook, observe paused state, then
ralph loops resume <id>unblocks run.
Step 8: Add suspend modes retry_backoff and wait_then_retry
Objective Complete suspend behavior options required by design with bounded retry semantics.
Subtasks
- 8a. Implement bounded
retry_backoffpolicy (schedule, cap, termination condition). - 8b. Implement
wait_then_retryflow (resume gate + single retry behavior). - 8c. Add retry attempt metadata/telemetry fields.
- 8d. Build deterministic timing/test harness for retry behavior.
Implementation Guidance
- Add bounded backoff policy for
retry_backoff. - Add
wait_then_retryflow:- wait for resume
- retry hook once
- apply disposition based on retry result
- Include metadata/telemetry to indicate mode and retry attempt counts.
Test Requirements
- Integration tests for both modes, including exhausted retries and mixed outcomes.
Integration Notes
- Keep backoff bounded and deterministic for test reliability.
Demo
- Show a failing hook recovering under
retry_backoff, and a manual release underwait_then_retry.
Step 9: Add remaining lifecycle mappings (plan.created, human.interact, loop.complete, loop.error)
Objective Reach full mandatory v1 lifecycle coverage.
Subtasks
- 9a. Add
pre/post.plan.createddispatch wiring. - 9b. Add
pre/post.human.interactdispatch wiring. - 9c. Add
pre/post.loop.completedispatch wiring. - 9d. Add
pre/post.loop.errordispatch wiring. - 9e. Add explicit success/error termination mapping tests.
Implementation Guidance
- Add dispatch points for:
pre/post.plan.created(derived from plan-topic publications)pre/post.human.interactpre/post.loop.completepre/post.loop.error
- Ensure
loop.completevsloop.errormapping follows termination reason success semantics.
Test Requirements
- Integration tests for all added event-phase mappings.
- Explicit tests proving no backpressure event hooks are required/included in v1.
Integration Notes
- Keep plan-created derivation explicit and documented to avoid hidden behavior.
Demo
- End-to-end run showing hook invocations across all mandatory v1 lifecycle events.
Step 10: Add opt-in metadata mutation (JSON-only, metadata namespace only)
Objective Enable safe, explicit v1 mutation surface without touching prompt/events/config.
Implementation Guidance
- Parse hook stdout JSON only when
mutate.enabled: true. - Accept only
{"metadata": {...}}contract. - Merge into reserved namespace (e.g.,
hook_metadata.<hook_name>). - Surface parse/schema errors through disposition policy.
Test Requirements
- Unit tests for opt-in gating, valid merge, invalid JSON, invalid shape.
- Integration tests proving no mutation occurs when disabled.
Integration Notes
- Prevent key collisions by namespacing metadata per hook.
Demo
- Hook emits metadata; subsequent iteration payload includes namespaced injected metadata.
Step 11: Integrate hooks validation into preflight + update docs/examples
Objective Make hooks validation part of normal run safety and provide clear user guidance.
Implementation Guidance
- Add hooks check to
PreflightRunner::default_checks(). - Respect preflight skip/strict behavior.
- Update docs for:
- hooks config schema
- lifecycle phases
ralph hooks validateralph loops resume <id>
- Add one minimal sample config + script examples.
Test Requirements
- Preflight tests verifying hooks check pass/fail and skip behavior.
- CLI docs/command help snapshot tests where applicable.
Integration Notes
- Keep docs aligned with v1 non-goals to avoid over-promising.
Demo
ralph preflightfails on broken hooks config and passes after fixes.
Step 12: Add mutation testing gates for hooks-critical modules
Objective Increase defect resistance by enforcing mutation quality on the new hooks runtime surfaces.
Subtasks
- 12a. Select mutation tooling + baseline command for this repo.
- 12b. Scope mutation targets to hooks-critical modules only.
- 12c. Run baseline mutation report and calibrate threshold