Skip to content

bug(cli)/discussion: post-#130 lifecycle terminal-state gap — TDE has no canonical resolved; META-003 rejects status: resolved and similar terminal states #149

@montfort

Description

@montfort

Summary

straymark validate rejects TDE documents that have moved past identified into a resolved terminal state. The validator's VALID_STATUSES enum (post-#130: draft | identified | review | accepted | superseded | deprecated) covers entry + standard ADR transitions but has no terminal lifecycle state for "the debt this TDE describes has been paid". Adopters that want to keep TDE files in-tree as audit history (rather than deleting them on closure) have no canonical way to mark the closure.

Same shape surfaces for AILOG: adopters who want to reflect that the work an AILOG documents has reached a terminal state (closed/completed/final) currently invent values outside the enum, which now fail validation.

Surfaced during the fw-4.13.4 → fw-4.14.1 adoption in StrangeDaysTech/sentinel (post-merge housekeeping of CHARTER-17). Empirical delta vs. previous bump (PR upstream #142, see Sentinel PR #73):

  • −8 errors: the long-standing CHARTER-07/09/16 originating_ailogs + originating_spec mutual-exclusion errors are now resolved. ✓
  • +6 new errors: lifecycle-terminal status values not in the validator enum.

Reported here per #130 §"No regression test catches this" — that gap closed for identified, but the same shape recurs at the terminal end of the lifecycle.

Minimal reproducer

cd /tmp && rm -rf sm-resolved-bug && mkdir sm-resolved-bug && cd sm-resolved-bug
straymark init .
straymark new --doc-type tde --title "test"
# → ✓ Created: .straymark/06-evolution/technical-debt/TDE-YYYY-MM-DD-001-test.md

# Mark the TDE as resolved (canonical lifecycle for "debt paid"):
sed -i 's/status: identified/status: resolved/' \
  .straymark/06-evolution/technical-debt/TDE-*.md

straymark validate .
# → error [META-003] Invalid status 'resolved'. Valid values: draft, identified, review, accepted, superseded, deprecated

The framework gives the operator no canonical way to record that a TDE's debt was paid — only superseded (which implies "another TDE replaced this") and deprecated (which implies "this TDE is no longer relevant"); neither captures "the debt described here was actually addressed".

Concrete surfacing

Six errors in Sentinel after the bump, all post-merge documents written when the corresponding work was completed:

Doc Sentinel status Semantic intent
TDE-2026-05-11-001 (CommsHub RequireScope architectural gap) resolved Resolved by CHARTER-15 — the gap was closed
TDE-2026-05-11-004 (charter-docs-layout drift vs fw-4.13.0) resolved Resolved by PR #64 housekeeping migration
AILOG-2026-05-07-041 (CHARTER-13 — CommsHub US2 channels) final The CHARTER it documents reached closed
AILOG-2026-05-11-042 (CHARTER-14 — CommsHub US2b broadcast) closed The CHARTER it documents reached closed
AILOG-2026-05-11-043 (CHARTER-15 — auth-hardening) closed The CHARTER it documents reached closed
AILOG-2026-05-13-048 (CHARTER-17 — CommsHub US4 templates+branding) completed The CHARTER it documents reached closed

The TDE cases are the principled gap. The AILOG cases are arguably Sentinel-side invention that should be migrated to accepted (the canonical AILOG terminal per TEMPLATE-AILOG.md and DOCUMENTATION-POLICY.md §6) — but the inconsistency in our repo grew because the validator never flagged it pre-fw-4.14.x, suggesting the previous enum was more permissive or skipped these doc types.

Per-doc-type lifecycle audit (extending #130's table to terminal states)

Doc type Entry state Canonical terminal Gap?
AILOG accepted (also the terminal per template) accepted None — but adopters may want a "work-done" state distinct from "doc-accepted"
AIDEC accepted accepted / superseded None
ETH draft accepted / deprecated None
ADR draft accepted / superseded / deprecated None — classic ADR shape
REQ draft accepted / deprecated None
TES draft accepted None
INC draft accepted None
TDE identified — (none for "debt paid") YES — resolved missing
SEC draft accepted / deprecated None
MCARD draft accepted / deprecated None
DPIA draft accepted / deprecated None

Root cause

Same as #130cli/src/validation.rs:45 VALID_STATUSES is a single flat list applied to all doc types via check_valid_status(). The fix in #130 added identified for TDE's entry state but did not add a TDE-specific terminal state.

There is no per-doc-type lifecycle declaration in DOCUMENTATION-POLICY.md §6 either — only the entry state column. Terminal states are assumed to be the universal accepted | superseded | deprecated triad. For TDE specifically this is semantically wrong: a paid debt is neither accepted (which would imply "we accept this debt continues to exist") nor superseded (which implies another TDE took its place) nor deprecated (which implies the TDE concept itself is no longer relevant).

Proposed resolutions (in increasing scope)

Option A — minimal (recommended): Add resolved to VALID_STATUSES and document it in DOCUMENTATION-POLICY.md §3 as a TDE-specific terminal state ("the technical debt described in this document has been addressed; kept on disk as audit history"). Update TEMPLATE-TDE.md body section to mention the transition. Cost: ~30 LOC + docs.

Option B — per-doc-type enums: Promote check_valid_status() from a flat enum to a HashMap<DocType, Vec<&str>> lookup, so each doc type has its own canonical lifecycle vocabulary. Aligns with #130's framing ("No doc.doc_type inspection, no branching"). Lets future doc types (e.g., RFC, runbooks) ship with their own state machines without enum bloat. Cost: ~150 LOC + per-type test matrix expansion.

Option C — lifecycle declarations in DOCUMENTATION-POLICY.md: Add a lifecycle: column to §6's table specifying the full canonical state set per doc type. The validator parses this at run-time instead of hardcoding. Aligns with the framework's pattern of governance-as-data (e.g., the DOC_TYPE_PREFIXES env-var construction in docs-validation.yml). Cost: ~250 LOC + non-trivial parsing.

Sentinel will likely take Option A locally if upstream defers (add resolved via local schema override), but we'd prefer to track the canonical fix upstream. We can contribute the patch if helpful — Sentinel's CHARTER-12 (follow-ups backlog upstream RFC #128#135 line) and CHARTER-15 (auth-hardening TDE-driven) demonstrate end-to-end TDE adoption that benefits directly from this resolution.

Workaround

Adopters can keep TDE documents at superseded with a note in the body or related: field pointing to the resolving Charter. Sentinel's TDE-001 (the resolved one) literally has Resolution: CHARTER-15 in its body and a backlink, but the status field is the wrong shape. Marking it superseded would be semantically wrong (nothing replaces it).

Regression-test ask

Mirror #130's ask: add test_validate_tde_document_terminal_resolved (and equivalents per Option chosen) to cli/tests/validate_test.rs. The test matrix should cover both entry states (identified per #130) and every documented terminal state per doc type.

Verification trail

Reproducible against current main:

straymark --version
# straymark 3.13.0

cat .straymark/00-governance/AGENT-RULES.md | tail -1
# *StrayMark v4.14.1 | [Strange Days Tech](https://strangedays.tech)*

Sentinel verification (cd to a Sentinel checkout with the fw-4.14.1 bump applied):

straymark validate 2>&1 | grep "META-003"
# 6 lines matching the table above

Net delta is −2 errors vs. the previous bump (8 CHARTER schema errors resolved by fw-4.14.x widening originating_ailogs + originating_spec to coexist, 6 new lifecycle-terminal errors introduced). Functional behavior unaffected (validate exit code 0; errors are non-blocking warnings in practice). Reported here for canonical-shape resolution, not as a release blocker.

Filed from Sentinel post-CHARTER-17 housekeeping (PR StrangeDaysTech/sentinel#75, branch chore/post-charter-17-housekeeping, commit e7b0fa5). Cross-reference: #130 (the identified entry-state fix that established the per-doc-type pattern), #128 (TDE activation triggers RFC), #142 (the CLI release that bundles the post-#130 validator).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions