fdeops 3.20.0 → 3.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/README.md +120 -114
- package/bin/check.js +24 -2
- package/bin/fde.js +141 -10
- package/bin/lib/trust.js +7 -4
- package/mcp/fdeops-ingest/package.json +1 -1
- package/package.json +2 -2
- package/plugin.json +2 -2
- package/skills/fde/SKILL.md +7 -5
- package/skills/fde/references/debrief.md +4 -2
- package/skills/fde/references/discover.md +20 -6
- package/skills/fde/references/encode-pattern.md +1 -0
- package/skills/fde/references/eval-pack.md +3 -3
- package/skills/fde/references/ingest.md +1 -1
- package/skills/fde/references/land.md +4 -4
- package/skills/fde/references/poc.md +4 -1
- package/skills/fde/references/readout.md +4 -3
- package/skills/fde/references/review.md +2 -2
- package/skills/fde/references/runbook.md +4 -2
- package/skills/fde/references/ship.md +10 -2
- package/skills/fde/references/test-assumptions.md +18 -7
- package/skills/fde/references/three-options.md +17 -10
- package/templates/.fde/README.md +1 -1
- package/templates/.fde/assumptions.md +3 -2
package/AGENTS.md
CHANGED
|
@@ -4,7 +4,7 @@ This repository **is** fdeops - the engagement record for Forward Deployed Engin
|
|
|
4
4
|
|
|
5
5
|
## If you are helping use fdeops in an engagement
|
|
6
6
|
|
|
7
|
-
Route via **`@fde`** - read `skills/fde/SKILL.md` (the single source of truth),
|
|
7
|
+
Route via **`@fde`** - read `skills/fde/SKILL.md` (the single source of truth), route to one `references/*.md`, do the work, and write `.fde/` memory. Never ask the human to pick a skill. Other tools get the same behavior through thin pointer files in [`adapters/`](adapters/README.md).
|
|
8
8
|
|
|
9
9
|
## If you are contributing to this repository
|
|
10
10
|
|
package/README.md
CHANGED
|
@@ -2,91 +2,78 @@
|
|
|
2
2
|
|
|
3
3
|
**Forward deployed engineering skills for AI coding agents.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
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.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Notes stay on your laptop. Their repo stays theirs. You confirm before anything is written down.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## Commands
|
|
9
|
+
Keep the coding pack you already use. Install this next to it. FDEOps is the client work. The other pack writes the code.
|
|
12
10
|
|
|
13
|
-
|
|
11
|
+
<img width="1536" height="1024" alt="fdeops" src="https://github.com/user-attachments/assets/2bcb8739-55ee-445d-8a1a-8b38433b7b58" />
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
| Work | Command | Stage | Principle |
|
|
18
|
-
|------|---------|-------|-----------|
|
|
19
|
-
| Engage | `/brief` | Land | Name who signs done |
|
|
20
|
-
| Diagnose | `/discover` | Discover | Treat the brief as a hypothesis |
|
|
21
|
-
| Align | `/plan` | Plan | Work backwards from done |
|
|
22
|
-
| Deliver | `/ship` | Ship | One visible change, then go live |
|
|
23
|
-
| Realize | `/outcome` | Outcome | Promised, measured, accepted |
|
|
24
|
-
| Transfer | `/close` | Close | They operate it without you |
|
|
13
|
+
---
|
|
25
14
|
|
|
26
|
-
|
|
15
|
+
## Quick Start
|
|
27
16
|
|
|
28
|
-
|
|
29
|
-
|------|---------|-----------|
|
|
30
|
-
| Diagnose trust | `/trust` | Process gap, or they stopped trusting you |
|
|
31
|
-
| Find the receipt | `/receipts` | A dated line, or it did not happen |
|
|
32
|
-
| Capture the meeting | `/debrief` | Notes into the record |
|
|
33
|
-
| Prepare the meeting | `/prep` | One page from the record |
|
|
34
|
-
| Report the outcome | `/readout` | Promised, measured, accepted |
|
|
17
|
+
**Try it first, nothing installed.** In any repo:
|
|
35
18
|
|
|
36
|
-
|
|
19
|
+
```bash
|
|
20
|
+
npx fdeops scan
|
|
21
|
+
```
|
|
37
22
|
|
|
38
|
-
|
|
23
|
+
Two minutes. It prints what to look at on day one and the questions to ask. Local only, nothing written.
|
|
39
24
|
|
|
40
|
-
|
|
25
|
+
**Then install the skill:**
|
|
41
26
|
|
|
42
27
|
```bash
|
|
43
28
|
npx skills add suboss87/fdeops --skill fde
|
|
44
29
|
```
|
|
45
30
|
|
|
46
|
-
|
|
31
|
+
One chat. Name the client:
|
|
47
32
|
|
|
48
33
|
```text
|
|
49
|
-
@fde this is
|
|
34
|
+
@fde this is client01
|
|
50
35
|
```
|
|
51
36
|
|
|
52
|
-
Paste kickoff notes in the same thread. `@fde`
|
|
37
|
+
That creates `~/fde-engagements/client01/.fde/` on your laptop. Paste kickoff notes in the same thread. `@fde` picks what to check. You still decide.
|
|
38
|
+
|
|
39
|
+
Day to day: [docs/USAGE.md](docs/USAGE.md).
|
|
53
40
|
|
|
54
41
|
<details>
|
|
55
|
-
<summary><b>Claude Code
|
|
42
|
+
<summary><b>Claude Code</b></summary>
|
|
56
43
|
|
|
57
44
|
```text
|
|
58
45
|
/plugin marketplace add suboss87/fdeops
|
|
59
46
|
/plugin install fdeops@fdeops
|
|
60
47
|
```
|
|
61
48
|
|
|
62
|
-
Hooks
|
|
49
|
+
Hooks resume where you left off. Slash commands match the table below.
|
|
63
50
|
|
|
64
51
|
</details>
|
|
65
52
|
|
|
66
53
|
<details>
|
|
67
54
|
<summary><b>Cursor</b></summary>
|
|
68
55
|
|
|
56
|
+
After the skill install, in the **client repo** you have open (pointer, not a second pack):
|
|
57
|
+
|
|
69
58
|
```bash
|
|
70
|
-
npx
|
|
59
|
+
npx fdeops adapters .
|
|
71
60
|
```
|
|
72
61
|
|
|
73
|
-
|
|
62
|
+
See [adapters/](adapters/README.md).
|
|
74
63
|
|
|
75
64
|
</details>
|
|
76
65
|
|
|
77
66
|
<details>
|
|
78
|
-
<summary><b>
|
|
67
|
+
<summary><b>Air-gap, PATH, override</b></summary>
|
|
79
68
|
|
|
80
69
|
```bash
|
|
81
|
-
|
|
70
|
+
git clone https://github.com/suboss87/fdeops.git && node bin/install.js
|
|
82
71
|
```
|
|
83
72
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Fallback if the agent cannot bind:
|
|
73
|
+
If the agent cannot create the folder:
|
|
87
74
|
|
|
88
75
|
```bash
|
|
89
|
-
npx fdeops resume --init
|
|
76
|
+
npx fdeops resume --init client01 # ~/fde-engagements/client01
|
|
90
77
|
```
|
|
91
78
|
|
|
92
79
|
Requires Node.js >= 18. Override: `FDEOPS_ENGAGEMENT`. See [docs/install.md](docs/install.md). Try the loop: `npx fdeops demo`.
|
|
@@ -95,13 +82,34 @@ Requires Node.js >= 18. Override: `FDEOPS_ENGAGEMENT`. See [docs/install.md](doc
|
|
|
95
82
|
|
|
96
83
|
---
|
|
97
84
|
|
|
85
|
+
## Commands
|
|
86
|
+
|
|
87
|
+
One command per stage. Skills load automatically.
|
|
88
|
+
|
|
89
|
+
Six stages, same order every job: Land, Discover, Plan, Ship, Outcome, Close.
|
|
90
|
+
|
|
91
|
+
| What you're doing | Command | Stage |
|
|
92
|
+
|-------------------|---------|-------|
|
|
93
|
+
| First days. Get the brief. Name who signs. | `/brief` | Land |
|
|
94
|
+
| Check the brief is the real job. | `/discover` | Discover |
|
|
95
|
+
| Sequence from done, not from the ticket. | `/plan` | Plan |
|
|
96
|
+
| Prove it on their staging, then go live. | `/ship` | Ship |
|
|
97
|
+
| What you promised, measured, and who accepted. | `/outcome` | Outcome |
|
|
98
|
+
| Hand it over. They run it without you. | `/close` | Close |
|
|
99
|
+
|
|
100
|
+
Same `@fde`, when you need them: `/debrief` (notes into the record), `/prep` (one page before you walk in), `/trust` (process gap, or they stopped trusting you), `/receipts` (a dated line, or it did not happen), `/readout` (Friday page for the sponsor; not a seventh stage).
|
|
101
|
+
|
|
102
|
+
You can also just say it: naming a client, a POC, changing their checkout, going live, asking what was agreed. A typo in a repo that is not a client job can skip this. A named client, a POC, or go-live cannot.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
98
106
|
## All 30 Skills
|
|
99
107
|
|
|
100
|
-
|
|
108
|
+
Thirty situations, grouped by stage. Not prompts - each one has steps, a file it writes, and a checkpoint with you. Type English or a slash command. `@fde` opens the matching skill. You never pick one by name.
|
|
101
109
|
|
|
102
110
|
Full detail: [docs/skills-reference.md](docs/skills-reference.md).
|
|
103
111
|
|
|
104
|
-
### Land
|
|
112
|
+
### Land
|
|
105
113
|
|
|
106
114
|
| Skill | What it does | Use when |
|
|
107
115
|
|--------|--------------|----------|
|
|
@@ -111,7 +119,7 @@ Full detail: [docs/skills-reference.md](docs/skills-reference.md).
|
|
|
111
119
|
| [earn-trust](skills/fde/references/earn-trust.md) | Earn access | Need access or credibility |
|
|
112
120
|
| [hold-scope](skills/fde/references/hold-scope.md) | Hold scope | "Also can you…", timeline unchanged |
|
|
113
121
|
|
|
114
|
-
### Discover
|
|
122
|
+
### Discover
|
|
115
123
|
|
|
116
124
|
| Skill | What it does | Use when |
|
|
117
125
|
|--------|--------------|----------|
|
|
@@ -120,7 +128,7 @@ Full detail: [docs/skills-reference.md](docs/skills-reference.md).
|
|
|
120
128
|
| [score-use-cases](skills/fde/references/score-use-cases.md) | Score use cases | Everything is P0 |
|
|
121
129
|
| [poc](skills/fde/references/poc.md) | Validate the solution | POC, spike, need to de-risk |
|
|
122
130
|
|
|
123
|
-
### Plan
|
|
131
|
+
### Plan
|
|
124
132
|
|
|
125
133
|
| Skill | What it does | Use when |
|
|
126
134
|
|--------|--------------|----------|
|
|
@@ -129,7 +137,7 @@ Full detail: [docs/skills-reference.md](docs/skills-reference.md).
|
|
|
129
137
|
| [three-options](skills/fde/references/three-options.md) | Generate options | "What should we do?" |
|
|
130
138
|
| [pick-three](skills/fde/references/pick-three.md) | Prioritize three | Everything is urgent |
|
|
131
139
|
|
|
132
|
-
### Ship
|
|
140
|
+
### Ship
|
|
133
141
|
|
|
134
142
|
| Skill | What it does | Use when |
|
|
135
143
|
|--------|--------------|----------|
|
|
@@ -139,7 +147,7 @@ Full detail: [docs/skills-reference.md](docs/skills-reference.md).
|
|
|
139
147
|
| [review](skills/fde/references/review.md) | Review the change | Before merge, scope creep |
|
|
140
148
|
| [rollback](skills/fde/references/rollback.md) | Rehearse rollback | "We can always revert" |
|
|
141
149
|
|
|
142
|
-
### Outcome
|
|
150
|
+
### Outcome
|
|
143
151
|
|
|
144
152
|
| Skill | What it does | Use when |
|
|
145
153
|
|--------|--------------|----------|
|
|
@@ -151,7 +159,7 @@ Full detail: [docs/skills-reference.md](docs/skills-reference.md).
|
|
|
151
159
|
| [ingest](skills/fde/references/ingest.md) | Ingest sources | Transcript, Notion, Slack |
|
|
152
160
|
| [connect](skills/fde/references/connect.md) | Connect a source | Connect Granola |
|
|
153
161
|
|
|
154
|
-
### Close
|
|
162
|
+
### Close
|
|
155
163
|
|
|
156
164
|
| Skill | What it does | Use when |
|
|
157
165
|
|--------|--------------|----------|
|
|
@@ -161,7 +169,7 @@ Full detail: [docs/skills-reference.md](docs/skills-reference.md).
|
|
|
161
169
|
| [encode-pattern](skills/fde/references/encode-pattern.md) | Encode the pattern | It will apply again |
|
|
162
170
|
| [red-team](skills/fde/references/red-team.md) | Challenge the plan | "Poke holes in this" |
|
|
163
171
|
|
|
164
|
-
Overlays (on signal, not on request): [ai](skills/fde/references/ai.md) · [artifacts](skills/fde/references/artifacts.md) · [fintech](skills/fde/references/fintech.md) · [healthcare](skills/fde/references/healthcare.md) · [gov](skills/fde/references/gov.md)
|
|
172
|
+
Overlays (on signal, not on request): [ai](skills/fde/references/ai.md) · [artifacts](skills/fde/references/artifacts.md) · [fintech](skills/fde/references/fintech.md) · [healthcare](skills/fde/references/healthcare.md) · [gov](skills/fde/references/gov.md). AI companion (not a sixth overlay): [eval-pack](skills/fde/references/eval-pack.md).
|
|
165
173
|
|
|
166
174
|
Optional pull: you add the source MCP; we **pull** on request. [mcp/recipes/](mcp/recipes/)
|
|
167
175
|
|
|
@@ -169,33 +177,84 @@ Optional pull: you add the source MCP; we **pull** on request. [mcp/recipes/](mc
|
|
|
169
177
|
|
|
170
178
|
## How Skills Work
|
|
171
179
|
|
|
172
|
-
One
|
|
180
|
+
One `@fde`. One file per situation. One folder per client.
|
|
173
181
|
|
|
174
182
|
```
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
▼
|
|
178
|
-
skills/fde/SKILL.md hosts load this one file
|
|
183
|
+
"@fde this is client01" creates ~/fde-engagements/client01/.fde/
|
|
184
|
+
/brief or English the AI coding agent loads skills/fde/SKILL.md
|
|
179
185
|
│ routes. you never pick a skill by name
|
|
180
186
|
▼
|
|
181
|
-
references
|
|
187
|
+
references/<one>.md one skill, then stop
|
|
182
188
|
│
|
|
183
189
|
▼
|
|
184
190
|
fde CLI (local) dates, gates, redacts. no network
|
|
185
191
|
│ after you confirm
|
|
186
192
|
▼
|
|
187
|
-
~/fde-engagements
|
|
193
|
+
~/fde-engagements/client01/.fde/
|
|
188
194
|
```
|
|
189
195
|
|
|
190
|
-
**
|
|
196
|
+
**A dated line, or it did not happen.** Promised → measured → accepted. If it is not in `.fde/`, it is not on the record.
|
|
191
197
|
|
|
192
|
-
**
|
|
198
|
+
**Confirm, then it is written.** The CLI stays on your laptop: git and files, no network. The AI coding agent runs the command. You say yes. Then it is in the folder.
|
|
193
199
|
|
|
194
|
-
**
|
|
200
|
+
**The record is on your laptop.** Change hosts, install `@fde` on the new one, keep talking. The notes are not inside any vendor.
|
|
195
201
|
|
|
196
|
-
|
|
202
|
+
One skill hosts load: `skills/fde/SKILL.md`. It opens one file in `skills/fde/references/` and stops. Slash commands live in `.claude/commands/`. The local CLI is `bin/fde.js` (git + files, no network). Layout: [docs/REPO_LAYOUT.md](docs/REPO_LAYOUT.md).
|
|
197
203
|
|
|
198
|
-
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Engagement memory (`.fde/`)
|
|
207
|
+
|
|
208
|
+
One folder per client. Plain markdown. Grep it, copy it, take it into a meeting.
|
|
209
|
+
|
|
210
|
+
| File | Holds |
|
|
211
|
+
|------|-------|
|
|
212
|
+
| `context.md` | Where you are |
|
|
213
|
+
| `brief.md` / `success.md` | What they asked; what “done” is and who signs |
|
|
214
|
+
| `reality.md` / `terrain.md` | The real problem; the map |
|
|
215
|
+
| `stakeholders.md` | `[signal:green\|amber\|red]` |
|
|
216
|
+
| `trust-profile.md` | Sacred data, AI policy, approval chain |
|
|
217
|
+
| `decisions.md` / `risks.md` / `delivery.md` | Dated choices; live risks; what shipped and how it rolls back |
|
|
218
|
+
|
|
219
|
+
Schema: [docs/schema.md](docs/schema.md). Local HTML: `npx fdeops dashboard`.
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## Who this is for
|
|
224
|
+
|
|
225
|
+
You sit with a customer's team. An AI coding agent writes in their repo. You need a record of the brief, who can say yes, what went live, and whether they signed off.
|
|
226
|
+
|
|
227
|
+
If you ship your own company's product from HQ, with no customer team that has to run it after you leave, you do not need this kit.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Your data stays yours
|
|
232
|
+
|
|
233
|
+
The **CLI** is local: git + files, no network, no telemetry. The **host model** sees `.fde/` the agent loads (usually a bounded `context.md`) and any client code you open. It must not see `<private>` blocks - redacted from CLI, dashboard, and hooks; do not paste them or open them with file tools. Nothing is written until you confirm. `~/fde-engagements` is in `$HOME`; iCloud/Dropbox is an NDA incident waiting.
|
|
234
|
+
|
|
235
|
+
[PRIVACY.md](PRIVACY.md) · [SECURITY.md](SECURITY.md)
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Why FDEOps?
|
|
240
|
+
|
|
241
|
+
AI coding agents are built for a repo, not for a client. Left alone they skip who signs, whether the brief is true, and whether anyone accepted the number. Monday they start from the ticket again.
|
|
242
|
+
|
|
243
|
+
This is the kit you take on site. `@fde` runs the client work around the code. A local command dates every decision. The notes are markdown on your laptop. You confirm; then it is on the record.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Principles
|
|
248
|
+
|
|
249
|
+
- **Who signs** - name them in the first days
|
|
250
|
+
- **Brief vs real job** - check the floor, not only the slide
|
|
251
|
+
- **Back from done** - sequence from signed-off, not from the ticket
|
|
252
|
+
- **Their staging then live** - prove it where they operate, then go live
|
|
253
|
+
- **Promised, measured, accepted** - a number nobody signed is claimed, not delivered
|
|
254
|
+
- **They run it** - if they cannot operate it without you, you are not done
|
|
255
|
+
- **A dated line, or it did not happen** - these files get defended in the room
|
|
256
|
+
- **One customer, one folder** - context never bleeds
|
|
257
|
+
- **The kit says what to check. You still decide.**
|
|
199
258
|
|
|
200
259
|
---
|
|
201
260
|
|
|
@@ -258,7 +317,7 @@ fdeops/
|
|
|
258
317
|
├── bin/ # local CLI: git + files, no network
|
|
259
318
|
├── hooks/ # session-start / session-stop / pre-compact
|
|
260
319
|
├── adapters/ # Cursor, Gemini, Copilot, Codex pointers
|
|
261
|
-
├── templates/.fde/ # memory files created on
|
|
320
|
+
├── templates/.fde/ # memory files created on first client
|
|
262
321
|
├── examples/ # fictional walkthroughs
|
|
263
322
|
├── mcp/ # optional ingest + source recipes
|
|
264
323
|
├── evals/ # routing checks
|
|
@@ -267,59 +326,6 @@ fdeops/
|
|
|
267
326
|
|
|
268
327
|
---
|
|
269
328
|
|
|
270
|
-
## Why FDEOps?
|
|
271
|
-
|
|
272
|
-
AI coding agents are built for a repo, not for a client. Left alone they skip who signs done, whether the brief is true, and whether anyone accepted the number. Monday morning they start from the ticket again.
|
|
273
|
-
|
|
274
|
-
FDEOps is the catalog you take on site. One `@fde` skill runs the embed from discovery to signed outcome: POC, their codebase, go-live, eval when a model judges, promised → measured → accepted. A local CLI dates every decision. `.fde/` is markdown on your laptop. You confirm; then it is on the record.
|
|
275
|
-
|
|
276
|
-
---
|
|
277
|
-
|
|
278
|
-
## Engagement memory (`.fde/`)
|
|
279
|
-
|
|
280
|
-
One folder per client. Plain markdown. Grep it, copy it, defend it.
|
|
281
|
-
|
|
282
|
-
| File | Holds |
|
|
283
|
-
|------|-------|
|
|
284
|
-
| `context.md` | Where you are |
|
|
285
|
-
| `brief.md` / `success.md` | What they asked; what “done” is and who signs |
|
|
286
|
-
| `reality.md` / `terrain.md` | The real problem; the map |
|
|
287
|
-
| `stakeholders.md` | `[signal:green\|amber\|red]` |
|
|
288
|
-
| `trust-profile.md` | Sacred data, AI policy, approval chain |
|
|
289
|
-
| `decisions.md` / `risks.md` / `delivery.md` | Dated choices; live risks; what shipped and how it rolls back |
|
|
290
|
-
|
|
291
|
-
Schema: [docs/schema.md](docs/schema.md). Local HTML: `npx fdeops dashboard`.
|
|
292
|
-
|
|
293
|
-
---
|
|
294
|
-
|
|
295
|
-
## Who this is for
|
|
296
|
-
|
|
297
|
-
You embed with a customer and an AI coding agent. Take this on the ground. Discovery through signed outcome lives in `@fde`. One `.fde/` per client so they do not blur.
|
|
298
|
-
|
|
299
|
-
If you only write code in your own repo with no client record to defend, you do not need this kit.
|
|
300
|
-
|
|
301
|
-
---
|
|
302
|
-
|
|
303
|
-
## Your data stays yours
|
|
304
|
-
|
|
305
|
-
Local only - `git` + files, no network, no telemetry. Plain markdown. The model sees client code only when you point the AI coding agent at it. `<private>` is redacted from CLI, dashboard, and hooks. Nothing is written until you confirm. `~/fde-engagements` is in `$HOME`; iCloud/Dropbox is an NDA incident waiting.
|
|
306
|
-
|
|
307
|
-
[PRIVACY.md](PRIVACY.md) · [SECURITY.md](SECURITY.md)
|
|
308
|
-
|
|
309
|
-
---
|
|
310
|
-
|
|
311
|
-
## Principles
|
|
312
|
-
|
|
313
|
-
- **One map** - any scale, greenfield or brownfield, any industry. Overlays carry the vertical
|
|
314
|
-
- **The artifact is the memory** - producing the work and recording it are one action
|
|
315
|
-
- **Ground loop** - name the change, characterise their code, prove it on their staging, go live, log the outcome
|
|
316
|
-
- **Skills, not autonomy** - the kit says what to check; judgment stays yours
|
|
317
|
-
- **Brief is a hypothesis** - discover before building the wrong thing
|
|
318
|
-
- **Evidence on every claim** - these files get defended in the room
|
|
319
|
-
- **One customer, one folder** - context never bleeds
|
|
320
|
-
|
|
321
|
-
---
|
|
322
|
-
|
|
323
329
|
## Contributing
|
|
324
330
|
|
|
325
331
|
**[Subash Natarajan](https://www.linkedin.com/in/subashn/)**. [Issues](https://github.com/suboss87/fdeops/issues) · [Discussions](https://github.com/suboss87/fdeops/discussions) · [CONTRIBUTING.md](CONTRIBUTING.md) · [Code of Conduct](CODE_OF_CONDUCT.md)
|
package/bin/check.js
CHANGED
|
@@ -153,8 +153,8 @@ ok(`router dispatch (${mentioned.length} reference targets verified) + memory co
|
|
|
153
153
|
}
|
|
154
154
|
if (!routed.size) fail('check.js could not parse the SKILL.md routing table')
|
|
155
155
|
|
|
156
|
-
// docs/skills-reference.md is the canonical per-
|
|
157
|
-
//
|
|
156
|
+
// docs/skills-reference.md is the canonical per-skill list: one row per
|
|
157
|
+
// skill inside the six stage tables, ending at the Overlays section.
|
|
158
158
|
const reference = read('docs/skills-reference.md')
|
|
159
159
|
const documented = new Set()
|
|
160
160
|
let documentedRows = 0
|
|
@@ -409,6 +409,28 @@ for (const f of exampleFiles) {
|
|
|
409
409
|
}
|
|
410
410
|
ok('examples walkthrough files')
|
|
411
411
|
|
|
412
|
+
// The examples are the first .fde/ a newcomer reads. They must pass the kit's
|
|
413
|
+
// own doctor, or the kit is telling people to do what its showcase does not.
|
|
414
|
+
// Tolerated: things a frozen reference copy cannot have (an owner, a memory
|
|
415
|
+
// git, a fresh trust signal).
|
|
416
|
+
{
|
|
417
|
+
const { spawnSync } = require('child_process')
|
|
418
|
+
const tolerated = /no \.owner|not git-versioned|trust signal is STALE/
|
|
419
|
+
for (const ex of fs.readdirSync(path.join(root, 'examples'))) {
|
|
420
|
+
const eng = path.join(root, 'examples', ex, '.fde')
|
|
421
|
+
if (!fs.existsSync(eng)) continue
|
|
422
|
+
const r = spawnSync(process.execPath, [path.join(root, 'bin', 'fde.js'), 'doctor'], {
|
|
423
|
+
encoding: 'utf8',
|
|
424
|
+
env: { ...process.env, FDEOPS_ENGAGEMENT: eng, HOME: fs.mkdtempSync(path.join(require('os').tmpdir(), 'fdeops-check-')) },
|
|
425
|
+
})
|
|
426
|
+
const issues = (r.stdout || '').split('\n')
|
|
427
|
+
.map(l => l.match(/^\s+\d+\.\s+(.*)$/)).filter(Boolean).map(m => m[1])
|
|
428
|
+
.filter(i => !tolerated.test(i))
|
|
429
|
+
if (issues.length) fail(`examples/${ex} fails its own doctor:\n - ${issues.join('\n - ')}`)
|
|
430
|
+
else ok(`examples/${ex} passes fde doctor`)
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
412
434
|
if (fs.existsSync(path.join(root, 'tasks', 'plan.md'))) {
|
|
413
435
|
fail('tasks/plan.md should not be in public tree (move to docs/internal)')
|
|
414
436
|
}
|
package/bin/fde.js
CHANGED
|
@@ -1515,8 +1515,8 @@ function smartProposeText(input) {
|
|
|
1515
1515
|
out.push(`decision: ${bare.replace(/^decided:\s+/i, '')}`)
|
|
1516
1516
|
continue
|
|
1517
1517
|
}
|
|
1518
|
-
if (/^(decision|risk|delivery|contact|next):\s*/i.test(bare)) {
|
|
1519
|
-
let routed = bare.replace(/^(decision|risk|delivery|contact|next):\s*/i, (m, t) => `${t.toLowerCase()}: `)
|
|
1518
|
+
if (/^(decision|risk|delivery|contact|next|signer):\s*/i.test(bare)) {
|
|
1519
|
+
let routed = bare.replace(/^(decision|risk|delivery|contact|next|signer):\s*/i, (m, t) => `${t.toLowerCase()}: `)
|
|
1520
1520
|
if (/^contact:/i.test(routed) && !/\[signal:(red|amber|green)\]/i.test(routed)) {
|
|
1521
1521
|
const sig = inferContactSignal(routed)
|
|
1522
1522
|
if (sig) routed = routed.replace(/\s*$/, ` [signal:${sig}]`)
|
|
@@ -1524,6 +1524,12 @@ function smartProposeText(input) {
|
|
|
1524
1524
|
out.push(routed)
|
|
1525
1525
|
continue
|
|
1526
1526
|
}
|
|
1527
|
+
// Sentence-level: a signer named mid-paragraph gets its own routed line and
|
|
1528
|
+
// the original stays as context, so nothing is invented or lost.
|
|
1529
|
+
for (const sentence of bare.split(/(?<=[.!?])\s+/)) {
|
|
1530
|
+
const who = signerFromLine(sentence)
|
|
1531
|
+
if (who) { out.push(`signer: ${who}`); break }
|
|
1532
|
+
}
|
|
1527
1533
|
if (/^(next action|follow-?ups?|action items?|todo):\s*/i.test(bare) ||
|
|
1528
1534
|
/\b(next action|walk in with|follow up with)\b/i.test(bare)) {
|
|
1529
1535
|
const next = bare.replace(/^(next action|follow-?ups?|action items?|todo):\s*/i, '').trim()
|
|
@@ -1549,6 +1555,56 @@ function smartProposeText(input) {
|
|
|
1549
1555
|
return out.join('\n') + (out.length ? '\n' : '')
|
|
1550
1556
|
}
|
|
1551
1557
|
|
|
1558
|
+
// "Priya signs off" is the most expensive sentence in a kickoff and used to land
|
|
1559
|
+
// in context.md as a note. signer: fills the success.md line the whole kit
|
|
1560
|
+
// keys on, and logs the person as a contact so prep/status can see them.
|
|
1561
|
+
const SIGNER_RX = /^(?<who>[A-Z][\w.'-]+(?:\s+[A-Z][\w.'-]+){0,3}(?:\s*\([^)]{1,40}\))?)\s+(?:signs?(?:\s+off)?|approves|has (?:the )?final say|can say yes|owns the decision|is the (?:sponsor|signer|decision[- ]maker))\b/
|
|
1562
|
+
|
|
1563
|
+
function signerFromLine(text) {
|
|
1564
|
+
const t = String(text || '').trim()
|
|
1565
|
+
const m = t.match(SIGNER_RX)
|
|
1566
|
+
if (!m) return ''
|
|
1567
|
+
const who = m.groups.who.trim()
|
|
1568
|
+
// "Staging exists" / "The API is slow" also match "Capital Word + verb"; a
|
|
1569
|
+
// sentence-initial common noun is not a person.
|
|
1570
|
+
if (/^(The|This|That|It|We|They|Staging|Budget|Prod|Production|Nobody|Someone|Everyone)\b/.test(who)) return ''
|
|
1571
|
+
return who
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
function setSigner(eng, who) {
|
|
1575
|
+
ensureMemoryGit(eng)
|
|
1576
|
+
const p = path.join(eng, 'success.md')
|
|
1577
|
+
let md = readEng(eng, 'success.md')
|
|
1578
|
+
if (!md) md = '# Success definition\n\n'
|
|
1579
|
+
const norm = (s) => String(s).replace(/\s+/g, ' ').trim().toLowerCase()
|
|
1580
|
+
const line = /^\*\*Stakeholder who signs off:\*\*\s*(.*)$/m
|
|
1581
|
+
const m = md.match(line)
|
|
1582
|
+
if (m && !m[1].trim()) {
|
|
1583
|
+
md = md.replace(line, `**Stakeholder who signs off:** ${who}`)
|
|
1584
|
+
} else if (m) {
|
|
1585
|
+
// Whole-name compare against the primary and every "also named" line under
|
|
1586
|
+
// it: "Sam" must not vanish inside "Samantha", and re-applying must not
|
|
1587
|
+
// stack duplicates.
|
|
1588
|
+
const start = md.indexOf(m[0]) + m[0].length
|
|
1589
|
+
const alsoNamed = []
|
|
1590
|
+
for (const l of md.slice(start).split('\n').slice(1)) {
|
|
1591
|
+
const a = l.match(/^- also named:\s*(.+)$/)
|
|
1592
|
+
if (!a) break
|
|
1593
|
+
alsoNamed.push(a[1])
|
|
1594
|
+
}
|
|
1595
|
+
const known = [m[1], ...alsoNamed].map(norm)
|
|
1596
|
+
if (known.includes(norm(who))) return false
|
|
1597
|
+
// A second, different name is a fact worth keeping next to the first, not
|
|
1598
|
+
// a silent overwrite - who signs is exactly the thing people argue about.
|
|
1599
|
+
const block = [m[0], ...alsoNamed.map(a => `- also named: ${a}`)].join('\n')
|
|
1600
|
+
md = md.replace(block, `${block}\n- also named: ${who}`)
|
|
1601
|
+
} else {
|
|
1602
|
+
md = md.replace(/\n*$/, `\n\n**Stakeholder who signs off:** ${who}\n`)
|
|
1603
|
+
}
|
|
1604
|
+
withFileLock(p, () => { atomicWriteFile(p, md.endsWith('\n') ? md : md + '\n') })
|
|
1605
|
+
return true
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1552
1608
|
function setNextAction(eng, text) {
|
|
1553
1609
|
ensureMemoryGit(eng)
|
|
1554
1610
|
const bullet = `- ${stripControlChars(String(text).replace(/^[-*]\s+/, '').trim())}`
|
|
@@ -1665,7 +1721,7 @@ function readSealedProposal(eng) {
|
|
|
1665
1721
|
function routeDebriefInput(eng, input, { dry, force, sealed = [] }) {
|
|
1666
1722
|
const d = new Date()
|
|
1667
1723
|
const date = d.toISOString().slice(0, 10)
|
|
1668
|
-
const counts = { decision: 0, risk: 0, delivery: 0, contact: 0, next: 0 }
|
|
1724
|
+
const counts = { decision: 0, risk: 0, delivery: 0, contact: 0, next: 0, signer: 0 }
|
|
1669
1725
|
const ctxLines = []
|
|
1670
1726
|
let nextAction = ''
|
|
1671
1727
|
ensureMemoryGit(eng)
|
|
@@ -1678,8 +1734,8 @@ function routeDebriefInput(eng, input, { dry, force, sealed = [] }) {
|
|
|
1678
1734
|
for (const raw of routable.split('\n')) {
|
|
1679
1735
|
let line = raw.trim()
|
|
1680
1736
|
if (!line) continue
|
|
1681
|
-
const bare = line.replace(/^[-*+]\s+/, '').replace(/^\*\*(decision|risk|delivery|contact|next):?\*\*:?\s*/i, '$1: ')
|
|
1682
|
-
const m = bare.match(/^(decision|risk|delivery|contact|next):\s*(.+)$/i)
|
|
1737
|
+
const bare = line.replace(/^[-*+]\s+/, '').replace(/^\*\*(decision|risk|delivery|contact|next|signer):?\*\*:?\s*/i, '$1: ')
|
|
1738
|
+
const m = bare.match(/^(decision|risk|delivery|contact|next|signer):\s*(.+)$/i)
|
|
1683
1739
|
if (m) {
|
|
1684
1740
|
const type = m[1].toLowerCase()
|
|
1685
1741
|
let body = m[2]
|
|
@@ -1688,6 +1744,18 @@ function routeDebriefInput(eng, input, { dry, force, sealed = [] }) {
|
|
|
1688
1744
|
console.error(`skipped ${type} line - looks like a ${hit}. Redact it, or re-run with --force.`)
|
|
1689
1745
|
continue
|
|
1690
1746
|
}
|
|
1747
|
+
if (type === 'signer') {
|
|
1748
|
+
const who = body.replace(/\s+signs?(?:\s+off)?\b.*$/i, '').trim() || body.trim()
|
|
1749
|
+
if (dry) {
|
|
1750
|
+
console.log(`→ success.md **Stakeholder who signs off:** ${previewLine(who)}`)
|
|
1751
|
+
console.log(`→ stakeholders.md ${previewLine(datedEntry(eng, date, `${who} signs off`))}`)
|
|
1752
|
+
} else {
|
|
1753
|
+
setSigner(eng, who)
|
|
1754
|
+
appendLogEntry(eng, 'contact', datedEntry(eng, date, `${who} signs off`), { skipCommit: true })
|
|
1755
|
+
}
|
|
1756
|
+
counts.signer++
|
|
1757
|
+
continue
|
|
1758
|
+
}
|
|
1691
1759
|
if (type === 'next') {
|
|
1692
1760
|
if (dry) console.log(`→ context.md ## Next action - ${previewLine(body)}`)
|
|
1693
1761
|
else nextAction = body
|
|
@@ -1760,7 +1828,7 @@ function cmdDebrief(args) {
|
|
|
1760
1828
|
if (smart) {
|
|
1761
1829
|
const { proposePath, clean, blocks } = writeProposal(eng, smartProposeText(input))
|
|
1762
1830
|
console.log('SMART PROPOSE (heuristic - review before apply; no new facts invented beyond line rewrites)\n')
|
|
1763
|
-
console.log('Prefix vocabulary (lines that route): decision: risk: delivery: contact: next:')
|
|
1831
|
+
console.log('Prefix vocabulary (lines that route): decision: risk: delivery: contact: next: signer:')
|
|
1764
1832
|
console.log('Everything else → context.md. Keep the prefixes; the preview gate stays.\n')
|
|
1765
1833
|
routeDebriefInput(eng, clean, { dry: true, force, sealed: blocks })
|
|
1766
1834
|
if (!apply) {
|
|
@@ -1776,7 +1844,7 @@ function cmdDebrief(args) {
|
|
|
1776
1844
|
const { counts, ctxLines, privateBlocks } = routeDebriefInput(eng, input, { dry, force, sealed })
|
|
1777
1845
|
if (!dry) {
|
|
1778
1846
|
const hash = commitMemory(eng, 'debrief', {
|
|
1779
|
-
files: ['decisions.md', 'risks.md', 'delivery.md', 'stakeholders.md', 'context.md', SIGNAL_LEDGER],
|
|
1847
|
+
files: ['decisions.md', 'risks.md', 'delivery.md', 'stakeholders.md', 'success.md', 'context.md', SIGNAL_LEDGER],
|
|
1780
1848
|
})
|
|
1781
1849
|
try { fs.unlinkSync(path.join(eng, DEBRIEF_PROPOSE)) } catch (_) {}
|
|
1782
1850
|
try { fs.unlinkSync(path.join(eng, DEBRIEF_PRIVATE)) } catch (_) {}
|
|
@@ -1784,7 +1852,7 @@ function cmdDebrief(args) {
|
|
|
1784
1852
|
if (hash) console.log(`memory @${hash}`)
|
|
1785
1853
|
}
|
|
1786
1854
|
const plural = {
|
|
1787
|
-
decision: 'decisions', risk: 'risks', delivery: 'deliveries', contact: 'contacts', next: 'next actions',
|
|
1855
|
+
decision: 'decisions', risk: 'risks', delivery: 'deliveries', contact: 'contacts', next: 'next actions', signer: 'signers',
|
|
1788
1856
|
}
|
|
1789
1857
|
const parts = Object.keys(counts).filter(t => counts[t])
|
|
1790
1858
|
.map(t => `${counts[t]} ${counts[t] === 1 ? (t === 'next' ? 'next action' : t) : plural[t]}`)
|
|
@@ -2176,6 +2244,66 @@ function findDuplicateOpenRisks(eng) {
|
|
|
2176
2244
|
return [...byKey.values()].filter(g => g.length >= 2)
|
|
2177
2245
|
}
|
|
2178
2246
|
|
|
2247
|
+
// The bound client repo moved and delivery.md did not. This is the one place
|
|
2248
|
+
// the CLI can catch "we shipped code and told the record nothing" without AI:
|
|
2249
|
+
// registry gives the workspace(s) bound to this engagement, git gives commits
|
|
2250
|
+
// newer than the last dated delivery line. Local reads only.
|
|
2251
|
+
// Only dates that stamp an entry count: a ledger row's Date cell, a dated
|
|
2252
|
+
// bullet, a dated heading. "trial night 2026-06-02" inside a Measured cell is a
|
|
2253
|
+
// promise, not a receipt - and a future promise must not hide today's commits.
|
|
2254
|
+
function latestDeliveryEntry(md) {
|
|
2255
|
+
const today = new Date().toISOString().slice(0, 10)
|
|
2256
|
+
let latest = { date: '', line: '' }
|
|
2257
|
+
for (const raw of stripTemplateNoise(md).split('\n')) {
|
|
2258
|
+
const t = raw.trim()
|
|
2259
|
+
const m = t.match(/^[-*]\s*\[(\d{4}-\d{2}-\d{2})\]/) ||
|
|
2260
|
+
t.match(/^#{1,6}\s+\[?(\d{4}-\d{2}-\d{2})(?:\]|\b)/) ||
|
|
2261
|
+
t.match(/^\|\s*(\d{4}-\d{2}-\d{2})\s*\|/)
|
|
2262
|
+
if (!m || m[1] > today) continue
|
|
2263
|
+
if (m[1] >= latest.date) latest = { date: m[1], line: t }
|
|
2264
|
+
}
|
|
2265
|
+
return latest
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
function silentCommitIssues(eng) {
|
|
2269
|
+
const slug = path.basename(path.dirname(eng))
|
|
2270
|
+
const workspaces = readRegistry().filter(r => r.slug === slug).map(r => r.workspace)
|
|
2271
|
+
if (!workspaces.length) return []
|
|
2272
|
+
const entry = latestDeliveryEntry(readClean(eng, 'delivery.md'))
|
|
2273
|
+
const lastDelivery = entry.date
|
|
2274
|
+
const out = []
|
|
2275
|
+
for (const ws of workspaces) {
|
|
2276
|
+
let st
|
|
2277
|
+
try { st = fs.statSync(ws) } catch (_) { continue }
|
|
2278
|
+
if (!st.isDirectory()) continue
|
|
2279
|
+
// The memory folder is itself a git repo; never lint it as the client repo.
|
|
2280
|
+
if (path.resolve(ws) === path.resolve(eng) || path.resolve(ws) === path.dirname(path.resolve(eng))) continue
|
|
2281
|
+
if (sh('git rev-parse --is-inside-work-tree', ws) !== 'true') continue
|
|
2282
|
+
// Memory git knows the exact moment that entry was written; dates in the
|
|
2283
|
+
// file are day-grained and would miss a commit made later the same day.
|
|
2284
|
+
// Pickaxe on the entry text, not the file: a later status edit to
|
|
2285
|
+
// delivery.md must not become the cutoff and hide commits before it.
|
|
2286
|
+
const raw = entry.line
|
|
2287
|
+
? sh(`git log -1 --format=%cI -S${JSON.stringify(entry.line)} -- delivery.md`, eng)
|
|
2288
|
+
: ''
|
|
2289
|
+
// git --since is inclusive at second grain; a commit in the same second as
|
|
2290
|
+
// the receipt is the receipt's own work, not a silent one.
|
|
2291
|
+
const stamp = raw && !Number.isNaN(Date.parse(raw)) ? new Date(Date.parse(raw) + 1000).toISOString() : ''
|
|
2292
|
+
const since = stamp ? `--since="${stamp}"`
|
|
2293
|
+
: lastDelivery ? `--since="${lastDelivery} 23:59:59"`
|
|
2294
|
+
: "--since='30 days ago'"
|
|
2295
|
+
const commits = sh(`git log ${since} --format=%h -- .`, ws).split('\n').filter(Boolean).length
|
|
2296
|
+
if (!commits) continue
|
|
2297
|
+
const where = path.basename(ws)
|
|
2298
|
+
out.push(
|
|
2299
|
+
lastDelivery
|
|
2300
|
+
? `${commits} commit(s) in ${where} since the last delivery line (${lastDelivery}) - code moved, ledger did not; log the receipt or say why nothing shipped`
|
|
2301
|
+
: `${commits} commit(s) in ${where} in 30d and delivery.md has no dated line - code moved, ledger did not; fde log delivery "slice | bucket | promised | measured | accepted | evidence | rollback"`
|
|
2302
|
+
)
|
|
2303
|
+
}
|
|
2304
|
+
return out
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2179
2307
|
// Deterministic fieldbook hygiene - shared by doctor + session TRIAGE.
|
|
2180
2308
|
// Silent when clean OR brand-new (no dated work yet). Never auto-rewrites.
|
|
2181
2309
|
// High-value moments: week-start (via triage), ship/close, after real work accrues.
|
|
@@ -2246,9 +2374,11 @@ function collectDoctorIssues(eng) {
|
|
|
2246
2374
|
'duplicate ## Next action headings in context.md - fill the first (template) section and remove extras; triage reads the last non-empty'
|
|
2247
2375
|
)
|
|
2248
2376
|
}
|
|
2249
|
-
|
|
2377
|
+
// Open, owned risks are normal mid-ship (triage already shows the count every
|
|
2378
|
+
// session). The gate is close: nothing still live when you call the embed done.
|
|
2379
|
+
if (s.phase === 'close' && s.openRisks > 0) {
|
|
2250
2380
|
issues.push(
|
|
2251
|
-
`phase is
|
|
2381
|
+
`phase is close with ${s.openRisks} open risk(s) - retire, hand off, or move still-live ones before calling the embed done`
|
|
2252
2382
|
)
|
|
2253
2383
|
}
|
|
2254
2384
|
if (s.phase === 'close' || s.phase === 'ship') {
|
|
@@ -2268,6 +2398,7 @@ function collectDoctorIssues(eng) {
|
|
|
2268
2398
|
`phase is ${s.phase} with AI in scope but no eval receipt (evals.md Verdict or delivery Eval / Ship receipts) - required before green ship/close`
|
|
2269
2399
|
)
|
|
2270
2400
|
}
|
|
2401
|
+
issues.push(...silentCommitIssues(eng))
|
|
2271
2402
|
}
|
|
2272
2403
|
const dupes = findDuplicateOpenRisks(eng)
|
|
2273
2404
|
if (dupes.length) {
|
package/bin/lib/trust.js
CHANGED
|
@@ -194,6 +194,9 @@ function createTrustApi(deps) {
|
|
|
194
194
|
}) || '').replace(/\|/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 80)
|
|
195
195
|
// Prefer trust trigger / memory warn over a random risk line; always keep mem.warn available
|
|
196
196
|
const reason = (trustReason || mem.warn) ? (trustReason || mem.warn) : topRisk
|
|
197
|
+
// What the triage line is actually quoting. A risk bullet printed under
|
|
198
|
+
// "trust:" read as a stakeholder problem that did not exist.
|
|
199
|
+
const reasonKind = mem.warn && trustReason === mem.warn ? 'memory' : trustReason ? 'trust' : mem.warn ? 'memory' : topRisk ? 'risk' : ''
|
|
197
200
|
const openRisks = countOpenRisks(eng)
|
|
198
201
|
const nextAction = nextActionLine(ctx)
|
|
199
202
|
let updated = 'never', ageDays = Infinity
|
|
@@ -203,7 +206,7 @@ function createTrustApi(deps) {
|
|
|
203
206
|
} catch (_) {}
|
|
204
207
|
const dirty = memoryDirtyManual(eng)
|
|
205
208
|
return {
|
|
206
|
-
phase, trust, signalAge, stale, topRisk, reason, memoryWarn: mem.warn,
|
|
209
|
+
phase, trust, signalAge, stale, topRisk, reason, reasonKind, memoryWarn: mem.warn,
|
|
207
210
|
dirtyFiles: dirty, openRisks, nextAction, updated, ageDays,
|
|
208
211
|
}
|
|
209
212
|
}
|
|
@@ -215,9 +218,9 @@ function createTrustApi(deps) {
|
|
|
215
218
|
const lines = [
|
|
216
219
|
`TRIAGE [${label.padEnd(6)}] phase:${phase} updated:${s.updated} open risks:${s.openRisks}`,
|
|
217
220
|
]
|
|
218
|
-
if (s.reason) {
|
|
219
|
-
const age = s.signalAge != null ? ` (${s.signalAge}d old${s.stale ? ', STALE - reconfirm' : ''})` : ''
|
|
220
|
-
lines.push(` trust: ${s.reason}${age}`)
|
|
221
|
+
if (s.reason && s.reasonKind !== 'memory') {
|
|
222
|
+
const age = s.reasonKind === 'trust' && s.signalAge != null ? ` (${s.signalAge}d old${s.stale ? ', STALE - reconfirm' : ''})` : ''
|
|
223
|
+
lines.push(` ${s.reasonKind === 'risk' ? 'top risk' : 'trust'}: ${s.reason}${age}`)
|
|
221
224
|
}
|
|
222
225
|
// Always surface corruption / unreadable memory - even when trust still reads green
|
|
223
226
|
if (s.memoryWarn) lines.push(` memory: ${s.memoryWarn}`)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fdeops",
|
|
3
|
-
"version": "3.
|
|
4
|
-
"description": "Forward deployed engineering skills for AI coding agents.
|
|
3
|
+
"version": "3.22.0",
|
|
4
|
+
"description": "Forward deployed engineering skills for AI coding agents. One @fde skill for the client work around the code: who can say yes, what went live, whether they signed off. Dated markdown on your laptop. You confirm each write.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fdeops": "bin/install.js",
|
|
7
7
|
"fde": "bin/fde.js"
|
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": "3.
|
|
5
|
-
"description": "Forward deployed engineering skills for AI coding agents
|
|
4
|
+
"version": "3.22.0",
|
|
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.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Subash Natarajan",
|
|
8
8
|
"url": "https://github.com/suboss87"
|
package/skills/fde/SKILL.md
CHANGED
|
@@ -42,6 +42,8 @@ On someone else's site the work is not "write code, remember later." Every chang
|
|
|
42
42
|
|
|
43
43
|
A throwaway file can skip the loop. Bound client work cannot.
|
|
44
44
|
|
|
45
|
+
**Skip is loud.** Bound + a change that will ship + no this-turn line in `delivery.md` = not done. Say that. Do not call it shipped. A coding pack may write the function; `@fde` still owns done.
|
|
46
|
+
|
|
45
47
|
## Human surface vs agent plumbing
|
|
46
48
|
|
|
47
49
|
**FDE (human):** `@fde` + English, or `/brief` `/discover` `/plan` `/ship` `/outcome` `/close` `/debrief` `/prep` `/trust` `/receipts` `/readout`. Never a skill catalog.
|
|
@@ -129,8 +131,8 @@ Work names (engage, diagnose, align, deliver, realize, transfer) are the same ma
|
|
|
129
131
|
|
|
130
132
|
| You hear | Skill | Reference |
|
|
131
133
|
|----------|-------|-----------|
|
|
132
|
-
| Diagnose, don't know the real problem, brief feels wrong, shadow processes, frame discovery, understand the problem space, data not ready, data estate, catalog the data | discover | `references/discover.md` |
|
|
133
|
-
| The brief feels too neat, assumptions untested, "we just need…", test assumptions | test-assumptions | `references/test-assumptions.md` |
|
|
134
|
+
| Diagnose, don't know the real problem, brief feels wrong, shadow processes, frame discovery, understand the problem space, data not ready, data estate, catalog the data, parts of the problem, decompose | discover | `references/discover.md` |
|
|
135
|
+
| The brief feels too neat, assumptions untested, "we just need…", test assumptions, inherited convention, why do we always | test-assumptions | `references/test-assumptions.md` |
|
|
134
136
|
| Multiple use cases competing, "we want to do everything", score use cases | score-use-cases | `references/score-use-cases.md` |
|
|
135
137
|
| Need to validate a direction, prototype, demo to de-risk, **POC**, spike, killer assumption, validate the solution, build prototype | poc | `references/poc.md` |
|
|
136
138
|
|
|
@@ -140,7 +142,7 @@ Work names (engage, diagnose, align, deliver, realize, transfer) are the same ma
|
|
|
140
142
|
|----------|-------|-----------|
|
|
141
143
|
| Align, break this down, what order, sequence the build, plan the roadmap, create user stories, write the tasks | plan | `references/plan.md` |
|
|
142
144
|
| Sponsor needs justification, need to defend budget or timeline, build the business case | business-case | `references/business-case.md` |
|
|
143
|
-
| Significant decision, multiple approaches, "what should we do?", generate solutions, generate options | three-options | `references/three-options.md` |
|
|
145
|
+
| Significant decision, multiple approaches, "what should we do?", generate solutions, generate options, not the playbook, from the surviving facts | three-options | `references/three-options.md` |
|
|
144
146
|
| 20 things are "urgent," need to pick the 3 that matter, prioritize three | pick-three | `references/pick-three.md` |
|
|
145
147
|
|
|
146
148
|
### Ship
|
|
@@ -196,8 +198,8 @@ Ready to build with no `terrain.md` / plan: discover or plan first. Takeover wit
|
|
|
196
198
|
|
|
197
199
|
- Never ask the FDE to pick a phase. That's your job.
|
|
198
200
|
- Same six stages at any scale. Overlays carry the industry. Greenfield and brownfield change the first move inside ship, not the map.
|
|
199
|
-
- Ground loop on a bound client: name → characterise → prove on their staging → go live → log.
|
|
200
|
-
- Do not call a change done until the signer in `success.md` can reject it on staging they operate.
|
|
201
|
+
- Ground loop on a bound client: name → characterise → prove on their staging → go live → log. A coding pack may write the function. `@fde` still owns done. When they disagree, their repo and the signer win.
|
|
202
|
+
- Do not call a change done until the signer in `success.md` can reject it on staging they operate. No this-turn receipt in `delivery.md` is a failed test, not a note to write later.
|
|
201
203
|
- Read `context.md` before speaking. One sharp question - never a barrage.
|
|
202
204
|
- Never invent people, meetings, or numbers - `unknown - ask:` beats a polished lie.
|
|
203
205
|
- Every phase ends with its artifact written. No artifact, no "done."
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
## Honest contract (read once)
|
|
12
12
|
|
|
13
13
|
- The `fde` CLI is **local, deterministic, no AI**. `--smart` is a **gate + writer**, not a brain.
|
|
14
|
-
- It keeps lines that already have `decision:` / `risk:` / `delivery:` / `contact:` / `next:` prefixes, plus a thin keyword pass (e.g. "we agreed", person+verb lines, "open question").
|
|
14
|
+
- It keeps lines that already have `decision:` / `risk:` / `delivery:` / `contact:` / `next:` / `signer:` prefixes, plus a thin keyword pass (e.g. "we agreed", person+verb lines, "open question", "X signs off").
|
|
15
|
+
- `signer: Priya` fills **Stakeholder who signs off** in `success.md` and logs Priya as a contact. The CLI proposes it when a sentence says someone signs off / approves / has final say. If the notes name who can say yes and the proposal does not carry a `signer:` line, add one - that is the most expensive sentence in the meeting.
|
|
15
16
|
- Real messy notes without prefixes often route **0 useful lines** - everything else lands as a context dump. That is expected. **You are the router:** rewrite `.debrief-propose` with type prefixes, then `--apply`.
|
|
16
17
|
- `.debrief-propose` is raw lines only (no routing annotations). "Edit if mis-routed" means **rewrite the line with the right prefix**, not leave a comment in the file.
|
|
17
18
|
|
|
@@ -25,6 +26,7 @@
|
|
|
25
26
|
- `decision: agreed chargebacks stay phase 2 - Priya`
|
|
26
27
|
- `risk: legal may reopen scope if we slip the SOW date`
|
|
27
28
|
- `contact: Priya pushed hard on Friday deck [signal:amber]`
|
|
29
|
+
- `signer: Priya` (she can say yes; lands in `success.md`)
|
|
28
30
|
- `next: send one-pager before Thursday 9am`
|
|
29
31
|
- unprefixed lines stay context color only
|
|
30
32
|
4. Show the **proposed** routing in plain language (what would become decisions, risks, contacts, next).
|
|
@@ -43,7 +45,7 @@ If `--smart` is unavailable or you already have clean prefixes:
|
|
|
43
45
|
- **Stakeholder signals** - tone shifts with evidence → green/amber/red
|
|
44
46
|
- **Risks** - new / confirmed / retired
|
|
45
47
|
- **Open questions** - what to chase next
|
|
46
|
-
2. Format lines as `decision:` / `risk:` / `delivery:` / `contact:` / `next:` (contacts may end with `[signal:green|amber|red]`).
|
|
48
|
+
2. Format lines as `decision:` / `risk:` / `delivery:` / `contact:` / `next:` / `signer:` (contacts may end with `[signal:green|amber|red]`).
|
|
47
49
|
3. Show that structured version to the FDE for confirmation.
|
|
48
50
|
4. Pipe to `fde debrief` (or write a file and run it).
|
|
49
51
|
|
|
@@ -54,6 +54,16 @@ Tests on **Question** - rewrite until all five hold:
|
|
|
54
54
|
|
|
55
55
|
Cannot write the Question → keep interrogating. Do not `fde scan`. Every later output of this phase aims at that Question. Sub-questions go to the operating map or `assumptions.md`, not into the Question.
|
|
56
56
|
|
|
57
|
+
## Parts of the problem (decompose only)
|
|
58
|
+
|
|
59
|
+
After the Question is locked, and **before** `fde scan` or any option: write what the problem is made of. No advice, no playbook, no solution.
|
|
60
|
+
|
|
61
|
+
If the stated brief hides a deeper job, name that deeper job in one sentence and **wait**. Do not silently replace their problem with yours.
|
|
62
|
+
|
|
63
|
+
In `terrain.md` under `## Parts`, list the smallest useful pieces that still change what you examine next. Typical cuts: people, process step, system, data, time, cost. For each piece: what it contains, and how it connects to the Question. Stop when a further split would not change where you dig.
|
|
64
|
+
|
|
65
|
+
Do not mark pieces as facts or assumptions here. That is `test-assumptions`. Do not assemble options here. That is `three-options`.
|
|
66
|
+
|
|
57
67
|
## Method - part 1: the codebase (you do this work)
|
|
58
68
|
|
|
59
69
|
**First move: `fde scan`** - it runs everything below deterministically in seconds (churn×tests, "temporary" archaeology, AI components, secrets redacted, previous attempts). Your job is then **interpretation**: read its output against the brief, follow the hotspots into the code, and connect the technical findings to the human signals in part 2.
|
|
@@ -127,9 +137,11 @@ When discovery requires a structured session with multiple stakeholders (alignme
|
|
|
127
137
|
|
|
128
138
|
**After the room:** Summary in `decisions.md` within 2 hours. Decisions decay - what felt clear at 3pm is debatable by 5pm if unwritten.
|
|
129
139
|
|
|
130
|
-
## Method - part 4: data estate
|
|
140
|
+
## Method - part 4: data estate and the pipe
|
|
141
|
+
|
|
142
|
+
Always map the estate before you score a use case - not only when someone said "AI." A path they cannot feed is a discover miss, not a ship surprise.
|
|
131
143
|
|
|
132
|
-
|
|
144
|
+
**Their words first.** In `terrain.md`, write the names the floor uses for the workaround, the sheet, the exception path, and the person who left. Later plan/ship/review sentences use those names. Do not translate their floor into generic product language.
|
|
133
145
|
|
|
134
146
|
**The 5 questions (ask the data owner, not the sponsor):**
|
|
135
147
|
1. **Where does data live?** - List every source: databases, warehouses, SaaS exports, spreadsheets, S3 buckets, vendor APIs. Map it.
|
|
@@ -138,13 +150,15 @@ When the engagement involves AI, analytics, or data-heavy automation, assess the
|
|
|
138
150
|
4. **What's the quality?** - Sample 100 rows from each critical source. Check: nulls, duplicates, format consistency, semantic correctness. A 60% null rate in a key field = that source is fiction.
|
|
139
151
|
5. **What are the governance constraints?** - PII classification, retention policies, cross-border rules, consent basis. One missed constraint = a compliance stop later.
|
|
140
152
|
|
|
153
|
+
**The pipe (what talks to what).** For each source that a use case depends on, write: the system it flows from and to, the contract (object, table, file, API), whose credentials, what happens when the vendor 500s or the Monday file does not land, and whether the join the sponsor described actually exists. Their IdP, CRM, and warehouse are delivery work when the path needs them - policy questions in `trust-profile.md` are not a substitute.
|
|
154
|
+
|
|
141
155
|
**The data readiness matrix:**
|
|
142
156
|
|
|
143
|
-
| Source | Location | Freshness | Owner | Quality (sample) | Governance | Verdict |
|
|
144
|
-
|
|
145
|
-
| _fill per source_ | | | | | | Ready / Needs work / Blocker |
|
|
157
|
+
| Source | Location | Freshness | Owner | Quality (sample) | Governance | Pipe | Verdict |
|
|
158
|
+
|--------|----------|-----------|-------|-----------------|------------|------|---------|
|
|
159
|
+
| _fill per source_ | | | | | | | Ready / Needs work / Blocker |
|
|
146
160
|
|
|
147
|
-
A use case that depends on a "Blocker" source doesn't get scored - it gets a
|
|
161
|
+
A use case that depends on a "Blocker" source **or a Blocker pipe** doesn't get scored - it gets a remediation conversation first. `what-breaks` finding an invisible integration at ship is already too late. Write this to `terrain.md` under a `## Data estate` section.
|
|
148
162
|
|
|
149
163
|
## When scope is a transformation, not a single problem
|
|
150
164
|
|
|
@@ -47,6 +47,7 @@ The difference between a 5-year FDE and a 15-year FDE is not talent - it's encod
|
|
|
47
47
|
| **Specific enough?** | Contains concrete steps, not just principles | "Build trust" / "Communicate well" - too vague to act on |
|
|
48
48
|
| **Repeatable?** | Applies to a class of situations, not just this one | Only worked because of a unique circumstance |
|
|
49
49
|
| **Falsifiable?** | You can tell when the pattern is working or not | No way to measure whether applying it helped |
|
|
50
|
+
| **Monday bag?** | You would refuse the next similar embed without this in your bag - a named move plus an artifact you can drop on day one (pipe questions, CAB dance, eval golden shape, floor-drill script) | "We learned to communicate." Patterns that only live in this client's `.fde/` do not compound |
|
|
50
51
|
|
|
51
52
|
**4. Classify by stage.** Patterns sort into the same stages as the skills:
|
|
52
53
|
|
|
@@ -22,9 +22,9 @@ Intelligence without evidence is token-maxing with a nicer name. An FDE earns tr
|
|
|
22
22
|
|
|
23
23
|
**3. Score pass/fail, not vibes.** Run the suite. Record count pass / fail. Failures get a failure-mode tag (missing data, wrong record, format drift, hallucination, retrieval miss, unsafe action, other).
|
|
24
24
|
|
|
25
|
-
**4. Human-in-the-loop gate.** Name which outcomes require human approve before side effects.
|
|
25
|
+
**4. Human-in-the-loop gate.** Name which outcomes require human approve before side effects. Judgement that has a side effect (write, send, transfer, ticket, deploy, pay, page) is **NO-SHIP** without a named human on their side in the loop. Do not write "none - allowed under policy" to bless lights-out write-access. Staging may run a supervised loop with a kill switch, a cost cap, and a golden set from **their** failures. Production stays gated until they have a written policy, a named owner, and dated eval receipts on real traffic.
|
|
26
26
|
|
|
27
|
-
**5. Ship rule.** Until `evals.md` shows Verdict **SHIP** with a dated run (critical fails = 0) and HITL filled
|
|
27
|
+
**5. Ship rule.** Until `evals.md` shows Verdict **SHIP** with a dated run (critical fails = 0) and HITL filled, AI-touching ship stays **fix-first**. Eval fails do not sit in a backlog - they reopen plan (descope, move the judgement, or kill the path). Log a one-line eval receipt in `delivery.md` → `## Ship receipts`. No "probably fine."
|
|
28
28
|
|
|
29
29
|
## Artifact - `evals.md`
|
|
30
30
|
|
|
@@ -39,5 +39,5 @@ Present to the FDE: suite size, pass rate, top failure mode, HITL gate, Verdict
|
|
|
39
39
|
- No golden set, no AI ship.
|
|
40
40
|
- Pass/fail beats “looks good.”
|
|
41
41
|
- Failure modes are the product - the happy path is table stakes.
|
|
42
|
-
- HITL is a gate, not a slide.
|
|
42
|
+
- HITL is a gate, not a slide. Side effects without a named human on their side are NO-SHIP.
|
|
43
43
|
- Non-AI work does not need this file.
|
|
@@ -31,7 +31,7 @@ List what you can actually call **this session**:
|
|
|
31
31
|
3. **Stage** - `fde ingest stage [--source NAME] [--title TEXT] [file|-]` writes raw text into `<engagement>/.inbox/` (outside the memory git ledger).
|
|
32
32
|
4. **List** (optional) - `fde ingest list` shows staged items when you need an id or filename.
|
|
33
33
|
5. **Propose** - `fde ingest propose <id-or-filename>` runs the debrief `--smart` path on the staged body (+ provenance line). Opens `.debrief-propose`.
|
|
34
|
-
6. **Rewrite prefixes** - same as debrief: lines without `decision:` / `risk:` / `delivery:` / `contact:` / `next:` need **you** to rewrite before showing the FDE. `--smart` is a gate, not a brain.
|
|
34
|
+
6. **Rewrite prefixes** - same as debrief: lines without `decision:` / `risk:` / `delivery:` / `contact:` / `next:` / `signer:` need **you** to rewrite before showing the FDE. `--smart` is a gate, not a brain.
|
|
35
35
|
7. **Show** the proposed routing in plain language. Wait for confirm.
|
|
36
36
|
8. **Apply** - on FDE confirm only → `fde ingest apply` (= `fde debrief --apply`). On reject → stop; ask what to change.
|
|
37
37
|
|
|
@@ -99,12 +99,12 @@ If `stakeholders.md` already has a `## Signal history` section (it does from the
|
|
|
99
99
|
|
|
100
100
|
**`trust-profile.md`** - sacred data (`<private>` tagged), fears heard, AI policy, approval chain. Sensitive: skip for status reads; use CLI/redacted surfaces; never paste raw `<private>` into prompts or subagents.
|
|
101
101
|
|
|
102
|
-
**`assumptions.md`** - seed every unverified claim from the brief (and the day-1 hypothesis) as rows with blast radius CRITICAL / LOAD-BEARING / CONVENIENCE and status `OPEN`. Do not wait for test-assumptions - land makes the register exist. Example:
|
|
102
|
+
**`assumptions.md`** - seed every unverified claim from the brief (and the day-1 hypothesis) as rows with Kind `UNKNOWN` (or `CONVENTION` if they said "we always"), blast radius CRITICAL / LOAD-BEARING / CONVENIENCE, and status `OPEN`. Do not wait for test-assumptions - land makes the register exist. Example:
|
|
103
103
|
|
|
104
104
|
```markdown
|
|
105
|
-
| # | Assumption | Blast radius | How we test | Status | Evidence |
|
|
106
|
-
|
|
107
|
-
| 1 | <claim from brief> | CRITICAL | <cheapest falsifying test> | OPEN | (stated, unverified) |
|
|
105
|
+
| # | Assumption | Kind | Blast radius | How we test | Status | Evidence |
|
|
106
|
+
|---|------------|------|--------------|-------------|--------|----------|
|
|
107
|
+
| 1 | <claim from brief> | UNKNOWN | CRITICAL | <cheapest falsifying test> | OPEN | (stated, unverified) |
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
One falsifiable hypothesis about the real problem also goes at the bottom of `brief.md` - discover / test-assumptions will test it.
|
|
@@ -8,7 +8,9 @@ A green check on synthetic data is not a validated solution. The person who can
|
|
|
8
8
|
|
|
9
9
|
## Method (you do this work)
|
|
10
10
|
|
|
11
|
-
**0. Name the killer assumption.** With the FDE: "What's the belief that kills the project if it's wrong?" Prototype **that** - not the pretty demo.
|
|
11
|
+
**0. Name the killer assumption.** With the FDE: "What's the belief that kills the project if it's wrong?" Prototype **that** - not the pretty demo. If `three-options` just ran: the cheapest test is for the recommended option first, unless they pick another.
|
|
12
|
+
|
|
13
|
+
**0b. Pass / fail before you build.** For the test you will run, write three lines in `prototype-log.md` first: what you will actually do (who you talk to, what you show, on whose screen); the result that **kills** this option; the result that keeps it alive. What you would learn either way. If every option's test would fail, name which `assumptions.md` block to reopen - do not invent a fourth playbook.
|
|
12
14
|
|
|
13
15
|
**1. Pick by score when several use cases compete.** Use the scoring model from `discover.md` - (Value × Data readiness) / Complexity. If discover already scored, reuse; never re-score independently.
|
|
14
16
|
|
|
@@ -37,6 +39,7 @@ Tell the FDE: did the riskiest assumption hold · what the customer's reaction a
|
|
|
37
39
|
## Principles
|
|
38
40
|
|
|
39
41
|
- Speed of learning beats code quality. Never more than a day.
|
|
42
|
+
- Write pass/fail before you build. A demo with no kill line is a show.
|
|
40
43
|
- Show it rough. Polish misleads.
|
|
41
44
|
- Prototype the killer assumption, not the demo.
|
|
42
45
|
- Kill fast; log the learning.
|
|
@@ -12,15 +12,16 @@
|
|
|
12
12
|
|
|
13
13
|
| Block | What to write | Source |
|
|
14
14
|
|-------|---------------|--------|
|
|
15
|
-
| **S - Situation** | Where we are against `success.md`, in their words | success.md, delivery value ledger |
|
|
15
|
+
| **S - Situation** | Where we are against `success.md`, in their words - including whether the floor still uses the old path | success.md, delivery value ledger, reality.md workaround |
|
|
16
16
|
| **C - Complication** | What changed, what is at risk, or what we learned (bad news first) | risks.md, assumptions DISPROVED/OPEN, stakeholders signal |
|
|
17
17
|
| **Q - Question / Ask** | The one decision or help you need from them | decisions.md, access/sign-off needs |
|
|
18
18
|
| **A - Answer** | What you recommend / what happens next week (≤3 bullets) | plan Now lane, delivery promised→measured |
|
|
19
19
|
|
|
20
20
|
Then add, still on the same page:
|
|
21
21
|
1. **Value this week** - from the value ledger: promised → measured (or "pending") → **accepted by whom**, with evidence citation. A measured number nobody on the customer side has agreed to is written as `claimed`, and the Ask never rests on it - if the whole case for the next phase is a claimed number, the real ask this week is "who signs off that this is real?".
|
|
22
|
-
2. **
|
|
23
|
-
3. **
|
|
22
|
+
2. **Are they using it?** - Situation must say whether the workaround is still open: spreadsheet still running, shadow paste still happening, named operator completed Tuesday's job on the new path without you at the keyboard. A measured metric with the old path still live is `claimed`. That week's Ask is not "fund phase 2." It is "who on their side stops the old way, by when."
|
|
23
|
+
3. **Kill / defer reminder** - one line from the plan kill list so scope fights stay visible.
|
|
24
|
+
4. **Hostile Q prep** - three questions a skeptical sponsor will ask, with one-line answers from memory.
|
|
24
25
|
|
|
25
26
|
Exec voice: no jargon, no hedging, every claim traceable (`(shipped Tue, delivery.md)`). Draft in the **FDE's voice, for the FDE to send** - never send anything yourself.
|
|
26
27
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# review - Review the change
|
|
2
2
|
|
|
3
|
-
**Enter when:** a change needs review before merge - "is this safe," "does it match what we agreed."
|
|
3
|
+
**Enter when:** a change needs review before merge - "is this safe," "does it match what we agreed." Their team commented on the PR: same skill. Comments are to check, not to obey.
|
|
4
4
|
|
|
5
5
|
**Read first:** `context.md`, `decisions.md`, `trust-profile.md`, `terrain.md`. Not `reality.md`/`stakeholders.md` - irrelevant to reviewing code against agreed scope.
|
|
6
6
|
|
|
@@ -52,7 +52,7 @@ Five dimensions, line-specific ("line 47 fails under concurrent writes - no lock
|
|
|
52
52
|
|
|
53
53
|
1. Read the full diff before commenting.
|
|
54
54
|
2. Verdicts: **Stage 1: Pass / Blocked (reason)** · **Stage 2: Pass / Concerns (line-specific)**.
|
|
55
|
-
3. Fix only **real** findings tied to this change - no drive-by refactors. Reject false positives with one sentence why.
|
|
55
|
+
3. Fix only **real** findings tied to this change - no drive-by refactors. Reject false positives with one sentence why. Their comments are to check, not to obey. Restate each against the one-line intent and `trust-profile.md`. One item unclear → ask before changing any of them. If it breaks a signed constraint, a sacred system, or nothing calls it: one-sentence pushback, then wait.
|
|
56
56
|
4. Add or update a test per bug found where possible.
|
|
57
57
|
5. Re-run tests/typechecks - state what ran.
|
|
58
58
|
6. Re-review. Repeat until Pass/Pass or a human must decide scope/product.
|
|
@@ -60,6 +60,7 @@ Rollback: <exact command and expected time>
|
|
|
60
60
|
|---------|-------|-----------|----------|--------|
|
|
61
61
|
| **Architecture walkthrough** | Why, not what. The decisions, the trade-offs, the things that almost went wrong. | Full team | 60-90 min | Recording + Q&A log |
|
|
62
62
|
| **Operational drill** | Deploy, rollback, incident response. They do it, you watch. | On-call team | 60 min | Drill report with confidence level |
|
|
63
|
+
| **Floor drill** | They run the real job (the exception on the operating map) while you watch, hands off. Then they teach the next person. If they cannot, the runbook is a PDF. | Named operator on the floor | 45-60 min | They used the 2am doc during the drill, or the embed is not closed |
|
|
63
64
|
| **Edge-case handover** | The things that aren't in any document. The workarounds, the fragile spots, the "ask Sarah because she's the only one who knows." | Team lead + 1 | 30 min | Additions to `handoff.md` |
|
|
64
65
|
|
|
65
66
|
**4. The confidence check.** After the knowledge transfer, score the team's readiness:
|
|
@@ -68,11 +69,12 @@ Rollback: <exact command and expected time>
|
|
|
68
69
|
|------|------------------|----------|
|
|
69
70
|
| Daily operations | | Can they deploy and rollback without help? |
|
|
70
71
|
| Incident response | | Did they complete the drill within acceptable time? |
|
|
72
|
+
| Floor job | | Did the named operator complete Tuesday's real exception without you at the keyboard? |
|
|
71
73
|
| Architecture decisions | | Can they explain why the system is built this way? |
|
|
72
74
|
| AI components (if any) | | Do they know how to monitor, retrain, and disable? |
|
|
73
75
|
| Stakeholder management | | Do they know who to update and how? |
|
|
74
76
|
|
|
75
|
-
**Average below 3.5 → the handoff is not complete.** Extend
|
|
77
|
+
**Average below 3.5 → the handoff is not complete.** Extend, or tell the sponsor the embed is not closed. Do not document the gap and leave.
|
|
76
78
|
|
|
77
79
|
**5. The successor brief.** If a new FDE is taking over, write a brief that gets them operational in one hour:
|
|
78
80
|
|
|
@@ -132,7 +134,7 @@ If you see 2+: accelerate the handoff immediately. The longer you stay past usef
|
|
|
132
134
|
|
|
133
135
|
- The goal of every engagement is to make yourself replaceable.
|
|
134
136
|
- The 2am document is the real handoff - everything else supports it.
|
|
135
|
-
- Knowledge transfer is
|
|
137
|
+
- Knowledge transfer is four sessions, not a doc dump. The floor drill is the named operator completing Tuesday's real job from the 2am doc, hands off. If they cannot, do not close.
|
|
136
138
|
- A confidence score below 3.5 means the handoff isn't done.
|
|
137
139
|
- The successor brief gets the next FDE operational in one hour or it's too long.
|
|
138
140
|
- Clean exit: no personal credentials left behind, ever.
|
|
@@ -27,7 +27,7 @@ Skip POC only when the killer assumption already lives in the repo (typical brow
|
|
|
27
27
|
|
|
28
28
|
**Done means:** the signer in `success.md` can reject this on staging they operate. A green check on your laptop is not delivery. Do not start the next change until this one is rejectable.
|
|
29
29
|
|
|
30
|
-
If `terrain.md` **Data estate** lists a **Blocker** this change depends on: stop. That is discover, not ship. Do not build a path they cannot feed.
|
|
30
|
+
If `terrain.md` **Data estate** lists a **Blocker** this change depends on (source or pipe): stop. That is discover, not ship. Do not build a path they cannot feed.
|
|
31
31
|
|
|
32
32
|
## Method - one change they can see
|
|
33
33
|
|
|
@@ -56,6 +56,10 @@ Each change is independently revertible.
|
|
|
56
56
|
- [ ] Rollback named: revert this change, or something more specific
|
|
57
57
|
- [ ] No dependency on an unmerged change (if dependent, state it and land in order)
|
|
58
58
|
- [ ] `Kill if` is written - the observation that stops this change
|
|
59
|
+
- [ ] Before-receipt captured: the failing output, number, or screen as it is today, dated in `delivery.md`, before you change anything
|
|
60
|
+
- [ ] Open PRs and uncommitted work in the area checked (`gh pr list`, `gh pr diff <n> --name-only`); overlap goes to `decisions.md` before you start
|
|
61
|
+
|
|
62
|
+
Your coding pack writes the function. This skill owns done. When they disagree with this repo, the repo wins.
|
|
59
63
|
|
|
60
64
|
**The loop.** In this order:
|
|
61
65
|
|
|
@@ -72,10 +76,12 @@ Read existing code in the area (search before creating)
|
|
|
72
76
|
|
|
73
77
|
**Prove it on their staging.** A green check on your laptop is not delivery.
|
|
74
78
|
|
|
75
|
-
- Run **their** test command,
|
|
79
|
+
- Run **their** test command, on **their** CI, with **their** fixtures. Write the command and the result in `delivery.md` in this turn. You do not add a runner they will not keep. If you have not run their command in this turn, you cannot write that it passed. Last session's green, "should pass," and "looks correct" are not a receipt. Missing this-turn line = not proven. Same as a failing test.
|
|
76
80
|
- If the signer in `success.md` cannot reject this on a screen they already use, it is not proven.
|
|
77
81
|
- Staging they operate beats a local demo. If you have no staging: `unknown - ask:` who owns an environment, then stop pretending it shipped.
|
|
82
|
+
- **Monday-shaped data.** Staging that is empty, synthetic, or last quarter is not next Tuesday. Before go-live, write what staging is missing (volume, PII, the batch that only runs in prod, the account that only exists in the warehouse) and what that means for the kill test. If the signer cannot reject it on a screen they already operate, with data that looks like next Tuesday, it is not proven.
|
|
78
83
|
- Model in the path: `eval-pack` until `evals.md` says SHIP. Do not skip because "it looked right in chat."
|
|
84
|
+
- A model drafts. A named human on their side ships. No unsupervised loop on their production. If the brief demands lights-out write-access, that is `who-decides` / `hold-scope`, not ship.
|
|
79
85
|
|
|
80
86
|
The proof is whatever this client already believes, plus one new receipt they can replay.
|
|
81
87
|
|
|
@@ -214,6 +220,8 @@ grep -rnE "(api[_-]?key|secret|password|token)\s*[:=]\s*['\"][^'\"]{8,}" \
|
|
|
214
220
|
|
|
215
221
|
**Canary:** 1-5% of traffic, ≥10 minutes. Watch error rate, latency, and **the business metric this change affects**. Anything looks wrong → roll back immediately; investigate safely; redeploy when confident. Never investigate during the canary. Then stage up: 5% → 25% → 100%, each confirmed stable.
|
|
216
222
|
|
|
223
|
+
**Canary receipt** (write it, or the canary did not happen): what was watched, on whose dashboard, for how long, and that the next change did not start in the window. If prod is a CAB console, vendor button, or their pipeline, write the owner and the click path - the host agent does not get to pretend it shipped.
|
|
224
|
+
|
|
217
225
|
**Programme-scale rollout (transformations)** - different problem from one service:
|
|
218
226
|
1. **Pilot** - one team, one use case; success metrics defined *before* it starts (after = fitting metrics to results).
|
|
219
227
|
2. **Limited release** - 3-5 teams, real load; this is where the failure modes the pilot hid show up.
|
|
@@ -8,7 +8,7 @@ Every engagement is built on assumptions. Most are invisible until they're wrong
|
|
|
8
8
|
|
|
9
9
|
## Method (you do this work)
|
|
10
10
|
|
|
11
|
-
**1. Extract the assumptions.** Read `brief.md` and `
|
|
11
|
+
**1. Extract the assumptions.** Read `brief.md`, `reality.md`, and `terrain.md` `## Parts` line by line. Every statement that isn't backed by evidence is an assumption. Treat every "obvious" block as a convention until a receipt proves it. Common hiding places:
|
|
12
12
|
|
|
13
13
|
| Where assumptions hide | Example | The real question |
|
|
14
14
|
|----------------------|---------|-------------------|
|
|
@@ -19,7 +19,17 @@ Every engagement is built on assumptions. Most are invisible until they're wrong
|
|
|
19
19
|
| **The data claim** | "We have good data for this" | Defined how? Validated when? By whom? Sample checked? |
|
|
20
20
|
| **The "just"** | "We just need to add a feature" | On what system? With what dependencies? What breaks? |
|
|
21
21
|
|
|
22
|
-
**2.
|
|
22
|
+
**2. Kind first, then blast radius.** For each row, classify:
|
|
23
|
+
|
|
24
|
+
| Kind | Meaning |
|
|
25
|
+
|------|---------|
|
|
26
|
+
| **FACT** | A dated receipt, a measurement, or the repo. You can point at it. |
|
|
27
|
+
| **CONVENTION** | How they have always done it. The playbook. "We just…" |
|
|
28
|
+
| **UNKNOWN** | No evidence either way. |
|
|
29
|
+
|
|
30
|
+
Order the list load-bearing first. For each CONVENTION or UNKNOWN, one line: what breaks if it is wrong, and what opens if you **invert** it (stop obeying it). A FACT with no receipt is UNKNOWN - do not promote it to protect the brief.
|
|
31
|
+
|
|
32
|
+
Then classify blast radius:
|
|
23
33
|
|
|
24
34
|
```
|
|
25
35
|
CRITICAL - if wrong, the engagement fails or the approach changes fundamentally
|
|
@@ -55,11 +65,11 @@ Evidence first, then the question. Let them reach the conclusion.
|
|
|
55
65
|
**`assumptions.md`** - this IS the register (create if land did not). Keep one live table; do not only bury results in `reality.md`:
|
|
56
66
|
|
|
57
67
|
```markdown
|
|
58
|
-
| # | Assumption | Blast radius | How we test | Status | Evidence |
|
|
59
|
-
|
|
60
|
-
| 1 | API is the bottleneck | CRITICAL | p95 instrumentation 24h | DISPROVED | 80% wait in DB layer (Day N) |
|
|
61
|
-
| 2 | Team will adopt new tool | LOAD-BEARING | 3 individual interviews | CONFIRMED | 2/3 describe a use case unprompted |
|
|
62
|
-
| 3 | Data clean enough for ML | CRITICAL | 200-record sample | PARTIAL → OPEN follow-up | 12% nulls on key field; cleaning task added |
|
|
68
|
+
| # | Assumption | Kind | Blast radius | How we test | Status | Evidence |
|
|
69
|
+
|---|------------|------|--------------|-------------|--------|----------|
|
|
70
|
+
| 1 | API is the bottleneck | CONVENTION | CRITICAL | p95 instrumentation 24h | DISPROVED | 80% wait in DB layer (Day N) |
|
|
71
|
+
| 2 | Team will adopt new tool | UNKNOWN | LOAD-BEARING | 3 individual interviews | CONFIRMED | 2/3 describe a use case unprompted |
|
|
72
|
+
| 3 | Data clean enough for ML | UNKNOWN | CRITICAL | 200-record sample | PARTIAL → OPEN follow-up | 12% nulls on key field; cleaning task added |
|
|
63
73
|
```
|
|
64
74
|
|
|
65
75
|
Status values: `OPEN` · `TESTING` · `CONFIRMED` · `DISPROVED` · `PARKED`. A CRITICAL row still `OPEN` blocks plan.
|
|
@@ -85,6 +95,7 @@ Result: acked in 40 minutes, by Marco, not finance. Assumption DISPROVED, and th
|
|
|
85
95
|
## Principles
|
|
86
96
|
|
|
87
97
|
- Every "just" is an assumption. Every "should" is an assumption.
|
|
98
|
+
- Kind before blast radius. A FACT with no receipt is UNKNOWN.
|
|
88
99
|
- Kill the riskiest, cheapest-to-test assumption first.
|
|
89
100
|
- Evidence first, then the question. Let the customer reach the conclusion.
|
|
90
101
|
- A brief with zero disproved assumptions wasn't audited - it was accepted.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Enter when:** a significant technical or strategic decision needs to be made, the FDE is asked "what should we do?", the team is stuck between approaches, or a fork in the engagement requires the sponsor's input.
|
|
4
4
|
|
|
5
|
-
**Read first:** `reality.md`, `terrain.md`, `success.md`, `context.md`. Load `business-case.md` if the decision has cost implications.
|
|
5
|
+
**Read first:** `reality.md`, `terrain.md`, `assumptions.md`, `success.md`, `context.md`. Load `business-case.md` if the decision has cost implications.
|
|
6
6
|
|
|
7
7
|
One option is a request for trust. Two options is a false choice. Three options is a conversation between professionals. The FDE who presents three genuine options earns the decision-maker's respect - and their protection when things get hard.
|
|
8
8
|
|
|
@@ -12,24 +12,30 @@ One option is a request for trust. Two options is a false choice. Three options
|
|
|
12
12
|
|
|
13
13
|
> "Decision: approach for the payment migration. Decided by: CTO. Needed by: Friday. Deferral cost: blocks the next sprint and delays the pilot by two weeks."
|
|
14
14
|
|
|
15
|
-
**2. Generate three genuine options.**
|
|
15
|
+
**2. Generate three genuine options from what survived.** Read `assumptions.md` (CONFIRMED / DISPROVED) and `terrain.md` `## Parts`. The playbook is unavailable. Assemble from those blocks only.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|-----------------|-------------|-------------|
|
|
19
|
-
| **Conservative** | Lowest risk, smallest change, longest timeline | When trust is thin or the system is fragile |
|
|
20
|
-
| **Pragmatic** | Balanced risk/reward, proven patterns, moderate timeline | When the team is competent and the deadline is real |
|
|
21
|
-
| **Ambitious** | Highest reward, most change, highest risk | When the sponsor has appetite and the team has capacity |
|
|
17
|
+
Not "good / medium / bad." Not three speeds of the same plan (Conservative / Pragmatic / Ambitious of one architecture). Three approaches that differ in **structure** - rearrange the same surviving parts.
|
|
22
18
|
|
|
23
|
-
Each option must be one the FDE would genuinely recommend under different circumstances. If you
|
|
19
|
+
Each option must be one the FDE would genuinely recommend under different circumstances. If you cannot defend an option, replace it - padding is visible.
|
|
20
|
+
|
|
21
|
+
For each option, name:
|
|
22
|
+
- which surviving blocks it is built from
|
|
23
|
+
- which CONVENTION it refuses to obey
|
|
24
|
+
- its single biggest point of failure
|
|
25
|
+
- any new building block, labelled as a new assumption (`UNKNOWN` in `assumptions.md`) - do not smuggle one in as a fact
|
|
26
|
+
|
|
27
|
+
If all three are the same system at different risk levels, you wrote the playbook. Start over.
|
|
24
28
|
|
|
25
29
|
**3. Structure each option identically.** Same dimensions, same format - so comparison is instant:
|
|
26
30
|
|
|
27
31
|
```markdown
|
|
28
|
-
### Option A: <name>
|
|
32
|
+
### Option A: <name>
|
|
33
|
+
- **Blocks:** <which surviving assumptions / parts it is built from>
|
|
34
|
+
- **Convention refused:** <the "we just…" it will not obey>
|
|
29
35
|
- **What:** <the approach in one paragraph>
|
|
30
36
|
- **Timeline:** <estimate with basis>
|
|
31
37
|
- **Cost:** <effort, infrastructure, external>
|
|
32
|
-
- **
|
|
38
|
+
- **Biggest failure:** <the single point that kills this option>
|
|
33
39
|
- **Trade-off:** <what you give up by choosing this>
|
|
34
40
|
- **Best when:** <the condition that makes this the right choice>
|
|
35
41
|
```
|
|
@@ -85,6 +91,7 @@ Recommendation: pragmatic, conditional - *if* Raj is on the design, otherwise sa
|
|
|
85
91
|
## Principles
|
|
86
92
|
|
|
87
93
|
- Three options, never one. One option is a request for trust; three is a real decision.
|
|
94
|
+
- Assemble from surviving blocks. Three speeds of the same plan is the playbook - start over.
|
|
88
95
|
- Each option must be genuinely defensible - no straw men.
|
|
89
96
|
- Same structure for each option. Comparison should take 30 seconds.
|
|
90
97
|
- Recommend one. State why. Accept the override gracefully.
|
package/templates/.fde/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Create with:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
|
|
8
|
+
fde resume --init <engagement-name> # or: npx fdeops resume --init <engagement-name>
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Stays on **your machine** - not in shared git by default.
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- Brief claims that are not yet evidence. Land seeds; test-assumptions / discover update. -->
|
|
4
4
|
|
|
5
|
-
| # | Assumption | Blast radius | How we test | Status | Evidence |
|
|
6
|
-
|
|
5
|
+
| # | Assumption | Kind | Blast radius | How we test | Status | Evidence |
|
|
6
|
+
|---|------------|------|--------------|-------------|--------|----------|
|
|
7
7
|
|
|
8
|
+
**Kind:** `FACT` · `CONVENTION` · `UNKNOWN`
|
|
8
9
|
**Blast radius:** `CRITICAL` · `LOAD-BEARING` · `CONVENIENCE`
|
|
9
10
|
**Status:** `OPEN` · `TESTING` · `CONFIRMED` · `DISPROVED` · `PARKED`
|
|
10
11
|
|