# v1.5 Milestone Audit — Tag Regression Fix, Reading-Shell Fold-In & v1.3 Closure

**Auditor:** architect (author of record) · **Date:** 2026-07-01
**Range:** `95ca67d..HEAD` on `main` (22 commits; Phases 28–30).
**Method:** refinement check — does the BUILT system satisfy the milestone's STATED intent? Grounded
in a direct re-read of the system (handler wire-in, batch body channel, the goldens, re-run suites,
the offset-parity drift-perturbation, the security review), NOT in the phase SUMMARYs.
**Substrate:** SMTC daemon OFFLINE this session — code claims are grep/read + test-execution
(Structural/Empirical), never Decision-tier. Native agent-teams spawn unavailable → all spans
delegated via the `task` tool.

**Verdict: v1.5 COMPLETE.** All three stated intents met against code reality; the milestone's central
risk (the "second `#tag` parser" drift) was retired with an independently-confirmed anti-drift gate.
Two LOW security findings tracked (one pre-existing, one env), one LOW security finding fixed.

---

## The milestone's three stated intents (REQUIREMENTS.md Overview) — refinement check

### Intent 1 — Fix the confirmed cross-milestone tag regression → MET

**Stated (P):** v1.4's inline-`#tag` materialization silently reverted v1.3's tag rename/merge/delete
(they rewrote frontmatter only; the client re-unioned body `#tags` on save, re-adding the removed tag).
**Required (Q):** server-authoritative body rewrite in the same byte-stable commit, durable for all
future ops incl. never-reopened pages.

**Built (observed directly):**
- `okf.RewriteHashtags` (internal/okf/hashtag.go, 468 ln) — region-aware (fenced/inline/escape/
  indented-code/HTML-block/ATX-heading), conservative (skip-when-uncertain), byte-stable on no-match.
- Wired into rename/merge/delete: `buildTagApplyItems` predicate widened to `(changed || bodyWillChange)`
  (handlers_tags_admin.go:240); `ApplyTagsBatch` recomputes the body from its OWN freshly-read body
  (TOCTOU-safe, batch.go:150-152) via RewriteHashtags — no longer emits `doc.Body` verbatim.
- Regression closed (goldens, batch_bodyrewrite_test.go): rename/merge/delete + a BODY-ONLY page all
  prove the removed tag does NOT reappear after reconcile; `TestBodyRewriteStaleClient409` pins the
  stale-open-client invariant a client-only fix could not cover.
- **Refinement holds:** the fix is server-authoritative + one byte-stable commit (Q satisfied). The
  ROADMAP's over-claim "for all pages including never-reopened" was honestly SOFTENED to "for all
  FUTURE operations" + a TR-06 detect+report scan (`DetectStaleBodyTags`) surfacing historical
  corruption — the record does not claim more than the code delivers.

### Intent 2 — Fold in the reading-shell refactor as a clean, verified baseline → MET

**Stated:** a ~65-file uncommitted Obsidian-parity refactor must become a verified-before-committed
baseline with its test gap closed + scope reductions documented.

**Built (observed directly):**
- Committed as 6 ordered, coherent commits (2ad00f0…4916a9d): reading-shell document view, inline
  navrail search, local-graph removal end-to-end, the CM6 fix ALONE (independently revertable), +
  housekeeping + docs. Verified BEFORE commit (the "committed-then-breaks" pattern the milestone exists
  to fix was NOT repeated).
- The one material coverage gap (the nav-search redesign) closed: NavSearchResults + navSearch + ⌘K
  tests. The CM6 coordinate change confirmed in a real browser via a deterministic `posAtCoords↔
  coordsAtPos` round-trip (maxErr=1 char; all wrapped rows exact) + 3 screenshots.
- **Refinement holds:** final HEAD green (go 18 pkgs; tsc 0; vitest 804/65; build 0), tree clean,
  dist/.gitkeep intact. 28-VERIFICATION.md: PASSED, no coverage loss (every deleted test maps to a
  removed feature).

### Intent 3 — Formally close v1.3 (verify + security + docs reconcile) → MET

