fdeops 4.0.4 → 4.1.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 (154) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +33 -6
  3. package/bin/check.js +14 -23
  4. package/bin/generate-skills.js +144 -0
  5. package/bin/install.js +2 -1
  6. package/bin/skill-catalog.js +17 -0
  7. package/mcp/fdeops-ingest/package.json +2 -2
  8. package/package.json +4 -3
  9. package/plugin.json +2 -2
  10. package/skills/fde/SKILL.md +18 -10
  11. package/skills/fde/references/board-memo.md +1 -1
  12. package/skills/fde/references/build.md +20 -0
  13. package/skills/fde/references/business-case.md +9 -7
  14. package/skills/fde/references/close.md +6 -4
  15. package/skills/fde/references/debug.md +18 -0
  16. package/skills/fde/references/encode-pattern.md +10 -8
  17. package/skills/fde/references/eval-pack.md +16 -33
  18. package/skills/fde/references/hold-scope.md +9 -7
  19. package/skills/fde/references/integrate.md +18 -0
  20. package/skills/fde/references/plan.md +4 -2
  21. package/skills/fde/references/poc.md +5 -3
  22. package/skills/fde/references/qa.md +18 -0
  23. package/skills/fde/references/readout.md +6 -4
  24. package/skills/fde/references/review.md +22 -60
  25. package/skills/fde/references/ship.md +42 -287
  26. package/skills/fde/references/task-context.md +12 -0
  27. package/skills/fde/references/test-assumptions.md +2 -2
  28. package/skills/fde/references/three-options.md +19 -27
  29. package/skills/fde/references/verification.md +31 -0
  30. package/skills/fde-build/.fde-generated.json +16 -0
  31. package/skills/fde-build/SKILL.md +21 -0
  32. package/skills/fde-build/references/build.md +20 -0
  33. package/skills/fde-build/references/debug.md +18 -0
  34. package/skills/fde-build/references/eval-pack.md +26 -0
  35. package/skills/fde-build/references/integrate.md +18 -0
  36. package/skills/fde-build/references/qa.md +18 -0
  37. package/skills/fde-build/references/review.md +39 -0
  38. package/skills/fde-build/references/ship.md +73 -0
  39. package/skills/fde-build/references/task-context.md +12 -0
  40. package/skills/fde-build/references/verification.md +31 -0
  41. package/skills/fde-debug/.fde-generated.json +16 -0
  42. package/skills/fde-debug/SKILL.md +21 -0
  43. package/skills/fde-debug/references/build.md +20 -0
  44. package/skills/fde-debug/references/debug.md +18 -0
  45. package/skills/fde-debug/references/eval-pack.md +26 -0
  46. package/skills/fde-debug/references/integrate.md +18 -0
  47. package/skills/fde-debug/references/qa.md +18 -0
  48. package/skills/fde-debug/references/review.md +39 -0
  49. package/skills/fde-debug/references/ship.md +73 -0
  50. package/skills/fde-debug/references/task-context.md +12 -0
  51. package/skills/fde-debug/references/verification.md +31 -0
  52. package/skills/fde-discover/.fde-generated.json +10 -0
  53. package/skills/fde-discover/SKILL.md +21 -0
  54. package/skills/fde-discover/references/audit.md +71 -0
  55. package/skills/fde-discover/references/discover.md +254 -0
  56. package/skills/fde-discover/references/task-context.md +12 -0
  57. package/skills/fde-evaluate/.fde-generated.json +16 -0
  58. package/skills/fde-evaluate/SKILL.md +21 -0
  59. package/skills/fde-evaluate/references/build.md +20 -0
  60. package/skills/fde-evaluate/references/debug.md +18 -0
  61. package/skills/fde-evaluate/references/eval-pack.md +26 -0
  62. package/skills/fde-evaluate/references/integrate.md +18 -0
  63. package/skills/fde-evaluate/references/qa.md +18 -0
  64. package/skills/fde-evaluate/references/review.md +39 -0
  65. package/skills/fde-evaluate/references/ship.md +73 -0
  66. package/skills/fde-evaluate/references/task-context.md +12 -0
  67. package/skills/fde-evaluate/references/verification.md +31 -0
  68. package/skills/fde-feedback/.fde-generated.json +9 -0
  69. package/skills/fde-feedback/SKILL.md +21 -0
  70. package/skills/fde-feedback/references/encode-pattern.md +96 -0
  71. package/skills/fde-feedback/references/task-context.md +12 -0
  72. package/skills/fde-handoff/.fde-generated.json +10 -0
  73. package/skills/fde-handoff/SKILL.md +21 -0
  74. package/skills/fde-handoff/references/close.md +66 -0
  75. package/skills/fde-handoff/references/encode-pattern.md +96 -0
  76. package/skills/fde-handoff/references/task-context.md +12 -0
  77. package/skills/fde-integrate/.fde-generated.json +16 -0
  78. package/skills/fde-integrate/SKILL.md +21 -0
  79. package/skills/fde-integrate/references/build.md +20 -0
  80. package/skills/fde-integrate/references/debug.md +18 -0
  81. package/skills/fde-integrate/references/eval-pack.md +26 -0
  82. package/skills/fde-integrate/references/integrate.md +18 -0
  83. package/skills/fde-integrate/references/qa.md +18 -0
  84. package/skills/fde-integrate/references/review.md +39 -0
  85. package/skills/fde-integrate/references/ship.md +73 -0
  86. package/skills/fde-integrate/references/task-context.md +12 -0
  87. package/skills/fde-integrate/references/verification.md +31 -0
  88. package/skills/fde-options/.fde-generated.json +11 -0
  89. package/skills/fde-options/SKILL.md +21 -0
  90. package/skills/fde-options/references/business-case.md +90 -0
  91. package/skills/fde-options/references/task-context.md +12 -0
  92. package/skills/fde-options/references/test-assumptions.md +102 -0
  93. package/skills/fde-options/references/three-options.md +90 -0
  94. package/skills/fde-poc/.fde-generated.json +23 -0
  95. package/skills/fde-poc/SKILL.md +21 -0
  96. package/skills/fde-poc/references/audit.md +71 -0
  97. package/skills/fde-poc/references/build.md +20 -0
  98. package/skills/fde-poc/references/business-case.md +90 -0
  99. package/skills/fde-poc/references/debug.md +18 -0
  100. package/skills/fde-poc/references/discover.md +254 -0
  101. package/skills/fde-poc/references/eval-pack.md +26 -0
  102. package/skills/fde-poc/references/integrate.md +18 -0
  103. package/skills/fde-poc/references/plan.md +167 -0
  104. package/skills/fde-poc/references/poc.md +55 -0
  105. package/skills/fde-poc/references/qa.md +18 -0
  106. package/skills/fde-poc/references/review.md +39 -0
  107. package/skills/fde-poc/references/ship.md +73 -0
  108. package/skills/fde-poc/references/task-context.md +12 -0
  109. package/skills/fde-poc/references/test-assumptions.md +102 -0
  110. package/skills/fde-poc/references/three-options.md +90 -0
  111. package/skills/fde-poc/references/verification.md +31 -0
  112. package/skills/fde-qa/.fde-generated.json +16 -0
  113. package/skills/fde-qa/SKILL.md +21 -0
  114. package/skills/fde-qa/references/build.md +20 -0
  115. package/skills/fde-qa/references/debug.md +18 -0
  116. package/skills/fde-qa/references/eval-pack.md +26 -0
  117. package/skills/fde-qa/references/integrate.md +18 -0
  118. package/skills/fde-qa/references/qa.md +18 -0
  119. package/skills/fde-qa/references/review.md +39 -0
  120. package/skills/fde-qa/references/ship.md +73 -0
  121. package/skills/fde-qa/references/task-context.md +12 -0
  122. package/skills/fde-qa/references/verification.md +31 -0
  123. package/skills/fde-readout/.fde-generated.json +11 -0
  124. package/skills/fde-readout/SKILL.md +21 -0
  125. package/skills/fde-readout/references/board-memo.md +108 -0
  126. package/skills/fde-readout/references/business-case.md +90 -0
  127. package/skills/fde-readout/references/readout.md +71 -0
  128. package/skills/fde-readout/references/task-context.md +12 -0
  129. package/skills/fde-review/.fde-generated.json +16 -0
  130. package/skills/fde-review/SKILL.md +21 -0
  131. package/skills/fde-review/references/build.md +20 -0
  132. package/skills/fde-review/references/debug.md +18 -0
  133. package/skills/fde-review/references/eval-pack.md +26 -0
  134. package/skills/fde-review/references/integrate.md +18 -0
  135. package/skills/fde-review/references/qa.md +18 -0
  136. package/skills/fde-review/references/review.md +39 -0
  137. package/skills/fde-review/references/ship.md +73 -0
  138. package/skills/fde-review/references/task-context.md +12 -0
  139. package/skills/fde-review/references/verification.md +31 -0
  140. package/skills/fde-scope/.fde-generated.json +9 -0
  141. package/skills/fde-scope/SKILL.md +21 -0
  142. package/skills/fde-scope/references/hold-scope.md +83 -0
  143. package/skills/fde-scope/references/task-context.md +12 -0
  144. package/skills/fde-ship/.fde-generated.json +16 -0
  145. package/skills/fde-ship/SKILL.md +21 -0
  146. package/skills/fde-ship/references/build.md +20 -0
  147. package/skills/fde-ship/references/debug.md +18 -0
  148. package/skills/fde-ship/references/eval-pack.md +26 -0
  149. package/skills/fde-ship/references/integrate.md +18 -0
  150. package/skills/fde-ship/references/qa.md +18 -0
  151. package/skills/fde-ship/references/review.md +39 -0
  152. package/skills/fde-ship/references/ship.md +73 -0
  153. package/skills/fde-ship/references/task-context.md +12 -0
  154. package/skills/fde-ship/references/verification.md +31 -0
