create-pmos 0.34.0 → 0.37.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/bin/create-pmos.js +15 -3
- package/package.json +1 -1
- package/payload/AGENTS.md +2 -0
- package/payload/KIT-VERSION +2 -2
- package/payload/okf/core/concepts/agent-harness.md +2 -2
- package/payload/okf/core/concepts/design-loop.md +5 -5
- package/payload/okf/core/concepts/eval-driven-pm.md +1 -1
- package/payload/okf/core/concepts/okr-anti-optimism.md +1 -1
- package/payload/okf/core/concepts/okr-tree.md +2 -2
- package/payload/okf/core/concepts/output-eval.md +2 -2
- package/payload/okf/core/concepts/sdlc-loop.md +1 -1
- package/payload/okf/core/concepts/self-refining-harness.md +3 -3
- package/payload/okf/core/templates/content-brief-template.md +1 -1
- package/payload/okf/core/templates/design-brief-template.md +1 -1
- package/payload/okf/core/templates/eval-rubric-template.md +1 -1
- package/payload/okf/core/templates/log.md +1 -1
- package/payload/okf/core/templates/prd-template.md +1 -1
- package/payload/okf/core/templates/spike-brief-template.md +1 -1
- package/payload/scripts/frontier.sh +6 -2
- package/payload/scripts/gate-initiative.sh +24 -3
- package/payload/scripts/metrics.py +20 -4
- package/payload/scripts/read-jsonl.js +53 -0
- package/payload/scripts/workflow-state.js +144 -35
- package/payload/scripts/workflow-sweep.js +39 -22
- package/payload/skills/SKILL-FORMAT.md +1 -1
- package/payload/skills/copywriting.skill +1 -1
- package/payload/skills/correction-signal-derive.skill +1 -1
- package/payload/skills/design-brief.skill +4 -4
- package/payload/skills/design-reconcile.skill +1 -1
- package/payload/skills/discovery.skill +3 -3
- package/payload/skills/eval-rubric.skill +4 -4
- package/payload/skills/evaluator.skill +1 -1
- package/payload/skills/explain-run.skill +1 -1
- package/payload/skills/explain-surface.skill +6 -6
- package/payload/skills/harness-refine.skill +1 -1
- package/payload/skills/ingest-repo.skill +6 -6
- package/payload/skills/okr.skill +2 -2
- package/payload/skills/pmos-router.skill +8 -8
- package/payload/skills/prd.skill +5 -5
- package/payload/skills/prototype-grill.skill +8 -8
- package/payload/skills/prototype.skill +7 -7
- package/payload/skills/skill-creator.skill +1 -1
- package/payload/skills/source-to-concept.skill +1 -1
- package/payload/skills/ste-term-map.md +69 -0
- package/payload/skills/strategic-brief.skill +4 -4
- package/payload/skills/technical-writing-ste.skill +176 -0
- package/payload/skills-native/copywriting/SKILL.md +1 -1
- package/payload/skills-native/correction-signal-derive/SKILL.md +1 -1
- package/payload/skills-native/design-brief/SKILL.md +4 -4
- package/payload/skills-native/design-reconcile/SKILL.md +1 -1
- package/payload/skills-native/discovery/SKILL.md +3 -3
- package/payload/skills-native/eval-rubric/SKILL.md +4 -4
- package/payload/skills-native/evaluator/SKILL.md +1 -1
- package/payload/skills-native/explain-run/SKILL.md +1 -1
- package/payload/skills-native/explain-surface/SKILL.md +6 -6
- package/payload/skills-native/harness-refine/SKILL.md +1 -1
- package/payload/skills-native/ingest-repo/SKILL.md +6 -6
- package/payload/skills-native/okr/SKILL.md +2 -2
- package/payload/skills-native/pmos-router/SKILL.md +8 -8
- package/payload/skills-native/prd/SKILL.md +5 -5
- package/payload/skills-native/prototype/SKILL.md +7 -7
- package/payload/skills-native/prototype-grill/SKILL.md +8 -8
- package/payload/skills-native/skill-creator/SKILL.md +1 -1
- package/payload/skills-native/source-to-concept/SKILL.md +1 -1
- package/payload/skills-native/strategic-brief/SKILL.md +4 -4
- package/payload/skills-native/technical-writing-ste/SKILL.md +178 -0
- package/workspace/bin/pmos.js +9 -3
- package/workspace/lib/engine/read-jsonl.js +53 -0
- package/workspace/lib/engine/workflow-state.js +144 -35
- package/workspace/lib/file-mode-parse.js +19 -4
- package/workspace/lib/tree-share.js +69 -1
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/board-read.js +38 -0
- package/workspace/public/pm/data.js +102 -1
- package/workspace/public/pm/modes.js +50 -28
- package/workspace/public/pm/panel-detail.js +2 -17
- package/workspace/public/pm/panel-initiatives.js +3 -20
- package/workspace/public/pm/panel-newsess.js +14 -1
- package/workspace/public/pm/panel-tabs.js +53 -14
- package/workspace/public/pm/tab-term.js +30 -2
- package/workspace/server.js +95 -70
- package/workspace/test-run.js +51 -9
|
@@ -58,18 +58,43 @@ const delivery = require('./delivery-index');
|
|
|
58
58
|
// The OKR documents and the budget registry are read once per root, not once per initiative: the
|
|
59
59
|
// board derives ~100 ladders in a single pass and re-reading the same three files each time is the
|
|
60
60
|
// same waste the shared commit-range exists to avoid.
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
//
|
|
62
|
+
// KEYED BY WHAT THE FILES ARE, never by where they live alone. The workspace server requires this
|
|
63
|
+
// module in-process and holds it for days, so a root-only key kept serving the ladder yesterday's
|
|
64
|
+
// anchor index after the PM fixed the OKR document — the OKR panel (which reads fresh) and the
|
|
65
|
+
// anchor rung disagreed about one field until a restart, the exact two-readers defect
|
|
66
|
+
// anchor-resolve was extracted to end. The stamp is mtime+size over the documents and their
|
|
67
|
+
// directories (the directory catches a file added or removed); the stats cost microseconds against
|
|
68
|
+
// the parse they guard. delivery-index keys its cache on the trunk head sha for the same reason.
|
|
69
|
+
// One stated limit: an `okrs/` directory born at a location no prior read has seen is picked up on
|
|
70
|
+
// the next change to a watched path or the next process, not instantly.
|
|
71
|
+
const _okrCache = new Map(); // root -> { stamp, files, index }
|
|
72
|
+
const _budCache = new Map(); // root -> { stamp, value }
|
|
73
|
+
function statStamp(p) {
|
|
74
|
+
try { const s = fs.statSync(p); return `${s.mtimeMs}:${s.size}`; } catch { return 'absent'; }
|
|
75
|
+
}
|
|
76
|
+
function okrStamp(files) {
|
|
77
|
+
const dirs = [...new Set(files.map((f) => path.dirname(f)))];
|
|
78
|
+
return files.concat(dirs).map((p) => `${p}=${statStamp(p)}`).join('|');
|
|
79
|
+
}
|
|
63
80
|
function okrIndexFor(root) {
|
|
64
|
-
|
|
65
|
-
|
|
81
|
+
const hit = _okrCache.get(root);
|
|
82
|
+
if (hit && hit.files.length && okrStamp(hit.files) === hit.stamp) return hit.index;
|
|
83
|
+
const files = anchors.listOkrFiles(root);
|
|
84
|
+
const entry = { stamp: okrStamp(files), files, index: anchors.parseOkrIndex(root) };
|
|
85
|
+
_okrCache.set(root, entry);
|
|
86
|
+
return entry.index;
|
|
66
87
|
}
|
|
67
88
|
function budgetsFor(root, L) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
89
|
+
const stamp = statStamp(L.budgets);
|
|
90
|
+
const hit = _budCache.get(root);
|
|
91
|
+
if (!hit || hit.stamp !== stamp) {
|
|
92
|
+
let value;
|
|
93
|
+
try { value = { ok: true, text: fs.readFileSync(L.budgets, 'utf8'), path: path.relative(root, L.budgets) }; }
|
|
94
|
+
catch { value = { ok: false, text: '', path: path.relative(root, L.budgets) }; }
|
|
95
|
+
_budCache.set(root, { stamp, value });
|
|
71
96
|
}
|
|
72
|
-
return _budCache.get(root);
|
|
97
|
+
return _budCache.get(root).value;
|
|
73
98
|
}
|
|
74
99
|
|
|
75
100
|
/*
|
|
@@ -105,18 +130,31 @@ function isBuildPath(p, lane) {
|
|
|
105
130
|
}
|
|
106
131
|
|
|
107
132
|
// ── Frontmatter ────────────────────────────────────────────────────────────────────────────────
|
|
108
|
-
// The LEADING block only — body text must never be able to fake a declaration.
|
|
109
|
-
//
|
|
110
|
-
//
|
|
133
|
+
// The LEADING block only — body text must never be able to fake a declaration. The closing ---
|
|
134
|
+
// fence is TERMINAL, never skipped: the fence used to be stepped over, so a key-shaped FIRST body
|
|
135
|
+
// line was absorbed into the block and could fake a declaration at the fence boundary (the one
|
|
136
|
+
// shape the gamma prose-then-keys fixture never exercised). Fenceless files keep parsing from line
|
|
137
|
+
// 1 and stop at the first non-key line — the kit's smoke fixtures carry no fences and must keep
|
|
138
|
+
// parsing. Mirrors workspace/lib/file-mode-parse.js, kit/scripts/metrics.py and the two bash gates'
|
|
139
|
+
// readers — first value wins, one pair of surrounding double quotes stripped — so all the readers
|
|
140
|
+
// agree on what a record declared (kit/fixtures/test_parsers.py + workspace/fixture-parity.test.mjs
|
|
141
|
+
// hold the agreement).
|
|
111
142
|
function parseLeadingFrontmatter(text) {
|
|
112
143
|
const fm = {};
|
|
144
|
+
let fenceOpen = false;
|
|
113
145
|
for (const line of String(text == null ? '' : text).split('\n')) {
|
|
146
|
+
if (line.startsWith('---')) {
|
|
147
|
+
// Only the FIRST ---, before any key, opens the block; every later --- ends it.
|
|
148
|
+
if (fenceOpen || Object.keys(fm).length) break;
|
|
149
|
+
fenceOpen = true;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
114
152
|
const m = line.match(/^([a-z_]+):\s*(.*?)\s*$/);
|
|
115
153
|
if (m) {
|
|
116
154
|
if (!(m[1] in fm)) fm[m[1]] = m[2].replace(/^"(.*)"$/s, '$1');
|
|
117
155
|
continue;
|
|
118
156
|
}
|
|
119
|
-
if (line.trim()
|
|
157
|
+
if (line.trim()) break;
|
|
120
158
|
}
|
|
121
159
|
return fm;
|
|
122
160
|
}
|
|
@@ -136,23 +174,32 @@ function readFrontmatter(file) {
|
|
|
136
174
|
* changed return type, because every existing call site wants the rows and only the sweep wants the
|
|
137
175
|
* damage. Changing the shape would have made ~six callers handle a case none of them can act on.
|
|
138
176
|
*/
|
|
177
|
+
// The implementation lives in scripts/read-jsonl.js now — this contract became the shared one after
|
|
178
|
+
// six hand-rolled readers each re-decided the torn-line question (and two got it wrong). The thin
|
|
179
|
+
// wrapper keeps this file's call sites and the out-of-band defects channel unchanged.
|
|
180
|
+
const jsonl = require('./read-jsonl');
|
|
139
181
|
function readJsonl(file, defects) {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
182
|
+
const r = jsonl.readJsonl(file);
|
|
183
|
+
// Dropped, never guessed — and said out loud to anyone who asked to hear it.
|
|
184
|
+
if (Array.isArray(defects)) defects.push(...r.defects);
|
|
185
|
+
return r.rows;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// The ledgers are read once per VERSION OF THE FILE, not once per initiative — the same per-root
|
|
189
|
+
// rule the OKR cache above states, applied to the other four per-root inputs it was skipping. The
|
|
190
|
+
// board filters ~665 KB of parsed rows once per ladder; re-reading and re-parsing the files each
|
|
191
|
+
// time was ~145 MB of work per board. The stamp is mtime+size for the same reason as the OKR
|
|
192
|
+
// cache's: the workspace server holds this module in-process, and a run or acceptance appended
|
|
193
|
+
// between requests must be visible on the next derivation, not after a restart. Callers filter the
|
|
194
|
+
// shared rows and never mutate them.
|
|
195
|
+
const _ledgerCache = new Map(); // absolute path -> { stamp, rows }
|
|
196
|
+
function cachedJsonl(file) {
|
|
197
|
+
const stamp = statStamp(file);
|
|
198
|
+
const hit = _ledgerCache.get(file);
|
|
199
|
+
if (hit && hit.stamp === stamp) return hit.rows;
|
|
200
|
+
const rows = readJsonl(file);
|
|
201
|
+
_ledgerCache.set(file, { stamp, rows });
|
|
202
|
+
return rows;
|
|
156
203
|
}
|
|
157
204
|
|
|
158
205
|
// ── Repo layout — pmos-self and the kit disagree on where state lives, so detect rather than assume
|
|
@@ -308,6 +355,39 @@ function firstAppearance(root, range, rel) {
|
|
|
308
355
|
return range.index.has(sha) ? range.index.get(sha) : -1;
|
|
309
356
|
}
|
|
310
357
|
|
|
358
|
+
/*
|
|
359
|
+
* When was each path last touched, answered from the range's OWN single pass. A `git log -1
|
|
360
|
+
* --format=%ct -- <paths>` per initiative was one subprocess per board row — ~85% of the board's
|
|
361
|
+
* runtime spent re-reading history buildRange had already captured (see the single-pass note
|
|
362
|
+
* above). Built lazily, memoised on the range object, so every ladder on the board shares one map.
|
|
363
|
+
*/
|
|
364
|
+
function touchIndex(range) {
|
|
365
|
+
if (!range._touch) {
|
|
366
|
+
const m = new Map();
|
|
367
|
+
for (let i = 0; i < range.shas.length; i++) {
|
|
368
|
+
const t = range.dates[i];
|
|
369
|
+
if (!Number.isFinite(t)) continue;
|
|
370
|
+
for (const f of (range.changed[i] || [])) {
|
|
371
|
+
const prev = m.get(f);
|
|
372
|
+
if (prev === undefined || t > prev) m.set(f, t);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
range._touch = m;
|
|
376
|
+
}
|
|
377
|
+
return range._touch;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// The set of paths git tracks, one `ls-files` shared across the board via the range object. Used
|
|
381
|
+
// only where a range is EMPTY — see the note at the chgIdx computation. `null` = git could not
|
|
382
|
+
// answer, which callers must treat as "fall back", never as "nothing is tracked".
|
|
383
|
+
function trackedIn(root, range) {
|
|
384
|
+
if (range._tracked === undefined) {
|
|
385
|
+
const out = git(root, ['ls-files', '-z']);
|
|
386
|
+
range._tracked = out == null ? null : new Set(out.split('\0').filter(Boolean));
|
|
387
|
+
}
|
|
388
|
+
return range._tracked;
|
|
389
|
+
}
|
|
390
|
+
|
|
311
391
|
/*
|
|
312
392
|
* The earliest position in the range at which this initiative's implementation was touched, plus a
|
|
313
393
|
* count. A docs-only PR has no build commits, which makes every "before the build" rule vacuous —
|
|
@@ -510,10 +590,38 @@ function computeWorkflow(id, opts) {
|
|
|
510
590
|
// that is true of everything, which makes the marker noise — so the board passes the GATE's range
|
|
511
591
|
// separately and the marker is answered from that, or dropped when there is no such range.
|
|
512
592
|
const changeRange = (opts && opts.changeRange) || (range.wholeHistory ? null : range);
|
|
513
|
-
|
|
593
|
+
// A ZERO-COMMIT range contains nothing, so firstAppearance's per-path fallback — a full-history
|
|
594
|
+
// `git log --diff-filter=A`, once per initiative on any full clone whose base..HEAD is empty
|
|
595
|
+
// (HEAD level with main) — can only conclude what one shared `ls-files` already says: a tracked
|
|
596
|
+
// record predates the range (-1), an on-disk-untracked one is uncommitted (Infinity). The gate's
|
|
597
|
+
// range — non-empty — keeps the fallback, which is what tells "added before the range" from
|
|
598
|
+
// "never committed" for O1/O2.
|
|
599
|
+
const emptyChange = !!changeRange && changeRange.ok && changeRange.shas.length === 0;
|
|
600
|
+
const tracked = emptyChange ? trackedIn(root, changeRange) : null;
|
|
601
|
+
const chgIdx = !changeRange ? null
|
|
602
|
+
: tracked ? (tracked.has(recRel) ? -1 : fs.existsSync(recAbs) ? Infinity : null)
|
|
603
|
+
: firstAppearance(root, changeRange, recRel);
|
|
514
604
|
const inChange = !!changeRange && chgIdx !== -1 && chgIdx !== null && chgIdx !== undefined;
|
|
515
605
|
const lastActivity = (() => {
|
|
516
606
|
const paths = [recRel, fm.prd_path, fm.rubric_path].filter(Boolean);
|
|
607
|
+
// The board supplies the trunk range, whose single pass already holds every commit's paths and
|
|
608
|
+
// dates — so the answer is a map lookup, not a subprocess. The trunk alone would demote
|
|
609
|
+
// branch-fresh work (`git log -1` reads from HEAD, which sees branch-local commits the trunk
|
|
610
|
+
// range excludes), so the gate's base..HEAD range is overlaid, max date per path. The
|
|
611
|
+
// subprocess survives only where no whole-history range was supplied — the single-ladder call.
|
|
612
|
+
// One deliberate difference from the per-path subprocess: a true merge lists no files in the
|
|
613
|
+
// single pass, so a merge that merely reconciles histories no longer counts as activity on the
|
|
614
|
+
// paths it carried — the date it stamped was the sync's, not the work's.
|
|
615
|
+
if (opts && opts.range && range.ok && range.wholeHistory) {
|
|
616
|
+
const trunk = touchIndex(range);
|
|
617
|
+
const branch = changeRange && changeRange.ok ? touchIndex(changeRange) : null;
|
|
618
|
+
let best = null;
|
|
619
|
+
for (const p of paths) {
|
|
620
|
+
const t = Math.max(trunk.get(p) || 0, (branch && branch.get(p)) || 0);
|
|
621
|
+
if (t > 0 && (best === null || t > best)) best = t;
|
|
622
|
+
}
|
|
623
|
+
return best;
|
|
624
|
+
}
|
|
517
625
|
const out = git(root, ['log', '-1', '--format=%ct', '--', ...paths]);
|
|
518
626
|
const t = out && parseInt(out.trim(), 10);
|
|
519
627
|
return Number.isFinite(t) ? t : null;
|
|
@@ -589,8 +697,9 @@ function computeWorkflow(id, opts) {
|
|
|
589
697
|
const build = buildCommits(root, range, effLane, {
|
|
590
698
|
id,
|
|
591
699
|
// Where this initiative's own record first appears in the range — the earliest commit that could
|
|
592
|
-
// possibly be its build.
|
|
593
|
-
|
|
700
|
+
// possibly be its build. Already computed above as `recIdx`, from identical arguments.
|
|
701
|
+
// See the note in buildCommits' attribution loop.
|
|
702
|
+
recordIdx: recIdx,
|
|
594
703
|
deliveryIndex: opts && opts.deliveryIndex,
|
|
595
704
|
declaredDelivery: fm.delivered_by,
|
|
596
705
|
});
|
|
@@ -748,7 +857,7 @@ function computeWorkflow(id, opts) {
|
|
|
748
857
|
}
|
|
749
858
|
}
|
|
750
859
|
|
|
751
|
-
const runs =
|
|
860
|
+
const runs = cachedJsonl(path.join(L.runsDir, L.runsFile)).filter((r) => r && r.initiative_id === id);
|
|
752
861
|
const runsRel = path.relative(root, path.join(L.runsDir, L.runsFile));
|
|
753
862
|
|
|
754
863
|
// ── priors ─ O7: the episodic read is CITED, and the citation is checked ─────────────────────
|
|
@@ -874,7 +983,7 @@ function computeWorkflow(id, opts) {
|
|
|
874
983
|
steps.push(step('quality', 'quality', 'unknown', 'CI decides on the PR — this engine never claims a test result it did not run'));
|
|
875
984
|
|
|
876
985
|
// ── review ─ the Review Gate's verdict, where the lane has a rubric ──────────────────────────
|
|
877
|
-
const evals =
|
|
986
|
+
const evals = cachedJsonl(path.join(L.runsDir, 'evals.jsonl')).filter((e) => e && e.initiative_id === id);
|
|
878
987
|
{
|
|
879
988
|
/* The rung reads the RUBRIC, not the lane.
|
|
880
989
|
*
|
|
@@ -946,7 +1055,7 @@ function computeWorkflow(id, opts) {
|
|
|
946
1055
|
}
|
|
947
1056
|
|
|
948
1057
|
// ── accept ─ the human PM, final and non-delegable (D12) ─────────────────────────────────────
|
|
949
|
-
const accepted =
|
|
1058
|
+
const accepted = cachedJsonl(path.join(L.runsDir, 'acceptances.jsonl'))
|
|
950
1059
|
.filter((a) => a && a.initiative_id === id && a.accepted === true);
|
|
951
1060
|
{
|
|
952
1061
|
const blockers = steps.filter((s) => s.status === 'blocked').map((s) => s.key);
|
|
@@ -974,7 +1083,7 @@ function computeWorkflow(id, opts) {
|
|
|
974
1083
|
{
|
|
975
1084
|
const metric = String(fm.outcome_metric || '').trim();
|
|
976
1085
|
const horizon = String(fm.outcome_horizon || '').trim();
|
|
977
|
-
const checks =
|
|
1086
|
+
const checks = cachedJsonl(path.join(L.runsDir, 'outcome_checks.jsonl'))
|
|
978
1087
|
.filter((c) => c && c.initiative_id === id && ['validated', 'refuted', 'inconclusive'].includes(c.disposition));
|
|
979
1088
|
const latest = checks.length ? checks[checks.length - 1] : null;
|
|
980
1089
|
const today = new Date().toISOString().slice(0, 10);
|
|
@@ -364,16 +364,16 @@ function sweep(opts) {
|
|
|
364
364
|
}
|
|
365
365
|
}
|
|
366
366
|
|
|
367
|
-
// a declared delivery that does not check out
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
}
|
|
367
|
+
// a declared delivery that does not check out — read ONCE, the owed gate below reuses it: a
|
|
368
|
+
// second declaredDelivery call on the same inputs paid its git subprocesses twice per record
|
|
369
|
+
// for the same answer.
|
|
370
|
+
const dd = delivery.declaredDelivery(root, fm.delivered_by);
|
|
371
|
+
if (dd.declared && !dd.ok && !dd.unknown) {
|
|
372
|
+
findings.push(finding('unverifiable-delivery', {
|
|
373
|
+
id, file: recRel,
|
|
374
|
+
what: dd.reason,
|
|
375
|
+
evidence: `delivered_by: ${String(fm.delivered_by).slice(0, 100)}`,
|
|
376
|
+
}));
|
|
377
377
|
}
|
|
378
378
|
|
|
379
379
|
// ── owed ────────────────────────────────────────────────────────────────────────────────────
|
|
@@ -381,7 +381,7 @@ function sweep(opts) {
|
|
|
381
381
|
// the reason is written on each finding rather than only in this file's header — the person
|
|
382
382
|
// reading the output is the one who needs to know why the tool stopped.
|
|
383
383
|
const signal = del.ok ? delivery.attribution(del, id) : 'unknown';
|
|
384
|
-
const declaredOk =
|
|
384
|
+
const declaredOk = dd.ok === true;
|
|
385
385
|
const delivered = del.ok && (!!signal || declaredOk);
|
|
386
386
|
const runs = runsBy.get(id) || [];
|
|
387
387
|
|
|
@@ -436,19 +436,38 @@ function sweep(opts) {
|
|
|
436
436
|
};
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
// Cached the same way the engine caches them — the sweep derives over every record in one pass
|
|
440
|
-
|
|
439
|
+
// Cached the same way the engine caches them — the sweep derives over every record in one pass —
|
|
440
|
+
// and keyed the same way too: mtime+size over the documents (plus their directories, so a file
|
|
441
|
+
// added or removed re-lists), never the root alone. The sweep is CLI-today, but a copy of the
|
|
442
|
+
// engine's cache with a weaker key is exactly the two-places-one-rule drift this file exists to
|
|
443
|
+
// sweep for.
|
|
444
|
+
const _okr = new Map(); // root -> { stamp, files, index }
|
|
445
|
+
const _bud = new Map(); // root -> { stamp, value }
|
|
446
|
+
function statStamp(p) {
|
|
447
|
+
try { const s = fs.statSync(p); return `${s.mtimeMs}:${s.size}`; } catch { return 'absent'; }
|
|
448
|
+
}
|
|
449
|
+
function okrStamp(files) {
|
|
450
|
+
const dirs = [...new Set(files.map((f) => path.dirname(f)))];
|
|
451
|
+
return files.concat(dirs).map((p) => `${p}=${statStamp(p)}`).join('|');
|
|
452
|
+
}
|
|
441
453
|
function engineOkrIndex(root) {
|
|
442
|
-
|
|
443
|
-
|
|
454
|
+
const hit = _okr.get(root);
|
|
455
|
+
if (hit && hit.files.length && okrStamp(hit.files) === hit.stamp) return hit.index;
|
|
456
|
+
const files = anchors.listOkrFiles(root);
|
|
457
|
+
const entry = { stamp: okrStamp(files), files, index: anchors.parseOkrIndex(root) };
|
|
458
|
+
_okr.set(root, entry);
|
|
459
|
+
return entry.index;
|
|
444
460
|
}
|
|
445
|
-
const _bud = new Map();
|
|
446
461
|
function budgetsFor(root, L) {
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
462
|
+
const stamp = statStamp(L.budgets);
|
|
463
|
+
const hit = _bud.get(root);
|
|
464
|
+
if (!hit || hit.stamp !== stamp) {
|
|
465
|
+
let value;
|
|
466
|
+
try { value = { ok: true, text: fs.readFileSync(L.budgets, 'utf8'), path: path.relative(root, L.budgets) }; }
|
|
467
|
+
catch { value = { ok: false, text: '', path: path.relative(root, L.budgets) }; }
|
|
468
|
+
_bud.set(root, { stamp, value });
|
|
450
469
|
}
|
|
451
|
-
return _bud.get(root);
|
|
470
|
+
return _bud.get(root).value;
|
|
452
471
|
}
|
|
453
472
|
|
|
454
473
|
/*
|
|
@@ -740,8 +759,6 @@ function main(argv) {
|
|
|
740
759
|
|
|
741
760
|
if (json) {
|
|
742
761
|
process.stdout.write(JSON.stringify({ ...res, repairs }, null, 2) + '\n');
|
|
743
|
-
} else if (!fix || owedOnly || defectsOnly) {
|
|
744
|
-
process.stdout.write(render(res, { owedOnly, defectsOnly, all }));
|
|
745
762
|
} else {
|
|
746
763
|
process.stdout.write(render(res, { owedOnly, defectsOnly, all }));
|
|
747
764
|
}
|
|
@@ -64,7 +64,7 @@ A skill exists to **wring determinism out of a stochastic system**: it makes the
|
|
|
64
64
|
|
|
65
65
|
### Per-step completion criteria
|
|
66
66
|
|
|
67
|
-
Every procedural step ends on a **checkable done-condition** — the agent must be able to tell done from not-done. Where it matters, make the criterion **exhaustive**, not vague: *"every living mirror of the rule accounted for"*, not *"produce a change list"*. A fuzzy criterion invites **premature completion** — the agent stops when it feels done, not when it is. The [semantic-pass rule](/
|
|
67
|
+
Every procedural step ends on a **checkable done-condition** — the agent must be able to tell done from not-done. Where it matters, make the criterion **exhaustive**, not vague: *"every living mirror of the rule accounted for"*, not *"produce a change list"*. A fuzzy criterion invites **premature completion** — the agent stops when it feels done, not when it is. The [semantic-pass rule](/skills/evaluator.skill) ("enumerate the live-doc set, then read each for meaning — a grep is evidence, never done") is the exemplar this generalizes: it exists because a vague "sweep it" criterion let real stale mirrors survive five "clean" passes.
|
|
68
68
|
|
|
69
69
|
### Pruning — hunt these named failure modes
|
|
70
70
|
|
|
@@ -16,7 +16,7 @@ The first `/skills/domains/` skill: how copy work runs through PMOS's **content
|
|
|
16
16
|
— never the `no-initiative` hatch — but it iterates fast, so the lane contracts on a **brief**,
|
|
17
17
|
and a rubric is *recommended, not required* (a required one recreates fake-rubric pressure).
|
|
18
18
|
|
|
19
|
-
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/
|
|
19
|
+
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)).
|
|
20
20
|
|
|
21
21
|
## When to use vs. not
|
|
22
22
|
|
|
@@ -13,7 +13,7 @@ self-report (`agent_runs.human_corrections`) — pre-commit conversational steer
|
|
|
13
13
|
git-invisible and stays the self-report's lane; the **delta** between the two is the point
|
|
14
14
|
(KR-B consumes it).
|
|
15
15
|
|
|
16
|
-
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/
|
|
16
|
+
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)).
|
|
17
17
|
> Level 1 frontmatter above is the trigger; this Level 2 body is the full procedure.
|
|
18
18
|
|
|
19
19
|
## Non-negotiable stance
|
|
@@ -12,10 +12,10 @@ scope: read:okf, write:planning/design
|
|
|
12
12
|
|
|
13
13
|
This skill produces a design brief instance for one initiative. It is **leg 1** of the
|
|
14
14
|
[design loop](/okf/core/concepts/design-loop.md): the context handoff from Claude Code to
|
|
15
|
-
claude.ai/design. The brief is to design what the [PRD](/skills/
|
|
15
|
+
claude.ai/design. The brief is to design what the [PRD](/skills/prd.skill) is to build — the shared
|
|
16
16
|
definition of *what to design* and the hard constraints, agreed before any design or build starts.
|
|
17
17
|
|
|
18
|
-
> **Format note:** three-level progressive disclosure ([SKILL-FORMAT](/skills/
|
|
18
|
+
> **Format note:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)).
|
|
19
19
|
> Level 1 above is the trigger; this body is Level 2; rare variants go to Level 3 sub-files by name.
|
|
20
20
|
|
|
21
21
|
## When to use vs. not
|
|
@@ -34,11 +34,11 @@ definition of *what to design* and the hard constraints, agreed before any desig
|
|
|
34
34
|
[design-reference grilling rubric](/planning/evals/design-reference-eval-rubric.md). Anything beyond
|
|
35
35
|
what the PRD decided is still flagged back.
|
|
36
36
|
- **Do not use** for the reconciliation gate — classifying a returned design against real capability is the
|
|
37
|
-
[design-reconcile skill](/skills/
|
|
37
|
+
[design-reconcile skill](/skills/design-reconcile.skill) (leg 3).
|
|
38
38
|
|
|
39
39
|
## Procedure
|
|
40
40
|
|
|
41
|
-
0. **Grill before you brief** (the [prd skill](/skills/
|
|
41
|
+
0. **Grill before you brief** (the [prd skill](/skills/prd.skill)'s step-0 discipline, applied
|
|
42
42
|
to design intent): where the PM's aesthetic or structural intent is ambiguous, ask **one question
|
|
43
43
|
at a time with a recommended answer**, stop at unambiguous — no cap; a batch questionnaire is not
|
|
44
44
|
compliant. Look up what the environment answers (the capability ledger, the design system, the
|
|
@@ -17,7 +17,7 @@ realizes the reconciliation rule captured in the product's capability ledger (PM
|
|
|
17
17
|
[watermelon-flag](/okf/core/concepts/watermelon-flag.md) defense applied to UI — it catches a design that
|
|
18
18
|
*looks* functional but has no backend, before it becomes hollow, green-on-the-surface code.
|
|
19
19
|
|
|
20
|
-
> **Format note:** three-level progressive disclosure ([SKILL-FORMAT](/skills/
|
|
20
|
+
> **Format note:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)).
|
|
21
21
|
> Level 1 above is the trigger; this body is Level 2.
|
|
22
22
|
|
|
23
23
|
## Non-negotiable stance
|
|
@@ -17,7 +17,7 @@ and a timebox**, not requirements, and closes with a mandatory **disposition** i
|
|
|
17
17
|
verdict. It resolves the stress-test finding that pre-certainty work either got blocked, got a
|
|
18
18
|
fake rubric, or escaped the record entirely.
|
|
19
19
|
|
|
20
|
-
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/
|
|
20
|
+
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)).
|
|
21
21
|
> Level 1 above is the trigger; this body is Level 2.
|
|
22
22
|
|
|
23
23
|
## When to use vs. not
|
|
@@ -25,7 +25,7 @@ fake rubric, or escaped the record entirely.
|
|
|
25
25
|
- **Use** when the deliverable is an answer: "is X feasible?", "which approach?", "what should the
|
|
26
26
|
PRD even say?" — and you could not write honest done-criteria today.
|
|
27
27
|
- **Do not use** when you *can* write done-criteria — that is a `feature` initiative
|
|
28
|
-
([prd skill](/skills/
|
|
28
|
+
([prd skill](/skills/prd.skill)). A spike that is secretly a small feature corrupts the lane.
|
|
29
29
|
- **Do not use** for urgent production fixes — that is the `incident` lane.
|
|
30
30
|
- A spike is still **anchored work** (D21): no anchor — a parent KR, or a declared health budget
|
|
31
31
|
`health:<id>` (D59) — no spike.
|
|
@@ -38,7 +38,7 @@ fake rubric, or escaped the record entirely.
|
|
|
38
38
|
- Tool form: `create_initiative({id, title, parent_kr, type: 'discovery'})`. The argument keeps
|
|
39
39
|
its name. Read it as "record the equivalent in file-mode state" (AGENTS.md).
|
|
40
40
|
1b. **Existing surface? Understand it first.** If answering the question honestly requires reading
|
|
41
|
-
code that already exists, run an [explain-surface](/
|
|
41
|
+
code that already exists, run an [explain-surface](/skills/explain-surface.skill) pass on
|
|
42
42
|
that region — in a fresh context, **before** writing the brief. A spike aimed at a system the PM
|
|
43
43
|
does not understand produces a learning they cannot act on. Greenfield → skip.
|
|
44
44
|
2. **Write the spike brief** from
|
|
@@ -12,11 +12,11 @@ scope: read:okf, write:planning/evals
|
|
|
12
12
|
|
|
13
13
|
This skill produces an eval rubric instance for one initiative. In eval-driven PM the rubric **is the
|
|
14
14
|
spec made measurable** ([eval-driven-pm](/okf/core/concepts/eval-driven-pm.md)): it is authored
|
|
15
|
-
*before* the build, alongside the [PRD](/skills/
|
|
15
|
+
*before* the build, alongside the [PRD](/skills/prd.skill), and it defines how output will pass
|
|
16
16
|
through the [three-tier gate](/okf/core/concepts/output-eval.md).
|
|
17
17
|
|
|
18
18
|
> **Format note:** three-level progressive disclosure
|
|
19
|
-
> ([SKILL-FORMAT](/skills/
|
|
19
|
+
> ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)). Level 1 frontmatter above is the trigger and
|
|
20
20
|
> carries only `name` + `description`. Note: the *rubric instances* this skill produces carry their
|
|
21
21
|
> own frontmatter (including `eval_type`, below) — that is the rubric's frontmatter, not this skill
|
|
22
22
|
> file's.
|
|
@@ -79,7 +79,7 @@ Before any work begins, the generator and evaluator **agree on done-criteria**
|
|
|
79
79
|
contract stage of the [SDLC loop](/okf/core/concepts/sdlc-loop.md). A rubric is incomplete without
|
|
80
80
|
one.
|
|
81
81
|
|
|
82
|
-
**Reach the criteria by grilling, not assumption** (the [prd skill](/skills/
|
|
82
|
+
**Reach the criteria by grilling, not assumption** (the [prd skill](/skills/prd.skill)'s step-0
|
|
83
83
|
discipline applies here too): where the PM's intent is ambiguous, ask **one question at a time with a
|
|
84
84
|
recommended answer**, stop at unambiguous — no cap, and a batch questionnaire is not compliant. If a
|
|
85
85
|
fact can be found by exploring the environment, look it up rather than asking — the decisions are the
|
|
@@ -129,7 +129,7 @@ construction; no per-contract justification needed beyond naming the doc set.
|
|
|
129
129
|
|
|
130
130
|
**Dimensions must be evidence-citable.** The Review Gate scores each dimension with a verbatim
|
|
131
131
|
quote or precise pointer from the artifact as evidence (an unevidenced dimension is `unverified`,
|
|
132
|
-
scored ≤ 0.5 — see [evaluator.skill](/
|
|
132
|
+
scored ≤ 0.5 — see [evaluator.skill](/skills/evaluator.skill)). Write every dimension as an
|
|
133
133
|
**observable outcome a quote can prove** — "the dry-run verdict names its exemplars" — never an
|
|
134
134
|
impression ("the verdict feels thorough"). If you cannot imagine the evidence quote for a
|
|
135
135
|
dimension, rewrite the dimension
|
|
@@ -12,7 +12,7 @@ the **interactive agent runtime** — any capable coding agent; heavy judgment b
|
|
|
12
12
|
an Edge Function ([D09](/DECISIONS.md)/[D34](/DECISIONS.md), amended 2026-07-23). PMOS-self runs it in
|
|
13
13
|
Claude Code, but the runtime is not Claude-specific.
|
|
14
14
|
|
|
15
|
-
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/
|
|
15
|
+
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)). Level 1
|
|
16
16
|
> frontmatter above is the trigger. This Level 2 body is the full procedure.
|
|
17
17
|
|
|
18
18
|
## Non-negotiable stance
|
|
@@ -21,7 +21,7 @@ accrues **cognitive debt** and makes the non-delegable Acceptance Gate ([D12](/D
|
|
|
21
21
|
*formally* human — the PM as their own [watermelon](/okf/core/concepts/watermelon-flag.md)
|
|
22
22
|
([skills-and-understanding](/planning/research/skills-and-understanding-research.md)).
|
|
23
23
|
|
|
24
|
-
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/
|
|
24
|
+
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)).
|
|
25
25
|
> Level 1 frontmatter above is the trigger; this Level 2 body is the full procedure.
|
|
26
26
|
|
|
27
27
|
## Non-negotiable stance
|
|
@@ -17,13 +17,13 @@ and it costs more there. A misread at acceptance costs one rejected run; a misre
|
|
|
17
17
|
inherited by the contract, the build, the rubric and the evaluator, and **every gate passes over it**.
|
|
18
18
|
A [watermelon](/okf/core/concepts/watermelon-flag.md) planted at the root.
|
|
19
19
|
|
|
20
|
-
The gap is sharpest in the [prd skill](/skills/
|
|
20
|
+
The gap is sharpest in the [prd skill](/skills/prd.skill)'s own grilling rule — *"if a fact can
|
|
21
21
|
be found by exploring the environment, look it up rather than asking."* That rule is right, and its
|
|
22
22
|
side effect is that **the agent acquires the understanding and the PM does not.**
|
|
23
23
|
|
|
24
|
-
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/
|
|
24
|
+
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)).
|
|
25
25
|
> L1 above is the trigger; this L2 body is the procedure.
|
|
26
|
-
> **Not [`ingest-repo`](/
|
|
26
|
+
> **Not [`ingest-repo`](/skills/ingest-repo.skill).** That builds an *agent-facing*,
|
|
27
27
|
> whole-repo, one-time OKF reference layer — a capability ledger is a lookup table, not a teaching
|
|
28
28
|
> artifact. This is PM-facing, one region, per-initiative, and pedagogically ordered. Read whatever
|
|
29
29
|
> layer `ingest-repo` produced as substrate; **treat the code as ground truth.**
|
|
@@ -74,8 +74,8 @@ explainer output decayed to *never* by the second run.
|
|
|
74
74
|
|
|
75
75
|
| Subject shape | Organise by | Reuse |
|
|
76
76
|
|---|---|---|
|
|
77
|
-
| A **user-facing surface** | the surface: views, states, controls, current copy | [design-reconcile](/skills/
|
|
78
|
-
| A **subsystem / flow** | execution and logical flow, never file order | the [explain-run](/
|
|
77
|
+
| A **user-facing surface** | the surface: views, states, controls, current copy | [design-reconcile](/skills/design-reconcile.skill)'s capability ledger §A–D — every control **backed vs. watermelon** — and the product's design system ([D38](/DECISIONS.md), D60(c)) |
|
|
78
|
+
| A **subsystem / flow** | execution and logical flow, never file order | the [explain-run](/skills/explain-run.skill) convention |
|
|
79
79
|
| A **data model** | entities, their invariants, what a migration must preserve | `ingest-repo`'s per-table `Reference` docs |
|
|
80
80
|
| A **gate / policy surface** | the decision it makes, its fail-closed behaviour, its escape hatches | the gate's scripts + the decisions locking it |
|
|
81
81
|
| A **harness surface** (skill, protocol) | trigger → procedure → stance, and what depends on it | SKILL-FORMAT's three levels |
|
|
@@ -94,7 +94,7 @@ explainer output decayed to *never* by the second run.
|
|
|
94
94
|
- **Constraints** — what will **bite a change here**: coupling, invariants, gates, migrations,
|
|
95
95
|
backfill, fail-closed behaviour. The cheapest possible place to learn these.
|
|
96
96
|
- **Open questions** — what you **could not determine from the code**. Genuine unknowns, never
|
|
97
|
-
rhetorical ones. This section feeds the [prd skill](/skills/
|
|
97
|
+
rhetorical ones. This section feeds the [prd skill](/skills/prd.skill)'s grilling directly
|
|
98
98
|
and is the honest alternative to a confident guess.
|
|
99
99
|
- **Quiz** — five questions (rules below), then a `---` divider, then **Answers**.
|
|
100
100
|
4. **Force counterfactuals in the BODY, not only in the quiz.** As you walk the region, repeatedly
|
|
@@ -88,7 +88,7 @@ field's "agents improving their own tools" loop while staying at the **L3 ceilin
|
|
|
88
88
|
- The sibling detect→draft→PR loop: [okf-author](/build-skills/okf-author.skill),
|
|
89
89
|
[D42](/okf/products/pmos/adr/d42-okf-author-on-demand.md).
|
|
90
90
|
- The evidence surfaces: recurring friction themes and self-reported context pressure, read from
|
|
91
|
-
file-mode state ([discoveries.md](/discoveries.md) and `runs.jsonl`; the hosted
|
|
91
|
+
file-mode state ([discoveries.md](/state/discoveries.md) and `runs.jsonl`; the hosted
|
|
92
92
|
`v_friction_recurrence` / `v_context_budget` views went with the backend in D66) plus
|
|
93
93
|
[eval-calibration.md](/planning/evals/eval-calibration.md).
|
|
94
94
|
- The cadence it serves: [D23](/okf/products/pmos/adr/d23-quarterly-cadence.md) (harness-assumption
|
|
@@ -17,7 +17,7 @@ the stack, assuming wrong, producing work the PM must heavily correct — the no
|
|
|
17
17
|
ingest-repo goes **code → what the product *is***. It emits a **draft** OKF
|
|
18
18
|
reference layer + a repo registration and then **stops for PM approval**.
|
|
19
19
|
|
|
20
|
-
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/
|
|
20
|
+
> **Format:** three-level progressive disclosure ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)). L1
|
|
21
21
|
> frontmatter above is the trigger; this L2 body is the full procedure.
|
|
22
22
|
> **Where it runs:** in **the interactive agent runtime** (any capable coding agent; PMOS-self uses
|
|
23
23
|
> Claude Code), in the tenant's own conversation, read-only — never a server-side scanner
|
|
@@ -33,7 +33,7 @@ reference layer + a repo registration and then **stops for PM approval**.
|
|
|
33
33
|
var *names* (`.env.example`, config keys). **Secret-scan the repo content AND git history** (real
|
|
34
34
|
repos leak keys in committed code/history); if a secret value is found, **never** place it in a doc,
|
|
35
35
|
a log, or the registry — note only that a leak exists and where, by path, so the PM can rotate it.
|
|
36
|
-
- **Default-to-refute on capability** (the [design-reconcile](/skills/
|
|
36
|
+
- **Default-to-refute on capability** (the [design-reconcile](/skills/design-reconcile.skill)
|
|
37
37
|
stance applied to code): a control/route is **watermelon** until a concrete backend read/write path
|
|
38
38
|
is found in the code. Unbacked controls land in **§D**, not invented as capability.
|
|
39
39
|
- **Everything is a DRAFT ([D37](/okf/products/pmos/adr/d37-tenant-okf-db-native.md)/[D12](/okf/products/pmos/adr/d12-three-tier-eval-gate.md)).**
|
|
@@ -65,7 +65,7 @@ reference layer + a repo registration and then **stops for PM approval**.
|
|
|
65
65
|
**no** value; record only "possible secret at `<path>`" for the PM.
|
|
66
66
|
4. **Emit the reference layer** — via `save_okf_doc(..., status:'draft', product_slug:<slug>)`,
|
|
67
67
|
namespaced `products/<slug>/...`, **each body carrying situating context + the exact repo path(s)**
|
|
68
|
-
it was derived from (the [source-to-concept](/skills/
|
|
68
|
+
it was derived from (the [source-to-concept](/skills/source-to-concept.skill) rule + the
|
|
69
69
|
analyze-product-infra cited-line bar, applied to code). Minimum six kinds:
|
|
70
70
|
|
|
71
71
|
| Doc | OKF `type` | id (under `products/<slug>/`) | derived from |
|
|
@@ -78,7 +78,7 @@ reference layer + a repo registration and then **stops for PM approval**.
|
|
|
78
78
|
| ADR seed(s) | `ADR` | `adr/<slug>-<n>-<topic>` | README / ADRs / commit history |
|
|
79
79
|
|
|
80
80
|
5. **Bootstrap the per-tenant capability ledger** (the artifact
|
|
81
|
-
[design-reconcile](/skills/
|
|
81
|
+
[design-reconcile](/skills/design-reconcile.skill) consumes, [D50](/okf/products/pmos/adr/d50-design-loop-product-scoped.md)
|
|
82
82
|
pattern — generic method, per-tenant instance). Classify **every** user-facing control/route into
|
|
83
83
|
exactly one bucket; each §A/§B entry **names the concrete read/write path** in the code:
|
|
84
84
|
- **§A — read-backed:** maps to a real backend read (query/endpoint) — cite it.
|
|
@@ -111,8 +111,8 @@ reference layer + a repo registration and then **stops for PM approval**.
|
|
|
111
111
|
approve them in PR review — the file-mode form of draft→approved. (D37's database-native tenant OKF
|
|
112
112
|
and its `save_okf_doc` tool were retired by [D66](/DECISIONS.md).)
|
|
113
113
|
- The ledger method: [design-loop concept](/okf/core/concepts/design-loop.md),
|
|
114
|
-
[design-reconcile](/skills/
|
|
115
|
-
- Extraction discipline: [source-to-concept](/skills/
|
|
114
|
+
[design-reconcile](/skills/design-reconcile.skill), [watermelon-flag](/okf/core/concepts/watermelon-flag.md).
|
|
115
|
+
- Extraction discipline: [source-to-concept](/skills/source-to-concept.skill).
|
|
116
116
|
- Scan location: [D51](/okf/products/pmos/adr/d51-scan-location.md). There is no longer a central
|
|
117
117
|
registry to register into — the hosted `product_repos` table and `register_product_repo` went with
|
|
118
118
|
the backend (D66); the product repo that carries the kit IS the registration.
|
package/payload/skills/okr.skill
CHANGED
|
@@ -15,14 +15,14 @@ measurable key results, structured as a tree that initiatives and agent runs lad
|
|
|
15
15
|
operationalizes the [okr-tree](/okf/core/concepts/okr-tree.md) concept.
|
|
16
16
|
|
|
17
17
|
> **Format note:** three-level progressive disclosure
|
|
18
|
-
> ([SKILL-FORMAT](/skills/
|
|
18
|
+
> ([SKILL-FORMAT](/skills/SKILL-FORMAT.md)). Level 1 above is the trigger; this body is the
|
|
19
19
|
> procedure; rare variants go to Level 3 sub-files.
|
|
20
20
|
|
|
21
21
|
## When to use vs. not
|
|
22
22
|
|
|
23
23
|
- **Use** to set strategy for a planning period, or to re-anchor an initiative that has drifted from
|
|
24
24
|
its parent KR.
|
|
25
|
-
- **Do not use** to spec a single piece of work — that is the [PRD skill](/skills/
|
|
25
|
+
- **Do not use** to spec a single piece of work — that is the [PRD skill](/skills/prd.skill).
|
|
26
26
|
OKRs sit one level above initiatives.
|
|
27
27
|
|
|
28
28
|
## Procedure
|