feat: artifact versioning, draft+confirm auto-refresh, retro roll-up & command contract lint - #46
Merged
Conversation
…& command contract lint
Four additive, advisory layers — protected core byte-for-byte unchanged, exit 0
on every path, existing record/impact/report output byte-identical:
- Content versioning: version {list,show,diff,rollback,gc} folded into
audit_artifacts.py, keyed to the change-ledger's existing 16-hex hashes via
one pure module (version_model.py) — the gitignored object store is the
ledger rehydrated to bytes, no second index. Rollback is preview ->
named-human confirm, append-only, torn-write-safe.
- Draft+confirm auto-refresh: refresh {detect,scan,draft,apply,reject,status}
back-propagates a merged spec's shipped reality into pre-Build artifacts.
Review-first/divergence-aware; agent edits only a .proposed; a named human
echoes the reviewed diffhash to apply (One Rule); lands as a rollback-able
refreshed version attributed via a source_spec rider key.
- Cross-ledger retro roll-up: retro_report.py + /sdlc-retro — recurring
findings, repeat-stale artifacts, the refresh funnel (divergence-heuristic
tuning signal), disposition-debt rollup. Patterns, not people; no data over
fabricated zeros.
- Command contract lint: test_command_contracts.py validates every command
doc's script invocations against live --help, plus agent/file cross-refs,
with self-tests proving each detection class fires.
Plus multi-machine honesty on every "content not captured" path, docs for
endpoint-only capture semantics, and 1.3.0 integration: the three new
commands registered in docs/commands.md (count-word vocabulary extended in
its guard test) and all new output routed through the _glyph() Windows-
console fallback. 807 tests passing on the rebased tree.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The versioning suite asserts on exact file bytes — `version show` returns what the store captured, byte for byte. But the fixture helpers wrote their corpus in text mode, which expands newlines to CRLF on Windows. So the assertions compared LF literals against CRLF content and failed on Windows while passing on Linux CI. The product code is not at fault: every content path in audit_artifacts.py already reads and writes bytes. Only the fixtures were platform-dependent. Routing every artifact-content write through the _write helper, which now pins LF, makes the corpus byte-identical on both platforms. Also adds a windows-latest CI job. The plugin is authored on Windows and ships PowerShell hooks, but CI ran only on Linux — this is the second Windows-only defect to reach master unseen, after the cp1252 console fault fixed in 1.3.0. It is a separate job rather than a matrix on `test` so the existing required check name is unchanged, and it keeps the runner's default CRLF checkout because that mirrors the machine it exists to protect. Verified on Windows: 801 passed, 7 skipped (was 799 passed, 2 failed). Co-Authored-By: Claude Opus 5 (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.
What this adds
Four additive, advisory layers on top of the 1.3.0 artifact update & audit layer. Protected core is byte-for-byte unchanged;
audit_artifacts.py's existingrecord/impact/reportoutput is byte-identical (enforced by invariant tests); everything exits 0 on every path.1. Content versioning (
/sdlc-version)version {list,show,diff,rollback,gc}folded intoaudit_artifacts.py. Version identity is derived from the change-ledger's existing 16-hex SHA-256 hashes via one new pure module (version_model.py) — the content-addressed object store (.sdlc/versions/objects/<xx>/<16hex>, gitignored) is just those hashes rehydrated to bytes, so there is no second index to drift. Rollback is preview → named-human confirm (--actor+ echoed diffhash,--ack-signofffor signed-off artifacts), append-only ("restored from vX"), torn-write-safe with idempotent recovery.gcis cross-ledger refcounted and protects anything whose sign-off it can't prove.2. Draft+confirm auto-refresh (
/sdlc-refresh)refresh {detect,scan,draft,apply,reject,status}— reverse propagation: a merged spec's shipped reality flows back up intorequirements.md/epics.md/feature-brief.md/business-rules.md. Review-first and divergence-aware (a faithful spec drafts nothing without--draft); the discipline agent edits only a.proposed; a named human echoes the reviewed diffhash to apply (the One Rule); the refresh lands as a rollback-ablerefreshedversion attributed via asource_specrider key (artifact_model.pyunchanged).3. Cross-ledger retro roll-up (
/sdlc-retro)retro_report.py— read-only report across the ledgers for retro input: recurring findings (permanent-check candidates), repeat-stale artifacts, the refresh funnel per spec and per stem (the tuning signal for the divergence heuristic), and a disposition-debt rollup naming each source ledger. Same discipline as the scorecard: "no data" over fabricated zeros, patterns keyed by category/artifact/stem and never by actor, refuses activity metrics.4. Command contract lint
scripts/tests/test_command_contracts.py— the static half of a command-prose safety harness: extracts everyuv run … scripts/<name>.pyinvocation from fenced blocks incommands/*.mdand validates script existence, subcommand chains, and every--flagagainst live--help(cached subprocesses), plus agent-name andreferences//templates/cross-references. Conservative (ambiguous → skipped, never a false violation), with self-tests proving each detection class fires. It found zero drift in the existing 25 command docs and already caught one malformed invocation during this PR's own authoring.1.3.0 integration
master(clean; the only overlapping file wasaudit_artifacts.py, where the_glyphfix and this PR's additions touch disjoint regions)._glyph()Windows-console fallback introduced in 1.3.0 — verified under a forced cp1252 stdout.docs/commands.md; the count-word vocabulary intest_registry_docs_consistency.pyextended (Thirteen–Eighteen) since the additional-commands table outgrew "Twelve".Also included
.gitignoreoverride.Testing
report/impactJSON byte-identical with the version store deleted, nostate.yamlwrites from the new verbs, the ledger stays its own JSONL with nogate_resultsrows, and the.gitignoreentries exist.references/artifact-versioning.mdfor the design (R1–R6, canonical mutate order, gc policy, deletability caveat).🤖 Generated with Claude Code