4.7 KiB
4.7 KiB
T10.2 — Reconciler port
| Field | Value |
|---|---|
| Phase | P10 — Orchestration |
| Size | M — 1 to 3 days |
| Status | Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | T10.6, T10.8 |
Goal
New port answering "how far toward the goal, what next" from a run's terminal
resting state — distinct from Verifier (fail-closed pass/fail) and Judge
(relative-only comparison).
Facts (inlined — no spec read needed)
Verifier::verifyis fail-closed pass/fail (rust-agentic-sys.md §10).Judge::compareis deliberately relative-only, "cannot return an absolute score" (rust-agentic-sys.md §11.1). "How far toward goal, what next" is a third question — forcing it through either port blurs a firewall built on purpose.Reconcileris new, sized likeVerifier(one method, S/M).- Fires only off
Verified{pass|fail},Graded,Ungraded— statesT1.3already designed to rest in, never resolved inline. - Reconciler failure degrades the record, never the run — same principle as
§1: "the framework cannot break the agent it runs." A
Reconcilererror or timeout leaves the run at its terminal state; the goal simply does not advance until retried. - The decision does not spawn anything itself. Spawning happens only
through the admission path (T10.3) via the inbox relay (T10.7) — never a
direct call from the
Reconcileror its caller. - One new
#[non_exhaustive]WorkEventvariant for provenance:SpawnedFromReconciliation{goal_id, parent_run, decision_ref}. Added, not replacing any existing variant.
Steps
- Define
ReconcileCtx—GoalId,GoalView(T10.1), the just-terminal run'sEpisodeView,WorkflowDef. - Define
ReconcileDecision—SpawnNext{workflow_ref, input, dispatch_key},Retry{step},Stop{reason}. - Define the trait:
#[async_trait] pub trait Reconciler: Send + Sync { fn id(&self) -> ReconcilerId; async fn reconcile(&self, cx: &ReconcileCtx) -> Result<ReconcileDecision>; } - Wire the trigger: on a run reaching
Verified/Graded/Ungradedwith aGoalIdset, call theReconcilerbound to that goal. - On
Reconcilererror or timeout: log, leave the run at its terminal state, do not auto-retry — matchesVerifier's explicit-only-retry discipline. - Add
WorkEvent::SpawnedFromReconciliation, written on the next spawned run's first log record, referencingparent_runand the decision. - Test with a stub
Reconcileragainst all three trigger states.
Acceptance
- A run reaching
Verified{pass}with aGoalIdtriggers exactly one reconcile call. - The decision is durably recorded and traceable from the next spawned run back to the parent run and goal.
- A
Reconcilererror leaves the run at its terminal state and does not crash the executor.
Verify
Harness: stub Reconciler returning each decision variant in turn; one
goal with a 2-run chain.
Integration test — tests/it_reconciler_trigger.rs:
- Run A (goal G) reaches
Verified{pass}; assertreconcile()called exactly once with the correctReconcileCtx. - Stub returns
SpawnNext; assert the decision is persisted, referencing run A. - Assert the next spawned run's first log record carries
SpawnedFromReconciliation{goal_id: G, parent_run: A, decision_ref}. - Repeat for
GradedandUngradedtrigger states. - Stub
Reconcilerpanics or times out; assert run A stays at its terminal state, no crash, no infinite retry. - Run with no
GoalIdreachesVerified{pass}; assertreconcile()is never called.
Command: cargo test -p kernel reconciler_trigger
False pass:
- Asserting reconcile "ran" via a boolean flag instead of asserting the
WorkEventprovenance record exists — passes even if a crash lost the decision before persistence. - Testing only
Verified{pass}, skippingGraded/Ungraded— those are the states most likely miswired, being newer additions to the FSM.
Traps
Reconcilerspawning the next run directly. Violates "never bypass the admission path" — see T10.3/T10.7.- Overloading
Judge::compareto also answer "what next." Collapses a boundaryJudge's signature deliberately enforces (rust-agentic-sys.md §11.1, "deliberately cannot return an absolute score"). - Auto-retrying a failed
Reconcilercall with no cap — a broken impl then spins forever against every terminal run in the tenant.
Background (not required to do this task): rust-agentic-sys.md §1, §10, §11.1, §17 · T1.3-run-executor.md · T4.1-verifier-port.md · T10.1-goalid-and-goal-scoped-query.md