Skip to content

/mold

When to invoke: Turn a high-leverage design decision into an approved spec through grounded dialogue. Use it when a leverage trigger fires (auth, irreversible effects, concurrency, contracts, destructive ops, a new slice, a cross-slice dependency, an invariant gap) or when the user asks for a spec or design. Typical phrases include “let’s design X”, “shape this into a spec”, “what should the API for Z look like”, and “/mold”. A feature ask with zero fired triggers is a /cook mini-spec, not a mold; route it through /cheese. Do NOT use it for free-form discussion without artifact intent (/culture), direct implementation (/cook), or research-only questions (/briesearch). Ceremony scales to the ask through references/tiers.md, so a small clear change gets a one-confirm mini-spec rather than the full dialogue.


Ceremony scales to the job. The Bounds pass picks one of three tiers from references/tiers.md: Quick writes a one-confirm mini-spec, Light runs only the dialogue modes the open forks need, and Full runs the whole Flow below. /cheese’s tier-1 escalation enters mini-spec mode with no confirm; see ## Agent-invoked mini-spec mode.

  1. Bounds pass — open the Goal / Decided / Asking / [AGENT-DECIDED] ledger with the goal pinned as one sentence; only an explicit user fork changes it. Split it into G-n clauses (references/handshake.md § Goal coverage). Map every input’s goals and non-goals as one [AGENT-DECIDED] line; ask the user only when the goal is genuinely unknown or a leverage trigger fires. Run the shape check, then announce the tier with its reason (references/tiers.md). Quick exits here: one fast confirm, then ## Agent-invoked mini-spec mode. Upgrade the tier whenever the evidence changes; never downgrade silently.
  2. Route — choose the secondary mode from references/modes.md, announce it, and correct false premises first.
  3. Dialogue — consequential forks are the user’s to pick. A fork is consequential per the leverage line in ../age/references/voice.md. Every other fork is [AGENT-DECIDED]. Supply options, trade-offs, and evidence before you ask. Ground each critical claim through code, the Validate Cycle, or a Prototype Cycle. Resolve every contradiction. Render the decision map after three consecutive fork questions, or on request.
  4. Sketch — For work across modules or with a new public interface, run references/shape-check.md. Bind identity and role nouns to code referents. Record the Placement block; no bodies.
  5. Plan for approval — run the fresh-context fork-coherence taste test with python3 skills/mold/scripts/mold.pyz taste-test and persist its digest-bound pass; a failure reopens only named forks, and the third failed verdict stops. Light with one expected curd stops here: no planner; finalize the bounded spec with python3 skills/mold/scripts/mold.pyz finalize and retain its canonical HandoffPointer. Otherwise dispatch a typed PlannerRequest, validate its PlannerResultWriterView (one retry, then stop before the handshake), normalize on the host, and persist only the typed PlannerResult and CurdPlan. Present the plan’s semantic curds and waves at the handshake. See references/curdle.md § “Pre-approval typed planner dispatch”.
  6. Two-key handshake — Before extraction, the user and agent must agree to the draft spec and displayed typed plan. The user provides an explicit verb. The agent performs a coherence self-check. Neither key changes nor disappears. See references/handshake.md.
  7. Curdle — Resolve the durable spec path with SPEC=$(python3 skills/mold/scripts/mold.pyz artifact-path specs <slug>). Phase one writes the local artifact and write-ahead prepared state before any external call. It writes the approved spec at "$SPEC". It also writes the host-validated PlannerResult and CurdPlan. It also writes local issue drafts and the session’s non-obvious decisions as durable ADRs. Phase two publishes approved follow-ups. Retain the prepared recovery state when an external capability is unavailable or publication fails. Phase two reconciles their state and references into the durable spec before any handoff. The resolved spec path is internal; Cook receives only the finalizer’s canonical HandoffPointer.
  8. Finalize and hand off — after reconciliation, run python3 skills/mold/scripts/mold.pyz curd-count, then finalize through ## Handoff. Full and Light both hand that pointer to Cook. Dispatch only the user’s non-stop selection.

Portability: rules. Slash commands are host renderings, not the control model.

Every non-goal and explicit dialogue deferral becomes a [FOLLOW-UP?] follow-up candidate. Dispose of the set before the two-key handshake; details: references/handshake.md § Follow-up disposition.

Mode Use when Goal
Explore The idea is vague Identify the real problem and pain point
Ground A file, bug, or existing doc is named Verify facts against evidence
Shape The goal is known but approach is open Compare viable options (Do Nothing always included)
Sketch Interfaces or module boundaries matter Lock responsibilities and seams
Grill A favoured approach needs stress-testing Steelman each item, then put every design-changing call to the user as a fork
Diagnose A symptom, failure, or trace is supplied Build a Loop → reproduce → hypothesize → confirm root cause

Modes: references/modes.md. Evals: references/evals.md. Canvas: review-canvas.md.

/cheese’s tier-1 escalation calls /mold after the call site passes all cook fast-path checks, and the Quick tier enters the same mode after its one confirm. It produces a spec without design dialogue. This mode skips the rest of the Flow above. Derive a slug. Write the mini-spec. Parse its declared gate applicability. Finalize through ## Handoff and return its canonical HandoffPointer to Cook. Append --hard when the user passed it.

The two-key handshake does not run in this mode. The agent-introduced-scope check still runs implicitly. Every distinguishing noun must come from the user’s input or tier-2 /culture//briesearch synthesis. Never add one silently.

Full procedure, the mini-spec schema, and the ## Provenance rules: references/mini-spec-mode.md.

Call source-code search, read, and edit backends according to code-intelligence-routing.md. Shape checks use semantic caller search plus dependency context; procedure: references/shape-check.md.

Mold-specific tools beyond source-code routing:

Need Prefer Fallback
External validation /briesearch with Context7/Tavily user docs, repo docs, or note as unverified
Wiki grounding (Ground entry + decision points; scope per references/grounding.md § When to probe) mcp__hallouminate__list_corpora + mcp__hallouminate__ground on repo:<repo>:wiki record hallouminate: absent in the ledger, proceed on code evidence, and cap at speculating when design rationale is central

The grounding record precedes the first structured question: the ledger holds a probe result — citations or hallouminate: absent — first. Mark each unsupported claim [?] until settled.

/mold owns the dialogue, contradictions, and approval state. Do not delegate these items. Delegate evidence-heavy code work to a fresh-context explorer and external research to a researcher. Shape uses an explorer digest as input. Record parent-context exploration as a degraded path. See references/context-budget.md for budgets and checkpoints.

python3 skills/mold/scripts/mold.pyz gate-graph --render dot|svg|png|mermaid renders one gate model. Image targets use Mermaid when Graphviz is unavailable. Tests keep gate nodes aligned with the handshake checklist. See references/gate-graph.md.

Every Mold-produced spec carries a provenance marker in frontmatter:

source: mold-handshake | agent-mini-spec

Every spec declares gate_applicability:

gate_applicability:
disposition: red-required | not-applicable
work_class: behavior | docs-only | refactor-only | test-only | appearance-only
ui_surface: browser | non-browser | not-applicable

ui_surface is required on the Mold production path: browser means every Test Contract names an existing browser/E2E interface and outer seam, non-browser is explicit and never inferred from prose, and not-applicable is required for closed non-behavior classes including appearance-only. red-required requires behavior plus a complete ## Test Contracts table with one executable red row; not-applicable requires a closed class, a reason, and no contracts. Mold never infers applicability. Row-level rules: references/curdle.md § Test Contracts.

python3 skills/mold/scripts/mold.pyz taste-test binds the verdict to draft SHA256 and each settled fork. Stale, partial, or blocked verdicts fail; a failure reopens only named forks, with two rounds. Approved red-required specs pass unchanged metadata and the published pointer to /cook --auto.

Each fork appears in Approach, Interface sketches, Acceptance, plus Test Contracts for red-required; none in not-applicable specs. Do not rename sections. Tag reflecting lines with fork id and run taste-test --precheck before dispatch. goal must survive verbatim in Problem statement (goal-drift); each G-n clause carries an Acceptance or disposition tag (goal-coverage). See references/curdle.md § Spec template and references/gate-graph.md.

Curdle requires the two-key handshake. It requires an explicit user verb: curdle or ship it. It also requires the agent’s coherence self-check. Present the validated typed CurdPlan’s N curds / M waves with the final approval request in Flow step 5; on Light’s single-curd path there is no plan, so present the spec alone and mark the plan boxes n/a. See references/handshake.md for the checklist, mandatory gates, and override semantics.

Before the handshake, print the narrowing delta (taste-test --coverage), then present the scope audit table once. One confirm approves the defaults; only leverage rows and unresolved bindings need a verb, and curdle anyway waives neither. See references/handshake.md.

If any gate is unmet, propose the smallest next question, evidence check, or planner correction. Do the same if the typed plan remains invalid after one retry. Write artifacts only after both keys pass.

Mold never runs the metacognitive check. Plate alone runs it at the verified-artifacts boundary. Mold appends --hard to every Cook command that it emits when the user passed the flag. Cure forwards the same flag to Plate. See ../hard-cheese/references/composition.md.

Pipeline: culture → [mold] → cook → press → age → cure → plate

After Curdle’s phase two finishes, run python3 skills/mold/scripts/mold.pyz curd-count for the blast-radius digest. Finalize with python3 skills/mold/scripts/mold.pyz finalize and keep its consumer-valid canonical HandoffPointer path; finalize does not require curd-count first. Prompt through the shared handoff gate (policy). Approved red-required behavior recommends handing the canonical HandoffPointer to /cook --auto. Keep the applicability, contract, and taste metadata unchanged. Append --hard when the user passed it. Never pre-select.

The digest’s mode is orientation, not a skill. Render the fixed blast-radius menu from decomposable, candidate_curds, verdict, and mode; see references/handoff-menus.md.

  • Dialogue first; artifacts are the by-product.
  • Tiered lettered options. Consequential forks use A/B/C/D choices via the question transport at ../cheese/references/ask-user-question.md. Never decide them silently. Everything below the leverage line is [AGENT-DECIDED] by default: make the call, log a one-line vetoable alternative in the ledger, and do not ask. A fork is valid only after its depth was contributed in-dialogue first. Precede every structured question with visible prose that weighs the fork and the evidence. Keep one open picker.
  • Altitude tag. Every Asking fork names the acceptance criterion, public seam, or non-goal it moves. A fork that moves none, or that sits below the leverage line, is [AGENT-DECIDED] or a follow-up candidate, never a user question.
  • Decision ledger. Each round prints Goal / Decided / Asking / [AGENT-DECIDED], the goal verbatim. Curdle persists consequential decisions to ADRs and minor ones to the spec. The taste verdict names every settled consequential entry exactly once.
  • Decision map and fork-round cap. Stop after three consecutive fork rounds that add no new evidence, or three consecutive forks that fail the altitude tag, or when the user requests a decision map. Show completed forks, remaining forks, and a ready or blocked verdict. A fourth round requires new grounding, a delegated digest, or a /wheypoint checkpoint. The map shows ledger state. It does not create an artifact.
  • Do not implement code.
  • Do not write production files before the approval gate.
  • Do not silently settle uncertain claims.
  • Apply the shared voice kernel at ../age/references/voice.md. Correct false premises. Mark each critical claim’s confidence as certain | speculating | don't know. Steelman before you dismiss. Put consequential forks to the user; depth informs the question, never replaces it.

The schema entanglement behind curdle’s spec-template and cook’s writer views is phase registry × schema catalog × models per transition. The generated ../cheese/references/schema-intertwine.md documents it.

Resolve delegates through ../cheese/references/agent-resolution.md.

Work Preferred types Permissions/isolation Minimum power Effort Fallback
Explore the codebase explorer read-only, fresh-context default medium compatible explorer, then general
Research external constraints researcher read-only, fresh-context default medium compatible researcher, then general
Plan for approval planner, general read-only, fresh-context powerful high compatible planner, then general

The canonical mold spec or mini-spec carries the shared agent_resolution block. Commands: references/commands.md.

Mold records the non-obvious decisions of a session as Architecture Decision Records (ADRs). The approved spec and its ADRs are durable project records: the spec keeps the implementation contract, while ADRs preserve the rationale a future session would otherwise re-derive. Write them at Curdle, after the two-key handshake, in phase one’s local atomic write.

One ADR per decision a future reader would otherwise have to reverse-engineer:

  • a chosen approach over a real alternative (the trade-off, not the obvious pick);
  • a constraint discovered mid-dialogue (a Prototype Cycle answer that changed the design; a [CONFLICT] the codebase forced);
  • anything tagged a Decision in the spec that has a substantive rationale.

Do not write an ADR for a forced move with no alternative, or for a detail the spec body already states plainly.

Mold’s per-round decision ledger (Decided / Asking / [AGENT-DECIDED], see ../SKILL.md § Rules) persists here at curdle: each consequential decision (per the leverage line in ../../age/references/voice.md) earns an ADR — it is exactly the kind of decision a future reader would otherwise reverse-engineer. [AGENT-DECIDED] calls ride the spec’s one-line decision-log (curdle.md § Spec template) with their vetoable alternative, never a full ADR; the ledger keeps no separate file (ADR-004).

Resolution — where ADRs land (portable, never hardcoded)

Section titled “Resolution — where ADRs land (portable, never hardcoded)”

Mold runs in arbitrary repos. The corpus is resolved dynamically at curdle, never hardcoded — there is no easy-cheese:wiki baked into a runtime path.