package/AGENTS.md CHANGED
@@ -8,7 +8,7 @@ Route via **`@fde`** - read `skills/fde/SKILL.md` (the single source of truth),
8
8
 
9
9
  ## If you are contributing to this repository
10
10
 
11
- - **One brain.** Method lives once in `skills/fde/SKILL.md` + `skills/fde/references/`. Adapters in `adapters/` only point at it - never fork logic per platform.
11
+ - **One brain.** Method lives once in `skills/fde/SKILL.md` + `skills/fde/references/`. Adapters in `adapters/` only point at it - never fork logic per platform. Standalone skills are generated by `node bin/generate-skills.js` from `bin/skill-catalog.js` and canonical references; do not hand-edit generated copies.
12
12
  - **Deterministic core.** `bin/fde.js` is local-only (git + file reads, no network, no AI). Keep it that way.
13
13
  - **Run the checks.** `npm run check` must pass before any PR (`node bin/check.js`).
14
14
  - **Conventions.** See `CONTRIBUTING.md`, `docs/REPO_LAYOUT.md`, and `docs/schema.md`.
package/README.md CHANGED
@@ -4,18 +4,41 @@
4
4
 
5
5
  <a name="why-use-it"></a>
6
6
 
7
- You're on a customer site. The AI coding agent writes code in their repo. This kit is the work around that code: the brief, who can say yes, proof on their staging then live, whether they signed off, whether they can run it after you leave.
7
+ Take a customer request from discovery through implementation, verification and handoff. Use one task skill for the work in front of you, or `@fde` to coordinate the engagement and keep its decisions and evidence together.
8
8
 
9
9
  Notes stay on your laptop, in a separate record for each client. Review the agent's proposed changes before saving them.
10
10
 
11
- Keep the coding pack you already use. FDEOps adds the client brief, decisions, and evidence around that work.
11
+ Implementation, integration, debugging and QA are included. Use your existing repository tools; no additional skill pack is required. Credentials, infrastructure and production authority still come from the customer.
12
12
 
