yadflow 3.5.3 → 3.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -3
- package/README.md +2 -2
- package/cli/epic-state.mjs +52 -0
- package/cli/manifest.mjs +1 -0
- package/cli/next.mjs +5 -1
- package/cli/thread.mjs +8 -3
- package/package.json +2 -2
- package/skills/sdlc/config.yaml +13 -0
- package/skills/sdlc/install.sh +1 -1
- package/skills/sdlc/module-help.csv +3 -2
- package/skills/yad-backfill/SKILL.md +33 -5
- package/skills/yad-change/SKILL.md +23 -1
- package/skills/yad-epic/references/state-schema.md +44 -1
- package/skills/yad-reconcile/SKILL.md +7 -0
- package/skills/yad-stub/SKILL.md +149 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
# [3.6.0](https://github.com/abdelrahmannasr/yadflow/compare/v3.5.3...v3.6.0) (2026-07-03)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
###
|
|
4
|
+
### Features
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* wire stub anchors into change/backfill/reconcile flows ([54d20f1](https://github.com/abdelrahmannasr/yadflow/commit/54d20f10340831bca503f4246606b0181c74a2b0))
|
|
7
|
+
* **yad-stub:** mint stub genesis epics for brownfield defect intake ([7e6c4cd](https://github.com/abdelrahmannasr/yadflow/commit/7e6c4cd74f5a6e7f886115f7b9e5c0a571c2e408))
|
|
7
8
|
|
|
8
9
|
# [2.2.0](https://github.com/abdelrahmannasr/yadflow/compare/v2.1.0...v2.2.0) (2026-06-14)
|
|
9
10
|
|
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ In one pass it produces:
|
|
|
72
72
|
|
|
73
73
|
- **The `yad` CLI** — zero-dependency Node (`setup`, `gate`, `commit`, `open-pr`, `ship`, `repo`,
|
|
74
74
|
`thread`, `reconcile`, `usage`, `doctor`), run via `npx` or a global install.
|
|
75
|
-
- **
|
|
75
|
+
- **38 workflow skills** installed into your AI assistant — **Claude Code** (`.claude/`) first-class,
|
|
76
76
|
plus `.agents`, Zencoder, and OpenCode.
|
|
77
77
|
- **`.sdlc/` config** — the product hub, connected repos, reviewer roster, and tool connections
|
|
78
78
|
(design, testing, learning), all as plain JSON you can read and diff.
|
|
@@ -157,7 +157,7 @@ workflow-hygiene flags — derived read-only, so an EM can see how the team actu
|
|
|
157
157
|
- **[Terminology & workflow report](https://abdelrahmannasr.github.io/yadflow/)** — every term, artifact, gate, and skill on one illustrated page.
|
|
158
158
|
- **[TEAM-GUIDE.md](TEAM-GUIDE.md)** — the short, plain-language version for a developer team.
|
|
159
159
|
- **[docs/CLI.md](docs/CLI.md)** — the full `yad` command reference, the PR-driven gate, and `yad doctor` codes.
|
|
160
|
-
- **[docs/SKILLS.md](docs/SKILLS.md)** — the catalogue of all
|
|
160
|
+
- **[docs/SKILLS.md](docs/SKILLS.md)** — the catalogue of all 38 agent skills.
|
|
161
161
|
- **[docs/WALKTHROUGH.md](docs/WALKTHROUGH.md)** — the by-hand, end-to-end path through every phase.
|
|
162
162
|
- **[CONTRIBUTING.md](CONTRIBUTING.md)** · **[RESEARCH-NOTES.md](RESEARCH-NOTES.md)** · **[RELEASING.md](RELEASING.md)**
|
|
163
163
|
|
package/cli/epic-state.mjs
CHANGED
|
@@ -396,6 +396,18 @@ export function buildNextActions(buildStates = []) {
|
|
|
396
396
|
}));
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
// Classify a stub / backfill anchor from its ledger state — the SINGLE source of truth so `nextAction`
|
|
400
|
+
// and `preconditionsMet` can never disagree, even on a partially-applied `promote`. The `stub` check
|
|
401
|
+
// takes precedence over `backfill-done`, so a half-cleared promote (`kind:stub` still set while
|
|
402
|
+
// `currentStep` already moved) reads as still-a-stub — the conservative side (needs promoting). Returns
|
|
403
|
+
// `'stub'` (un-promoted anchor), `'documented'` (light-promoted anchor), or `null` (a normal epic).
|
|
404
|
+
export function backfillAnchorKind(state) {
|
|
405
|
+
if (!state) return null;
|
|
406
|
+
if (state.kind === 'stub' || state.currentStep === 'backfill-pending') return 'stub';
|
|
407
|
+
if (state.currentStep === 'backfill-done') return 'documented';
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
|
|
399
411
|
// PURE precondition guard. Is `stepId` runnable right now? A step is runnable iff every step BEFORE it
|
|
400
412
|
// in the chain is `done` and the step itself is not already `done`. With no state yet (greenfield), the
|
|
401
413
|
// only runnable steps are the entry authoring steps (analysis | epic). Used by `yad next --check`
|
|
@@ -405,6 +417,18 @@ export function preconditionsMet(state, stepId) {
|
|
|
405
417
|
const ok = stepId === 'epic' || stepId === 'analysis' || stepId === 'discovery';
|
|
406
418
|
return { ok, blockedBy: null, reason: ok ? 'entry step (no state seeded yet)' : `start with yad-epic — no epic state for '${stepId}'` };
|
|
407
419
|
}
|
|
420
|
+
// A stub anchor (backfill-pending) or a light-promoted anchor (backfill-done) has NO runnable front
|
|
421
|
+
// step: its front chain is intentionally left `blocked`. It evolves via `yad-backfill promote` / a
|
|
422
|
+
// threaded `yad-change`, never by authoring `epic` against the anchor itself — so the precondition
|
|
423
|
+
// guard must not green-light one (its blocked steps would otherwise read as "entry step ready").
|
|
424
|
+
const anchorKind = backfillAnchorKind(state);
|
|
425
|
+
if (anchorKind) {
|
|
426
|
+
const anchor = anchorKind === 'documented';
|
|
427
|
+
return { ok: false, blockedBy: null,
|
|
428
|
+
reason: anchor
|
|
429
|
+
? `${stepId} is not runnable — this is a documented backfill anchor; evolve it with yad-change`
|
|
430
|
+
: `${stepId} is not runnable — this is a stub (backfill pending); run yad-backfill then promote, or thread a change with yad-change` };
|
|
431
|
+
}
|
|
408
432
|
const i = state.steps.findIndex((s) => s.id === stepId);
|
|
409
433
|
if (i === -1) return { ok: false, blockedBy: null, reason: `unknown step '${stepId}'` };
|
|
410
434
|
if (state.steps[i].status === 'done') return { ok: false, blockedBy: null, reason: `${stepId} is already done` };
|
|
@@ -449,6 +473,26 @@ export function nextAction(ledger, { epic } = {}) {
|
|
|
449
473
|
why: dpr ? `review PR #${dpr.number} is open — sync its state to advance` : `${dstep.id} is open — create the review PR/MR` };
|
|
450
474
|
}
|
|
451
475
|
|
|
476
|
+
// A STUB genesis epic (yad-stub) or a light-promoted anchor: classified by the SHARED
|
|
477
|
+
// `backfillAnchorKind` helper (the same one `preconditionsMet` uses), so the two readers can never
|
|
478
|
+
// disagree — even on a partially-applied `promote`. A stub is (epic.md `stub:backfill-pending`) ⟺
|
|
479
|
+
// (state.kind:stub + currentStep:backfill-pending); `yad-backfill promote` clears ALL of these
|
|
480
|
+
// atomically (see state-schema.md), keeping this sentinel in step with `isStubEpic` (frontmatter).
|
|
481
|
+
const anchorKind = backfillAnchorKind(state);
|
|
482
|
+
if (anchorKind === 'stub') {
|
|
483
|
+
// No build half until backfilled + promoted — route to yad-backfill (not to authoring the epic),
|
|
484
|
+
// and remind that bugs can thread off it now with yad-change.
|
|
485
|
+
return { epicId, kind: 'backfill-pending', step: 'backfill-pending', status: 'stub',
|
|
486
|
+
why: 'stub epic (backfill pending) — document the code with yad-backfill then `yad-backfill promote` to make it real; thread bugs now with yad-change' };
|
|
487
|
+
}
|
|
488
|
+
if (anchorKind === 'documented') {
|
|
489
|
+
// `yad-backfill promote` documented the feature (verified) but did NOT wake the front chain (its docs
|
|
490
|
+
// live in the backfill spec). Terminal like `discovery-done` — no build half runs directly; the
|
|
491
|
+
// feature evolves by threading a change/defect off it.
|
|
492
|
+
return { epicId, kind: 'backfill-done', step: 'backfill-done', status: 'documented',
|
|
493
|
+
why: 'backfilled anchor (documented) — no build half runs directly; evolve it by threading a change/defect with yad-change' };
|
|
494
|
+
}
|
|
495
|
+
|
|
452
496
|
// The parallel test-cases track stays workable even once the epic is ready-for-build.
|
|
453
497
|
const tc = state.steps.find((s) => s.id === 'test-cases');
|
|
454
498
|
const tcOpen = !!tc && tc.status !== 'done' && tc.status !== 'blocked';
|
|
@@ -527,6 +571,14 @@ export function epicLineage(root, epic) {
|
|
|
527
571
|
};
|
|
528
572
|
}
|
|
529
573
|
|
|
574
|
+
// Is this a STUB genesis epic (minted by yad-stub as a brownfield thread anchor)? A stub is kind:feature
|
|
575
|
+
// but carries `stub: backfill-pending` in epic.md frontmatter until `yad-backfill promote` flips it to a
|
|
576
|
+
// real, verified epic (which clears the marker). Missing/greenfield-safe. Read by yad thread / yad-status
|
|
577
|
+
// / the reconciler to render "stub (backfill pending)" and never treat it as a fully-specced feature.
|
|
578
|
+
export function isStubEpic(root, epic) {
|
|
579
|
+
return readFrontmatter(path.join(epicRoot(root, epic), 'epic.md')).stub === 'backfill-pending';
|
|
580
|
+
}
|
|
581
|
+
|
|
530
582
|
// Walk `parent` to the thread root. Cycle- and missing-safe. Returns the genesis-first `chain`, the
|
|
531
583
|
// computed `rootId`, and a `broken` reason (missing parent dir, a cycle, or a denormalized `thread`
|
|
532
584
|
// cache that disagrees with the computed root) — the signal yad doctor / yad next --check report.
|
package/cli/manifest.mjs
CHANGED
package/cli/next.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// `yad next` — the unified next-step driver. Read-only: it never writes state or acts. It reads the
|
|
2
2
|
// file ledger and prints the ONE concrete, copy-pasteable next action (and a one-line why), so a user
|
|
3
|
-
// never has to remember which of the
|
|
3
|
+
// never has to remember which of the 38 skills / gate commands comes next. "Guide, don't act" — the
|
|
4
4
|
// front half still never auto-advances. Once an epic is `ready-for-build`, it reads each story's
|
|
5
5
|
// build-state and prints the next BUILD sub-step per repo (spec → tasks → implement → checks → engineer-review)
|
|
6
6
|
// plus the remaining chain — so the build half is guided too, not just hinted at.
|
|
@@ -91,6 +91,10 @@ function actionLine(a, { solo } = {}) {
|
|
|
91
91
|
}
|
|
92
92
|
case 'discovery-done':
|
|
93
93
|
return `invoke the ${c.bold('yad-epic')} skill ${c.dim('(seed a feature epic from roadmap.md)')}`;
|
|
94
|
+
case 'backfill-pending':
|
|
95
|
+
return `invoke the ${c.bold('yad-backfill')} skill ${c.dim('(document the code, then `yad-backfill promote` — or thread bugs now with yad-change)')}`;
|
|
96
|
+
case 'backfill-done':
|
|
97
|
+
return `invoke the ${c.bold('yad-change')} skill ${c.dim('(documented anchor — evolve it by threading a change/defect off it)')}`;
|
|
94
98
|
default:
|
|
95
99
|
return c.dim('nothing to do');
|
|
96
100
|
}
|
package/cli/thread.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import path from 'node:path';
|
|
|
6
6
|
import fs from 'node:fs';
|
|
7
7
|
import { c, log, ok, info, warn, hand, readJSON, exists } from './lib.mjs';
|
|
8
8
|
import {
|
|
9
|
-
epicRoot, isValidEpicId, epicLineage, readFrontmatter,
|
|
9
|
+
epicRoot, isValidEpicId, epicLineage, readFrontmatter, isStubEpic,
|
|
10
10
|
resolveThread, threadEpics, resolveCurrentArtifacts, resolveCurrentStories, THREAD_ARTIFACT_BASES,
|
|
11
11
|
} from './epic-state.mjs';
|
|
12
12
|
|
|
@@ -54,6 +54,7 @@ export function threadSummary(root, threadOrEpic) {
|
|
|
54
54
|
id, kind: lin.kind, parent: lin.parent, inherits: lin.inherits,
|
|
55
55
|
currentStep: state?.currentStep || 'unseeded',
|
|
56
56
|
sealed: sealedEpic(root, id),
|
|
57
|
+
stub: isStubEpic(root, id),
|
|
57
58
|
depth: change?.depth || null,
|
|
58
59
|
defect: change?.defect || null,
|
|
59
60
|
brokenThread: resolveThread(root, id).broken || null,
|
|
@@ -86,7 +87,8 @@ export async function runThread(root, { epic, json = false } = {}) {
|
|
|
86
87
|
for (const r of [...roots].sort()) {
|
|
87
88
|
const s = threadSummary(root, r);
|
|
88
89
|
const debt = s.openDebt.length ? c.red(` ⚠ ${s.openDebt.length} open reconcile-debt`) : '';
|
|
89
|
-
|
|
90
|
+
const stub = s.nodes[0]?.stub ? c.yellow(' [stub · backfill pending]') : '';
|
|
91
|
+
log(` ${c.bold(r)} ${c.dim(`${s.nodes.length} epic(s)`)}${stub}${debt}`);
|
|
90
92
|
}
|
|
91
93
|
log(c.dim('\n yad thread <epic> show one thread in full'));
|
|
92
94
|
return;
|
|
@@ -100,8 +102,9 @@ export async function runThread(root, { epic, json = false } = {}) {
|
|
|
100
102
|
for (const n of s.nodes) {
|
|
101
103
|
const tag = KIND_TAG[n.kind] || n.kind;
|
|
102
104
|
const seal = n.sealed ? c.dim(' [sealed]') : '';
|
|
105
|
+
const stub = n.stub ? c.yellow(' [stub · backfill pending]') : '';
|
|
103
106
|
const dep = n.depth ? c.dim(` ${n.depth}`) : '';
|
|
104
|
-
log(` • ${c.bold(n.id)} ${tag}${dep} ${c.dim('@ ' + n.currentStep)}${seal}`);
|
|
107
|
+
log(` • ${c.bold(n.id)} ${tag}${dep} ${c.dim('@ ' + n.currentStep)}${seal}${stub}`);
|
|
105
108
|
if (n.parent) log(c.dim(` parent: ${n.parent} inherits: [${n.inherits.join(', ') || '—'}]`));
|
|
106
109
|
if (n.defect) log(c.dim(` defect: ${n.defect.severity || '?'} · escaped@${n.defect.escape_stage || '?'} · ${n.defect.root_cause || ''}`));
|
|
107
110
|
if (n.brokenThread) log(c.red(` ✗ ${n.brokenThread}`));
|
|
@@ -161,6 +164,8 @@ export async function runReconcile(root, { action = 'check', thread = null } = {
|
|
|
161
164
|
log('');
|
|
162
165
|
info('refresh is advisory: open a reconcile change-epic with `yad-change` (kind: change) threaded to');
|
|
163
166
|
info('the affected feature, then pay any open debt (update artifacts + add a regression test).');
|
|
167
|
+
info('for shipped brownfield code with NO epic at all, anchor it first with `yad-stub`, then thread');
|
|
168
|
+
info('the change/defect off that stub (and run `yad-backfill` to make the anchor real).');
|
|
164
169
|
}
|
|
165
170
|
if (action === 'wire') {
|
|
166
171
|
log('');
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yadflow",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Yadflow — the gated, team, multi-repo SDLC: author → review → build with a PR-driven review gate and a zero-dependency `yad` CLI (setup, gate, commit, open-pr, ship, repo, thread, reconcile). A BMAD module +
|
|
3
|
+
"version": "3.6.0",
|
|
4
|
+
"description": "Yadflow — the gated, team, multi-repo SDLC: author → review → build with a PR-driven review gate and a zero-dependency `yad` CLI (setup, gate, commit, open-pr, ship, repo, thread, reconcile). A BMAD module + 38 yad-* skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "AbdelRahman Nasr",
|
|
7
7
|
"license": "MIT",
|
package/skills/sdlc/config.yaml
CHANGED
|
@@ -146,6 +146,7 @@ build:
|
|
|
146
146
|
pack_flags: "--compress --include <globs> --include-logs --style markdown" # one feature at a time; Secretlint by default
|
|
147
147
|
spec_location: "specs/backfill/<feature>/spec.md" # draft (verified: false) until a human approves
|
|
148
148
|
gate_scope: touched-features # block a change only until the features it touches have approved specs
|
|
149
|
+
promote: yad-stub # `yad-backfill promote EP-<slug>` flips a brownfield stub epic (change.stub) to a real, verified feature epic once its spec is approved
|
|
149
150
|
|
|
150
151
|
# Code context (yad-connect-repos) — the front/"brain" phases are made code-aware. Code repos are
|
|
151
152
|
# connected to the product hub once (or any time a new repo is added), and an AI-readable picture of
|
|
@@ -317,6 +318,18 @@ change:
|
|
|
317
318
|
# behaviour on a sealed epic -> the change must land in a new threaded change-epic (the front half is
|
|
318
319
|
# forced to stay current; staleness is unshippable).
|
|
319
320
|
seal_on: all-stories-shipped
|
|
321
|
+
# Brownfield stub anchor (yad-stub): an already-built feature with NO epic can't be a change parent
|
|
322
|
+
# (yad-change requires a real parent; lineage-check rejects a missing one). yad-stub mints the smallest
|
|
323
|
+
# real thread anchor — a genesis epic.md (kind:feature, thread:self) marked `stub: backfill-pending` /
|
|
324
|
+
# `verified: false`, with a state.json carrying `kind: stub` + `currentStep: backfill-pending`. A change
|
|
325
|
+
# threaded off a stub inherits only what exists (boundHash: null for the undocumented surface bases) and
|
|
326
|
+
# writes NO pointer-lock; change.json records `parentStub: true`. `yad-backfill promote` flips the stub
|
|
327
|
+
# to verified (clears the marker), at which point contract protection begins for the thread.
|
|
328
|
+
stub:
|
|
329
|
+
marker: backfill-pending # epic.md `stub:` value while the anchor is un-promoted
|
|
330
|
+
state_kind: stub # state.json top-level `kind` + `currentStep: backfill-pending` sentinel
|
|
331
|
+
allow_stub_parent: true # a change/defect MAY thread off an un-promoted stub (default on)
|
|
332
|
+
promote_by: yad-backfill # `yad-backfill promote EP-<slug>` documents + flips the stub to real
|
|
320
333
|
hotfix:
|
|
321
334
|
ship_first: true # a hotfix may run the build half BEFORE its front gates approve
|
|
322
335
|
debt_blocks_next_change: true # but opens reconcile-debt.json; the next change on the thread is blocked until paid
|
package/skills/sdlc/install.sh
CHANGED
|
@@ -11,7 +11,7 @@ set -euo pipefail
|
|
|
11
11
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
12
12
|
cd "$ROOT"
|
|
13
13
|
|
|
14
|
-
SKILLS=(yad-discovery yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-sync-repos yad-connect-design yad-connect-testing yad-connect-learning yad-connect-docs yad-docs yad-docs-overview yad-docs-sync yad-learn yad-spec yad-implement yad-checks yad-pr-template yad-hub-bridge yad-commit yad-open-pr yad-ship yad-engineer-review yad-backfill yad-run yad-review-gate yad-review-companion yad-pair-review yad-status yad-report yad-change yad-timeline yad-defects yad-reconcile)
|
|
14
|
+
SKILLS=(yad-discovery yad-analysis yad-epic yad-architecture yad-ui yad-stories yad-test-cases yad-connect-repos yad-sync-repos yad-connect-design yad-connect-testing yad-connect-learning yad-connect-docs yad-docs yad-docs-overview yad-docs-sync yad-learn yad-spec yad-implement yad-checks yad-pr-template yad-hub-bridge yad-commit yad-open-pr yad-ship yad-engineer-review yad-backfill yad-run yad-review-gate yad-review-companion yad-pair-review yad-status yad-report yad-change yad-timeline yad-defects yad-reconcile yad-stub)
|
|
15
15
|
|
|
16
16
|
# Skills removed in a later release: this installer only refreshes names still in SKILLS, so a
|
|
17
17
|
# rerun would otherwise leave a dropped skill sitting in the IDE dirs. Purge any lingering copy
|
|
@@ -24,7 +24,7 @@ SDLC Workflow,yad-open-pr,Open PR/MR,OP,"Build-half helper: open a code-repo tas
|
|
|
24
24
|
SDLC Workflow,yad-ship,Commit + Open PR/MR,SP2,"Build-half helper: commit AND open the task PR/MR in one step — a thin orchestration over yad-commit then yad-open-pr. Commits the staged atomic change by the conventions, then pushes the branch and opens the PR/MR from the committed template with the roster auto-assigned. The PR step runs ONLY if the commit lands (a failed commit, tripped guard, or --dry-run stops before pushing). Drives the yad ship CLI. Never merges; never auto-advances.",,{type: feat|fix|...} {message: subject} {ai: <tool|none>} {repo: <name>} {risk: low|medium|high} {contract-change: true|false},3-build,yad-pr-template,,false,<repo>/,one commit + one PR/MR
|
|
25
25
|
SDLC Workflow,yad-hub-bridge,Hub Review Bridge,HB,"The templated PR/MR bridge for the front-half review gate: when the product hub has a platform (.sdlc/hub.json), open a review PR/MR on the hub for an authored artifact, set required reviewers/labels from the routing rule, and provide the read-only gh/glab recipes yad-review-gate's sync uses to pull platform comments + approvals into the file ledger. Local-user auth, no stored tokens; file ledger stays the source of truth; degrades to file-only when no platform/CLI. Never auto-advances.",,{epic: EP-<slug>} {artifact: epic.md|architecture.md|ui-design.md|stories/} {action: open|route},1-front,yad-review-gate,yad-review-gate,false,epics/EP-<slug>/.sdlc/,hub-prs.json
|
|
26
26
|
SDLC Workflow,yad-engineer-review,Engineer Review & Merge,ER,"Build-half Step E: wire an advisory AI first-pass (CodeRabbit) on the PR, record the human engineer review with the same human_approve discipline as the front gates (owner + 1 reviewer, escalating to domain owners on high risk / contract / auth / payments), and on merge record the ship in epics/<epic>/.sdlc/build-log.json and update the story state. AI review is advisory, never the authority; the human owns the merge. Never auto-advances.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {task: T0N} {repo: <repo>} {action: ai-review|approve|ship},3-build,yad-ship,,false,epics/EP-<slug>/.sdlc/,build-log.json story-status
|
|
27
|
-
SDLC Workflow,yad-backfill,Backfill Specs,BF,"Build-half Step G: generate specs for already-built features in an existing repo. Confirm Repomix (npx repomix CLI), pack ONE feature (compress + git logs, secret-scan), feed to AI with a 'describe what exists, do not invent' prompt, write a DRAFT spec marked verified: false. Human approval (reuse yad-review-gate) makes it real. Boundary auto-proposed and human-confirmed. A change is blocked only until the features it touches have approved specs. Never auto-advances.",,{repo: <repo>} {feature: <name + globs>} {action: pack|draft|approve|gate},3-build,,,false,demo-repos/<repo>/specs/backfill/<feature>/,spec.md backfill-check.sh
|
|
27
|
+
SDLC Workflow,yad-backfill,Backfill Specs,BF,"Build-half Step G: generate specs for already-built features in an existing repo. Confirm Repomix (npx repomix CLI), pack ONE feature (compress + git logs, secret-scan), feed to AI with a 'describe what exists, do not invent' prompt, write a DRAFT spec marked verified: false. Human approval (reuse yad-review-gate) makes it real. Boundary auto-proposed and human-confirmed. A change is blocked only until the features it touches have approved specs. The promote action flips a brownfield stub epic (yad-stub) to a real, verified feature epic once its backfill spec is approved. Never auto-advances.",,{repo: <repo>} {feature: <name + globs>} {action: pack|draft|approve|gate|promote} {epic: EP-<slug> (promote)},3-build,,,false,demo-repos/<repo>/specs/backfill/<feature>/,spec.md backfill-check.sh
|
|
28
28
|
SDLC Workflow,yad-run,Run (Automation),RN,"Phase 4 orchestrator: drive a story's back-half loop (spec→tasks→implement→checks) in one code repo, reading each step's automation dial from build-state — on machine_advance it advances on its own, on human_approve it stops for a human. Records every run in the trust log. Realizes Step B (a clean checks pass auto-advances to the engineer review when earned; any FAIL / scope overrun / contract touch HALTS). set-dial earns/reverts a step's automation (machine_advance gated by the trust threshold; front states and engineer-review refused); kill/unkill toggles the system-wide kill switch. Front states and the human merge gate never auto-advance.",,{epic: EP-<slug>} {story: EP-<slug>-S0N} {repo: <repo>} {action: run|set-dial|kill|unkill} {step: <back-step>} {to: human_approve|machine_advance},4-automate,yad-spec,yad-engineer-review,false,epics/EP-<slug>/.sdlc/,build-state/<story-id>.json trust-log.json
|
|
29
29
|
SDLC Workflow,yad-status,SDLC Status,SS,"Read-only: print the full front-state chain, per-step dials, contract lock, story repo tags, and pending approvals at the active gate. For stories in the build half, also print each back-half step's automation dial and status, the trust record (runs / % approved-unchanged / earned vs gathering evidence), and the system-wide kill-switch state.",,{epic: EP-<slug>},1-front,,,false,,
|
|
30
30
|
SDLC Workflow,yad-report,Report Issue,RP,"Self issue reporter: when a yad flow breaks, file a well-formed bug in the upstream yadflow repo with AUTO-SCRUBBED diagnostics (yadflow/node/os version, tool present+authenticated booleans, hub platform enum, the YadError code/hint, a path-scrubbed message, and the failing command + flag NAMES only) — never absolute paths, hostnames, git URLs, repo names, roster logins/emails, epic/story IDs, branch names, or flag values. Searches open issues first to avoid duplicates, previews the exact payload, and asks before posting to the public repo; files via an authenticated gh/glab or a prefilled issues/new URL fallback. Also offered automatically after an unexpected failure (interactive only; YAD_NO_REPORT=1 disables). Never a gate, never touches epic state.",,{message: one-line summary},,,,false,,
|
|
@@ -35,4 +35,5 @@ SDLC Workflow,yad-docs-sync,Docs Sync,DY,"Maintenance/CI: keep the generated doc
|
|
|
35
35
|
SDLC Workflow,yad-change,Change/Defect Intake,CH,"Phase 6 post-lock change management: the INTAKE + TRIAGE step of a feature thread. Classifies the change DEPTH (defect-fix / behavioral-no-surface / contract-surface / new-capability), seeds a NEW EP-<slug> change-epic threaded to its parent (lineage frontmatter kind/parent/thread/inherits/supersedes + a state.json whose inherited steps are pre-marked done and only the changed steps run; a pointer-lock contract-lock.json when architecture is inherited), and records the intake in change.json (escape_stage + root_cause for defects). For hotfixes it records the ship-first exception and opens reconcile-debt.json. Never auto-advances — hands off to the normal authoring skills + yad-review-gate.",,{parent: EP-<slug>} {title: one-line} {kind: change|defect|hotfix} {origin: production|staging|qa|review} {severity: sev1..sev4} {description: text} {affected: artifacts},1-front,,yad-review-gate,false,epics/EP-<slug>/,epic.md state.json change.json reconcile-debt.json contract-lock.json
|
|
36
36
|
SDLC Workflow,yad-timeline,Feature Timeline,TL,"Render a feature THREAD (its linked epics, genesis->changes->defects) as an evolution view (the vendored React/Vite/Tailwind shell HTML + a TIMELINE.md summary) AND resolve the inheritance chain into the authoritative current artifact set (thread-resolved.md: the winning source per artifact + the resolved contract-lock hash) — the composed source-of-truth AI/humans read for the next change. Reads frontmatter lineage + each change.json + build-log.json. An OUTPUT ENRICHMENT — never a gate; never mutates state.",,{thread: EP-<genesis>} {action: generate|deploy},,,,false,epics/EP-<genesis>/,timeline-site/ thread-resolved.md TIMELINE.md
|
|
37
37
|
SDLC Workflow,yad-defects,Quality-Gap Report,DF,"Generate a per-epic AND per-thread defect/bug report (same vendored shell + DEFECTS.md). Walks the thread for every kind:defect change-epic + each change.json defect block + shipped regressions in build-log.json, aggregates by escape_stage (the SDLC gate that should have caught it) and root_cause, and visualizes WHERE quality gaps systematically come from (e.g. % of thread defects that escaped at the test-cases gate) so the team can harden the originating stage. An OUTPUT ENRICHMENT — never a gate; never mutates state.",,{epic: EP-<slug> | thread: EP-<genesis>} {action: generate|deploy},,,,false,epics/EP-<slug>/,defects-site/ DEFECTS.md
|
|
38
|
-
SDLC Workflow,yad-reconcile,Change Reconciler,RE,"Maintenance/CI (mirrors yad-docs-sync — never a gate): detect post-lock DRIFT/ORPHANS — shipped code or a repo HEAD advance (the repos.json syncedHead-vs-current-HEAD rule) with NO owning change-epic in any thread — plus open hotfix reconcile debt, and report which thread drifted and why. refresh
|
|
38
|
+
SDLC Workflow,yad-reconcile,Change Reconciler,RE,"Maintenance/CI (mirrors yad-docs-sync — never a gate): detect post-lock DRIFT/ORPHANS — shipped code or a repo HEAD advance (the repos.json syncedHead-vs-current-HEAD rule) with NO owning change-epic in any thread — plus open hotfix reconcile debt, and report which thread drifted and why. refresh points at yad-change to open a reconcile change-epic (or yad-stub first, to anchor orphan brownfield code that has no epic at all) — never silent; wire commits advisory CI ([skip ci] + concurrency, like yad-docs-sync). The actual merge BLOCK is the lineage-check / reconcile-debt gates; this only discovers.",,{action: check|refresh|wire} {thread: EP-<genesis>},,,,false,epics/EP-<genesis>/.sdlc/,(report) reconcile-debt.json yad-reconcile.yml
|
|
39
|
+
SDLC Workflow,yad-stub,Stub Genesis Epic,SG,"Phase 6 brownfield helper: mint a STUB genesis epic for an already-built feature that has no epic in the hub, so a defect/change can thread off it TODAY (yad-change requires a real parent and dead-ends without one). Creates the smallest real thread anchor — a tiny epic.md (kind:feature, thread:self, verified:false, stub:backfill-pending) + a seeded state.json (kind:stub / currentStep:backfill-pending) + empty ledgers — never inventing behaviour. Defects thread off it immediately (gates pass; the bug list is derived by the thread rollup); yad-backfill + its promote step later flip it into a real feature epic. Never auto-advances.",,{feature: <name>} {repos: [<repo>]} {description: one-line},1-front,,yad-change,false,epics/EP-<slug>/,epic.md state.json approvals.json comments.json
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: yad-backfill
|
|
3
|
-
description: 'Build-half Step G of the gated SDLC — backfill: generate specs for already-built features in an existing repo so new work does not break them. Confirm Repomix (the one true CLI subprocess: npx repomix), pack ONE feature at a time (compress + git logs, secret-scan), feed it to AI with a "describe what exists, do not invent" prompt, and write a DRAFT spec marked unverified. Require human approval (reuse yad-review-gate) before the spec counts as real. Boundary is auto-proposed from the project convention and human-confirmed. A change is blocked only until the features IT touches have approved specs. Use when the user says "backfill specs", "document an existing feature",
|
|
3
|
+
description: 'Build-half Step G of the gated SDLC — backfill: generate specs for already-built features in an existing repo so new work does not break them. Confirm Repomix (the one true CLI subprocess: npx repomix), pack ONE feature at a time (compress + git logs, secret-scan), feed it to AI with a "describe what exists, do not invent" prompt, and write a DRAFT spec marked unverified. Require human approval (reuse yad-review-gate) before the spec counts as real. Boundary is auto-proposed from the project convention and human-confirmed. A change is blocked only until the features IT touches have approved specs. The `promote` action flips a brownfield stub epic (minted by yad-stub, so defects could thread off it) to a real, verified feature epic once its backfill spec is approved. Use when the user says "backfill specs", "document an existing feature", "spec the legacy code", or "promote the stub epic".'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SDLC — Backfill (existing-code specs)
|
|
@@ -26,7 +26,9 @@ have approved specs — never the whole repo at once.
|
|
|
26
26
|
|
|
27
27
|
- `repo` — the existing code repo to backfill.
|
|
28
28
|
- `feature` — the feature name (and its file globs, e.g. `src/<feature>/**`).
|
|
29
|
-
- `action` — `pack` | `draft` | `approve` | `gate` (default `pack`).
|
|
29
|
+
- `action` — `pack` | `draft` | `approve` | `gate` | `promote` (default `pack`).
|
|
30
|
+
- `epic` — `promote` only: the stub epic `EP-<slug>` (minted by `yad-stub`) to flip to real once the
|
|
31
|
+
feature's backfill spec is approved.
|
|
30
32
|
|
|
31
33
|
## On Activation
|
|
32
34
|
|
|
@@ -74,9 +76,33 @@ that feature's spec is `verified: true`. It is **per touched feature** — a cha
|
|
|
74
76
|
not blocked by an unverified feature B. Forward-spec'd features (those with their own `specs/<story>/`)
|
|
75
77
|
are not this gate's concern.
|
|
76
78
|
|
|
77
|
-
### Step 6 —
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
### Step 6 — `promote` (flip a stub epic → real, once its spec is approved)
|
|
80
|
+
When a brownfield feature was anchored with a **stub genesis epic** (`yad-stub`) so that defects could
|
|
81
|
+
thread off it, `promote` is what makes that anchor real — run it once the feature's backfill spec is
|
|
82
|
+
`verified: true`. Given `epic EP-<slug>` (a stub: `stub: backfill-pending` / `verified: false`):
|
|
83
|
+
- **Verify readiness:** the matching `specs/backfill/<feature>/spec.md` must exist and be
|
|
84
|
+
`verified: true`. If not, STOP — approve the spec first (Step 4).
|
|
85
|
+
- **Clear the stub markers ATOMICALLY.** A stub is marked in two files at once — `epic.md`
|
|
86
|
+
(`stub: backfill-pending`) **and** `state.json` (top-level `kind: "stub"` + `currentStep:
|
|
87
|
+
"backfill-pending"`). Promote must clear **all** of them together, or `yad next` (reads `state.json`)
|
|
88
|
+
and `yad thread` / `yad-status` (read `epic.md`) will disagree about whether the epic is still a stub.
|
|
89
|
+
- **`epic.md`:** set `verified: true`, **remove** the `stub:` marker, and add a `backfill:` block linking
|
|
90
|
+
the approved spec, e.g. `backfill: { spec: specs/backfill/<feature>/spec.md, promoted: <YYYY-MM-DD> }`.
|
|
91
|
+
- **`state.json`:** **remove** the top-level `kind: "stub"`, and move `currentStep` off the
|
|
92
|
+
`backfill-pending` sentinel (per the promote flavour below).
|
|
93
|
+
- **Light promote (default):** the feature's documentation lives in the approved backfill spec — do NOT
|
|
94
|
+
wake the front chain. Set `state.json` `currentStep: "backfill-done"` (a terminal sentinel, like
|
|
95
|
+
`discovery-done`): the epic is now a real, verified anchor and its later evolution threads normally with
|
|
96
|
+
`yad-change`. `yad next` then reports it as a documented anchor, not a pending stub.
|
|
97
|
+
- **Full promote (opt-in):** to bring the feature fully under the front half and lock a real contract,
|
|
98
|
+
"wake" the state chain — set `currentStep: "epic"`, `epic` step `status: "in_progress"` — then run
|
|
99
|
+
`yad-epic` → `yad-architecture` → … the normal way. This re-locks a contract that subsequent thread
|
|
100
|
+
changes will inherit.
|
|
101
|
+
- Never auto-advances; a human confirms the promotion.
|
|
102
|
+
|
|
103
|
+
### Step 7 — Stop (no auto-advance)
|
|
104
|
+
Report the packed feature, the draft path (or the approval / promotion), and what is still unverified.
|
|
105
|
+
Nothing auto-advances; a human owns the approval.
|
|
80
106
|
|
|
81
107
|
## Hard rules (build plan §G, Cross-cutting)
|
|
82
108
|
|
|
@@ -88,4 +114,6 @@ auto-advances; a human owns the approval.
|
|
|
88
114
|
## Reference
|
|
89
115
|
- The "describe what exists" prompt, the spec shape, and the gate: `references/backfill.md`.
|
|
90
116
|
- The human approval discipline reused: `../yad-review-gate/SKILL.md`.
|
|
117
|
+
- Minting the stub epic that `promote` later flips to real (brownfield thread anchor):
|
|
118
|
+
`../yad-stub/SKILL.md`.
|
|
91
119
|
- Repomix flags: `RESEARCH-NOTES.md` §3.
|
|
@@ -54,6 +54,13 @@ lineage is broken (a cycle, or a `thread` cache ≠ the computed root) — fix t
|
|
|
54
54
|
parent is a **genesis epic not yet migrated** (no `kind:`), migrate it now: add `kind: feature` and
|
|
55
55
|
`thread: <its own id>` to its `epic.md` (a one-line, non-gated frontmatter add).
|
|
56
56
|
|
|
57
|
+
**Missing parent (brownfield) — never silent.** If the requested `parent` does **not exist at all**
|
|
58
|
+
because the feature was built before it had an epic, do NOT dead-end: point the user at **`yad-stub`** to
|
|
59
|
+
mint a stub genesis epic (a minimal `kind: feature` thread anchor) for that feature first, then re-run
|
|
60
|
+
this skill with that stub as the `parent`. A change MUST still thread to a real parent — `yad-stub` just
|
|
61
|
+
creates the smallest real one so the defect can be captured now (the `yad-reconcile` → anchor → change
|
|
62
|
+
discipline).
|
|
63
|
+
|
|
57
64
|
The new epic's `thread` = the parent's thread (the genesis id). Its `parent` = the given `parent` (the
|
|
58
65
|
immediate predecessor — usually the current tip; if the parent is not the tip, see "concurrent changes"
|
|
59
66
|
in `references/triage.md`).
|
|
@@ -132,11 +139,25 @@ When `architecture` is **inherited**, materialize the **pointer-lock** `.sdlc/co
|
|
|
132
139
|
There is no `contract.md` in the change-epic, so the surface cannot drift, and `contract-check` passes
|
|
133
140
|
unchanged because the hash is identical. (Exact recipe + field shapes: `references/triage.md`.)
|
|
134
141
|
|
|
142
|
+
**Stub parent (brownfield, no locked surface yet).** When the `parent` is a **stub**
|
|
143
|
+
(`stub: backfill-pending` / `verified: false`, minted by `yad-stub`), it has no `architecture.md` /
|
|
144
|
+
`contract.md` / `ui-design.md` to inherit — the surface has not been documented yet. So for the bases the
|
|
145
|
+
stub lacks, mark the inherited steps `"inherited": true, "inheritedFrom": "<stub>", "boundHash": null`
|
|
146
|
+
(a `null` boundHash is treated as "nothing locked upstream → no drift" by the gate predicate, so the step
|
|
147
|
+
passes and never blocks), and write **NO** `contract-lock.json` (there is no surface to point at — the
|
|
148
|
+
child never touches `specs/*/contracts/**`, so `contract-check` passes trivially). Contract protection on
|
|
149
|
+
this thread begins only after `yad-backfill promote` documents and locks the feature. Record
|
|
150
|
+
`"parentStub": true` in `change.json` so it is auditable that the change threaded off an undocumented
|
|
151
|
+
stub. Prefer the `defect-fix` / `behavioral-no-surface` depth against a stub — a `contract-surface`
|
|
152
|
+
change against an undocumented feature should wait until the stub is promoted (backfilled + a real
|
|
153
|
+
contract locked).
|
|
154
|
+
|
|
135
155
|
### Step 6 — Write `.sdlc/change.json` (intake + triage record)
|
|
136
156
|
Record the intake: `epicId`, `thread`, `parent`, `kind`, `depth`, `intakeBy`, `intakeDate`, `title`,
|
|
137
157
|
`description`, `affectedArtifacts`, `reauthors`, `inherits`, and for a defect/hotfix the `defect` block
|
|
138
158
|
(`origin`, `severity`, `escape_stage`, `root_cause`). This is what `yad-defects` reads to attribute the
|
|
139
|
-
defect to the gate that should have caught it.
|
|
159
|
+
defect to the gate that should have caught it. Add `"parentStub": true` when the parent is an
|
|
160
|
+
un-promoted stub (Step 5) — omit it (or `false`) otherwise.
|
|
140
161
|
|
|
141
162
|
### Step 7 — Hotfix only: record the ship-first exception + open reconcile debt
|
|
142
163
|
If `kind: hotfix`, the build half MAY run before these front gates approve (severity demands it). Record
|
|
@@ -165,6 +186,7 @@ Report: the new `EP-<slug>`, its thread + parent, the re-author-vs-inherit split
|
|
|
165
186
|
- **Never auto-advances.** This skill seeds + records; humans author and approve via the normal gates.
|
|
166
187
|
|
|
167
188
|
## Reference
|
|
189
|
+
- Minting a stub genesis epic when the feature has no epic (brownfield): `../yad-stub/SKILL.md`.
|
|
168
190
|
- Depth triage details, the exact seeding shape, the pointer-lock recipe, genesis migration, and the
|
|
169
191
|
concurrent-change (re-parent) rule: `references/triage.md`.
|
|
170
192
|
- The lineage frontmatter + ledger schemas: `../yad-epic/references/state-schema.md` (Phase 6).
|
|
@@ -263,6 +263,47 @@ locked `state.json` step shape. Genesis epics are backfilled once with `kind: fe
|
|
|
263
263
|
| `severity` | `sev1` … `sev4` | **defect/hotfix only.** |
|
|
264
264
|
| `escape_stage` | an SDLC stage id (`stories`, `test-cases`, `architecture`, …) | **defect/hotfix only.** The gate that *should* have caught it — feeds the `yad-defects` quality report. |
|
|
265
265
|
| `root_cause` | short tag | **defect/hotfix only.** e.g. `missing-negative-test`. |
|
|
266
|
+
| `stub` | `backfill-pending` | **stub genesis only** (`yad-stub`). A brownfield feature anchored so a change can thread off it before it is documented. Cleared by `yad-backfill promote`. |
|
|
267
|
+
| `verified` | `true` \| `false` | `false` on a stub genesis (not yet documented/human-authored); `yad-backfill promote` sets it `true`. Absent ⇒ treated as a normal (verified) epic. |
|
|
268
|
+
|
|
269
|
+
## Stub genesis epics (brownfield anchors — `yad-stub`)
|
|
270
|
+
|
|
271
|
+
In a brownfield repo not every already-built feature has an epic, so a defect/change has no parent to
|
|
272
|
+
thread from (`yad-change` requires one; `lineage-check` rejects a missing parent). `yad-stub` mints the
|
|
273
|
+
smallest **real** node — a **stub genesis epic** — so the bug can be captured now and formalized later.
|
|
274
|
+
|
|
275
|
+
A stub is a normal genesis (`kind: feature`, `thread == id`, no `parent`) whose `epic.md` carries
|
|
276
|
+
`stub: backfill-pending` + `verified: false` and whose `state.json` uses a **sentinel**, mirroring
|
|
277
|
+
`EP-discovery` / `discovery-done`:
|
|
278
|
+
- top-level `kind: "stub"` and `currentStep: "backfill-pending"`;
|
|
279
|
+
- the **same 10-step front chain** as a normal epic, every step `status: "blocked"` (so `validateState`
|
|
280
|
+
passes and `promote` can "wake" the chain into normal authoring with no re-seed);
|
|
281
|
+
- empty `approvals.json` / `comments.json`; **no** `contract-lock.json` (no surface locked yet).
|
|
282
|
+
|
|
283
|
+
`nextAction` routes a stub to a `backfill-pending` action (`yad-backfill` → `yad-backfill promote`), never
|
|
284
|
+
to authoring. A change threaded off a stub inherits only what exists — the undocumented surface bases are
|
|
285
|
+
marked `inherited: true` with `boundHash: null` (the gate predicate reads `null` as "nothing locked → no
|
|
286
|
+
drift → pass"), no pointer-lock is written, and `change.json` records `parentStub: true`.
|
|
287
|
+
|
|
288
|
+
**The stub invariant (two files, kept in lockstep).** A stub is encoded in *both* `epic.md`
|
|
289
|
+
(`stub: backfill-pending`) *and* `state.json` (top-level `kind: "stub"` + `currentStep:
|
|
290
|
+
"backfill-pending"`), because two readers use different sources: `isStubEpic` / `yad thread` / `yad-status`
|
|
291
|
+
read the frontmatter, while `nextAction` / `yad next` is pure-ledger and reads `state.json`. So:
|
|
292
|
+
|
|
293
|
+
> **A stub ⟺ `epic.md stub:backfill-pending` AND `state.kind:stub` AND `currentStep:backfill-pending`.**
|
|
294
|
+
> Any promote MUST clear all three atomically, or the two readers disagree.
|
|
295
|
+
|
|
296
|
+
`yad-backfill promote` enforces this: it sets `epic.md` `verified: true`, removes `stub:`, links the
|
|
297
|
+
approved backfill spec, **and** rewrites `state.json` — removing `kind: "stub"` and moving `currentStep`
|
|
298
|
+
off the sentinel:
|
|
299
|
+
- **light promote (default)** → `currentStep: "backfill-done"`, a **terminal sentinel** (like
|
|
300
|
+
`discovery-done`): the feature is a real, verified anchor documented by its backfill spec; `nextAction`
|
|
301
|
+
reports "documented anchor — evolve it by threading a change/defect", never a pending stub, and no build
|
|
302
|
+
half runs directly against it;
|
|
303
|
+
- **full promote (opt-in)** → `currentStep: "epic"`, `epic.status: "in_progress"`, to run the normal front
|
|
304
|
+
half and lock a real contract.
|
|
305
|
+
|
|
306
|
+
From promotion on, the thread's contract protection is live.
|
|
266
307
|
|
|
267
308
|
## Inherited steps in `state.json`
|
|
268
309
|
|
|
@@ -324,7 +365,9 @@ Intake + triage record, one per change/defect/hotfix epic (sibling of `approvals
|
|
|
324
365
|
|
|
325
366
|
`depth` ∈ `defect-fix | behavioral-no-surface | contract-surface | new-capability` (`config.yaml`
|
|
326
367
|
`change.depths`). `defect` is `null` for a plain `change`; `hotfix` is `{ "shipFirst": true }` only for
|
|
327
|
-
a `hotfix`.
|
|
368
|
+
a `hotfix`. `parentStub: true` is added when the epic threads off an un-promoted stub genesis
|
|
369
|
+
(`yad-stub`) — a brownfield feature not yet documented, so no contract surface is inherited yet.
|
|
370
|
+
Thread-level rollups (`yad-timeline` / `yad-defects`) are **derived** — walk every epic
|
|
328
371
|
sharing `thread` and read each `change.json`; there is no duplicated thread registry.
|
|
329
372
|
|
|
330
373
|
## `reconcile-debt.json`
|
|
@@ -49,6 +49,12 @@ shipped, and pay any open debt (update the artifacts + add a regression test, th
|
|
|
49
49
|
`reconcile-debt.json` entry `status: paid`). It never seeds the epic itself — opening a change-epic is a
|
|
50
50
|
human, triaged act (`yad-change` Step 2).
|
|
51
51
|
|
|
52
|
+
**Orphan code with no epic at all (brownfield):** when the drift is shipped code that belongs to a
|
|
53
|
+
feature with **no owning epic in any thread**, there is nothing to thread a change off yet. Point the
|
|
54
|
+
human at **`yad-stub`** first — mint a stub genesis epic (a minimal `kind: feature` thread anchor) for
|
|
55
|
+
that feature — then thread the reconcile change off that stub (and run `yad-backfill` +
|
|
56
|
+
`yad-backfill promote` to make the anchor real). The reconciler never mints the stub itself.
|
|
57
|
+
|
|
52
58
|
### Step 3 — `wire` (advisory CI, no block)
|
|
53
59
|
Commit an advisory CI job that runs `yad reconcile --check` on push, carrying `[skip ci]` on any commit
|
|
54
60
|
it makes and a concurrency group — the same loop-prevention `yad-docs-sync` uses. The job **reports**;
|
|
@@ -72,4 +78,5 @@ the reconciler is advisory.
|
|
|
72
78
|
- The drift/refresh/wire discipline this mirrors: `../yad-docs-sync/SKILL.md`.
|
|
73
79
|
- The thread model + ledgers: `../yad-epic/references/state-schema.md` (Phase 6).
|
|
74
80
|
- The change-epic this hands off to: `../yad-change/SKILL.md`.
|
|
81
|
+
- Anchoring brownfield code that has no epic (before a change can thread off it): `../yad-stub/SKILL.md`.
|
|
75
82
|
- The gates that block at merge: `../yad-checks/` (lineage-check, epic-open, reconcile-debt).
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yad-stub
|
|
3
|
+
description: 'Phase 6 brownfield helper — mint a STUB genesis epic for an already-built feature that has no epic in the hub, so a defect/change can thread off it TODAY. In a brownfield repo not every feature has an epic; yad-change requires a real parent and dead-ends without one. This skill creates the smallest real thread anchor — a tiny epic.md (kind:feature, thread:self, verified:false, stub:backfill-pending) + a seeded state.json + empty ledgers — never inventing behaviour. Defects thread off it immediately (gates pass, the bug list is derived by the thread rollup), and yad-backfill + its promote step later fill/flip it into a real feature epic. Never auto-advances. Use when the user says "there is no epic for this feature", "file a bug on a legacy feature", "anchor a brownfield feature", or when yad-change / yad-reconcile point here because a parent epic is missing.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SDLC — Stub Genesis Epic (Phase 6, the brownfield thread anchor)
|
|
7
|
+
|
|
8
|
+
**Goal:** Give an **already-built feature that has no epic** the smallest node that is still *real* — a
|
|
9
|
+
**stub genesis epic** — so a defect or change can thread off it right now (`yad-change` needs a real
|
|
10
|
+
parent), the gates pass, and the list of linked bugs is **derived for free** by the thread rollup. The
|
|
11
|
+
stub is a **thread anchor, not a spec**: it invents no behaviour. It stays `verified: false` until
|
|
12
|
+
`yad-backfill` documents the feature and its `promote` step flips the stub into a real feature epic.
|
|
13
|
+
|
|
14
|
+
This is the missing connective tissue in a brownfield adoption: `yad-backfill` produces a *draft spec in
|
|
15
|
+
the code repo*, and `yad-reconcile` *detects* shipped code with no owning epic — but neither mints the
|
|
16
|
+
hub epic a defect must thread from. `yad-stub` does exactly that, and only that.
|
|
17
|
+
|
|
18
|
+
## Conventions
|
|
19
|
+
|
|
20
|
+
- `{project-root}` resolves from the product hub. Epic artifacts live under
|
|
21
|
+
`{project-root}/epics/EP-<slug>/` (build plan §6), exactly like a normal genesis epic.
|
|
22
|
+
- **A stub is NOT a reserved-empty id.** An epic in yad *is* a directory + `epic.md`; the tooling skips
|
|
23
|
+
any epic dir lacking `epic.md` (`threadEpics`) and `lineage-check` rejects a parent that is not a real
|
|
24
|
+
`epic.md`. So the stub is a real (if minimal) genesis — that is what makes it a valid parent.
|
|
25
|
+
- The stub is a **genesis** (`kind: feature`, `thread == id`, no `parent`) — the root of a new thread.
|
|
26
|
+
Lineage frontmatter, the sentinel state, and the `stub`/`verified` fields are defined in
|
|
27
|
+
`../yad-epic/references/state-schema.md` (Phase 6 section).
|
|
28
|
+
- Speak in the configured `communication_language`; write documents in `document_output_language`.
|
|
29
|
+
|
|
30
|
+
## Inputs
|
|
31
|
+
|
|
32
|
+
- `feature` — **required.** The already-built feature's name (→ slugged into `EP-<slug>`).
|
|
33
|
+
- `repos` — the code repo(s) this feature lives in (for the backfill boundary later).
|
|
34
|
+
- `description` — one line: what the feature is (as built). **Do not** describe design or invent
|
|
35
|
+
behaviour — a stub records that the feature exists, nothing more.
|
|
36
|
+
|
|
37
|
+
## On Activation
|
|
38
|
+
|
|
39
|
+
### Step 1 — Confirm there really is no epic (auto-propose, human-confirm)
|
|
40
|
+
Check `{project-root}/epics/` for an existing epic that already owns this feature (a genesis or any
|
|
41
|
+
thread). **If one exists, STOP** and point at `yad-change` (`--parent <that epic>`) — a stub is only for
|
|
42
|
+
a feature with *no* epic at all. If the code is already forward-spec'd or has a `specs/<story>/`, it is
|
|
43
|
+
not a stub case either. Confirm the brownfield-no-epic situation with the human before creating anything.
|
|
44
|
+
|
|
45
|
+
### Step 2 — Derive the stub epic id (engine-assigned, never by hand)
|
|
46
|
+
Derive `EP-<slug>` where `slug` is **2–4 lowercase words** from the feature name (e.g.
|
|
47
|
+
`EP-legacy-billing`). `EP-discovery` is **reserved** — never use it. Check `epics/` for collisions;
|
|
48
|
+
append a distinguishing word if the slug exists. **The id is assigned once and never renamed** (a rename
|
|
49
|
+
breaks every downstream link) — so pick from the best-known feature name and accept it as permanent.
|
|
50
|
+
|
|
51
|
+
### Step 3 — Open the authoring branch
|
|
52
|
+
Open `epic/EP-<slug>` per the shared "Authoring branches" procedure
|
|
53
|
+
(`../yad-epic/references/state-schema.md`): git-safe (skip with a note if `{project-root}` is not a git
|
|
54
|
+
work tree), check out if it exists, else create from the hub's default branch.
|
|
55
|
+
|
|
56
|
+
### Step 4 — Write the stub `epic.md` (thread anchor — never invent behaviour)
|
|
57
|
+
Write `{project-root}/epics/EP-<slug>/epic.md` using EXACTLY this shape:
|
|
58
|
+
|
|
59
|
+
```markdown
|
|
60
|
+
---
|
|
61
|
+
id: EP-<slug>
|
|
62
|
+
status: draft
|
|
63
|
+
kind: feature # genesis / thread root — a valid parent for lineage-check + threads
|
|
64
|
+
thread: EP-<slug> # thread == id for a genesis
|
|
65
|
+
verified: false # not a real, human-authored epic yet — a stub awaiting backfill
|
|
66
|
+
stub: backfill-pending # the honest marker; cleared on promote
|
|
67
|
+
origin: brownfield
|
|
68
|
+
owner:
|
|
69
|
+
repos: [<the code repos this feature lives in>]
|
|
70
|
+
created: <YYYY-MM-DD>
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Feature (stub)
|
|
74
|
+
<!-- one line: what this already-built feature IS. No design, no invented behaviour. -->
|
|
75
|
+
|
|
76
|
+
## Known issues
|
|
77
|
+
<!-- Do not hand-maintain a list. `yad thread EP-<slug>` derives every defect/change threaded off this
|
|
78
|
+
stub from lineage — it is always current. -->
|
|
79
|
+
|
|
80
|
+
## Backfill
|
|
81
|
+
<!-- To make this real: run `yad-backfill` for <repo> (globs for this feature), get the spec approved
|
|
82
|
+
(verified: true), then `yad-backfill promote EP-<slug>`. -->
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Leave `owner` for the human to set. Set `repos` to the code repo(s) the feature lives in.
|
|
86
|
+
|
|
87
|
+
### Step 5 — Seed the stub `state.json` (a `backfill-pending` sentinel)
|
|
88
|
+
Create `{project-root}/epics/EP-<slug>/.sdlc/state.json`. It carries the top-level marker
|
|
89
|
+
`kind: "stub"` and the sentinel `currentStep: "backfill-pending"`, and the **same 10-step front chain**
|
|
90
|
+
as a normal epic (`yad-epic` Step 5) but with **every step `status: "blocked"`** — so the state is valid
|
|
91
|
+
(`validateState` needs a non-empty `steps` + a string `currentStep`) and `promote` can later "wake" it
|
|
92
|
+
into normal authoring with zero re-seeding.
|
|
93
|
+
|
|
94
|
+
```json
|
|
95
|
+
{
|
|
96
|
+
"epicId": "EP-<slug>",
|
|
97
|
+
"createdAt": "<YYYY-MM-DD>",
|
|
98
|
+
"kind": "stub",
|
|
99
|
+
"currentStep": "backfill-pending",
|
|
100
|
+
"steps": [
|
|
101
|
+
{ "id": "epic", "type": "author", "artifact": "epic.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
|
|
102
|
+
{ "id": "epic-review", "type": "review+approve", "artifact": "epic.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
|
|
103
|
+
{ "id": "architecture", "type": "author", "artifact": "architecture.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
|
|
104
|
+
{ "id": "architecture-review","type": "review+approve", "artifact": "architecture.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": ["contract"] },
|
|
105
|
+
{ "id": "ui-design", "type": "author", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
|
|
106
|
+
{ "id": "ui-design-review", "type": "review+approve", "artifact": "ui-design.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
|
|
107
|
+
{ "id": "stories", "type": "author", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
|
|
108
|
+
{ "id": "stories-review", "type": "review+approve", "artifact": "stories/", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
|
|
109
|
+
{ "id": "test-cases", "type": "author", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] },
|
|
110
|
+
{ "id": "test-cases-review", "type": "review+approve", "artifact": "test-cases.md", "assistance": "review", "automation": "human_approve", "locked": true, "status": "blocked", "risk_tags": [] }
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Also create the empty ledgers `{.sdlc/approvals.json}` and `{.sdlc/comments.json}` (each `[]`) and the
|
|
116
|
+
`reviews/` directory. **Do NOT** write a `contract-lock.json` — a stub has no locked surface yet.
|
|
117
|
+
|
|
118
|
+
### Step 6 — Stop; hand off (NO auto-advance)
|
|
119
|
+
Report the new `EP-<slug>`, that it is a **stub (backfill pending)**, and the two next moves:
|
|
120
|
+
- **File bugs now:** `yad-change` (`--parent EP-<slug>`, `kind: defect|change`) — the defect threads off
|
|
121
|
+
the stub, its gates pass, and `yad thread EP-<slug>` lists it. A defect off a stub inherits only what
|
|
122
|
+
exists (the stub `epic.md`), re-authors its own stories/test-cases, and locks no contract (there is no
|
|
123
|
+
surface yet — see `../yad-change/SKILL.md`).
|
|
124
|
+
- **Make it real later:** `yad-backfill` for the code repo, then `yad-backfill promote EP-<slug>` to flip
|
|
125
|
+
the stub to `verified: true`.
|
|
126
|
+
|
|
127
|
+
Front states do not auto-advance. Suggest `yad next EP-<slug>` (prints the backfill-pending guidance) and
|
|
128
|
+
`yad thread EP-<slug>` to see the anchor and everything threaded off it.
|
|
129
|
+
|
|
130
|
+
## Hard rules
|
|
131
|
+
|
|
132
|
+
- **A stub is an anchor, not a spec.** Never invent behaviour in `epic.md`. It records only that the
|
|
133
|
+
feature exists so a change can thread off it.
|
|
134
|
+
- **Only for a feature with NO epic.** If any epic already owns the feature, use `yad-change` instead.
|
|
135
|
+
- **Never `verified: true` here.** A stub is `verified: false` / `stub: backfill-pending` until
|
|
136
|
+
`yad-backfill promote` flips it — approval is earned by documenting the real code, not by minting.
|
|
137
|
+
- **You never implement directly against a stub.** It owns no `stories/`, so there is nothing to
|
|
138
|
+
implement against directly — real work threads off it as a change/defect epic (which has its own
|
|
139
|
+
stories). (This is a convention of the empty stub, not a hard gate: `lineage-check` passes a
|
|
140
|
+
`kind: feature` genesis and `epic-open` treats a story-less epic as un-sealed, so neither blocks a
|
|
141
|
+
story mistakenly linked to the stub — keep the stub story-less.)
|
|
142
|
+
- **Never auto-advances.** This skill seeds the anchor and stops; humans thread changes and run backfill.
|
|
143
|
+
|
|
144
|
+
## Reference
|
|
145
|
+
- The lineage frontmatter, the `stub`/`verified` fields, and the `kind: "stub"` /
|
|
146
|
+
`currentStep: "backfill-pending"` sentinel: `../yad-epic/references/state-schema.md` (Phase 6).
|
|
147
|
+
- Threading a defect/change off the stub: `../yad-change/SKILL.md`.
|
|
148
|
+
- Documenting the code + promoting the stub to real: `../yad-backfill/SKILL.md`.
|
|
149
|
+
- Detecting brownfield code with no owning epic: `../yad-reconcile/SKILL.md`.
|