adr_target():
# 1. Probe for a hallouminate wiki at the CONSUMER's root repo.
# Shape-match the corpus, never exact-match a placeholder — the repo name is
# dynamic (the bundle command uses the same shape-match).
corpus = first(c for c in hallouminate.list_corpora()
if c.startswith("repo:") and c.endswith(":wiki")) # dynamic; their repo, not ours
if corpus:
return ("hallouminate", corpus) # searchable, cross-session
# 2. Fall back to a tracked file path everywhere else.
return ("file", "docs/adr/<slug>-NNN.md") # tracked in the consumer's repo
  • hallouminate present: write each ADR into the consumer’s repo:<their-repo>:wiki corpus via add_markdown. The corpus is shape-matched from list_corpora, never a literal — that is the portability invariant.
  • hallouminate absent: write docs/adr/<slug>-NNN.md (tracked), emit a loud one-line note that the ADR went to a file rather than the wiki (never a silent degrade — see curdle.md § Atomic write), and recommend installing hallouminate so future rationale becomes searchable.
  • probe shape varies by harness [?]: if list_corpora is unreachable, fall back to the tracked file path and say so — never block curdle on the probe.

The spec path follows the separate artifact resolver contract and remains in its durable project corpus; ADR target resolution does not make the approved spec transient.

Mirror the spec’s own ## ADRs section shape (this very spec uses it):

### ADR-NNN: <one-line decision title> [status: accepted]
- **Context:** <what made this a real decision; the forces in play>
- **Decision:** <what we chose>
- **Alternatives:** <the rejected options and why>
- **Consequences:** <what this buys and what it costs later>

Numbering is per-slug (<slug>-001, -002, …) on the file fallback; in the wiki, prefix the page slug the same way so the series stays grouped.

ADRs are a phase-one Curdle by-product. Write them after both handshake keys pass, in the same local atomic step as the durable spec and before any external follow-up publication. They never substitute for the spec’s own ## Decisions line: the ADR is the long form; the Decisions bullet is the index entry.

scripts/render_generated_regions.py generates this file from the static COMMANDS manifest in src/easy_cheese/skills/mold/commands.py. Do not edit this file. Run each command as python3 skills/mold/scripts/mold.pyz <command> [args...]. Each command returns an integer exit status. Pass --help to a command for its arguments and output format. Keep worked examples in the skill instructions.

Command Purpose Subcommands
approve Record the user’s literal approval response as a retained MoldCookApproval
artifact-path Resolve the durable or transient artifact path for a phase and slug
curd-count Count candidate curds in a spec and recommend the next skill
domain-model-target Resolve the domain-model store from explicit Hallouminate probe results
finalize Finalize a Mold spec and publish only a consumer-valid handoff
gate-graph Render the gate state machine as dot, svg, png, or mermaid
normalize-planner Materialize a planner writer envelope into a canonical PlannerResult
render-html Render a markdown report into one self-contained offline HTML file
review Serve and manage the local Mold review canvas
taste-test Run the applicability, contract, and fork-coherence taste gate; –precheck runs the lexical pre-check on the draft without a verdict
validate-spec Check a spec against the current Mold specification requirements

A long Mold dialogue can reduce model recall and coherence. This risk increases near 120,000 to 140,000 tokens. Mold controls this risk in three ways. It delegates heavy work, limits active orchestration, and recommends a checkpoint when the context grows. Delegation is the most reliable control. See ADR-003.

Use the sub-agent context gate for heavy work. Resolve the read-only role through ../../cheese/references/agent-resolution.md. Select the exact specialist first. Then select a compatible specialist. Use a constrained general worker with degraded: true only when no specialist is available. Work in the parent context only when dispatch is unavailable.

  • Research: Delegate deep /briesearch work to a researcher. Deep work uses at least three document fetches or two search angles.
  • Shape check: Delegate wide analysis to an explorer. Wide analysis includes more than five symbols or a large caller graph.
  • Prototype Cycle: Always run the temporary build in an explorer. See prototype-cycle.md.
  • Diagnose: Delegate large logs and traces to an explorer. Keep only the concise root-cause hypothesis in the parent context.

The sub-agent returns a digest of 2 KB or less. Do not copy raw evidence into the parent context. The parent keeps the dialogue, contradictions, approval state, and two-key handshake. Never delegate these items.

Token pressure gives a late warning. Active orchestration consumes the context before this warning occurs. One Mold episode can include bounds, grounding, research, Shape, taste tests, planning, approval, and publication.

The 2026-08 workflow sample contains 16 top-level invocations. The median active time is 35.6 minutes. Large sessions use 100 to 276 tool calls and up to 11 sub-agent spawns.

Each phase has a bound and an exhaustion action. When a phase reaches its bound, stop new work. Record the settled work and take the checkpoint.

Budget Bound On exhaustion
Ground per topic 1 wiki probe and 1 delegated digest Mark the remainder [?]. Treat a third probe as repeated work.
Shape or Sketch per option set 1 explorer digest require a new question before a second dispatch
Fork rounds 3 consecutive rounds that add no new evidence show the decision map and stop questions
Fork altitude 3 consecutive forks that move no acceptance criterion, seam, or non-goal show the decision map; demote the pending fork to [AGENT-DECIDED] or a follow-up candidate
Parent-context tool calls approximately 40 calls run /wheypoint before the next heavy step
Sub-agent spawns 6 spawns per episode Run /wheypoint. Resume from the digests.
Repeated failures 2 consecutive failures from the same tool or agent record the degraded path and use a different route

Count only calls and spawns from the parent context. Do not count internal sub-agent tool calls. Delegation exists to move those calls out of the parent context.

These budgets are not terminal limits. Exhaustion requires a checkpoint or a recorded degraded path. It does not permit a truncated design. Do not make a silent third attempt.

No precise live token count is available. Use the visible signals to estimate context use.

Estimate Action
approximately 120,000 tokens Advise the user that the context is filling. Delegate the next heavy step. Keep each question concise.
approximately 140,000 tokens Recommend /wheypoint. Resume in a fresh context.

Estimate from the turn count, sub-agent digests, and large pasted inputs. Do not report false precision. Delegate before you recommend a checkpoint. Delegation directly reduces parent-context use.

At approximately 140,000 tokens, recommend this action. Do not run it automatically.

The dialogue can now reduce model recall. Run /wheypoint to save the settled decisions in .cheese/notes/<slug>.md.
Resume in a fresh session with /cheese --continue <slug>.

/wheypoint preserves the dialogue, contradictions, approval state, and open Validate or Prototype cycles. The fresh agent resumes at the current handshake state. It does not derive the design again. See skills/wheypoint/SKILL.md.

A hard token gate can stop confidence work too early. ADR-003 rejects this behavior for cycle caps. Context pressure is approximate. Therefore, Mold recommends delegation or a checkpoint instead of ending the dialogue.

Runs after Curdle writes the spec, before finalization. Pushes the parse-and-count work into a Python script so the sizing signal is deterministic and stays out of the conversation’s token budget.

Curd-count reports candidate sizing and the recommended downstream skill. The digest includes goal, quality-gate, and decision signals; landing metadata; and an advisory Cook wave-plan mode. It has no handoff field and does not construct an execution command. Finalization alone evaluates all required evidence and publishes a canonical Cook handoff when the result is ready.

The recommendation names the skill only. The count is advisory sizing: it never authorizes Cook, bypasses finalization, or turns a saved non-ready result into a runnable handoff.

A decomposition of PARALLEL_THRESHOLD (2) or more curds signals a parallel Cook wave-plan; below that, high blast radius signals a linear chain. The decomposer stays authoritative—the count is a pre-dispatch hint, not the mode gate. /ultracook is retired as a top-level skill choice.

After curdle.md writes the spec to disk, run the script and read the JSON digest into context:

Terminal window
SPEC=$(python3 skills/mold/scripts/mold.pyz artifact-path specs <slug>)
python3 skills/mold/scripts/mold.pyz curd-count "$SPEC" \
--blast-radius <low|medium|high>

Pass the --blast-radius value verbatim from the shape-check verdict line (see shape-check.md). If shape-check was skipped or its verdict was [?], omit the flag; the route stays /cook, while sub-threshold specs receive no Cook mode hint.

Signal Source in the spec
goals Bullets under ## Goals
quality_gates Bullets under ## Quality gates (also matches ## Acceptance criteria for legacy specs) — reported, not counted
decisions Bullets under ## Decisions (reported but not used in the rule)

