@ericrisco/rsc 3.7.5 → 4.0.1

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.
Files changed (62) hide show
  1. package/README.md +16 -9
  2. package/manifest.json +48 -40
  3. package/package.json +1 -1
  4. package/scripts/lib/domains.js +1 -1
  5. package/scripts/lib/onboarding-apply.js +1 -1
  6. package/scripts/lib/onboarding.js +7 -7
  7. package/scripts/lib/retired-skills.js +4 -0
  8. package/scripts/lib/review-findings.js +3 -0
  9. package/scripts/lib/review-ledger.js +187 -0
  10. package/scripts/lib/risk.js +68 -0
  11. package/scripts/lib/spec-gate.js +10 -1
  12. package/scripts/rsc.js +104 -6
  13. package/scripts/spec-gate.js +13 -3
  14. package/skills/analyze/SKILL.md +3 -3
  15. package/skills/asdd/SKILL.md +183 -0
  16. package/skills/{sdd → asdd}/evals/README.md +10 -10
  17. package/skills/{sdd → asdd}/evals/cases.yaml +5 -5
  18. package/skills/{sdd → asdd}/references/model-routing.md +2 -2
  19. package/skills/building-agents/SKILL.md +1 -1
  20. package/skills/clarify/SKILL.md +59 -55
  21. package/skills/clarify/evals/cases.yaml +6 -7
  22. package/skills/constitution/SKILL.md +30 -16
  23. package/skills/constitution/evals/README.md +1 -1
  24. package/skills/constitution/evals/cases.yaml +1 -1
  25. package/skills/debug/SKILL.md +3 -3
  26. package/skills/fastapi/SKILL.md +1 -1
  27. package/skills/flutter/SKILL.md +1 -1
  28. package/skills/ftd/SKILL.md +26 -10
  29. package/skills/ftd/evals/cases.yaml +1 -1
  30. package/skills/ftd/evals/routing.json +4 -4
  31. package/skills/go/SKILL.md +1 -1
  32. package/skills/implement/SKILL.md +147 -132
  33. package/skills/implement/evals/cases.yaml +4 -2
  34. package/skills/implement/references/per-task-review.md +26 -24
  35. package/skills/implement/scripts/task-brief +28 -22
  36. package/skills/nextjs/SKILL.md +1 -1
  37. package/skills/parallel/SKILL.md +1 -1
  38. package/skills/plan/SKILL.md +3 -3
  39. package/skills/plan/evals/cases.yaml +1 -1
  40. package/skills/plan/references/plan-template.md +2 -2
  41. package/skills/postgresdb/SKILL.md +1 -1
  42. package/skills/react/SKILL.md +1 -1
  43. package/skills/review/SKILL.md +80 -41
  44. package/skills/sdd-init/SKILL.md +11 -9
  45. package/skills/ship/SKILL.md +58 -30
  46. package/skills/specify/SKILL.md +42 -41
  47. package/skills/specify/evals/cases.yaml +2 -2
  48. package/skills/specify/references/eliciting-requirements.md +1 -1
  49. package/skills/specify/references/spec-template.md +28 -6
  50. package/skills/suggest/SKILL.md +4 -4
  51. package/skills/tasks/SKILL.md +37 -33
  52. package/skills/tasks/evals/README.md +1 -1
  53. package/skills/tasks/evals/cases.yaml +3 -3
  54. package/skills/verify/SKILL.md +34 -18
  55. package/skills/verify/evals/README.md +2 -2
  56. package/skills/verify/evals/cases.yaml +2 -1
  57. package/skills/verify/references/generic-gate.md +43 -0
  58. package/skills/worktrees/SKILL.md +1 -1
  59. package/targets/clone-bootstrap.mjs +1 -1
  60. package/targets/hook-once.mjs +2 -2
  61. package/targets/sello.mjs +9 -3
  62. package/skills/sdd/SKILL.md +0 -250
@@ -0,0 +1,68 @@
1
+ // `rsc risk` — how much review and whose approval a change needs, decided by WHAT it touches.
2
+ //
3
+ // ASDD reviews inside every task, so the depth of that review cannot be the agent's own call: the
4
+ // change sets the tier, never the author (Anthropic, "How the Claude Code team does code review").
5
+ // The table is the sello's (targets/sello.mjs), reused as is, and it works with the sello off.
6
+ // Spec: 02-DOCS/wiki/sdd/specs/asdd-v4.md.
7
+
8
+ import { spawnSync } from 'node:child_process';
9
+ import { NESTED_WORKTREES, SELLO_STATE_PATHS, classifyRisk, lensesRequired, readEffectiveConfig, trunkRef } from '../../targets/sello.mjs';
10
+
11
+ function git(root, args) {
12
+ const r = spawnSync('git', ['-C', root, '-c', 'core.quotePath=false', ...args], { encoding: 'utf8', windowsHide: true, maxBuffer: 64 * 1024 * 1024 });
13
+ return r.status === 0 ? r.stdout : null;
14
+ }
15
+ const nul = (out) => (out || '').split('\0').filter(Boolean);
16
+
17
+ // Every path that differs from the base (committed, staged, unstaged or untracked). The base is the
18
+ // merge-base with `--base`, or with the trunk when none is given.
19
+ export function changedPaths(root, base = null) {
20
+ const cfg = readEffectiveConfig(root);
21
+ const ref = base || trunkRef(root, cfg);
22
+ if (!ref) return { ok: false, message: 'rsc: no base to compare against. Pass --base <ref> or the paths themselves: npx @ericrisco/rsc risk <path…>' };
23
+ const head = git(root, ['rev-parse', 'HEAD']);
24
+ const mb = head ? (git(root, ['merge-base', 'HEAD', ref]) || '').trim() : '';
25
+ if (!mb) return { ok: false, message: `rsc: cannot find a common ancestor with ${ref}. Pass --base <ref> or the paths themselves: npx @ericrisco/rsc risk <path…>` };
26
+ // --no-renames: a rename reports both paths, so moving a file OUT of a tier-2 place still counts.
27
+ // Nested worktrees are skipped only while UNTRACKED (another checkout sitting in the tree). Once
28
+ // committed, a path under them is part of the change and keeps its tier.
29
+ const untracked = () => nul(git(root, ['ls-files', '--others', '--exclude-standard', '-z'])).filter((n) => !NESTED_WORKTREES.test(n));
30
+ const diff = (from) => new Set([...nul(git(root, ['diff', '--name-only', '--no-renames', '-z', from])), ...untracked()]
31
+ .filter((n) => !SELLO_STATE_PATHS.includes(n)));
32
+ let names = diff(mb);
33
+ let shown = ref;
34
+ // Working straight on the trunk (FTD on an open main), the merge-base IS HEAD, and a change
35
+ // already committed would read as no change at all: tier 0, no reviewer, no escalation. With
36
+ // nothing uncommitted, the last commit is the change being asked about.
37
+ if (!base && mb === head.trim() && !names.size && git(root, ['rev-parse', '--verify', '--quiet', 'HEAD~1']) !== null) {
38
+ names = diff('HEAD~1');
39
+ shown = 'HEAD~1 (on the trunk: the last commit)';
40
+ }
41
+ return { ok: true, base: shown, paths: [...names].sort() };
42
+ }
43
+
44
+ export function assess(paths, cfg = null) {
45
+ const { tier, classes, configError } = classifyRisk(paths, cfg);
46
+ return {
47
+ tier,
48
+ classes: [...new Set(Object.values(classes))].sort(),
49
+ reviewers: lensesRequired(tier, cfg),
50
+ humanApproval: tier >= 2,
51
+ paths: classes,
52
+ ...(configError ? { configError } : {}),
53
+ };
54
+ }
55
+
56
+ export function render(r) {
57
+ const top = Object.entries(r.paths).filter(([, c]) => r.tier === 0 || c !== 'docs');
58
+ return [
59
+ `tier ${r.tier}`,
60
+ `classes: ${r.classes.join(', ') || 'none'}`,
61
+ `reviewers: ${r.reviewers}`,
62
+ `human approval: ${r.humanApproval ? 'required' : 'not required'}`,
63
+ ...(r.base ? [`base: ${r.base}`] : []),
64
+ ...(r.configError ? [`config ignored: ${r.configError}`] : []),
65
+ ...top.slice(0, 20).map(([p, c]) => ` ${c.padEnd(10)} ${p}`),
66
+ ...(top.length > 20 ? [` … ${top.length - 20} more`] : []),
67
+ ].join('\n');
68
+ }
@@ -18,6 +18,10 @@ import { parseFrontmatter } from './frontmatter.js';
18
18
  // would be exactly the parallel accounting P3 forbids.
