fdeops 3.16.0 → 3.20.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/README.md +82 -84
- package/adapters/AGENTS.md +1 -1
- package/adapters/GEMINI.md +1 -1
- package/adapters/LOCAL-LLM.md +5 -5
- package/adapters/README.md +2 -2
- package/adapters/copilot-instructions.md +1 -1
- package/adapters/cursor.fde.mdc +1 -1
- package/bin/check.js +42 -11
- package/bin/fde.js +157 -20
- package/bin/lib/render.js +2 -2
- package/bin/lib/trust.js +42 -5
- package/mcp/fdeops-ingest/package.json +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/skills/fde/SKILL.md +43 -42
- package/skills/fde/references/ai.md +2 -2
- package/skills/fde/references/audit.md +2 -2
- package/skills/fde/references/board-memo.md +1 -1
- package/skills/fde/references/business-case.md +1 -1
- package/skills/fde/references/close.md +1 -1
- package/skills/fde/references/connect.md +1 -1
- package/skills/fde/references/dashboard.md +2 -2
- package/skills/fde/references/debrief.md +1 -1
- package/skills/fde/references/demo-prep.md +1 -1
- package/skills/fde/references/discover.md +34 -15
- package/skills/fde/references/earn-trust.md +1 -1
- package/skills/fde/references/encode-pattern.md +3 -3
- package/skills/fde/references/eval-pack.md +2 -2
- package/skills/fde/references/hold-scope.md +1 -1
- package/skills/fde/references/ingest.md +1 -1
- package/skills/fde/references/land.md +1 -1
- package/skills/fde/references/pick-three.md +1 -1
- package/skills/fde/references/plan.md +10 -7
- package/skills/fde/references/poc.md +4 -2
- package/skills/fde/references/readout.md +1 -1
- package/skills/fde/references/red-team.md +1 -1
- package/skills/fde/references/rescue.md +3 -2
- package/skills/fde/references/review.md +1 -1
- package/skills/fde/references/rollback.md +1 -1
- package/skills/fde/references/runbook.md +1 -1
- package/skills/fde/references/score-use-cases.md +1 -1
- package/skills/fde/references/ship.md +124 -17
- package/skills/fde/references/switch-clients.md +1 -1
- package/skills/fde/references/test-assumptions.md +2 -2
- package/skills/fde/references/three-options.md +1 -1
- package/skills/fde/references/what-breaks.md +1 -1
- package/skills/fde/references/who-decides.md +1 -1
- package/templates/.fde/assumptions.md +1 -1
- package/templates/.fde/reality.md +6 -1
- package/templates/.fde/terrain.md +1 -1
- package/skills/fde/references/thin-slices.md +0 -100
package/bin/fde.js
CHANGED
|
@@ -408,6 +408,8 @@ const SECRET_PATTERNS = [
|
|
|
408
408
|
{ name: 'Slack token', re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/ },
|
|
409
409
|
{ name: 'PEM private key', re: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/ },
|
|
410
410
|
{ name: 'Bearer token', re: /\bBearer\s+[A-Za-z0-9._\-]{20,}\b/ },
|
|
411
|
+
{ name: 'database URL', re: /\b[a-z][a-z0-9+.-]*:\/\/[^/\s:]+:[^/\s@]+@/i },
|
|
412
|
+
{ name: 'api key assignment', re: /\b(?:api[_-]?key|secret|password)\s*=\s*\S{8,}/i },
|
|
411
413
|
]
|
|
412
414
|
|
|
413
415
|
function findSecretHit(text) {
|
|
@@ -738,11 +740,11 @@ function appendLogEntry(eng, type, entry, opts = {}) {
|
|
|
738
740
|
// degrading to "nothing found" rather than guessing when the shape does not
|
|
739
741
|
// match. Never fabricate a number, a name, or a signal that is not in the text.
|
|
740
742
|
|
|
741
|
-
const PHASES = ['land', 'discover', 'plan', 'ship', '
|
|
742
|
-
const PHASE_ALIASES = { build: 'ship' } // legacy
|
|
743
|
+
const PHASES = ['land', 'discover', 'plan', 'ship', 'outcome', 'close']
|
|
744
|
+
const PHASE_ALIASES = { build: 'ship', prove: 'outcome' } // legacy names; public map is ship / outcome
|
|
743
745
|
const PHASE_LABELS = {
|
|
744
746
|
land: 'Land', discover: 'Discover', plan: 'Plan',
|
|
745
|
-
ship: 'Ship',
|
|
747
|
+
ship: 'Ship', outcome: 'Outcome', close: 'Close',
|
|
746
748
|
}
|
|
747
749
|
function canonicalPhase(phase) {
|
|
748
750
|
const p = String(phase).toLowerCase()
|
|
@@ -769,6 +771,89 @@ function firstLine(md, maxLen) {
|
|
|
769
771
|
return ''
|
|
770
772
|
}
|
|
771
773
|
|
|
774
|
+
// reality.md is the one panel whose job is "not the brief". If the file does
|
|
775
|
+
// not carry Working theory / Evidence / Differs from brief, do not scrape a
|
|
776
|
+
// first line that might be the inherited brief and label it truth.
|
|
777
|
+
function parseReality(md, maxLen) {
|
|
778
|
+
const theory = (md.match(/\*\*Working theory:\*\*\s*(.*)/i) || [])[1]
|
|
779
|
+
const hasSchema = /\*\*(Working theory|Evidence|Differs from brief how):\*\*/i.test(md)
|
|
780
|
+
const theoryText = (theory || '').trim()
|
|
781
|
+
if (theoryText) {
|
|
782
|
+
const line = theoryText.length > maxLen ? theoryText.slice(0, maxLen - 1).trim() + '…' : theoryText
|
|
783
|
+
return { line, missing: '' }
|
|
784
|
+
}
|
|
785
|
+
if (hasSchema) return { line: '', missing: '' }
|
|
786
|
+
const prose = firstLine(md, maxLen)
|
|
787
|
+
if (prose) {
|
|
788
|
+
return {
|
|
789
|
+
line: '',
|
|
790
|
+
missing: 'UNREADABLE - reality.md does not match the schema (Working theory / Evidence / Differs from brief). Not showing the brief as truth.',
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return { line: '', missing: '' }
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function appendValueLedgerRow(eng, cells) {
|
|
797
|
+
ensureMemoryGit(eng)
|
|
798
|
+
const p = path.join(eng, 'delivery.md')
|
|
799
|
+
let md = readEng(eng, 'delivery.md')
|
|
800
|
+
if (!md) md = '# Delivery log\n\n## Value ledger\n\n'
|
|
801
|
+
const date = new Date().toISOString().slice(0, 10)
|
|
802
|
+
const cols = []
|
|
803
|
+
for (let i = 0; i < 7; i++) cols.push((cells[i] || '').replace(/\|/g, '\\|').trim() || ' ')
|
|
804
|
+
const row = `| ${date} | ${cols.join(' | ')} |`
|
|
805
|
+
const lines = md.split('\n')
|
|
806
|
+
let inLedger = false
|
|
807
|
+
let lastTableLine = -1
|
|
808
|
+
for (let i = 0; i < lines.length; i++) {
|
|
809
|
+
if (/^##\s+Value ledger\b/i.test(lines[i])) { inLedger = true; continue }
|
|
810
|
+
if (inLedger && /^##\s+/.test(lines[i])) break
|
|
811
|
+
if (inLedger && /^\|/.test(lines[i].trim())) lastTableLine = i
|
|
812
|
+
}
|
|
813
|
+
if (lastTableLine === -1) md = appendUnderSection(md, 'Value ledger', row)
|
|
814
|
+
else {
|
|
815
|
+
lines.splice(lastTableLine + 1, 0, row)
|
|
816
|
+
md = lines.join('\n')
|
|
817
|
+
}
|
|
818
|
+
withFileLock(p, () => { atomicWriteFile(p, md.endsWith('\n') ? md : md + '\n') })
|
|
819
|
+
recordLastWrite(eng, 'delivery.md', row)
|
|
820
|
+
commitMemory(eng, 'log delivery', { files: ['delivery.md'] })
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
function retireOpenRisks(eng, needle) {
|
|
824
|
+
const n = String(needle || '').toLowerCase()
|
|
825
|
+
if (!n) return 0
|
|
826
|
+
const p = path.join(eng, 'risks.md')
|
|
827
|
+
const md = readEng(eng, 'risks.md')
|
|
828
|
+
if (!md) return 0
|
|
829
|
+
const retired = []
|
|
830
|
+
const kept = []
|
|
831
|
+
let inRetired = false
|
|
832
|
+
for (const raw of md.split('\n')) {
|
|
833
|
+
const t = raw.trim()
|
|
834
|
+
if (/^#{1,6}\s+Retired\b/i.test(t)) { inRetired = true; kept.push(raw); continue }
|
|
835
|
+
if (!inRetired) {
|
|
836
|
+
const m = t.match(/^-\s*\[\d{4}-\d{2}-\d{2}\]\s*(?:\[@[^\]]+\]\s*)?(.*)$/)
|
|
837
|
+
if (m && m[1].toLowerCase().includes(n)) {
|
|
838
|
+
retired.push(raw)
|
|
839
|
+
continue
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
kept.push(raw)
|
|
843
|
+
}
|
|
844
|
+
if (!retired.length) return 0
|
|
845
|
+
let out = kept.join('\n')
|
|
846
|
+
if (!/^#{1,6}\s+Retired\b/im.test(out)) out = out.replace(/\n*$/, '\n\n## Retired\n')
|
|
847
|
+
const stamp = new Date().toISOString().slice(0, 10)
|
|
848
|
+
const block = retired.map(l => {
|
|
849
|
+
const body = l.trim().replace(/^-\s*/, '')
|
|
850
|
+
return `- [${stamp}] (retired) ${body}`
|
|
851
|
+
}).join('\n')
|
|
852
|
+
out = appendUnderSection(out, 'Retired', block)
|
|
853
|
+
withFileLock(p, () => { atomicWriteFile(p, out.endsWith('\n') ? out : out + '\n') })
|
|
854
|
+
return retired.length
|
|
855
|
+
}
|
|
856
|
+
|
|
772
857
|
// Engagement age from the .fde/ directory's own birth time - hidden (not
|
|
773
858
|
// fabricated as 0) on filesystems that do not report birthtime.
|
|
774
859
|
function daysElapsed(eng) {
|
|
@@ -817,6 +902,7 @@ function parseMdTable(md) {
|
|
|
817
902
|
function colIndex(headers, rx) { return headers.findIndex(h => rx.test(h)) }
|
|
818
903
|
|
|
819
904
|
const {
|
|
905
|
+
personFromSignalText,
|
|
820
906
|
signalSubjectKey,
|
|
821
907
|
nextActionLine,
|
|
822
908
|
computeSignals,
|
|
@@ -856,6 +942,8 @@ function parseSignalHistoryEntries(eng) {
|
|
|
856
942
|
}
|
|
857
943
|
|
|
858
944
|
function displayNameFromSignalText(text) {
|
|
945
|
+
const person = personFromSignalText(text)
|
|
946
|
+
if (person) return person
|
|
859
947
|
const t = String(text).trim()
|
|
860
948
|
const proper = t.match(/^([A-Z][a-z]+(?:\s+[A-Z][a-z]+)?)/)
|
|
861
949
|
if (proper) return proper[1]
|
|
@@ -885,11 +973,11 @@ function extractStakeholders(eng) {
|
|
|
885
973
|
const stance = stanceIdx !== -1 ? (cs[stanceIdx] || '').trim() : ''
|
|
886
974
|
const note = notesIdx !== -1 ? (cs[notesIdx] || '').trim() : ''
|
|
887
975
|
const words = name.replace(/\([^)]*\)/g, '').split(/\s+/).filter(w => w && !/^(dr|mr|mrs|ms)\.?$/i.test(w))
|
|
888
|
-
const
|
|
976
|
+
const nameKey = signalSubjectKey(name)
|
|
889
977
|
let signal = null, matchedDate = null
|
|
890
|
-
if (
|
|
978
|
+
if (nameKey) {
|
|
891
979
|
for (const h of history) {
|
|
892
|
-
if (h.text
|
|
980
|
+
if (signalSubjectKey(h.text) === nameKey && (!matchedDate || h.date >= matchedDate)) {
|
|
893
981
|
signal = h.signal; matchedDate = h.date
|
|
894
982
|
}
|
|
895
983
|
}
|
|
@@ -1317,11 +1405,14 @@ function cmdLog(args) {
|
|
|
1317
1405
|
if (!['red', 'amber', 'green'].includes(signal)) { console.error('usage: fde log contact <text> --signal red|amber|green'); process.exit(1) }
|
|
1318
1406
|
args.splice(sigIdx, 2)
|
|
1319
1407
|
}
|
|
1408
|
+
let retire = false
|
|
1409
|
+
const retireIdx = args.indexOf('--retire')
|
|
1410
|
+
if (retireIdx !== -1) { retire = true; args.splice(retireIdx, 1) }
|
|
1320
1411
|
const type = args[0]; const text = args.slice(1).join(' ')
|
|
1321
1412
|
const eng = resolveEngagement({ forWrite: true })
|
|
1322
1413
|
if (!eng) { console.error('no engagement - run: fde resume --init <name>'); process.exit(2) }
|
|
1323
1414
|
|
|
1324
|
-
// fde log phase <land|discover|plan|ship|
|
|
1415
|
+
// fde log phase <land|discover|plan|ship|outcome|close> - advances portfolio phase (prove → outcome)
|
|
1325
1416
|
if (type === 'phase') {
|
|
1326
1417
|
const phase = canonicalPhase((text || '').toLowerCase().trim())
|
|
1327
1418
|
if (!PHASES.includes(phase)) {
|
|
@@ -1333,11 +1424,26 @@ function cmdLog(args) {
|
|
|
1333
1424
|
return
|
|
1334
1425
|
}
|
|
1335
1426
|
|
|
1336
|
-
if (!LOG_FILES[type] || !text) { console.error(`usage: fde log <decision|risk|delivery|contact> <text> [--signal red|amber|green] [--force]\n fde log phase <${PHASES.join('|')}>\n fde log --undo`); process.exit(1) }
|
|
1427
|
+
if (!LOG_FILES[type] || !text) { console.error(`usage: fde log <decision|risk|delivery|contact> <text> [--signal red|amber|green] [--force]\n fde log risk --retire <text>\n fde log phase <${PHASES.join('|')}>\n fde log --undo`); process.exit(1) }
|
|
1337
1428
|
if (signal && type !== 'contact') { console.error('--signal only applies to: fde log contact'); process.exit(1) }
|
|
1429
|
+
if (retire && type !== 'risk') { console.error('--retire only applies to: fde log risk'); process.exit(1) }
|
|
1338
1430
|
const hit = findSecretHit(text)
|
|
1339
1431
|
if (hit && !force) { refuseSecret('log text', hit); process.exit(1) }
|
|
1340
1432
|
if (hit && force) console.error(`warning: logging possible ${hit} (--force)`)
|
|
1433
|
+
if (retire) {
|
|
1434
|
+
const n = retireOpenRisks(eng, text)
|
|
1435
|
+
if (!n) { console.error(`no open risk matched ${JSON.stringify(text)}`); process.exit(1) }
|
|
1436
|
+
const hash = commitMemory(eng, 'retire risk', { files: ['risks.md'] })
|
|
1437
|
+
console.log(`retired ${n} risk(s) → risks.md${hash ? ` @${hash}` : ''}`)
|
|
1438
|
+
return
|
|
1439
|
+
}
|
|
1440
|
+
if (type === 'delivery' && text.includes('|')) {
|
|
1441
|
+
const cells = text.split('|').map(s => s.trim())
|
|
1442
|
+
appendValueLedgerRow(eng, cells)
|
|
1443
|
+
const hash = memoryHead(eng)
|
|
1444
|
+
console.log(`logged → delivery.md (value ledger)${hash ? ` @${hash}` : ''}`)
|
|
1445
|
+
return
|
|
1446
|
+
}
|
|
1341
1447
|
const date = new Date().toISOString().slice(0, 10)
|
|
1342
1448
|
const entry = datedEntry(eng, date, text, signal || '')
|
|
1343
1449
|
appendLogEntry(eng, type, entry)
|
|
@@ -1651,9 +1757,11 @@ function cmdDebrief(args) {
|
|
|
1651
1757
|
input = readDebriefInput(args)
|
|
1652
1758
|
}
|
|
1653
1759
|
|
|
1654
|
-
|
|
1760
|
+
if (smart) {
|
|
1655
1761
|
const { proposePath, clean, blocks } = writeProposal(eng, smartProposeText(input))
|
|
1656
1762
|
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:')
|
|
1764
|
+
console.log('Everything else → context.md. Keep the prefixes; the preview gate stays.\n')
|
|
1657
1765
|
routeDebriefInput(eng, clean, { dry: true, force, sealed: blocks })
|
|
1658
1766
|
if (!apply) {
|
|
1659
1767
|
console.log(`\nproposal saved → ${proposePath}`)
|
|
@@ -2129,7 +2237,7 @@ function collectDoctorIssues(eng) {
|
|
|
2129
2237
|
}
|
|
2130
2238
|
}
|
|
2131
2239
|
const success = readClean(eng, 'success.md')
|
|
2132
|
-
if (!firstLine(success, 80)) issues.push('success.md has no stated done-definition - fill before plan/
|
|
2240
|
+
if (!firstLine(success, 80)) issues.push('success.md has no stated done-definition - fill before plan/ship')
|
|
2133
2241
|
const ctxMd = readClean(eng, 'context.md')
|
|
2134
2242
|
if (!sectionBody(ctxMd, 'Next action', { lastNonEmpty: true })) {
|
|
2135
2243
|
issues.push('no ## Next action in context.md - Monday morning has nothing to drive')
|
|
@@ -2170,7 +2278,7 @@ function collectDoctorIssues(eng) {
|
|
|
2170
2278
|
}
|
|
2171
2279
|
// Failure-path (exception-led operating map): required once past discover.
|
|
2172
2280
|
// Land seeds; discover fills; plan+ without a real break→owner row is wallpaper.
|
|
2173
|
-
if (/^(plan|ship|
|
|
2281
|
+
if (/^(plan|ship|outcome|close)$/.test(s.phase) && !hasOperatingMapContent(eng)) {
|
|
2174
2282
|
issues.push(
|
|
2175
2283
|
`phase is ${s.phase} with empty operating map - fill terrain.md ## Operating map (exception-led): break → who notices → workaround → evidence`
|
|
2176
2284
|
)
|
|
@@ -2182,6 +2290,8 @@ function collectDoctorIssues(eng) {
|
|
|
2182
2290
|
`${aliases.length} stakeholder identity cluster(s) (e.g. "${sample}") - same person under different names? consolidate in stakeholders.md`
|
|
2183
2291
|
)
|
|
2184
2292
|
}
|
|
2293
|
+
const reality = parseReality(readClean(eng, 'reality.md'), 220)
|
|
2294
|
+
if (reality.missing) issues.push(reality.missing)
|
|
2185
2295
|
return issues
|
|
2186
2296
|
}
|
|
2187
2297
|
|
|
@@ -2484,7 +2594,7 @@ function cmdRedact(args) {
|
|
|
2484
2594
|
console.log('nothing changed')
|
|
2485
2595
|
return
|
|
2486
2596
|
}
|
|
2487
|
-
const hash = commitMemory(eng, `redact ${
|
|
2597
|
+
const hash = commitMemory(eng, `redact ${hits.length} line(s)`, { files: touched })
|
|
2488
2598
|
console.log(`redacted ${hits.length} line(s) in ${touched.join(', ')}${hash ? ` @${hash}` : ''}`)
|
|
2489
2599
|
console.log('rotate the real credential if this was a secret - history may still contain it')
|
|
2490
2600
|
}
|
|
@@ -2585,6 +2695,15 @@ function cmdGarden(args) {
|
|
|
2585
2695
|
sessionBlocks,
|
|
2586
2696
|
})
|
|
2587
2697
|
}
|
|
2698
|
+
const dirty = memoryDirtyManual(eng)
|
|
2699
|
+
if (dirty.length) {
|
|
2700
|
+
proposals.push({
|
|
2701
|
+
id: 'bless-manual',
|
|
2702
|
+
kind: 'apply',
|
|
2703
|
+
text: `Bless ${dirty.length} hand-written file(s) into the ledger: ${dirty.slice(0, 5).join(', ')}${dirty.length > 5 ? '…' : ''}`,
|
|
2704
|
+
files: dirty,
|
|
2705
|
+
})
|
|
2706
|
+
}
|
|
2588
2707
|
if (!proposals.length) {
|
|
2589
2708
|
console.log('\nNothing to tidy.')
|
|
2590
2709
|
return
|
|
@@ -2613,6 +2732,12 @@ function cmdGarden(args) {
|
|
|
2613
2732
|
}
|
|
2614
2733
|
continue
|
|
2615
2734
|
}
|
|
2735
|
+
if (p.id === 'bless-manual') {
|
|
2736
|
+
applied++
|
|
2737
|
+
for (const f of p.files) touched.add(f)
|
|
2738
|
+
console.log(`applied: bless ${p.files.join(', ')}`)
|
|
2739
|
+
continue
|
|
2740
|
+
}
|
|
2616
2741
|
if (p.id !== 'archive-sessions') continue
|
|
2617
2742
|
const cutDates = new Set(p.sessionBlocks.map(b => b.date))
|
|
2618
2743
|
const keep = []
|
|
@@ -2744,6 +2869,10 @@ function cmdStatus(args) {
|
|
|
2744
2869
|
}
|
|
2745
2870
|
}
|
|
2746
2871
|
if (!all) console.log('\n(current engagement only - pass --all for the full portfolio)')
|
|
2872
|
+
if (!all) {
|
|
2873
|
+
const current = resolveEngagement()
|
|
2874
|
+
if (current) for (const line of hygieneTriageLines(current)) console.log(line)
|
|
2875
|
+
}
|
|
2747
2876
|
console.log('\ntrust: worst active [signal:x] across stakeholders (latest per person) - a green from B cannot clear an amber/red on A; keyword heuristic only when none exists.')
|
|
2748
2877
|
}
|
|
2749
2878
|
|
|
@@ -2801,7 +2930,9 @@ function cmdDashboard(args) {
|
|
|
2801
2930
|
// line instead of sharing one, a shorter cap just meant more sentences
|
|
2802
2931
|
// cut off mid-thought for no reason.
|
|
2803
2932
|
e.brief = firstLine(readClean(e.dir, 'brief.md'), 220)
|
|
2804
|
-
|
|
2933
|
+
const reality = parseReality(readClean(e.dir, 'reality.md'), 220)
|
|
2934
|
+
e.reality = reality.line
|
|
2935
|
+
e.realityMissing = reality.missing
|
|
2805
2936
|
e.overlay = detectOverlay(e.dir)
|
|
2806
2937
|
e.days = daysElapsed(e.dir)
|
|
2807
2938
|
e.phaseLabel = phaseLabel(e.signals.phase)
|
|
@@ -2839,6 +2970,10 @@ function cmdDashboard(args) {
|
|
|
2839
2970
|
}
|
|
2840
2971
|
console.log(`fieldbook → ${outPath}`)
|
|
2841
2972
|
console.log(`${engagements.length} engagement(s) rendered · ${counts.RED} red / ${counts.amber} amber / ${counts.green} green · 0 tokens (pure render)`)
|
|
2973
|
+
if (!all) {
|
|
2974
|
+
const current = resolveEngagement()
|
|
2975
|
+
if (current) for (const line of hygieneTriageLines(current)) console.log(line)
|
|
2976
|
+
}
|
|
2842
2977
|
if (args.includes('--open')) {
|
|
2843
2978
|
// arg-array form: the path is never interpolated into a shell string.
|
|
2844
2979
|
const [bin, pre] = process.platform === 'darwin' ? ['open', []]
|
|
@@ -2985,7 +3120,8 @@ function cmdVault(args) {
|
|
|
2985
3120
|
const ctx = readClean(e.dir, 'context.md')
|
|
2986
3121
|
e.next = (sectionBody(ctx, 'Next action', { lastNonEmpty: true }).split('\n').find(l => l.trim()) || '').trim()
|
|
2987
3122
|
e.brief = firstLine(readClean(e.dir, 'brief.md'), 400)
|
|
2988
|
-
|
|
3123
|
+
const reality = parseReality(readClean(e.dir, 'reality.md'), 400)
|
|
3124
|
+
e.reality = reality.line || reality.missing
|
|
2989
3125
|
e.overlay = detectOverlay(e.dir)
|
|
2990
3126
|
e.days = daysElapsed(e.dir)
|
|
2991
3127
|
e.stakeholders = extractStakeholders(e.dir)
|
|
@@ -3186,19 +3322,20 @@ function printUsage() {
|
|
|
3186
3322
|
fde resume --init <name> create + bind engagement for this workspace (rebind replaces)
|
|
3187
3323
|
fde resume --bind show what this workspace is bound to, and what resolves
|
|
3188
3324
|
fde triage TRIAGE block only (hooks / Cursor session entry)
|
|
3189
|
-
fde log <type> <text> append decision|risk|delivery|contact (contact takes --signal red|amber|green; --force to allow secret-like text)
|
|
3190
|
-
fde log
|
|
3325
|
+
fde log <type> <text> append decision|risk|delivery|contact (contact takes --signal red|amber|green; delivery "a|b|c" writes the value ledger; --force to allow secret-like text)
|
|
3326
|
+
fde log risk --retire move matching open-risk bullets to ## Retired
|
|
3327
|
+
fde log phase <phase> set engagement phase (land|discover|plan|ship|outcome|close)
|
|
3191
3328
|
fde log --undo remove the last CLI log/debrief entry from memory
|
|
3192
3329
|
fde debrief [file] meeting notes → memory (prefixed lines; --dry-run; --force)
|
|
3193
|
-
fde debrief --smart heuristic propose (
|
|
3330
|
+
fde debrief --smart heuristic propose (prints decision:/risk:/delivery:/contact:/next:); --apply after confirm
|
|
3194
3331
|
fde ingest stage … stage raw pull into <engagement>/.inbox/ (not .fde/)
|
|
3195
3332
|
fde ingest list list staged inbox items
|
|
3196
3333
|
fde ingest propose <id> smart-propose a staged item → .debrief-propose (confirm before apply)
|
|
3197
3334
|
fde ingest apply same as: fde debrief --apply
|
|
3198
3335
|
fde prep [label] grounded walk-in brief from existing .fde/ only
|
|
3199
|
-
fde doctor lint engagement memory (stale signals, gaps)
|
|
3200
|
-
fde redact <term> preview/remove lines containing a buried term (pass --apply to commit)
|
|
3201
|
-
fde tidy [--apply] propose
|
|
3336
|
+
fde doctor lint engagement memory (stale signals, gaps). status/dashboard/resume print the same issues
|
|
3337
|
+
fde redact <term> preview/remove lines containing a buried term (pass --apply to commit; subject never repeats the term)
|
|
3338
|
+
fde tidy [--apply] propose consolidations; blesses hand-written dirty files when you apply
|
|
3202
3339
|
fde owner [set email] who keeps this engagement record
|
|
3203
3340
|
fde receipts <term> "what did we agree?" with dates
|
|
3204
3341
|
fde status [--all] value ledger, then trust (pass --all for full portfolio)
|
package/bin/lib/render.js
CHANGED
|
@@ -467,10 +467,10 @@ ${e.lastSession ? `<div class="fb-now-session">${inlineMd(e.lastSession)}</div>`
|
|
|
467
467
|
// needed vs what's actually true. Squeezed onto one truncated line, that
|
|
468
468
|
// contrast disappears. Each gets its own line, its own room to finish a
|
|
469
469
|
// thought, not a race to fit before an ellipsis.
|
|
470
|
-
const whyBlock = (e.brief || e.reality) ? `<div class="fb-block">
|
|
470
|
+
const whyBlock = (e.brief || e.reality || e.realityMissing) ? `<div class="fb-block">
|
|
471
471
|
<div class="fb-sec">Why</div>
|
|
472
472
|
${e.brief ? `<p class="fb-why"><span class="t-faint">What they asked for:</span> ${inlineMd(e.brief)}</p>` : ''}
|
|
473
|
-
${e.reality ? `<p class="fb-why fb-why-reality"><span class="fb-accent-label">What's actually true:</span> ${inlineMd(e.reality)}</p>` : ''}
|
|
473
|
+
${e.realityMissing ? `<p class="fb-why fb-why-missing"><span class="fb-accent-label">What's actually true:</span> ${escapeHtml(e.realityMissing)}</p>` : e.reality ? `<p class="fb-why fb-why-reality"><span class="fb-accent-label">What's actually true:</span> ${inlineMd(e.reality)}</p>` : ''}
|
|
474
474
|
</div>` : ''
|
|
475
475
|
|
|
476
476
|
// Vitals: a fixed field-facing gut-check panel, not a Movement block that
|
package/bin/lib/trust.js
CHANGED
|
@@ -48,12 +48,46 @@ function createTrustApi(deps) {
|
|
|
48
48
|
return { ok: true, warn: '' }
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
//
|
|
52
|
-
//
|
|
51
|
+
// Event labels are not people. "INCIDENT: … Marcus escalated" must key on
|
|
52
|
+
// Marcus, or a recovered engagement stays RED in front of the sponsor.
|
|
53
|
+
const SIGNAL_EVENT_KEYS = new Set([
|
|
54
|
+
'incident', 'recovery', 'alert', 'update', 'note', 'status', 'escalation',
|
|
55
|
+
'blocker', 'outage', 'fire', 'issue', 'sev', 'sev1', 'sev2', 'p1', 'p2', 'p3',
|
|
56
|
+
'resolved', 'risk', 'decision', 'delivery', 'contact',
|
|
57
|
+
])
|
|
58
|
+
|
|
59
|
+
function personFromSignalText(text) {
|
|
60
|
+
let cleaned = String(text || '')
|
|
61
|
+
.replace(/\[@[^\]]+\]/g, '')
|
|
62
|
+
.replace(/\([^)]*\)/g, '')
|
|
63
|
+
.replace(/\[signal:[^\]]+\]/gi, '')
|
|
64
|
+
.replace(/\[\d{4}-\d{2}-\d{2}\]/g, '')
|
|
65
|
+
.replace(/^([A-Z]{2,}[A-Z0-9_-]*):?\s+/, '')
|
|
66
|
+
.trim()
|
|
67
|
+
const names = cleaned.match(/\b[A-Z][a-z]{1,20}(?:\s+[A-Z][a-z]{1,20})?\b/g) || []
|
|
68
|
+
for (const name of names) {
|
|
69
|
+
const first = name.split(/\s+/)[0].toLowerCase()
|
|
70
|
+
if (SIGNAL_EVENT_KEYS.has(first)) continue
|
|
71
|
+
return name
|
|
72
|
+
}
|
|
73
|
+
return ''
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Subject key for a signal-history line. Prefer a proper name in the bullet.
|
|
77
|
+
// A green about Randy must not clear an amber about Denise.
|
|
53
78
|
// Strip author tags [@email-local] so attribution never becomes the subject key.
|
|
54
79
|
function signalSubjectKey(text) {
|
|
80
|
+
const person = personFromSignalText(text)
|
|
81
|
+
if (person) {
|
|
82
|
+
const frag = person.split(/\s+/)[0].replace(/[^a-z0-9]/gi, '').toLowerCase()
|
|
83
|
+
if (frag.length >= 3) return frag
|
|
84
|
+
}
|
|
55
85
|
const cleaned = String(text).replace(/\[@[^\]]+\]/g, '').replace(/\([^)]*\)/g, '')
|
|
56
|
-
|
|
86
|
+
.replace(/^([A-Z]{2,}[A-Z0-9_-]*):?\s+/, '')
|
|
87
|
+
const words = cleaned.split(/\s+/).filter(w => {
|
|
88
|
+
const n = w.replace(/[^a-z0-9]/gi, '').toLowerCase()
|
|
89
|
+
return n.length >= 3 && !SIGNAL_EVENT_KEYS.has(n) && !/^(dr|mr|mrs|ms)$/i.test(w)
|
|
90
|
+
})
|
|
57
91
|
const frag = (words[0] || '').replace(/[^a-z0-9]/gi, '').toLowerCase()
|
|
58
92
|
return frag.length >= 3 ? frag : ('anon:' + cleaned.slice(0, 48).toLowerCase())
|
|
59
93
|
}
|
|
@@ -64,9 +98,11 @@ function createTrustApi(deps) {
|
|
|
64
98
|
if (!m) return '?'
|
|
65
99
|
const raw = m[1].replace(/\*/g, '').trim()
|
|
66
100
|
if (!raw || /\|/.test(raw) || /^unset$/i.test(raw) || /^[\[(]/.test(raw)) return '?'
|
|
67
|
-
const one = raw.toLowerCase().match(/^(land|discover|plan|build|ship|prove|close)\b/)
|
|
101
|
+
const one = raw.toLowerCase().match(/^(land|discover|plan|build|ship|prove|outcome|close)\b/)
|
|
68
102
|
if (!one) return '?'
|
|
69
|
-
|
|
103
|
+
if (one[1] === 'build') return 'ship'
|
|
104
|
+
if (one[1] === 'prove') return 'outcome'
|
|
105
|
+
return one[1]
|
|
70
106
|
}
|
|
71
107
|
|
|
72
108
|
function countOpenRisks(eng) {
|
|
@@ -196,6 +232,7 @@ function createTrustApi(deps) {
|
|
|
196
232
|
|
|
197
233
|
return {
|
|
198
234
|
stakeholdersMemoryHealth,
|
|
235
|
+
personFromSignalText,
|
|
199
236
|
signalSubjectKey,
|
|
200
237
|
parsePhase,
|
|
201
238
|
countOpenRisks,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fdeops",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.20.0",
|
|
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.
|
|
4
|
+
"version": "3.20.0",
|
|
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,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: fde
|
|
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,
|
|
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, change the client's codebase, prove it on their staging, go live, 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, 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.
|
|
10
|
+
The **engagement record** for one client, from first meeting to signed outcome. One skill; six stages (land → close). Same map at any scale, on greenfield or brownfield, in any industry (overlays). You route; they never pick a skill. Confirm, then write `.fde/`. The workspace still compiles and commits. `@fde` does not leave.
|
|
11
11
|
|
|
12
12
|
## When to use
|
|
13
13
|
|
|
@@ -17,7 +17,7 @@ The **engagement record** for one client, from first meeting to signed outcome.
|
|
|
17
17
|
|
|
18
18
|
## When NOT to use
|
|
19
19
|
|
|
20
|
-
A one-line typo or compile error in a file that will not ship. On a bound client: stay here for POC,
|
|
20
|
+
A one-line typo or compile error in a file that will not ship. On a bound client: stay here for POC, characterisation, the change on their repo, eval, go-live, rollback, and acceptance.
|
|
21
21
|
|
|
22
22
|
## Use these first
|
|
23
23
|
|
|
@@ -32,15 +32,15 @@ After a meeting: `fde debrief --smart` → confirm → `--apply`. Walk-in: `fde
|
|
|
32
32
|
|
|
33
33
|
## Ground loop
|
|
34
34
|
|
|
35
|
-
On someone else's site the work is not "write code, remember later." Every
|
|
35
|
+
On someone else's site the work is not "write code, remember later." Every change on a bound client stays on `@fde`:
|
|
36
36
|
|
|
37
37
|
1. **Name it** in `decisions.md` (plan) or kill it in a day (poc).
|
|
38
|
-
2. **Characterise their code** before you change it
|
|
39
|
-
3. **Prove it
|
|
40
|
-
4. **If a model judges:** `evals.md` Verdict SHIP before
|
|
41
|
-
5. **Log delivery.** Outcome is promised → measured → accepted, not a green CI.
|
|
38
|
+
2. **Characterise their code** before you change it. Brownfield: their tests, their runner. Greenfield: the empty tree, first path they can click.
|
|
39
|
+
3. **Prove it on their staging.** Staging they operate, a screen the signer in `success.md` can reject.
|
|
40
|
+
4. **If a model judges:** `evals.md` Verdict SHIP before that change is done (eval-pack).
|
|
41
|
+
5. **Log delivery.** Outcome is promised → measured → accepted, not a green CI. Then go live with a rollback you have run (`ship`).
|
|
42
42
|
|
|
43
|
-
A throwaway file can skip the loop.
|
|
43
|
+
A throwaway file can skip the loop. Bound client work cannot.
|
|
44
44
|
|
|
45
45
|
## Human surface vs agent plumbing
|
|
46
46
|
|
|
@@ -77,7 +77,7 @@ Writes need a bind (`FDEOPS_ENGAGEMENT` or registry). Never install fdeops on in
|
|
|
77
77
|
|
|
78
78
|
## The memory contract
|
|
79
79
|
|
|
80
|
-
1. **On entry:** `fde resume` only. Pull other `.fde/` files when the
|
|
80
|
+
1. **On entry:** `fde resume` only. Pull other `.fde/` files when the skill needs them.
|
|
81
81
|
2. **Deliverable = memory.** The work *is* the `.fde/` file. The reference names which one.
|
|
82
82
|
3. **Evidence.** Every claim has a source. Traceable beats plausible.
|
|
83
83
|
4. **No invented facts.** People, quotes, meetings, numbers: they said it or the repo shows it. Else `unknown - ask: <question>`.
|
|
@@ -107,84 +107,83 @@ CLI is local (`git` + files, no network). You see their code only when they poin
|
|
|
107
107
|
|
|
108
108
|
Direct. Their words. No "Certainly." Playback 2-4 lines, then act. One question only when a missing fact changes the next move.
|
|
109
109
|
|
|
110
|
-
New embed: sprint / standard / programme changes depth, not which
|
|
110
|
+
New embed: sprint / standard / programme changes depth, not which skills 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.
|
|
111
111
|
|
|
112
112
|
Muddy signal: name it ("discover or rescue - leaning X"). Never a phase-picker interview. Default: land if new, audit if takeover.
|
|
113
113
|
|
|
114
114
|
## Routing - 6 stages
|
|
115
115
|
|
|
116
|
-
Read **one** reference and follow it. Do not improvise from memory.
|
|
116
|
+
Work names (engage, diagnose, align, deliver, realize, transfer) are the same map. Read **one** reference and follow it. Do not improvise from memory.
|
|
117
117
|
|
|
118
118
|
### Land
|
|
119
119
|
|
|
120
120
|
| You hear | Skill | Reference |
|
|
121
121
|
|----------|-------|-----------|
|
|
122
|
-
|
|
|
122
|
+
| Engage, onboarding, starting fresh, new customer, first meeting, just got the brief, set product strategy, define success metrics, scope the brief | land | `references/land.md` |
|
|
123
123
|
| Taking over, previous consultant left, joining mid-project | audit | `references/audit.md` |
|
|
124
|
-
| Need to understand who matters, who decides, who blocks quietly | who-decides | `references/who-decides.md` |
|
|
124
|
+
| Need to understand who matters, who decides, map decision rights, who blocks quietly | who-decides | `references/who-decides.md` |
|
|
125
125
|
| Need to earn access, navigate AI policy, build credibility | earn-trust | `references/earn-trust.md` |
|
|
126
|
-
| "Also can you…", scope expanding, timeline unchanged | hold-scope | `references/hold-scope.md` |
|
|
126
|
+
| "Also can you…", scope expanding, timeline unchanged, hold scope, scope the brief after kickoff | hold-scope | `references/hold-scope.md` |
|
|
127
127
|
|
|
128
128
|
### Discover
|
|
129
129
|
|
|
130
130
|
| You hear | Skill | Reference |
|
|
131
131
|
|----------|-------|-----------|
|
|
132
|
-
|
|
|
133
|
-
| The brief feels too neat, assumptions untested, "we just need…" | test-assumptions | `references/test-assumptions.md` |
|
|
134
|
-
| Multiple use cases competing, "we want to do everything" | score-use-cases | `references/score-use-cases.md` |
|
|
135
|
-
| Need to validate a direction, prototype, demo to de-risk, **POC**, spike, killer assumption | poc | `references/poc.md` |
|
|
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
|
+
| Multiple use cases competing, "we want to do everything", score use cases | score-use-cases | `references/score-use-cases.md` |
|
|
135
|
+
| Need to validate a direction, prototype, demo to de-risk, **POC**, spike, killer assumption, validate the solution, build prototype | poc | `references/poc.md` |
|
|
136
136
|
|
|
137
137
|
### Plan
|
|
138
138
|
|
|
139
139
|
| You hear | Skill | Reference |
|
|
140
140
|
|----------|-------|-----------|
|
|
141
|
-
|
|
|
142
|
-
| Sponsor needs justification, need to defend budget or timeline | business-case | `references/business-case.md` |
|
|
143
|
-
| Significant decision, multiple approaches, "what should we do?" | three-options | `references/three-options.md` |
|
|
144
|
-
| 20 things are "urgent," need to pick the 3 that matter | pick-three | `references/pick-three.md` |
|
|
141
|
+
| Align, break this down, what order, sequence the build, plan the roadmap, create user stories, write the tasks | plan | `references/plan.md` |
|
|
142
|
+
| 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` |
|
|
144
|
+
| 20 things are "urgent," need to pick the 3 that matter, prioritize three | pick-three | `references/pick-three.md` |
|
|
145
145
|
|
|
146
146
|
### Ship
|
|
147
147
|
|
|
148
148
|
| You hear | Skill | Reference |
|
|
149
149
|
|----------|-------|-----------|
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
| Review this change, is it safe, does it match what we agreed | review | `references/review.md` |
|
|
150
|
+
| What could go wrong, touching shared infrastructure, need to assess impact, assess impact, provision, IaC, shared infra | what-breaks | `references/what-breaks.md` |
|
|
151
|
+
| Production down, urgent, fix a prod bug, resolve incident, restore service - OR stakeholder gone quiet, trust slipping | rescue | `references/rescue.md` |
|
|
152
|
+
| Deliver, start building, update their checkout, first module, visible progress, their tests, POC follow-through, ready to deploy, going live, pre-flight, deliver the increment, build the increment, create the launch plan, design their UI | ship | `references/ship.md` |
|
|
153
|
+
| Review this change, review the pull request, is it safe, does it match what we agreed | review | `references/review.md` |
|
|
155
154
|
| 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 |
|
|
156
155
|
| 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 |
|
|
157
|
-
| "We can always revert" - need to actually test the escape route | rollback | `references/rollback.md` |
|
|
156
|
+
| "We can always revert" - need to actually test the escape route, rehearse rollback | rollback | `references/rollback.md` |
|
|
158
157
|
|
|
159
|
-
###
|
|
158
|
+
### Outcome
|
|
160
159
|
|
|
161
160
|
| You hear | Skill | Reference |
|
|
162
161
|
|----------|-------|-----------|
|
|
163
|
-
|
|
|
164
|
-
| Demo coming up, show-and-tell, exec walkthrough | demo-prep | `references/demo-prep.md` |
|
|
165
|
-
| Just out of a meeting, raw notes, "they said…", "debrief" | debrief | the debrief verb (above) + `references/debrief.md` |
|
|
162
|
+
| Realize, weekly update due, "need to send the sponsor something", report the outcome | readout | `references/readout.md` |
|
|
163
|
+
| Demo coming up, show-and-tell, exec walkthrough, prepare the demo | demo-prep | `references/demo-prep.md` |
|
|
164
|
+
| Just out of a meeting, raw notes, "they said…", "debrief", user interviews, workshop notes, capture the meeting | debrief | the debrief verb (above) + `references/debrief.md` |
|
|
166
165
|
| Make sure we're up to date, pull what's relevant, fetch from Granola/Slack/Gmail/transcript | ingest | `references/ingest.md` (capability check → stage → propose → confirm → apply) |
|
|
167
166
|
| Connect a new MCP / connect Granola Slack or Notion / what can you pull | connect | `references/connect.md` (+ `mcp/recipes/`) |
|
|
168
167
|
| Prep me for a meeting / walk-in brief / "what should I know before I talk to…" | - | run `fde prep "<label>"`, present in plain language |
|
|
169
|
-
| Sponsor's boss needs a summary, board update, justify continued investment | board-memo | `references/board-memo.md` |
|
|
170
|
-
| Status across all my customers | dashboard | `references/dashboard.md` |
|
|
168
|
+
| Sponsor's boss needs a summary, board update, brief the board, justify continued investment | board-memo | `references/board-memo.md` |
|
|
169
|
+
| Status across all my customers, view the portfolio | dashboard | `references/dashboard.md` |
|
|
171
170
|
|
|
172
171
|
### Close
|
|
173
172
|
|
|
174
173
|
| You hear | Skill | Reference |
|
|
175
174
|
|----------|-------|-----------|
|
|
176
|
-
| Juggling 2+ customers, losing track, context-switching | switch-clients | `references/switch-clients.md` |
|
|
177
|
-
|
|
|
178
|
-
| Engagement ending, team needs to operate without you | runbook | `references/runbook.md` |
|
|
179
|
-
| Something worked well and will apply to future engagements | encode-pattern | `references/encode-pattern.md` |
|
|
180
|
-
| "Red-team this," "stress-test my plan," poke holes, what am I missing | red-team | `references/red-team.md` |
|
|
175
|
+
| Juggling 2+ customers, losing track, context-switching, switch engagements | switch-clients | `references/switch-clients.md` |
|
|
176
|
+
| Transfer, wrapping up, handoff, making yourself replaceable, transfer operations | close | `references/close.md` |
|
|
177
|
+
| Engagement ending, team needs to operate without you, write the runbook | runbook | `references/runbook.md` |
|
|
178
|
+
| Something worked well and will apply to future engagements, encode the pattern | encode-pattern | `references/encode-pattern.md` |
|
|
179
|
+
| "Red-team this," "stress-test my plan," poke holes, challenge the plan, what am I missing | red-team | `references/red-team.md` |
|
|
181
180
|
| "What did we agree about X?", scope dispute, receipts | - | run `fde receipts <term>`, answer with dates |
|
|
182
181
|
|
|
183
182
|
**Overlays - activate alongside any skill on signal, don't wait to be told:**
|
|
184
183
|
|
|
185
184
|
| Signal | Overlay |
|
|
186
185
|
|--------|---------|
|
|
187
|
-
| AI, ML, LLM, model, embeddings, RAG, agents, fine-tuning, inference, drift | `references/ai.md` |
|
|
186
|
+
| AI, ML, LLM, model, embeddings, RAG, agents, fine-tuning, inference, drift, train the model | `references/ai.md` |
|
|
188
187
|
| Golden set, eval suite, eval pack, pass/fail before AI ship, HITL gate for model, POC the model | `references/eval-pack.md` (+ `ai.md`) |
|
|
189
188
|
| Deck, slides, report, governance framework, compliance pack, ADR, PDF | `references/artifacts.md` |
|
|
190
189
|
| Patient data, PHI, HIPAA, EHR, clinical | `references/healthcare.md` |
|
|
@@ -196,7 +195,9 @@ Ready to build with no `terrain.md` / plan: discover or plan first. Takeover wit
|
|
|
196
195
|
## Principles
|
|
197
196
|
|
|
198
197
|
- Never ask the FDE to pick a phase. That's your job.
|
|
199
|
-
-
|
|
198
|
+
- 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. Do not hand their repo to a generic coding pack.
|
|
200
|
+
- Do not call a change done until the signer in `success.md` can reject it on staging they operate.
|
|
200
201
|
- Read `context.md` before speaking. One sharp question - never a barrage.
|
|
201
202
|
- Never invent people, meetings, or numbers - `unknown - ask:` beats a polished lie.
|
|
202
203
|
- Every phase ends with its artifact written. No artifact, no "done."
|