diff --git a/docs/implementation/v1.0-tag-cut/README.md b/docs/implementation/v1.0-tag-cut/README.md new file mode 100644 index 00000000..cb95c781 --- /dev/null +++ b/docs/implementation/v1.0-tag-cut/README.md @@ -0,0 +1,44 @@ +# Clarion v1.0.0 — Tag-Cut Readiness + +**Status**: RC1 hardening — `v1.0.0` tag held pending closure of the gap register. + +This directory holds the canonical pre-tag-cut artifacts for Clarion v1.0.0. +It supersedes `docs/implementation/v0.1-publish/` (which was renamed in intent +when the v0.1 → v1.0 rebrand landed but never moved on disk) as the current +program-of-work surface for the tag. + +## Documents + +| File | Purpose | +|------|---------| +| [`gap-register.md`](gap-register.md) | Single source of truth for every gap between the current RC1 commit and a defensible `v1.0.0` tag. 24 gaps in 7 categories with evidence, fix, and effort. | +| [`execution-plan.md`](execution-plan.md) | Day-by-day sequenced execution plan, with parallel-execution markers, operator-vs-engineering split, and the exit criteria for each day. | +| [`filigree-issue-bodies.md`](filigree-issue-bodies.md) | Pre-drafted bodies for the Filigree issues that track each gap. Reference for issue creation; the live issues are authoritative once created. | + +## Origin + +The gap register is the integrated output of seven review passes: + +1. The 2026-05-20 RC1 architecture archaeology at + [`../arch-analysis-2026-05-20-2124/`](../arch-analysis-2026-05-20-2124/), + which originated risks R1–R6. +2. Six parallel subagent deep-dives executed 2026-05-22: + - Architecture critique of the five flagged blast-radius files. + - Test/quality coverage and E2E gate audit. + - Security threat review of the federation HTTP API and WP5 secret scanner. + - CI/CD pipeline review of release governance and tag safety. + - Embedded-database (SQLite) discipline review against the 13-sheet + reference set. + - Documentation/contract drift audit against ADR precedence. + +The arch-analysis snapshot is preserved as the prior baseline; this gap +register is the operational supersession. + +## Reading order + +For tag-cut decisions: `gap-register.md` → `execution-plan.md`. + +For Filigree issue creation: `filigree-issue-bodies.md`. + +For why a specific gap was raised: the gap register cites the originating +review file:line. diff --git a/docs/implementation/v1.0-tag-cut/execution-plan.md b/docs/implementation/v1.0-tag-cut/execution-plan.md new file mode 100644 index 00000000..9ab66b4b --- /dev/null +++ b/docs/implementation/v1.0-tag-cut/execution-plan.md @@ -0,0 +1,311 @@ +# Clarion v1.0.0 — Tag-Cut Execution Plan + +**Date**: 2026-05-22 +**Closes**: every gap in [`gap-register.md`](gap-register.md). +**Total effort**: ~13 hours engineering + ~3.5 hours operator. + +This plan sequences the gaps into three days of focused work, with explicit +parallel-execution markers. The first day is mechanical doc + bug fixes +that have no inter-dependencies and can run as parallel PRs. Day 2 is the +storage + CI hardening work that needs sequential review. Day 3 is the +operator-led governance + smoke + tag-cut. + +## Branching strategy + +**Decision required from operator before Day 1**: The current `RC1` branch +carries two post-1.0 dogfood commits (`a32c162`, `4dd7b63`) on top of the +release-prep stack. Pick one: + +- **Option A (recommended)**: Branch a clean `release/1.0` from `a089a21` + (last pure release-prep commit) and apply the gap-closure work there. + Leave the dogfood commits on `RC1` as the post-1.0 working branch. + - Pro: 1.0 tag is what it claims to be — release-prep only. + - Pro: Dogfood work continues unblocked on `RC1`. + - Con: One-time branch surgery; PR #12 needs re-targeting. +- **Option B**: Keep `RC1` as the release branch; document the two + dogfood commits in CHANGELOG as "1.0 also ships agent-orientation + affordances (`project_status`, `analyze_start/cancel/status`, + `index_diff`, etc.)" Update the operator MCP surface section to + enumerate the additional tools. + - Pro: No branch surgery; PR #12 ships as-is. + - Con: CHANGELOG and operator docs need expansion to cover the + new MCP tools; commitment to support the additional surface + under semver. + +This plan assumes **Option A** unless the operator instructs otherwise. +If Option B, add a Day 1.5 step to expand CHANGELOG and MCP operator +docs to cover the additional tools. + +--- + +## Day 1 — Mechanical fixes (parallelisable, no review interlock) + +**Owner**: engineering, can be dispatched to parallel agents. +**Duration**: 4–5 hours wall-clock with parallel execution. +**Exit**: all PRs merged or in-review with green CI. + +### Stream 1A — Single-line doc edits (one PR, ~30 min) + +Bundle these into a single PR titled `docs: pre-tag-cut v1.0 contract drift fixes`. + +| Gap | File | Change | +|-----|------|--------| +| DOC-01 | `CHANGELOG.md:60` | `UNAUTHORIZED` → `UNAUTHENTICATED` | +| DOC-02 | `CHANGELOG.md:60` | Add `BATCH_TOO_LARGE` | +| DOC-05 | `docs/clarion/1.0/requirements.md:573` | `See:` line add `, ADR-034` | +| DOC-08 | `docs/operator/secret-scanning.md:83` | drop `in v0.1` qualifier | +| DOC-10 | `CHANGELOG.md` | adjust "(through ADR-034)" phrasing | + +### Stream 1B — Multi-line doc rewrites (one PR, ~1 hr) + +Bundle: `docs: refresh v1.0 docs against ADR-034`. + +| Gap | File | Change | +|-----|------|--------| +| DOC-03 | `docs/clarion/1.0/requirements.md:771-783` | Rewrite NFR-SEC-03 statement + verification for ADR-034 rules | +| DOC-04 | `docs/clarion/1.0/requirements.md:558-573` | Rewrite REQ-HTTP-03 statement + verification | +| DOC-06 | `docs/suite/loom.md:65-70`, `CHANGELOG.md:108` | "v0.1 asterisks" → "v1.0 asterisks"; "deferred to v0.2" → "deferred to v1.1" | +| DOC-07 | `CLAUDE.md:144` | Rewrite HMAC paragraph; HMAC is preferred in 1.0 | +| DOC-09 | `CHANGELOG.md` (known limitations) | Add Wardline REGISTRY asterisk entry | + +### Stream 1C — New operator docs (one PR, ~1.5 hr) + +Bundle: `docs(operator): pre-tag-cut governance + storage docs`. + +| Gap | New file | Content | +|-----|----------|---------| +| GOV-03 | `docs/operator/v1.0-release-rollback.md` | Rollback / yank runbook (see gap register) | +| DOC-11 | append §Storage section to `README.md` and / or new `docs/clarion/1.0/operations.md` | Deployment constraints (NFS prohibition, no double-analyze, backup procedure) | +| SEC-02 | append section to `docs/operator/secret-scanning.md` and `docs/operator/clarion-http-read-api.md` | Loopback-no-token trust statement | +| SEC-03 | append to `docs/operator/secret-scanning.md` and CHANGELOG known-limits | Pre-WP5 catalogue upgrade requirement | + +### Stream 1D — One-line code fixes (one PR, ~1 hr including tests) + +Bundle: `fix(v1.0): pre-tag correctness fixes`. + +| Gap | File | Change | +|-----|------|--------| +| SEC-01 | `crates/clarion-storage/src/query.rs:296-302` | Fail-closed `entity_briefing_block_reason`; add malformed-JSON unit test | +| CI-02 | `crates/clarion-cli/src/http_read.rs:426-431` | Use a non-`InvalidPath` error code on body-parse failure; add a test exercising oversized body | +| SEC-02 (code half) | `crates/clarion-mcp/src/config.rs` and / or `crates/clarion-cli/src/serve.rs` startup | Emit explicit startup banner line when loopback-no-token mode is in effect | + +### Stream 1E — Filigree issue creation (one batch, ~30 min) + +Create one Filigree issue per gap, labelled `release:v1.0`. File the v1.1 +backlog items with `release:v1.1`. See [`filigree-issue-bodies.md`](filigree-issue-bodies.md). + +### Day 1 exit + +- 4 PRs (1A–1D) green in CI. +- All Filigree issues filed and labelled. +- `git log` shows no remaining R3/D1–D10/SEC-01/CI-02 gaps. + +--- + +## Day 2 — Storage + CI hardening (sequential review) + +**Owner**: engineering. Single reviewer for the storage work. +**Duration**: 4–5 hours. +**Exit**: storage and CI gaps closed; `walking-skeleton` job green with +new gates wired. + +### 2.1 — Storage cross-process safety (STO-01, ~2 hr) + +PR: `fix(storage): cross-process lock for clarion analyze`. + +- Add `fs2 = "0.4"` to `crates/clarion-cli/Cargo.toml`. +- At top of `analyze::run` (and `serve` write paths), open + `.clarion/clarion.lock`, call `try_lock_exclusive`. Hold for the + duration of the writer-actor lifetime. +- Refuse a second concurrent invocation with a clear error message: + "another `clarion analyze` is in progress against this project". +- Add a test that spawns two `clarion analyze` subprocesses against the + same project root and asserts exactly one succeeds. +- Update `docs/clarion/1.0/operations.md` (or the README §Storage) + paragraph from Stream 1C to reference the fail-fast behaviour. + +### 2.2 — Storage identity + integrity (STO-02, STO-04, ~1 hr) + +PR: `fix(storage): application_id PRAGMA + e2e integrity check`. + +- Add `PRAGMA application_id = 0x434C524E` to + `crates/clarion-storage/src/pragma.rs::apply_write_pragmas`. +- On open, assert `application_id == 0` (then set) or `0x434C524E` + (recognise); refuse any other value with a clear error. +- Add `PRAGMA integrity_check` final assertion to + `tests/e2e/sprint_1_walking_skeleton.sh`. Fail the script if + output is not exactly `ok`. +- Unit test for the application_id assertion path. + +### 2.3 — CI wiring (TEST-01, TEST-02, CI-01, ~1 hr) + +PR: `ci: wire MCP surface, Phase 3, and tag-lineage gates`. + +- Add steps to `walking-skeleton` job in `.github/workflows/ci.yml`: + ```yaml + - name: Sprint 2 MCP surface + run: bash tests/e2e/sprint_2_mcp_surface.sh + - name: Phase 3 subsystem clustering determinism + run: bash tests/e2e/phase3_subsystems.sh + ``` +- Mirror in `release.yml verify` job. +- Add ancestor check to `release.yml verify`: + ```yaml + - name: Assert tagged commit is on main + run: | + git fetch origin main + git merge-base --is-ancestor "$GITHUB_SHA" origin/main || \ + { echo "::error::tag does not point to a commit on main"; exit 1; } + ``` +- Verify both scripts pass under the CI environment before merging. + +### 2.4 — SLSA coverage for Python sdist (CI-03, ~30 min) + +PR: `ci: extend SLSA provenance to plugin sdist`. + +- Edit `release-subjects` step in `.github/workflows/release.yml:201-225` + to glob `clarion-*.tar.gz` + `clarion_plugin_python*.tar.gz`. +- Update release notes template to mention plugin-sdist provenance. + +### 2.5 — Post-publish verification (CI-04, ~45 min, optional for v1.0) + +Recommended as a v1.0 Critical-but-not-blocking; if time is tight, +defer to v1.1 with a documented gap in the rollback runbook. + +PR: `ci: verify published release matches signed artifacts`. + +- Add `verify-published-release` job after `create-release` that + downloads from the public Release URL, recomputes SHA256, and + cosign-verifies against public Rekor entries. + +### Day 2 exit + +- All Critical / High storage and CI gaps closed except STO-03 + (deferred to tag-cut moment) and CI-04 (optional). +- CI floor on `main` passes including new gates. + +--- + +## Day 3 — Operator: governance, smoke, tag-cut + +**Owner**: operator (single human, no parallelism). +**Duration**: 3–4 hours. +**Exit**: `v1.0.0` tag exists on `main`, GitHub Release published, +artifacts smoke-tested. + +### 3.1 — Enable GitHub controls (GOV-01, GOV-02, ~30 min) + +- Configure `RELEASE_GOVERNANCE_TOKEN` repository Actions secret with + read access to branch-protection, ruleset, and Actions policy + settings. +- Enable a ruleset targeting `main` that requires PR flow and these + three required CI checks: `Rust`, `Python plugin`, `Sprint 1 walking + skeleton (end-to-end)`. (After Day 2.3, optionally add the new MCP + surface + Phase 3 checks if they appear as separate Check Runs.) +- Enable a ruleset targeting `refs/tags/v*` restricting tag creators + to repository owner (or write-to-`main` set). +- Enable a constrained Actions source policy (SHA-pinning required, + or allow-list). + +### 3.2 — Live governance dry-run (~15 min) + +- Run `scripts/check-github-release-governance.py --repository + tachyon-beep/clarion --branch main` locally with a token that can + read the policy settings. Confirm exit 0. + +### 3.3 — Merge the gap-closure PRs to main (~15 min) + +- Review and merge the Day 1 + Day 2 PR stack into `main` (or merge + PR #12 if Option B was chosen, with the gap-closure work cherry-picked + on top). + +### 3.4 — Release workflow dry-run (~15 min) + +- `gh workflow run Release --ref main` (workflow_dispatch). +- Verify all jobs green; verify artifacts produced; verify no public + Release is created (the workflow conditions on `event_name == 'push'` + for the publish step). + +### 3.5 — External-operator smoke (GOV-04, ~2 hr) + +- Provision a fresh Linux x86_64 VM and a fresh macOS host (or use a + CI runner that satisfies "outside-operator" semantics). +- Run `tests/e2e/external-operator-smoke.md` checklist on each. +- Record results in + `docs/implementation/v1.0-tag-cut/external-operator-smoke-result.md` + with timestamp, host OS+arch, install command, "improvisation events" + count (target: 0), and an attestation signature. +- Commit and push to `main`. + +### 3.6 — Filigree F-1 lockstep check (~5 min) + +- Confirm Filigree's F-1 (`clarion-cd21b98463` — registry-backend + consumer rename) status against the 2026-05-19 coordinator memo. If + F-1 has merged and uses the old field name, halt — fix in Filigree + first. + +### 3.7 — Commit `published_build.txt` (STO-03, ~5 min) + +- The final pre-tag commit on `main`: + ```bash + echo "$(git rev-parse HEAD)" > crates/clarion-storage/migrations/published_build.txt + git add crates/clarion-storage/migrations/published_build.txt + git commit -m "release: mark v1.0.0 as published-build baseline" + git push origin main + ``` + +### 3.8 — Cut the tag (~5 min) + +- `git tag -a v1.0.0 -m "Clarion v1.0.0"` +- `git push origin v1.0.0` +- The release workflow fires on the push. Watch the `verify` → + `release-governance` → `build-rust` / `build-plugin` → `release` → + `provenance` chain. + +### 3.9 — Public artifact smoke (~30 min) + +- Once the release is public, install Clarion from the + GitHub-Release-hosted assets on a fresh host (or one of the + external-operator smoke VMs). +- Verify cosign signatures via `cosign verify-blob` with the + Rekor entry. +- Verify SLSA provenance via `slsa-verifier`. +- Run the walking-skeleton against a small Python corpus. +- Record success or initiate the rollback runbook. + +### Day 3 exit + +- `v1.0.0` tag pushed, Release public, artifacts verified. +- This document and the gap register move to historical-anchor status + (no edits except status-flip on the exit-criteria checklist). + +--- + +## Risk mitigation + +**If anything goes wrong on Day 3**: +- The rollback runbook (GOV-03) covers the h+30min "bad release" + scenario. The first action is always + `gh release edit v1.0.0 --prerelease`. +- The release workflow does not delete or modify the tag, so the + worst-case is an orphaned release with assets that need re-pointing. + +**If Day 2 storage work uncovers more issues**: +- The fs2 lock is the load-bearing change. If it breaks an existing + test, the issue is likely in `serve` write paths (which we may need + to scope more narrowly than `analyze`). Surface immediately; do not + merge the PR. + +**If governance check fails after Day 3.1**: +- The runbook at `docs/operator/v1.0-release-governance.md` covers the + configuration steps. If the script still fails after applying them, + the failure mode is documented in + `scripts/check-github-release-governance.py` exit codes. + +## Confidence + +This plan is grounded in the seven-pass deep dive. Effort estimates are +conservative — most Day 1 streams should complete in less than the +quoted time. The main schedule risk is Day 3.5 (external-operator +smoke), which depends on VM provisioning time and the operator's +availability. diff --git a/docs/implementation/v1.0-tag-cut/filigree-issue-bodies.md b/docs/implementation/v1.0-tag-cut/filigree-issue-bodies.md new file mode 100644 index 00000000..fd7a9488 --- /dev/null +++ b/docs/implementation/v1.0-tag-cut/filigree-issue-bodies.md @@ -0,0 +1,270 @@ +# Filigree Issue Bodies — v1.0 Tag-Cut Gaps + +Pre-drafted Filigree issues for every gap in [`gap-register.md`](gap-register.md). +Every issue cites the gap register as its source of truth so the issue body +can stay terse and the register stays canonical. + +**Filing convention**: every v1.0 issue gets `release:v1.0` + the +priority-appropriate `priority:Pn` label + a `category:` label. The +v1.1 backlog items get `release:v1.1` + the same category labels. + +## v1.0 issues + +### GOV-01 — Live GitHub governance is permissive +- **title**: `[v1.0 blocker] Enable live GitHub governance: branch protection, ruleset, RELEASE_GOVERNANCE_TOKEN` +- **priority**: P1 +- **type**: feature +- **labels**: `release:v1.0`, `category:governance`, `tier:a` +- **body**: + > Closes GOV-01 in `docs/implementation/v1.0-tag-cut/gap-register.md`. + > + > Operator action: configure live GitHub controls per + > `docs/operator/v1.0-release-governance.md`. Exit criterion is + > `scripts/check-github-release-governance.py` exits 0. +- **owner**: operator + +### GOV-02 — No tag protection rule +- **title**: `[v1.0 blocker] Add ruleset for refs/tags/v* and tag-protection check to governance script` +- **priority**: P1, **type**: feature, **labels**: `release:v1.0`, `category:governance`, `tier:a` +- **body**: Closes GOV-02 in the gap register. Two-part: (1) operator enables ruleset on `refs/tags/v*`; (2) engineering extends `scripts/check-github-release-governance.py` to assert tag protection. + +### GOV-03 — Rollback / yank runbook +- **title**: `[v1.0 blocker] Write rollback/yank runbook for bad-release scenario` +- **priority**: P1, **type**: docs, **labels**: `release:v1.0`, `category:governance` +- **body**: Closes GOV-03. Add `docs/operator/v1.0-release-rollback.md`. See gap register for required sections. + +### GOV-04 — External-operator smoke evidence +- **title**: `[v1.0 blocker] Run external-operator smoke on fresh VMs and archive dated result` +- **priority**: P1, **type**: task, **labels**: `release:v1.0`, `category:governance` +- **body**: Closes GOV-04. Result archived at `docs/implementation/v1.0-tag-cut/external-operator-smoke-result.md`. + +### DOC-01 — CHANGELOG UNAUTHORIZED → UNAUTHENTICATED +- **title**: `[v1.0 blocker] Fix CHANGELOG error-code UNAUTHORIZED → UNAUTHENTICATED` +- **priority**: P1, **type**: bug, **labels**: `release:v1.0`, `category:docs` +- **body**: Closes DOC-01. One-line edit at `CHANGELOG.md:60`. + +### DOC-02 — Add BATCH_TOO_LARGE to CHANGELOG error enum +- **title**: `[v1.0 blocker] Add BATCH_TOO_LARGE to CHANGELOG error-enum list` +- **priority**: P1, **type**: bug, **labels**: `release:v1.0`, `category:docs` +- **body**: Closes DOC-02. Edit `CHANGELOG.md:60`. + +### DOC-03 — NFR-SEC-03 stale post-ADR-034 +- **title**: `[v1.0 blocker] Refresh NFR-SEC-03 for ADR-034 authenticated-non-loopback rule` +- **priority**: P1, **type**: docs, **labels**: `release:v1.0`, `category:docs`, `adr:034` +- **body**: Closes DOC-03. Rewrite at `docs/clarion/1.0/requirements.md:771-783`. + +### DOC-04 — REQ-HTTP-03 stale post-ADR-034 +- **title**: `[v1.0 blocker] Refresh REQ-HTTP-03 for ADR-034 authenticated-non-loopback rule` +- **priority**: P1, **type**: docs, **labels**: `release:v1.0`, `category:docs`, `adr:034` +- **body**: Closes DOC-04. Rewrite at `docs/clarion/1.0/requirements.md:558-573`. + +### DOC-05 — REQ-HTTP-03 See line missing ADR-034 +- **title**: `[v1.0] Add ADR-034 to REQ-HTTP-03 See line` +- **priority**: P2, **type**: docs, **labels**: `release:v1.0`, `category:docs` +- **body**: Closes DOC-05. One-line edit. + +### DOC-06 — loom.md "v0.1 asterisks" labels +- **title**: `[v1.0 blocker] Rename loom.md "v0.1 asterisks" to "v1.0 asterisks"; CHANGELOG "deferred to v0.2" → "v1.1"` +- **priority**: P1, **type**: docs, **labels**: `release:v1.0`, `category:docs` +- **body**: Closes DOC-06. + +### DOC-07 — CLAUDE.md HMAC misrepresentation +- **title**: `[v1.0 blocker] Update CLAUDE.md: HMAC ships in 1.0 per ADR-034, not "post-1.0 hardening"` +- **priority**: P1, **type**: docs, **labels**: `release:v1.0`, `category:docs`, `adr:034` +- **body**: Closes DOC-07. Edit `CLAUDE.md:144`. + +### DOC-08 — secret-scanning.md v0.1 reference +- **title**: `[v1.0] Drop v0.1 reference from docs/operator/secret-scanning.md` +- **priority**: P2, **type**: docs, **labels**: `release:v1.0`, `category:docs` +- **body**: Closes DOC-08. One-line edit. + +### DOC-09 — CHANGELOG Wardline REGISTRY asterisk +- **title**: `[v1.0] Add Wardline REGISTRY import to CHANGELOG known limitations` +- **priority**: P2, **type**: docs, **labels**: `release:v1.0`, `category:docs` +- **body**: Closes DOC-09. + +### DOC-10 — CHANGELOG ADR-count phrasing +- **title**: `[v1.0] Adjust CHANGELOG ADR-count "(through ADR-034)" phrasing` +- **priority**: P3, **type**: docs, **labels**: `release:v1.0`, `category:docs` +- **body**: Closes DOC-10. Cosmetic. + +### DOC-11 — Storage operator constraints in README +- **title**: `[v1.0 blocker] Document storage deployment constraints (NFS, double-analyze, backup)` +- **priority**: P1, **type**: docs, **labels**: `release:v1.0`, `category:docs`, `category:storage` +- **body**: Closes DOC-11. New `docs/clarion/1.0/operations.md` or README §Storage. + +### SEC-01 — entity_briefing_block_reason fail-open +- **title**: `[v1.0 blocker] Fail-closed: entity_briefing_block_reason on malformed properties JSON` +- **priority**: P1, **type**: bug, **labels**: `release:v1.0`, `category:security`, `crate:storage` +- **body**: Closes SEC-01. One-line code change at `crates/clarion-storage/src/query.rs:296-302` plus malformed-JSON unit test. + +### SEC-02 — Loopback-no-token trust assumption +- **title**: `[v1.0 blocker] Document loopback-no-token trust + emit startup banner` +- **priority**: P1, **type**: feature, **labels**: `release:v1.0`, `category:security`, `category:docs` +- **body**: Closes SEC-02. Operator-doc additions + startup-banner warning. + +### SEC-03 — Pre-WP5 catalogue upgrade requirement +- **title**: `[v1.0 blocker] Document pre-WP5 .clarion/ upgrade requirement (re-analyze needed)` +- **priority**: P1, **type**: docs, **labels**: `release:v1.0`, `category:security`, `category:docs` +- **body**: Closes SEC-03. + +### CI-01 — Tag-lineage in-workflow check +- **title**: `[v1.0 blocker] Add ancestor-of-main check to release.yml verify` +- **priority**: P1, **type**: feature, **labels**: `release:v1.0`, `category:ci` +- **body**: Closes CI-01. Workflow edit. + +### CI-02 — Federation error code on body parse failure +- **title**: `[v1.0 blocker] Fix HMAC body-parse error code (currently returns InvalidPath)` +- **priority**: P1, **type**: bug, **labels**: `release:v1.0`, `category:federation`, `crate:cli` +- **body**: Closes CI-02. Edit `crates/clarion-cli/src/http_read.rs:426-431`. + +### CI-03 — SLSA coverage for Python sdist +- **title**: `[v1.0 blocker] Extend SLSA provenance to Python plugin sdist` +- **priority**: P1, **type**: feature, **labels**: `release:v1.0`, `category:ci` +- **body**: Closes CI-03. Edit `release-subjects` step. + +### CI-04 — Post-publish artifact verification +- **title**: `[v1.0] Verify-published-release job after create-release` +- **priority**: P2, **type**: feature, **labels**: `release:v1.0`, `category:ci` +- **body**: Closes CI-04. Optional for v1.0; defer to v1.1 if time-constrained. + +### STO-01 — Cross-process lock +- **title**: `[v1.0 blocker] fs2 advisory lock for clarion analyze (prevent concurrent corruption)` +- **priority**: P1, **type**: bug, **labels**: `release:v1.0`, `category:storage`, `crate:cli`, `crate:storage` +- **body**: Closes STO-01. Add fs2 + lock + test. + +### STO-02 — PRAGMA application_id +- **title**: `[v1.0 blocker] Set PRAGMA application_id on writer open` +- **priority**: P1, **type**: bug, **labels**: `release:v1.0`, `category:storage`, `crate:storage` +- **body**: Closes STO-02. + +### STO-03 — published_build.txt marker +- **title**: `[v1.0 blocker] Commit migrations/published_build.txt at v1.0 tag-cut` +- **priority**: P1, **type**: task, **labels**: `release:v1.0`, `category:storage` +- **body**: Closes STO-03. Final pre-tag commit on `main`. + +### STO-04 — Backup + integrity_check +- **title**: `[v1.0 blocker] PRAGMA integrity_check in e2e + documented backup procedure` +- **priority**: P1, **type**: feature, **labels**: `release:v1.0`, `category:storage` +- **body**: Closes STO-04. + +### STO-05 — Recovery liveness guard +- **title**: `[v1.0] Document run-recovery race; defer schema columns to v1.1` +- **priority**: P2, **type**: docs, **labels**: `release:v1.0`, `category:storage` +- **body**: Closes STO-05 doc-only at v1.0. v1.1 issue tracks the heartbeat columns separately. + +### TEST-01 — Wire sprint_2_mcp_surface.sh +- **title**: `[v1.0 blocker] Add sprint_2_mcp_surface.sh to CI walking-skeleton job` +- **priority**: P1, **type**: feature, **labels**: `release:v1.0`, `category:ci`, `category:tests` +- **body**: Closes TEST-01. + +### TEST-02 — Wire phase3_subsystems.sh +- **title**: `[v1.0 blocker] Add phase3_subsystems.sh to CI walking-skeleton job` +- **priority**: P1, **type**: feature, **labels**: `release:v1.0`, `category:ci`, `category:tests` +- **body**: Closes TEST-02. + +--- + +## v1.1 backlog issues + +(File these before tag-cut so post-1.0 work is tracked.) + +### V11-ARCH-01 — Extract clarion-core::errors shared error vocabulary +- priority: P2, type: feature, labels: `release:v1.1`, `category:architecture` +- body: deep-dive-arch v1.1 priority #1. Closes the MCP/HTTP error-code drift smell. + +### V11-ARCH-02 — Split analyze.rs run_with_options +- priority: P2, type: feature, labels: `release:v1.1`, `category:architecture`, `crate:cli` +- body: deep-dive-arch v1.1 priority #2. Extract `analyze/phase3.rs` and `analyze/mapping.rs`. Removes the `#[allow(too_many_lines)]`. + +### V11-ARCH-03 — Split llm_provider.rs per provider +- priority: P2, type: feature, labels: `release:v1.1`, `category:architecture`, `crate:core` +- body: deep-dive-arch v1.1 priority #3. `openrouter.rs` / `cli_provider.rs` (Codex + Claude on shared base) / `prompts.rs`. + +### V11-ARCH-04 — Split clarion-mcp/src/lib.rs into tools/ subdir +- priority: P3, type: feature, labels: `release:v1.1`, `category:architecture`, `crate:mcp` +- body: deep-dive-arch v1.1 priority #4. + +### V11-ARCH-05 — Split plugin/host.rs validation from transport +- priority: P3, type: feature, labels: `release:v1.1`, `category:architecture`, `crate:core` +- body: deep-dive-arch v1.1 priority #5. + +### V11-SEC-01 — Replace local HMAC with hmac + subtle crates +- priority: P2, type: feature, labels: `release:v1.1`, `category:security`, `crate:cli` +- body: deep-dive-security recommendation. If schedule allows, ship in v1.0 instead. + +### V11-SEC-02 — HMAC replay protection (timestamp + nonce window) +- priority: P2, type: feature, labels: `release:v1.1`, `category:security` +- body: Called out in ADR-034 forward-work. Required when non-loopback bind becomes common. + +### V11-SEC-03 — Mandatory auth on loopback binds +- priority: P3, type: feature, labels: `release:v1.1`, `category:security` +- body: Doctrine change closing T-9. Breaks the documented v0.1 trust matrix. + +### V11-STO-01 — runs.owner_pid + heartbeat_at columns +- priority: P2, type: feature, labels: `release:v1.1`, `category:storage` +- body: deep-dive-db. Schema-additive 0002_*; refine recovery WHERE-clause. + +### V11-STO-02 — clarion db backup subcommand +- priority: P2, type: feature, labels: `release:v1.1`, `category:storage`, `crate:cli` +- body: deep-dive-db. `rusqlite::backup::Backup`-based. + +### V11-STO-03 — summary_cache.entity_id FK +- priority: P2, type: bug, labels: `release:v1.1`, `category:storage` +- body: deep-dive-db. Confirmed bug, not intentional. Requires table-rebuild migration. + +### V11-STO-04 — briefing_blocked generated column + index +- priority: P2, type: feature, labels: `release:v1.1`, `category:storage` +- body: Federation read-API hot path. + +### V11-STO-05 — BEGIN IMMEDIATE + SQLITE_BUSY retry helper +- priority: P3, type: feature, labels: `release:v1.1`, `category:storage` +- body: Matters once V11-STO-01 cross-process work lands. + +### V11-STO-06 — FTS5 content_text decision +- priority: P3, type: feature, labels: `release:v1.1`, `category:storage` +- body: Drop or populate the dead-schema column. + +### V11-STO-07 — ReaderPool eager validation +- priority: P3, type: feature, labels: `release:v1.1`, `category:storage` +- body: `clarion serve` boot should fail-fast on bad DB. + +### V11-STO-08 — briefing_blocked typed column +- priority: P3, type: feature, labels: `release:v1.1`, `category:storage` +- body: Promote from JSON property. Closes a class of drift. + +### V11-CI-01 — Reusable verify workflow +- priority: P2, type: feature, labels: `release:v1.1`, `category:ci` +- body: Eliminate ci.yml/release.yml drift risk. + +### V11-CI-02 — pip-audit in build-plugin +- priority: P3, type: feature, labels: `release:v1.1`, `category:ci` +- body: Python supply chain. + +### V11-CI-03 — workflow_permissions + tag-protection in governance script +- priority: P3, type: feature, labels: `release:v1.1`, `category:ci` +- body: Extend `check-github-release-governance.py`. + +### V11-CI-04 — macOS Gatekeeper workaround doc +- priority: P3, type: docs, labels: `release:v1.1`, `category:docs` +- body: In `getting-started.md`. + +### V11-CI-05 — SBOM emission +- priority: P3, type: feature, labels: `release:v1.1`, `category:ci` +- body: cyclonedx-bom or syft. + +### V11-TEST-01 — pyright-langserver hard-fail in CI +- priority: P2, type: bug, labels: `release:v1.1`, `category:tests` +- body: deep-dive-quality. Currently silent-skip if missing. + +### V11-TEST-02 — Pyright pin lockstep script +- priority: P3, type: feature, labels: `release:v1.1`, `category:tests` +- body: deep-dive-quality provided concrete script. Adds drift defence. + +### V11-TEST-03 — Wardline version-bounds validation script +- priority: P3, type: feature, labels: `release:v1.1`, `category:tests` +- body: deep-dive-quality concrete script. + +### V11-TEST-04 — EntityCountCap ADR/code lockstep script +- priority: P3, type: feature, labels: `release:v1.1`, `category:tests` +- body: deep-dive-quality concrete script. diff --git a/docs/implementation/v1.0-tag-cut/gap-register.md b/docs/implementation/v1.0-tag-cut/gap-register.md new file mode 100644 index 00000000..d62d35fb --- /dev/null +++ b/docs/implementation/v1.0-tag-cut/gap-register.md @@ -0,0 +1,565 @@ +# Clarion v1.0.0 Tag-Cut Gap Register + +**Date**: 2026-05-22 +**Branch**: `RC1` at `4dd7b63` +**Status**: 24 gaps open against the `v1.0.0` tag-cut criterion. + +This document is the single source of truth for everything that stands between +the current RC1 commit and a defensible `v1.0.0` tag. Every gap is +evidence-cited, has a concrete fix, and has an effort estimate. The +[execution-plan.md](execution-plan.md) sequences these into a day-by-day +schedule. + +## Severity scale + +- **Critical** — supply-chain integrity or wire-contract correctness; ship a + tag without these and downstream tooling breaks or an attacker has a + concrete path. Must close before tag-cut. +- **High** — observable user-facing wrongness, missed test coverage on + shipped surfaces, or operator-visible safety gap. Must close before + tag-cut. +- **Medium** — drift between authoritative sources, documentation + inaccuracies. Must close before tag-cut for any gap on the documented + release surface. +- **Low** — internal-only polish, performance, or non-shipping discipline. + Listed for completeness; may defer to v1.1. + +## Category summary + +| Category | Count | Effort | +|----------|------:|-------:| +| Operator / governance (live GitHub controls, smoke evidence) | 4 | 3.5 hr | +| Documentation and contract drift | 11 | 1 hr | +| Security fail-closed + doc gaps | 3 | 1 hr | +| CI/CD release-path correctness | 4 | 2 hr | +| Storage / SQLite discipline | 5 | 4 hr | +| Test gate wiring | 2 | 1 hr | +| Code bug (federation error-code) | 1 | 15 min | +| **Total** | **30** | **~13 hr** | + +(The 24-count from the executive summary collapsed some related items; +this register breaks every item out individually for tracking. The 1.0 +blocker count is unchanged.) + +--- + +## Originating-review key + +- **arch-2026-05-20** — [`../arch-analysis-2026-05-20-2124/04-final-report.md`](../arch-analysis-2026-05-20-2124/04-final-report.md) and siblings +- **deep-dive-arch** — Architecture critique subagent, 2026-05-22 +- **deep-dive-security** — Threat analyst subagent, 2026-05-22 +- **deep-dive-pipeline** — Pipeline reviewer subagent, 2026-05-22 +- **deep-dive-db** — Embedded-database reviewer subagent, 2026-05-22 +- **deep-dive-docs** — Doc consistency reviewer subagent, 2026-05-22 +- **deep-dive-quality** — Coverage gap analyst subagent, 2026-05-22 + +--- + +## Gap register + +### Category 1 — Operator / governance + +#### GOV-01 (Critical) — Live GitHub governance is permissive + +- **Origin**: arch-2026-05-20 R1; deep-dive-pipeline. +- **Evidence**: `docs/implementation/v1.0-cicd-readiness.md:103-114` documents + the live state at 2026-05-20: `main` returns `404 Branch not protected`, + rulesets empty, `allowed_actions=all`, `sha_pinning_required=false`. +- **Fix**: Operator action. Enable branch protection (or active ruleset) + targeting `main` that (a) requires pull-request flow with at least the + three release CI checks (`Rust`, `Python plugin`, + `Sprint 1 walking skeleton (end-to-end)`), (b) enables a constrained + Actions source policy (SHA-pinning or allow-list), (c) configures the + `RELEASE_GOVERNANCE_TOKEN` repository Actions secret with permission to + read branch-protection, ruleset, and Actions policy settings. +- **Runbook**: [`docs/operator/v1.0-release-governance.md`](../../operator/v1.0-release-governance.md) +- **Effort**: 1 hr operator. +- **Exit criterion**: `scripts/check-github-release-governance.py + --repository tachyon-beep/clarion --branch main` exits 0. + +#### GOV-02 (Critical) — No tag protection rule + +- **Origin**: deep-dive-pipeline (net-new vs arch analysis). +- **Evidence**: `scripts/check-github-release-governance.py` does not query + `GET /repos/{owner}/{repo}/tags/protection` or check for a ruleset + targeting `refs/tags/v*`. The operator runbook only covers branch + protection. +- **Risk**: An actor with tag-push permission can push `refs/tags/v1.0.0` + pointing at any commit (a feature branch, a detached commit). If that + commit passes the workflow's gates, the release publishes from it. +- **Fix**: Add a GitHub ruleset targeting `refs/tags/v*` that restricts + who can push matching tags (creators allow-list: repository owner only, + or the same actors who can write to `main`). Add a tag-protection check + to `check-github-release-governance.py`. +- **Effort**: 5 min operator (ruleset) + 30 min engineering (script). +- **Exit criterion**: Governance script asserts tag protection on `refs/tags/v*`. + +#### GOV-03 (Critical) — No rollback / yank runbook + +- **Origin**: deep-dive-pipeline (net-new). +- **Evidence**: Neither `docs/operator/v1.0-release-governance.md` nor + `docs/implementation/v1.0-cicd-readiness.md` covers what happens after + a bad release ships. Concrete gaps for h+30min "bad release" scenario: + GitHub Release asset deletion guidance absent; Sigstore Rekor + non-revocability not stated; downstream (Filigree) notification path + undocumented. +- **Fix**: Add `docs/operator/v1.0-release-rollback.md` covering: + (a) `gh release edit v1.0.0 --prerelease` as the first action; + (b) asset-deletion policy (don't delete unless sensitive — broken URLs + are worse than stale files); (c) explicit statement that cosign + signatures in Rekor cannot be revoked, only superseded; (d) `v1.0.1` + publication procedure with `superseded by` note in the release body; + (e) Filigree consumer notification path. +- **Effort**: 1 hr docs. +- **Exit criterion**: Runbook exists, reviewed, linked from + `v1.0-release-governance.md`. + +#### GOV-04 (High) — No dated external-operator smoke result + +- **Origin**: arch-2026-05-20 R6. +- **Evidence**: `tests/e2e/external-operator-smoke.md` defines a checklist. + No dated result file exists in the tree. +- **Fix**: Operator runs the checklist on a fresh Linux x86_64 VM and a + fresh macOS host. Archive the result as + `docs/implementation/v1.0-tag-cut/external-operator-smoke-result.md` + with a timestamp, OS+arch, the install command run, the + "improvisation events" count (target: 0), and an attestation signature. +- **Effort**: 2 hr operator (mostly VM provisioning + walkthrough time). +- **Exit criterion**: Dated result file committed. + +--- + +### Category 2 — Documentation and contract drift + +#### DOC-01 (Critical) — CHANGELOG advertises non-existent `UNAUTHORIZED` error code + +- **Origin**: arch-2026-05-20 R3; deep-dive-docs C1. +- **Evidence**: `CHANGELOG.md:60` lists `UNAUTHORIZED`. Authoritative + sources (`docs/federation/contracts.md:82`, `82,202,276`, + `docs/clarion/adr/ADR-014:126`, `docs/clarion/adr/ADR-034:45,87,145`, + implementation tests at `crates/clarion-cli/tests/serve.rs:1457,1495,1547,1579,1614`) + all use `UNAUTHENTICATED`. +- **Fix**: `Edit` `UNAUTHORIZED` → `UNAUTHENTICATED` in `CHANGELOG.md:60`. +- **Effort**: 1 min. + +#### DOC-02 (High) — CHANGELOG error enum missing `BATCH_TOO_LARGE` + +- **Origin**: deep-dive-docs H1 (net-new). +- **Evidence**: `CHANGELOG.md:59-61` lists six codes plus `UNAUTHORIZED`. + `docs/federation/contracts.md:83-84` and `ADR-034:87` enumerate the same + set plus `BATCH_TOO_LARGE`. PR #12's own body claims the correction was + made — it was made in `contracts.md` but not in the CHANGELOG. +- **Fix**: `Edit` `CHANGELOG.md:60` to add `BATCH_TOO_LARGE`. +- **Effort**: 1 min. + +#### DOC-03 (High) — NFR-SEC-03 is stale post-ADR-034 + +- **Origin**: deep-dive-docs H2 (net-new). +- **Evidence**: `docs/clarion/1.0/requirements.md:771-783` says non-loopback + "When enabled, startup logs a warning that the endpoint is + unauthenticated and must be protected outside Clarion." `ADR-034:43` + says "Non-loopback binds require both `allow_non_loopback: true` and a + resolved HMAC identity secret or legacy bearer token; either alone is + insufficient." Per CLAUDE.md precedence (ADR > requirements), the + requirement statement is the bug. +- **Fix**: Rewrite NFR-SEC-03 statement and verification clause to + describe the post-ADR-034 rule: non-loopback **requires** auth; the + startup warning is for the loopback-without-token mode only. +- **Effort**: 10 min docs. + +#### DOC-04 (High) — REQ-HTTP-03 is stale post-ADR-034 + +- **Origin**: deep-dive-docs H2. +- **Evidence**: `docs/clarion/1.0/requirements.md:558-573` still describes + the HTTP API as "unauthenticated and loopback-only by default" and the + verification as "unauthenticated-surface warning". Same drift as DOC-03 + but on the routing side. +- **Fix**: Rewrite REQ-HTTP-03 statement and verification to reflect + ADR-034's authenticated-non-loopback rule. +- **Effort**: 10 min docs. + +#### DOC-05 (Medium) — REQ-HTTP-03 `See` line missing ADR-034 + +- **Origin**: deep-dive-docs M3. +- **Evidence**: `requirements.md:573` reads `See: System Design §9 + (Integrations, HTTP Read API), ADR-014.` ADR-034 partially extends + ADR-014's Security Posture and Error Envelope; a reader cannot + discover ADR-034 from the requirement. +- **Fix**: `Edit` to add `, ADR-034`. +- **Effort**: 1 min. + +#### DOC-06 (High) — loom.md still labels asterisks as "v0.1" + +- **Origin**: deep-dive-docs H3. +- **Evidence**: `docs/suite/loom.md:65,69` heads "v0.1 asterisks" and says + "The asterisk ships with v0.1 and retires in v0.2." CLAUDE.md:64 and + the CHANGELOG say both asterisks persist into v1.0 and retire + post-release. +- **Fix**: Rename "v0.1 asterisks" → "v1.0 asterisks" and update the + retirement wording. Update `CHANGELOG.md:108` "deferred to v0.2" → + "deferred to a future release (tracked under `release:v1.1`)." +- **Effort**: 5 min docs. + +#### DOC-07 (High) — CLAUDE.md misrepresents HMAC as post-1.0 + +- **Origin**: deep-dive-docs H4. +- **Evidence**: `CLAUDE.md:144` "HMAC inbound auth (C-4) — bearer is the + 1.0 wire surface; HMAC is forward-compatible and tracked for post-1.0 + hardening." Code reads `identity_token_env` and enforces + `X-Loom-Component: clarion:` today + (`crates/clarion-cli/src/http_read.rs:129-130,184,373-374`); ADR-034 + marks HMAC as the preferred mechanism; CHANGELOG:115-117 documents it + shipping. +- **Fix**: Update CLAUDE.md HMAC paragraph: HMAC ships in v1.0 per + ADR-034 as the preferred non-loopback authentication; document + `identity_token_env` config. Move "post-1.0 hardening" to refer only + to replay protection (timestamp + nonce window, ADR-034 forward-work). +- **Effort**: 10 min. + +#### DOC-08 (Medium) — secret-scanning.md carries v0.1 reference + +- **Origin**: deep-dive-docs M2. +- **Evidence**: `docs/operator/secret-scanning.md:83` "Contextual + credential suppression only recognises shell/Python `#` comments in + v0.1." +- **Fix**: Drop the version qualifier or change to "in v1.0". +- **Effort**: 1 min. + +#### DOC-09 (Medium) — loom.md asterisk 2 (Wardline REGISTRY) absent from CHANGELOG + +- **Origin**: deep-dive-docs M1. +- **Evidence**: `loom.md:70` names the Wardline REGISTRY import as + asterisk 2 with no retirement condition citation. CHANGELOG "Known + v1.0 limitations" (lines 105-117) does not mention it. +- **Fix**: Add an entry to CHANGELOG "Known limitations": "The Python + plugin imports `wardline.core.registry.REGISTRY` at startup + (loom.md §5 asterisk 2). Retirement condition: Wardline ships a + stable runtime probe API." +- **Effort**: 5 min. + +#### DOC-10 (Low) — CHANGELOG "Documentation" ADR count phrasing + +- **Origin**: deep-dive-docs L2. +- **Evidence**: CHANGELOG says "28 Accepted at 1.0 (through ADR-034)"; + the math is right but the parenthetical "(through ADR-034)" reads as + a contiguous range when in fact ADR-008/009/010/019/020 are + Backlog/Superseded. +- **Fix**: Adjust phrasing to "(ADR-001…ADR-034 with the documented + Backlog/Superseded subset excluded)" or similar. +- **Effort**: 2 min. + +#### DOC-11 (High) — Storage operator constraints missing from README + +- **Origin**: deep-dive-db low-severity finding, promoted to High here + because the cross-process race (STO-01) will surface as operator + confusion without operator-facing guidance. +- **Evidence**: There is no operator-facing doc that says (a) do not + put `.clarion/` on NFS, (b) do not run two `clarion analyze` + simultaneously, (c) backup procedure is "stop analyze → + `PRAGMA wal_checkpoint(TRUNCATE)` → file copy". +- **Fix**: Add a §Storage paragraph to top-level README or a new + `docs/clarion/1.0/operations.md` covering deployment constraints. +- **Effort**: 30 min. + +--- + +### Category 3 — Security: fail-closed + documentation + +#### SEC-01 (Critical) — `entity_briefing_block_reason` fail-open on malformed JSON + +- **Origin**: deep-dive-security T-11 (net-new). +- **Evidence**: `crates/clarion-storage/src/query.rs:296-302` + `entity_briefing_block_reason` returns `None` (= unblocked) when + `serde_json::from_str(properties_json)` fails. A plugin emitting + malformed `properties` JSON silently disables the WP5 briefing + block, exposing the entity through every federation read path. +- **Fix**: One-line change: on `from_str` failure, return + `Some("malformed_properties_json")` (or equivalent + block-with-reason). Add a unit test covering the malformed-JSON + path. +- **Effort**: 15 min code + 15 min test. + +#### SEC-02 (High) — Loopback-no-token trust assumption not documented + +- **Origin**: deep-dive-security T-9. +- **Evidence**: `crates/clarion-cli/src/http_read.rs:384-386` admits any + request when both `identity_secret` and `auth_token` are `None`. + `validate_auth_trust` (`crates/clarion-mcp/src/config.rs:307-345`) + only refuses *non-loopback* binds. On a shared developer host or CI + runner, any local process can read the entire (non-blocked) + catalogue. +- **Fix**: (a) Add explicit operator-doc section to + `docs/operator/secret-scanning.md` and `docs/operator/clarion-http-read-api.md` + describing the loopback-without-token trust assumption. (b) Add a + startup-banner line when loopback-no-token mode is in effect: + "HTTP API serving on loopback without authentication; any local + process can read the catalogue." +- **Effort**: 20 min docs + 20 min code. + +#### SEC-03 (High) — Legacy `.clarion/` upgrade requirement undocumented + +- **Origin**: deep-dive-security T-11 (sub-point). +- **Evidence**: `entity_briefing_block_reason` reads + `properties.briefing_blocked`. Pre-WP5 binaries never wrote that + property; a 1.0 binary opening a pre-WP5 `.clarion/clarion.db` will + serve the entire catalogue without refusal because every row's + `briefing_blocked` is structurally absent. +- **Fix**: Document the upgrade requirement in + `docs/operator/secret-scanning.md` and in the CHANGELOG "Known + limitations": "Upgrading from a pre-WP5 binary requires + `clarion analyze` re-run before any HTTP API serves the catalogue." +- **Effort**: 10 min. + +--- + +### Category 4 — CI / release path correctness + +#### CI-01 (Critical) — No in-workflow tag-lineage check + +- **Origin**: deep-dive-pipeline (net-new). +- **Evidence**: `release.yml verify` job runs against `$GITHUB_SHA` + (whatever the pushed tag resolves to). Nothing in the workflow asserts + that `$GITHUB_SHA` is an ancestor of `origin/main`. Combined with + GOV-02, this is the supply-chain bypass path. +- **Fix**: Add to the start of `verify`: + ```yaml + - name: Assert tagged commit is on main + run: | + git fetch origin main + git merge-base --is-ancestor "$GITHUB_SHA" origin/main || \ + { echo "::error::tag does not point to a commit on main"; exit 1; } + ``` +- **Effort**: 10 min. + +#### CI-02 (Critical) — Federation error code wrong on HMAC body parse failure + +- **Origin**: deep-dive-arch (architecture critic) + deep-dive-security + (corroborating). +- **Evidence**: `crates/clarion-cli/src/http_read.rs:426-431` — if + `to_bytes(body, HTTP_BODY_LIMIT_BYTES)` fails inside HMAC middleware, + the response returns `ErrorCode::InvalidPath` with message + "request body is invalid". A federation client pattern-matching on + `code` will mis-route a body/IO failure as a path-validation failure. +- **Fix**: Use a separate `ErrorCode::InvalidBody` (or + `ErrorCode::Internal`) on body-parse failure. Add a test that + exercises an oversized body and asserts the code field. +- **Effort**: 15 min code + 15 min test. + +#### CI-03 (High) — Python sdist not SLSA-attested + +- **Origin**: deep-dive-pipeline. +- **Evidence**: `release-subjects` in `.github/workflows/release.yml:201-225` + globs only `clarion-*.tar.gz` (Rust archives) into the SLSA provenance + subjects. The Python plugin sdist has cosign signing but no SLSA + attestation file. A user installing via `pipx install` from the + release URL has no `slsa-verifier` path. +- **Fix**: Either (a) extend the glob to include + `clarion_plugin_python*.tar.gz` and append to the existing provenance + file, or (b) document the gap explicitly in the release notes and in + `docs/operator/v1.0-release-governance.md`. +- **Effort**: 30 min for option (a); 5 min for option (b). +- **Recommended**: option (a) — it's a one-line change to the glob. + +#### CI-04 (Medium) — No automated post-publish verification + +- **Origin**: deep-dive-pipeline. +- **Evidence**: `release.yml` runs cosign verify on the same runner + immediately after signing (lines 313-323). This proves signing + worked; it does not prove the published GitHub Release artifacts + match the signed local copies. +- **Fix**: Add a final `verify-published-release` job that runs after + `create-release`, downloads the assets from the public Release URL, + recomputes SHA256, and cosign-verifies against the public Rekor + entries. +- **Effort**: 45 min. + +--- + +### Category 5 — Storage / SQLite discipline + +#### STO-01 (Critical) — No cross-process lock; second `clarion analyze` corrupts run state + +- **Origin**: deep-dive-db (highest-priority finding). +- **Evidence**: `crates/clarion-cli/src/run_lifecycle.rs:19-25` + unconditionally executes `UPDATE runs SET status='failed' WHERE + status='running'` at the top of every `clarion analyze`, then opens + the writer-actor connection. There is no `fs2::FileExt::try_lock_exclusive()` + on `.clarion/clarion.lock` or the DB file. A second concurrent + `clarion analyze` flips the live run's status to `failed` while the + first writer holds an open connection mid-batch. +- **Fix**: Add `fs2 = "0.4"` to `clarion-cli/Cargo.toml`. At the top + of `analyze::run` (and `serve` write paths), acquire + `File::open(".clarion/clarion.lock")?.try_lock_exclusive()`. Hold for + writer-actor lifetime. Fail fast with "another clarion analyze is in + progress against this project". +- **Effort**: 1-2 hr code + test. + +#### STO-02 (High) — No `PRAGMA application_id` + +- **Origin**: deep-dive-db. +- **Evidence**: `crates/clarion-storage/src/pragma.rs` sets WAL, + synchronous, busy, foreign-keys, but never `application_id` or + `user_version`. The SQLite file has no identity marker; tooling + like `file(1)` or `sqlite3 .dbinfo` cannot distinguish a Clarion DB + from any other SQLite file. +- **Fix**: Add `PRAGMA application_id = 0x434C524E` ("CLRN") to + `apply_write_pragmas`. On open, assert the application_id is 0 + (legacy / unset, then set it) or `0x434C524E` (recognise). Refuse + any other value with a clear error. +- **Effort**: 30 min code + 15 min test. + +#### STO-03 (Critical) — No `published_build.txt` migration marker + +- **Origin**: deep-dive-db; arch-2026-05-20 follow-up. +- **Evidence**: ADR-024 migration retirement guard + (`scripts/check-migration-retirement.py`) requires + `crates/clarion-storage/migrations/published_build.txt` to mark the + v1.0 commit SHA as the baseline. The file does not exist. +- **Fix**: Create the file at tag-cut time with the exact + `v1.0.0` commit SHA. Block CI on its presence going forward. +- **Effort**: 1 min, but must be done at tag-cut moment after the + release-prep PR merges. +- **Sequencing note**: STO-03 is the *last* gap closed, immediately + before pushing the `v1.0.0` tag. + +#### STO-04 (High) — No backup / `integrity_check` path + +- **Origin**: deep-dive-db. +- **Evidence**: No `VACUUM INTO`, no `rusqlite::backup::Backup`, no + `PRAGMA integrity_check` invocation in CI. A user who `cp`s + `.clarion/clarion.db` during a live `clarion analyze` gets a torn + copy because WAL pages live in `clarion.db-wal` separately. +- **Fix v1.0**: (a) Add `PRAGMA integrity_check` final assertion to + `tests/e2e/sprint_1_walking_skeleton.sh`. (b) Document the + supported backup procedure in DOC-11's README §Storage paragraph + (shutdown → `PRAGMA wal_checkpoint(TRUNCATE)` → file copy). + (c) `clarion db backup` subcommand deferred to v1.1. +- **Effort**: 30 min (parts a and b only). + +#### STO-05 (Medium) — `recover_preexisting_running_runs` has no liveness guard + +- **Origin**: deep-dive-db. +- **Evidence**: `crates/clarion-cli/src/run_lifecycle.rs:19-25` + recovery sweep is `UPDATE runs SET status='failed' WHERE + status='running'`. No PID column, no heartbeat, no startup-instance + token. STO-01's fs2 lock is necessary but not sufficient; even + fixing it leaves a same-process restart vulnerable. +- **Fix v1.0**: Document the constraint in DOC-11. Defer the + schema-additive `runs.owner_pid` + `heartbeat_at` columns to v1.1. +- **Effort**: 5 min (doc only; the v1.1 follow-up is filed as a + separate issue). + +--- + +### Category 6 — Test gate wiring + +#### TEST-01 (Critical) — `sprint_2_mcp_surface.sh` not in CI + +- **Origin**: arch-2026-05-20 R2; deep-dive-quality (highest-severity + finding). +- **Evidence**: `tests/e2e/sprint_2_mcp_surface.sh` exists and exercises + all 8 MCP navigation tools (`entity_at`, `find_entity`, `callers_of`, + `execution_paths_from`, `summary`, `issues_for`, `neighborhood`, + `subsystem_members`) over stdio against a real `clarion analyze` + output with the Python plugin venv. The MCP `serve.rs` integration + test only covers `initialize`; the `storage_tools.rs` test exercises + the underlying storage layer but not the MCP wire serialization. A + wrong-JSON-shape regression in any tool would silently corrupt an + agent's context. +- **Fix**: Add an additional step to the `walking-skeleton` job in + `ci.yml` after the existing two scripts: + ```yaml + - name: Sprint 2 MCP surface + run: bash tests/e2e/sprint_2_mcp_surface.sh + ``` + Mirror in `release.yml verify` job. +- **Effort**: 30 min (wire + verify it passes in CI). + +#### TEST-02 (High) — `phase3_subsystems.sh` not in CI + +- **Origin**: arch-2026-05-20 R2; deep-dive-quality. +- **Evidence**: `tests/e2e/phase3_subsystems.sh` exists, exercises + subsystem clustering determinism (two analyze runs, byte-for-byte + identical cluster assignments). Subsystem clustering is part of + the v1.0 advertised surface per CHANGELOG. +- **Fix**: Add to `walking-skeleton` job after `sprint_2_mcp_surface.sh`. + Mirror in `release.yml verify`. +- **Effort**: 30 min. + +--- + +### Category 7 — Code bug + +(CI-02 also fits this category; left in CI for sequencing.) + +--- + +## Out-of-scope for v1.0 (file as `release:v1.1` before tag-cut) + +Filed as Filigree issues with `release:v1.1` label so they don't get lost. + +**Architecture refactors (deep-dive-arch v1.1 priority order):** +1. Extract `clarion-core::errors` shared error-code vocabulary. +2. Split `analyze.rs` → `analyze/phase3.rs` + `analyze/mapping.rs`. +3. Split `llm_provider.rs` per-provider. +4. Split `clarion-mcp/src/lib.rs` into `tools/` subdir. +5. Split `plugin/host.rs` validation from transport. +6. Replace local HMAC-SHA256 with `hmac` + `subtle` crates. + +**Storage hardening (deep-dive-db):** +1. `runs.owner_pid` + `heartbeat_at` columns and refined recovery WHERE-clause. +2. `clarion db backup` subcommand via `rusqlite::backup::Backup`. +3. `summary_cache.entity_id` FK via table-rebuild migration (confirmed + bug — not intentional asymmetry). +4. `briefing_blocked` generated column + partial index (federation + read-API hot path). +5. `BEGIN IMMEDIATE` + `SQLITE_BUSY` retry helper across the writer. +6. FTS5 `content_text` dead-schema decision (drop or populate). +7. ReaderPool eager validation + PRAGMA `post_create` hook. +8. Promote `briefing_blocked` from JSON property to typed column. + +**Security hardening (deep-dive-security):** +1. HMAC replay protection (timestamp + nonce window). Already called + out in ADR-034 forward-work. +2. Mandatory authentication on loopback binds (doctrine change). +3. Make pyright-langserver-missing a CI hard-fail, not a silent skip. + +**CI / release hardening (deep-dive-pipeline):** +1. Refactor `release.yml verify` into a reusable workflow shared with + `ci.yml` (eliminates drift risk). +2. Add `pip-audit` step to `build-plugin` job. +3. Add tag-protection and `workflow_permissions` checks to the + governance script. +4. macOS Gatekeeper workaround doc in `getting-started.md`. +5. SBOM emission (cyclonedx-bom or syft). + +**Drift-test scripts (deep-dive-quality):** +1. `scripts/check-pyright-pin-lockstep.py` — pyproject.toml, + plugin.toml, and ci.yml cache key. +2. `scripts/check-wardline-version-bounds.py` — semver validity and + eventual server-side cross-check. +3. `scripts/check-entity-cap-lockstep.py` — limits.rs `DEFAULT_MAX` + against ADR-021 §2c. + +--- + +## Exit criteria for v1.0.0 tag-cut + +All of: + +1. Every Critical and High gap above is closed (status: ✅ in this + register, with a commit SHA citation). +2. Every Medium documentation gap is closed (operator-facing accuracy + is non-negotiable on a release artifact). +3. `scripts/check-github-release-governance.py` exits 0 against live + `tachyon-beep/clarion`. +4. PR #12 (or its successor) is merged to `main` and is the parent of + the `v1.0.0` tag commit. +5. `release.yml workflow_dispatch` dry-run from `main` produces all + expected artifacts. +6. External-operator smoke result file is dated, signed-off, and + shows 0 improvisation events on both target platforms. +7. Filigree F-1 lockstep (registry-backend consumer rename) confirmed + still aligned. +8. `published_build.txt` written with the tag commit SHA (last step + before `git tag`).