A reusable named Workflow (.claude/workflows/) that runs the
brainstorm-to-ready groundwork this repo does before any feature code:
- Understand: read the target story (or find the NEXT-PLAN target) +
scout relevant .dev/reference projects for the concern
- Analyze: one agent per reference project — how it handles the concern,
gaps vs our planned approach, recommendations (the fiber/Go step,
generalized)
- Audit: story-format/frontmatter/plans-no-raw-code + dependency-graph /
status-board consistency
- Consolidate: settle open forks (KISS defaults), fold reference gaps as
locked requirements, acceptance-criteria gaps, go/no-go on readiness
Parameterized via args {story?, concern?, references?}; grounds every
agent in on-disk files. Does the parallelizable research half; the
fork-settling stays an interactive brainstorm. Invoke:
Workflow({name:'prebuild-feature', args:{...}}) or /workflows.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 2bfbb0ca5ca17cb2d1ead39f93aa42aeb50b1639)
180 lines
9.2 KiB
JavaScript
180 lines
9.2 KiB
JavaScript
export const meta = {
|
|
name: 'prebuild-feature',
|
|
description: 'Pre-build research fan-out: ground a feature story, compare references, audit story discipline, produce a go/no-go brief',
|
|
whenToUse: 'Before writing code for a feature/iteration — run the brainstorm-to-ready groundwork as parallel research and get a consolidated pre-build brief',
|
|
phases: [
|
|
{ title: 'Understand', detail: 'read the target story + scout relevant .dev/reference projects' },
|
|
{ title: 'Analyze', detail: 'one agent per reference project vs the feature concern' },
|
|
{ title: 'Audit', detail: 'story-format/frontmatter + dependency-graph/status-board consistency' },
|
|
{ title: 'Consolidate', detail: 'settle open forks, fold gaps, go/no-go on readiness' },
|
|
],
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
* Encodes the ritual this repo follows BEFORE any code lands on a feature:
|
|
* understand the story -> ground the forks in the actual runtime ->
|
|
* compare against .dev/reference implementations for gaps -> lock the
|
|
* decisions with KISS defaults -> acceptance criteria + deps/status.
|
|
* It does the *parallelizable research* half and hands back a brief; the
|
|
* fork-settling itself stays an interactive brainstorm (human in the loop).
|
|
*
|
|
* Invoke: Workflow({ name: 'prebuild-feature', args: {
|
|
* story: 'docs/stories/runtime-v2/09-in-process-tls.md', // optional
|
|
* concern: 'outbound TLS client integration', // optional
|
|
* references: ['fiber', 'go'] } }) // optional
|
|
* With no args it locates the current NEXT PLAN target itself.
|
|
* ------------------------------------------------------------------------- */
|
|
|
|
const story = (args && args.story) || null
|
|
const concern = (args && args.concern) || null
|
|
const givenRefs = (args && Array.isArray(args.references)) ? args.references : null
|
|
const REF_CAP = 6 // keep the fan-out bounded (medium workflow-size guideline)
|
|
|
|
const UNDERSTAND_SCHEMA = {
|
|
type: 'object',
|
|
properties: {
|
|
storyPath: { type: 'string' },
|
|
concern: { type: 'string' },
|
|
readiness: { type: 'string' },
|
|
lockedDecisions: { type: 'array', items: { type: 'string' } },
|
|
openForks: { type: 'array', items: { type: 'string' } },
|
|
acceptanceCriteria: { type: 'string' },
|
|
outOfScopePresent: { type: 'boolean' },
|
|
summary: { type: 'string' },
|
|
},
|
|
required: ['storyPath', 'concern', 'readiness', 'openForks', 'summary'],
|
|
}
|
|
|
|
const SCOUT_SCHEMA = {
|
|
type: 'object',
|
|
properties: {
|
|
references: { type: 'array', items: { type: 'string' } },
|
|
rationale: { type: 'string' },
|
|
},
|
|
required: ['references'],
|
|
}
|
|
|
|
const REF_SCHEMA = {
|
|
type: 'object',
|
|
properties: {
|
|
project: { type: 'string' },
|
|
howItHandles: { type: 'string' },
|
|
gapsInOurApproach: { type: 'array', items: { type: 'string' } },
|
|
recommendations: { type: 'array', items: { type: 'string' } },
|
|
},
|
|
required: ['project', 'howItHandles'],
|
|
}
|
|
|
|
const AUDIT_SCHEMA = {
|
|
type: 'object',
|
|
properties: {
|
|
area: { type: 'string' },
|
|
ok: { type: 'boolean' },
|
|
issues: { type: 'array', items: { type: 'string' } },
|
|
},
|
|
required: ['area', 'ok', 'issues'],
|
|
}
|
|
|
|
const BRIEF_SCHEMA = {
|
|
type: 'object',
|
|
properties: {
|
|
ready: { type: 'boolean' },
|
|
goNoGo: { type: 'string' },
|
|
unsettledForks: { type: 'array', items: { type: 'string' } },
|
|
recommendedDefaults: { type: 'array', items: { type: 'string' } },
|
|
gapsToFold: { type: 'array', items: { type: 'string' } },
|
|
acceptanceGaps: { type: 'array', items: { type: 'string' } },
|
|
blockers: { type: 'array', items: { type: 'string' } },
|
|
summary: { type: 'string' },
|
|
},
|
|
required: ['ready', 'goNoGo', 'summary'],
|
|
}
|
|
|
|
const CONVENTIONS =
|
|
'Repo discipline: story frontmatter is the ONLY source of status (status + readiness); ' +
|
|
'story docs carry NO code blocks (plans-no-raw-code); brainstorm to readiness:ready with ' +
|
|
'decisions LOCKED and Given/When/Then acceptance criteria + an out-of-scope list before any ' +
|
|
'code lands; docs live under ./docs; the dependency graph is docs/00-dependency-graph.md and ' +
|
|
'the status board docs/stories/00-status.md. Read CLAUDE.md and docs/stories/00-status.md to confirm.'
|
|
|
|
phase('Understand')
|
|
|
|
// The target story: use args.story, else let the agent find the NEXT PLAN target.
|
|
const storyClause = story
|
|
? `The target story is ${story}.`
|
|
: 'No story path was given — read docs/stories/00-status.md, find the current in-progress / NEXT-PLAN feature, and use its story file.'
|
|
const concernClause = concern ? `The feature concern is: ${concern}.` : 'Infer the feature concern from the story.'
|
|
|
|
const [understanding, scout] = await parallel([
|
|
() => agent(
|
|
`${storyClause} ${concernClause}\n\n` +
|
|
`Read that story and the repo conventions. ${CONVENTIONS}\n\n` +
|
|
`Report, as data: the resolved story path, the feature concern in one line, the story's ` +
|
|
`readiness, the decisions already LOCKED, the OPEN forks still unsettled, whether ` +
|
|
`Given/When/Then acceptance criteria and an out-of-scope list are present, and a short summary. ` +
|
|
`Do not propose fixes — just report what is and isn't settled.`,
|
|
{ label: 'understand-story', phase: 'Understand', agentType: 'general-purpose', schema: UNDERSTAND_SCHEMA },
|
|
),
|
|
() => agent(
|
|
(givenRefs
|
|
? `The caller named these reference projects: ${givenRefs.join(', ')}. Confirm each exists under .dev/reference/ and return the ones that do.`
|
|
: `List .dev/reference/ (\`ls .dev/reference\`). ${concernClause} `) +
|
|
`Pick the reference projects most relevant to studying this concern (at most ${REF_CAP}), newest/most-relevant first. ` +
|
|
`Return their directory names and a one-line rationale. Grounded in what actually exists on disk.`,
|
|
{ label: 'scout-references', phase: 'Understand', agentType: 'general-purpose', schema: SCOUT_SCHEMA },
|
|
),
|
|
])
|
|
|
|
const theConcern = (understanding && understanding.concern) || concern || 'the feature concern'
|
|
const theStory = (understanding && understanding.storyPath) || story || '(the NEXT-PLAN story)'
|
|
let refs = (scout && scout.references) || givenRefs || []
|
|
refs = refs.slice(0, REF_CAP)
|
|
if (refs.length === 0) log('No reference projects identified — skipping the reference-analysis fan-out.')
|
|
|
|
// One research batch: a reference-analysis agent per project + two audit agents,
|
|
// all independent, all needed by the consolidation barrier.
|
|
const research = await parallel([
|
|
...refs.map((r) => () => agent(
|
|
`Analyze how the reference project .dev/reference/${r} handles "${theConcern}". ` +
|
|
`Read its actual source (grep/read the relevant files). Report: how it handles the concern; ` +
|
|
`where writeonce's planned approach in ${theStory} has GAPS or missing safeguards versus it; ` +
|
|
`and concrete recommendations. Be specific and cite files. Return raw data, not prose for a human.`,
|
|
{ label: `ref:${r}`, phase: 'Analyze', agentType: 'general-purpose', schema: REF_SCHEMA },
|
|
)),
|
|
() => agent(
|
|
`Audit ${theStory} against the repo's STORY DISCIPLINE. ${CONVENTIONS}\n` +
|
|
`Check: frontmatter carries status + readiness; NO code fences in the doc; decisions are LOCKED ` +
|
|
`(not vague); Given/When/Then acceptance criteria present; out-of-scope list present. ` +
|
|
`Report each violation as an issue; ok=true only if clean.`,
|
|
{ label: 'audit:story-format', phase: 'Audit', agentType: 'general-purpose', schema: AUDIT_SCHEMA },
|
|
),
|
|
() => agent(
|
|
`Audit consistency between ${theStory}, the dependency graph (docs/00-dependency-graph.md) and the ` +
|
|
`status board (docs/stories/00-status.md) for "${theConcern}". Check: the feature's node/row exists, ` +
|
|
`its status matches the story frontmatter, and blockers/dependencies named in the story appear in the ` +
|
|
`graph. Report mismatches as issues; ok=true only if consistent.`,
|
|
{ label: 'audit:deps-status', phase: 'Audit', agentType: 'general-purpose', schema: AUDIT_SCHEMA },
|
|
),
|
|
])
|
|
|
|
const refResults = research.slice(0, refs.length).filter(Boolean)
|
|
const audits = research.slice(refs.length).filter(Boolean)
|
|
|
|
phase('Consolidate')
|
|
|
|
const brief = await agent(
|
|
`You are consolidating a PRE-BUILD brief for "${theConcern}" (story ${theStory}) — the go/no-go before code.\n\n` +
|
|
`Understanding of the story:\n${JSON.stringify(understanding, null, 2)}\n\n` +
|
|
`Reference analyses (gaps vs our approach):\n${JSON.stringify(refResults, null, 2)}\n\n` +
|
|
`Story-discipline + deps/status audits:\n${JSON.stringify(audits, null, 2)}\n\n` +
|
|
`Produce the brief: the OPEN forks still to settle (each with a recommended KISS default); ` +
|
|
`the gaps from the reference analyses worth FOLDING IN as locked requirements before build; ` +
|
|
`any acceptance-criteria gaps; blockers; and a clear go/no-go on whether the story is truly ` +
|
|
`ready to build. ready=true only if the forks are settled, the audits are clean, and the ` +
|
|
`reference gaps are either folded in or explicitly deferred. Ground every point in the inputs above.`,
|
|
{ label: 'consolidate-brief', phase: 'Consolidate', effort: 'high', schema: BRIEF_SCHEMA },
|
|
)
|
|
|
|
log(`Pre-build brief for ${theConcern}: ${brief && brief.goNoGo ? brief.goNoGo : '(no verdict)'}`)
|
|
|
|
return { story: theStory, concern: theConcern, understanding, references: refResults, audits, brief }
|