docir — design documents Graph

Documents / Issues / issue-b47a1203baa2

`schema validate` does not check that transition targets are declared statuses

A command whose entire purpose is "check an edit before it reaches a write" (app.py:138) passes the most likely schema error, and the eventual message misdirects.

issue-b47a1203baa2issueresolved#material#schema
View as Markdown◉ View in graph

Class: misleading · Severity: material Flow: arch-90c90751344f · Step: docir schema validate Question: issue-2b28fd8b1dfa · Frequency: any hand-edited schema with a typo — the case the command exists for

Finding

schema validate does not check that transition targets or inactive_statuses are declared statuses, so a type can pass validation with no reachable exit from its default status.

What happens today

OBSERVED. statuses: {open: [closd], closed: []} plus inactive_statuses: [done] returns {"valid":true}. The failure appears later as invalid transition 'open' -> 'closed' — naming a status that IS declared, which points the reader at the write instead of at the typo in the schema.

Impact

A command whose entire purpose is "check an edit before it reaches a write" (app.py:138) passes the most likely schema error, and the eventual message misdirects. Documents can be created in a state with no legal exit.

Proposed default

In _parse_type, reject transition targets and inactive_statuses not present in the type's declared status set; warn when a non-inactive status has no outgoing transitions.

Resolution

FIXED 2026-07-28 — the rejections, not the warning. _parse_type now rejects any status name the type does not declare: a transition target, an inactive_statuses entry, and default_status (which was unchecked too, and would have failed every add of that type). The message names the offending value and lists the declared statuses, so it points at the schema instead of at the write. PROBE-12 replayed: the typo'd schema now exits 3 with "type 'ticket' status 'open' transitions to undeclared status(es) 'closd'; declared: closed, open" where it returned {"valid":true}. THE PROPOSED WARNING WAS BUILT AND THEN DROPPED, on evidence. "Warn when a non-inactive status has no outgoing transitions" fires on 5 of the 15 shipped typesrelease_note.published, postmortem.published, experiment.complete, hypothesis.supported, obligation.breached — and every one is a correct terminal state for a document that stays live and relevant. Marking them inactive to silence it would be worse: it would hide published postmortems from the default read path. That is issue-40d1792bc9f9 exactly, two commits after fixing it: a warning that fires on the product's own defaults, which teaches users to ignore the command. The heuristic is not merely noisy, it is wrong — "terminal" and "closed" are different properties. Recorded here rather than shipped, and noted in the schema validate docstring so it is not rebuilt naively. The "every state has an exit" coverage-checklist item is therefore NOT met, deliberately. A rule that could tell a missing transition from an intended terminal state would need to know the type's intent, which nothing in the schema expresses.

Actors affected

  • repository maintainer

Evidence

  • src/docir/modules/documents/infra/schema_loader.py:142-192
  • src/docir/modules/documents/domain/schema.py:97-117
  • ref-1509d5dbb4c3 (discovery probe log)

Migrated from the discovery gap register (GAP-010); the register itself now lives in this store.

To amend: Re-verify: