diff --git a/adapters/LOCAL-LLM.md b/adapters/LOCAL-LLM.md index db9d8d9..7e800dd 100644 --- a/adapters/LOCAL-LLM.md +++ b/adapters/LOCAL-LLM.md @@ -62,7 +62,7 @@ The model reads SKILL.md, routes to the right skill, and produces artifacts in y ## Model size recommendations -The methodology is detailed (35 skills, routing logic, evidence format, memory contract). Larger models handle it better: +The methodology is detailed (37 methods, routing logic, evidence format, memory contract). Larger models handle it better: | Model class | Experience | |-------------|-----------| diff --git a/adapters/README.md b/adapters/README.md index 7750833..ea52979 100644 --- a/adapters/README.md +++ b/adapters/README.md @@ -31,4 +31,4 @@ Defaults to the current directory if no path is given. Existing files are never ## The principle -The adapter only tells the tool **where the brain is and how to behave**. All the method - the 35 skills, the overlays, the memory contract - lives once in `skills/fde/SKILL.md`. Update the brain, every platform gets it. That's why fdeops feels native in whatever the FDE already uses, without five things to keep in sync. +The adapter only tells the tool **where the brain is and how to behave**. All the method - the 37 methods, the overlays, the memory contract - lives once in `skills/fde/SKILL.md`. Update the brain, every platform gets it. That's why fdeops feels native in whatever the FDE already uses, without five things to keep in sync. diff --git a/bin/check.js b/bin/check.js index d6db136..952f1cc 100644 --- a/bin/check.js +++ b/bin/check.js @@ -88,6 +88,94 @@ for (const refFile of mentioned) { } ok(`router dispatch (${mentioned.length} reference targets verified) + memory contract`) +// Public claims must match the router. The docs advertise a method count and a +// per-domain list; both drifted from SKILL.md once (ingest / ingest-connect +// routed but undocumented), and a number nobody can verify is worse than none. +{ + // Every routing row must parse. A row this misses is a method that could go + // undocumented for free, so an unparsed row is a hard failure, not a silent skip. + const routed = new Set() + const routing = (router.split('## Routing - 6 domains')[1] || '').split('**Overlays')[0] + const methodCell = line => (line.split('|')[2] || '').trim().replace(/\s*\([^)]*\)\s*$/, '') + for (const line of routing.split('\n')) { + if (!/^\|/.test(line)) continue + // A row that names a method but no reference would route that method while + // nothing requires anyone to document it - shape checks can only police rows + // they recognise, so name a method here and you must name its reference. + if (!/references\/[a-z0-9-]+\.md/.test(line)) { + const orphan = methodCell(line) + // a method name, not a `-` placeholder (CLI-only rows) or a `---` separator + if (/^[a-z][a-z0-9]*(-[a-z0-9]+)*$/.test(orphan)) { + fail(`SKILL.md routes '${orphan}' without naming a reference: ${line.trim().slice(0, 80)}`) + } + continue + } + // Candidate rows are selected on the reference name in ANY form, then the + // shape is enforced - a row this cannot read must fail, never be skipped, + // or a method could go undocumented by being written unusually. + if (!/`references\/[a-z0-9-]+\.md`/.test(line)) { + fail(`SKILL.md routing row must name its reference as \`references/.md\`: ${line.trim().slice(0, 80)}`) + continue + } + // | You hear | | | + const method = methodCell(line) + if (!/^[a-z0-9-]+$/.test(method)) { + fail(`check.js cannot read the method name in a SKILL.md routing row: ${line.trim().slice(0, 80)}`) + continue + } + routed.add(method) + } + if (!routed.size) fail('check.js could not parse the SKILL.md routing table') + + // docs/skills-reference.md is the canonical per-method list: one row per + // method inside the six domain tables, ending at the Overlays section. + const reference = read('docs/skills-reference.md') + const documented = new Set() + let documentedRows = 0 + for (const line of reference.split('### Overlays')[0].split('\n')) { + const m = line.match(/^\|\s*\[([a-z0-9-]+)\]\(\.\.\/skills\/fde\/references\/([a-z0-9-]+\.md)\)/) + if (!m) continue + documentedRows++ + documented.add(m[1]) + // A link nobody followed is the same unverifiable claim this gate exists for: + // the target must exist, and it must be the method the text names. + if (m[2] !== `${m[1]}.md`) { + fail(`docs/skills-reference.md links [${m[1]}] at references/${m[2]}`) + } else if (!fs.existsSync(path.join(root, 'skills', 'fde', 'references', m[2]))) { + fail(`docs/skills-reference.md links references/${m[2]}, which does not exist`) + } + } + if (documented.size !== documentedRows) { + fail(`docs/skills-reference.md lists ${documentedRows} method rows for ${documented.size} methods - a duplicate row inflates the count`) + } + const undocumented = [...routed].filter(name => !documented.has(name)) + if (undocumented.length) { + fail(`SKILL.md routes skill(s) missing from docs/skills-reference.md: ${undocumented.join(', ')}`) + } + // and the other direction: a documented method nothing routes to is a method + // the agent can never reach, advertised anyway. + const unrouted = [...documented].filter(name => !routed.has(name)) + if (unrouted.length) { + fail(`docs/skills-reference.md documents method(s) SKILL.md never routes to: ${unrouted.join(', ')}`) + } + for (const rel of ['docs/skills.md', 'docs/skills-reference.md']) { + const body = read(rel) + // `-` is a word boundary, so \bingest\b matches inside `ingest-connect`: + // a method could disappear from the docs behind a hyphenated sibling. + const absent = [...documented].filter(name => !new RegExp(`(? Number(m[1])) + if (!claims.length) fail(`${rel} must state how many methods it documents`) + const wrong = [...new Set(claims.filter(n => n !== documented.size))] + if (wrong.length) { + fail(`${rel} claims ${wrong.join('/')} methods; ${documented.size} are documented`) + } + } + ok(`public method count is verifiable (${documented.size} documented, ${routed.size} routed)`) +} + const install = read('bin/install.js') if (install.includes('scaffoldFdeInProject(process.cwd())')) { fail('install.js must not auto-scaffold .fde in customer cwd') diff --git a/docs/REPO_LAYOUT.md b/docs/REPO_LAYOUT.md index 8b22be7..9f1bb1f 100644 --- a/docs/REPO_LAYOUT.md +++ b/docs/REPO_LAYOUT.md @@ -2,7 +2,7 @@ | Path | Purpose | |------|---------| -| `skills/fde/` | **The one skill** - router (`SKILL.md`) + 35 methods, 5 overlays, and AI companion `eval-pack` under `references/` - installed to `~/.claude/skills/` | +| `skills/fde/` | **The one skill** - router (`SKILL.md`) + 37 methods, 5 overlays, and AI companion `eval-pack` under `references/` - installed to `~/.claude/skills/` | | `adapters/` | Thin per-tool pointers (Codex/`AGENTS.md`, Gemini, Cursor, Copilot, local LLMs) - `node bin/install.js adapters ` | | `templates/.fde/` | Core memory templates for `fde resume --init` (phase artifacts are created by phases on demand; `evals.md` is optional) | | `examples/` | Fictional walkthroughs with sample `.fde/` files | diff --git a/docs/skills-reference.md b/docs/skills-reference.md index 6928f8e..b5d8cee 100644 --- a/docs/skills-reference.md +++ b/docs/skills-reference.md @@ -1,4 +1,4 @@ -# fdeops Reference - one skill, 35 methods across 6 domains +# fdeops Reference - one skill, 37 methods across 6 domains v3 ships **one skill**: `@fde` ([skills/fde/SKILL.md](../skills/fde/SKILL.md)). You describe the situation; it routes to a phase and follows that phase's method from [skills/fde/references/](../skills/fde/references/). Engagement memory lives in `~/fde-engagements//.fde/` (one folder per customer). @@ -78,6 +78,8 @@ Each reference is a **method, not advice**: the thinking the agent does, the art | [handoff-engineering](../skills/fde/references/handoff-engineering.md) | Operations runbook, knowledge transfer, confidence scoring | Engagement ending, team needs to operate without you | | [pattern-extract](../skills/fde/references/pattern-extract.md) | If you did it twice, encode it; patterns are compound interest | Something worked well and will apply to future engagements | | [red-team](../skills/fde/references/red-team.md) | Stress-test a plan, handoff, or narrative before someone else does | "Red-team this," "stress-test my plan," poke holes, what am I missing | +| [ingest](../skills/fde/references/ingest.md) | Pull raw text from any source MCP into `.inbox/`, propose, you confirm | "Pull today's transcript," "bring in the Notion page" | +| [ingest-connect](../skills/fde/references/ingest-connect.md) | Guided config for a source MCP you already trust, plus a reusable recipe | "Connect Granola," "wire up Drive" | ### Overlays (activate on signal, alongside whatever skill is running) diff --git a/docs/skills.md b/docs/skills.md index 2d4ae1e..e6fdd48 100644 --- a/docs/skills.md +++ b/docs/skills.md @@ -8,7 +8,7 @@ One skill (`@fde`) routes by situation - you never pick a method by name. Three The full map is below; per-method details live in [skills-reference.md](./skills-reference.md). -## Engagement methods (35 across 6 domains) +## Engagement methods (37 methods across 6 domains) | Domain | Skills | What it covers | |--------|--------|---------------| @@ -17,7 +17,7 @@ The full map is below; per-method details live in [skills-reference.md](./skills | **Plan & Align** | plan, business-case, options-analysis, initiative-triage | Sequencing work, getting sponsor alignment | | **Build & Guard** | build, incremental-build, test-on-legacy, blast-radius, debug, rescue, security-audit, observability | Building safely on their codebase | | **Ship & Verify** | ship, review, rollback-drill, qa-live | Getting to production without surprises | -| **Operate & Close** | status, demo-prep, debrief, exec-narrative, dashboard, multi-customer-ops, close, handoff-engineering, pattern-extract, red-team | Running and ending the engagement well | +| **Operate & Close** | status, demo-prep, debrief, exec-narrative, dashboard, multi-customer-ops, close, handoff-engineering, pattern-extract, red-team, ingest, ingest-connect | Running and ending the engagement well; pulling from source MCPs | Each skill is a **method, not advice**: the thinking the agent does, the artifact it drafts into `.fde/` under `~/fde-engagements/`, and the checkpoint with the human FDE.