**Stated:** v1.3 shipped as code (`9de5077`) with no independent verification, security review, or
audit; v1.5 backfills all three.

**Built (observed directly):**
- **TC-01 (qa):** 30-VERIFICATION.md — v1.3 TM-01…05 independently verified against code reality
  (endpoints admin-gated, preview→approve, N→1 batched commit, per-page 409, audit; TM-05 graph
  consistency proven via rebuild==incremental + `-race` clean); holistic coherence confirmed. Verdict
  PASSED.
- **TC-02 (security):** v1.5-SECURITY-REVIEW.md — ASVS-L1 block-on-high review of the tag-admin +
  body-rewrite + enumeration surface. Verdict SHIP, 0 critical/high; all 5 scope areas verified clean
  (RBAC/CSRF, inline-gate closes the ValidateTagCharset gap, byte-stable writes, RewriteHashtags
  breakout structurally impossible for validated input, SEC-01 enumeration read path).
- **TC-03 (architect):** PROJECT.md already corrected the "deferred" framing; the v1.3 archive
  (v1.3-REQUIREMENTS.md, v1.3-ROADMAP.md) annotated with closure notes.
- **Refinement holds:** the record is now honest — v1.3 shipped `9de5077`, closed retroactively in v1.5.

---

## The central risk (roadmap) — retired

The milestone's central risk was the Phase-21-warned "second `#tag` parser" drift (a Go scanner that
disagrees with the TS Lezer grammar). Mitigation: an offset-level, whole-document, single-sourced
parity test. **Independently confirmed real, not hollow:** qa perturbed a corpus span → BOTH the Go
`LezerParity` test AND the TS regenerate-diff FAILED → reverted → both pass. The anti-drift guarantee
is CI-enforced.

## Findings ledger (routed + dispositioned)

| Finding | Sev | Disposition |
|---|---|---|
| Phase-28: RS-04 human-mouse-drag spot-check | LOW | Verified to a stronger deterministic standard; optional co-driver spot-check (not a gap) |
| Phase-29 F1: delete-dialog body marker + frontend test | LOW | FIXED (commit 1435ea6) |
| Phase-29 F2: handler validator-gate test granularity | LOW | FIXED (commit 1435ea6) |
| SEC-v1.5-F01: nested-tag target 500→400 (validator divergence) | LOW | FIXED (commit e0fedaa) |
| SEC-v1.5-F02: uncapped JSON body on tag-admin POSTs | LOW | TRACKED — pre-existing; folded into the v1.4-F02 global-JSON-cap follow-up |
| SEC-v1.5-F03: govulncheck toolchain panic | INFO | TRACKED — env; zero new Go deps (empty go.mod diff) |
| TC-01 LOW: rename conflict/dedupe lacks a dedicated seeded test | LOW | TRACKED — logic correct by trace; identical merge-dedup IS tested (test-granularity) |

No high/critical findings; block-on-high not triggered. Nothing blocks milestone completion.

## Measurability + substrate honesty

- `anvil-cc debt query` = **debt=0** (no open obligations/handoffs).
- `anvil-cc measure refusal-rate` = **0 gates**. This is NOT a rubber-stamp streak: the run delegated
  every span via the `task` tool (native agent-teams + typed `gate_result` ledger emission were not
  wired this session; SMTC offline). The gates genuinely occurred — four verdict-bearing artifacts on
  disk (28/29/30-VERIFICATION + v1.5-SECURITY-REVIEW), two of which recorded fail→fix→pass loops (the
  Phase-29 + F-01 findings). The sidecar reads 0 because the emission path was inactive, not because
  the spine was skipped. Stated plainly per the honesty floor.
- Each phase was independently verified by a different role than the one that built it (engineer→qa,
  build→security), and gaps routed UP to the architect who dispositioned them — the judge≠doer
  separation held throughout.

## Bottom line

v1.5 delivered its three intents against code reality: the confirmed regression is closed
server-authoritatively (durable for all future ops; historical corruption surfaced not claimed away),
the reading-shell refactor is a verified committed baseline, and v1.3 is formally closed (verified +
security-reviewed + record reconciled). Final suites green at HEAD. Milestone COMPLETE.