13
- [Quick start](#quick-start) · [Daily fieldbook](#your-daily-fieldbook) · [30 skills](#all-30-skills) · [Documentation](docs/README.md)
13
+ [Quick start](#quick-start) · [Daily fieldbook](#your-daily-fieldbook) · [Task skills](#task-skills) · [Documentation](docs/README.md)
14
14
 
15
15
  <img width="960" height="640" alt="FDEOps: client delivery from the first meeting to handover" src="https://github.com/user-attachments/assets/2bcb8739-55ee-445d-8a1a-8b38433b7b58" />
16
16
 
17
17
  ---
18
18
 
19
+ ## Task skills
20
+
21
+ Call the task directly when you know what you need. No client folder is required for a one-off task.
22
+
23
+ | Work | Skill |
24
+ |------|-------|
25
+ | Understand the workflow | `fde-discover` |
26
+ | Handle a new scope request | `fde-scope` |
27
+ | Compare approaches | `fde-options` |
28
+ | Test a risky assumption | `fde-poc` |
29
+ | Implement, integrate, repair | `fde-build`, `fde-integrate`, `fde-debug` |
30
+ | Review, evaluate AI, test the journey | `fde-review`, `fde-evaluate`, `fde-qa` |
31
+ | Release a verified increment | `fde-ship` |
32
+ | Present evidence, transfer operation, reuse a lesson | `fde-readout`, `fde-handoff`, `fde-feedback` |
33
+
34
+ For example, install just the integration skill:
35
+
36
+ ```bash
37
+ npx skills add suboss87/fdeops --skill fde-integrate
38
+ ```
39
+
40
+ Ask it to connect a permitted customer API and test retry behavior. It includes the references it needs. `@fde` uses those same methods when the integration is part of an ongoing engagement. [Full pack and selective installation](docs/install.md#individual-skills-and-the-full-pack).
41
+
19
42
  ## Quick Start
20
43
 
21
44
  **Try it in a local checkout.** Requires Node.js 18+ and Git:
@@ -138,9 +161,9 @@ You can also just say it: “Prep me for the sponsor meeting,” “What did we
138
161
 
139
162
  ---
140
163
 
141
- ## All 30 Skills
164
+ ## Engagement workflows
142
165
 
143
- Thirty situations, grouped by stage. Each skill gives the agent steps to follow, a record or report to produce, and a checkpoint with you. You describe the work; `@fde` finds the skill.
166
+ Thirty-four workflows, grouped by stage. Each skill gives the agent steps to follow, a record or report to produce, and a checkpoint with you. You describe the work; `@fde` finds the skill.
144
167
 
145
168
  Full detail: [docs/skills-reference.md](docs/skills-reference.md).
146
169
 
@@ -176,7 +199,11 @@ Full detail: [docs/skills-reference.md](docs/skills-reference.md).
176
199
 
177
200
  | Skill | What it does | Use when |
178
201
  |--------|--------------|----------|
179
- | [ship](skills/fde/references/ship.md) | Deliver the increment | Building, updating, or going live |
202
+ | [build](skills/fde/references/build.md) | Implement the increment | A scoped change in their repository |
203
+ | [integrate](skills/fde/references/integrate.md) | Prove the system boundary | APIs, imports and write-back |
204
+ | [debug](skills/fde/references/debug.md) | Reproduce and repair a failure | Unexpected behavior or a regression |
205
+ | [qa](skills/fde/references/qa.md) | Exercise the delivered journey | Functional acceptance or browser QA |
206
+ | [ship](skills/fde/references/ship.md) | Release with evidence | Ready for an authorized rollout |
180
207
  | [what-breaks](skills/fde/references/what-breaks.md) | Assess impact | Touching shared infrastructure |
181
208
  | [rescue](skills/fde/references/rescue.md) | Resolve the incident | Down, or they went quiet |
182
209
  | [review](skills/fde/references/review.md) | Review the change | Before merge, scope creep |
package/bin/check.js CHANGED
@@ -6,6 +6,7 @@ const fs = require('fs')
6
6
  const path = require('path')
7
7
 
8
8
  const root = path.join(__dirname, '..')
9
+ const catalog = require('./skill-catalog')
9
10
  let failed = 0
10
11
 
11
12
  function fail(msg) {
@@ -190,8 +191,8 @@ ok(`router dispatch (${mentioned.length} reference targets verified) + memory co
190
191
  // a skill could disappear from the docs behind a hyphenated sibling.
191
192
  const absent = [...documented].filter(name => !new RegExp(`(?<![\\w-])${name}(?![\\w-])`).test(body))
192
193
  if (absent.length) fail(`${rel} does not list skill(s): ${absent.join(', ')}`)
193
- const claims = [...body.matchAll(/(\d+)\s+skills/g)].map(m => Number(m[1]))
194
- if (!claims.length) fail(`${rel} must state how many skills it documents`)
194
+ const claims = [...body.matchAll(/(\d+)\s+workflows/g)].map(m => Number(m[1]))
195
+ if (!claims.length) fail(`${rel} must state how many workflows it documents`)
195
196
  const wrong = [...new Set(claims.filter(n => n !== documented.size))]
196
197
  if (wrong.length) {
197
198
  fail(`${rel} claims ${wrong.join('/')} skills; ${documented.size} are documented`)
@@ -204,20 +205,10 @@ ok(`router dispatch (${mentioned.length} reference targets verified) + memory co
204
205
  if (extra.length) fail(`unrouted reference file(s) - dead skill: ${extra.join(', ')}`)
205
206
  else ok('no unrouted reference files')
206
207
 
207
- // The on-site change loop lives in ship.md. A sibling skill is a split.
208
- for (const dead of ['small-prs.md', 'thin-slices.md', 'implement.md']) {
209
- if (fs.existsSync(path.join(refDir, dead))) {
210
- fail(`${dead} must not exist - that craft lives in ship.md`)
211
- }
212
- }
213
- ok('ship is one skill (no implement / small-prs / thin-slices sibling)')
214
-
215
208
  if (/^### Prove\b/m.test(read('skills/fde/SKILL.md'))) {
216
209
  fail('SKILL.md must not use Prove as a stage heading - the public stage is Outcome')
217
210
  } else ok('SKILL.md stage heading is Outcome')
218
- if (/\b31 names\b|\b31 skills\b/.test(read('README.md'))) {
219
- fail('README must not advertise 31 skills')
220
- } else ok('README skill count is 30')
211
+
221
212
  }
222
213
 
223
214
  const install = read('bin/install.js')
@@ -292,11 +283,11 @@ if (!readme.includes('fde-engagements') || !/fdeops.*resume --init/i.test(readme
292
283
  // The advertised command must name the one skill a field user wants.
293
284
  // Contributor CLI attack notes live in evals/testing-fieldbook.md, not as a skill.
294
285
  for (const m of readme.match(/^.*npx skills add .*$/gm) || []) {
295
- if (!m.includes('--skill fde')) {
296
- fail(`README skills-add command must pin --skill fde: ${m.trim()}`)
286
+ if (!['fde', ...catalog.map(s => s.name)].some(name => new RegExp(`--skill ${name}(?:\\s|$)`).test(m))) {
287
+ fail(`README skills-add command must name a shipped skill: ${m.trim()}`)
297
288
  }
298
289
  }
299
- ok('README skills install is one skill')
290
+ ok('README skill install commands name a discoverable entry')
300
291
 
301
292
  // A skill-only install has no fde on the PATH; the router must reach npx before
302
293
  // falling back to writing memory by hand.
@@ -661,9 +652,10 @@ for (const entry of fs.readdirSync(path.join(root, 'skills'))) {
661
652
  ok(`skill ${entry} discoverable`)
662
653
  }
663
654
  }
664
- if (skillDirs.length !== 1 || skillDirs[0] !== 'fde') {
665
- fail(`public tree ships one skill (skills/fde); found: ${skillDirs.join(', ') || '(none)'}`)
666
- } else ok('one public skill')
655
+ const expectedSkills = ['fde', ...catalog.map(s => s.name)].sort()
656
+ if (JSON.stringify(skillDirs.sort()) !== JSON.stringify(expectedSkills)) fail('Skill directories differ from catalog')
657
+ try { require('./generate-skills').generate(true); ok('generated standalone reference closure and freshness') }
658
+ catch (error) { fail(error.message) }
667
659
 
668
660
  function findSkillMd(dir, acc = []) {
669
661
  for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
@@ -675,10 +667,9 @@ function findSkillMd(dir, acc = []) {
675
667
  return acc
676
668
  }
677
669
  const skillFiles = findSkillMd(root)
678
- const allowedSkill = path.join('skills', 'fde', 'SKILL.md')
679
- if (skillFiles.length !== 1 || skillFiles[0] !== allowedSkill) {
680
- fail(`only ${allowedSkill} may exist; found: ${skillFiles.join(', ') || '(none)'}`)
681
- } else ok('one SKILL.md')
670
+ const allowedSkills = expectedSkills.map(name => path.join('skills', name, 'SKILL.md')).sort()
671
+ if (JSON.stringify(skillFiles.sort()) !== JSON.stringify(allowedSkills)) fail('Unexpected SKILL.md outside public catalog')
672
+ else ok('all skill entry points accounted for')
682
673
 
683
674
  if (!fs.existsSync(path.join(root, '.github', 'ISSUE_TEMPLATE', 'bug_report.yml'))) {
684
675
  fail('GitHub issue template missing')
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env node
2
+ // Generated packages are independent installations, not independently authored methods.
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
+ const { createHash } = require('node:crypto')
6
+ const catalog = require('./skill-catalog')
7
+ const root = path.resolve(__dirname, '..')
8
+ const source = path.join(root, 'skills/fde/references')
9
+
10
+ function referencesFor(method, referenceRoot = source) {
11
+ const known = new Set(fs.readdirSync(referenceRoot).filter(f => f.endsWith('.md')))
12
+ const pending = [`${method}.md`, 'task-context.md']
13
+ const found = new Set()
14
+ while (pending.length) {
15
+ const file = pending.pop()
16
+ if (found.has(file)) continue
17
+ if (!known.has(file)) throw new Error(`Missing canonical reference: ${file}`)
18
+ found.add(file)
19
+ const body = fs.readFileSync(path.join(referenceRoot, file), 'utf8')
20
+ // Includes plain/backticked method links in older references as well as Markdown links.
21
+ for (const match of body.matchAll(/(?<![\w-])([a-z][a-z-]*\.md)\b/g)) {
22
+ if (known.has(match[1]) && !found.has(match[1])) pending.push(match[1])
23
+ }
24
+ for (const match of body.matchAll(/\]\(([^)]+\.md)(?:#[^)]*)?\)/g)) {
25
+ if (/^(?:https?:|#)/.test(match[1])) continue
26
+ const target = path.resolve(referenceRoot, match[1])
27
+ if (path.dirname(target) !== referenceRoot || !known.has(path.basename(target))) {
28
+ throw new Error(`Nonportable instruction link in ${file}: ${match[1]}`)
29
+ }
30
+ }
31
+ }
32
+ return [...found].sort()
33
+ }
34
+ function entrypoint(item) {
35
+ return `---\nname: ${item.name}\ndescription: ${item.description}\n---\n\n# ${item.name}\n\n<!-- Generated by bin/generate-skills.js; edit the canonical references and catalog. -->\n\n## Purpose\n\n${item.description}\n\nRead [the task context contract](references/task-context.md), then [the method](references/${item.method}.md). Load further references only when the task needs them. Everything linked is included in this skill; no other skill pack is required.\n\n## Principles\n\n- Work directly from the supplied permitted context. Standalone work does not require an engagement folder or initialization. Record filenames in the method are optional persistence destinations when no engagement is bound.\n- If called by @fde, reuse its current sanitized packet and scope. Do not restart setup, discovery or questions already answered.\n- The task context contract controls persistence and authority in both modes. Preserve unknowns and distinguish implementation, verification, deployment and acceptance.\n- Use the customer's repository instructions and available tools. Report a missing capability or unrun check honestly; do not claim that installing a skill provisions infrastructure.\n`
36
+ }
37
+ function expectedFiles(item, referenceRoot = source) {
38
+ const files = new Map([['SKILL.md', entrypoint(item)]])
39
+ for (const ref of referencesFor(item.method, referenceRoot)) files.set(`references/${ref}`, fs.readFileSync(path.join(referenceRoot, ref), 'utf8'))
40
+ return files
41
+ }
42
+ const marker = '<!-- Generated by bin/generate-skills.js; edit the canonical references and catalog. -->'
43
+ const manifestName = '.fde-generated.json'
44
+ const digest = body => createHash('sha256').update(body).digest('hex')
45
+ const stat = file => {
46
+ try { return fs.lstatSync(file) } catch (error) { if (error.code === 'ENOENT') return null; throw error }
47
+ }
48
+ function walk(dir, prefix = '') {
49
+ const entries = []
50
+ for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
51
+ const rel = prefix + e.name
52
+ if (e.isSymbolicLink() || (!e.isDirectory() && !e.isFile()) || (e.isFile() && fs.lstatSync(path.join(dir, e.name)).nlink > 1)) throw new Error(`Unsafe generated path: ${path.join(dir, e.name)}`)
53
+ if (e.isDirectory()) {
54
+ if (rel !== 'references') throw new Error(`Unowned generated directory: ${path.join(dir, e.name)}`)
55
+ entries.push(...walk(path.join(dir, e.name), rel + '/'))
56
+ } else entries.push(rel)
57
+ }
58
+ return entries
59
+ }
60
+ function generate(check = false, options = {}) {
61
+ const skillRoot = path.join(options.root || root, 'skills')
62
+ const referenceRoot = options.referenceRoot || source
63
+ const items = options.catalog || catalog
64
+ const wanted = new Map()
65
+ for (const item of items) {
66
+ if (!/^fde-[a-z-]+$/.test(item.name) || wanted.has(item.name)) throw new Error(`Invalid generated skill name: ${item.name}`)
67
+ wanted.set(item.name, expectedFiles(item, referenceRoot))
68
+ }
69
+ const rootStat = stat(skillRoot)
70
+ if (rootStat && (!rootStat.isDirectory() || rootStat.isSymbolicLink())) throw new Error(`Unsafe skills root: ${skillRoot}`)
71
+ const names = new Set([...wanted.keys(), ...(rootStat ? fs.readdirSync(skillRoot).filter(n => /^fde-[a-z-]+$/.test(n)) : [])])
72
+ const errors = []
73
+ const plans = []
74
+ for (const name of names) {
75
+ const dest = path.join(skillRoot, name)
76
+ const expected = wanted.get(name) || new Map()
77
+ const destStat = stat(dest)
78
+ let owned = {}
79
+ let actual = []
80
+ if (destStat) {
81
+ if (!destStat.isDirectory() || destStat.isSymbolicLink()) {
82
+ if (wanted.has(name)) throw new Error(`Unsafe generated path: ${dest}`)
83
+ continue
84
+ }
85
+ const entry = path.join(dest, 'SKILL.md')
86
+ const entryStat = stat(entry)
87
+ const isGenerated = entryStat && entryStat.isFile() && fs.readFileSync(entry, 'utf8').includes(marker)
88
+ if (!isGenerated) {
89
+ if (wanted.has(name) || stat(path.join(dest, manifestName))) throw new Error(`Unowned skill: ${name}`)
90
+ continue
91
+ }
92
+ actual = walk(dest)
93
+ if (actual.includes(manifestName)) {
94
+ const manifest = JSON.parse(fs.readFileSync(path.join(dest, manifestName), 'utf8'))
95
+ if (manifest.generator !== 'bin/generate-skills.js' || manifest.version !== 1 || !manifest.files || Array.isArray(manifest.files) || typeof manifest.files !== 'object') throw new Error(`Invalid ownership manifest: ${name}`)
96
+ owned = manifest.files
97
+ for (const [rel, hash] of Object.entries(owned)) {
98
+ if (!/^(?:SKILL\.md|references\/[a-z][a-z-]*\.md)$/.test(rel) || !/^[a-f0-9]{64}$/.test(hash)) throw new Error(`Invalid ownership entry: ${name}/${rel}`)
99
+ }
100
+ } else {
101
+ // Migrate 4.1.0 packages conservatively: only the marked entry and exact
102
+ // canonical copies are known to belong to the generator.
103
+ for (const rel of actual) {
104
+ const body = fs.readFileSync(path.join(dest, rel))
105
+ const canonical = path.join(referenceRoot, path.basename(rel))
106
+ if (rel === 'SKILL.md' || (/^references\/[a-z][a-z-]*\.md$/.test(rel) && stat(canonical)?.isFile() && body.equals(fs.readFileSync(canonical)))) owned[rel] = digest(body)
107
+ }
108
+ }
109
+ for (const rel of actual.filter(rel => rel !== manifestName)) {
110
+ if (!Object.hasOwn(owned, rel)) throw new Error(`Unowned generated file: ${name}/${rel}`)
111
+ if (!expected.has(rel) && digest(fs.readFileSync(path.join(dest, rel))) !== owned[rel]) throw new Error(`Modified obsolete generated file: ${name}/${rel}`)
112
+ }
113
+ }
114
+ const output = new Map(expected)
115
+ if (wanted.has(name)) output.set(manifestName, JSON.stringify({ generator: 'bin/generate-skills.js', version: 1, files: Object.fromEntries([...expected].map(([rel, body]) => [rel, digest(body)])) }, null, 2) + '\n')
116
+ for (const rel of actual) if (!output.has(rel)) errors.push(`Obsolete generated file: ${name}/${rel}`)
117
+ for (const [rel, body] of output) {
118
+ if (!actual.includes(rel) || fs.readFileSync(path.join(dest, rel), 'utf8') !== body) errors.push(`Stale generated skill: ${name}/${rel}`)
119
+ }
120
+ plans.push({ dest, actual, output })
121
+ }
122
+ if (check) {
123
+ if (errors.length) throw new Error(errors.join('\n'))
124
+ return
125
+ }
126
+ // All ownership and path checks finish before any package is changed.
127
+ for (const { dest, actual, output } of plans) {
128
+ for (const rel of actual) if (!output.has(rel)) fs.unlinkSync(path.join(dest, rel))
129
+ for (const [rel, body] of output) {
130
+ fs.mkdirSync(path.dirname(path.join(dest, rel)), { recursive: true })
131
+ fs.writeFileSync(path.join(dest, rel), body)
132
+ }
133
+ if (!output.size) {
134
+ const refs = path.join(dest, 'references')
135
+ if (stat(refs)) fs.rmdirSync(refs)
136
+ fs.rmdirSync(dest)
137
+ }
138
+ }
139
+ }
140
+ module.exports = { referencesFor, expectedFiles, generate }
141
+ if (require.main === module) {
142
+ try { generate(process.argv.includes('--check')); console.log('Skill packages match canonical methods.') }
143
+ catch (error) { console.error(error.message); process.exitCode = 1 }
144
+ }
package/bin/install.js CHANGED
@@ -100,7 +100,7 @@ function wasInstalledByUs(dir) {
100
100
  } catch (_) { return false }
101
101
  }
102
102
 
103
- // v2 shipped 16 standalone skills; v3 is one `fde` skill + references.
103
+ // Retire v2 entries only when the current pack does not ship their replacement.
104
104
  // Leaving the old ones in place would route users to stale content.
105
105
  const LEGACY_SKILL_DIRS = [
106
106
  'fde-land', 'fde-discover', 'fde-audit', 'fde-rescue', 'fde-sketch',
@@ -115,6 +115,7 @@ function removeLegacySkills(opts = {}) {
115
115
  const skipped = []
116
116
  const links = []
117
117
  for (const dir of LEGACY_SKILL_DIRS) {
118
+ if (fs.existsSync(path.join(SKILLS_SRC, dir, 'SKILL.md'))) continue
118
119
  const p = path.join(GLOBAL_SKILLS_DIR, dir)
119
120
  if (isLink(p)) { links.push(dir); continue }
120
121
  if (!fs.existsSync(path.join(p, 'SKILL.md'))) continue
@@ -0,0 +1,17 @@
1
+ // Public entry points. Methods remain authored once in skills/fde/references/.
2
+ module.exports = [
3
+ ['fde-discover', 'discover', 'Trace a customer workflow and identify the problem, baseline and evidence gaps. Use for discovery or an unclear customer brief, before choosing a solution.'],
4
+ ['fde-scope', 'hold-scope', 'Assess a new customer request against agreed scope, trade-offs and ownership. Use when an engagement expands or a custom feature needs a commitment decision.'],
5
+ ['fde-options', 'three-options', 'Compare feasible approaches to a customer problem and recommend a path with costs, constraints and evidence. Use for an architecture or delivery decision, not implementation.'],
6
+ ['fde-poc', 'poc', 'Run a bounded customer proof of concept to test a consequential uncertainty. Use for a spike or pilot with a question and decision deadline, not a full rollout.'],
7
+ ['fde-build', 'build', 'Implement a scoped customer-facing software change in the existing repository and verify its behavior. Use for delivery work with an understood outcome, not incident response.'],
8
+ ['fde-integrate', 'integrate', 'Build or change a customer-system integration with explicit data mapping, permissions, retries and reconciliation. Use for connectors, imports, write-back and upstream APIs.'],
9
+ ['fde-debug', 'debug', 'Investigate and repair a reproducible failure in a customer integration or application. Use for diagnosis and regression prevention; follow incident authority for live mitigation.'],
10
+ ['fde-review', 'review', 'Review a proposed customer code change against its intended outcome and operational risks. Use for a diff or PR review; report evidence and actionable findings.'],
11
+ ['fde-evaluate', 'eval-pack', 'Evaluate an AI workflow against representative cases and its permitted actions. Use for model, retrieval or agent evaluation; tests do not grant release authority.'],
12
+ ['fde-qa', 'qa', 'Exercise the delivered customer journey using real runtime or browser evidence. Use for functional acceptance testing after implementation, including failure paths.'],
13
+ ['fde-ship', 'ship', 'Prepare or execute an authorized controlled release with verified checks, recovery and an operating owner. Use when a customer increment is ready for deployment.'],
14
+ ['fde-readout', 'readout', 'Prepare a sponsor update separating promised outcomes, measured results and customer acceptance. Use for progress readouts or defending a delivery claim.'],
15
+ ['fde-handoff', 'close', 'Transfer operation of a customer deployment with ownership, evidence and a tested support path. Use for handoff or an engineer rotation, not merely code delivery.'],
16
+ ['fde-feedback', 'encode-pattern', 'Assess a field lesson for reuse or product feedback without exposing customer context. Use for recurring deployment lessons; distinguish a hypothesis from a validated pattern.'],
17
+ ].map(([name, method, description]) => ({ name, method, description }))
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "fdeops-ingest-mcp",
3
- "version": "4.0.4",
3
+ "version": "4.1.1",
4
4
  "private": true,
5
- "description": "Thin stdio MCP sink for FDEOps ingest (stage → propose → apply). Zero runtime dependencies.",
5
+ "description": "Thin stdio MCP sink for FDEOps ingest (stage \u2192 propose \u2192 apply). Zero runtime dependencies.",
6
6
  "bin": {
7
7
  "fdeops-ingest-mcp": "./server.js"
8
8
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fdeops",
3
- "version": "4.0.4",
4
- "description": "Client delivery tools for Forward Deployed Engineers. One @fde skill, local Markdown engagement records, and an offline dashboard for decisions, evidence, approvals, and next actions.",
3
+ "version": "4.1.1",
4
+ "description": "Forward deployed engineering skills for AI coding agents. Use focused task skills or @fde for discovery, implementation, verification and handoff, with local engagement records.",
5
5
  "bin": {
6
6
  "fdeops": "bin/install.js",
7
7
  "fde": "bin/fde.js"
@@ -10,7 +10,8 @@
10
10
  "check": "node bin/check.js && npm test",
11
11
  "test": "node --test test/*.test.js",
12
12
  "test:skill-routing": "node evals/skill-routing/check.js && node evals/skill-routing/live-smoke.js",
13
- "prepublishOnly": "node bin/check.js && npm test"
13
+ "prepublishOnly": "node bin/check.js && npm test",
14
+ "generate:skills": "node bin/generate-skills.js"
14
15
  },
15
16
  "files": [
16
17
  "bin/",
package/plugin.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "fdeops",
4
- "version": "4.0.4",
5
- "description": "Forward deployed engineering skills for AI coding agents. One @fde skill for the client work around the code. You confirm; then it lands in .fde/ on your laptop.",
4
+ "version": "4.1.1",
5
+ "description": "Forward deployed engineering skills for AI coding agents. Use focused task skills or @fde for discovery, implementation, verification and handoff, with local engagement records.",
6
6
  "author": {
7
7
  "name": "Subash Natarajan",
8
8
  "url": "https://github.com/suboss87"
@@ -7,7 +7,11 @@ description: Keeps the engagement record for client work. Use when they name a c
7
7
 
8
8
  ## Purpose
9
9
 
10
- The **engagement record** for one client, from first meeting to signed outcome. One skill; six stages (land → close). Same map at any scale, on greenfield or brownfield, in any industry (overlays). You route; they never pick a skill. Confirm, then write `.fde/`. The workspace still compiles and commits. `@fde` does not leave.
10
+ The **engagement record** for one client, from first meeting to signed outcome. One coordinator; six stages (land → close), with task skills that also work independently. Same methods on greenfield or brownfield engagements. Route from the request; never make the user choose a phase. Confirm, then write `.fde/`. The workspace still compiles and commits. `@fde` does not leave.
11
+
12
+ ## Task entry
13
+
14
+ Read `references/task-context.md` first. An explicitly selected `fde-*` task runs directly; do not wrap it in another coordinator or repeat entry. For a one-off task with supplied context, use the relevant method without initializing `.fde/`. For ongoing client work use the bounded entry and memory contract below. Missing record files alone are not a reason to restart discovery.
11
15
 
12
16
  ## When to use
13
17
 
@@ -42,15 +46,15 @@ On someone else's site the work is not "write code, remember later." Every chang
42
46
 
43
47
  Scale the loop to the change. A routine, reversible fix within confirmed scope reuses the existing outcome, signer, acceptance criteria, and engineering plan; batch its verification into a concise delivery receipt. It does not need a new sponsor decision or staging ceremony per edit. New outcomes, changed acceptance or authority, and production release decisions still need the relevant confirmation and evidence. This does not bypass confirmation for judgment written into the engagement record.
44
48
 
45
- **Status is explicit.** Record what is implemented, verified, deployed, and accepted separately. A routine fix may be implementation-complete before release or customer acceptance; state what remains and attach the current verification receipt. A coding pack may write the function; `@fde` owns the engagement evidence.
49
+ **Status is explicit.** Record what is implemented, verified, deployed, and accepted separately. A routine fix may be implementation-complete before release or customer acceptance; state what remains and attach the current verification receipt. The included build, integrate, debug and QA methods cover implementation; `@fde` connects their evidence to the engagement.
46
50
 
47
- ## Working with an engineering pack
51
+ ## Engineering within the pack
48
52
 
49
- Use the customer's existing coding, testing, review, and repository instructions for implementation. Carry the confirmed outcome, scope boundary, acceptance criteria, and evidence requirements into that workflow. Reference its existing plan from `decisions.md`; do not create a competing backlog or repeat questions already answered. FDEOps owns the engagement record and acceptance status. A coding pack's green tests do not establish customer acceptance. Never claim compatibility was tested with a host or pack you have not run.
53
+ Use `references/build.md` for implementation, `references/integrate.md` for customer-system boundaries, `references/debug.md` for failures and `references/qa.md` for the delivered journey. Each uses `references/verification.md` for actual evidence. These methods use the customer's coding conventions and installed tools; another skill pack is not required. Use an existing engineering plan rather than creating a competing backlog. Green tests establish tested behavior, not customer acceptance or production authority.
50
54
 
51
55
  ## Human surface vs agent plumbing
52
56
 
53
- **FDE (human):** `@fde` + English, or `/brief` `/discover` `/plan` `/ship` `/outcome` `/close` `/debrief` `/prep` `/trust` `/receipts` `/readout`. Never a skill catalog.
57
+ **FDE (human):** `@fde` + English, or `/brief` `/discover` `/plan` `/ship` `/outcome` `/close` `/debrief` `/prep` `/trust` `/receipts` `/readout`. They may also invoke an individual `fde-*` skill directly.
54
58
 
55
59
  **You (agent):** run the CLI. **Never tell the FDE to type** `fde …`. If unbound, you run `fde resume --init` after one question. Never ask them to run the CLI.
56
60
 
@@ -91,7 +95,7 @@ Writes need a bind (`FDEOPS_ENGAGEMENT` or registry). Never install fdeops on in
91
95
  ## The memory contract
92
96
 
93
97
  1. **On entry:** follow **Entry (every session)** above for the current packet and refresh rules. Retrieve additional evidence through targeted `fde recall` when needed.
94
- 2. **Deliverable = memory.** The work *is* the `.fde/` file. The reference names which one.
98
+ 2. **Deliverable plus memory.** Deliver the requested code, evidence or decision artifact. On a bound engagement, record the confirmed result in the file named by the method. A standalone artifact does not require a `.fde/` folder.
95
99
  3. **Evidence.** Without a supplied source, a decision or measurement remains CLAIM. Use `[source: meeting YYYY-MM-DD]`, a PR/URL, transcript ID, or artifact path. The automatic log date is not attribution. ON RECORD means a source was supplied, not that it was authenticated or the customer approved. Never invent a source, signer, or acceptance.
96
100
  4. **No invented facts.** People, quotes, meetings, numbers: they said it or the repo shows it. Else `unknown - ask: <question>`.
97
101
  5. **Session digest** (end of session and before a PR) - thinking, not the chat. Confirm, then write. Never a transcript dump.
@@ -162,7 +166,11 @@ Work names (engage, diagnose, align, deliver, realize, transfer) are the same ma
162
166
  |----------|-------|-----------|
163
167
  | What could go wrong, touching shared infrastructure, need to assess impact, assess impact, provision, IaC, shared infra | what-breaks | `references/what-breaks.md` |
164
168
  | Production down, urgent, fix a prod bug, resolve incident, restore service - OR stakeholder gone quiet, trust slipping | rescue | `references/rescue.md` |
165
- | Deliver, start building, update their checkout, first module, visible progress, their tests, POC follow-through, ready to deploy, going live, pre-flight, deliver the increment, build the increment, create the launch plan, design their UI | ship | `references/ship.md` |
169
+ | Deliver, start building, update their checkout, first module, their tests, build the increment, design their UI | build | `references/build.md` |
170
+ | Customer API, connector, data mapping, write-back, import, upstream integration | integrate | `references/integrate.md` |
171
+ | Reproduce a failure, unexpected output, regression, debug a connector | debug | `references/debug.md` |
172
+ | Exercise the customer journey, browser acceptance, functional QA | qa | `references/qa.md` |
173
+ | Ready to deploy, going live, pre-flight, release the verified increment | ship | `references/ship.md` |
166
174
  | Review this change, review the pull request, is it safe, does it match what we agreed | review | `references/review.md` |
167
175
  | Diff grew / scope creep in the PR / "did we only build what we said" / KEEP JUSTIFY SPLIT DROP | review (+ ship if going live) | `references/review.md` Stage 1 · `references/ship.md` Intent vs diff |
168
176
  | Wrap the session / share the thinking / catch teammates up / before I open the PR | (memory contract - session digest) | SKILL.md **Session digest** - write TL;DR + decisions/why into `.fde/`; no transcript sync |
@@ -203,17 +211,17 @@ Work names (engage, diagnose, align, deliver, realize, transfer) are the same ma
203
211
  | Payments, cardholder data, PCI-DSS, anything that moves money | `references/fintech.md` |
204
212
  | Government agency, FedRAMP, ATO, CUI, classified | `references/gov.md` |
205
213
 
206
- Ready to build with no `terrain.md` / plan: discover or plan first. Takeover without `audit.md`: audit first. Two customers in one message: confirm which folder.
214
+ Ready to build: check that the supplied facts establish the outcome, constraints and verification path. Use discover or plan only for material gaps. On a takeover, audit inherited claims that affect the task. Two customers in one message: confirm which folder.
207
215
 
208
216
  ## Principles
209
217
 
210
218
  - Never ask the FDE to pick a phase. That's your job.
211
219
  - Same six stages at any scale. Overlays carry the industry. Greenfield and brownfield change the first move inside ship, not the map.
212
- - Ground loop on a bound client: name → characterise → verify in the agreed environment → authorize release → log. A coding pack may write the function. `@fde` still owns done. When they disagree, their repo and the signer win.
220
+ - Ground loop on a bound client: name → characterise → verify in the agreed environment → authorize release → log. The included engineering methods do the build. `@fde` keeps delivery status explicit. When they disagree, their repo and the signer win.
213
221
  - Customer delivery needs a replayable acceptance check in the agreed environment; reuse existing criteria for routine fixes. Missing evidence means unproven, not an observed test failure. Never equate implementation-complete with deployed or customer-accepted.
214
222
  - Read the current entry packet before speaking; follow **Entry (every session)** above. One sharp question - never a barrage.
215
223
  - Never invent people, meetings, or numbers - `unknown - ask:` beats a polished lie.
216
- - Every phase ends with its artifact written. No artifact, no "done."
224
+ - Deliver the task artifact; persist confirmed engagement judgments only when bound. Never manufacture a record to satisfy a checklist.
217
225
  - Evidence on every claim. The FDE will be challenged on these files.
218
226
  - Overlays activate on signal, not on request.
219
227
  - Load `.fde/` files on demand, never the whole folder.
@@ -12,7 +12,7 @@ Technical FDEs lose renewals by presenting work instead of outcomes. The exec do
12
12
 
13
13
  ```
14
14
  GOVERNING THOUGHT: (one sentence - the conclusion)
15
- "The payment processing overhaul cut manual reconciliation from
15
+ "The payment processing overhaul cut manual reconciliation from
16
16
  3 FTEs to 0.5 FTE and eliminated the $2M annual audit risk."
17
17
 
18
18
  SUPPORT 1: What was done (one paragraph)
@@ -0,0 +1,20 @@
1
+ # build - Implement a verifiable increment
2
+
3
+ **Enter when:** an agreed behavior needs implementation in an existing or new repository. For a broken behavior, start with [debug](debug.md); for a system boundary, use [integrate](integrate.md).
4
+
5
+ Use the permitted context and authority in [task context](task-context.md). This method works without `.fde/`; an existing engagement record can supply the same contract. Do not initialize memory just to write code.
6
+
7
+ ## Method
8
+
9
+ 1. Identify the repository, its instructions, working tree, relevant callers, and test commands. Inspect examples before creating abstractions. Preserve unrelated edits and state which dependencies or interfaces the change touches.
10
+ 2. State the observable outcome, constraints, and acceptance checks. Reuse agreed criteria for routine fixes. If a consequential product choice is unresolved, surface that choice while continuing independent investigation; do not invent acceptance.
11
+ 3. Choose the smallest coherent path that demonstrates the outcome through the real entry point. Include the necessary storage, error handling, and interface behavior in that slice. Name the failure that stops expansion and the recovery path for stateful changes.
12
+ 4. Implement using the repository's tools and conventions. Search for existing services, fixtures, and validation before adding alternatives. Keep cleanup limited to what makes the changed path understandable; do not expand scope to repair unrelated code.
13
+ 5. Run focused checks, then required repository checks. Exercise the actual affected journey with [QA](qa.md) when appropriate. For uncertain model behavior, use [eval-pack](eval-pack.md). Record results with [verification](verification.md), including checks that could not run.
14
+ 6. Inspect the final diff against the agreed outcome. For substantial or risky work, seek [review](review.md) using an actual separate reviewer when available; identify a self-check honestly. Reverify affected behavior after fixes.
15
+
16
+ ## Deliverable and acceptance
17
+
18
+ Return the implemented behavior, relevant paths, evidence, remaining limitations, and any decision needed. Done means the agreed checks have applicable evidence and the change is reviewable; passing tests does not imply deployment or customer acceptance. Committing, opening a PR, merging, and publishing happen only when the requested workflow authorizes those actions.
19
+
20
+ When coordinated through `@fde`, record implementation and verification in the existing decisions/delivery records under their write rules. Standalone work can return the same receipt directly or use the repository's task record.
@@ -1,5 +1,7 @@
1
1
  # business-case - Build the business case
2
2
 
3
+ **Context:** apply [task context and evidence](task-context.md) before using the named records below.
4
+
3
5
  **Enter when:** the sponsor needs justification for the next phase, the FDE needs to defend budget or timeline, a feature decision needs cost/benefit evidence, or poc produced a direction that needs funding.
4
6
 
5
7
  **Read first:** `reality.md`, `success.md`, `delivery.md`, `context.md`. Load `business-case.md` from poc if it exists - extend it, don't restart.
@@ -12,7 +14,7 @@ Technical FDEs lose engagements by shipping good code without business justifica
12
14
 
13
15
  | Cost type | How to find it | Example |
14
16
  |-----------|---------------|---------|
15
- | **Direct cost** | Ask: "What does this problem cost per month in money?" | Manual reconciliation: 3 people × 8h/week × loaded cost = $X/month |
17
+ | **Labor capacity / direct spend** | Ask: "What does this problem cost per month in money?" | Manual reconciliation hours × loaded rate = capacity value; separately identify reducible spend |
16
18
  | **Opportunity cost** | Ask: "What can't you do because of this problem?" | Can't onboard enterprise clients because the API can't handle their volume |
17
19
  | **Risk cost** | Ask: "What happens if this breaks at the worst time?" | A payment processing outage during Black Friday = $X/hour in lost sales |
18
20
  | **Velocity cost** | Measure: deployment frequency, lead time, change failure rate | Team ships once/month instead of once/week; each delay = N features not reaching customers |
@@ -22,17 +24,17 @@ Technical FDEs lose engagements by shipping good code without business justifica
22
24
  ```
23
25
  Investment: <hours × rate, or fixed cost>
24
26
  → Delivers: <specific outcome from success.md>
25
- → Saves: <cost-of-nothing × probability of success>
26
- → Net: savings - investment over <time horizon>
27
+ → Benefit: <capacity released, avoidable cash spend, revenue, or risk reduction>
28
+ → Net cash: realizable incremental cash benefit - full costs over <time horizon>
27
29
  ```
28
30
 
29
- Keep the drivers explicit. "We estimate $200K savings" means nothing. "3 people × 8h/week × $75/h × 52 weeks = $93.6K/year, minus $40K build cost = $53.6K net year one" is defensible.
31
+ Keep drivers, units, sources, and ranges explicit. For example, 3 people × 8h/week × $75/h × 52 weeks = $93.6K/year of labor capacity value. It is cash savings only if spend actually falls (for example, paid overtime or a contractor cost ends). Name who can realize the benefit and how. Include build, ongoing operation, adoption, and transition costs; avoid double-counting capacity and revenue enabled by the same hours. Do not calculate cash payback from capacity value alone.
30
32
 
31
33
  **3. Sensitivity check - name the two drivers that swing the result:**
32
34
 
33
35
  Every business case has 1-2 variables where a small change flips the outcome. Name them explicitly:
34
36
 
35
- > "This case holds if the team actually reclaims 6+ hours/week per person. If it's only 3 hours, the payback extends from 5 months to 14 months. The validation: measure time-spent before and after pilot with 2 team members."
37
+ > "The capacity case assumes the team reclaims 6 hours/week per person. At 3 hours, that benefit halves. Cash payback remains unproven until finance identifies avoidable spend. Validate time-spent before and after the pilot with representative team members."
36
38
 
37
39
  The sponsor who sees you've identified where the case could break trusts the case more, not less.
38
40
 
@@ -53,7 +55,7 @@ The sponsor who sees you've identified where the case could break trusts the cas
53
55
  **The problem costs:** <one line, quantified>
54
56
  **The investment:** <hours and cost>
55
57
  **The return:** <quantified, with time horizon>
56
- **Payback:** <months>
58
+ **Payback:** <months from realizable cash benefits, or not established>
57
59
  **Sensitivity:** <the 1-2 drivers that swing it, with thresholds>
58
60
  **Risks:** <what must be true for this to hold>
59
61
  **Recommendation:** <proceed / proceed-with-conditions / defer>
@@ -67,7 +69,7 @@ The sponsor who sees you've identified where the case could break trusts the cas
67
69
 
68
70
  ## Checkpoint
69
71
 
70
- Walk the FDE through: the cost of doing nothing (anchor), the investment, the return, and the one sensitivity that matters most. If the FDE says "the sponsor won't buy the ROI number" - ask what number they would believe and work backwards from there.
72
+ Walk the FDE through: the cost of doing nothing (anchor), the investment, the return, and the one sensitivity that matters most. If the FDE says "the sponsor won't buy the ROI number," inspect the disputed inputs and sources, test plausible ranges, and identify what measurement would resolve the disagreement. Never reverse-engineer assumptions to hit a desired number.
71
73
 
72
74
  ## Worked example
73
75
 
@@ -1,8 +1,10 @@
1
1
  # close - Transfer operations
2
2
 
3
+ **Context:** apply [task context and evidence](task-context.md) before using the named records below.
4
+
3
5
  **Enter when:** the engagement is ending - the customer team must run this without the FDE.
4
6
 
5
- **Read first:** bounded `fde handoff` or `fde resume`, then targeted `fde recall` for missing evidence. Build the full picture through relevant excerpts, not a full-directory load. Consult `terrain.md` only for the code paths needed by the successor.
7
+ **Read first:** for standalone work, use the supplied permitted operating notes, evidence and ownership; no engagement binding or CLI command is required. For a bound engagement, use bounded `fde handoff` or `fde resume`, then targeted `fde recall` for missing evidence. Never initialize records merely to draft a handoff. Build the picture through relevant excerpts, not a full-directory load. Consult `terrain.md` only for code paths needed by the successor.
6
8
 
7
9
  The engagement doesn't end at ship. It ends when the customer can maintain what was built without calling.
8
10
 
@@ -20,10 +22,10 @@ The engagement doesn't end at ship. It ends when the customer can maintain what
20
22
  **1b. Value + receipts close gate (refuse green close if any fail):**
21
23
  - Primary value bucket in `success.md` matches what the sponsor funded; at least one ledger row has **Measured** (not forever-`pending`) with evidence **and a named customer-side owner in Accepted by** for that bucket - or the retrospective explicitly records “not measured; sponsor accepted pending.” A measured-but-unaccepted number closes as `claimed`; say so in the retrospective rather than closing green on arithmetic nobody signed.
22
24
  - Audit receipt exists for the final shipped path (exceptions/operating map walked; cite file).
23
- - Eval receipt: **n/a if no AI**, else final golden/eval result + HITL owner recorded; kill switch / fallback named in `handoff.md`.
25
+ - Eval receipt: **n/a if no AI**, else final scoped eval result + operating owner and required human-review or bounded-automation authority recorded; kill switch / fallback named in `handoff.md`.
24
26
  - One line in the retrospective: which bucket moved, by how much, vs baseline.
25
27
 
26
- **2. The pattern.** Anything that happened here and will happen again - a compliance approach, a migration pattern, a stakeholder dynamic - gets encoded for reuse. **If you do it twice, encode it.**
28
+ **2. The pattern.** Anything that happened here and will happen again - a compliance approach, a migration pattern, a stakeholder dynamic - gets encoded for reuse. Use [encode-pattern](encode-pattern.md) to distinguish candidate patterns from supported ones and protect customer data.
27
29
 
28
30
  **3. The handoff.** Operational knowledge for the person woken at 2am, not technical documentation: the 3 things that will break and the fix for each · who holds the tribal knowledge · what each alert means · deploy and rollback in plain language. AI components additionally: model version, what normal output looks like (so drift is recognisable), fallback behaviour, who owns retraining, **how to disable the AI path without taking down the feature** - without this the team turns it off at the first misbehaviour and it stays off.
29
31
 
@@ -41,7 +43,7 @@ The engagement doesn't end at ship. It ends when the customer can maintain what
41
43
 
42
44
  **Check the handoff as a lookup tool.** Give the intended operator one realistic task, such as finding the owner and recovery steps for a failed run. Can they locate the answer and its source in the permitted handoff without your explanation? A reader finding the instructions is not proof they can execute them; verify operation separately in the agreed safe environment. Correct the passage they could not use, rather than adding a longer introduction.
43
45
 
44
- If the operator is unavailable, a fresh reviewer can attempt the same lookup using only the permitted draft and task. Report this as a simulated clarity check, not operator validation, customer approval, or a green close. Use one focused pass for a consequential handoff; do not add a committee or a second approval ritual.
46
+ If the operator is unavailable, a fresh reviewer can attempt the same lookup using only the permitted draft and task. Report this as a simulated clarity check, not operator validation, customer approval, or a green close. Claim independent review only if a separate reviewer actually performed it; identify the reviewer and evidence available. If none is available, perform a labeled self-check and report independent review as unperformed. Use one focused pass for a consequential handoff; do not add a committee or a second approval ritual.
45
47
 
46
48
  Direct assessment to the FDE: did the engagement achieve `success.md` · 2-3 lessons that matter · is the pattern worth encoding · is the handoff complete or where are the gaps. Also: value bucket + audit receipt green; eval **n/a or green**. Pending Measured without sponsor acceptance = gap, not green close. Honest - a gap named now is cheaper than a callback in six weeks.
47
49