Added: Phase 59 — CRUCIBLE-MODES
Audience: Plan Forge operators and contributors
The Crucible intake pipeline supports multiple mode lanes, each with its own question bank, critical-field set, and body renderer. The correct mode is inferred from the raw idea text (via inferLane) and can be overridden at submission time.
| Lane | Label | Critical Fields | Best For |
|---|---|---|---|
tweak |
Tweak | scope-file, validation, forbidden-actions | Small targeted changes, minor fixes |
feature |
Feature | scope-files, validation-gates, forbidden-actions | Multi-file feature additions |
full |
Full | scope-in, forbidden-actions, rollback-plan | Broad architectural or multi-phase work |
bug-batch |
Bug Batch | scope-files, validation-gates, forbidden-actions, slice-breakdown | Bug fixes requiring multiple slices |
bug-batchThe bug-batch mode is designed for bugs that require coordinated changes across files and need multiple slices to land safely.
symptom-observed — What symptom are you observing?expected-behavior — What is the expected behavior?suspected-component — Which component or file is suspected?scope-files — Which files need to change to fix this bug?slice-breakdown — Break the fix into slices (see format below)validation-gates — What validation gates will confirm the fix?forbidden-actions — What changes are forbidden while fixing this bug?rollback — How do we roll back if the fix makes things worse?slice-breakdown FormatEach line in the slice-breakdown answer must follow this format:
<slice-name> | <files-changed> | <test-command-or-acceptance>
Example:
Guard empty scope-files | pforge-mcp/orchestrator/plan-parser.mjs | npm run test:parser
Add regression test | pforge-mcp/tests/plan-parser.test.mjs | npm run test:parser
Finalization refuses with CrucibleFinalizeRefusedError if any line has fewer than 3 |-separated parts.
bug-batch produces a plan document with:
## Root Cause Hypothesis — three fields: symptom observed, expected behavior, suspected component## Scope Contract → ### In Scope (from scope-files) + ### Forbidden (from forbidden-actions)## Slices — N synthesized ### Slice N — <name> [scope: <files>] blocks from slice-breakdown## Validation Gates, ## Stop Conditions, ## Rollback, ## Change ManifestbugId and linkedBugsPass bugId at submit time to automatically populate frontmatter:
{ "rawIdea": "Fix crash in parser", "lane": "bug-batch", "bugId": "RMG-0035" }
The finalized plan’s YAML frontmatter will include:
---
crucibleId: <uuid>
lane: bug-batch
source: human
phaseId: Phase-60
linkedBugs: [RMG-0035]
bugId: RMG-0035
---
Additional linked bugs can be recorded via the linked-bugs interview question (appended at the end of each mode’s question bank).
All rendered plan documents now use heading shapes that the orchestrator’s plan-parser can consume directly:
| Renderer Heading | Parser Function | Field |
|---|---|---|
### In Scope |
parseScopeContract |
contract.inScope |
### Out of Scope |
parseScopeContract |
contract.outOfScope |
### Forbidden |
parseScopeContract |
contract.forbidden |
### Slice N — <name> [scope: <files>] |
parseSlices |
slice.scope |
The ## Anti-patterns & Forbidden Actions heading has been removed. Forbidden actions now live exclusively under ## Scope Contract → ### Forbidden.
Before Phase 59 Slice 2, unanswered non-critical fields rendered as `` markers in the plan document. This allowed operators to hand-fill the values later. Post-S2, unanswered non-critical fields are omitted instead (truthful refusal principle), keeping the plan body clean.
If you need the old behavior temporarily, set this in .forge/crucible/config.json:
{
"legacy": {
"tbdPlaceholders": true
}
}
When this flag is enabled, Plan Forge will emit a one-time warning at startup:
[Plan Forge] crucible.legacy.tbdPlaceholders is enabled.
This flag is deprecated and will be removed in the major-after-next release.
Critical fields are never affected by this flag. A smelt missing a critical field will always fail finalization with CrucibleFinalizeRefusedError regardless of legacy.tbdPlaceholders.
| Release | Status |
|---|---|
| Current (Phase 59+) | Flag available; one-time deprecation warning emitted |
| Next major release | Flag still available; warning escalates to console.error |
| Major-after-next | Flag removed; config key silently ignored |
Modes self-register by calling registerMode() from pforge-mcp/crucible/registry.mjs. Each mode must satisfy the CrucibleMode interface defined in pforge-mcp/crucible/mode.mjs:
import { registerMode } from "./crucible/registry.mjs";
const myMode = {
id: "my-mode",
label: "My Mode",
criticalFields: new Set(["scope-files", "validation-gates"]),
questionBank: () => MY_QUESTIONS,
renderDraft: (smelt, opts) => myRenderFn(smelt, opts),
finalize: () => { throw new Error("use handleFinalize"); },
};
registerMode(myMode);
Then import the mode file as a side effect in crucible-server.mjs:
import "./crucible/modes/my-mode.mjs";
For modes with custom body layouts (like bug-batch), implement renderBody(smelt, opts) — the crucible-draft.mjs renderer will delegate to it automatically.