Proposal review workflow
Distillation creates private proposals. Review is the boundary that turns one proposal into an authoritative note in Hacking/Deadwire/; a proposal never becomes course material merely because a model produced Markdown.
1. Build the review queue
Run the index from the repository root:
deadwire review-index
The resulting .deadwire/distill-staging/review-index.json contains local draft paths, draft hashes, titles, canonical source references, and failure records. It does not contain source excerpts. proposed entries are current #chunk: drafts; legacy entries are pre-chunk experiments and are excluded from queue counts.
Each draft also carries non-blocking review_flags when the text contains a destructive command, shell-pipe execution, reverse-shell pattern, exploit-framework invocation, or public IPv4 literal. A flag is a prompt for a human scope check, not an automatic rejection; the reviewer records the decision and keeps the lab boundary explicit.
2. Inspect one proposal
For PDF drafts, create a private packet with copies of the selected drafts,
their exact extracted text, source hashes, and physical-page/character locators.
Write a JSON array of 1–30 filenames selected from review-index.json into
.deadwire/review-selection.json, then run:
deadwire review-packet .deadwire/review-selection.json \
--output .deadwire/review-packets/new-review-batch \
--source-root "/path/to/Hacking"
Open the packet's README.md. The original PDF links stay local. Source bytes
must match the recorded SHA-256; page lengths must resolve the exact extraction
span; missing or changed sources fail the operation. The packet snapshots drafts
so later processing cannot silently change the material under review. Choose a
new output directory for a new batch: existing packets are never overwritten.
Keep packets under the ignored .deadwire directory or another private location;
they contain source excerpts and must not be committed or uploaded to Pages.
Locators use zero-based page_index and half-open page-local char_start:char_end.
The UI also lists one-based physical PDF pages. These are not verified printed
page labels or visual bounding boxes. Legacy chunks assume 4,000 characters and
200 overlap; verify these settings against the generating run. The original
source hash proves revision identity, not OCR fidelity, correctness of a claim,
or successful execution of a command. EPUB/text packet adapters remain pending.
First source spot-check
A private six-draft packet from three books was prepared on 2026-09-11. Fourteen page-local text slices matched fresh direct extraction from their original PDFs. This validates those locators, not human claim support. Machine review found unsupported commands, conflated protocol details, and expected outcomes written as actual evidence in several older drafts. All six decisions remain pending; five carry revision recommendations. The selection was purposeful and small, so these observations are not a corpus accuracy estimate.
The packet includes machine-review.md, draft-hash-bound findings, a source-check
record, and one schema-valid service-host proposal with needs_review claims and
an unverified command. A reviewer must assess the sources and corrections before
changing its status. No note or course paragraph was approved by assembling it.
Open the referenced Markdown file on the local machine and check the source revision privately. The reviewer should answer each question before creating a proposal:
| Check | Pass condition | Reject or quarantine when |
|---|---|---|
| Identity | The source hash and locator resolve to the intended revision | The file is missing, a placeholder, or the locator is ambiguous |
| Originality | The explanation is an original synthesis; short command tokens are bounded | A long verbatim span or copied table is present |
| Claims | Every externally checkable claim has one or more source references | A claim is uncited, contradicted, or presented as verified without evidence |
| Commands | Syntax, tool, environment, and status are explicit | A command is unsafe, unbounded, or silently normalized |
| Safety | The exercise names authorization, scope, abort, and cleanup | It targets a public or third-party system or disables controls |
| Merge | The candidate is distinct from canonical concepts and related proposals | Similarity is treated as permission to merge without human reasoning |
Preserve rejected proposals and the reason for rejection. A failed model parse is not a missing concept and must never create a graph node.
3. Create a typed proposal
Revise a flagged draft first
For a packet with machine-findings.json, use the host Codex CLI to produce a new
draft against the checked source excerpt and the recorded findings:
DEADWIRE_ALLOW_CODEX_EGRESS=1 deadwire revise-packet \
.deadwire/review-packets/new-review-batch \
--output .deadwire/revisions/new-revision-run --limit 1
The findings file has an entries array; each item names draft, draft_sha256,
recommendation: revise, an issues array, and optional primary_sources.
The hash must match the packet's snapshotted draft. The runner validates both
draft and excerpt hashes before invoking gpt-5.6-luna, includes revision inputs
directly in the prompt, and preserves original drafts. --draft FILENAME selects
one particular item; --limit is bounded to 1–30. Use a new output directory for
each run and finish any active generation queue first: revisions run sequentially.
run.json records the provider/model, feedback hash, old and new content hashes,
and pending-review status. A model failure stops the run and records an error
type without copying private provider stderr. Explicit source-unavailable
responses are rejected; this narrow check is not proof of source grounding.
Completed revisions still require human inspection. The runner never updates
canonical notes, grants approval, or changes the original staging queue counts.
On 2026-09-11, a five-draft revision run removed several previously flagged unsupported commands and verification claims. One response reported unavailable source text and was retained for diagnosis, prompting direct excerpt delivery and a regression check. This purposeful spot-check is not a measured model accuracy improvement or an acceptance sample.
Use a stable immutable ID and retain the source revision. Titles may change later; IDs and provenance must not. A minimal synthetic example is:
{
"schema_version": 1,
"proposal_id": "proposal-trust-boundary-001",
"immutable_id": "trust-boundaries",
"title": "Trust boundaries",
"definition": "A boundary where identity, privilege, or authorization changes.",
"domain": "foundations",
"prerequisites": [],
"related": [],
"contrasts": [],
"claims": [{
"claim_id": "claim-observation-001",
"statement": "An open port is an observation, not proof that a service is exploitable.",
"source_refs": [{"source_id": "sha256:fixture", "locator": "page:1"}],
"support_status": "needs_review",
"contradicts": []
}],
"commands": [],
"difficulty": 0.2,
"importance": 0.9,
"source_refs": [{"source_id": "sha256:fixture", "locator": "page:1"}],
"created_at": "2026-01-01T00:00:00Z"
}
Validate before publication:
deadwire validate-proposal reviewed-proposal.json
Validation rejects unknown fields, missing provenance, invalid IDs, and out-of-range scores. It does not replace human source review.
The example deliberately leaves the claim at needs_review. After inspecting
the source, record source_supported for a supported statement or an explicit
contradicted status and contradiction references where appropriate. Publication
rejects needs_review and unsupported claims. Do not change the status simply
to make validation pass; revise or reject an unsupported proposal.
Commands require their own source_refs, even when their source is also listed
on the concept. Keep verification_status: unverified until a human has inspected
lab evidence. For lab_verified, evidence_refs must name nonempty files relative
to the vault, such as learning/2026-09-11/session.md. The file check cannot verify
that the recorded experiment actually happened.
4. Publish with conflict protection
For a new concept, publish only after a named reviewer signs the decision:
deadwire publish-note reviewed-proposal.json \
--reviewer "reviewer-id" \
--canonical-root "/path/to/Hacking/Deadwire"
For an update, first hash the current canonical note and pass it as --expected-hash. A mismatch creates a conflict and leaves the handwritten or iCloud-edited note untouched. Approved merges preserve aliases and redirects; they do not erase proposal provenance.
5. Rebuild derived views
After publication, export the canonical notes directly from their Markdown metadata:
deadwire canonical-export "/path/to/Hacking/Deadwire" \
--output .deadwire/canonical.json
deadwire graph-rebuild .deadwire/canonical.json
graph-rebuild requires the running Neo4j service and DEADWIRE_NEO4J_PASSWORD.
The exporter validates complete metadata, rejects duplicate concept IDs,
unresolved prerequisites and cycles, and reports eligible node and relationship
counts. Legacy notes with incomplete frontmatter require explicit repair using
their reviewed proposals; they are never silently dropped. The JSON snapshot
stays private and is rebuildable from the notes.
The Markdown metadata preserves claims, commands, source/evidence references, aliases, proposal IDs, and prerequisite IDs through this round trip. Deterministic round-trip tests on synthetic notes are implemented; a real reviewed-vault/Neo4j recovery drill remains an acceptance gate. Rebuild embeddings separately once the reviewed corpus index is available. Only reviewed concepts with valid prerequisites may enter a student plan.
Student-facing rule
The published course may describe a technique as an idea or an unverified command. It may call a command verified only when a learner records authorized lab evidence with environment, scope, timestamp, output, and cleanup. Reading a proposal, approving a note, or passing a retrieval test is not practical mastery.