---
phase: 30-v13-closure
plan: 01
type: closure
wave: 1
depends_on: [28, 29]
files_modified: [.planning/phases/30-v13-closure/30-VERIFICATION.md, .planning/v1.5-SECURITY-REVIEW.md, .planning/PROJECT.md, .planning/STATE.md, .planning/MILESTONES.md, .planning/milestones/v1.3-REQUIREMENTS.md, .planning/milestones/v1.3-ROADMAP.md]
autonomous: false
requirements: [TC-01, TC-02, TC-03]

must_haves:
  truths:
    - "This is the milestone-close verification/security spine, NOT a code-build phase. Its three tasks are OWNED BY DIFFERENT SPANS: TC-01 = qa (independent verify), TC-02 = security-engineer (independent review), TC-03 = architect (planning-doc reconcile). No engineer execute span. Gaps/findings from TC-01/TC-02 route UP to the architect; high/critical security findings BLOCK milestone completion."
    - "TC-01 verifies against CODE REALITY (re-read + re-run suites), NOT by trusting Phase-28/29 SUMMARYs. NEW work: verify every v1.3 requirement (TM-01 rename, TM-02 merge, TM-03 delete, TM-04 admin UI, TM-05 graph-store consistency) — never independently verified — PLUS confirm the whole tag surface (v1.3 ops + the Phase-29 body-rewrite fix) is coherent end-to-end. Recorded in 30-VERIFICATION.md."
    - "TC-02 is an independent security review (ASVS L1, BLOCK-ON-HIGH) of the tag surface, in the v1.4-SECURITY-REVIEW.md format (threat-model table → findings most-severe-first with typed finding/evidence blocks → risk verdict). Scope: admin-only RBAC+CSRF on all tag-admin endpoints; server-side ValidateInlineTag re-validation (no space/metachar/all-digit token can splice into a body — the ValidateTagCharset gap closed); byte-stable frontmatter+body writes (no YAML/Markdown injection, no prose corruption, no malformed doc); RewriteHashtags untrusted-input surface (crafted tag/body cannot break out of a code region); the NEW body-inclusive enumeration read path (PagesWithBodyTag/DetectStaleBodyTags MUST go through the SEC-01 resolver with the graph-rebuild skip rules — no traversal, no trashed/non-.md, no bypass). Verdict → .planning/v1.5-SECURITY-REVIEW.md; high/critical block."
    - "TC-03 corrects the docs that falsely call v1.3 'deferred/not built': PROJECT.md, STATE.md, MILESTONES.md reflect that v1.3 shipped (9de5077), was regression-fixed + verified + security-reviewed in v1.5, no longer deferred; the v1.3 archive (v1.3-REQUIREMENTS.md, v1.3-ROADMAP.md) is annotated with a closure note. Architect's span."
    - "SMTC OFFLINE — cert tiers capped at Structural/Honest in both the verification and the security review; a grep-only claim about behavior is MEDIUM, never HIGH. State the substrate honestly in both artifacts."
---

<objective>
Backfill the closure v1.3 Tag Management never received: an independent qa verification of the whole
tag surface (v1.3 TM-01…05 + the v1.5 fix) against code reality; an independent security review of the
tag-admin + body-rewrite + enumeration surface (the milestone-close security gate, block-on-high); and
a reconciliation of the planning docs that falsely described v1.3 as "deferred." TC-01…TC-03. The
MILESTONE-AUDIT + docs/codebase-map refresh are the close steps AFTER this phase.
</objective>

<execution_context>
This phase is executed by DELEGATION to the owning spans (qa, security-engineer, architect), not a
single engineer. Each task below names its owner + its artifact.
</execution_context>

<context>
@.planning/phases/30-v13-closure/30-CONTEXT.md
@.planning/REQUIREMENTS.md §TC-01…TC-03
@.planning/milestones/v1.3-REQUIREMENTS.md §TM-01…TM-05
@.planning/v1.4-SECURITY-REVIEW.md
</context>

<tasks>

