Plan P1 — trp: the trellis-prose checker
References: docs/trellis-prose.md (the whole design), docs/lock-schema.md
(for the spirit of lock conventions), examples/prose/ (normative
fixtures). This is the first milestone of the trellis-prose sibling track;
it shares the repo’s Rust workspace but depends on nothing in the Soil
build order.
Decisions resolved with the user (2026-09-14)
These four were decided before this plan was written; they are recorded here with reasoning, per the working rules.
- Deliverable: plan doc first, build later. Building starts in a separate session from this plan, the way plans 01–07 work.
- Code home: a
trpcrate in the existingrust/workspace. Shares the toolchain,check.sh, and testing conventions; parser/checker/lock code benefits from Rust’s strictness. A separate repo was rejected while trellis-prose remains a sibling design inside this one; Python was rejected as a weaker fit with the workspace (and the segmentation rule below removes the need for NLP libraries). - Lowering: checker-only tool, in-session agent.
trpis a pure oracle: it parses, verifies, and manages locks, and never calls a model. A Claude Code session performs the lowering and iterates againsttrp checkuntil green — mirroring how Trellis lowering works pre-daemon. Atrp lowerAPI wrapper was rejected for v1 (it would own prompting, keys, and retry policy before the workflow is understood); it may return as a later milestone. - Style judge: the human, in v1. The mechanical checks gate; setting
accepteddoubles as the style verdict, recorded in the lock asjudge: human. Agent judging (in-session or tool-invoked) is deferred until the workflow has been exercised. Recorded indocs/trellis-prose.md§5.1.
Goal
A Rust crate trp providing the trellis-prose oracle: parse .trp files,
mechanically verify a candidate lowering against its plan (the strict 1:1
mapping, §4 of the design), compute the three-part hashes, and read, write,
and report on .lock sidecars. With trp green and a human accept, a
document has the full trellis-prose discipline: hashed spec, verified
structure, tracked freshness.
Scope
- Spec finalization gate. Before code: resolve design §8.3
(segmentation — proposal below) and §8.6 (the provisional container,
frontmatter, heading-level, and lock decisions) with the user, and
freeze the lock’s v1 field set. Outcomes are recorded in
docs/trellis-prose.mdin place, andexamples/prose/is updated to conform before the parser is written against it. - Crate and CLI skeleton.
rust/trpjoins the workspace. Binary with three subcommands:trp check <name>.trp— parse and validate the spec; if the sibling<name>.mdexists, verify the lowering against the plan;--write-lockupdates the lock on green (with the lowering provenance passed by flag).trp status [dir]— freshness table across documents:fresh,stale(plan hash mismatch),review-suggested(notes or style hash mismatch),unlowered,unlocked.trp accept <name>— the human-only action: setsaccepted, records the style verdict asjudge: human. Refuses if checks are not green or the lock is stale.
- Parser. Frontmatter (
name,style; unknown keys are errors), the single requiredplanfence, directive lines against the closed move set, with positioned diagnostics (file, line, what was expected). Structural rules from design §3.2: prose directive outside an open paragraph is an error;headingcloses the open paragraph. - Segmenter and structural verifier. Split the output
.mdinto headings and paragraphs; segment paragraphs into sentences; verify the four mechanical checks of design §4 (known directives, verbatim headings in order and level, paragraph count, per-paragraph sentence counts). Diagnostics name the first offending directive/sentence pair. - Hashing, locks, freshness. Compute
plan/notes/stylehashes and the output hash; serialize and parse the lock; implement the freshness states and thereview-suggestedsemantics for notes and style edits. - Fixtures.
examples/prose/is the normative positive fixture; add a negative corpus underrust/trp/tests/fixtures/(unknown move, sentence outside a paragraph, count mismatch, reordered/reworded heading, ambiguous segmentation, twoplanblocks, unknown frontmatter key). - Workflow validation. Lower at least two real documents end-to-end in
a Claude Code session using
trp checkas the oracle, throughaccept. Friction observed here (especially re-lowering churn, design §8.5) is recorded back into the design’s open questions, not fixed ad hoc.
Segmentation proposal (decision point 2)
Resolve design §8.3 as: sentence terminators are ., !, ?; a sentence
ends at the first terminator; terminators may not appear
sentence-internally in v1 output. No abbreviations (“e.g.”, “Dr.”), no
decimal numerals, no ellipses. Segmentation becomes trivial and exact — the
checker needs no heuristics — and the burden falls on the agent’s wording,
which is the tenet working as intended (“verbosity for the agent is
acceptable”). A lowering that needs an internal period must re-word.
Non-goals
trp lower (API-driven lowering), the agent style judge, a mechanical
style floor (design §8.2), richer output than headings and paragraphs
(§8.4), wording-stability across re-lowerings (§8.5), multi-document
projects (§8.7). Each stays in the design’s open questions until the
workflow validation produces evidence.
Testing
- Rust unit tests per module; golden-file tests for diagnostics (match
soil0’s conventions). trp checkruns green overexamples/prose/inrust/check.sh; the negative corpus asserts each documented diagnostic.- Lock round-trip: parse → serialize is byte-identical for the example
lock; freshness states are exercised by mutating fixture copies (edit
plan →
stale; edit notes →review-suggested; edit style →review-suggestedfor every referencing document).
Exit criteria
trp check examples/prose/small_languages.trpis green, and every negative fixture produces its documented diagnostic.trp statusandtrp acceptdemonstrate all freshness states and the accept gate on the fixtures.- Two new real documents lowered in-session to green, accepted, with locks committed.
- Design §8.3 and §8.6 are resolved in place in
docs/trellis-prose.md,examples/prose/conforms, and this plan is updated with the decision outcomes. rust/check.shcoverstrp.
Decision points (for the user, at milestone start)
- §8.6 confirmations: the CommonMark container with a single
planfence, requiredstylefrontmatter, explicit heading levels, lock field shapes. - Segmentation: the no-internal-terminators rule proposed above.
- Hash canonicalization: hash raw bytes of each region, or newline-normalize first (affects cross-platform lock stability).
- Example hash policy: repo convention is abbreviated fake hashes in
examples, but
trp checkin CI cannot verify those. Options: a--no-hash-verifymode for the docs examples; or real (still abbreviated?) hashes inexamples/prose/regenerated by CI. - CLI contract: whether to freeze a
docs/contracts/trp-cli.md(as soil0 did) before agents start relying on the interface, or let the CLI settle through the workflow validation first.