Outcome
A completion claim becomes a durable record keyed to the issue, the attempt, and the exact revision, graded by the accepted-outcome contract, and a repository may opt in to closing an issue from an accepted result. An unverified claim leaves the issue open with a typed reason.
This is stage 6 of #10 and the whole of Track E's E7. The design is docs/2026-08-23-issue-work-receipt-linkage-design.md, section 5.
Current behavior
Three mechanisms meet here and none of them connects to the others.
OpenAgents.AcceptedOutcome.evaluate/1 (lib/openagents/accepted_outcome.ex:104) grades a claim map and returns {:accepted, ...}, {:not_accepted, type, reasons}, or {:not_applicable, exemption}. It is a pure function. Nothing stores a claim, and nothing stores a result, so no surface can answer which evidence qualified an issue and no policy can key on it. Its only caller in lib/ is continual learning.
compensation_outcome_decisions — the record PROMISE-001 gates LIVE promises on — is keyed on tool_step_id (lib/openagents/compensation/outcome_decision.ex:11), one immutable module outcome inside one conversation. It cannot be reached from an issue.
#130 closes an issue from a Closes #N trailer when a commit lands on the default branch, attributed to the pusher. That is a human assertion, authenticated and bound to a merge, and it is the right default for most work.
Work
- Store the claim, keyed on
{issue, attempt, revision}, so the evaluation is reproducible and the issue can name which receipt satisfied which acceptance criterion.
- Grade it with
AcceptedOutcome.evaluate/1 and record the typed result. Do not reimplement the contract; AcceptedOutcome.validate/1 already refuses a contract that drifts from the code.
- Fill the five required attempt fields from records that exist:
forge_assignments carries the issue, the repository, the requesting principal, and the terminal revision, and stage 1's work_job_id reaches the budget snapshot on the work job.
- Make verified closing an explicit, per-repository, opt-in policy. Never reopen from this path: an unverified claim leaves the issue open with an
incomplete, unauthorized, or failed result.
- Leave
#130 untouched. Trailer-driven closing is a person's assertion and stays attributed to that person. If both paths fire, record both and close once.
- Publish only the bounded projection
AcceptedOutcome.public_projection/1 already produces: result state, typed reasons, criterion names, and public receipt references.
Acceptance criteria
- A claim that satisfies the contract closes the issue on a repository that opted in, and the issue records the evidence used.
- A claim missing an issue section, an attempt field, a verifier, a falsifier, or a criterion's evidence leaves the issue open with the exact typed reason.
- A result carrying any of the five named false-green classes is
failed even when the verifier reported green.
- Human-only work and repositories with agents disabled evaluate to
not_applicable and are unaffected.
- A public projection never carries a prompt, a log, a private repository name, or a private receipt reference.
- Closing through this path clears the derived
blocked flag on dependents exactly as a manual close does, per #100.
Verification
Context tests for the claim lifecycle and every typed non-accepted result, policy tests for the opt-in and the human-only exemption, authority tests for private evidence, and an interaction test with #130's trailer close; mix precommit.
Dependencies
Stage 6 of #10, and it should be last. Needs stage 4 (#148) for the evidence edges and coordinates with #130. INVARIANTS.md, OUTCOME-001 and PROMISE-001. Track E, E7.
Outcome
A completion claim becomes a durable record keyed to the issue, the attempt, and the exact revision, graded by the accepted-outcome contract, and a repository may opt in to closing an issue from an
acceptedresult. An unverified claim leaves the issue open with a typed reason.This is stage 6 of
#10and the whole of Track E's E7. The design isdocs/2026-08-23-issue-work-receipt-linkage-design.md, section 5.Current behavior
Three mechanisms meet here and none of them connects to the others.
OpenAgents.AcceptedOutcome.evaluate/1(lib/openagents/accepted_outcome.ex:104) grades a claim map and returns{:accepted, ...},{:not_accepted, type, reasons}, or{:not_applicable, exemption}. It is a pure function. Nothing stores a claim, and nothing stores a result, so no surface can answer which evidence qualified an issue and no policy can key on it. Its only caller inlib/is continual learning.compensation_outcome_decisions— the recordPROMISE-001gatesLIVEpromises on — is keyed ontool_step_id(lib/openagents/compensation/outcome_decision.ex:11), one immutable module outcome inside one conversation. It cannot be reached from an issue.#130closes an issue from aCloses #Ntrailer when a commit lands on the default branch, attributed to the pusher. That is a human assertion, authenticated and bound to a merge, and it is the right default for most work.Work
{issue, attempt, revision}, so the evaluation is reproducible and the issue can name which receipt satisfied which acceptance criterion.AcceptedOutcome.evaluate/1and record the typed result. Do not reimplement the contract;AcceptedOutcome.validate/1already refuses a contract that drifts from the code.forge_assignmentscarries the issue, the repository, the requesting principal, and the terminal revision, and stage 1'swork_job_idreaches the budget snapshot on the work job.incomplete,unauthorized, orfailedresult.#130untouched. Trailer-driven closing is a person's assertion and stays attributed to that person. If both paths fire, record both and close once.AcceptedOutcome.public_projection/1already produces: result state, typed reasons, criterion names, and public receipt references.Acceptance criteria
failedeven when the verifier reported green.not_applicableand are unaffected.blockedflag on dependents exactly as a manual close does, per#100.Verification
Context tests for the claim lifecycle and every typed non-accepted result, policy tests for the opt-in and the human-only exemption, authority tests for private evidence, and an interaction test with
#130's trailer close;mix precommit.Dependencies
Stage 6 of
#10, and it should be last. Needs stage 4 (#148) for the evidence edges and coordinates with#130.INVARIANTS.md,OUTCOME-001andPROMISE-001. Track E, E7.