<task type="delegate:qa" tdd="false">
  <name>Task 1 (TC-01): independent qa verification of the v1.3 tag surface + the v1.5 fix</name>
  <owner>qa</owner>
  <files>.planning/phases/30-v13-closure/30-VERIFICATION.md</files>
  <read_first>
    - .planning/milestones/v1.3-REQUIREMENTS.md §TM-01…TM-05 — the v1.3 bar (never verified)
    - .planning/phases/28-reading-shell-fold-in/28-VERIFICATION.md + 29-VERIFICATION.md — prior passes (build on, do NOT re-run)
    - internal/server/handlers_tags_admin.go + handlers_tags_admin_test.go — the endpoints + tests
    - internal/graph/query.go + query_test.go + the enqueueGraphUpsert path in internal/pages/batch.go — TM-05
  </read_first>
  <action>
    Verify goal-backward against CODE REALITY (re-read + re-run the suites). For each v1.3 requirement —
    TM-01 rename, TM-02 merge, TM-03 delete, TM-04 admin UI, TM-05 graph-store consistency — confirm it
    holds against the current code+tests (the endpoints, the RBAC/preview/apply/validation tests, the
    admin UI, the graph incremental-update + rebuild-reproduces-adjacency invariant). Then confirm the
    INTEGRATED whole: v1.3 admin ops + the Phase-29 body-rewrite fix work coherently end-to-end (a rename
    now durably rewrites body+frontmatter, no client re-materialization). TM-05 may lean on the green graph
    tests + a reasoned trace of enqueueGraphUpsert after a batched tag commit. Re-run backend + frontend
    suites. Each item passes or a gap routes UP to the architect. Write 30-VERIFICATION.md (per-requirement
    verdict + evidence + overall). Do NOT re-verify Phase 28's RS-* or re-run Phase 29's TR-* in full (those
    have their own VERIFICATION artifacts) — TC-01 is the v1.3 TM-* verification + the holistic coherence check.
  </action>
  <acceptance_criteria>
    - 30-VERIFICATION.md records a per-requirement verdict for TM-01…TM-05 with observed evidence (test names, endpoint behavior, graph-consistency trace)
    - the suites are re-run (backend 18 pkgs; frontend tsc/vitest/build) with results recorded
    - the integrated tag surface (v1.3 ops + v1.5 fix) is confirmed coherent, or a gap is routed up
    - verdict: passed | gaps_found | human_needed
  </acceptance_criteria>
</task>

<task type="delegate:security-engineer" tdd="false">
  <name>Task 2 (TC-02): independent security review of the tag-admin + body-rewrite + enumeration surface</name>
  <owner>security-engineer</owner>
  <files>.planning/v1.5-SECURITY-REVIEW.md</files>
  <read_first>
    - .planning/v1.4-SECURITY-REVIEW.md — the format + rigor precedent (threat-model table, typed finding/evidence, block-on-high verdict)
    - internal/okf/validate.go (ValidateInlineTag) + internal/okf/repair.go (ValidateTagCharset — the gap) + internal/okf/hashtag.go (RewriteHashtags)
    - internal/server/handlers_tags_admin.go (RBAC + CSRF + validators) + internal/pages/batch.go (body channel + byte-stability) + internal/pages/tagscan.go (enumeration read path)
    - internal/repo/path.go (the SEC-01 resolver)
  </read_first>
  <action>
    Conduct an independent ASVS-L1, BLOCK-ON-HIGH security review of the tag surface as a whole, in the
    v1.4-SECURITY-REVIEW.md format. Enumerate sources→sinks→sanitizers: sources = the admin's tag string
    (new_name/target/tag) + page bodies; sinks = on-disk .md bytes (YAML tags region + body) + the
    enumeration file reads; sanitizers = ValidateInlineTag + the conservative region-aware RewriteHashtags
    scanner + the SEC-01 resolver. Cover: (a) admin-only RBAC + nosurf CSRF on rename/merge/delete/list
    (role from session, never client input); (b) server-side inline-tag re-validation — confirm no
    space/metachar/all-digit token can be spliced into a body (the ValidateTagCharset-vs-inline-grammar gap
    is closed, enforced at the HTTP layer); (c) byte-stable frontmatter+body writes — no YAML injection, no
    Markdown injection, no prose corruption, no malformed document from a crafted tag/body; (d) the
    RewriteHashtags untrusted-input surface — a crafted tag/body cannot break out of a code region or inject
    Markdown (conservative skip-when-uncertain is the control); (e) the NEW body-inclusive enumeration read
    path — PagesWithBodyTag + DetectStaleBodyTags go through the SEC-01 resolver with the graph-rebuild skip
    rules (no path traversal, no trashed/non-.md reads, no resolver bypass). Write .planning/v1.5-SECURITY-REVIEW.md
    with the threat-model table + findings (most-severe-first, typed finding/evidence blocks) + a risk verdict.
    SMTC OFFLINE — cap cert tiers honestly. High/critical findings BLOCK and route UP to the architect.
  </action>
  <acceptance_criteria>
    - .planning/v1.5-SECURITY-REVIEW.md exists in the v1.4 format (threat model → findings → verdict)
    - all five scope areas (RBAC/CSRF, inline re-validation, byte-stable writes, RewriteHashtags input surface, enumeration read path) are addressed on the record
    - verdict recorded (SHIP / findings by severity); any high/critical is flagged as BLOCKING and routed up
    - the substrate (SMTC offline) is stated; cert tiers capped honestly
  </acceptance_criteria>
