Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ jobs:
run: go install github.com/go-task/task/v3/cmd/task@latest
- name: PCS compare exit gate (RELSE-03 — REQ-PCS-S09)
run: bash hack/compare/exitgate_test.sh
# AUD-S02 (REQ-AUD-S02-02) — CHANGELOG.md drift gate, main + schedule only.
# NOT on pull_request (D-125): there actions/checkout builds refs/pull/N/merge, a
# merge commit minted at CI time whose subject ("Merge <sha> into <sha>") git-cliff
# renders through the catch-all commit parser. That line cannot exist in any
# committed CHANGELOG.md, so a PR-scoped step would be red by construction and
# unfixable by the author. `task check` runs this gate on every local commit.
- name: changelog drift gate (AUD-S02 — REQ-AUD-S02-02)
if: github.event_name != 'pull_request'
run: task changelog-verify

# RELSE-03: release exit gate includes goreleaser snapshot + docs-build — main push and
# weekly schedule only; PRs run compare-exitgate above without this job.
Expand Down
106 changes: 106 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,114 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Release notes are generated from gitmoji-conventional commits on the default branch using
[git-cliff](https://git-cliff.org/).

## Compatibility notes

Long-lived notes for consumers of released artifacts. They live in `cliff.toml`'s changelog
header because `CHANGELOG.md` is regenerated in full from commit history and a hand-edit here
would be silently overwritten by the next `task changelog-write`.

- **`pins.toolDigest` changes value after `v0.1.0` (D-120).** DecisionRecords emitted by
`v0.1.0` and earlier pin `toolDigest` as sha256 over the *tool version string*, so every
build stamped with the same version shared one digest; records carrying that derivation are
identifiable by exactly it. Builds after D-120 derive `toolDigest` from the binary's
canonical Go build info instead (main module path/version/sum, dependency checksums, VCS
revision and dirty flag), falling back to `sha256("buildinfo-unavailable\n" + toolVersion)`
when that build info is absent or does not identify the main module's content (no module sum
and no VCS revision — `go build -buildvcs=false`, test binaries). The field, its type and the
frozen `v1alpha1` schema are
unchanged — records published with `v0.1.0` remain schema-valid — but the *value* is not
comparable across the boundary: a mismatch between a pre-D-120 and a post-D-120 record means
"derived differently", not "different build".

## Unreleased

### Chores
- :wrench: chore(changelog): render version headings in Keep-a-Changelog bracket form

### Documentation
- :memo: docs(decisions): close D-111 E9 exit gate after v0.1.0
- :memo: docs(release): Homebrew tap operator runbook and honest install
- :memo: docs(install): document live Homebrew tap install
- :memo: docs(decisions): record D-111 Homebrew Formula published
- :memo: docs(openspec): Formula live; residual is PAT rotate
- :memo: docs(adr): ADR-0020 forge snapshot changed-file completeness (REL-07 P1)
- :memo: docs(decisions): D-119..D-123 audit-remediation designs
- :memo: docs(openspec): P5-AUD audit-remediation epic — 18 stories, 3 release conditions
- :memo: docs(adr): drop AI-provenance marker + review polish (D-019)
- :memo: docs(openspec): review polish — S01 lane coordination, SEC-08 alias, RELSE-08 residual
- :memo: docs(conformance): point each AUD-S01 catalog row at the test that proves it
- :memo: docs(adr): ADR-0011 Amendment 3 -- boundary enforcement mechanism made true (D-123)
- :memo: docs(release): note the jq dependency and fix the release-tooling list numbering
- :memo: docs(usage): CLI reference pinned to the binary's help output (REQ-AUD-S05-02)
- :memo: docs(usage): cover the two compare exit codes outside the gate contract (REQ-AUD-S05-02)
- :memo: docs(schemas): publish the D-120 toolDigest description (annotation only)
- :memo: docs(cli): correct the `assent run` step order to emit-before-reconcile (D-122)
- :memo: docs(install): `go install` binaries report 0.0.0-dev, not a stamped version (DOC-11)
- :memo: docs(contract): fileEvents ships add/delete — retire the "not yet implemented" note (DOC-06)
- :memo: docs(walkthrough): per-step Shipped/Planned banners replace the design-fiction header (DOC-09)
- :memo: docs(meta-plan): renumber the Phase-5 epic table to the epics that executed (DOC-10)
- :memo: docs(adr): ADR-0020 is Accepted — its contract shipped in AUD-S01
- :memo: docs(examples): drop the pre-alpha banner and the "once it exists" harness caveat
- :memo: docs(usage): document the checkout-less enumeration contract on the -checkout flag
- :memo: docs(release): mandate patch tags over in-place asset replacement (SEC-07)
- :memo: docs(install): narrow the 0.0.0-dev consequence to the version string (D-120)
- :memo: docs(decisions): record D-124 — the AUD-S06 docs gates are unwired, Lane B owns the wiring
- :memo: docs(decisions): fold three unfixed residuals into D-124
- :memo: docs(changelog): warn record consumers that pins.toolDigest changed value (D-120)
- :memo: docs(decisions): record D-125 — CHANGELOG drift gate placement and its cost
- :memo: docs(release): the changelog drift gate is in task check now, not outside it
- :memo: docs(changelog): name the second toolDigest fallback branch

