Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion adapters/LOCAL-LLM.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
|-------------|-----------|
Expand Down
2 changes: 1 addition & 1 deletion adapters/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
88 changes: 88 additions & 0 deletions bin/check.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/<name>.md\`: ${line.trim().slice(0, 80)}`)
continue
}
// | You hear | <method> | <cell mentioning references/*.md> |
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(`(?<![\\w-])${name}(?![\\w-])`).test(body))
if (absent.length) fail(`${rel} does not list method(s): ${absent.join(', ')}`)
// every count claim, not just the first: an earlier sentence must not shadow
// a stale headline (or the reverse).
const claims = [...body.matchAll(/(\d+)\s+methods/g)].map(m => 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')
Expand Down
2 changes: 1 addition & 1 deletion docs/REPO_LAYOUT.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <dir>` |
| `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 |
Expand Down
4 changes: 3 additions & 1 deletion docs/skills-reference.md
Original file line number Diff line number Diff line change
@@ -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/<name>/.fde/` (one folder per customer).

Expand Down Expand Up @@ -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)

Expand Down
4 changes: 2 additions & 2 deletions docs/skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
|--------|--------|---------------|
Expand All @@ -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.

Expand Down
Loading