Versioned contracts
Implemented proposal and canonical-note contracts carry schema_version: 1.
Proposals carry created_at; canonical notes carry updated_at, recording the
publication update. IDs are stable and titles are editable. Unknown fields are
rejected at write boundaries; older incomplete files require explicit review
and migration instead of silent data loss. The source/extraction/event examples
below describe the broader target contracts; they are not all executable schemas.
The executable Pydantic models in src/deadwire/contracts.py enforce these boundaries for proposals and canonical notes. deadwire validate-proposal proposal.json is the local pre-review check; validation errors quarantine the payload rather than allowing partial model output into persistence.
deadwire publish-note proposal.json --reviewer <name> is the only MVP publication path. It writes Hacking/Deadwire/notes/<immutable_id>.md atomically, requires a reviewer, and requires the current content hash when updating an existing note. A stale or missing expected hash creates a conflict and leaves the canonical file unchanged.
Publication now writes the complete CanonicalNote metadata as JSON-compatible
YAML frontmatter, including proposal IDs, aliases, title, prerequisites, domain,
scores, definition, claims, commands, relationships, source references, reviewer,
and update time. The body remains readable Markdown. The source list is the union
of concept, claim, and command references. Identical approved reruns keep the
timestamp and bytes unchanged; reviewed edits advance the timestamp and preserve
prior proposal IDs and aliases. Claim status needs_review or unsupported
blocks publication; explicitly reviewed contradicted statements retain their
status and contradiction IDs instead of being silently converted to support.
Each command requires its own nonempty source_refs. A lab_verified command
requires nonblank evidence_refs, and publication checks each vault-relative
reference resolves to a nonempty file within the canonical root. File existence
does not establish authenticity, authorization, or successful execution: the
named reviewer must inspect those properties before choosing lab_verified.
Existing command JSON without source references must be completed by review;
there is no automatic inference of command provenance.
deadwire canonical-export VAULT --output .deadwire/canonical.json reloads
frontmatter, rejects incomplete metadata and duplicate IDs, validates graph
prerequisites/cycles, and writes a rebuildable snapshot. It never approves notes.
graph-rebuild consumes this snapshot. Legacy notes lacking required metadata
fail export with validation errors; repair them privately against their reviewed
proposal before retrying, without guessing their sources or reviewer.
deadwire review-index writes a text-free JSON manifest for staged drafts. It records each draft’s local path, content hash, title, and canonical source reference, plus quarantined failures. Chunked drafts are proposed; pre-chunk files from ignored experiments are legacy and excluded from current queue counts; files without provenance are unlinked. The manifest is an index for a human reviewer; it is not an approval record and never contains private source excerpts.
Source revision
{"schema_version":1,"source_id":"sha256:...","path":"Books/x.pdf","sha256":"...","bytes":123,"media_type":"application/pdf","availability":"ready","observed_at":"2026-09-10T00:00:00Z"}
source_id is stable for content. path is current location. availability is ready|placeholder|missing|unreadable|excluded. Never treat a placeholder as an empty file.
Extraction, chunk, claim
{"schema_version":1,"source_id":"sha256:...","method":"pdfkit|vision_ocr|ebooklib|markdown","pages":[{"index":0,"label":"1","text_artifact":"sha256:...","confidence":0.98,"regions":[{"x":0.1,"y":0.2,"w":0.8,"h":0.1,"text":"..."}]}]}
Chunks contain chunk_id, source reference, heading path, text artifact hash, token count, and extractor version. A claim contains claim_id, statement, source_refs[], support_status, and optional contradicts[]. Commands additionally contain tool, syntax, environment, verification_status (unverified|lab_verified|rejected), and evidence references.
Concept proposal and canonical note
Proposals contain proposal_id, immutable candidate ID, name, definition, domain, prerequisites, related concepts, contrasts, commands, claims, difficulty, importance, and source_refs[]. Canonical notes add status (draft|approved|deprecated), aliases, merge history, reviewer, and expected file hash. Approved merges create redirects from every proposal ID; they do not delete provenance.
API
GET /healthz is unauthenticated and returns {status: "ok"}. PUT /v1/artifacts/{sha256} requires Authorization: Bearer ..., streams at most 1 MiB chunks, validates lowercase SHA-256, rejects unsafe paths, and returns 201 for a new object or 200 for a byte-identical replay. A replay with different bytes is rejected. GET /v1/artifacts/{sha256} is authenticated and permits only an exact lowercase digest lookup, returning the checksum header; the service never accepts a filesystem path, command, archive extraction request, or arbitrary read operation.
Learning and evaluation
Learning events contain event_id, concept ID, event type (recall|lab|explanation|review), score, evidence reference, occurred time, and client id. Duplicate event IDs are no-ops. Evaluation runs contain run ID, source split, fixture version, model revisions, metric definitions, sample counts, and results; a missing result is not_measured, never zero.
Planning input and output
The planning CLI accepts an array of approved concepts with concept_id, editable title, prerequisites[], bounded goal_relevance, importance, difficulty, estimated_minutes, and status. Optional learner state maps each concept ID to nullable recall, lab, and explanation scores. Output includes the stable concept ID, prerequisites, total score, all four weighted score components, estimated minutes, and the fixed 20/15/40/15 daily time budget. A cycle, unresolved prerequisite, out-of-range score, or deprecated concept fails closed before recommendations are printed.
Graph projection
The MVP projects Concept, Technique, Tool, Lab, Source, Chapter, CVE, Detection, Defense, Goal, and Card labels. Allowed relationships are PREREQUISITE_OF, PART_OF, RELATED_TO, CONTRASTS_WITH, EXAMPLE_OF, DETECTED_BY, MITIGATED_BY, SOURCED_FROM, PRACTICED_IN, TEACHES, and SUPERSEDES. Each projected entity carries stable id, name, status, and provenance; concept nodes additionally carry bounded mastery, difficulty, importance, last_reviewed, and next_review. Relationship writes are parameterized and reconciled against the canonical-note projection.
The pure projection in src/deadwire/graph.py includes only approved notes, points PREREQUISITE_OF from foundation to dependent, rejects dangling references and cycles, and returns deterministic node/relationship sets. reconcile_relationships computes additions and removals for a future parameterized Neo4j transaction; Neo4j remains a rebuildable projection.
Anki export
deadwire anki-export reviewed-concepts.json cards.tsv emits deterministic front/back/tags fields with a deadwire:<concept-id> tag. The export contains no scheduling intervals; Anki owns card scheduling and Deadwire retains the learner evidence and path selection.
Evaluation reports use {run_id, model_revision, synthetic, metrics[]}. Each metric records name, nullable value, optional threshold, sample_size, and direction. A missing value is rendered as not measured; synthetic: true is displayed as SYNTHETIC EXAMPLE.