</task>

<task type="architect" tdd="false">
  <name>Task 3 (TC-03): reconcile planning docs to reality</name>
  <owner>architect</owner>
  <files>.planning/PROJECT.md, .planning/STATE.md, .planning/MILESTONES.md, .planning/milestones/v1.3-REQUIREMENTS.md, .planning/milestones/v1.3-ROADMAP.md</files>
  <read_first>
    - .planning/PROJECT.md, .planning/STATE.md, .planning/MILESTONES.md — the current "deferred" framing
    - .planning/milestones/v1.3-REQUIREMENTS.md + v1.3-ROADMAP.md — the archive to annotate
  </read_first>
  <action>
    After TC-01 + TC-02 land (their verdicts feed the honest record), correct the docs: PROJECT.md,
    STATE.md, MILESTONES.md reflect that v1.3 Tag Management SHIPPED (9de5077, 2026-06-28), was
    regression-fixed (Phase 29) + independently verified (TC-01) + security-reviewed (TC-02) in v1.5, and is
    no longer "deferred." Annotate the v1.3 archive (v1.3-REQUIREMENTS.md + v1.3-ROADMAP.md) with a closure
    note: built outside the workflow, closed retroactively in v1.5. Keep this distinct from the
    archaeologist's docs/ + codebase-map refresh (a separate close step).
  </action>
  <acceptance_criteria>
    - PROJECT.md, STATE.md, MILESTONES.md no longer describe v1.3 as "deferred / not built" — they record the ship + v1.5 closure
    - the v1.3 archive carries a closure-note annotation
    - the correction reflects the actual TC-01 + TC-02 verdicts (not written before they land)
  </acceptance_criteria>
</task>

</tasks>

<verification>
- TC-01: 30-VERIFICATION.md records TM-01…TM-05 verdicts + the holistic coherence check, suites re-run.
- TC-02: v1.5-SECURITY-REVIEW.md records the threat model + findings + verdict; high/critical block.
- TC-03: the three planning docs + the v1.3 archive reflect the honest ship+closure record.
- Gaps/findings from TC-01/TC-02 are routed to the architect; blocking items resolved before milestone completion.
</verification>

<success_criteria>
1. TC-01: independent qa verification of v1.3 TM-01…05 + the integrated v1.5 fix, against code reality, recorded in 30-VERIFICATION.md; gaps routed up.
2. TC-02: independent security review (block-on-high) of the tag-admin + body-rewrite + enumeration surface, recorded in v1.5-SECURITY-REVIEW.md; verdict + findings by severity.
3. TC-03: PROJECT/STATE/MILESTONES + the v1.3 archive reconciled to reality (v1.3 shipped + closed in v1.5, no longer "deferred").
</success_criteria>
