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 #130 — cli/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).
Summary
straymark validaterejects TDE documents that have moved pastidentifiedinto a resolved terminal state. The validator'sVALID_STATUSESenum (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.1adoption inStrangeDaysTech/sentinel(post-merge housekeeping of CHARTER-17). Empirical delta vs. previous bump (PR upstream #142, see Sentinel PR #73):originating_ailogs + originating_specmutual-exclusion errors are now resolved. ✓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
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") anddeprecated(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:
resolvedresolvedfinalclosedclosedclosedclosedclosedcompletedclosedThe TDE cases are the principled gap. The AILOG cases are arguably Sentinel-side invention that should be migrated to
accepted(the canonical AILOG terminal perTEMPLATE-AILOG.mdandDOCUMENTATION-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)
accepted(also the terminal per template)acceptedacceptedaccepted/supersededdraftaccepted/deprecateddraftaccepted/superseded/deprecateddraftaccepted/deprecateddraftaccepteddraftacceptedidentifiedresolvedmissingdraftaccepted/deprecateddraftaccepted/deprecateddraftaccepted/deprecatedRoot cause
Same as #130 —
cli/src/validation.rs:45VALID_STATUSESis a single flat list applied to all doc types viacheck_valid_status(). The fix in #130 addedidentifiedfor 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 §6either — only the entry state column. Terminal states are assumed to be the universalaccepted | superseded | deprecatedtriad. 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
resolvedtoVALID_STATUSESand document it inDOCUMENTATION-POLICY.md §3as a TDE-specific terminal state ("the technical debt described in this document has been addressed; kept on disk as audit history"). UpdateTEMPLATE-TDE.mdbody section to mention the transition. Cost: ~30 LOC + docs.Option B — per-doc-type enums: Promote
check_valid_status()from a flat enum to aHashMap<DocType, Vec<&str>>lookup, so each doc type has its own canonical lifecycle vocabulary. Aligns with #130's framing ("Nodoc.doc_typeinspection, 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 alifecycle: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., theDOC_TYPE_PREFIXESenv-var construction indocs-validation.yml). Cost: ~250 LOC + non-trivial parsing.Sentinel will likely take Option A locally if upstream defers (add
resolvedvia 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
supersededwith a note in the body orrelated:field pointing to the resolving Charter. Sentinel's TDE-001 (the resolved one) literally hasResolution: CHARTER-15in its body and a backlink, but the status field is the wrong shape. Marking itsupersededwould 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) tocli/tests/validate_test.rs. The test matrix should cover both entry states (identifiedper #130) and every documented terminal state per doc type.Verification trail
Reproducible against current
main:Sentinel verification (cd to a Sentinel checkout with the fw-4.14.1 bump applied):
Net delta is −2 errors vs. the previous bump (8 CHARTER schema errors resolved by fw-4.14.x widening
originating_ailogs + originating_specto coexist, 6 new lifecycle-terminal errors introduced). Functional behavior unaffected (validateexit 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, branchchore/post-charter-17-housekeeping, commite7b0fa5). Cross-reference: #130 (theidentifiedentry-state fix that established the per-doc-type pattern), #128 (TDE activation triggers RFC), #142 (the CLI release that bundles the post-#130 validator).