### Features
- :sparkles: feat(cli): dispatch-table help listing the real subcommands (REQ-AUD-S05-01)

### Fixes
- :bug: fix(ci): install uv on release-exitgate for docs-build
- :bug: fix(release): do not skip=publish so Homebrew tap can push
- :bug: fix(release): use a POSIX class, not \t, in the gate-step if: guard
- :bug: fix(cli): show GITLAB_TOKEN in the run usage form (REQ-AUD-S05-01)
- :bug: docs(readme): pass the repo root to lint/test, and execute the quick-start (DOC-07)
- :bug: docs(readme): point the ADR-0014 link at the file that exists (DOC-05)
- :bug: fix(docs-gates): the scripts claimed a wiring that does not exist
- :bug: docs(examples): starter packs advertised a subcommand that does not exist

### Other
- :construction_worker: ci(lint): depguard deny-rules for the D-123 pure tree (REQ-AUD-S07-01)
- :closed_lock_with_key: ci(release): gate the release job on verify green at the tag SHA
- :construction_worker: ci(release): enforce the verify-tag gate's own test in CI and document it
- :construction_worker: ci(release): run the verify-tag gate's test in task check
- :construction_worker: ci(schemadrift): fence the D-120 toolDigest annotation edit
- :construction_worker: ci(release): run the CHANGELOG drift gate in task check and on main CI
- :construction_worker: ci(docs): wire the AUD-S06 docs truth-lag gates into task check (D-124)
- :construction_worker: ci(lint): wire the depguard polarity proof into task check (AUD-S07)
- :construction_worker: ci(release): wire the changelog gate test itself into task check

### Security
- :lock: fix(forge): prove changed-file enumeration completeness or declare a gap
- :lock: fix(run): degrade a checkout-less run to REVIEW when enumeration is incomplete
- :lock: fix(release): reject a tag whose only verify run is a pull_request run
- :lock: fix(run): derive pins.toolDigest from Go build info (D-120)
- :lock: fix(run): emit the DecisionRecord before forge reconcile (D-122)

### Testing
- :white_check_mark: test(forge): model truncation and diff-endpoint failure in the fake
- :white_check_mark: test(cmd): serve the paginated diffs cassette in the run-path fakes
- :white_check_mark: test(conformance): require the changed-file-completeness cases
- :white_check_mark: test(core): extend the purity walk to evaldecode, compare and schemas (REQ-AUD-S07-02)
- :white_check_mark: test(lint): fail the depguard gate on an unmapped deny target (REQ-AUD-S07-01)
- :white_check_mark: test(release): scope the gate-step wiring assertions to the gate step
- :white_check_mark: test(release): make the all-runs-green rule discriminate, and pin the gate step armed
- :white_check_mark: test(release): bind the query string, drop the pipelines, and control the negatives
- :white_check_mark: test(cli): prove each dispatch name reaches its own handler (REQ-AUD-S05-01)
- :white_check_mark: test(cli): make the binding probe unsatisfiable by the usage listing (REQ-AUD-S05-01)
- :white_check_mark: test(cli): walk the whole cmd/ tree in the stale-claim pin (REQ-AUD-S05-01)
- :white_check_mark: test(schemadrift): derive the D-120 baseline anchor instead of pinning it
- :white_check_mark: test(run): pin the atomic --emit replace by target file mode (D-122)
- :white_check_mark: test(run): pin the emit-before-reconcile invariant on stdout too (D-122)
- :white_check_mark: test(docs): pin the retired truth-lag claims so they cannot come back (DOC-05/06/09/10/11)
- :white_check_mark: test(release): pin the CHANGELOG drift gate content, wiring and polarity
## [0.1.0] - 2026-08-05

