fix(status): opt-in stage-report-presence guard on status advance (#235)#236
Open
gcko wants to merge 1 commit into
Open
fix(status): opt-in stage-report-presence guard on status advance (#235)#236gcko wants to merge 1 commit into
gcko wants to merge 1 commit into
Conversation
…acedock-dev#235) Issue spacedock-dev#235 reports an intermittent failure mode in the spacedock-answer-questions workflow: the FO advances entity status past a stage that emits stage-side YAML approval evidence (e.g. a semantic-mapping.yml with `review.status: approved`) without appending the corresponding `## Stage Report: <stage>` section to the entity body. A downstream consumer that gates artifact publishing on the approval- evidence pair then either rejects valid runs or has to backfill the log section manually. Add an opt-in mechanical guard in `status --set`. When the workflow declares `stages.defaults.require-stage-report: true`, the status binary refuses `--set <slug> status=<next>` unless the entity body contains `## Stage Report: <prior_status>` (with optional `(cycle N)` suffix for feedback rounds). The initial stage is exempt because some workflows use a pure-state intake; only transitions out of stages that ran a worker need the audit log to exist. `--force` bypasses the guard for explicit manual recovery. Default OFF preserves backward compatibility — all 643 existing tests pass without change. Workflows opt in by adding the flag to their README's stages.defaults block. Docs updated in three places: - skills/first-officer/references/first-officer-shared-core.md (Completion and Gates — new "Stage-report-presence enforcement" para) - skills/ensign/references/ensign-shared-core.md (Stage Report Protocol — note about the FO's advancement refusal) - skills/commission/SKILL.md (commission template — optional defaults flag documented) Tests: 8 new test cases in TestStageReportGuard cover refusal, allowed transitions with the report, initial-stage exemption, idempotent --set, --force bypass, opt-in absence (backward compat), `(cycle N)` suffix matching, and the error message contract. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #235.
Summary
Add an opt-in mechanical guard against the failure mode reported in #235: an FO that advances entity status past a stage which emits stage-side YAML approval evidence (e.g.
semantic-mapping.ymlwithreview.status: approved) without appending the corresponding## Stage Report: <stage>section to the entity body. The downstream consumer ends up with an entity where the YAML records "approved" but the prose audit log is missing.When a workflow declares
stages.defaults.require-stage-report: truein its README, the status binary refuses:unless the entity body contains
## Stage Report: <prior_status>(with optional(cycle N)suffix for feedback rounds).--forcebypasses for explicit manual recovery.Why opt-in
The default
worktree: false / concurrency: 2test fixture (and several user workflows) use a pure-state initial stage likebacklogwith no worker — there is no prior report to require for the first advancement. Making the guard always-on would break those workflows. Workflows where every stage produces an audit log section can opt in with a single line.The initial stage is exempt regardless of the opt-in (it has no prior to require a report for).
What changed
skills/commission/bin/status— new guard block in the--sethandler. Readsstages.defaults.require-stage-reportfrom the workflow README; when true and the update changesstatusto a different non-empty value, scans the entity body for^## Stage Report: <prior>(\s*\(cycle N\))?$. Exits 1 with a clear error naming the missing section if absent (and not--force'd).tests/test_status_script.py—TestStageReportGuard(8 tests): refusal, allowed transitions with the report, initial-stage exemption, idempotent--set(no transition),--forcebypass, opt-in absence (backward compatibility),(cycle N)suffix matching, error message contract.skills/first-officer/references/first-officer-shared-core.md— new "Stage-report-presence enforcement" paragraph in## Completion and Gatesdescribing the opt-in and the--forcediscipline.skills/ensign/references/ensign-shared-core.md— Stage Report Protocol rules gain a bullet about committing the section in the same commit as stage work, noting the FO's advancement refusal under opt-in.skills/commission/SKILL.md— commission template'sstages.defaultsblock documents the new optional flag.Test plan
make test-static— 643 passed, 27 deselected, 15 subtests passed (full repo, no regressions)TestStageReportGuardsuite — 8 tests, all RED first then GREEN, covering every branch of the guardNotes for the maintainer
The guard's "initial stage exempt" rule reads the workflow's
stagesblock and skips whencurrent_statusis in the set ofinitial: truestages. This matches the conventional shape (one initial stage, optionally pure-state) but degrades gracefully when no stage is marked initial — every transition is then checked.I chose opt-in via
stages.defaultsrather than per-stage because the recurring bug is the same shape across stages (the FO writes YAML but forgets the prose), and a per-stage flag would force every workflow author to enumerate it for each stage. If you'd prefer per-stage, the change is mechanical — happy to revise.If you want the default flipped to ON in a future release, that's a one-line change in
parse_stages_with_defaultsonce existing fixtures are updated.🤖 Generated with Claude Code