fdeops 3.15.1 → 3.15.2
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 +116 -78
- package/adapters/AGENTS.md +2 -2
- package/adapters/GEMINI.md +2 -2
- package/adapters/README.md +1 -1
- package/adapters/copilot-instructions.md +2 -2
- package/adapters/cursor.fde.mdc +3 -3
- package/bin/check.js +58 -6
- package/bin/fde.js +16 -16
- package/bin/lib/memory.js +1 -1
- package/bin/lib/render.js +1 -1
- package/bin/lib/trust.js +1 -1
- package/mcp/README.md +2 -2
- package/mcp/fdeops-ingest/README.md +8 -8
- package/mcp/fdeops-ingest/package.json +1 -1
- package/mcp/fdeops-ingest/server.js +1 -1
- package/mcp/recipes/README.md +2 -2
- package/mcp/recipes/file.md +1 -1
- package/mcp/recipes/granola.md +5 -5
- package/mcp/recipes/notion.md +2 -2
- package/mcp/recipes/slack.md +5 -5
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/fde/SKILL.md +36 -23
- package/skills/fde/references/ai.md +8 -8
- package/skills/fde/references/assumption-audit.md +2 -2
- package/skills/fde/references/blast-radius.md +1 -1
- package/skills/fde/references/business-case.md +3 -3
- package/skills/fde/references/close.md +5 -5
- package/skills/fde/references/debrief.md +2 -2
- package/skills/fde/references/discover.md +7 -7
- package/skills/fde/references/eval-pack.md +6 -6
- package/skills/fde/references/handoff-engineering.md +2 -2
- package/skills/fde/references/incremental-build.md +20 -11
- package/skills/fde/references/ingest-connect.md +6 -6
- package/skills/fde/references/ingest.md +14 -14
- package/skills/fde/references/initiative-triage.md +6 -6
- package/skills/fde/references/land.md +8 -8
- package/skills/fde/references/multi-customer-ops.md +1 -1
- package/skills/fde/references/options-analysis.md +2 -2
- package/skills/fde/references/plan.md +6 -6
- package/skills/fde/references/red-team.md +2 -2
- package/skills/fde/references/review.md +7 -7
- package/skills/fde/references/scope-defense.md +3 -3
- package/skills/fde/references/ship.md +16 -16
- package/skills/fde/references/sketch.md +3 -3
- package/skills/fde/references/stakeholder-radar.md +4 -4
- package/skills/fde/references/status.md +8 -8
- package/skills/fde/references/trust-engineering.md +1 -1
- package/skills/fde/references/use-case-scoring.md +1 -1
package/bin/fde.js
CHANGED
|
@@ -1134,7 +1134,7 @@ function cmdResume(args) {
|
|
|
1134
1134
|
const fdeDir = path.join(engRoot, '.fde')
|
|
1135
1135
|
const existed = fs.existsSync(fdeDir)
|
|
1136
1136
|
|
|
1137
|
-
// Optional stubs (AI eval pack, …) stay in templates/ for copy-on-use
|
|
1137
|
+
// Optional stubs (AI eval pack, …) stay in templates/ for copy-on-use - not day-1 scaffold.
|
|
1138
1138
|
const SKIP_INIT_TEMPLATES = new Set(['evals.md'])
|
|
1139
1139
|
const fillTemplates = (destFde) => {
|
|
1140
1140
|
for (const f of fs.readdirSync(tpl)) {
|
|
@@ -1381,7 +1381,7 @@ function setContextPhase(eng, phase) {
|
|
|
1381
1381
|
// as one dated debrief block. contact: lines may carry an inline [signal:x]
|
|
1382
1382
|
// token anywhere in the text - preserved verbatim so computeSignals can trust it.
|
|
1383
1383
|
// --smart: thin heuristic propose (existing prefixes + light keywords). Not a
|
|
1384
|
-
// brain
|
|
1384
|
+
// brain - the agent rewrites .debrief-propose with prefixes; --apply commits.
|
|
1385
1385
|
// --dry-run prints the routing without writing anything.
|
|
1386
1386
|
function inferContactSignal(text) {
|
|
1387
1387
|
const t = String(text)
|
|
@@ -1392,7 +1392,7 @@ function inferContactSignal(text) {
|
|
|
1392
1392
|
}
|
|
1393
1393
|
|
|
1394
1394
|
function looksLikePersonLine(text) {
|
|
1395
|
-
// "Denise …" / "Randy opened…"
|
|
1395
|
+
// "Denise …" / "Randy opened…" - capitalized subject + field verb.
|
|
1396
1396
|
return /^[A-Z][a-z]{1,20}\b/.test(text) &&
|
|
1397
1397
|
/\b(helping|quiet|skipped|said|will|opened|resistant|champion|warm|cold|unresponsive|demo|sheet|slack)\b/i.test(text)
|
|
1398
1398
|
}
|
|
@@ -1688,7 +1688,7 @@ function cmdDebrief(args) {
|
|
|
1688
1688
|
|
|
1689
1689
|
// Pull sink: stage raw artifacts outside the memory ledger, then reuse debrief
|
|
1690
1690
|
// propose/apply. Never writes .fde/ until the FDE confirms apply. Source SaaS
|
|
1691
|
-
// (Granola/Gmail/…) is not here
|
|
1691
|
+
// (Granola/Gmail/…) is not here - only staging + the existing confirm gate.
|
|
1692
1692
|
function inboxDir(eng) {
|
|
1693
1693
|
return path.join(path.dirname(eng), '.inbox')
|
|
1694
1694
|
}
|
|
@@ -1863,7 +1863,7 @@ function cmdIngest(args) {
|
|
|
1863
1863
|
console.log(`staged → ${dest}`)
|
|
1864
1864
|
console.log(`id: ${id}`)
|
|
1865
1865
|
console.log('next: fde ingest propose ' + id)
|
|
1866
|
-
console.log('(does not write .fde/
|
|
1866
|
+
console.log('(does not write .fde/ - confirm via propose → apply)')
|
|
1867
1867
|
}
|
|
1868
1868
|
|
|
1869
1869
|
function cmdReceipts(args) {
|
|
@@ -2157,7 +2157,7 @@ function collectDoctorIssues(eng) {
|
|
|
2157
2157
|
}
|
|
2158
2158
|
if (engagementTouchesAI(eng) && !hasEvalReceipt(eng)) {
|
|
2159
2159
|
issues.push(
|
|
2160
|
-
`phase is ${s.phase} with AI in scope but no eval receipt (evals.md Verdict or delivery Eval / Ship receipts)
|
|
2160
|
+
`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`
|
|
2161
2161
|
)
|
|
2162
2162
|
}
|
|
2163
2163
|
}
|
|
@@ -2302,7 +2302,7 @@ function hasValueBucket(eng) {
|
|
|
2302
2302
|
// "pending review", "TBD.", "n/a (blocked)" and "..." are all the same thing an
|
|
2303
2303
|
// FDE means by an empty cell - nagging about them teaches people to ignore doctor.
|
|
2304
2304
|
const PENDING_CELL_RE =
|
|
2305
|
-
/^(?:pending|tbd|to ?be ?(?:measured|confirmed|determined)|n\s*\/\s*a|na|none|unknown|not measured|\?+|\.{2,}
|
|
2305
|
+
/^(?:pending|tbd|to ?be ?(?:measured|confirmed|determined)|n\s*\/\s*a|na|none|unknown|not measured|\?+|\.{2,}|…|-+|-+|-+)(?:[^\w].*)?$/i
|
|
2306
2306
|
|
|
2307
2307
|
function parseValueLedger(eng) {
|
|
2308
2308
|
const ledger = stripTemplateNoise(sectionBody(readClean(eng, 'delivery.md'), 'Value ledger') || '')
|
|
@@ -2359,7 +2359,7 @@ function valueLedgerStatusLines(eng, opts = {}) {
|
|
|
2359
2359
|
return lines
|
|
2360
2360
|
}
|
|
2361
2361
|
|
|
2362
|
-
// AI in scope for ship/close hygiene
|
|
2362
|
+
// AI in scope for ship/close hygiene - delivery/decisions/trust evidence only.
|
|
2363
2363
|
// Do not scan terrain.md: its template headers mention LLM and would false-positive every ship.
|
|
2364
2364
|
function engagementTouchesAI(eng) {
|
|
2365
2365
|
const trust = readClean(eng, 'trust-profile.md')
|
|
@@ -2376,7 +2376,7 @@ function hasEvalReceipt(eng) {
|
|
|
2376
2376
|
const evalsPath = path.join(eng, 'evals.md')
|
|
2377
2377
|
if (fs.existsSync(evalsPath)) {
|
|
2378
2378
|
const e = stripTemplateNoise(readClean(eng, 'evals.md'))
|
|
2379
|
-
// Empty G1 stub + "Pass / fail" heading is not a receipt
|
|
2379
|
+
// Empty G1 stub + "Pass / fail" heading is not a receipt - need a real verdict/run/result.
|
|
2380
2380
|
if (/\*\*Verdict:\*\*\s*SHIP\b/i.test(e) || /(?:^|\n)\s*-\s*\*\*Verdict:\*\*\s*SHIP\b/i.test(e)) return true
|
|
2381
2381
|
if (/\bLast run:\s*\d{4}-\d{2}-\d{2}/i.test(e)) return true
|
|
2382
2382
|
if (/\|\s*G\d+\s*\|[^|\n]+\|[^|\n]+\|[^|\n]+\|[^|\n]+\|\s*pass\s*\|/i.test(e)) return true
|
|
@@ -2397,7 +2397,7 @@ function hygieneTriageLines(eng) {
|
|
|
2397
2397
|
if (!issues.length) return []
|
|
2398
2398
|
const top = issues[0].replace(/\s+/g, ' ').trim().slice(0, 72)
|
|
2399
2399
|
return [
|
|
2400
|
-
` hygiene: ${issues.length} issue(s)
|
|
2400
|
+
` hygiene: ${issues.length} issue(s) - ${top}${issues[0].length > 72 ? '…' : ''}`,
|
|
2401
2401
|
' → say "@fde clean up the fieldbook" when ready (agent runs fde doctor; nothing auto-rewrites)',
|
|
2402
2402
|
]
|
|
2403
2403
|
}
|
|
@@ -2494,7 +2494,7 @@ function cmdPrep(args) {
|
|
|
2494
2494
|
if (!eng) { console.error('no engagement - run: fde resume --init <name>'); process.exit(2) }
|
|
2495
2495
|
const label = args.join(' ').trim() || 'next meeting'
|
|
2496
2496
|
// Grounded brief: only text already in .fde/. No invention (Rowboat meeting-prep rule).
|
|
2497
|
-
console.log(`MEETING PREP
|
|
2497
|
+
console.log(`MEETING PREP - ${label}`)
|
|
2498
2498
|
console.log('(grounded in local .fde/ only - if a fact is missing, it is missing)\n')
|
|
2499
2499
|
console.log(resumeTriage(eng))
|
|
2500
2500
|
const owner = readOwner(eng)
|
|
@@ -2504,7 +2504,7 @@ function cmdPrep(args) {
|
|
|
2504
2504
|
const people = extractStakeholders(eng).slice(0, 8)
|
|
2505
2505
|
console.log('\nStakeholders (table + signal history)')
|
|
2506
2506
|
if (!people.length) console.log(' (none yet - log contacts with --signal)')
|
|
2507
|
-
else people.forEach(p => console.log(` [${p.signal}] ${p.name}${p.role ? `
|
|
2507
|
+
else people.forEach(p => console.log(` [${p.signal}] ${p.name}${p.role ? ` - ${p.role}` : ''}${p.note ? ` · ${p.note.slice(0, 60)}` : ''}`))
|
|
2508
2508
|
|
|
2509
2509
|
const risks = extractRisks(eng).slice(0, 5)
|
|
2510
2510
|
console.log('\nOpen risks (table + dated bullets)')
|
|
@@ -2537,16 +2537,16 @@ function cmdGarden(args) {
|
|
|
2537
2537
|
if (gitHealth.ok) {
|
|
2538
2538
|
console.log('TIDY (contract: no new facts · no deleted substance · reversible via memory git)')
|
|
2539
2539
|
} else if (gitHealth.reason === 'broken') {
|
|
2540
|
-
console.log('TIDY (contract: no new facts · no deleted substance · ⚠ memory git BROKEN
|
|
2540
|
+
console.log('TIDY (contract: no new facts · no deleted substance · ⚠ memory git BROKEN - NOT reversible until ledger is repaired)')
|
|
2541
2541
|
} else {
|
|
2542
|
-
console.log('TIDY (contract: no new facts · no deleted substance · ⚠ memory not git-versioned
|
|
2542
|
+
console.log('TIDY (contract: no new facts · no deleted substance · ⚠ memory not git-versioned - NOT reversible)')
|
|
2543
2543
|
}
|
|
2544
2544
|
console.log(resumeTriage(eng))
|
|
2545
2545
|
if (!gitHealth.ok) {
|
|
2546
2546
|
console.log(
|
|
2547
2547
|
gitHealth.reason === 'broken'
|
|
2548
2548
|
? '\n⚠ ledger is UNVERSIONED (corrupt .git). Repair before trusting tidy apply: mv .fde/.git .fde/.git.broken && run any fde write to re-init.'
|
|
2549
|
-
: '\n⚠ no memory git
|
|
2549
|
+
: '\n⚠ no memory git - tidy apply cannot create a reversible commit until the ledger exists.'
|
|
2550
2550
|
)
|
|
2551
2551
|
}
|
|
2552
2552
|
const proposals = []
|
|
@@ -2564,7 +2564,7 @@ function cmdGarden(args) {
|
|
|
2564
2564
|
proposals.push({
|
|
2565
2565
|
id: 'dedupe-risks',
|
|
2566
2566
|
kind: 'apply',
|
|
2567
|
-
text: `Consolidate ${dupes.length} duplicate open-risk cluster(s) (e.g. "${sample}${sample.length >= 50 ? '…' : ''}")
|
|
2567
|
+
text: `Consolidate ${dupes.length} duplicate open-risk cluster(s) (e.g. "${sample}${sample.length >= 50 ? '…' : ''}") - keep first, retire echoes`,
|
|
2568
2568
|
clusters: dupes,
|
|
2569
2569
|
})
|
|
2570
2570
|
}
|
package/bin/lib/memory.js
CHANGED
|
@@ -152,7 +152,7 @@ function createMemoryApi(deps) {
|
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
// True only when .git exists AND can resolve HEAD. A corrupt ledger (broken
|
|
155
|
-
// HEAD / missing objects) still has a .git directory
|
|
155
|
+
// HEAD / missing objects) still has a .git directory - existsSync alone lied.
|
|
156
156
|
function memoryGitHealthy(eng) {
|
|
157
157
|
if (!eng) return { ok: false, reason: 'missing' }
|
|
158
158
|
if (!fs.existsSync(path.join(eng, '.git'))) return { ok: false, reason: 'missing' }
|
package/bin/lib/render.js
CHANGED
|
@@ -438,7 +438,7 @@ function todayViewHtml({ today, total, attentionCount, highRiskTotal, todayQueue
|
|
|
438
438
|
<h1 class="fb-h1">Today</h1>
|
|
439
439
|
<div class="fb-meta-line">${escapeHtml(today)} · ${total} engagement${total === 1 ? '' : 's'} · ${attentionCount} need you · ${highRiskTotal} high risk${highRiskTotal === 1 ? '' : 's'} open</div>
|
|
440
440
|
<div class="fb-block">
|
|
441
|
-
<div class="fb-sec">Queue
|
|
441
|
+
<div class="fb-sec">Queue - next action per client, worst first</div>
|
|
442
442
|
${todayQueue}
|
|
443
443
|
</div>
|
|
444
444
|
${flagsHtml ? `<div class="fb-block">
|
package/bin/lib/trust.js
CHANGED
|
@@ -95,7 +95,7 @@ function createTrustApi(deps) {
|
|
|
95
95
|
|
|
96
96
|
function nextActionLine(ctx) {
|
|
97
97
|
// lastNonEmpty: template ships an empty ## Next action; agents often append a
|
|
98
|
-
// second heading with the real bullet
|
|
98
|
+
// second heading with the real bullet - first-match would report "(none set)".
|
|
99
99
|
const body = sectionBody(ctx, 'Next action', { lastNonEmpty: true })
|
|
100
100
|
for (const raw of body.split('\n')) {
|
|
101
101
|
const t = raw.trim().replace(/^[-*]\s+/, '')
|
package/mcp/README.md
CHANGED
|
@@ -9,7 +9,7 @@ FDEOps MCP servers follow a **pluggable source model**: core owns the **sink**,
|
|
|
9
9
|
| **Source** | FDE configures separately | Granola, Slack, Notion, Gmail, file |
|
|
10
10
|
| **Sink** | FDEOps (`fdeops-ingest`) | stage → propose → apply into engagement memory |
|
|
11
11
|
|
|
12
|
-
Source MCPs fetch raw text from SaaS APIs using credentials the FDE manages. The ingest MCP never stores OAuth tokens or calls external services
|
|
12
|
+
Source MCPs fetch raw text from SaaS APIs using credentials the FDE manages. The ingest MCP never stores OAuth tokens or calls external services - it only shells out to the local `fde` CLI.
|
|
13
13
|
|
|
14
14
|
## Ground loop
|
|
15
15
|
|
|
@@ -28,7 +28,7 @@ Source MCP(s) fdeops-ingest MCP fde CLI
|
|
|
28
28
|
1. Agent pulls from whichever source MCPs are configured.
|
|
29
29
|
2. Agent stages raw content via `ingest_stage` (with `source` provenance).
|
|
30
30
|
3. Agent proposes routing via `ingest_propose`; FDE confirms.
|
|
31
|
-
4. Agent applies via `ingest_apply`
|
|
31
|
+
4. Agent applies via `ingest_apply` - nothing writes `.fde/` unreviewed.
|
|
32
32
|
|
|
33
33
|
## Packages
|
|
34
34
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Thin stdio MCP server for the FDEOps **ingest sink** only: **stage → propose → apply**.
|
|
4
4
|
|
|
5
|
-
This package shells out to the local `fde` CLI. It never calls SaaS APIs. Source MCPs (Granola, Slack, Notion, etc.) are **separate**
|
|
5
|
+
This package shells out to the local `fde` CLI. It never calls SaaS APIs. Source MCPs (Granola, Slack, Notion, etc.) are **separate** - you add those in your own `mcp.json`.
|
|
6
6
|
|
|
7
|
-
**Prefer the CLI when this workspace is bound:** `fde ingest stage|list|propose|apply`. Use this MCP when the host did not start in a bound workspace
|
|
7
|
+
**Prefer the CLI when this workspace is bound:** `fde ingest stage|list|propose|apply`. Use this MCP when the host did not start in a bound workspace - then pass `engagement` (path to `.fde/` from `fde resume --bind`) on every tool call.
|
|
8
8
|
|
|
9
9
|
## Tools
|
|
10
10
|
|
|
@@ -70,12 +70,12 @@ Or after `npm link` in this directory:
|
|
|
70
70
|
|
|
71
71
|
## Daily loop (with separate source MCPs)
|
|
72
72
|
|
|
73
|
-
1. **Fetch**
|
|
74
|
-
2. **Stage**
|
|
75
|
-
3. **List**
|
|
76
|
-
4. **Propose**
|
|
77
|
-
5. **Confirm**
|
|
78
|
-
6. **Apply**
|
|
73
|
+
1. **Fetch** - Use your source MCP (e.g. Granola, Gmail) to pull raw text. FDEOps does not bundle these.
|
|
74
|
+
2. **Stage** - `ingest_stage` with `content`, `source` (e.g. `"granola"`), optional `title`.
|
|
75
|
+
3. **List** - `ingest_list` to see staged items in `.inbox/`.
|
|
76
|
+
4. **Propose** - `ingest_propose` with the staged `id`; agent reviews `.debrief-propose`.
|
|
77
|
+
5. **Confirm** - FDE approves the proposal in chat.
|
|
78
|
+
6. **Apply** - `ingest_apply` writes dated facts into `.fde/` with provenance.
|
|
79
79
|
|
|
80
80
|
Raw artifacts stay in `.inbox/`; the system of record (`.fde/`) stays thin and reviewed.
|
|
81
81
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* fdeops-ingest MCP
|
|
5
|
+
* fdeops-ingest MCP - thin stdio sink for FDEOps ingest.
|
|
6
6
|
* Shells out to local `fde` CLI only. Never calls SaaS.
|
|
7
7
|
* MCP stdio transport: newline-delimited JSON-RPC 2.0
|
|
8
8
|
* (Content-Length frames are accepted on input).
|
package/mcp/recipes/README.md
CHANGED
|
@@ -4,13 +4,13 @@ FDEOps does **not** bundle Granola / Slack / Notion OAuth and does **not** push
|
|
|
4
4
|
|
|
5
5
|
**Daily (no MCP):** paste notes to `@fde debrief`, or drop a file ([file.md](./file.md)).
|
|
6
6
|
|
|
7
|
-
**Pull (optional):** you add a **source** MCP. The agent fetches text, then runs `fde ingest` in this bound workspace (stage → propose → you confirm → apply). The `fdeops-ingest` MCP is optional
|
|
7
|
+
**Pull (optional):** you add a **source** MCP. The agent fetches text, then runs `fde ingest` in this bound workspace (stage → propose → you confirm → apply). The `fdeops-ingest` MCP is optional - only if you are not using the CLI from a bound workspace.
|
|
8
8
|
|
|
9
9
|
| Recipe | When |
|
|
10
10
|
|--------|------|
|
|
11
11
|
| [file.md](./file.md) | Transcript / export already on disk, or paste |
|
|
12
12
|
| [granola.md](./granola.md) | Meeting transcripts via a notes MCP (or export) |
|
|
13
|
-
| [slack.md](./slack.md) | Pull a thread/channel as text
|
|
13
|
+
| [slack.md](./slack.md) | Pull a thread/channel as text - never post |
|
|
14
14
|
| [notion.md](./notion.md) | Read a Notion page (or export markdown) |
|
|
15
15
|
|
|
16
16
|
**Natural language:** `@fde I want to connect Granola` (or Slack / Notion) → `skills/fde/references/ingest-connect.md`.
|
package/mcp/recipes/file.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Recipe: local file / paste (no source MCP)
|
|
2
2
|
|
|
3
|
-
**Use when:** you already have a transcript, `.eml`, or export on disk
|
|
3
|
+
**Use when:** you already have a transcript, `.eml`, or export on disk - or you paste into chat. This is the default FDE path.
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
package/mcp/recipes/granola.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Recipe: Granola-shaped meeting transcripts
|
|
2
2
|
|
|
3
|
-
**Use when:** meeting notes live in Granola (or a similar notes MCP). FDEOps does not ship a Granola server
|
|
3
|
+
**Use when:** meeting notes live in Granola (or a similar notes MCP). FDEOps does not ship a Granola server - you add whichever MCP/export path you trust.
|
|
4
4
|
|
|
5
5
|
Daily path if the notes are already in chat or on disk: paste to `@fde debrief` or [file.md](./file.md). This recipe is only for **pull**.
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ Daily path if the notes are already in chat or on disk: paste to `@fde debrief`
|
|
|
8
8
|
|
|
9
9
|
1. Enable a **Granola (or notes) MCP** in Cursor/Claude per that product’s docs.
|
|
10
10
|
2. Reload MCP / restart the host.
|
|
11
|
-
3. Test: `@fde what can you pull?`
|
|
11
|
+
3. Test: `@fde what can you pull?` - notes-source tools should appear.
|
|
12
12
|
|
|
13
13
|
The sink is **`fde ingest` in this bound workspace.** You do not need `fdeops-ingest` MCP for daily pull.
|
|
14
14
|
|
|
@@ -40,15 +40,15 @@ Replace command/args with whatever the real Granola MCP documents. FDEOps only n
|
|
|
40
40
|
|
|
41
41
|
## Agent steps
|
|
42
42
|
|
|
43
|
-
1. Capability check
|
|
43
|
+
1. Capability check - notes-source tools present?
|
|
44
44
|
2. Fetch transcript text (ask which meeting if ambiguous).
|
|
45
45
|
3. `fde ingest stage --source granola --title "<short>"` then propose → confirm → apply.
|
|
46
|
-
4. Extract decisions/risks/next
|
|
46
|
+
4. Extract decisions/risks/next - do not dump the raw transcript into `.fde/`.
|
|
47
47
|
|
|
48
48
|
## Common fails
|
|
49
49
|
|
|
50
50
|
| Symptom | Fix |
|
|
51
51
|
|---------|-----|
|
|
52
|
-
| No Granola tools | Source MCP not loaded
|
|
52
|
+
| No Granola tools | Source MCP not loaded - they save + reload |
|
|
53
53
|
| Wrong meeting | One clarifying question, then fetch |
|
|
54
54
|
| Wrong engagement | `fde resume` in this workspace before staging |
|
package/mcp/recipes/notion.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Recipe: Notion docs / meeting notes
|
|
2
2
|
|
|
3
|
-
**Use when:** useful engagement notes live in Notion. FDEOps does not ship a Notion server
|
|
3
|
+
**Use when:** useful engagement notes live in Notion. FDEOps does not ship a Notion server - use a Notion MCP (or export markdown). We do not write back to Notion.
|
|
4
4
|
|
|
5
5
|
## Setup (once)
|
|
6
6
|
|
|
@@ -36,7 +36,7 @@ The sink is **`fde ingest` in this bound workspace.** `fdeops-ingest` MCP is opt
|
|
|
36
36
|
|
|
37
37
|
## Agent steps
|
|
38
38
|
|
|
39
|
-
1. Capability check
|
|
39
|
+
1. Capability check - Notion read tools present?
|
|
40
40
|
2. Fetch page/block text (ask which page if ambiguous).
|
|
41
41
|
3. `fde ingest stage --source notion --title "<short>"` → propose → confirm → apply.
|
|
42
42
|
|
package/mcp/recipes/slack.md
CHANGED
|
@@ -8,7 +8,7 @@ You add whatever Slack MCP your host already supports. We only accept **text you
|
|
|
8
8
|
|
|
9
9
|
1. Enable a **Slack MCP** (official or community) with read access to the threads you need.
|
|
10
10
|
2. Reload MCP / restart the host.
|
|
11
|
-
3. Test: `@fde what can you pull?`
|
|
11
|
+
3. Test: `@fde what can you pull?` - Slack fetch tools should appear. The FDEOps **CLI** (`fde ingest`) is the sink if this workspace is bound; you do not need `fdeops-ingest` MCP for daily use.
|
|
12
12
|
|
|
13
13
|
### Example mcp.json shape (illustrative)
|
|
14
14
|
|
|
@@ -40,10 +40,10 @@ Optional sink MCP (only if you are not running `fde ingest` from this workspace)
|
|
|
40
40
|
|
|
41
41
|
## Agent steps
|
|
42
42
|
|
|
43
|
-
1. Capability check
|
|
43
|
+
1. Capability check - Slack **read** tools present? If not → this recipe, then stop.
|
|
44
44
|
2. Fetch the thread/channel as **text** (ask which channel/thread if ambiguous).
|
|
45
45
|
3. `fde ingest stage --source slack --title "<short>"` (CLI in this bound workspace).
|
|
46
|
-
4. Propose → FDE confirms → apply. Extract decisions/risks/asks
|
|
46
|
+
4. Propose → FDE confirms → apply. Extract decisions/risks/asks - do not dump the thread into `.fde/`.
|
|
47
47
|
|
|
48
48
|
## Never
|
|
49
49
|
|
|
@@ -55,7 +55,7 @@ Optional sink MCP (only if you are not running `fde ingest` from this workspace)
|
|
|
55
55
|
|
|
56
56
|
| Symptom | Fix |
|
|
57
57
|
|---------|-----|
|
|
58
|
-
| No Slack tools | Source MCP not loaded
|
|
59
|
-
| Missing channel | Token/scopes cannot read that workspace
|
|
58
|
+
| No Slack tools | Source MCP not loaded - they save + reload; we cannot silent-install |
|
|
59
|
+
| Missing channel | Token/scopes cannot read that workspace - their Slack admin, not FDEOps |
|
|
60
60
|
| Huge dump | Stage full text in `.inbox/`; apply only short dated facts |
|
|
61
61
|
| Wrong client | Bind this workspace (`fde resume`) before staging |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fdeops",
|
|
3
|
-
"version": "3.15.
|
|
3
|
+
"version": "3.15.2",
|
|
4
4
|
"description": "Forward deployed engineering skills for AI coding agents. Your agent forgets the client every morning - the sponsor, the promise, who signed off. FDEOps keeps that as dated markdown on your laptop: one @fde skill, a deterministic local CLI, and hooks that make it automatic. Claude Code plugin and any agent that loads skills.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fdeops": "bin/install.js",
|
package/plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "fdeops",
|
|
4
|
-
"version": "3.15.
|
|
4
|
+
"version": "3.15.2",
|
|
5
5
|
"description": "Forward deployed engineering skills for AI coding agents: per-client memory in local .fde/ files, one @fde skill. Local-only, no network.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Subash Natarajan",
|
package/skills/fde/SKILL.md
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fde
|
|
3
|
-
description: Keeps engagement
|
|
3
|
+
description: Keeps the engagement record for client work. Use when they name a client or stakeholder. Use when they debrief a meeting or paste notes. Use when they ask what was agreed. Use when they run a POC, slice a feature on the client's codebase, prove it on their staging, or need evals before a model acts. Use when they prep a readout, when trust shifts, or they say @fde. Route and run the local fde CLI (or npx --yes fdeops). Never ask them to type commands. Not for ordinary code edits in an unbound repo.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# @fde
|
|
7
7
|
|
|
8
8
|
## Purpose
|
|
9
9
|
|
|
10
|
-
The **engagement record** for one client. One skill; six stages (land → close). You
|
|
10
|
+
The **engagement record** for one client, from first meeting to signed outcome. One skill; six stages (land → close). You route; they never pick a skill. Confirm, then write `.fde/`. The workspace still compiles and commits. `@fde` does not leave.
|
|
11
11
|
|
|
12
12
|
## When to use
|
|
13
13
|
|
|
14
14
|
- They named a client, pasted notes, or asked what was agreed
|
|
15
15
|
- The brief feels wrong, a sponsor went quiet, or Friday needs the ledger
|
|
16
|
-
- Unbound
|
|
16
|
+
- Unbound - ask the name once, then **you** run `fde resume --init`
|
|
17
17
|
|
|
18
18
|
## When NOT to use
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
A one-line typo or compile error in a file that will not ship. On a bound client: stay here for POC, slice, characterisation, eval, go-live, rollback, and acceptance.
|
|
21
21
|
|
|
22
22
|
## Use these first
|
|
23
23
|
|
|
@@ -25,14 +25,26 @@ TypeScript errors, unit tests, refactors, git commits, generic debug: **host age
|
|
|
25
25
|
|---------|-----------------|---------|-----------|
|
|
26
26
|
| **The brief is wrong** | "If this works, who in their company would have to agree that it worked?" | `fde resume` then discover | `references/discover.md` |
|
|
27
27
|
| **They went quiet** | "Is this a process gap, or a trust problem?" | `fde log contact "…" --signal amber\|red\|green` | `references/rescue.md` |
|
|
28
|
-
| **When did we agree?** | Don't argue from memory. Search the record. | `fde receipts <term>` |
|
|
29
|
-
| **What
|
|
28
|
+
| **When did we agree?** | Don't argue from memory. Search the record. | `fde receipts <term>` | - |
|
|
29
|
+
| **What's the outcome?** | A number nobody signed is claimed, not delivered. | `fde status` | `references/status.md` |
|
|
30
30
|
|
|
31
31
|
After a meeting: `fde debrief --smart` → confirm → `--apply`. Walk-in: `fde prep`. Friday: `fde status`.
|
|
32
32
|
|
|
33
|
+
## Ground loop
|
|
34
|
+
|
|
35
|
+
On someone else's site the work is not "write code, remember later." Every slice stays on `@fde`:
|
|
36
|
+
|
|
37
|
+
1. **Name it** in `decisions.md` (plan) or kill it in a day (sketch).
|
|
38
|
+
2. **Characterise their code** before you change it (incremental-build). Their tests, their runner.
|
|
39
|
+
3. **Prove it where they live.** Staging they operate, a screen the signer in `success.md` can reject.
|
|
40
|
+
4. **If a model judges:** `evals.md` Verdict SHIP before the slice is done (eval-pack).
|
|
41
|
+
5. **Log delivery.** Outcome is promised → measured → accepted, not a green CI.
|
|
42
|
+
|
|
43
|
+
A throwaway file can skip the loop. A client slice cannot.
|
|
44
|
+
|
|
33
45
|
## Human surface vs agent plumbing
|
|
34
46
|
|
|
35
|
-
**FDE (human):** `@fde` + English, or `/brief` `/discover` `/plan` `/ship` `/
|
|
47
|
+
**FDE (human):** `@fde` + English, or `/brief` `/discover` `/plan` `/ship` `/outcome` `/close` `/debrief` `/prep` `/quiet` `/agreed` `/status`. Never a skill catalog.
|
|
36
48
|
|
|
37
49
|
**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.
|
|
38
50
|
|
|
@@ -42,7 +54,7 @@ Fallbacks: `node ~/.claude/fdeops/fde.js …`, then `npx --yes fdeops …`. Skil
|
|
|
42
54
|
|
|
43
55
|
1. `fde resume` (bounded `context.md`). `--full` only if you need the whole log.
|
|
44
56
|
2. **NO ENGAGEMENT:** ask "What should we call this client?" then **you** init. Pasted notes → debrief after bind.
|
|
45
|
-
3. Playback 2
|
|
57
|
+
3. Playback 2-3 lines. `hygiene:` → offer `fde doctor`; **never auto-rewrite**.
|
|
46
58
|
4. Route. Read **one** `references/*.md`. Confirm, then write.
|
|
47
59
|
|
|
48
60
|
Writes need a bind (`FDEOPS_ENGAGEMENT` or registry). Never install fdeops on infrastructure they do not control.
|
|
@@ -54,10 +66,10 @@ Writes need a bind (`FDEOPS_ENGAGEMENT` or registry). Never install fdeops on in
|
|
|
54
66
|
| debrief / pasted notes | `fde debrief --smart` → you rewrite prefixes → confirm → `--apply`. `--smart` is a gate, not a brain. `references/debrief.md` |
|
|
55
67
|
| prep me for … | `fde prep "<label>"` |
|
|
56
68
|
| when did we agree | `fde receipts <term>` |
|
|
57
|
-
| sponsor update /
|
|
69
|
+
| sponsor update / the outcome | `fde status` |
|
|
58
70
|
| they went quiet | `fde log contact "…" --signal amber\|green\|red` |
|
|
59
71
|
| fieldbook page | `fde dashboard` |
|
|
60
|
-
| clean up the fieldbook | `fde doctor`
|
|
72
|
+
| clean up the fieldbook | `fde doctor` - never auto-rewrite |
|
|
61
73
|
| scrub a secret | `fde redact <term>` then `--apply` after confirm |
|
|
62
74
|
| pull Granola/Slack/transcript | capability check → `fde ingest stage` → confirm → apply. Never auto-apply. `references/ingest.md` |
|
|
63
75
|
| connect an MCP | `references/ingest-connect.md` |
|
|
@@ -69,35 +81,35 @@ Writes need a bind (`FDEOPS_ENGAGEMENT` or registry). Never install fdeops on in
|
|
|
69
81
|
2. **Deliverable = memory.** The work *is* the `.fde/` file. The reference names which one.
|
|
70
82
|
3. **Evidence.** Every claim has a source. Traceable beats plausible.
|
|
71
83
|
4. **No invented facts.** People, quotes, meetings, numbers: they said it or the repo shows it. Else `unknown - ask: <question>`.
|
|
72
|
-
5. **Session digest** (end of session and before a PR)
|
|
84
|
+
5. **Session digest** (end of session and before a PR) - thinking, not the chat. Confirm, then write. Never a transcript dump.
|
|
73
85
|
|
|
74
86
|
| Digest beat | Lands in |
|
|
75
87
|
|-------------|----------|
|
|
76
88
|
| **TL;DR** | `context.md` |
|
|
77
|
-
| **Key decisions & why** | `decisions.md`
|
|
89
|
+
| **Key decisions & why** | `decisions.md` - skip if none |
|
|
78
90
|
| **Pivot / aha** | `context.md` or `decisions.md` |
|
|
79
91
|
| **Scope + verification** | `delivery.md` if code/PR; else skip |
|
|
80
92
|
| **Gotchas** | `context.md` |
|
|
81
|
-
| **Next action** | existing `## Next action`
|
|
93
|
+
| **Next action** | existing `## Next action` - **replace**; never append a second heading |
|
|
82
94
|
|
|
83
95
|
Judgment ships in the fieldbook. Raw transcripts stay on the machine. The `session-stop` hook is a thin backstop; **you** write the digest.
|
|
84
96
|
|
|
85
97
|
6. **One customer, one folder.**
|
|
86
98
|
7. Never drop `## Signal history` or `## Retired` when rewriting those files.
|
|
87
99
|
|
|
88
|
-
**Don't invent.** Don't tell them to run the CLI. Don't fill `success.md` / `terrain.md` with guesses. Don't ship on "probably fine"
|
|
100
|
+
**Don't invent.** Don't tell them to run the CLI. Don't fill `success.md` / `terrain.md` with guesses. Don't ship on "probably fine" - intent vs diff, then pre-blast. Don't grill mid-flow. Don't sync transcripts into git.
|
|
89
101
|
|
|
90
102
|
## Data boundary
|
|
91
103
|
|
|
92
|
-
CLI is local (`git` + files, no network). You see their code only when they point you at it. AI policy unknown → ask before loading code. `<private>` is redacted from CLI/dashboard/hooks
|
|
104
|
+
CLI is local (`git` + files, no network). You see their code only when they point you at it. AI policy unknown → ask before loading code. `<private>` is redacted from CLI/dashboard/hooks - do not open raw private blocks with file tools.
|
|
93
105
|
|
|
94
106
|
## Voice
|
|
95
107
|
|
|
96
|
-
Direct. Their words. No "Certainly." Playback 2
|
|
108
|
+
Direct. Their words. No "Certainly." Playback 2-4 lines, then act. One question only when a missing fact changes the next move.
|
|
97
109
|
|
|
98
110
|
New embed: sprint / standard / programme changes depth, not which methods exist. Before first code: safe place to break things, plus AI-code policy. Before go-live: who needs to know, what's the rollback. Before a sponsor artifact: as-is or gut-check first.
|
|
99
111
|
|
|
100
|
-
Muddy signal: name it ("discover or rescue
|
|
112
|
+
Muddy signal: name it ("discover or rescue - leaning X"). Never a phase-picker interview. Default: land if new, audit if takeover.
|
|
101
113
|
|
|
102
114
|
## Routing - 6 stages
|
|
103
115
|
|
|
@@ -120,7 +132,7 @@ Read **one** reference and follow it. Do not improvise from memory.
|
|
|
120
132
|
| Don't know the real problem, brief feels wrong, shadow processes | discover | `references/discover.md` |
|
|
121
133
|
| The brief feels too neat, assumptions untested, "we just need…" | assumption-audit | `references/assumption-audit.md` |
|
|
122
134
|
| Multiple use cases competing, "we want to do everything" | use-case-scoring | `references/use-case-scoring.md` |
|
|
123
|
-
| Need to validate a direction, prototype, demo to de-risk | sketch | `references/sketch.md` |
|
|
135
|
+
| Need to validate a direction, prototype, demo to de-risk, **POC**, spike, killer assumption | sketch | `references/sketch.md` |
|
|
124
136
|
|
|
125
137
|
### Plan
|
|
126
138
|
|
|
@@ -135,13 +147,13 @@ Read **one** reference and follow it. Do not improvise from memory.
|
|
|
135
147
|
|
|
136
148
|
| You hear | Skill | Reference |
|
|
137
149
|
|----------|-------|-----------|
|
|
138
|
-
| Large feature, need visible progress every 2
|
|
150
|
+
| Large feature, need visible progress every 2-3 days, slice it, characterise their tests, POC follow-through | incremental-build | `references/incremental-build.md` |
|
|
139
151
|
| What could go wrong, touching shared infrastructure, need to assess impact | blast-radius | `references/blast-radius.md` |
|
|
140
152
|
| Production down, urgent - OR stakeholder gone quiet, trust slipping | rescue | `references/rescue.md` |
|
|
141
153
|
| Ready to deploy, going live, pre-flight check | ship | `references/ship.md` |
|
|
142
154
|
| Review this change, is it safe, does it match what we agreed | review | `references/review.md` |
|
|
143
155
|
| 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 |
|
|
144
|
-
| Wrap the session / share the thinking / catch teammates up / before I open the PR | (memory contract
|
|
156
|
+
| Wrap the session / share the thinking / catch teammates up / before I open the PR | (memory contract - session digest) | SKILL.md **On exit** - write TL;DR + decisions/why into `.fde/`; no transcript sync |
|
|
145
157
|
| "We can always revert" - need to actually test the escape route | rollback-drill | `references/rollback-drill.md` |
|
|
146
158
|
|
|
147
159
|
### Prove
|
|
@@ -173,7 +185,7 @@ Read **one** reference and follow it. Do not improvise from memory.
|
|
|
173
185
|
| Signal | Overlay |
|
|
174
186
|
|--------|---------|
|
|
175
187
|
| AI, ML, LLM, model, embeddings, RAG, agents, fine-tuning, inference, drift | `references/ai.md` |
|
|
176
|
-
| Golden set, eval suite, eval pack, pass/fail before AI ship, HITL gate for model | `references/eval-pack.md` (+ `ai.md`) |
|
|
188
|
+
| Golden set, eval suite, eval pack, pass/fail before AI ship, HITL gate for model, POC the model | `references/eval-pack.md` (+ `ai.md`) |
|
|
177
189
|
| Deck, slides, report, governance framework, compliance pack, ADR, PDF | `references/artifacts.md` |
|
|
178
190
|
| Patient data, PHI, HIPAA, EHR, clinical | `references/healthcare.md` |
|
|
179
191
|
| Payments, cardholder data, PCI-DSS, anything that moves money | `references/fintech.md` |
|
|
@@ -184,8 +196,9 @@ Ready to build with no `terrain.md` / plan: discover or plan first. Takeover wit
|
|
|
184
196
|
## Principles
|
|
185
197
|
|
|
186
198
|
- Never ask the FDE to pick a phase. That's your job.
|
|
187
|
-
-
|
|
188
|
-
-
|
|
199
|
+
- Ground loop on a bound client: name → characterise → prove where they live → log. Do not hand the slice to a generic coding pack.
|
|
200
|
+
- Read `context.md` before speaking. One sharp question - never a barrage.
|
|
201
|
+
- Never invent people, meetings, or numbers - `unknown - ask:` beats a polished lie.
|
|
189
202
|
- Every phase ends with its artifact written. No artifact, no "done."
|
|
190
203
|
- Evidence on every claim. The FDE will be challenged on these files.
|
|
191
204
|
- Overlays activate on signal, not on request.
|
|
@@ -29,7 +29,7 @@ Never start with the most powerful model. Start with the cheapest that meets the
|
|
|
29
29
|
4. **Does it need fine-tuning?** Only when: you have 500+ high-quality examples, the base model fails consistently on your domain, and the cost of inference at scale justifies the training cost.
|
|
30
30
|
|
|
31
31
|
**Evaluation method (before choosing):**
|
|
32
|
-
- Build a test set: 50
|
|
32
|
+
- Build a test set: 50-100 representative inputs with expected outputs.
|
|
33
33
|
- Run every candidate model against the test set.
|
|
34
34
|
- Score: accuracy, latency, cost per call, failure modes.
|
|
35
35
|
- The cheapest model that scores above the quality threshold wins.
|
|
@@ -38,14 +38,14 @@ Write model selection rationale to `decisions.md`. Include: models tested, test
|
|
|
38
38
|
|
|
39
39
|
## Engagement eval pack (before AI ships)
|
|
40
40
|
|
|
41
|
-
When any slice touches a model, embeddings, RAG, or an agent: create or update `.fde/evals.md` **before** ship. Full method: `references/eval-pack.md`. This is the engagement-local test set
|
|
41
|
+
When any slice touches a model, embeddings, RAG, or an agent: create or update `.fde/evals.md` **before** ship. Full method: `references/eval-pack.md`. This is the engagement-local test set - not unit tests.
|
|
42
42
|
|
|
43
43
|
**Minimum pack (do not grow until the minimum exists):**
|
|
44
|
-
1. **Component + quality bar**
|
|
45
|
-
2. **Golden cases**
|
|
46
|
-
3. **Failure modes**
|
|
47
|
-
4. **Pass/fail**
|
|
48
|
-
5. **HITL gate**
|
|
44
|
+
1. **Component + quality bar** - one sentence each; kill switch / fallback named.
|
|
45
|
+
2. **Golden cases** - 5-20 representative inputs with expected outputs and a pass rule. Prefer real production-shaped data (sanitized).
|
|
46
|
+
3. **Failure modes** - at least the silent ones: hallucination/ungrounded, retrieval miss (if RAG), drift, cost runaway.
|
|
47
|
+
4. **Pass/fail** - dated run; Verdict **SHIP** or **NO-SHIP**; critical fails must be 0.
|
|
48
|
+
5. **HITL gate** - which decisions need human review before action (align with `trust-profile.md`). Empty when policy requires review → NO-SHIP.
|
|
49
49
|
|
|
50
50
|
**When to write:** plan seeds the pack; sketch/build grows goldens; ship requires Verdict SHIP and a receipt in `delivery.md` → `## Ship receipts`. Non-AI work skips this file entirely.
|
|
51
51
|
|
|
@@ -59,7 +59,7 @@ When the AI needs to answer questions about the client's data:
|
|
|
59
59
|
3. **Generate** - chunks + query → LLM → answer with citations
|
|
60
60
|
|
|
61
61
|
**Common failure modes:**
|
|
62
|
-
- **Chunk size wrong.** Too small = lost context. Too large = noise drowns signal. Start at 500
|
|
62
|
+
- **Chunk size wrong.** Too small = lost context. Too large = noise drowns signal. Start at 500-1000 tokens with 100-token overlap.
|
|
63
63
|
- **No citation/grounding.** If the model can't point to where it found the answer, you can't verify it. Always require source attribution.
|
|
64
64
|
- **Stale index.** Documents update, embeddings don't. Define the refresh cadence. Real-time for critical data, daily for reference docs.
|
|
65
65
|
- **Retrieval miss.** The right document exists but wasn't retrieved. Test with known-answer queries where the answer IS in the corpus - if retrieval misses these, the embedding model or chunking strategy needs work.
|
|
@@ -76,11 +76,11 @@ Tell the FDE: how many assumptions extracted, how many critical, which ones were
|
|
|
76
76
|
|
|
77
77
|
Acme's brief reads cleanly, which is the signal.
|
|
78
78
|
|
|
79
|
-
Extracted assumptions include one nobody said aloud: *finance would act on an alert*. The whole plan rests on it, and the evidence behind it is a sentence in a kickoff. Blast radius CRITICAL
|
|
79
|
+
Extracted assumptions include one nobody said aloud: *finance would act on an alert*. The whole plan rests on it, and the evidence behind it is a sentence in a kickoff. Blast radius CRITICAL - if false, alerting changes nothing and the engagement delivers a page nobody answers.
|
|
80
80
|
|
|
81
81
|
Validation is a test, not a discussion, and it is cheap: send one real failure notification to the finance channel and watch what happens. It goes first because highest blast radius × cheapest test is the killer test.
|
|
82
82
|
|
|
83
|
-
Result: acked in 40 minutes, by Marco, not finance. Assumption DISPROVED, and the plan changes before six weeks are spent on it
|
|
83
|
+
Result: acked in 40 minutes, by Marco, not finance. Assumption DISPROVED, and the plan changes before six weeks are spent on it - the alert needs a rota with an owner, which is a different piece of work than the one that was funded. `assumptions.md` records the status, the evidence, and the date; the finding is presented to the FDE as a fact base, not as "the brief was wrong".
|
|
84
84
|
|
|
85
85
|
## Principles
|
|
86
86
|
|
|
@@ -26,7 +26,7 @@ CONTAINED → Only the module you're changing is affected
|
|
|
26
26
|
Rollback: revert the PR
|
|
27
27
|
Example: changing a utility function with no external callers
|
|
28
28
|
|
|
29
|
-
ADJACENT → 2
|
|
29
|
+
ADJACENT → 2-5 callers or one downstream system affected
|
|
30
30
|
Rollback: revert the PR + verify downstream
|
|
31
31
|
Example: changing an API response format used by the frontend
|
|
32
32
|
|