### Chores
- :tada: chore: scaffold repository — vision, ADRs, C4, meta-plan, specs skeleton, Go module, examples
- build(deps): bump actions/setup-go from 5.6.0 to 7.0.0
Expand Down
35 changes: 35 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,40 @@ tasks:
- task: build
- task: dogfood-comparison
- task: compare-exitgate-test
# AUD-S02 (REQ-AUD-S02-02): CHANGELOG.md drift gate. It is the LOCAL half of the
# wiring — CI runs it only off pull_request (D-125), because on the PR merge ref
# the generated changelog always carries a synthetic merge subject. Consequence,
# stated plainly: a commit that changes history without regenerating CHANGELOG.md
# makes the NEXT `task check` red until `task changelog-write` is committed.
- task: changelog-verify
# AUD-S02: the drift gate's own guard, and the ONLY thing that reds when the
# `- task: changelog-verify` line above is deleted. Runs after changelog-verify
# because it probes the real CHANGELOG.md (append + trap restore) — a restore
# failure must not red the simpler gate for the wrong reason.
- task: release-changelog-gate-test
# AUD-S03: the release gate's own guard. Its only other CI caller is release-exitgate,
# which is skipped on pull_request — without this a regression merges green and only
# reds main afterwards. Fast and offline (stubbed `gh`); needs jq.
- task: release-verify-tag-gate-test
# D-124: the AUD-S06 docs truth-lag gates shipped green and invoked by nothing,
# which is the same manual-gate defect they exist to close. Sequential (not a
# `deps:` entry) — go-task runs deps in parallel and readme_smoke_test.sh builds
# a binary while `fmt` rewrites the tree.
- task: docs-gates
# AUD-S07 / D-123: `task lint` proves only the CLEAN depguard polarity. Without
# this, deleting `- depguard` from .golangci.yml passes every wired gate.
- task: lint-depguard-test

lint-depguard-test:
desc: "AUD-S07 gate: adversarial proof the D-123 depguard boundary rules fire (REQ-AUD-S07-01)"
cmds:
- bash hack/lint/depguard_test.sh

docs-gates:
desc: "D-124 docs truth-lag gates: README quick-start executes (REQ-AUD-S06-01) + 18 doc pins (REQ-AUD-S06-02)"
cmds:
- bash hack/docs/readme_smoke_test.sh
- bash hack/docs/truthlag_pins_test.sh

compare-exitgate-test:
desc: "PCS-S09 exit gate: full suite + E6 seed dir + schema drift guard (REQ-PCS-S09-01..03)"
Expand Down Expand Up @@ -178,6 +208,11 @@ tasks:
cmds:
- bash hack/release/verify_test.sh

release-changelog-gate-test:
desc: "AUD-S02 gate: the CHANGELOG drift gate is wired and fires (REQ-AUD-S02-01/02)"
cmds:
- bash hack/release/changelog_gate_test.sh

release-verify-tag-gate-test:
desc: "AUD-S03 gate: verify-green-on-tag-SHA polarity table + release.yaml step order (REQ-AUD-S03-01/02)"
cmds:
Expand Down
21 changes: 20 additions & 1 deletion cliff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Release notes are generated from gitmoji-conventional commits on the default branch using
[git-cliff](https://git-cliff.org/).

## Compatibility notes

Long-lived notes for consumers of released artifacts. They live in `cliff.toml`'s changelog
header because `CHANGELOG.md` is regenerated in full from commit history and a hand-edit here
would be silently overwritten by the next `task changelog-write`.

- **`pins.toolDigest` changes value after `v0.1.0` (D-120).** DecisionRecords emitted by
`v0.1.0` and earlier pin `toolDigest` as sha256 over the *tool version string*, so every
build stamped with the same version shared one digest; records carrying that derivation are
identifiable by exactly it. Builds after D-120 derive `toolDigest` from the binary's
canonical Go build info instead (main module path/version/sum, dependency checksums, VCS
revision and dirty flag), falling back to `sha256("buildinfo-unavailable\\n" + toolVersion)`
when that build info is absent or does not identify the main module's content (no module sum
and no VCS revision — `go build -buildvcs=false`, test binaries). The field, its type and the
frozen `v1alpha1` schema are
unchanged — records published with `v0.1.0` remain schema-valid — but the *value* is not
comparable across the boundary: a mismatch between a pre-D-120 and a post-D-120 record means
"derived differently", not "different build".

"""
body = """
{% if version %}## {{ version }} - {{ timestamp | date(format="%Y-%m-%d") }}{% else %}## Unreleased{% endif %}
{% if version %}## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}{% else %}## Unreleased{% endif %}
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group }}
{% for commit in commits %}- {{ commit.message | split(pat="\n") | first }}
Expand Down
Loading