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.

brainstorm define design.epic design.story plan build code-review

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.

Triage routes first. 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 epicdefine; 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.
StageProducesWhat it isopenQuestions
brainstormSPEC — spec artifactOptional 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 bugfixISSUE — defect recordFirst 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
defineDEF — definitionFrames the Epic: problem statement, non-goals, assumptions, constraints, and 1–10 Stories.yes
design.epicHLD — high-level designOne per Epic: chosen framework, shared contracts, Story boundaries, rollout phases, alternatives considered.yes
design.storyLLD — low-level designOne per Story: detailed contract (APIs + data model), error paths, test strategy, migration.yes
planPLAN — ordered TasksA sequenced list of implementable Tasks for a Story. Carries no open questions — the decisions were already made upstream.none
buildcode + validationThe controller implements each Task and the daemon runs a read-only verdict.
code-reviewCR — review recordAfter the build, an independent review of the changed code across four dimensions. Approving the BUILD is the review-gated completion act for the Story.
DEF, HLD, and LLD each carry an 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.

Brainstorm is deliberately un-automated on the model side — you run the convergence, so the human stays in the loop on every fork. Drive it with 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 magnitudesmall 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.

triage · bugfix ─▸ issue ─▸ locate parent ─┬─ small ─▸ build └─ sized ─▸ design.story ─▸ plan ─▸ build both branches complete through the code-review gate
PieceWhat it does
issue stageCaptures 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-locatorAttaches 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 routeA 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 issueWhere 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}/.

If a recorded GitHub-issue reference is ever lost or missing where one should exist, the flow prompts you with next steps — retry the record, enter the ref manually, re-discover it by label, or skip — rather than silently degrading.

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.

ArtifactDoc pathCanonical JSON
SPECdocs/specs/SPEC-<slug>.md.insrc/artifacts/SPEC-<h16>.json
ISSUEdocs/issues/ISSUE-<slug>.md.insrc/artifacts/ISSUE-<h16>.json
DEFdocs/defines/DEF-<h16>.md.insrc/artifacts/DEF-<h16>.json
HLDdocs/designs/HLD-<h16>.md.insrc/artifacts/HLD-<h16>.json
LLDdocs/designs/LLD-<h16>-<storyId>.md.insrc/artifacts/LLD-<h16>-<storyId>.json
PLANdocs/plans/PLAN-<epic-slug>-<story-id>.md.insrc/artifacts/PLAN-<epic-hash>-<story-id>.json
BUILDdocs/builds/BUILD-<h16>-<storyId>.md.insrc/artifacts/BUILD-<h16>-<storyId>.json
CRdocs/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:

bash
$ insrc workflow approve docs/defines/DEF-<h16>.md   # unlocks design.epic
On approval the tracker can auto-push and commit. Approving an HLD auto-creates the Epic issue; approving an LLD auto-creates the Story issue; approving a PLAN can push the Task issues. The artifact docs are committed and linked back from each issue. Repeat approves are idempotent — an existing ref is detected and the push is skipped. Pass --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:

For each question you have three moves, recorded by recordResolution as a resolved | ignored | deferred status:

MoveMeaningRe-surfaces?
resolveRecord a decision (optionally a chosen option + rationale).No — closed.
ignoreLeave it to implementer judgment; no decision recorded.No — closed.
deferPark 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):

Canonical E<YYYYMMDD><hash8>:S<nnn>:T<nnn> Slug E<YYYYMMDD><hash8>-S<nnn>-T<nnn> Example E20260717185807ba:S001:T003 └── Epic ──┘ Story Task

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…FunctionCreates
HLDautoPushEpicOnHldAn Epic issue (native issue type Epic).
LLDautoPushStoryOnLldA Story issue, linked as a native sub-issue of its Epic.
PLANautoPushTasksOnPlanOne Task issue per PlanTask, each a native sub-issue of its Story.
Epic E20260717185807ba #typed: Epic ▸ Story E…:S001 #native sub-issue · typed: Story ▸ Task E…:S001:T001 #native sub-issue · typed: Task ▸ Task E…:S001:T002 ▸ Story E…:S002 ▸ Task E…:S002:T001

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:

bash
$ 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.

The tracker is opt-in even when your git origin points at GitHub. With no matching ~/.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/):

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.

insrc_build_step — implement
# 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:

DimensionAsks
adherenceDoes the code implement its approved PLAN + LLD — no silent departures from an approved decision?
conventionsDoes it follow the repo's documented rules, naming, and idioms?
coverageAre the acceptance criteria actually exercised by tests?
qualityCorrectness, edge cases, and clarity of the changed code.

The step runs startjudgementsdone 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.

The review grounds on a fresh index of the changed code. If the index queue is still catching up, the step says so rather than emitting a hollow pass over an empty graph — you either wait for a fresh index or abort. An honest “grounding was thin” beats a fabricated “looks good.”

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-drivenDaemon-driven
Surfaceinsrc_workflow_step MCP toolworkflow.run IPC (Unix socket)
Who runs the LLMYour client (Claude Code / Codex CLI) — every turn stays in your sessionThe daemon, via its resolved provider (config → client CLI → Ollama)
ShapeMulti-turn: startplanstepsynthesize (plus resolve_question / review_deferred)Single call that streams progress frames per phase, then a terminal frame
ProgressPer tool-call, in-bandIncremental frames streamed over the socket (decompose, plan-ready, …)
meta.model'client'The resolved provider id (e.g. ollama:qwen3… / claude / codex)
Both paths write the same artifacts to the same locations and honour the same approval gates. Client-driven is the more accurate default (no subprocess spawn, no separate billing path); daemon-driven is the hands-off runner for long jobs where you want streamed progress. Structured-output support is required — workflow.run refuses a provider that cannot emit structured output.
insrc_workflow_step — define
# 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

Where next