Workflow
A citation-grounded, human-in-the-loop authoring pipeline. One natural-language ask is sized and routed by triage, optionally shaped first by brainstorm, then flows through the authoring stages — each produces exactly one approved artifact, and each stage hard-refuses to start until the upstream artifact is approved. Build writes code; an independent code review then gates the Story's completion.
Every authoring stage follows the same shape — context.assemble (deterministic or a coarse handoff to insrc_analyze_step) → N sequential LLM steps → checklist.verify → synthesize — and every claim in every artifact is cited to a step output or an analyze bundle. Ungrounded citations refuse to synthesize. No step is ever skipped to save turns, and no LLM call runs in parallel: accuracy first, cost last.
insrc_triage sizes the request — grounded on its own insrc_analyze_step passes — and returns a pre-filled next call so nothing is hand-picked: an epic → define; a feature → a standalone design.story → plan → build; a small change → a standalone design.story → build; a trivial one → straight to build; and a bugfix → the issue stage, then scope-routed by its magnitude (see Bugfix triage). The guarantee is that every change, big or small, is tracked. The size→route table lives in src/workflow/triage/classify.ts.| Stage | Produces | What it is | openQuestions |
|---|---|---|---|
brainstorm | SPEC — spec artifact | Optional front stage for a rough idea: one paused elicit turn where you converge a fuzzy ask into a crisp, decision-recorded spec. Skipped when the ask is already scoped. | none |
issue bugfix | ISSUE — defect record | First stage of a bugfix: captures reproduction, root cause, and fix intent. The single source of truth for both the internal chain record and the GitHub issue body. | none |
define | DEF — definition | Frames the Epic: problem statement, non-goals, assumptions, constraints, and 1–10 Stories. | yes |
design.epic | HLD — high-level design | One per Epic: chosen framework, shared contracts, Story boundaries, rollout phases, alternatives considered. | yes |
design.story | LLD — low-level design | One per Story: detailed contract (APIs + data model), error paths, test strategy, migration. | yes |
plan | PLAN — ordered Tasks | A sequenced list of implementable Tasks for a Story. Carries no open questions — the decisions were already made upstream. | none |
build | code + validation | The controller implements each Task and the daemon runs a read-only verdict. | — |
code-review | CR — review record | After the build, an independent review of the changed code across four dimensions. Approving the BUILD is the review-gated completion act for the Story. | — |
openQuestions list on their body (see src/workflow/artifacts/{define,hld,lld}.ts). PLAN deliberately does not — by plan time every design decision is settled, so a plan is pure sequencing. SPEC, ISSUE, and CR carry none either — a spec records decisions, an issue records a defect, and a review records findings.Brainstorm — shape a rough idea first
When the ask is a vague idea rather than a crisp spec, the brainstorm stage runs before triage. It is a single paused elicit turn: you conduct the whole clarify → fold → show-back → confirm convergence in chat — folding each answer into a running statement, showing it back, re-asking only the open gaps, and at every fork recording the ruled-out choice as a non-goal — then resume once with confirmed: true and no open items. The result is a SpecArtifact you review and approve like any other artifact; it then seeds define (for an epic) or a standalone design.story (for a feature), so the elicited decisions flow straight into the tracked chain instead of being lost in chat.
insrc_workflow_step / insrc_workflow_run at workflow: 'brainstorm'; skip it entirely when the request is already a clear scoped spec.Bugfix triage — a defect fix, tracked
A defect fix is a first-class triage category, not just a small feature. When triage recognises a bugfix it sizes it by magnitude — small vs sized — and routes it through a dedicated issue stage before any code is written, so even a one-line fix leaves a cited, reviewable record attached to the code it corrects.
| Piece | What it does |
|---|---|
| issue stage | Captures the defect as an ISSUE artifact — title, reproduction, root cause, fix intent — grounded against the real code. It is reviewed and approved like any other artifact, and its rendered body is the single source of truth for both the internal chain record and the GitHub issue body: there is no divergent second copy. |
| tiered parent-locator | Attaches the fix to the Epic/Story whose behaviour it corrects, through five tiers in order — a deterministic ref-resolver → graph code-ownership → semantic match → a user prompt → standalone — recording which tier decided and at what confidence. It auto-attaches only above a high threshold; a tie, a near-miss, or an absent match falls to a prompt, then to standalone — never a silent wrong attach. |
| scope-gated route | A small fix goes issue → build (no LLD); a sized fix goes issue → design.story → plan → build. Either way the existing post-build code review gates completion. |
| GitHub issue | Where a tracker is configured, the same issue content becomes a real GitHub issue — created from renderIssueMarkdown verbatim, linked as a sub-issue of the located parent, and closed on completion. Idempotent (a recorded ref is reused, not duplicated) and a clean no-op when no tracker is configured. |
Because the ISSUE body is the one source for both records, the GitHub issue never drifts from the internal chain. The whole path stays on the same gh CLI the tracker already uses — no new external surface, no direct cloud REST. The category is feature-flagged (bugfixCategory) and lives in src/workflow/{triage,artifacts/issue.ts,locate,bugfix}/.
Artifacts & approval gates
Each artifact is written twice: a human-readable .md under docs/ and the canonical JSON under .insrc/artifacts/. The JSON is the machine contract downstream stages read; the Markdown is what you review.
| Artifact | Doc path | Canonical JSON |
|---|---|---|
| SPEC | docs/specs/SPEC-<slug>.md | .insrc/artifacts/SPEC-<h16>.json |
| ISSUE | docs/issues/ISSUE-<slug>.md | .insrc/artifacts/ISSUE-<h16>.json |
| DEF | docs/defines/DEF-<h16>.md | .insrc/artifacts/DEF-<h16>.json |
| HLD | docs/designs/HLD-<h16>.md | .insrc/artifacts/HLD-<h16>.json |
| LLD | docs/designs/LLD-<h16>-<storyId>.md | .insrc/artifacts/LLD-<h16>-<storyId>.json |
| PLAN | docs/plans/PLAN-<epic-slug>-<story-id>.md | .insrc/artifacts/PLAN-<epic-hash>-<story-id>.json |
| BUILD | docs/builds/BUILD-<h16>-<storyId>.md | .insrc/artifacts/BUILD-<h16>-<storyId>.json |
| CR | docs/reviews/CR-<h16>-<storyId>.md | .insrc/artifacts/CR-<h16>-<storyId>.json |
The gate is hard, not advisory. Every downstream workflow refuses to run against an unapproved (or rejected) upstream artifact. You approve from the CLI:
$ insrc workflow approve docs/defines/DEF-<h16>.md # unlocks design.epic
--no-tracker to opt out for one call.Independent review before approve
Before you approve a design artifact, a second pair of eyes checks it. insrc_review_step extracts the artifact's load-bearing premises and re-verifies each against the real source tree with deterministic probes (ripgrep re-derivations, file:line confirmations), then returns findings graded HIGH / MED / LOW with a block / warn / pass verdict. It is a genuine independent pass — not the model grading its own work — so it routinely catches a stale citation or an under-specified change the author missed. Resolve any HIGH/MED before approving; a block verdict withholds approval unless you pass an explicit override.
Open-question resolution
DEF, HLD, and LLD each surface the questions the stage could not settle. Resolving them is a two-beat contract:
- Optional at the end of the stage that raised them — you can synthesize with questions still open.
- Mandatory at the start of the next stage — the gate is lifted to stage-start and applies to the immediate-upstream artifact.
unresolvedOpenmust be empty before the next stage proceeds (src/workflow/questions.ts).
For each question you have three moves, recorded by recordResolution as a resolved | ignored | deferred status:
| Move | Meaning | Re-surfaces? |
|---|---|---|
| resolve | Record a decision (optionally a chosen option + rationale). | No — closed. |
| ignore | Leave it to implementer judgment; no decision recorded. | No — closed. |
| defer | Park it for a dedicated review pass. A deferred question is not open, so it never auto-blocks a stage. | Only in the review-deferred flow. |
When you resolve, the daemon generates candidate solution options through the shaper LLM (QuestionWithOptions / QuestionOption), so you pick from grounded choices rather than inventing them. Three insrc_workflow_step phases drive this: start runs the stage-start gate, resolve_question records one resolution, and review_deferred walks every parked question across the Epic's DEF + HLD + all LLDs (src/mcp/workflow-step/phases/{start,resolve-question,review-deferred}.ts).
Hierarchical IDs
Every Epic, Story, and Task carries a single canonical identifier that encodes its place in the hierarchy. The separator between levels is :, and the slug form simply swaps each : for - (toCanonical / toSlug in src/workflow/id.ts):
The date prefix plus 8-hex hash is separator-free, so E<8><8> parses unambiguously. The levels nest: an Epic is E…, a Story is E…:S…, and a Task is E…:S…:T…. This gives a clean two-way Epic → Story → Task relationship — parseWorkflowId reads any string back into its level, and parentId / epicOf walk upward. The minting helpers are epicWorkflowId, storyWorkflowId, and taskWorkflowId. The same ID appears in issue titles and in the stored docs, so a glance at an issue tells you exactly where it sits.
The GitHub tracker
The tracker is opt-in and never stores a token — it shells out to gh, which owns your OAuth session. When enabled, approval-time auto-push turns each approved artifact into a typed, natively-nested GitHub issue (src/workflow/tracker-auto.ts):
| On approval of… | Function | Creates |
|---|---|---|
| HLD | autoPushEpicOnHld | An Epic issue (native issue type Epic). |
| LLD | autoPushStoryOnLld | A Story issue, linked as a native sub-issue of its Epic. |
| PLAN | autoPushTasksOnPlan | One Task issue per PlanTask, each a native sub-issue of its Story. |
The nesting is real GitHub sub-issue linkage via ghLinkSubIssue, not just a label convention. Alongside the issues, the tracker commits the artifact docs and links the committed blobs back from each issue, and can seed a GitHub Projects v2 dashboard so you get Epic → Story → Task drill-down on a board. Issue-type names and Task pushing are configurable per repo via src/workflow/config/github.ts (epicIssueType / storyIssueType / taskIssueType, pushTasks, useMilestones); types fail open to untyped issues when the org lacks them.
Bootstrap the automatable GitHub setup once with:
$ insrc tracker setup # config + labels + issue types
$ insrc tracker setup --project # also create a Projects v2 board
runTrackerSetup (src/workflow/tracker/setup.ts) checks that gh is authenticated, verifies the OAuth scopes it needs (admin:org for issue types, project for the board), writes the resolved config, upserts the fixed tracker labels, ensures the Epic/Story issue types exist, and — behind --project — creates the board. Steps whose prerequisite is missing come back as manual with the exact command to run, so nothing is done behind your back.
~/.insrc/github.json entry, resolveGithubConfig returns { type: 'none' }, auto-push short-circuits, and the manual tracker.* workflows refuse with a clear message. You must explicitly enable it (via insrc tracker setup or by writing the config).The build stage
Build is a controller-driven MCP tool, insrc_build_step, with two phases (src/mcp/build-step/):
- implement — the daemon resolves the Task, runs the admission gate, then hands the controller LLM a templated implement prompt pointed at that Task. The daemon does not edit code here; your controller performs the edits.
- validate — the daemon itself runs a read-only agentic verdict session over the result.
The admission gate is strict: admitBuild (src/workflow/runners/build/admission.ts) requires an approved PLAN for the Story. An unapproved or rejected plan short-circuits to plan-unapproved; only an approved plan reaches the freshness/drift check. Because a PLAN carries no open questions, build inherits the decisions already settled at plan-start rather than re-litigating them.
# controller calls insrc_build_step { phase: 'implement', ref: 'E…:S001:T003' }
▚ admission plan E20260717185807ba:S001 approved · fresh
▚ task T003 add tag index to the query planner
▚ next implement → controller runs the templated prompt
# …controller edits files, then:
# insrc_build_step { phase: 'validate', ref: 'E…:S001:T003' }
▚ verdict read-only session · PASS
Code review & the completion gate
Once a build has produced its changes, insrc_code_review_step reviews the changed code — distinct from the design review above, which reviews the artifact. It grounds on the Story's real changed files and judges four independent dimensions, each returning block / warn / pass:
| Dimension | Asks |
|---|---|
| adherence | Does the code implement its approved PLAN + LLD — no silent departures from an approved decision? |
| conventions | Does it follow the repo's documented rules, naming, and idioms? |
| coverage | Are the acceptance criteria actually exercised by tests? |
| quality | Correctness, edge cases, and clarity of the changed code. |
The step runs start → judgements → done and writes a CR record. Crucially, approving the BUILD artifact is the code-review-gated completion act for the Story: under codeReview.enforce a block verdict — or no review having run at all — withholds completion unless you pass an explicit override. The gate defaults to advisory (off) and can be turned to enforcing in config.
Two ways to run
The same five stages run either from your MCP client turn-by-turn, or entirely inside the daemon over the socket. The only visible difference in the output is what meta.model gets stamped with.
| Client-driven | Daemon-driven | |
|---|---|---|
| Surface | insrc_workflow_step MCP tool | workflow.run IPC (Unix socket) |
| Who runs the LLM | Your client (Claude Code / Codex CLI) — every turn stays in your session | The daemon, via its resolved provider (config → client CLI → Ollama) |
| Shape | Multi-turn: start → plan → step → synthesize (plus resolve_question / review_deferred) | Single call that streams progress frames per phase, then a terminal frame |
| Progress | Per tool-call, in-band | Incremental frames streamed over the socket (decompose, plan-ready, …) |
meta.model | 'client' | The resolved provider id (e.g. ollama:qwen3… / claude / codex) |
workflow.run refuses a provider that cannot emit structured output.# in your MCP client:
> Use insrc_workflow_step to define an Epic for "Add tag filtering to todos".
phase=start → decomposer prompt
phase=plan → 4-step plan · context.assemble uses insrc_analyze_step
phase=step ×N → epic.frame → stories.compose → checklist.verify
phase=synthesize → docs/defines/DEF-3f9c…e2.md meta.model: client
$ insrc workflow approve docs/defines/DEF-3f9c…e2.md
approved · design.epic unlocked