Adds the canonical cross-role handoff schema and its fail-closed validator, live-state recovery, role-limited continuation, mandatory durable posting, the merged-awaiting-controller boundary, controller accept/reject continuation, and workflow-failure escalation with duplicate handling. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
4.8 KiB
4.8 KiB
Canonical self-propagating handoff schema (#626)
Applies to: every workflow actor — author, reviewer, merger, controller, operator, reconciler.
#494–#507 defined the ledger, the canonical state comments, and the
Canonical Thread Handoff shape. This schema owns the chain: each actor
consumes exactly one canonical handoff, performs exactly one authorized role,
records the result durably in Gitea, and emits the next complete handoff —
until the controller records final closure.
Implemented and enforced by self_propagating_handoff.py; wired into
final_report_validator.py as rule shared.self_propagating_handoff.
The block
Post this block into the Gitea issue or PR thread, and include it verbatim in the final report. It is not an optional prose section.
<!-- sph:v1 -->
## Canonical Handoff
```text
REPOSITORY: <org>/<repo>
ISSUE: <number>
PR: <number or none>
WORKFLOW_STATE: <one of the workflow states below>
HEAD_SHA: <current head, or none before a branch exists>
BASE_BRANCH: <base branch>
BASE_OR_MERGE_SHA: <base SHA, or merge commit SHA after merge>
ACTING_ROLE: <author|reviewer|merger|controller|operator|reconciler>
ACTING_IDENTITY: <username (profile)>
COMPLETED_ACTIONS: <what this actor actually did>
VALIDATION_EVIDENCE: <commands run and their results>
MUTATION_LEDGER: <every durable mutation performed>
BLOCKERS: <active blockers, or none>
NEXT_ACTOR: <role authorized by WORKFLOW_STATE, or none when complete>
NEXT_ACTION: <exact next action, or none when complete>
PROHIBITED_ACTIONS: <what the next actor must not do>
NEXT_PROMPT: <complete ready-to-run prompt, or none when complete>
WORKFLOW_FAILURE_ISSUES: <durable issue refs for tooling defects, or none>
LAST_UPDATED: <UTC timestamp>
## Workflow states and the single authorized actor
| `WORKFLOW_STATE` | `NEXT_ACTOR` |
| --------------------------- | ------------ |
| `needs-author` | `author` |
| `needs-review` | `reviewer` |
| `approved-awaiting-merge` | `merger` |
| `merged-awaiting-controller`| `controller` |
| `blocked` | `operator` |
| `complete` | `none` |
A merged PR is **not** accepted work: merge transitions to
`merged-awaiting-controller` unless the configured workflow explicitly
authorizes automatic acceptance.
## Fail-closed rules
* Every field is required. Only `PR`, `HEAD_SHA`, `BASE_OR_MERGE_SHA`,
`BLOCKERS`, `WORKFLOW_FAILURE_ISSUES`, `NEXT_ACTION`, and `NEXT_PROMPT` may
carry `none`, and `PR`/`HEAD_SHA` only in `needs-author` or `blocked`.
* `NEXT_ACTOR` must equal the actor the declared state authorizes.
* `blocked` requires a concrete `BLOCKERS` entry.
* A non-terminal handoff requires a concrete `NEXT_ACTION` and a
`NEXT_PROMPT` long enough to be ready to run.
* `complete` must carry no `NEXT_ACTION` and no `NEXT_PROMPT`: a finished
workflow terminates instead of manufacturing more work.
* `NEXT_PROMPT` must name the repository and the issue, and must not depend on
outside chat history. The issue or PR thread, workflow documentation, and
live repository state must be sufficient to recover the task.
* The handoff must be posted to Gitea. A chat-only report is not durable
workflow state.
## Live-state recovery before acting
The receiving actor re-derives truth from live state instead of trusting the
inherited handoff. `assess_handoff_live_state` detects and fails closed on:
`changed_pr_head`, `stale_approval`, `issue_closed`, `issue_reopened`,
`pr_merged`, `pr_closed_unmerged`, `stale_lease`, `foreign_lease`,
`missing_worktree`, `dirty_worktree`, `namespace_mismatch`, `stale_runtime`,
`changed_base`, and `conflicting_canonical_comments`.
A changed head invalidates any inherited review or merge handoff; the chain
recovers to `needs-review`.
## Controller closure
`accept` is only honored with all four closure proofs recorded:
`acceptance_criteria_satisfied`, `cleanup_complete`,
`canonical_final_state_posted`, `issue_closed_through_workflow`. Otherwise the
work item stays at `merged-awaiting-controller`.
`request_tests`, `request_proof`, `request_corrections`, and `reopen` return
the work to the author; `return_to_actor` returns it to a named earlier actor.
## Workflow-failure escalation
Tooling or workflow defects found while working an item never get folded into
the active feature issue. Each distinct failure carries `classification`,
`linked_issue`, `temporary_impact`, `next_valid_actor`, and `recovery_prompt`.
A failure whose signature already has a durable issue must reuse that issue
instead of filing a duplicate.
## Applicability
Enforcement is applicability-gated exactly like the #495 canonical-state gate:
once a report carries the `sph:v1` marker, the `Canonical Handoff` heading, or
a `WORKFLOW_STATE:` line, the full schema is enforced and incomplete handoffs
are rejected. Reports written before the protocol existed are unaffected.