candidate_curds = goals — only distinct behavioural goals drive the count. quality_gates (acceptance criteria) and decisions are reported as signals but deliberately excluded from the count: they are facets of one coherent change, not independent file-disjoint curds. Counting acceptance criteria as curds inflated the recommendation toward parallel fan-out for single coherent refactors whose own criteria reference the same files (issue #111) — the more thoroughly a spec was written, the more likely it mis-recommended fan-out.

recommended_skill is always /cook; it is an advisory destination, not an execution decision. Curd-count emits no handoff or command. The independent Cook mode signal follows the curd count and blast radius:

candidate_curds blast_radius mode
≥ 2 (PARALLEL_THRESHOLD) any parallel
< 2 high linear
< 2 medium, low, or unknown null
{
"spec_path": "<resolver-owned durable spec path for <slug>>",
"slug": "<slug>",
"blast_radius": "high",
"candidate_curds": 7,
"signals": {"goals": 7, "quality_gates": 6, "decisions": 3},
"threshold": 2,
"decomposable": true,
"recommended_skill": "/cook",
"mode": "parallel",
"rationale": "7 candidate curds >= 2 threshold; parallel fan-out (advisory)"
}

The script counts; it cannot verify that the candidate curds are file-disjoint (criterion 4) from spec text alone. Before a parallel wave-plan runs, mold confirms independence with the user — typically by naming each curd’s scope from the typed CurdPlan (the planner derives file footprints from the Placement block’s slice: and public interface: lines plus the shape-check importer list; ## Interface sketches itself carries no file paths) and asking whether any two candidate curds touch the same file. If they do, the decomposer folds the shared-file curds back into the linear chain; the dispatched skill is /cook either way.

The script depends only on the Python 3 stdlib. If the host has no python3, Mold may report the sizing signal manually: count the behavioural goals and apply the blast-radius mode table. Finalization still owns readiness and publishes any Cook handoff; do not substitute an execution command.

Curdle is the terminal state of mold. It runs only after the two-key handshake (see handshake.md).

Resolve the spec path with SPEC=$(python3 skills/mold/scripts/mold.pyz artifact-path specs <slug>) — it anchors at the per-project durable corpus (see ../../cheese/references/formatting.md § Corpus location). Issues stay repo-local: write them as .cheese/issues/<slug>-NNN.md.

Type When Path
Spec Any meaningful design discussion $SPEC (resolver output)
Spec + Issues Accepted follow-ups whose disposition calls for local recovery or tracker payload spec at $SPEC; issues at .cheese/issues/<slug>-001.md, -002.md, …
Issues only Pure standalone bug tickets, no design .cheese/issues/<slug>-001.md, …

A spec is the rich container. It absorbs problem framing, requirements, approach, decisions, interface sketches, risks, and gates. An issue is a separate GitHub-flavoured item. The user can paste it into a tracker.

  • Lowercase the working problem statement, drop stopwords, kebab-case, cap at 5 words.
  • Validate every slug before any Curdle write. Run python3 skills/mold/scripts/mold.pyz artifact-path specs <slug>. The command applies validate_slug and returns a nonzero status for an invalid slug. Stop on a nonzero status.
  • Accept a user-passed slug only after that command returns status zero. A slug can otherwise contain .. or / and write outside .cheese.
  • Reuse the validated slug for every repo-local path. Repo-local paths include .cheese/issues/, .cheese/glossary/, and .cheese/.out-of-scope/. Never interpolate a raw slug into a path.
  • Match the spec’s parent slug for issues (<slug>-001.md, -002.md).
Existing Action
Same slug, status draft Overwrite (default) or rev (<slug>-v2) — ask if unsure
Same slug, status approved Default to rev; never silently overwrite
Existing spec, new issues for same slug Append issues to that slug’s series

Cross-cutting house style and citation form: formatting.md. This section owns the spec shape; formatting.md owns the voice rules and the footnote primitive.

landing.layers records ordered groups of canonical curd ids from the approved CurdPlan, as a one-line flow list. Leave it [] only when shape is single; a non-single shape requires at least one layer.

Fork-id tags. Every settled consequential fork in the decision ledger must appear in Approach, Interface sketches, and Acceptance, plus Test Contracts for red-required. Each line that reflects a fork carries the fork id in parentheses, for example - AC-3: WHEN ... THE SYSTEM SHALL ... (F-3) or public interface: parse(...) -> Result (F-2). The taste test matches the fork id literally, or every 3+ letter word of the decision text; the tag is the reliable form.

Goal-clause tags. Every G-n clause from the ledger’s goal_clauses carries its tag on at least one Acceptance line, or on exactly one disposition line: a Non-goals bullet, a Deferred follow-ups entry, or a [TBD] item under Open questions. The pre-check fails goal-coverage:G-n for an untagged clause and goal-coverage-cap:<covered>/<total> when fewer than half the clauses reach Acceptance. See handshake.md § Goal coverage. Run python3 skills/mold/scripts/mold.pyz taste-test --precheck --draft <draft> --ledger <ledger> before the reviewer dispatch; fix every reported gap first.

---
slug: <slug>
status: draft
source: mold-handshake
created: <YYYY-MM-DD>
confidence: <low | medium | high>
leverage: [] # fired trigger ids per `../../cheese/references/routing-policy.md` § Leverage triggers; copied from the handoff packet, extended when a later mode fires one
gates_overridden: [] # list of unchecked handshake items if `curdle anyway` was used
agent_introduced_scope: [] # terms in the spec the user did not type — approved through the scope audit table per `handshake.md` § Scope audit table (audit trail; downstream skills trust this list)
goal_coverage: {} # G-n -> covered | non-goal | follow-up | tbd, the final disposition map printed as the narrowing delta per `handshake.md` § Goal coverage (audit trail; downstream skills trust this map)
entity_referent_bindings: [] # list of binding records {noun, verdict, referent, citation, note} for identity/ownership-role nouns bound to code referents or marked NEW ENTITY — each resolved per `handshake.md` § Entity-referent binding (audit trail; downstream skills trust this list)
agent_resolution: [] # the shared agent-resolution block per `../../cheese/references/agent-resolution.md`
gate_applicability:
disposition: red-required | not-applicable
work_class: behavior | docs-only | refactor-only | test-only | appearance-only
ui_surface: browser | non-browser | not-applicable
reason: <required only for not-applicable>
landing:
shape: single | orthogonal_flat | stacked_linear | diamond_stack
layers: []
per_layer_green: required | tip-only
review_fixes: fold | top-up
# <Title>
## Problem
<one paragraph; what's broken or missing today, who feels it>
## Goals
- G-1: <one outcome clause of the pinned goal, verbatim from the ledger>
- G-2: <...>
## Non-goals
- <bullet> (G-n when this bullet defers a goal clause)
## Deferred follow-ups
- **<deterministic follow-up ID>** — <summary>
- Destination: <github_issue | roadmap_goal | local_draft>
- State: <prepared | linked | created>
- Reference: <local draft path | URL | durable roadmap reference>
## Grounding
Add exactly one row for each probe. Use `hit`, `miss`, or `unavailable` for `Outcome`.
Use `unavailable` when a probe cannot run. Record the attempted action in `Evidence`.
Do not leave `Evidence` blank. You can skip a probe, but do not assume its result.
| Probe | Outcome | Evidence |
| --- | --- | --- |
| wiki | <hit \| miss \| unavailable> | <wiki path and one-line finding, or what was attempted> |
| explorer | <hit \| miss \| unavailable> | <explorer digest path and one-line finding, or what was attempted> |
## Approach
<chosen option summary>
## Decisions
- <one-line decision> — <one-line rationale>
- _Minor decisions:_ <one line capturing the `[AGENT-DECIDED]` calls the user did not veto — the per-round ledger's minor tier; major decisions get full ADRs per `adr.md`>
## Acceptance
Write acceptance criteria in **EARS form** by default:

WHEN THE SYSTEM SHALL

If the trigger cannot be stated precisely (e.g. pure internal utilities with no external event), use prose with a `[prose-fallback]` marker.
- AC-1: WHEN <trigger> THE SYSTEM SHALL <response> (<fork-id>, <G-n>)
- AC-2: WHEN <trigger> THE SYSTEM SHALL <response> (<fork-id>, <G-n>)
## Test Contracts
Include this entire section only when `gate_applicability.disposition` is
`red-required`. Every numbered Acceptance ID appears exactly once in this table.
`expected_failure` names a deterministic witness and expected red assertion.
`mode` is `tracer`, `contract-matrix`, or `guard`.
The seam is the outer boundary that proves the behavior.
A matrix names its ratified interface version and each unique row identity.
Separate row identities with `<br>`.
A guard or tracer leaves those cells blank.
Add one row for each criterion.
| Acceptance ID | Interface referent | Outermost stable seam | Expected failure | Mode | Interface version | Matrix rows |
| --- | --- | --- | --- | --- | --- | --- |
| AC-1 | <public interface> | <existing outer seam> | <witness and expected red assertion> | tracer | | |
| AC-2 | <public interface> | <existing outer seam> | <witness and expected red assertion> | contract-matrix | <ratified version> | <row 1><br><row 2> |
## Interface sketches
The Placement block from Sketch (`modes.md` § Sketch). Architecture at 10,000 ft, not bodies.
```text
slice: <owning slice | NEW SLICE>
spine step: <entry | workflow | domain | infra | none>
public interface: <one signature per new or changed crust export> (<fork-id>)
private: <responsibilities that stay behind the crust>
crust delta: <new exports | cross-slice imports | contract changes | none>
arrows: <dependency directions added, or none>
  • [TBD]
  • [BLOCKED]
  • :

<failing test, curl, replay command, etc.>

<one footnote definition per cited source; include only when out-of-scope evidence was cited above per ../../cheese/references/formatting.md § Citations>

`source: mold-handshake` marks the strict Mold production path. Every new
behavior declaration must set `ui_surface` to `browser` or `non-browser`;
closed non-behavior declarations, including `appearance-only`, set it to
`not-applicable`. A browser declaration is valid only when every Test Contract
names an existing browser/E2E interface and outer seam.
Specs without this provenance marker are v0.13-era legacy and stay
**readable forever**: `validate-spec` accepts them, waiving only the two parts
the hardened format added after v0.13 — the `Test Contracts` section and the
`gate_applicability` block — and printing a one-line `NOTICE:` rather than an
error. Everything a v0.13 spec did carry is still validated. Minting is the
mirror image: Curdle writes only the template above and gates it with
`validate-spec --strict`, which enforces the hardened format *and* the
`source:` marker unconditionally. Legacy is a read-side grace, never a
write-side option. The policy lives once in
`src/easy_cheese_schemas/spec_format.py` so every release channel inherits it.
#### Current mold-spec schema (generated)
The `mold-spec` document contract enforced by `validate-spec` (`shape-check.md`), refreshed by `scripts/render_generated_regions.py` from the `MoldSpecDocument` model in `src/easy_cheese_schemas/contracts.py`. Do not hand-edit the generated region.
<!-- BEGIN GENERATED: mold-spec-schema -->
document mold-spec {
section "Problem"
section "Goals"
section "Non-goals"
section "Deferred follow-ups"?
section "Grounding" {
columns: ['Probe', 'Outcome', 'Evidence']
per_row: ['Probe and Outcome are drawn from their closed sets', 'Evidence is non-empty, including for unavailable outcomes']
}
section "Approach"
section "Decisions"
section "Acceptance"
section "Test Contracts"? {
columns: ['Acceptance ID', 'Interface referent', 'Outermost stable seam', 'Expected failure', 'Mode', 'Interface version', 'Matrix rows']
per_row: ['tracer rows leave Interface version and Matrix rows blank', 'contract-matrix rows require both Interface version and Matrix rows']
}
section "Interface sketches"
section "Risks"
section "Open questions"
section "Quality gates"
section "Curds"
section "Reproduction"?
section "References"?
}
rule ac-coverage-exactly-once: "Every Acceptance ID must appear exactly once in the Test Contracts table."
rule tracer-row-blank-matrix-cells: "Tracer rows must leave Interface version and Matrix rows blank."
rule contract-matrix-row-requires-both: "Contract-matrix rows require both Interface version and Matrix rows."
rule grounding-probe-recorded: "The Grounding table must record the wiki probe exactly once with non-empty evidence."
rule delegation-digest-recorded: "The Grounding table must record the explorer probe exactly once with non-empty evidence."
rule not-applicable-closed-class: "red-required requires Test Contracts; not-applicable forbids them and requires a reason."
rule landing-closed-class: "landing fields take only their declared values; layers is empty when shape is single."
type GateApplicability {
disposition GateApplicabilityDisposition
work_class WorkClass
ui_surface UiSurface
reason? str | None = None
}
type GroundingRow {
probe GroundingProbe
outcome GroundingOutcome
evidence str
}
type Landing {
shape LandingShape
layers? tuple[tuple[str, ...], ...] = ()
per_layer_green? PerLayerGreen = required
review_fixes? ReviewFixes = fold
}
type MoldSpecDocument {
frontmatter MoldSpecFrontmatter
acceptance_ids? tuple[str, ...] = ()
test_contract_rows? tuple[TestContractRow, ...] = ()
grounding_rows? tuple[GroundingRow, ...] = ()
}
type MoldSpecFrontmatter {
slug str
status str
source str
created str
confidence SpecConfidence
gate_applicability GateApplicability
gates_overridden? tuple[str, ...] = ()
agent_introduced_scope? tuple[str, ...] = ()
entity_referent_bindings? tuple[Mapping[str, object], ...] = ()
landing? Landing | None = None
}
type TestContractRow {
acceptance_id str
interface_referent str
outermost_stable_seam str
expected_failure str
mode TestContractMode
interface_version? str = ''
matrix_rows? tuple[str, ...] = ()
}
enum GateApplicabilityDisposition = "red-required" | "not-applicable"
enum GroundingOutcome = "hit" | "miss" | "unavailable"
enum GroundingProbe = "wiki" | "explorer"
enum LandingShape = "single" | "orthogonal_flat" | "stacked_linear" | "diamond_stack"
enum PerLayerGreen = "required" | "tip-only"
enum ReviewFixes = "fold" | "top-up"
enum SpecConfidence = "low" | "medium" | "high"
enum TestContractMode = "tracer" | "contract-matrix" | "guard"
enum UiSurface = "browser" | "non-browser" | "not-applicable"
enum WorkClass = "behavior" | "docs-only" | "refactor-only" | "test-only" | "appearance-only"
<!-- END GENERATED -->
### Issue template
```markdown
---
slug: <slug>-<NNN>
status: open
flavor: bug | chore | slice
parent_spec: <slug>
---
# <One-line summary>
## Context
<why this exists, in 1–3 sentences>
## Acceptance
- AC-1: <bullet — verifiable outcome>
## Notes
- <optional caveat or pointer>

Accepted follow-ups use a local-first two-phase Curdle. Each receives a deterministic follow-up ID composed from the spec slug and its one-based ordinal, for example mold-follow-up-routing-F001.

Preserve every existing Curdle by-product: the spec, ADRs, glossary, domain model, and all rejected-direction records. Add a local issue draft for each accepted follow-up that needs a recoverable tracker payload. Before any external call, persist its ID, destination, prepared state, and draft reference in Deferred follow-ups.

$SPEC is the authoritative store for prepared follow-up state because the resolver anchors it in the durable project corpus. Local issue drafts are auxiliary publication payloads, not the authoritative record. Stage and move this complete local set under the existing atomic-write rule before phase two begins.

Phase two — external publication and reconciliation

Section titled “Phase two — external publication and reconciliation”

Only units whose approved action is create/link now and whose destination is external enter phase two. A local issue draft destination completes as prepared in phase one:

  • For GitHub Issues, use the host GitHub capability first and gh as the portable fallback. Discover repository labels and issue forms instead of assuming them.
  • For roadmap goals, run the owned /wiki-roadmap workflow when that skill and its required capability are available. New roadmap creation and extension remain owned by that workflow.
  • Put the deterministic follow-up ID in every published item. On every retry, search for the exact deterministic follow-up ID before creation. If an exact match exists, link it. You SHALL NOT create a duplicate.
  • A reused external item becomes linked; a newly published item becomes created. Reconcile that state and the final URL or durable roadmap reference into Deferred follow-ups.
  • When a capability is unavailable or publication fails, retain the recovery draft. Keep the follow-up prepared. Report the failed action and retry path. Continue without blocking the approved spec.

Finish roadmap publication and all mechanical spec reconciliation before the implementation handoff. Reconciliation records the already-approved result; it does not reopen the design.

Write the session’s non-obvious decisions as durable ADRs after both handshake keys pass. Include them with the durable spec in phase one’s local atomic write. Both stay in the durable project corpus. The spec is the approved implementation contract. The ADRs preserve its rationale. Resolve the corpus dynamically. Probe for the consumer’s repo:<their-repo>:wiki hallouminate corpus. Write there when it exists. Otherwise write a tracked docs/adr/<slug>-NNN.md. Never hardcode a corpus name. See adr.md for the full resolution rule and the ADR format.

Write resolved canonical terms to .cheese/glossary/<slug>.md in the same atomic step as the spec and ADRs. Downstream skills (/cook, /age, /press) read this file for naming consistency. The glossary is the output of the Ground phase’s term resolution; it is not reconstructed from the spec.

Format:

# Glossary — <slug>
| Term | Canonical meaning | Code referent (file:line or NEW ENTITY) | Avoid |
| --- | --- | --- | --- |
| <term> | <one-line definition> | <referent> | <losing synonym, …> |

The Avoid column records losing synonyms that the Ground phase rejected in favour of the canonical term. Separate multiple synonyms with commas. Use when none exist. Omit the file if Ground resolved no terms and no overloaded-term dialogue occurred.

During the same atomic step as the spec, ADRs, and per-slug glossary, merge the session’s resolved terms with their Avoid synonyms. Merge them into the project-level domain model. After the Hallouminate probe, resolve the target through the Mold bundle command:

python3 skills/mold/scripts/mold.pyz domain-model-target \
--probe <unavailable|no-match|match> \
[--corpus repo:<repo>:wiki --model <present|absent|unknown>]

Pass unavailable when Hallouminate is not loaded or the probe failed. Pass no-match when the listing completed but contained no repo:*:wiki corpus. Pass match with the exact corpus name and its model status when the listing found one. The command accepts only these explicit probe results; it does not import or invoke MCP machinery. It emits canonical JSON with backend, string location, and wiki_reachable. Preserve the resolver’s wiki, tracked docs/, then XDG read precedence and report a wiki_reachable: false fallback before writing a file target.

The per-slug glossary is a branch-local handoff. The domain model is cumulative cross-session memory. It builds the project’s ubiquitous language across every session. Add only context-specific terms. Never add general programming concepts.

Merge, don’t overwrite:

  • New term — append an entry.
  • Changed term (definition, referent, or Avoid set differs) — update that entry in place.

Entry format:

**<Term>**<definition>.
_Avoid_: <syn1>, <syn2>
_Code_: <file:line (or NEW ENTITY)>

Omit the _Avoid_ line when no synonyms were rejected.

Lazy context-map split. A single bounded context lives as one domain-model.md at the store root. When a second bounded context crystallises, split lazily into:

  • domain-model/index.md — the context map: the bounded contexts and their relationships (Pocock CONTEXT-MAP shape).
  • domain-model/<context>.md — one page per bounded context, each holding that context’s entries.

Do not pre-split for a single context. This layout is identical across all three stores selected by the bundle command (wiki, docs/, XDG corpus).

Write the rejection to .cheese/.out-of-scope/<slug>-NNN.md when the agent-introduced-scope audit rejects a direction. Do the same when the two-key handshake rejects it. Rejections include “drop ” for an approach or design knob, and “not that approach.” An explicit deferral becomes a follow-up candidate. A rejected direction is not a follow-up candidate.

Format:

# Rejected direction — <slug>-<NNN>
## Direction
<one-line description of what was rejected>
## Rationale
<why it was rejected, in 1–2 sentences>
## Context
Session: <slug>; rejected at: <handshake | scope-audit>

/cheese consults this store before it proposes a direction again. See skills/cheese/SKILL.md § Rejected-directions check. Do not write ordinary scope boundaries or accepted follow-ups here. Non-goal-only dispositions create no artifact. Accepted follow-ups use Deferred follow-ups and any auxiliary .cheese/issues/ recovery draft. Write only direction-level rejections. These include approaches, design knobs, and named features that the user explicitly declined. Note: this store uses the dot-prefixed path .out-of-scope. Its sibling stores, glossary/, issues/, and specs/, do not use dot prefixes. Each scan must explicitly target the dotted path. A bare .cheese/* glob does not match it.

Run /spec-verify as an independent spec-review pass before the hand-off when the harness provides it. Skip it without a warning when it is absent. Note the absence once. This pass is optional. It must not block Curdle in an environment that does not bundle the skill. Never create a hard dependency on it.

Detect the skill at the instruction level, not in code. Check whether /spec-verify appears in the agent’s available toolset. Use the pattern in ../../cheese/references/optional-plugins.md § Probe pattern. Do not use command -v or another shell probe. /spec-verify is a skill, not a $PATH executable.

Stage to a temp directory under ${TMPDIR} first, then move into place. Never leave partial files on a write failure.

This is the runtime home of the Durable writes coherence gate (handshake.md § Agent key). The gate locks the commitment before the handshake; this step honours it. For each durable write — every ADR and the domain-model merge — run:

  1. Resolve the target dynamically. For the ADR, use the resolution procedure in adr.md § Resolution. For the model, run the domain-model-target bundle command shown above with the Hallouminate probe result. It emits backend, string location, and wiki_reachable; False means that the wiki probe was not consulted because it was unavailable. Thus, its file backend is a degraded fallback, not a confirmed absence. This condition triggers the loud fallback below.
  2. Write to that target: add_markdown when the backend is hallouminate, a staged file write when it is file.
  3. Read back the entry and confirm that it exists. For the wiki backend, use ground or read_markdown. For the file backend, read the file again. Treat a write that you cannot read back as a failure. Fail loudly, and do not claim the write.
  4. Record it in the curdle completion record printed to the user: one line per durable write naming <artifact> → <location> (<backend>).

Loud fallback. If hallouminate is unavailable and the resolver degrades to a file backend, state this in one visible line. File backends include docs/adr/…, docs/domain-model*, and the XDG corpus. Never write silently to files when the author expected the wiki. See the absent-plugin degrade contract in ../../cheese/references/optional-plugins.md.

Before this procedure, run the digest-bound fresh-context fork taste test on the dialogue-state draft. It must pass; failures reopen only named forks, with two correction rounds. Then run the procedure on the draft before the two-key handshake. It prepares the typed plan that the handshake approves; Curdle later persists the host-validated artifacts.

  1. Dispatch a fresh-context planner on a PlannerRequest built from the current draft spec text. The planner returns a PlannerResultWriterView; it does not own contract versions, identifiers, digests, lineage, or evidence references.
  2. Validate and normalize the writer view on the host. The normal selected path is the typed PlannerResult containing a typed CurdPlan; reject malformed or wrong-kind output before approval.
  3. Still invalid after one retry — stop before the two-key handshake. Do not approve or persist an invalid plan.
  4. On success, count semantic curds and waves from the typed CurdPlan, then show N curds / M waves with the final approval request. The typed plan is part of what both handshake keys approve. When candidate curds are two or more, ask the landing shape once in that same approval request, alongside the curd-independence confirmation.
  5. Persist the approved spec, typed PlannerResult, and typed CurdPlan. Do not regenerate or mutate them after approval.

Finalize the approved spec and plan before the hand-off. The host owns three values and sets each one once:

  • REQUEST_ID is the request_id of the PlannerRequest. On a path with no planner, use the spec slug.
  • ARTIFACT_ROOT is .cheese/cook/<slug>-artifacts. Mold and Cook must use the same directory.
  • The operation id is <slug>-<ordinal>. Keep it for an identical retry. Increase the ordinal when an input changes.

Record the handshake verb first. Pass the user’s reply word for word; a reply that is not an approval records a rejection:

Terminal window
python3 skills/mold/scripts/mold.pyz approve "$SPEC" \
--artifact-root "$ARTIFACT_ROOT" \
--request-id "$REQUEST_ID" \
--kind plan \
--response "<the user's literal reply>" \
--planner-result "$PLANNER_RESULT_JSON"

Use --kind partial_plan for a partial planner disposition. On the Light path, use --kind scope --curd-id <curd-id> and omit --planner-result. The command prints approval_path. Then finalize:

Terminal window
python3 skills/mold/scripts/mold.pyz finalize "$SPEC" \
--approval "$APPROVAL_PATH" \
--artifact-root "$ARTIFACT_ROOT" \
--operation-id "<slug>-<ordinal>" \
--request-id "$REQUEST_ID" \
--mode full \
--planner-result "$PLANNER_RESULT_JSON" \
--taste-result "$TASTE_RESULT_JSON" \
--ledger "$TASTE_LEDGER_JSON"

--taste-result is the persisted taste-test verdict. --ledger is the decision ledger as JSON. --plan is optional when the PlannerResult embeds its plan.

Read status in the output, not only the exit status. A nonzero exit status is an input error; stop. ready stores a canonical HandoffPointer and prints the Cook command. saved-not-ready exits zero, stores no pointer, and lists each unmet requirements entry and hold.

For ready, render the ready branch of handoff-menus.md. The printed command contains --auto; run it only when the user selects an automatic choice. A manual choice uses /cook <pointer path> --spec "$SPEC". For saved-not-ready, follow the saved-not-ready branch of the same menu.

Do not render this hand-off until phase-two publication attempts and the mechanical Deferred follow-ups reconciliation are complete. After writing, suggest the next step inline. Never auto-invoke.

Artifact Suggested next step
Red-required Spec /cook --auto <pointer path> --spec "$SPEC" (add --hard when the user passed it)
Spec /cook <pointer path> --spec "$SPEC" (add --hard when the user passed it)
Issues Paste each into your tracker, or gh issue create --body-file <path>

Trigger and trace tests for /mold. Run these against real session transcripts when the skill changes.

These prompts must invoke /mold (or its router parent /cheese must hand off to it):

  • “grill the agent-decided items”
  • “let’s design a rate limiter for the API”
  • “I’m thinking about adding OAuth support”
  • “what should the schema for the events table look like”
  • “should we do the migration now or wait, thinking about downtime”
  • “/mold rename the parse_flags helper to parse_args in cli.py” — must trigger, and must land in the Quick tier: one confirm, a mini-spec, no fork questions (tiers.md).

These prompts must NOT invoke /mold:

  • “fix the failing test in auth.ts” — direct implementation, route to /cook.
  • “review this diff for bugs” — review-only, route to /age.
  • “just thinking out loud, no need to write anything down” — no artifact intent, route to /culture.
  • “what does the Stripe API say about idempotency keys” — external research, route to /briesearch.

If a should-not query triggers /mold, the description in SKILL.md is over-broad — tighten it.

For every completed /mold run, verify first that the tier was announced in one line with its reason before the first fork question, and that any tier change was an announced upgrade, never a silent downgrade (tiers.md).

For each completed /mold Grill-mode run, verify:

  1. Every grilled item produces a steelman + tension statement before any verdict. Do not proceed directly to an uphold or amend verdict. Surface the steelman first.
  2. One user-fork round or more for each consequential grilled item. Consequential is the leverage line in ../../age/references/voice.md. Invoke the question primitive at least once for each consequential item. Use AskUserQuestion on Claude Code and Conductor. On another harness, use the equivalent in ask-user-question.md. Require a real user turn. Never render and answer an A/B/C/D block yourself. Never issue a verdict monologue on a consequential item without a user turn. Items below the leverage line are batch-reported as upheld with a one-line steelman and never become a user turn; a user turn spent on one is a regression.
  3. Amendments surface as questions before ledger entry. Ask the user about each amendment that a grill produces. Ask before you write the amendment to the per-round decision ledger.
  4. Clean-steelman batching stays scoped. Batch-report a consequential item as upheld only when its steelman finds nothing. Never include a consequential item with a live tension in a batch. Below-the-line items always batch, tension or not; the ledger carries their vetoable alternative.
  5. Every user fork names what it moves. Each Asking entry cites the acceptance criterion, public seam, or non-goal it changes. A fork with no such target is [AGENT-DECIDED] or a follow-up candidate, never a question.
  6. The Goal: line survives every round. The ledger repeats the pinned goal verbatim from the bounds pass to the handshake. A reworded goal traces to an explicit user fork.
  7. Every G-n clause reaches the handshake with a disposition. The bounds pass prints the clauses. The narrowing delta names each one as covered, follow-up, non-goal, or [TBD]. A spec that covers fewer than half the clauses triggers a re-pin-or-widen fork, never a silent proceed.
  • Verdict monologue — the agent steelmans every item, self-issues uphold/amend verdicts, and presents a finished verdict block with no user turn. This is the regression this eval exists to catch (see issue #279, and the Grill section in skills/mold/references/modes.md).
  • Amendment silently folded into the ledger — an amendment appears in Decided without a prior question to the user. Log as a regression.
  • Over-batching — a consequential item with a real tension gets swept into the “batch-reported as upheld” exception meant only for clean steelmans.
  • Under-batching — a below-the-line item (naming, internal scope, a trade-off that fires no trigger) gets its own user turn instead of a ledger line.
  • Altitude drift — three or more consecutive forks move no acceptance criterion, seam, or non-goal, and the decision map never renders. The rabbit hole this eval exists to catch: every noun traces to the user, so the noun-level scope gates stay green while the dialogue burrows.
  • Goal fade — the Goal: line drops out of the ledger render or silently rewords. The taste test catches the terminal form as goal-drift; the per-round render is the early form.
  • Quarter build — the goal sentence survives and every noun traces, but Acceptance delivers a fraction of the clauses and the agent presents the slice as the whole. The pre-check catches the terminal form as goal-coverage:G-n or goal-coverage-cap; a missing narrowing delta at the handshake is the early form.

Run this scenario with a fresh OMP task agent in an isolated repository. Supply the changed Mold and Cook bundles explicitly.

  1. Give the agent a strict, valid spec that needs two curds.
  2. Supply the planner result, taste verdict, and user approval as scripted host events.
  3. Require mold.pyz finalize; reject any publish command or direct CurdPlan hand-off.
  4. Pass the emitted HandoffPointer to Cook’s preparation command.
  5. Capture actual tool calls, approval events, and final artifact references.

The trace passes only when the bounded transcript checker accepts it and Cook loads the emitted handoff. Missing approval evidence must stop finalization.

These evals are intentionally manual today.

GATE_MODEL in src/easy_cheese/skills/mold/gate_graph.py holds the one canonical model of Mold’s gate state machine. mold.pyz bundles it as the gate-graph subcommand. Both render targets derive from that one model, so they cannot drift. See ADR-001. The model is also the gate-prose-sync source. One test asserts that the handshake coherence-checklist items equal the model’s gate nodes. No gate can therefore disappear from the prose without a failure.

Terminal window
python3 skills/mold/scripts/mold.pyz gate-graph \
[--state <state.json>] [--render dot|svg|png|mermaid] [--out <path>]
  • --render dot (default): canonical Graphviz .dot to stdout.
  • --render mermaid: a fenced ```mermaid flowchart block to stdout — renders natively in GitHub and markdown viewers, no binary required.
  • --render svg|png: shells out to Graphviz dot when it is on PATH; pass --out <path> for binary targets. When dot is absent it degrades to mermaid and prints a note to stderr — run-anywhere by construction.
  • --state: an optional Mold state.json. The command validates its shape. The gate model stays static, so the state does not change the graph today.
  • Onboarding a contributor to mold’s flow — one picture of modes → gates → handshake → curdle.
  • Auditing that the prose checklist and the enforced gates still agree (the gate-prose-sync test is the automated form; the rendered graph is the human form).
  • Embedding the mermaid block in a doc or PR description where no Graphviz toolchain exists.

Requiring Graphviz would break run-anywhere (it is absent on many machines, including the dev box). Mermaid-only would lose the canonical .dot / enforcement angle. Emitting both from one in-memory model keeps zero hard dependency and keeps the two targets in lockstep — the no-drift guarantee is structural, not a convention someone has to remember.

skills/mold/scripts/mold.dot is the committed canonical .dot. A test asserts it byte-matches to_dot(), so the snapshot can never go stale against the model. Regenerate it whenever the model changes:

Terminal window
python3 skills/mold/scripts/mold.pyz gate-graph --render dot \
--out skills/mold/scripts/mold.dot

One coherence gate is worth calling out on its own: non-goals-audit (rendered non_goals_audit in the .dot; label Non-goals audit: every bullet traces to a user-stated out-of-scope item or is marked [AGENT-INTRODUCED]). Like every gate node it feeds the handshake and is kept in lockstep with the handshake.md checklist. It makes the most consequential lean — narrowing scope via Non-goals — a first-class, testable gate rather than a prose-only check (ADR-002). The audit procedure lives in handshake.md § Non-goals audit.

goal-coverage (rendered goal_coverage in the .dot) is the subtraction sibling of non-goals-audit. The noun audits catch scope the agent added; this gate catches scope the agent dropped. The bounds pass splits the pinned goal into G-n clauses and records them in the ledger as goal_clauses. The taste pre-check then reads each clause’s disposition from the draft alone: covered when an Acceptance line carries the tag, non-goal / follow-up / tbd when the matching section carries it, uncovered otherwise. An uncovered clause fails goal-coverage:G-n. Fewer than half covered fails goal-coverage-cap:<covered>/<total> and forces a re-pin-or-widen fork. taste-test --coverage prints the disposition map for the narrowing-delta line. Procedure: handshake.md § Goal coverage.

fork_taste_test_passed is the only edge into the typed planner stage. Mold hashes the exact draft before it accepts a verdict. The strict ForkTasteVerdict must cover each settled consequential decision exactly once.

The verdict fails for a stale digest, missing reflection, contradiction, orphan, unsupported assumption, or acceptance gap. A semantic pass with blockers also fails.

The ledger JSON may carry the pinned goal as a top-level goal string beside forks; a blank or non-string goal is a ledger error, ledger-goal-empty. The draft’s goal section is the first heading, at any level, whose text after case-folding and punctuation stripping is exactly Problem statement, Problem, or Goal; a heading with extra words such as ## Goal alignment or ## Problem statement (v2) is not the goal section, and a later goal heading never overrides the first. That section must contain the pinned sentence unchanged, compared case- and whitespace-insensitively. A reworded or dropped goal fails as goal-drift. A missing or unrecognised goal heading fails as missing-section:goal:problem. A ledger without goal skips the check. This is the zoom-out gate: the goal the user pinned in the bounds pass is the goal the spec ships. The devil in the details changes Acceptance or an Interface sketch; cruft changes neither, and a fork that changes neither never reaches the user.

The required reflection set depends on the disposition. A red-required draft requires Approach, Interface sketches, Acceptance, and Test Contracts. A not-applicable draft requires the first three reflections. The applicability gate prohibits Test Contracts in a not-applicable draft.

The lexical pre-check (taste-test --precheck) runs the deterministic sub-checks (section presence, fork-id or decision-text mention, goal drift, applicability) against the draft alone. It runs before the reviewer dispatch. It does not count toward the two correction rounds. The full taste-test with --verdict repeats the same checks on the digest-bound verdict.

A third failure stops typed planning and the two-key handshake.

The automatic handoff is /cook --auto <pointer path>. It passes the published pointer and the approved metadata without changes. This metadata includes applicability, contract, and taste data.

Probe the current repository’s wiki corpus at Ground phase entry. Probe only when hallouminate is available. Also probe at decision points in any Dialogue mode before you ask the next question. See § When to probe. Add each matching rationale entry or ADR entry to the evidence base.

Record one named probe outcome in the ledger for every probe. The outcome is hit, miss, or unavailable. Record hallouminate: absent once when the tool is missing. Then continue with diff evidence and code evidence only.

The grounding-recorded coherence gate blocks the first structured question until the ledger carries a probe outcome. The outcome carries citations, or it names the reason for the absence. Keep the degrade path cheap and visible.

Do not map the topic outcomes to domain-model-target arguments. Use the domain-model probe transport in ../../cheese/references/optional-plugins.md.

The probe mirrors the wiki probe pattern in the detect-and-degrade contract. See ../../cheese/references/optional-plugins.md.

ground_wiki(topic, repo_name, session_corpus):
# 1. Check hallouminate availability.
if "mcp__hallouminate__list_corpora" not in available_tools:
record once: "hallouminate: absent (tool not loaded)"
return unavailable
# 2. Select the corpus for THIS repository. Reuse the session selection.
if session_corpus is None:
corpora = mcp__hallouminate__list_corpora()
matches = [c for c in corpora if c == "repo:" + repo_name + ":wiki"]
if len(matches) != 1:
record once: "hallouminate: unavailable (no wiki for " + repo_name + ")"
return unavailable
session_corpus = matches[0]
# 3. Ground the topic.
results = mcp__hallouminate__ground(query=topic, corpus=session_corpus, limit=5)
record: "hallouminate: hit" with citations, or "hallouminate: miss"
return results
  • Derive repo_name from the configured origin remote. Use the host-reported repository root name only when no origin exists.
  • Match the corpus name exactly. Never select the first repo:*:wiki entry. A different repository’s wiki carries another project’s private rationale.
  • Record unavailable when zero corpora match. Record unavailable when more than one corpus matches. Never guess between two candidates.
  • Never block the dialogue on the probe. An unavailable outcome satisfies the gate.
  • Retain session_corpus for one Mold episode. Repeat discovery only after an unavailable outcome or a registry change.
  • State the absence once for each run. Write it to the ledger once. Do not repeat it for each question.

Probe at Ground phase entry. In other Modes (Shape/Sketch/Grill), probe at decision points before the next question when any condition applies:

  • The dialogue is about to decide a consequential fork whose options could have prior rationale (e.g. “why not X”).
  • Probe when the wiki can possibly answer the next user question. Examples include a settled decision, an ADR, or a recorded convention.
  • The dialogue is about to restate rationale for an existing system or module that may have ADRs.
  • The spec being molded overlaps with a prior mold session in this repo.

Skip the probe in pure Explore mode when there is no named system. Also skip it in Diagnose mode. Diagnose evidence comes from code and logs, not rationale documents.

Cite hits in that round’s decision ledger under Decided / Asking / [AGENT-DECIDED]. See ../SKILL.md § Rules. Put a settled wiki decision under Decided and cite its wiki page. Do not reopen it as a new question.

A session can start with prior evidence. Sources include a /culture synthesis, a /briesearch report, or a .cheese/notes/<slug>.md wheypoint. Sources also include an earlier Mold draft or an ADR. Read this evidence once during the Bounds pass. Add each settled item to the ledger. Do not derive settled items again in the parent context.

An item covers a decision only when all three conditions apply:

  • Cited. The item names evidence that a reader can check. Use a path and symbol, line range, wiki page, or dated URL.
  • Fresh. The citation resolves at the current HEAD. Probe the evidence again when a referent moved, changed its name, or no longer exists.
  • Decisive. The item names the decision that it settles. A topic alone does not settle a decision.

Put covered items under Decided. Include the source artifact and citation, such as via: .cheese/notes/<slug>.md. Run the normal pass for each uncovered item. The fast path skips work. It never skips a gate. It cannot skip the two-key handshake or fresh-context taste test. It also cannot skip a consequential fork that the user did not select.

Record the intake result for each session. Record prior evidence: none when the session starts with no prior evidence. This intake record is not a probe outcome. It does not satisfy grounding-recorded. Run the wiki probe and record its outcome separately.

If hallouminate is absent and design rationale is central to the current question, cap the result at speculating. Note this limit inline. See ../../cheese/references/optional-plugins.md for the full degrade contract.

Read this when rendering /mold’s post-Curdle handoff. The curd-count digest is advisory sizing; finalization owns readiness and execution authority.

  • A ready finalization result carries a consumer-valid pointer. A saved-not-ready result carries only durable preparation requirements and holds, so render no pointer command and no automatic command. Show each requirement and hold. Cook does not read these saved requirements, so offer Let Cook prepare the spec/cook --spec "$SPEC" only when the result has no hold and every requirement kind is approval, scope, or plan. Cook preparation then asks for each missing plan and approval. For any other requirement, such as a failed taste verdict, render no Cook choice.
  • For a ready result, a non-null handoff means red-required; a null handoff means closed not-applicable or legacy input. Both route to Cook with the canonical pointer from finalization: auto choices use /cook --auto <pointer-path> --spec "$SPEC" and manual choices use /cook <pointer-path> --spec "$SPEC".

Then render the branch selected by mode:

Decomposable specs (decomposable: true, candidate_curds ≥ 2, mode: parallel):

  • Run the full pipeline (parallel fan-out when disjoint, else linear) (recommended) — use the disposition-selected auto command above. Cook later uses the approved curds to select parallel or linear execution; /plate publishes the selected ordinary or stacked layout.
  • Implement manually, one phase at a time — use the disposition-selected manual command above.
  • Stop — dispatch none; leave the spec for later.

Non-decomposable, high-blast-radius specs (decomposable: false, verdict high only, mode: linear):

  • Run the full pipeline in fresh-context isolation (recommended) — use the disposition-selected auto command. Red-required behavior continues cook → press → age → cure → age → cure → age; closed N/A skips Press and continues cook → age → cure → age → cure → age.
  • Implement manually, one phase at a time — use the disposition-selected manual command.
  • Compact and resume by hand — dispatch none; clear context, then use the disposition-selected manual command. /cheese --continue scans phase handoff slugs, so a fresh spec must be resumed through its explicit path.
  • Stop — dispatch none; leave the spec for later.

Non-decomposable, low- or medium-blast-radius specs (decomposable: false, verdict low or medium, mode: null):

  • Implement the spec (recommended) — use the disposition-selected manual command.
  • Implement and auto-review — use the disposition-selected auto command. Opening or updating a PR remains /plate’s explicit step.
  • Research more first/briesearch.
  • Stop — dispatch none; leave the spec for later.

mode: parallel|linear selects fresh-context Cook execution; mode: null selects the smaller in-session path. The user must still opt into --auto.

Curdle (artifact extraction) requires both keys. Neither is optional.

The user key shows explicit extraction intent: approval to write the spec. The direct form is curdle. ship it, extract, or that's enough have the same effect. A clear affirmative also turns the key when it directly answers an agent’s extraction question. Examples include ok let's go, sounds good, and go ahead.

Judge the key by intent, never by spelling. Capitalization, surrounding whitespace, or punctuation never invalidate otherwise-clear approval. Never demand an exact respelling or a magic string. Do not infer the key from unrelated or ambiguous approval. Ask explicitly when the context does not establish that the user approves Curdle.

Print this checklist and require every box checked before extraction (or an explicit curdle anyway override):

Coherence self-check before curdle:
- [ ] Problem statement: grounded, agreed
- [ ] Grounding recorded: a wiki probe result — citations or an explicit hallouminate-absent note — preceded the first structured question
- [ ] At least 2 options weighed (Do Nothing included)
- [ ] Chosen option grounded in codebase evidence
- [ ] Exploration delegated: evidence-heavy reads carry an explorer digest, or the parent-context fallback is recorded
- [ ] Interface sketches: Placement block complete (slice, spine step, public interface, private, crust delta, arrows)
- [ ] Cross-module calls go through public interfaces, not internals
- [ ] Identity nouns: each bound to a code referent or marked NEW ENTITY (an ALIAS must be resolved, not just noted)
- [ ] Non-goals audit: every bullet traces to a user-stated out-of-scope item or is marked [AGENT-INTRODUCED]
- [ ] Goal coverage: every G-n clause is covered by an AC or carries an explicit disposition, at least half ship, and the narrowing delta is printed
- [ ] Validate cycles: all launched cycles judged
- [ ] Chosen option Grilled (≥1 stress-test entry per major branch)
- [ ] Open questions all marked [TBD] / [BLOCKED] / [?] (none silent)
- [ ] Quality gates specified (≥1 runnable command)
- [ ] Reproduction loop captured if Diagnose ran (or [BLOCKED] if no loop is possible)
- [ ] Durable writes: ADR + domain-model targets resolved and the write, read-back, and completion-record protocol committed for the atomic step (or loud fallback noted)
- [ ] Fork taste test passed: fresh-context verdict covers every settled consequential decision before typed planning
- [ ] Spec format valid: validate-spec --strict exits 0 on the draft

If any box is unchecked, name it and propose the smallest move to fill it. The user can override with curdle anyway.

The last box — Durable writes — is a commitment checked before the handshake. It does not claim that the write already occurred. It confirms that the ADR + domain-model targets are resolved. It also locks in the write → read-back → completion-record protocol for the atomic-write step (curdle.md § Atomic write). The read-back verification and visible completion record occur during that step. Note the hallouminate-absent fallback clearly, never silently.

These checklist items match the gates in Mold’s machine-readable gate model. See gate-graph.md. A passing fork_taste_test_passed verdict opens the typed planner stage. A stale, partial, contradictory, or blocker-bearing verdict keeps typed planning closed. A test compares this checklist with the model nodes. Edit both sources together. Render the flow with python3 skills/mold/scripts/mold.pyz gate-graph.

These are not soft suggestions — Curdle hard-blocks until they are addressed:

  • Ground gate: ≥1 Ground pass with a citation before Shape’s options. Exception: pure greenfield (the agent must say so out loud).
  • Shape gate: ≥1 Option block weighed (Do Nothing counts).
  • Sketch gate: mandatory when the chosen option touches more than one module or introduces a new public interface. Skip only for trivial single-function changes (the agent must say so out loud).
  • Grill gate: mandatory for high-blast-radius decisions. The shape check (shape-check.md) ranks blast radius low | medium | high from semantic caller search and tilth_deps when available. A high verdict — multi-module callers or more than five importers — makes Grill mandatory.
  • Open hypotheses: any Validate Cycle launched but unjudged blocks Curdle unless the user accepts it as [TBD].
  • Agent-introduced scope: every distinguishing noun in the spec must trace to a user-typed mention or carry an approved scope-audit row. See the full procedure in § Agent-introduced scope below. Curdle is the single chokepoint because downstream skills trust the resulting frontmatter and do not re-block.
  • Entity-referent binding: bind every identity noun to a code referent or mark it NEW ENTITY. Resolve each ALIAS; do not only note it. See the full procedure in § Entity-referent binding below.
  • Non-goals audit: every Non-goals bullet traces to a user-stated out-of-scope item or is marked [AGENT-INTRODUCED]. Full procedure in § Non-goals audit below.
  • Goal coverage: every G-n goal clause from the bounds pass is covered by an Acceptance criterion or carries an explicit disposition. At least half the clauses ship. The handshake prints the narrowing delta. curdle anyway does not waive this gate. Full procedure in § Goal coverage below.
  • Fork taste test: Require a fresh-context verdict before decomposition. Dispatch the verdict to a read-only reviewer (taste-test) at default / medium, resolved through ../../cheese/references/agent-resolution.md. The verdict must match the draft SHA256. It must cover each settled consequential decision exactly once. It cannot contain contradictions, orphaned decisions, unsupported assumptions, or acceptance gaps. When the ledger pins a goal, the draft’s Problem statement must contain it unchanged, compared case- and whitespace-insensitively, or the verdict fails as goal-drift. Hand the reviewer the ledger’s goal and goal_clauses with the draft, so its Drift lens judges the spec against the original ask and not against the already-narrowed draft. Mold permits the initial verdict and two corrective rounds. A third failure stops the process. Before the reviewer dispatch, run taste-test --precheck on the draft and ledger. Fix every mechanical gap it reports; the pre-check consumes no correction round.
  • Spec format gate: Run validate-spec --strict on the draft before Curdle extracts it. The command must exit with status 0. Curdle writes only the current hardened format. It does not use the legacy read grace period.
  • UI surface classification: every Mold-produced spec carries a provenance marker and an explicit ui_surface value under gate_applicability. browser requires an existing browser/E2E interface and outer seam for every Test Contract; non-browser is explicit and never inferred from prose; closed non-behavior work, including appearance-only, uses not-applicable. The taste and curd gates enforce this field without changing legacy specs.

The agent-introduced scope, non-goals, entity-referent, and follow-up audits below populate one table, presented once, before the handshake. Each row carries a default disposition. One confirm of the table approves every default except the rows marked needs your verb. A row needs its own explicit verb only when it fires a leverage trigger (../../cheese/references/routing-policy.md § Leverage triggers) or is an unresolved ALIAS / NEW ENTITY binding. A row fires a trigger when the term, bullet, or noun it names would itself fire one of the eight ids if kept: an auth knob fires auth, a new export fires contract, a new domain fires new-slice. Such a row renders needs your verb in its Default cell, and the confirm never covers it. The grep and semantic search that populate the rows still run; the per-row approval round does not.

Scope audit:
| # | Kind | Term / bullet / noun | Source | Default | Leverage |
| --- | --- | --- | --- | --- | --- |
| 1 | scope | <noun> | agent / citation | keep | — |
| 2 | non-goal | <bullet> | agent | keep | — |
| 3 | entity | <noun> | search | Bound <referent> | — |
| 4 | follow-up | <unit: member, member> | dialogue | non-goal only | — |
| 5 | scope | <noun> | citation | needs your verb | contract |
| 6 | entity | <noun> | search | needs your verb (ALIAS <referent>) | — |
Confirm the table to accept the defaults. Rows marked `needs your verb` block until you name a verb for each.

Curdle runs the table as the terminal backstop. It remains the single chokepoint that downstream skills trust (RC3).

Before curdle, audit the draft spec for features the user did not type the name of.

Procedure:

  1. Extract distinguishing nouns from the spec’s Approach, Decisions, and Interface sketches blocks. Include proper-noun-like terms, library names, algorithm names, Greek parameter letters, config keys, and knobs.

  2. For each noun, grep the prior user turns for a literal mention. Search only the user’s typed messages, not agent or sub-agent output.

  3. Any noun with zero hits is agent-introduced. Mark it [AGENT-INTRODUCED] inline in the draft and add a scope row to the scope audit table. Default keep when the noun restates the user’s ask or binds to a code referent, follow-up when it names new work, drop otherwise.

  4. One confirm of the table approves the defaults. A row that fires a leverage trigger needs its own verb: “keep ”, “drop ”, or “make a follow-up”. “Make a follow-up” records a candidate within Decided. This choice does not create an issue or other artifact.

  5. When a direction is dropped, whether the user typed the verb or confirmed a drop default, write a rejection record to .cheese/.out-of-scope/<slug>-NNN.md. A direction can be an approach, design knob, or named feature that the user declines. Use the format in curdle.md § Rejected-directions store. Do not make a rejected direction a follow-up candidate. Add explicit deferrals to the follow-up candidate set instead.

  6. Do not silently promote a flagged term from a research citation into a design knob. This applies to briesearch sub-agent citations, fetched docs, and MCP results. The citation is evidence, not a mandate. See skills/briesearch/references/synthesis.md § Alternatives are open questions.

This gate exists because research sub-agents have historically over-synthesised. For example, a Tavily snippet mentioning “X or Y” became a shipped [setting].knob = "x" | "y" flag. The flag passed through curdle → cook although the user never typed the distinguishing noun. The grep heuristic detects this type of drift early.

Curdle is the single chokepoint for this gate. Downstream skills (/cook, etc.) trust the spec frontmatter and do not re-block. Record approved-but-flagged terms in spec frontmatter as agent_introduced_scope: [<term>, …]. This record preserves the paper trail.

Non-goals narrows scope — it removes work the user may have wanted without ever asking. That makes it the single most consequential lean, and the existing drift gates never audited it (they read only Approach / Decisions / Interface sketches). This gate guards it, as a sibling of Agent-introduced scope.

Procedure:

  1. For each Non-goals bullet, grep prior user turns for a statement that puts the item out of scope. Search only the user’s typed messages. Examples include “don’t bother with X”, “leave Y alone”, and an explicit deferral.
  2. Any bullet with no such user statement is agent-introduced. Mark it [AGENT-INTRODUCED] inline and add a non-goal row to the scope audit table with default keep. Non-goals that the bounds pass authored as [AGENT-DECIDED] are agent-introduced by definition and enter the table the same way. The user keeps, drops, or rewords it by confirming or editing the row.
  3. Record approved-but-flagged non-goals in the same agent_introduced_scope frontmatter list, so the paper trail survives downstream.
  4. Add every audited non-goal to the follow-up candidate set, including approved [AGENT-INTRODUCED] bullets. Candidate status preserves the scope boundary without accepting future work.

This audit is the Non-goals audit coherence gate. It is the non_goals_audit node in the gate model (gate-graph.md). Populate its rows as non-goals are proposed; present them once, in the scope audit table. Curdle reruns it as the terminal backstop and hard-blocks extraction until every bullet traces to the user or has an approved [AGENT-INTRODUCED] row.

The noun-level gates catch additions. This gate catches subtractions. A spec can keep the goal sentence verbatim, pass every noun audit, and still ship one quarter of the ask. The remaining three quarters vanish through a narrow Acceptance section, never through a Non-goals bullet. The goal-coverage gate makes every cut visible and puts a floor under it.

Procedure:

  1. Decompose in the bounds pass. Split the pinned goal into 2–6 outcome clauses, G-1G-n. Each clause names one observable outcome the user asked for. Print the clauses under the Goal: ledger line in round one, and repeat them each round. Only an explicit user fork adds, removes, or rewords a clause. Record them in the ledger JSON as goal_clauses: [{id: G-n, text: ...}, ...].
  2. Tag the draft. Every Acceptance line that delivers a clause carries its tag, for example - AC-2: WHEN ... THE SYSTEM SHALL ... (F-1, G-2). A clause the spec does not deliver carries its tag on exactly one disposition line instead: a Non-goals bullet, a Deferred follow-ups entry, or an Open questions item marked [TBD]. Acceptance wins when a tag appears in more than one place.
  3. Run the check. python3 skills/mold/scripts/mold.pyz taste-test --precheck --draft <draft> --ledger <ledger> fails goal-coverage:G-n for each clause with no tag in any of those four sections. It fails goal-coverage-cap:<covered>/<total> when fewer than half the clauses are covered by Acceptance. Both codes also fail the digest-bound verdict.
  4. Print the narrowing delta. Before the handshake, run python3 skills/mold/scripts/mold.pyz taste-test --coverage --draft <draft> --ledger <ledger> and print one line: Original ask: G-1..G-n. This spec ships: <covered>. Deferred: <G-n (follow-up)>, <G-n (non-goal)>, <G-n (tbd)>. The delta is the visible cut list. A spec with no deferred clause prints Deferred: none.
  5. Respect the cap. When goal-coverage-cap fires, the spec is a slice of the goal. Do not proceed. Put one fork to the user: re-pin the goal to the slice (the ledger Goal: line and clauses change through an explicit user fork), or widen the spec until at least half the clauses are covered. Never rename a slice as the whole.
  6. No override. curdle anyway accepts unchecked coherence items. It does not waive an uncovered clause or the cap, for the same reason it does not waive a leverage row: downstream skills trust the spec and never re-check.

The disposition the spec records on each deferred clause feeds the scope audit table. A non-goal disposition enters as a non-goal row; a follow-up disposition enters as a follow-up row. Record the final disposition map in spec frontmatter as goal_coverage: {G-1: covered, G-2: follow-up, ...} so the paper trail survives downstream.

This gate exists because the agent-introduced-scope and non-goals audits ask did the user type this. Neither asks did the spec keep everything the user typed. Agents narrow an ask by building the first tractable quarter and describing it as the whole. The goal sentence survives, the nouns all trace, and the Acceptance section is one quarter the size it should be. The coverage matrix reads the cut from the Acceptance section and the cap refuses a spec that is mostly cut.

Follow-up disposition (inside the non-goals audit)

Section titled “Follow-up disposition (inside the non-goals audit)”

Before the two-key handshake, dispose of every follow-up candidate in one batch: the follow-up rows of the scope audit table. This process extends the existing Non-goals audit gate. It does not add or rename a gate. The default destination is non-goal only; every other destination is a user edit on the row.

  1. Group related candidates into independently deliverable units. Each unit is one follow-up row whose cell lists its members; confirming the table accepts the grouping, and the user splits or merges by editing the row.
  2. Search GitHub Issues and Hallouminate roadmap goals when discovery is available. A semantic match is surfaced on the row as a recommended link #<id>, not adopted as the default: the default destination stays non-goal only, and the user adopts the link by editing the row.
  3. Recommend one destination per unit:
    • non-goal only — keep the scope boundary, create no follow-up artifact, and offer no action choice;
    • GitHub Issue — use for discrete, independently actionable work;
    • roadmap goal — use for coordinated, milestone-scale, or dependency-linked work;
    • local issue draft — use when publication is not desired or available.
  4. The user approves the destination by confirming the row’s default or editing it. An edited destination also names the action: create/link now or leave prepared.
  5. Record accepted units for Curdle. Keep rejected design directions in the rejection store. Do not add them to this batch.

The user approves grouping, splitting, semantic-match reuse, destination, and action choices by confirming the table’s defaults or editing the rows. Mold settles none silently; every default is visible in the table. Omit this batch when no candidates exist. Preserve the current handshake and Curdle flow.

Record each candidate within Decided as [FOLLOW-UP?]. Include its summary, source, and rationale. A follow-up candidate is dialogue state only. It does not create an artifact or future commitment.

After both keys pass, Curdle writes local artifacts first. It then publishes approved follow-ups. It reconciles their state and references into the durable spec. It only then renders the implementation handoff.

Before curdle, audit the draft for identity/ownership-role nouns. These nouns identify roles that hold, own, span, or claim state or lifecycle. Examples include owner, run, session, claim-holder, coordinator, worker, lease, tenant, and lock-holder. The role triggers the audit, not a fixed word list. Flag domain-specific identities. Do not flag plain value nouns such as formats, algorithms, or config knobs.

The mechanism is semantic symbol search, one query per identity noun, following the shared routing contract. The gate is not “did search find something” — it is a three-way verdict on what search returns:

Search outcome Verdict Action
Symbol whose shape matches the design’s assumed role Bound record code referent + file:line citation
Symbol of a different shape/referent (aliasing) ALIAS state the divergence; resolve by renaming to the real entity or designing the intended one
No symbol NEW ENTITY add a spec section designing it

Procedure:

  1. Extract identity/ownership-role nouns from the spec’s Approach, Decisions, and Interface sketches blocks.

  2. Search each noun and classify it Bound / ALIAS / NEW ENTITY per the table above.

  3. Add one entity row per identity-role noun to the scope audit table. A Bound row needs no approval; an ALIAS or NEW ENTITY row blocks until resolved. The binding detail reads:

    Entity-referent binding check:
    | design noun | code referent | citation | divergence note |
    | --- | --- | --- | --- |
    | run | ALIAS — make_run_id (one dispatch) | — | code `run` is one dispatch, not a session; design assumed a session spanning siblings (a search *hit* of the wrong shape) — state the divergence, rebind to the real entity |
    | session | NEW ENTITY | — | no symbol; the coordinator session the design needs must be designed |
  4. An unresolved binding hard-blocks curdle, exactly as an unapproved [AGENT-INTRODUCED] noun does. A search hit does not resolve the binding. Determine whether the design’s usage differs from the code’s existing meaning of the same word. If it differs, state and settle the aliasing before extraction.

This gate is the referent-level sibling of Agent-introduced scope. That gate asks did the user type this noun. This gate asks does the code have it, with the assumed shape. One example shows why the gate exists. A fully handshook spec declares its goal-claims “owned by the run/session”. The code’s run names one task dispatch, not a coordinator session. The aliased noun then reaches a re-age blocker and a cure-pass-2 design decision that belongs in Mold. Curdle is the single chokepoint. Downstream skills (/cook, etc.) trust the spec frontmatter and do not re-block. Record bound and flagged nouns in frontmatter as entity_referent_bindings: [{noun, verdict, referent, citation, note}, …]. Use a list of binding records. Preserve the referent and promised file:line citation in this record.

curdle anyway permits one durable save when the agent coherence key is unchecked. It does not grant execution authority and does not disable later gates. Record the override and every unchecked item in the spec frontmatter. It accepts every other default. Finalization returns saved-not-ready with a preparation hold; it must not publish a pointer or an automatic Cook command until the requirements are cleared through fresh approval. The override does not waive the scope-audit leverage rows, unresolved identity bindings, failed taste, stale references, invalid landing IDs, an uncovered G-n clause, the goal-coverage cap, or a user do-not-implement hold. Downstream skills trust the saved preparation result and never reinterpret the override as approval.

The user knows their intent; the agent knows the dialogue’s coherence. Either one alone produces drift — user-only writes incoherent specs; agent-only writes specs the user didn’t actually want.

Agent-invoked mini-spec mode — full procedure

Section titled “Agent-invoked mini-spec mode — full procedure”

Read this when /mold uses agent-invoked mini-spec mode. This mode is the tier-1 escalation from /cheese, per SKILL.md § Agent-invoked mini-spec mode. It provides the full procedure, mini-spec schema, and ## Provenance rules.

  1. Derive slug from the user’s ask (kebab-case noun-phrase, ≤ 4 words). Check the leverage triggers in ../../cheese/references/routing-policy.md first. A mini-spec always carries leverage: []. When any trigger fires, refuse the mint, name the trigger ids, and return the ask to /cheese for /mold’s user mode.
  2. Write the resolver-owned <spec-path> with the mini-spec schema below. Resolve it via python3 skills/mold/scripts/mold.pyz artifact-path specs <slug>. Never hardcode a repo-local spec path: the resolver anchors it at the durable corpus, matching the Curdle step.
  3. Validate the minted spec with python3 skills/mold/scripts/mold.pyz validate-spec --strict <spec-path>. Stop on a nonzero exit; no malformed or legacy-compatible artifact advances to Cook.
  4. Return the resolved spec path to /cheese: every disposition dispatches /cook --auto <spec-path>. Return the full resolver path, never a bare slug.
  5. Append --hard to that command when the user passed the flag. Every disposition carries it. Plate alone runs the gate.

The two-key handshake does not fire in this mode. The agent-introduced-scope check still runs implicitly. Every distinguishing noun in the mini-spec must come from the user’s input or the tier-2 /culture or /briesearch synthesis. Record that synthesis in ## Provenance. Never add any other noun. The mini-spec records only the user’s request. It never records the agent’s interpretation.

Mini-spec mode always writes shape: single, so landing.layers stays []. Do not add an inline # comment inside the landing block. The front-matter reader keeps the comment as part of the value.

---
slug: <kebab-slug>
status: draft
source: agent-mini-spec
created: <YYYY-MM-DD>
confidence: <low | medium | high>
intent: <one-sentence restatement of the user's ask>
blast_radius: low | medium | high
leverage: []
inputs: <one-line>
outputs: <one-line>
agent_resolution: []
gate_applicability:
disposition: red-required | not-applicable
work_class: behavior | docs-only | refactor-only | test-only | appearance-only
ui_surface: browser | non-browser | not-applicable
reason: <required only for not-applicable>
landing:
shape: single | orthogonal_flat | stacked_linear | diamond_stack
layers: []
per_layer_green: required | tip-only
review_fixes: fold | top-up
verification: <one-line: the obvious check>
---
## Contract
<one paragraph: behavior change, scope boundary>
## Grounding
Add exactly one row for each probe. Record the real outcome. Never invent a row.
| Probe | Outcome | Evidence |
| --- | --- | --- |
| wiki | <hit \| miss \| unavailable> | <wiki path and one-line finding, or what was attempted> |
| explorer | <hit \| miss \| unavailable> | <explorer digest path and one-line finding, or what was attempted> |
## Acceptance
- AC-1: <verifiable check 1>
- AC-2: <verifiable check 2>
## Test Contracts
Include this section only for `red-required`; omit it for `not-applicable`.
| Acceptance ID | Interface referent | Outermost stable seam | Expected failure | Mode | Interface version | Matrix rows |
| --- | --- | --- | --- | --- | --- | --- |
| AC-1 | <public interface> | <existing outer seam> | <expected RED assertion> | tracer | | |
| AC-2 | <public interface> | <existing outer seam> | <expected RED assertion> | contract-matrix | <ratified version> | <row 1><br><row 2> |
## Non-goals
- <what we are NOT changing>
## Provenance (tier 2 only)
- culture: <one-line synthesis of what /culture concluded>
- briesearch: <one-line synthesis>; artifact: <absolute report path returned by research-layout>

Mini-spec mode writes shape: single unless the user named a shape.

source: agent-mini-spec marks the strict Mold production path. New behavior specs must set ui_surface to exactly browser or non-browser; closed non-behavior specs, including appearance-only, set it to not-applicable. The taste and curd gates reject an omitted or unsupported value. browser requires every Test Contract to name an existing browser/E2E interface and outer seam; non-browser never consults contract prose for classification. User-invoked ceremony specs use source: mold-handshake and the same rules. Specs without either marker remain legacy-compatible.

## Provenance appears only when /cheese reaches tier 2 before falling into tier 1. This occurs when /culture or /briesearch supplies context absent from the original input. Omit the section when tier 1 fires on the raw input.

Mold owns the dialogue and the approval state for every research request. Apply these rules to each /briesearch call:

  • Send invocation: sidechain in the request. A missing value defaults to top-level, which releases the run from Mold’s control.
  • Send allow_question: false. Mold asks every user question itself.
  • Reuse the Mold parent slug for the request. Do not derive a second slug. The parent slug also names the research artifact.
  • Record the full absolute report path returned by research-layout in the artifact: field. This link preserves the citations. It lets /cook or any later skill read them again without new research.
  • Map a don't know result to an open hypothesis. Record no outcome. Keep Curdle blocked until new evidence or an explicit [TBD] decision settles it.
  • Omit artifact: only when /briesearch reads local code patterns and writes no durable file.

Mold has no fixed entry point. Inspect the input shape and pick a starting mode. Announce the mode in one line. Low-confidence classifications default to Explore.

Input shape Start mode Heuristic
Stack trace, “X is broken/slow/flaky” Diagnose error markers, file:line refs, symptom verbs
File path, PR ref, existing spec in the durable spec corpus (resolver-owned; see SKILL.md Curdle) Ground concrete artifact exists; read it first
Half-baked design doc with signatures or schemas Sketch already has interfaces; refine them
“I want to add X” with concrete nouns Bounds pass → Shape run the bounds pass first (edges → goals/non-goals), then jump to options
“Should we do X? thinking about Y” Bounds pass → Grill bounds pass first, then stress-test the tentative plan
Vague noun, half-sentence, “thinking about” Explore no grounded artifact, no chosen direction

Front-loaded bounds pass. Every row above selects a secondary mode. Regardless of input shape, mold first runs the bounds pass from SKILL.md Flow step 1 and opens the per-round decision ledger. For a concrete ask (“I want to add X”, “Should we do X”) the pass is one ledger line: goals and non-goals as the agent read them, marked [AGENT-DECIDED]. It becomes a question only when the goal is genuinely unknown or a leverage trigger fires. Then Shape/Grill receives the scope.

Job: collapse ambiguity with high-leverage questions. Borrow the Job-To-Be-Done frame: Why Now, What This Unlocks, Who Has The Pain, Do Nothing. Use lettered options to compress decisions.

Exit when: the dialogue states one problem statement and one concrete pain point, and the ledger pins that statement on its Goal: line.

Job: anchor every claim to evidence from code, docs, or prior research. When the user uses overloaded terms (“account”, “session”, “user”), pause and ask a canonical-term question. At the curdle atomic step, write resolved terms to the session’s durable glossary at .cheese/glossary/<slug>.md. See curdle.md § Durable glossary. Downstream skills (/cook, /age, /press) can read these terms for naming consistency.

On Ground entry: after the Hallouminate probe, resolve and load the project’s cumulative domain model through the Mold bundle command:

python3 skills/mold/scripts/mold.pyz domain-model-target \
--probe <unavailable|no-match|match> \
[--corpus repo:<repo>:wiki --model <present|absent|unknown>]

Use the domain-model probe transport in ../../cheese/references/optional-plugins.md. Do not derive these arguments from Ground’s topic-search hit or miss.

Pass unavailable when Hallouminate is not loaded or the probe failed. Pass no-match when the listing completed but contained no repo:*:wiki corpus. Pass match with the exact corpus name and its model status when the listing found one. The command accepts only explicit probe results and does not import MCP machinery. Its JSON contains backend, string location, and wiki_reachable. Complete the read-probe cascade before any write: wiki, tracked docs/domain-model*, then <project_corpus_root()>/domain-model*. A listed wiki corpus does not win without a confirmed model. An existing file-store model wins over a wiki corpus without a confirmed model. The resolution mirrors the adr_target() pattern in adr.md § Resolution. It is dynamic, and the existing model always wins. If the probe is unreachable, degrade to “not loaded” and report it. Never block Ground because the probe is unreachable. False means no wiki probe occurred because no hook existed or the hook raised. In that case, announce “domain model not loaded from the wiki”. Do not present the file-store result as the whole picture. The model is cross-session memory. The per-slug glossary is the branch-local handoff. Challenge immediately when a user term conflicts with an existing model entry. Ask: “the model defines X as …, you seem to mean Y — which is it?” Challenges are LIVE here. Defer model writes to the approval gate. Curdle owns the write; see curdle.md. Never write inline during Ground.

Invariant: never say “I think the code does X” without semantic source-code evidence gathered according to the shared routing contract.

Exit when: every critical claim has a citation.

Job: turn a grounded problem into 2+ candidate approaches with trade-offs. Always include Do Nothing. Present lettered options (A/B/C/D) for the user to select. The user chooses consequential forks (the leverage line in ../../age/references/voice.md); do not settle them. Settle every fork below the line and log it. Give a one-line rationale for each option, not a verdict. Validate Cycle every critical assumption behind an option. Score options by the information they leave behind. Prefer the option that reduces the next maintainer’s required knowledge or makes that knowledge more obvious.

Exit when: the user picks one option (→ Sketch). Return to Explore when no option survives.

Job: place the change in the architecture at 10,000 ft. The output is the Placement block that fills ## Interface sketches (curdle.md § Spec template):

  • slice: which domain slice owns the change, or NEW SLICE (a new-slice leverage trigger)
  • spine step: where it sits on the request path (entry → workflow → domain → infra), or none
  • public interface: one signature per new or changed crust export, nothing else
  • private: what stays behind the crust, as a one-line list of responsibilities, not signatures
  • crust delta: new exports, cross-slice imports, or contract changes, or none
  • arrows: any dependency direction the change adds, checked against ../../cheese/references/sliced-bread.md

Run the shape check (shape-check.md) before drafting when the change touches multiple modules or introduces a new public interface; print its summary block first. No block, no sketch. Single-module, internals-only sketches note “shape check skipped: single-module change”; that skip line satisfies the “no block, no sketch” precondition, as shape-check.md § When to skip defines for every sanctioned skip. Do not write bodies, helper signatures, or internal pseudocode in Sketch; those belong to /cook. A non-empty crust delta is a consequential fork.

Acceptance notation (EARS): for every public interface line, emit acceptance criteria in EARS form: WHEN <trigger> THE SYSTEM SHALL <response>. If the trigger cannot be stated precisely (e.g. pure internal utilities), fall back to prose with a [prose-fallback] marker.

Language default: when a sketch introduces a component with a free language choice, default to the repository’s incumbent memory-safe typed language (Python under a strict type checker, or TypeScript in strict mode). When the incumbent is C or C++, treat Rust as the default for new components. Choose Go only for standalone tooling where diff uniformity dominates. Treat Elm and Gleam as niche. Reviewer familiarity and model generation accuracy dominate review speed; cross-language review throughput itself is unmeasured. Record any departure as an [AGENT-DECIDED] item for Grill.

Exit when: the Placement block is complete, every acceptance criterion uses EARS form or has [prose-fallback], every cross-module call uses public interfaces, not internals, and the shape-check verdict is recorded or explicitly skipped under the gate above.

Job: stress-test the chosen approach and sketched interfaces. Grill runs on consequential forks and high blast-radius options only. Tag each grilled item with what it moves: an acceptance criterion, a public seam, or a non-goal. An item that moves none of those, or that sits below the leverage line, is not a user fork: batch-report it as upheld with a one-line steelman, or record it [AGENT-DECIDED]; either way the ledger carries its vetoable alternative (../../age/references/voice.md). Such items never become a user turn. Use one grilled item per turn for consequential items, except for the clean-steelman batch below. For each consequential decision, produce at most a steelman + tension statement. Then present a user fork: uphold / amend-as-proposed / user’s own call. Invoke the question primitive in ../../cheese/references/ask-user-question.md. Use a real user turn. Do not render an A/B/C/D prose block and answer it yourself. Never self-issue verdicts for items that change the design. When the steelman fails cleanly and grilling finds nothing, you MAY batch-report items as upheld. When grilling produces an amendment, ask through the same primitive before adding the amendment to the ledger. Traverse decision branches and contract corners. Pause for a Validate Cycle when an unverified assumption appears.

Exit when: every branch and contract corner is touched and agent confidence ≥ user confidence.

Job: entry mode for stack traces and “X is broken”. Phases: Build a Loop → Reproduce → Hypothesize (3–5 ranked, falsifiable) → Confirm root cause.

Phase 0 (Build a Loop) is the core discipline. Agree on a fast, deterministic, falsifiable feedback technique BEFORE chasing hypotheses. Techniques include a failing test, curl/CLI script, headless browser, replay, bisection harness, or differential loop. The chosen loop becomes the Reproduction block in the bug-shaped spec. Thus, /cook can verify the fix against the same signal.

Diagnose is diagnostic-only — hand off to Shape (“what’s the fix?”) then Curdle emits a bug-shaped spec.

explore, ground, shape, sketch, grill, diagnose, validate <hypothesis>, prototype <question>, curdle, pause, enough, plus the tier knobs quick, light, and full (tiers.md). Honour these immediately.

prototype <question> launches a Prototype Cycle (prototype-cycle.md): a throwaway built in a hermetic sub-agent worktree to settle an ungrillable design unknown, returning only the answer as a digest. The code is discarded; the answer is the keeper.

Marker Meaning
[?] Agent uncertain; needs validation
[TBD] User uncertain; decision deferred
[BLOCKED] External dependency unresolved
[CONFLICT <id>] Codebase contradicts a stated assumption

An escape hatch for design questions that can’t be settled by reasoning, search, or doc-reading — only by trying it. Spawnable at any point in the dialogue, in parallel with a Validate Cycle. Mold owns the parent dialogue; the sub-agent owns the throwaway. The code is discarded; the answer is the keeper.

Use it when a question is ungrillable: an API’s real behaviour, whether two libraries compose, the actual shape of an error, an ergonomics call that only a running sketch can answer. Do not use it for questions a semantic source search, bounded source read, or Validate Cycle already settles — those are cheaper. Follow the shared routing contract for source-code evidence.

Always announce the cycle before dispatching — the announcement is the discipline.

Launching a prototype cycle on question: "<ungrillable design unknown>"
Plan:
spawn sub-agent(isolation: "worktree") # degrade to a temp dir if the repo is not git
build a throwaway (may try several variations)
digest ← { question, answer, snippet?, confidence } # <=2 KB; no code dumped
discard the worktree # the answer is the keeper, never the code
log state.prototype_cycles[] ; optionally emit an ADR (adr.md)

1 cycle == 1 design question resolved, not 1 per variation. The sub-agent may try several throwaways internally; that is still one cycle.

A digest, never a code dump (sub-agent split + digest size live in the shared kernel at ../../age/references/sub-agent-gate.md):

Field Content
question the design unknown, restated
answer the resolved behaviour, in one or two sentences
snippet? the minimal decision-encoding fragment, only if it captures the answer better than prose
confidence certain | speculating | don't know

The worktree is discarded after the digest is extracted. Nothing from the throwaway tree is committed, copied back, or referenced by path — if a fragment matters, it lives in the digest snippet.

  • Default: isolation: "worktree" — a hermetic git worktree, auto-cleaned.
  • The repo is not a git repo: degrade to a temp directory under ${TMPDIR}, say so out loud, and clean it up after extraction.
  • Harness lacks sub-agent spawning: run the throwaway inline in a scratch dir, note the loss of isolation, and still discard it.

Prototype cycles are context-bounded, not capped (ADR-003). Run as many as confidence needs; the 120k/140k context-budget mechanic (context-budget.md) is the natural limiter. A soft backstop of 10 prompts a single “still gathering — continue?” check; it is not a hard stop and the user can wave it through.

Every launched cycle is logged in the mold state file:

prototype_cycles:
- id: pc-1
question: "Does library X's streaming API surface backpressure?"
answer: "Yes — it yields when the consumer is slow; no manual pause needed."
confidence: certain
adr: ADR-002 # optional, when the answer encodes a non-obvious decision

An open cycle (no answer:) blocks Curdle until it settles or the user accepts it as [TBD], exactly like a Validate Cycle.

Use the bundled review canvas when visual artifacts or browser interaction help resolve Mold forks.

  1. Publish the review document with python3 skills/mold/scripts/mold.pyz review publish --state-dir DIR --input FILE [--base-revision N].
  2. Start python3 skills/mold/scripts/mold.pyz review serve --state-dir DIR --port 0 and give the returned private URL to the user.
  3. Read submitted snapshots with python3 skills/mold/scripts/mold.pyz review poll --state-dir DIR --after CURSOR --timeout SECONDS.
  4. Reconcile each exact question and option identifier into the decision ledger.
  5. Publish a new revision when the review document changes. Never replace dirty feedback silently.
  6. Stop the server with python3 skills/mold/scripts/mold.pyz review close --state-dir DIR when the review ends.

Autosave only preserves a working copy. Send to agent creates feedback, not approval.

Browser feedback never satisfies the taste gate, typed-plan gate, or two-key handshake.

The canvas is optional. It is a local-only review aid, not an approval mechanism. It must not request approval, bypass the taste gate, bypass the typed-plan gate, or bypass the two-key handshake.

Run this before drafting in Sketch mode. Also run it whenever discussion in any mode depends on “what does this touch” or “what depends on this”. The check is read-only. Culture and mold both run it; only the artifact stage differs.

  • Signatures: what does the touched function/type look like today? What sibling signatures already exist in the same module so a new one fits convention?
  • Callers (upstream): who calls the touched symbol, and from which modules?
  • Callees (downstream): what does the touched symbol call into? Surfaced by the same symbol query — kind: "symbol" returns a ── calls ── footer with one-hop callees. No extra call.
  • Imports / blast radius: which files import this module? Which does this module import?

These four answers describe the change’s shape and bound its blast radius. Upstream identifies who breaks if you change it. Downstream identifies what you could pull into the change. InlineCoder-style bidirectional inlining gives the downstream half empirical value for repo-level edits. Ignoring it leaves a known gap.

Run all three queries. Each query is cheap when the answers are small. A skipped query causes silent misrouting later.

Question Backend capability Call
Current signature? Sibling signatures? Downstream callees (── calls ── footer)? semantic symbol search tilth_search(queries: [{query: "<symbol>", kind: "symbol"}], expand: 2, scope: "<module>")
Who calls this? (upstream) semantic caller search tilth_search(queries: [{query: "<symbol>", kind: "callers"}])
What’s the import / blast radius? dependency search tilth_deps(path: "<file>")

The first call serves two purposes. Its ── calls ── footer provides the cheap callee read. Do not issue a separate query.

For multi-symbol changes, batch up to five symbols in a single semantic search call (query: "a, b, c"). Re-run only when a new symbol enters scope. Follow the shared routing contract.

A summary at the top of the Sketch turn (or culture’s blast-radius step):

Shape check on <symbol(s)>:
signature(s): <one line per touched seam>
callers: <count> sites in <N> non-test files (paths)
callees: <count> one-hop calls (names) — omit line if empty
blast radius: imported by <count> files; imports <count> modules
slice: <owning slice from the import path>
crust delta: <new exports | cross-slice imports | contract changes | none>
verdict: low | medium | high

The callees line is optional — print it only when the symbol query’s ── calls ── footer is non-empty. A leaf function with no callees should drop the line, not print 0.

slice and crust delta come from the same tilth_deps answer read against ../../cheese/references/sliced-bread.md: the touched symbol’s own path names the owning slice; the importer list shows which other slices consume it. Any importer outside the owning slice that reads past its crust, any new cross-slice import, or any new export is a crust delta.

Three consumers print this block: mold’s Sketch turn, culture’s blast-radius step, and cook’s Contract step. The block shape is the same in all three; only the gate that follows differs.

A high verdict means multi-module callers, more than five importers, or a non-empty crust delta. It makes the Grill gate mandatory in mold; see handshake.md. Before continuing trade-off talk, culture must label the option [high blast radius].

When semantic source tooling is unavailable

Section titled “When semantic source tooling is unavailable”

Shape-check must not block dialogue when its preferred tools are missing. Use a sanctioned alternative when available. For shape-check specifically, do not substitute textual search. grep / rg over a symbol name counts string occurrences, not callers or importers. A guessed blast-radius verdict is worse than an unknown.

  • Callers / callees: fall back to LSP textDocument/references / textDocument/prepareCallHierarchy when a language server is reachable. Note the substitution out loud.
  • Imports / blast radius: no LSP equivalent. Skip the count, mark the line unknown, and lean on the verdict downgrade below.
  • Verdict: cap it at [?], not low | medium | high. A guessed verdict is worse than an honest unknown. For gating, Sketch and culture must treat [?] like high until the user accepts the gap. The Grill gate engages, and culture labels the option [high blast radius].

If both tilth and LSP are unavailable, say so once and proceed with [?]. Do not silently substitute a textual search for the shape-check itself.

  • The touched symbol has zero callers (greenfield) — say so out loud.
  • The change affects one private function in one file and has no exports. Sibling signature lookup still applies. You can skip dependencies and callers.
  • The user explicitly said skip the shape check.

A skipped check prints one line in place of the block, shape check skipped: <reason>, and that line satisfies the “no block, no code” precondition in cook’s Contract and mold’s Sketch. Closed non-behavior work (docs-only, appearance-only) always takes this path.

Trade-offs and seams discussed without a shape check rely on the agent’s guess at impact. The check converts that guess into numbers — caller count, callee count, importer count — the user can argue with.

Direction matters. Upstream callers identify who breaks if the seam changes. Downstream callees identify what the change can pull in. Bidirectional structural context measurably improves accuracy on repo-level edits. The existing symbol query provides the downstream half at no additional cost.

Mold picks a tier at the end of the Bounds pass (../SKILL.md Flow step 1), once the goals, non-goals, and shape-check verdict are on the ledger. Announce the tier in one line with its reason before the first fork question:

tier: quick — clarity check passed, verdict low, no open fork

The user overrides with the quick, light, or full knob at any time. Mold upgrades on its own when the evidence changes. It never downgrades silently.

Tier Enter when every condition holds Runs Skips Artifact → handoff
Quick Cook’s standalone fast-path check passes on the bounded ask (../../cook/SKILL.md § Standalone fast-path), which includes zero fired leverage triggers (../../cheese/references/routing-policy.md § Leverage triggers); shape-check verdict low, or skipped as greenfield or a single private function; no consequential fork open after bounds Bounds, one wiki probe, one fast confirm (go, yes, ship it) Explore, Shape, Sketch, Grill, Validate and Prototype cycles, fork taste test, typed planner, handshake checklist, ADRs, follow-up publication mini-spec through mini-spec-mode.md, still gated by validate-spec --strict; finalize with python3 skills/mold/scripts/mold.pyz finalize and hand Cook its canonical HandoffPointer
Light Goal is clear after bounds; verdict low or medium; at most two consequential forks; no new public seam across modules; one expected curd Bounds with G-n clauses, Ground, Shape for the open forks, Sketch only when a public seam changes, fork taste test, goal coverage and narrowing delta, two-key handshake, Curdle phase one Explore; Grill unless the user asks or a fork turns high-blast; typed planner (one curd needs no CurdPlan); issue-draft publication full spec (source: mold-handshake) → python3 skills/mold/scripts/mold.pyz finalize → canonical HandoffPointer handed to Cook
Full Anything else: verdict high or [?], three or more forks, a new cross-module public seam, two or more expected curds, a Diagnose input, or the full knob The whole Flow Nothing spec + PlannerResult + CurdPlanpython3 skills/mold/scripts/mold.pyz finalize → canonical HandoffPointer## Handoff menu

A fired leverage trigger rules Quick out: the fork it names is consequential, so the ask enters Light or Full by the rows above. Leverage picks whether the user steers; the verdict, fork count, and curd count pick how much of the Flow runs.

The fast confirm is Quick’s user key and validate-spec --strict is its agent key. The checklist in handshake.md applies to Light and Full only. Light marks each handshake box that does not apply as n/a: <reason> out loud rather than leaving it unchecked. curdle anyway is never needed to leave a gate that was never entered.

Upgrade the moment any condition breaks. Announce the new tier with its trigger and keep every ledger entry.

  • A consequential fork opens after Quick’s confirm → Light. The confirm is void until the fork is picked.
  • The shape-check verdict rises to high or [?] → Full, Grill mandatory.
  • A second module or a new public seam enters scope → Full.
  • The expected curd count reaches two → Full. The typed planner is required.
  • Cook returns next: mold on a Quick mini-spec → re-enter at Light with Cook’s failure as prior evidence.

Downgrade only on the user’s knob. Say what the lower tier skips before continuing.

  • The wiki probe and its ledger record (grounding-recorded).
  • The agent-introduced-scope check: every distinguishing noun traces to the user’s words.
  • validate-spec --strict on whatever artifact ships.
  • The user’s say on every consequential fork.
  • The leverage-trigger check: a fired trigger never lands in Quick.

/cheese enters mini-spec mode directly at its tier 1 with no confirm, because the clarity check already passed on the raw input. Mold’s Quick tier is the same mode entered from a user invocation, with one confirm because the user chose to talk first. A mold intent from /cheese still lands in user mode and tiers itself here.

Any mode can invoke a Validate Cycle. Always announce the cycle before dispatching — the announcement is part of the discipline.

Launching a validate cycle on hypothesis: "<single declarative sentence>"
Plan:
/briesearch — fetch evidence
Judge — support, contradict, or refine?
Settle — accept, revise, or reject. Continue from current mode.

A bare /briesearch call without this frame is discouraged. The frame forces commitment to a hypothesis plus a judgment step.

Outcome Meaning Next action
SUPPORTED Evidence aligns with the hypothesis Promote to a decision
CONTRADICTED Evidence disagrees Mark [CONFLICT <id>], revise or abandon
REFINED Evidence partially aligns Restate with new precision and re-validate or accept

Diagnose’s parallel hypothesis ranking IS this cycle, parallelized.

Validate cycles are context-bounded, not capped (ADR-003 of the mold-parity spec). Confidence-gathering is the goal; an arbitrary cap cuts it short.

  • No hard cap. Run as many /briesearch cycles as confidence needs. The context-budget mechanic (context-budget.md) is the natural limiter — offload deep evidence to a sub-agent and watch the window.
  • Soft backstop of 10. At the 10th launched cycle, ask once “still gathering — continue?”; it is a check, not a stop, and the user can wave it through.
  • Cycles backed by local semantic source-code evidence alone are unbudgeted — they do not count toward the backstop.

The same context-bounded rule governs Prototype Cycles (prototype-cycle.md).

  • The claim is already grounded by a bounded source read or earlier cycle; source-code reads follow the shared routing contract.
  • The decision is reversible and small — running a cycle costs more than just trying it.
  • The user explicitly said “skip the cycle”.

Every launched cycle is logged in the mold state file:

validate_cycles:
- id: vc-1
hypothesis: "Express's Router supports per-route middleware arrays"
outcome: SUPPORTED
sources: [Context7]
- id: vc-2
hypothesis: "We can hot-swap the auth middleware without restart"
outcome: CONTRADICTED
conflict_id: cf-1

Open hypotheses (no outcome:) block Curdle until they settle or are explicitly accepted as [TBD].