19
19
  export const NEW_FAMILIES_SINCE = '2026-09-06';
20
20
 
21
+ // ASDD (4.0) makes a spec prove itself: four questions a long autonomous run is checked against.
22
+ // Same date rule as above, for the same reason — the corpus before it cannot answer them honestly.
23
+ export const ASDD_FAMILIES_SINCE = '2026-11-03';
24
+
21
25
  // The four states a point can be in. Order is the table's order in the skill body.
22
26
  export const OPEN_POINT_TYPES = [
23
27
  'pregunta abierta', // formulable now, unanswered → clarify asks it
@@ -32,7 +36,7 @@ export const OPEN_POINT_TYPES = [
32
36
  const FAMILIES = {
33
37
  problem: { prefixes: ['problema & por que', 'problema y por que', 'problem & why'] },
34
38
  goals: { prefixes: ['objetivos', 'goals'] },
35
- nongoals: { prefixes: ['no-objetivos', 'no objetivos', 'non-goals'] },
39
+ nongoals: { prefixes: ['no-objetivos', 'no objetivos', 'non-goals', 'fuera de alcance', 'out of scope'] },
36
40
  users: { prefixes: ['usuarios & contexto', 'usuarios y contexto', 'users & context'] },
37
41
  behaviour: { prefixes: ['comportamiento', 'behaviour', 'behavior'] },
38
42
  acceptance: { prefixes: ['criterios de aceptacion', 'acceptance criteria'] },
@@ -53,6 +57,10 @@ const FAMILIES = {
53
57
  prefixes: ['la alternativa mas barata', 'alternativa mas barata', 'cheapest alternative', 'the cheapest alternative'],
54
58
  since: NEW_FAMILIES_SINCE,
55
59
  },
60
+ can: { prefixes: ['que podra hacer el usuario', 'what the user can do'], since: ASDD_FAMILIES_SINCE },
61
+ result: { prefixes: ['que resultado obtiene', 'what result they get'], since: ASDD_FAMILIES_SINCE },
62
+ respect: { prefixes: ['que debe respetar', 'what it must respect'], since: ASDD_FAMILIES_SINCE },
63
+ proof: { prefixes: ['como lo demuestra', 'how it proves it'], since: ASDD_FAMILIES_SINCE },
56
64
  };
57
65
 
58
66
  const UNCHECKED = [
@@ -66,6 +74,7 @@ const UNCHECKED = [
66
74
  // Sin esta línea, el verde sería más estrecho que la regla que aparenta imponer — que es la
67
75
  // definición de puerta decorativa, sólo que por omisión en vez de por ausencia.
68
76
  `las specs anteriores a ${NEW_FAMILIES_SINCE} — quedan exentas de "qué pasa si no lo construimos" y de "la alternativa más barata", que no pueden rellenarse a posteriori`,
77
+ `las specs anteriores a ${ASDD_FAMILIES_SINCE} — quedan exentas de las cuatro preguntas de ASDD (qué podrá hacer el usuario, qué resultado obtiene, qué debe respetar, cómo lo demuestra)`,
69
78
  ];
70
79
 
71
80
  function fold(s) {
package/scripts/rsc.js CHANGED
@@ -46,7 +46,7 @@ if (['--version', '-v', 'version'].includes(rawArgv[0])) {
46
46
  process.exit(0);
47
47
  }
48
48
  const GENERAL_HELP = [
49
- 'Use: npx @ericrisco/rsc onboard | reassess | add <id...> | install --profile <p> | consult "<text>" | list | capabilities [--full|gap-log] | audit | registry refresh | doctor | sync | agent-model <target> <model|inherit|status> | agents [status|reset <name|--all>] | memory <on|off|status|save|resume|learn|metrics> | review consolidate [reports…] | ingest <file...> | copy check <file> | copy rules | sello <on|off|status|…> | worktrees [reap [path] [--confirm]] | backups | restore <id|latest> | upgrade | repair | uninstall <id...> | purge | help',
49
+ 'Use: npx @ericrisco/rsc onboard | reassess | add <id...> | install --profile <p> | consult "<text>" | list | capabilities [--full|gap-log] | audit | registry refresh | doctor | sync | agent-model <target> <model|inherit|status> | agents [status|reset <name|--all>] | memory <on|off|status|save|resume|learn|metrics> | risk [--base <ref>] [paths…] | spec gate [specs…] | review consolidate|ledger|escapes | ingest <file...> | copy check <file> | copy rules | sello <on|off|status|…> | worktrees [reap [path] [--confirm]] | backups | restore <id|latest> | upgrade | repair | uninstall <id...> | purge | help',
50
50
  'Any command takes --target <claude|codex|cursor|copilot|gemini|…> (comma-separate for several)',
51
51
  ' → without it, rsc uses the assistant already installed here; if two are, it asks instead of guessing.',
52
52
  ].join('\n');
@@ -77,7 +77,7 @@ const PURGE_HELP = [
77
77
  const GLOBAL_VALUE_FLAGS = new Set([
78
78
  '--topic', '--target', '--technical-level', '--accompaniment', '--project-kind', '--goal', '--goal-base64', '--software-scope', '--workflow', '--accept-plan',
79
79
  ]);
80
- const COMMANDS = new Set(['ingest', 'copy', 'onboard', 'reassess', 'add', 'install', 'consult', 'catalog', 'audit', 'list', 'doctor', 'memory', 'knowledge-sync', 'git-permissions', 'agent-model', 'agents', 'main', 'isolation', 'sync', 'backups', 'restore', 'upgrade', 'registry', 'worktrees', 'capabilities', 'review', 'sello', 'repair', 'uninstall', 'purge']);
80
+ const COMMANDS = new Set(['ingest', 'copy', 'onboard', 'reassess', 'add', 'install', 'consult', 'catalog', 'audit', 'list', 'doctor', 'memory', 'knowledge-sync', 'git-permissions', 'agent-model', 'agents', 'main', 'isolation', 'sync', 'backups', 'restore', 'upgrade', 'registry', 'worktrees', 'capabilities', 'review', 'risk', 'spec', 'sello', 'repair', 'uninstall', 'purge']);
81
81
  function positionalTokens(input) {
82
82
  const out = [];
83
83
  for (let i = 0; i < input.length; i++) {
@@ -509,7 +509,7 @@ async function selectAgents() {
509
509
  // the assistant (with the user present), never blindly from this CLI.
510
510
  function printNextSteps(targets, ids) {
511
511
  const hasHarness = ids.includes('harness');
512
- const hasSdd = ids.includes('sdd') || ids.includes('sdd-init');
512
+ const hasSdd = ids.includes('asdd') || ids.includes('sdd-init');
513
513
  const label = (id) => TARGETS.find((t) => t.id === id)?.label || id;
514
514
  const openLine = `Open this project in: ${targets.map((t) => `**${label(t)}**`).join(' · ')}`;
515
515
 
@@ -667,7 +667,7 @@ async function wizard(flagTargets, declaredTargets = []) {
667
667
  // First, because it is what someone who already has a harness came here for nine times in ten.
668
668
  ...(declared ? [{ key: 'update', label: `Update this project — keeps its ${declared.skills.length} skills, refreshes hooks and content` }] : []),
669
669
  { key: 'base', label: `Base install — the essentials (${baseIds.length} skills)` },
670
- { key: 'sdd', label: 'Base + Spec-Driven Development — the specify → plan → implement → ship flow' },
670
+ { key: 'asdd', label: 'Base + Agent Spec-Driven Development (ASDD) — spec → plan → review-in-the-loop → ship' },
671
671
  { key: 'manual', label: 'Pick skills by hand, by area' },
672
672
  ]);
673
673
  if (choice === null) { say('\nOK — nothing installed. Anytime: npx @ericrisco/rsc'); return; }
@@ -675,7 +675,7 @@ async function wizard(flagTargets, declaredTargets = []) {
675
675
 
676
676
  let ids;
677
677
  if (choice === 'base') ids = baseIds;
678
- else if (choice === 'sdd') ids = skillsForProfile(m, 'core');
678
+ else if (choice === 'asdd') ids = skillsForProfile(m, 'core');
679
679
  else if (choice === 'manual') {
680
680
  const picked = await manualSelect();
681
681
  if (picked === null) continue; // backed out → re-show this menu
@@ -1232,11 +1232,109 @@ async function main() {
1232
1232
  if (hits.length) process.exitCode = 1;
1233
1233
  return;
1234
1234
  }
1235
+ case 'spec': {
1236
+ // `spec gate` — the specify phase's exit gate, reachable from a user project. It was only an npm
1237
+ // script inside this package, so the one check ASDD's spec depends on could not run where specs live.
1238
+ const USE = 'Use: npx @ericrisco/rsc spec gate [spec.md ...]\n Without paths it checks 02-DOCS/wiki/sdd/specs/ of this project. Exits 1 when a spec fails.';
1239
+ if (argv[1] !== 'gate') { say(USE); if (argv[1]) process.exitCode = 1; return; }
1240
+ const { spawnSync } = await import('node:child_process');
1241
+ const { fileURLToPath: toPath } = await import('node:url');
1242
+ const rest = argv.slice(2);
1243
+ const specs = rest.filter((a, i) => !a.startsWith('--') && rest[i - 1] !== '--target');
1244
+ const r = spawnSync(process.execPath, [toPath(new URL('./spec-gate.js', import.meta.url)), ...specs], { stdio: 'inherit', windowsHide: true });
1245
+ process.exitCode = r.status ?? 1;
1246
+ return;
1247
+ }
1248
+ case 'risk': {
1249
+ // How much review and whose approval a change needs, by WHAT it touches (ASDD, P1/P7). The
1250
+ // agent never picks its own tier: this does, from the sello's path table, sello on or off.
1251
+ const K = await import('./lib/risk.js');
1252
+ const S = await import('../targets/sello.mjs');
1253
+ const root = S.resolveRoot(process.cwd());
1254
+ const base = flag('base');
1255
+ if (base === true) { say('Use: npx @ericrisco/rsc risk [--base <ref>] [paths…] [--json]'); process.exitCode = 1; return; }
1256
+ const rest = argv.slice(1);
1257
+ // Values of value-taking flags are not paths (`--target` is global). Paths given from a
1258
+ // subdirectory are resolved against the repository root, or the table would never match them.
1259
+ const VALUED = new Set(['--base', '--target']);
1260
+ const { resolve: res, relative: rel } = await import('node:path');
1261
+ const given = rest.filter((a, i) => !a.startsWith('--') && !VALUED.has(rest[i - 1]))
1262
+ .map((p) => rel(root, res(process.cwd(), p)).split('\\').join('/'));
1263
+ let paths = given;
1264
+ let against = null;
1265
+ if (!given.length) {
1266
+ const c = K.changedPaths(root, base || null);
1267
+ if (!c.ok) { say(c.message); process.exitCode = 1; return; }
1268
+ paths = c.paths;
1269
+ against = c.base;
1270
+ }
1271
+ const r = { ...K.assess(paths, S.readEffectiveConfig(root)), ...(against ? { base: against } : {}) };
1272
+ say(argv.includes('--json') ? JSON.stringify(r, null, 2) : K.render(r));
1273
+ return;
1274
+ }
1235
1275
  case 'review': {
1236
1276
  // `review consolidate` — the deterministic half of the refuter panel (P1): the three lenses
1237
1277
  // often report one defect three ways, so grouping happens here, by location and evidence, and
1238
1278
  // only the unique serious findings go on to `finding-verifier`, which is judgement.
1239
- const USE = 'Use: npx @ericrisco/rsc review consolidate [report.md|findings.json ...] [--json] [--window <lines>]\n Without files it reads stdin. Each lens report carries its findings in ```json findings blocks.';
1279
+ const USE = [
1280
+ 'Use: npx @ericrisco/rsc review consolidate [report.md|findings.json ...] [--json] [--window <lines>]',
1281
+ ' Without files it reads stdin. Each lens report carries its findings in ```json findings blocks.',
1282
+ ' npx @ericrisco/rsc review ledger record <findings.json> [--task <id>] | outcome <id> <fixed|false|intentional|irrelevant|parked> | round <slug>/<task> | stats [--json]',
1283
+ ' npx @ericrisco/rsc review escapes [--since <ref>] [--json]',
1284
+ ].join('\n');
1285
+ if (argv[1] === 'ledger' || argv[1] === 'escapes') {
1286
+ // ASDD's fix loop and the reviewer's learning, token-free (P1): rounds capped, repeats caught,
1287
+ // outcomes counted, reverts listed as the cases the reviewer should be scored against.
1288
+ const L = await import('./lib/review-ledger.js');
1289
+ const { resolveRoot } = await import('../targets/sello.mjs');
1290
+ const root = resolveRoot(process.cwd());
1291
+ const str = (name) => { const v = flag(name); return typeof v === 'string' ? v : undefined; };
1292
+ const json = argv.includes('--json');
1293
+ const fail = (msg) => { say(msg); process.exitCode = 1; };
1294
+ if (argv[1] === 'escapes') {
1295
+ const res = L.escapes(root, { since: str('since') || '' });
1296
+ if (!res.ok) return fail(res.message);
1297
+ say(json ? JSON.stringify(res, null, 2) : L.renderEscapes(res));
1298
+ return;
1299
+ }
1300
+ const [, , sub, a, b] = argv;
1301
+ if (sub === 'record') {
1302
+ const { readFileSync } = await import('node:fs');
1303
+ const R = await import('./lib/review-findings.js');
1304
+ const args = argv.slice(3);
1305
+ const file = args.find((x, i) => !x.startsWith('--') && args[i - 1] !== '--task');
1306
+ let raw;
1307
+ try { raw = file ? readFileSync(file, 'utf8') : readFileSync(0, 'utf8'); }
1308
+ catch (e) { return fail(`rsc: cannot read ${e.path || 'stdin'} (${e.code || e.message}).\n${USE}`); }
1309
+ let list;
1310
+ try { const v = JSON.parse(raw); list = Array.isArray(v) ? v : (Array.isArray(v?.findings) ? v.findings : [v]); }
1311
+ catch { const p = R.parseFindings(raw, file || 'stdin'); if (p.errors.length) return fail(p.errors.join('\n')); list = p.findings; }
1312
+ const out = L.record(root, list, { task: str('task') || '' });
1313
+ if (json) say(JSON.stringify(out, null, 2));
1314
+ else say(out.map((f) => `${f.id} ${f.status === 'repeat' ? `repeat of ${f.repeatOf} — do not raise it again` : 'new'} ${f.file || '(no file)'} ${f.claim}`).join('\n') || 'nothing to record');
1315
+ return;
1316
+ }
1317
+ if (sub === 'outcome') {
1318
+ if (!a || !b) return fail(USE);
1319
+ const r = L.outcome(root, a, b);
1320
+ if (!r.ok) return fail(r.message);
1321
+ say(r.message);
1322
+ return;
1323
+ }
1324
+ if (sub === 'round') {
1325
+ if (!a) return fail(USE);
1326
+ const r = L.round(root, a);
1327
+ say(r.message);
1328
+ if (!r.ok) process.exitCode = 1;
1329
+ return;
1330
+ }
1331
+ if (sub === 'stats') {
1332
+ const s = L.stats(root);
1333
+ say(json ? JSON.stringify(s, null, 2) : L.renderStats(s));
1334
+ return;
1335
+ }
1336
+ return fail(USE);
1337
+ }
1240
1338
  if (argv[1] !== 'consolidate') { say(USE); if (argv[1]) process.exitCode = 1; return; }
1241
1339
  const { readFileSync } = await import('node:fs');
1242
1340
  const R = await import('./lib/review-findings.js');
@@ -6,14 +6,24 @@
6
6
  // the phase's own instrument, and the author's.
7
7
  import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
8
8
  import { join, dirname, basename, resolve, relative } from 'node:path';
9
- import { fileURLToPath } from 'node:url';
10
9
  import { execFileSync } from 'node:child_process';
11
10
  import { specCompleteness, statusClaims, checkClaims } from './lib/spec-gate.js';
12
11
 
13
- const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
14
- const DEFAULT_DIR = join(ROOT, '02-DOCS', 'wiki', 'sdd', 'specs');
12
+ // The corpus of the project the gate is run FROM — not of the package that ships the script. Run as
13
+ // `npx @ericrisco/rsc spec gate` in a user project, a package-relative default found nothing.
14
+ function projectRoot() {
15
+ try {
16
+ const top = execFileSync('git', ['rev-parse', '--show-toplevel'], { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
17
+ if (top) return top;
18
+ } catch { /* not a git project: look upward for 02-DOCS instead */ }
19
+ for (let dir = process.cwd(); ; dir = dirname(dir)) {
20
+ if (existsSync(join(dir, '02-DOCS'))) return dir;
21
+ if (dirname(dir) === dir) return process.cwd();
22
+ }
23
+ }
15
24
 
16
25
  function defaultTargets() {
26
+ const DEFAULT_DIR = join(projectRoot(), '02-DOCS', 'wiki', 'sdd', 'specs');
17
27
  try {
18
28
  return readdirSync(DEFAULT_DIR)
19
29
  .filter((f) => f.endsWith('.md') && !f.endsWith('.plan.md'))
@@ -11,11 +11,11 @@ origin: risco
11
11
 
12
12
  You have a **constitution**, a **spec**, a **plan** and a **task list**. Four artifacts written at four different moments, by a mind that drifted a little each time. `analyze` reads all four *against each other* and surfaces where they disagree. It is the cheapest place in the whole chain to catch a problem — a contradiction found here costs a sentence; the same contradiction found mid-implement costs a rewrite.
13
13
 
14
- Sixth phase of the rsc-sdd chain (`constitution → specify → clarify → plan → tasks → analyze → implement`); the method itself lives in `../sdd/SKILL.md`. It is a **gate, not a worker**: it produces a report and stops. The user reads the findings and decides what to fix, and which phase to send each fix back to.
14
+ Sixth phase of the rsc-sdd chain (`constitution → specify → clarify → plan → tasks → analyze → implement`); the method itself lives in `../asdd/SKILL.md`. It is a **gate, not a worker**: it produces a report and stops. The user reads the findings and decides what to fix, and which phase to send each fix back to.
15
15
 
16
16
  **Report only. Resolve nothing.** Never edit the constitution, spec, plan or tasks; never open a code file to "just fix it"; never silently reconcile a contradiction by picking a side. This one is absolute because a gate that quietly fixes things stops being a gate: the user never learns the spec was wrong, the plan built on the old assumption stays stale, and the "consistency check" has manufactured a new inconsistency. Name the conflict, show both sides with locations, propose where it should be resolved, hand the decision back.
17
17
 
18
- **Model tier: `heavy`** (adversarial cross-reading). Resolve and apply it per `../sdd/references/model-routing.md`; routing is off unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`.
18
+ **Model tier: `heavy`** (adversarial cross-reading). Resolve and apply it per `../asdd/references/model-routing.md`; routing is off unless `models.enabled: true` in `02-DOCS/wiki/sdd/config.yaml`.
19
19
 
20
20
  **Register.** Read `technical_level` in `02-DOCS/wiki/harness/user-profile.md` before reporting; with no profile, use analogies and say the harness has not set the register yet. The register changes how the report reads, never what gets checked; the six analyses always run in full.
21
21
 
@@ -99,7 +99,7 @@ Render it in the orient voice, in the user's register. Do not log a decision to
99
99
  ## Result envelope
100
100
 
101
101
  End with the parseable block every SDD phase shares, so the dispatcher can chain without
102
- interpreting prose (contract: `../sdd/SKILL.md`):
102
+ interpreting prose (contract: `../asdd/SKILL.md`):
103
103
 
104
104
  ```json result-envelope
105
105
  {
@@ -0,0 +1,183 @@
1
+ ---
2
+ name: asdd
3
+ description: "Use when a feature or substantial change should go from idea to shipped, verified software and an agent may run it for hours on its own — the ASDD (Agent Spec-Driven Development) dispatcher and front door, in any language. A spec that must prove itself, review inside every task scaled by `rsc risk`, a decision queue for what the agent must not decide alone. Routes constitution -> specify -> clarify -> plan -> tasks -> analyze -> implement (verify + review per task) -> closing -> ship. Successor of `sdd`. NOT a single phase (it dispatches), NOT small authorised changes (that is `ftd`), NOT the workspace harness (that is `harness`)."
4
+ tags: [sdd, asdd, spec, workflow, plan, review]
5
+ recommends: [sdd-init, constitution, specify]
6
+ profiles: [core, full]
7
+ origin: risco
8
+ ---
9
+
10
+ # asdd — Agent Spec-Driven Development
11
+
12
+ *The front door for letting an agent build for hours without coming back with a Frankenstein: intent in, shipped-and-verified software out, every decision recorded in `02-DOCS/wiki/sdd/`.*
13
+
14
+ **Why it exists.** Classic SDD reviewed at the end. On paper it checked the spec; in a long autonomous run, the only thing that bit for hours was the test — and tests prove the code works, not that it does what was asked (a real run: 8 hours, 4,500 green tests, and a dropped token plus a QR nobody requested). ASDD keeps the phases and changes two things: **the spec must prove itself**, and **the review moves inside every task**, scaled by risk. The human stops reviewing everything and only takes the decisions the agent must not take alone.
15
+
16
+ This skill does **not** do a phase. It names the method, reads the register, places the request on the map and hands off. Each phase skill points at the next one.
17
+
18
+ ## The five stages
19
+
20
+ ```text
21
+ request ─► decisor (answer · ftd · asdd)
22
+ │ asdd
23
+ 1. BASE ─────► constitution + REVIEW.md (once per project)
24
+ 2. SPEC ─────► specify (4 questions + out of scope) ─► clarify (gap checklist) ◄── HUMAN APPROVES
25
+ 3. PLAN ─────► plan ─► tasks (criterion · risk forecast · check) ─► analyze
26
+ 4. LOOP ─────► per task, in implement:
27
+ a build + self-check b rsc risk c review by tier
28
+ d verify findings e mandatory controls
29
+ f fix (max 3 rounds) g fix & record ─or─ park ─► DECISION QUEUE ◄── HUMAN
30
+ 5. CLOSE ────► verify against main + rollback plan · closing review (whole branch vs spec) · explainer
31
+ SHIP ─────► remote: PR (self-merge only tier 0-1 + green CI + no blocking entry) │ no remote: park ◄── HUMAN
32
+ LEARN ────► finding outcomes · reverts → reviewer cases · repeated finding → rule
33
+ ```
34
+
35
+ | Stage | Phase skills | Writes |
36
+ | --- | --- | --- |
37
+ | **1 Base** | `../constitution/SKILL.md` (also drafts `REVIEW.md`), `../sdd-init/SKILL.md` | `constitution.md`, `REVIEW.md`, `config.yaml` |
38
+ | **2 Spec** | `../specify/SKILL.md` → `../clarify/SKILL.md` | `specs/<slug>.md` |
39
+ | **3 Plan** | `../plan/SKILL.md` → `../tasks/SKILL.md` → `../analyze/SKILL.md` | `plans/<slug>.md`, `analysis/<slug>.md` |
40
+ | **4 Loop** | `../implement/SKILL.md`, calling `../verify/SKILL.md` and `../review/SKILL.md` per task | `progress/<slug>.md` (with `## Decision queue`), `decisions.md` |
41
+ | **5 Close** | `../verify/SKILL.md` (closing mode), then `../review/SKILL.md` (closing review, on what will ship), `../ship/SKILL.md` | `verifications/<slug>-<date>.md`, `reviews/<slug>.md`, the PR or parked branch |
42
+ | on demand | `../debug/SKILL.md` · `../worktrees/SKILL.md` · `../parallel/SKILL.md` | — |
43
+
44
+ **Where the human enters — three places only:** approving the spec, answering the decision queue, approving a tier-2 merge. Everything else runs.
45
+
46
+ No ASDD phase edits the root `CLAUDE.md`: every artifact is indexed in `02-DOCS/wiki/index.md` (the Knowledge map). The root `CLAUDE.md` is owned by `harness` and is tier 2 in `rsc risk`.
47
+
48
+ ### The spec proves itself
49
+
50
+ Every spec answers four questions, and `spec:gate` refuses one dated 2026-11-03 or later without them: **what the user can do**, **what result they get** (against a reference the human approved — a screenshot, a sketch, an expected API response), **what it must respect** (prior decisions, specs and behaviour; read from the brain when the project has one, else `decisions.md`), **how it proves it** (how the check runs, what evidence remains — "yes, I tested it" is not evidence). Plus an explicit **out of scope**: what the agent might add "just in case".
51
+
52
+ ### The risk decides, never the agent
53
+
54
+ `npx @ericrisco/rsc risk [--base <ref>] [paths…]` classifies a change by **what it touches** (the sello's path table; works with the sello off): tier 0 docs/copy → no reviewer; tier 1 ordinary code → one fresh-context reviewer; tier 2 auth, billing, CI workflows, migrations, secrets, harness config → the three-refuter panel **and human approval**. An agent's approval never counts as a human's.
55
+
56
+ ### The loop converges
57
+
58
+ `npx @ericrisco/rsc review ledger` holds it to the rules that stopped Anthropic's 50-round reviews: `round <slug>/<task>` refuses a 4th round (rethink the approach or park the task), `record` marks a finding already raised as a `repeat` that is never raised again, `outcome` closes each finding as `fixed|false|intentional|irrelevant`, `stats` measures the noise. Flag the bug class, not the line; from round 2 re-review only the new diff.
59
+
60
+ ### Park, don't block
61
+
62
+ Tier 2, a contradiction with a recorded decision, a gap that would make the build unsafe, or no convergence in 3 rounds: the task goes to the **decision queue** (`## Decision queue` in `progress/<slug>.md`) with its evidence, and the agent **continues with the independent tasks**. The run never waits at 2 a.m. for an answer.
63
+
64
+ Every entry carries a kind, one line each:
65
+
66
+ ```markdown
67
+ - [blocking] Q3 — <what must be decided>, <evidence>, <task>
68
+ - [review-after] Q4 — <assumption taken>, <where it is recorded>, <task>
69
+ ```
70
+
71
+ - **`blocking`** — tier 2, a contradiction with a recorded decision, no convergence in 3 rounds, a hard failure. It stops a self-merge.
72
+ - **`review-after`** — clarify's recorded assumptions, a spec reference the agent had to write itself, a provisional constitution ratification. It never stops a merge; it is listed in the PR body (or the parked summary) for the human.
73
+
74
+ ## The invoke rule — route to the phase that fits
75
+
76
+ > **The new-feature gate (hard).** The moment the user is thinking about a new feature or change — "add…", "build…", "it should also…", "quiero añadir…" — and the decisor chose this lane, it goes to `specify` first, *even if a stack skill also fired*. **No feature code is written — by any skill — until a spec AND a plan exist and are approved.** Small authorised changes go to `../ftd/SKILL.md`, which escalates here when `rsc risk` says tier 2 or the change contradicts a recorded decision.
77
+
78
+ 1. **Empty repo (nothing to detect)?** → `constitution` first (it decides the stack), then `sdd-init`.
79
+ 2. **No `02-DOCS/wiki/sdd/config.yaml` and this is non-trivial?** → `sdd-init`. **No constitution, or `status: draft`?** → `constitution` once (it also drafts `REVIEW.md`).
80
+ 3. **Ambiguous / architectural / risky before a spec?** → optional proposal via `specify`.
81
+ 4. **Fuzzy idea, no spec?** → `specify`. **Spec with gaps?** → `clarify`.
82
+ 5. **Approved spec, no plan?** → `plan`, then `tasks`, then `analyze`.
83
+ 6. **Green light?** → `implement` (the per-task loop; `parallel` / `worktrees` when useful).
84
+ 7. **All tasks done or parked?** → closing: `verify` (closing mode, main merged in), then `review` (closing review of that exact state), then `ship`.
85
+ 8. **Broken mid-flight?** → `debug`, then resume.
86
+
87
+ Cannot tell the phase? Ask one question: *"Do we have a written spec for this yet?"*
88
+
89
+ Skip honestly: `constitution` runs once per project; `clarify` and `analyze` are gates, not paperwork — pass through only when the change is genuinely tiny, and say so.
90
+
91
+ ## Autopilot — one up-front yes
92
+
93
+ The user says "take it all the way" once (or `execution_mode: automatic` under `sdd:` in `config.yaml`, set by `sdd-init`) and the chain runs end to end **without asking to continue between phases**, still writing every artifact. The up-front yes satisfies the new-feature gate.
94
+
95
+ Autopilot does not stop the run for questions — it **parks**: real ambiguity, tier 2, a contradiction with a recorded decision or a non-converging task go to the decision queue while independent work continues. It **stops** only for a hard failure nothing else can proceed past, and for destructive or irreversible actions.
96
+
97
+ **In autopilot, per phase:**
98
+
99
+ - `constitution` ratifies provisionally → `review-after` entry.
100
+ - `specify` uses a checkable reference it wrote itself (expected output, sample response) → `review-after` entry.
101
+ - `clarify` records its assumptions (`suposición tomada`) → `review-after` entries.
102
+ - `implement` parks `blocking` items and continues with the independent tasks.
103
+ - `ship` lands by the rule below.
104
+
105
+ **Landing — there is always a legal path.** A remote exists → open a PR; self-merge only when `rsc risk` says tier 0-1, CI is green and no `blocking` entry is open; `review-after` entries go into the PR body. No remote → **park**: leave the branch committed, write the explainer, the open entries and the exact merge command for the human into `progress/<slug>.md`, and stop. Never merge locally around the branch guard: when `rsc main status` says `closed: true`, the guard denies it.
106
+
107
+ Narrate in the `orient` voice: name each stage as it passes and show its artifact, one line each.
108
+
109
+ ## Read the register first
110
+
111
+ Read `technical_level` in `02-DOCS/wiki/harness/user-profile.md`. It picks the words (technical, or plain with analogies), never whether the gates exist. No profile → analogies, ask once "technical or with analogies?", persist it (that is `init`'s job; `asdd` honors it). At gates, ask only what changes what gets built, one question at a time, recommendation first.
112
+
113
+ ## Per-phase model routing (opt-in)
114
+
115
+ Each phase declares a tier — `heavy` (constitution, plan, analyze, review, debug), `balanced` (specify, clarify, tasks, implement, verify), `light` (ship, worktrees, sdd-init) — resolved to a model in `config.yaml` under `models`. Off by default (`models.enabled: false`). Never switch unasked, never claim a switch a tool can't make; the dispatcher itself stays on the session model. Protocol: `references/model-routing.md`.
116
+
117
+ ## Where the artifacts live
118
+
119
+ ```text
120
+ 02-DOCS/wiki/sdd/
121
+ ├── config.yaml ← runtime calibration (sdd-init)
122
+ ├── constitution.md ← non-negotiables (once)
123
+ ├── proposals/<slug>.md ← optional pre-execution briefing
124
+ ├── specs/<slug>.md ← one spec per feature (four questions + out of scope)
125
+ ├── plans/<slug>.md ← plan + tasks
126
+ ├── analysis/<slug>.md ← analyze's cross-artifact report
127
+ ├── progress/<slug>.md ← append-only loop ledger + ## Decision queue
128
+ ├── verifications/<slug>-YYYY-MM-DD.md
129
+ ├── reviews/<slug>.md ← closing review record
130
+ ├── review-evals/<sha>.md ← one case per revert that escaped review (`rsc review escapes`)
131
+ ├── archive/<slug>/
132
+ ├── sessions/<date>-<slug>.md
133
+ └── decisions.md ← append-only: every decision, and the task it was taken in
134
+ REVIEW.md ← project root: how this repo is judged
135
+ .rsc/review-ledger.jsonl ← findings, outcomes, rounds (tool-written)
136
+ ```
137
+
138
+ The directory keeps its `sdd/` name: existing projects keep working. Index it in `02-DOCS/wiki/index.md` under the `sdd/` topic — never in the root `CLAUDE.md`; the harness keeps it honest.
139
+
140
+ ## Stack handoff and skill registry
141
+
142
+ ASDD is process, not stack: `plan` defers stack specifics, `implement` borrows the stack skill's testing approach, `verify` runs its checks (`../fastapi/SKILL.md`, `../nextjs/SKILL.md`, `../go/SKILL.md`, `../postgresdb/SKILL.md`, `../flutter/SKILL.md`; UI → `../design/SKILL.md`). Choose skills through `.rsc/skill-registry.json`, digest them into 4-5 compact rules per subagent brief, never load the catalog.
143
+
144
+ ## Standard result envelope
145
+
146
+ Every phase ends with this block so the dispatcher can chain without interpreting prose:
147
+
148
+ ```json result-envelope
149
+ {
150
+ "status": "complete|blocked|failed",
151
+ "executive_summary": "one short paragraph",
152
+ "artifact": "path/to/artifact-or-none",
153
+ "next_recommended": "sdd-init|constitution|specify|clarify|plan|tasks|analyze|implement|verify|review|ship",
154
+ "risk": "low|medium|high",
155
+ "model": { "tier": "heavy|balanced|light", "resolved": "model-id", "routing": "on|off" },
156
+ "skill_resolution": { "used": [], "missing": [], "fallback": [], "compact_rules": [] },
157
+ "evidence": []
158
+ }
159
+ ```
160
+
161
+ A run stopped mid-flight writes `02-DOCS/wiki/sdd/sessions/<date>-<slug>.md` instead: stage, artifacts, last verdict, done and parked tasks, the decision queue, next steps.
162
+
163
+ ## Anti-patterns → STOP
164
+
165
+ | Rationalization | Reality / fix |
166
+ | --- | --- |
167
+ | "Tests are green, the feature is done." | Green proves it runs. Traceability to the spec proves it is what was asked; untraced behaviour is a finding. |
168
+ | "I'll review everything at the end." | That is how 40 correct tasks become a Frankenstein. Review inside each task, then once more at closing. |
169
+ | "This change is obviously low risk." | `rsc risk` decides, from the paths. The agent never picks or argues down its own tier. |
170
+ | "One more round and it converges." | Round 4 is refused. Rethink the approach or park the task. |
171
+ | "I'll wait for the user's answer." | Park the task in the decision queue and continue with independent work. |
172
+ | "The reviewer agent approved it, merge." | An agent's approval never counts as a human's. Tier 2 waits. |
173
+ | "The screenshots look alike." | Compare against a reference the human approved, or a deterministic diff. |
174
+ | "`asdd` should write the spec itself." | It dispatches. `specify` owns the spec. |
175
+ | "Ship with Co-Authored-By: Claude." | Authorship is the responsible person. No AI trailer, no generated-with footer. |
176
+
177
+ ## Start here
178
+
179
+ - **New project** → `sdd-init`, `constitution`, then `specify`. **Empty repo** → `constitution` first (it decides the stack), then `sdd-init`.
180
+ - **Existing project, new feature** → `specify` (proposal first if risky or architectural).
181
+ - **Small authorised change** → `../ftd/SKILL.md`.
182
+
183
+ Then follow the arrows: each phase ends by pointing at the next, all the way to `ship`.
@@ -1,6 +1,6 @@
1
- # Eval harness — `sdd`
1
+ # Eval harness — `asdd`
2
2
 
3
- Evaluates the `sdd` dispatcher (the rsc-sdd front door) on two axes: **triggering**
3
+ Evaluates the `asdd` dispatcher (the rsc-sdd front door) on two axes: **triggering**
4
4
  (does it fire when a feature should run the spec-driven flow, and stay quiet when a
5
5
  single phase or a different skill owns the request) and **capability** (does loading it
6
6
  measurably improve how the agent opens and routes the work). Cases live in `cases.yaml`.
@@ -9,7 +9,7 @@ feeds prompts to Claude Code and judges the result against the rubrics.
9
9
 
10
10
  ## What's in `cases.yaml`
11
11
 
12
- - `should_trigger` (7) — prompts that MUST invoke `sdd`, including non-obvious phrasings
12
+ - `should_trigger` (7) — prompts that MUST invoke `asdd`, including non-obvious phrasings
13
13
  ("which step am I on", "decide what and why, write it down, then build") with no SDD
14
14
  jargon.
15
15
  - `should_not_trigger` (6) — near-misses that must route elsewhere. Each carries a
@@ -19,12 +19,12 @@ feeds prompts to Claude Code and judges the result against the rubrics.
19
19
 
20
20
  ## A. Triggering eval
21
21
 
22
- 1. Load **only** `sdd` into the agent (no other rsc skills available, so routing is honest).
22
+ 1. Load **only** `asdd` into the agent (no other rsc skills available, so routing is honest).
23
23
  2. For each `should_trigger` prompt: open a fresh session, paste the prompt verbatim, and
24
- record whether `sdd` activates — a pass means it names the method, places the request on
24
+ record whether `asdd` activates — a pass means it names the method, places the request on
25
25
  the phase map, and routes to a phase (it should NOT start writing the spec or coding
26
26
  itself). Run **3–5 trials** per prompt.
27
- 3. For each `should_not_trigger` prompt: same procedure, but a **pass** = `sdd` does NOT
27
+ 3. For each `should_not_trigger` prompt: same procedure, but a **pass** = `asdd` does NOT
28
28
  fire. Where a `route_to` sibling is named, sanity-check the prompt genuinely belongs there
29
29
  (e.g. "write the spec" → `specify`, "open the PR and merge" → `ship`).
30
30
  4. Score: a prompt passes if the **majority of its trials** go the expected way.
@@ -35,7 +35,7 @@ prompts (at most 1 of the 13 may misbehave).
35
35
  ## B. Capability eval
36
36
 
37
37
  1. **Without the skill:** fresh session, skill NOT loaded, give the `scenario` prompt. Save output A.
38
- 2. **With the skill:** fresh session, `sdd` loaded, same prompt. Save output B.
38
+ 2. **With the skill:** fresh session, `asdd` loaded, same prompt. Save output B.
39
39
  3. Grade each output against that scenario's `must_include` points — count points clearly covered.
40
40
  4. Repeat across **3 trials** per scenario per condition and average the coverage.
41
41
 
@@ -47,14 +47,14 @@ the skill — or these rubrics — needs work.
47
47
 
48
48
  - This is **LLM-as-judge / human-in-the-loop**, not deterministic. Use a consistent grader
49
49
  (same model + rubric) across A/B to keep the comparison fair.
50
- - The headline differentiators for `sdd`: it **dispatches, it does not perform a phase**; it
50
+ - The headline differentiators for `asdd`: it **dispatches, it does not perform a phase**; it
51
51
  states the method ("the artifact is the contract"), reads the **register** (`technical_level`)
52
52
  and speaks in the orient voice (never skipping gates), presents the **chained phase map**, applies the **invoke
53
53
  rule** and the **skip rules** honestly, and writes artifacts under `02-DOCS/wiki/sdd/`.
54
- - Key confusables to watch: "write the spec" is `specify`, not `sdd`; "set the principles" is
54
+ - Key confusables to watch: "write the spec" is `specify`, not `asdd`; "set the principles" is
55
55
  `constitution`; "open the PR" is `ship`; "build 01-TOOLS/02-DOCS" is `harness`. If the agent
56
56
  does the phase work itself instead of routing, that's a fail even if the output looks good.
57
- - Because `sdd` is the front door, some phase siblings may not yet be installed. A correct
57
+ - Because `asdd` is the front door, some phase siblings may not yet be installed. A correct
58
58
  response still routes by name and does the phase inline if the sibling is absent — it must
59
59
  never invent a phase that isn't in the chain.
60
60
  - Run after any edit to `SKILL.md` or its `description`, since both axes are sensitive to wording.
@@ -1,7 +1,7 @@
1
- skill: sdd
1
+ skill: asdd
2
2
 
3
- # Cases for the rsc-sdd "sdd" dispatcher / front door.
4
- # sdd states the SDD method, reads the user's register (technical_level) from
3
+ # Cases for the "asdd" dispatcher / front door (ASDD, successor of sdd in 4.0).
4
+ # asdd states the SDD method, reads the user's register (technical_level) from
5
5
  # 02-DOCS/wiki/harness/user-profile.md, and ROUTES to the right phase skill in
6
6
  # the chain constitution -> specify -> clarify -> plan -> tasks -> analyze ->
7
7
  # implement -> verify -> review -> ship (debug/worktrees/parallel on demand).
@@ -17,7 +17,7 @@ should_trigger:
17
17
  - prompt: "I'm somewhere in the middle of building a feature and I've lost track of which step I'm on. Can you orient me?"
18
18
  why: "Non-obvious: no SDD jargon, but 'which step am I on' is precisely the dispatcher's 'which phase are you in' job — it asks the one orienting question (is there a written spec?) and routes."
19
19
 
20
- - prompt: "Run the full sdd flow on this new authentication requirement."
20
+ - prompt: "Run the full asdd flow on this new authentication requirement."
21
21
  why: "Direct request to run the chained flow — the dispatcher owns the flow and routes the first phase; it should not try to do every phase inline itself."
22
22
 
23
23
  - prompt: "I have a one-paragraph idea for a notifications feature. Take it all the way to a PR the disciplined way."
@@ -69,7 +69,7 @@ capability:
69
69
  - "Includes sdd-init as step zero when 02-DOCS/wiki/sdd/config.yaml is missing, and explains that it detects test commands, strict TDD and .rsc/skill-registry.json"
70
70
  - "Applies the invoke rule: a fuzzy idea with no spec on disk routes to specify (and notes constitution runs once per project if not yet present)"
71
71
  - "Does NOT itself write the spec or start coding — it hands off to the specify phase skill (../specify/SKILL.md)"
72
- - "Notes that artifacts will be written under 02-DOCS/wiki/sdd/ (specs/, plans/, decisions.md) and indexed in the root CLAUDE.md Knowledge map"
72
+ - "Notes that artifacts will be written under 02-DOCS/wiki/sdd/ (specs/, plans/, decisions.md) and indexed in 02-DOCS/wiki/index.md (never the root CLAUDE.md)"
73
73
  - "Mentions standard result envelopes with status, artifact, next_recommended, risk, skill_resolution and evidence"
74
74
  - "Defers concrete stack tooling (test runner, build, framework idioms) to the relevant stack skill rather than prescribing it here"
75
75
 
@@ -6,7 +6,7 @@ that lets each phase run on the model its work deserves — cheaper where it's s
6
6
  where it matters — without surprising anyone and without faking a switch a tool can't make.*
7
7
 
8
8
  This file is the **single source of truth** for the behavior. `sdd-init` writes the profile,
9
- `sdd` summarizes it, and every phase skill points here for the procedure. If the three ever
9
+ `asdd` summarizes it, and every phase skill points here for the procedure. If the three ever
10
10
  disagree, this file wins.
11
11
 
12
12
  ## Why this exists
@@ -64,7 +64,7 @@ session model, never announce a switch, never nag. Everything below applies only
64
64
 
65
65
  **Two phases are deliberately absent from `phases`:**
66
66
 
67
- - `sdd` (the dispatcher) — routing decisions are cheap; it stays on the session model.
67
+ - `asdd` (the dispatcher) — routing decisions are cheap; it stays on the session model.
68
68
  - `parallel` — it has no fixed tier. Each fanned-out subagent inherits the tier of the *kind
69
69
  of work* its unit does (an implement-type unit → `balanced`; a scan/research unit → `light`;
70
70
  a deep-reasoning unit → `heavy`).
@@ -14,7 +14,7 @@ A thin provider adapter, a disciplined agent loop, schema-validated tools, provi
14
14
 
15
15
  > Program against a **capability interface**, never a vendor SDK. Vendor specifics (model id, tool-schema shape, JSON mode, caching, token limits) live behind one adapter resolved from config. Model names and prices rot — if one appears in business logic it's a bug, and re-verify the dated tables before quoting a number.
16
16
 
17
- **Hand off instead when:** a new non-trivial feature has no approved spec + plan under `02-DOCS/wiki/sdd/` → stop and run [`specify`](../specify/SKILL.md) first (method: [`sdd`](../sdd/SKILL.md)), which routes back here once the plan is approved; one-line/low-risk changes go straight through. Anthropic-SDK internals (caching, thinking, batch) in a file that *only* imports `anthropic` → `claude-api` if your environment has it, since this skill stays multi-provider. Workspace scaffolding → [`harness`](../harness/SKILL.md). Choosing *which* coding agent to use → agent-eval territory. Pure prompt-wording tuning with no architecture change → prompt engineering, not this. A one-shot throwaway prompt, or no retrieval/tools/loop/evals at all → you don't need an agent; call the SDK directly and say so.
17
+ **Hand off instead when:** a new non-trivial feature has no approved spec + plan under `02-DOCS/wiki/sdd/` → stop and run [`specify`](../specify/SKILL.md) first (method: [`asdd`](../asdd/SKILL.md)), which routes back here once the plan is approved; one-line/low-risk changes go straight through. Anthropic-SDK internals (caching, thinking, batch) in a file that *only* imports `anthropic` → `claude-api` if your environment has it, since this skill stays multi-provider. Workspace scaffolding → [`harness`](../harness/SKILL.md). Choosing *which* coding agent to use → agent-eval territory. Pure prompt-wording tuning with no architecture change → prompt engineering, not this. A one-shot throwaway prompt, or no retrieval/tools/loop/evals at all → you don't need an agent; call the SDK directly and say so.
18
18
 
19
19
  ## Decision rules (read before writing code)
20
20