Skip to content

feat: finalize captured runs into durable experience - #5

Merged
fabbrik merged 1 commit into
mainfrom
feat/2-5-finalize-runs
Sep 22, 2026
Merged

fabbrik merged 1 commit into
mainfrom
feat/2-5-finalize-runs

Conversation

@fabbrik

@fabbrik fabbrik commented Sep 21, 2026

Copy link
Copy Markdown
Owner

Summary

Story 2.5. Capture, verification, reflection and persistence become one call, so the library now does end to end what it is for.

ExperienceFinalizationService.FinalizeAsync runs the stages in order and stops at the first one that ends the call:

Load → Evaluate → Authorize + storage decision → Reflect → Create record → Commit initial event

  • The run is evaluated against its own round. Finalization takes the closed verification round plus its evidence and aggregates the result itself, so an evaluation from another run cannot be supplied. (Settles a decision deferred from Story 1.3.)
  • Gates before reflection. A run the host refuses on authorization or storage grounds is never handed to IExperienceReflector, the documented seam for a model-backed reflector.
  • Candidate first. The record is created as Candidate, and its initial lifecycle event performs the real transition to Validated (reuse confidence 2/3) or Quarantined (no lesson). A commit that never lands leaves a Candidate, which retrieval never reuses — not a reusable record with no history.
  • Retries converge. The record, reflection and event IDs derive from the run ID, so a retry re-derives them byte for byte: an already-finalized run reports AlreadyFinalized, and a record whose create landed but whose commit did not has that same commit finished.
  • Every failure is structured. Each stage names itself in the result; a database failure is never reported as durable success, and the captured snapshot stays available for another attempt.
  • Required checks can name an evaluator kind. RequiredCheck(CheckId, ExpectedKind?) — a human approval can no longer satisfy a test-result check by ID collision. (Settles a decision deferred from Story 1.5.)
  • Registration. AddAgentExperienceCore and AddAgentExperiencePostgresStore, both TryAdd-based so a host's own implementations win. The MAF adapter finalizes a fully captured run through a host-supplied resolver.

Review

Three reviewers raised 31 findings. Two were intent-level and resolved as deliberate changes to the story's intent — the gate ordering and Candidate-first above. 30 fixed, 1 deferred (whether derived record IDs should include the scope, so an ID cannot be squatted from another scope).

Test plan

  • dotnet build --configuration Release: 0 warnings, 0 errors
  • dotnet test --configuration Release: 446 of 446 pass (Core 256, Postgres 99, MAF 49). Locally with TESTCONTAINERS_RYUK_DISABLED=true.
  • End to end through the real store: capture → finalize → read the record and its history back; plus replay, storage denial, quarantine, and a create that lands while its commit fails, then converges on retry.
  • Service registration resolved from a real container, including a host's own implementations winning.
  • CI green

🤖 Generated with Claude Code

Add ExperienceFinalizationService: one call that loads a completed captured
run, evaluates its own closed verification round, checks host authorization and
the host's storage decision, reflects, creates the record as Candidate, and
commits its initial lifecycle event to Validated or Quarantined. Record,
reflection and event IDs derive from the run, so a retry re-derives them and
converges instead of duplicating.

Required checks can now name the evaluator kind that may satisfy them, and Core
and Storage.Postgres each expose a service-registration extension. The MAF
adapter finalizes a fully captured run through the host's resolver.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@fabbrik
fabbrik merged commit d78c7f1 into main Sep 22, 2026
1 check passed
@fabbrik
fabbrik deleted the feat/2-5-finalize-runs branch September 22, 2026 21:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant