Repository navigation
Traces written before ResourceState marshaled names cannot be decoded; the store's history is unreadable #758
Description
Feature: #458 — Schema generation and document contracts
Traces written before ResourceState began marshaling a name cannot be decoded, so the store's history is
unreadable and writ status reports nothing about what is deployed.
Measured, on a live machine
$ writ status -o json | jq '.health'
{
"runs": 0,
"findings": [
"trace .../20260809T071528....yaml cannot be read: op.LoadTrace: yaml decode:
op.ResourceState: unknown state \"1\"",
... two more
]
}
runs: 0 and entries: null follow directly: nothing folded, so nothing is known about deployed files.
Cause
The stored document holds the integer:
state: 1ResourceState's decoder accepts names only (pkg/op/resource_state.go:118-122):
case "pending":
case "active":
case "gone":So every trace written before the change to name-marshaling is undecodable. Three exist on this machine,
dated 2026-08-09.
This may not be a code defect
The failure is handled the way it should be: Health.Findings reports each unreadable trace by path and
reason, and the command still succeeds. That is the designed behavior for a store whose contents disagree
with itself, and the message names the cause precisely.
Under the governing principle -- greenfield, no legacy, no backward compatibility -- accepting the old form
would be the bug. So the likely resolution is discard: the three files are from a retired format and
should be removed, not migrated.
Decided 2026-08-31
Discard is the ruling, and a trace gains a version so a stale document reads as stale.
The scheme already exists in the tree and is applied to one document of three. A graph carries it:
kind: com.noblefactor.DevLore.Graph
schema_version: 1A trace opens straight into graph_checksum, and the run index carries nothing per line. So the work is
not to invent versioning -- it is to apply GraphSchemaVersion's scheme (graph.go:41, a uint32) to the
two documents that lack it.
With a version on a trace, the loader reads it first: absent or below the supported floor means written by
a retired format, and a decode failure at the current version means damaged. That is the distinction
this issue was filed for, and it needs no legacy-decoding path -- a retired document is still refused, with
an honest reason instead of unknown state "1".
The three files on the affected machine are the operator's to delete. Nothing here migrates them.
The versioning policy this sits under
Recorded in docs/architecture/10-command-line-interface.md §13. An application and the documents it writes
carry separate versions -- the application from git describe, a document as a uint32 -- because the
application version changes on every commit and would otherwise claim a new format between identical ones.
On the release of a major application version the store version is bumped with it, as policy rather than as
an invariant: the two remain independently versionable, a format bumps whenever it actually changes, and the
policy is revisited regularly.
Remaining work
-
schema_versionwidens fromuint32to a declared semver string. The value tracks the
application's for now -- a reader attributing"1.0.0"to a release needs no lookup table -- but it is
a literal rather than a computed value, so the two diverge the day a format changes and the
application does not, by editing one constant. An integer forecloses that: it cannot express1.2.0,
so an additive format change would have to become a major bump or go unrecorded.
This changes a serialized field's type, so it moves the canonical form and every stored checksum. It
belongs with the serialization work, not with a CLI branch. - A trace carries
kind: com.noblefactor.DevLore.Traceandschema_version, as a graph does. - The run index carries a version. Per line rather than as a header, so it stays self-describing and
survives the truncationTestIndexTornLastLinealready covers. -
LoadTracereports a retired version distinctly from a decode failure, andwrit statussurfaces the
two differently -- only damage deserves alarm.
Related
The visible symptom overlaps #756: on that machine entries is null and runs is 0, so writ status
answers nothing useful. #756 covers the missing-index case; this covers the unreadable-document case.
Activity
doc(cli): suite membership is infrastructure, and two version numbers (…
Every presentation is a presentation of the JSON: formatting rules, l…
A value in an any slot carries its type, a resource slot records the …