@sabaiway/agent-workflow-kit 10.4.0 → 10.6.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +1 -1
  3. package/SKILL.md +1 -1
  4. package/bridges/antigravity-cli-bridge/SKILL.md +7 -1
  5. package/bridges/antigravity-cli-bridge/bin/agy-review.sh +69 -17
  6. package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +73 -2
  7. package/bridges/antigravity-cli-bridge/capability.json +2 -2
  8. package/bridges/antigravity-cli-bridge/references/review-prompt.md +3 -0
  9. package/bridges/codex-cli-bridge/SKILL.md +8 -1
  10. package/bridges/codex-cli-bridge/bin/codex-exec.sh +1 -1
  11. package/bridges/codex-cli-bridge/bin/codex-review-honesty.test.mjs +1 -1
  12. package/bridges/codex-cli-bridge/bin/codex-review.sh +89 -18
  13. package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +55 -2
  14. package/bridges/codex-cli-bridge/capability.json +2 -2
  15. package/capability.json +1 -1
  16. package/package.json +1 -1
  17. package/references/agents/review-lens.md +5 -3
  18. package/references/modes/agents.md +1 -1
  19. package/references/modes/core-evidence.md +1 -1
  20. package/references/modes/procedures.md +11 -5
  21. package/references/modes/recipes.md +2 -2
  22. package/references/modes/review-state.md +3 -3
  23. package/references/modes/set-recipe.md +4 -4
  24. package/references/modes/status.md +1 -1
  25. package/references/modes/velocity.md +1 -0
  26. package/references/templates/orchestration.json +1 -1
  27. package/tools/bridge-posture.mjs +48 -0
  28. package/tools/carriers.mjs +21 -9
  29. package/tools/cheap-agents-read.mjs +86 -24
  30. package/tools/cheap-agents.mjs +47 -7
  31. package/tools/commit-guard.mjs +4 -3
  32. package/tools/detect-backends.mjs +2 -2
  33. package/tools/direct-run.mjs +3 -0
  34. package/tools/dispatch-store-read.mjs +269 -0
  35. package/tools/dispatch-store.mjs +21 -357
  36. package/tools/fold-scope.mjs +5 -60
  37. package/tools/grounding.mjs +2 -2
  38. package/tools/held-session.mjs +165 -0
  39. package/tools/orchestration-config.mjs +19 -78
  40. package/tools/orchestration-readme.mjs +70 -0
  41. package/tools/plan-shape-cli.mjs +112 -0
  42. package/tools/plan-shape-facts.mjs +204 -0
  43. package/tools/plan-shape.mjs +348 -0
  44. package/tools/procedures.mjs +161 -31
  45. package/tools/recipes.mjs +60 -79
  46. package/tools/repo-lex.mjs +40 -0
  47. package/tools/review-roster-resolve.mjs +104 -0
  48. package/tools/review-roster.mjs +128 -0
  49. package/tools/review-rounds-cli.mjs +92 -0
  50. package/tools/review-rounds.mjs +115 -0
  51. package/tools/review-state.mjs +85 -6
  52. package/tools/set-recipe-roster.mjs +167 -0
  53. package/tools/set-recipe.mjs +80 -23
  54. package/tools/velocity-profile.mjs +8 -22
@@ -0,0 +1,269 @@
1
+ import { join, isAbsolute, normalize, sep } from 'node:path';
2
+ import { spawnSync } from 'node:child_process';
3
+ import {
4
+ validateDelegationRecord, canonicalDelegationDigest, allowedSuccessorKinds,
5
+ isThreadTerminalRecord,
6
+ } from './dispatch-record.mjs';
7
+ import { readRegularFileNoFollow } from './fs-read-nofollow.mjs';
8
+
9
+ export const DELEGATION_STORE_STOP = 'DELEGATION_STORE_STOP';
10
+ export const delegationStoreStop = (message) => Object.assign(new Error(`[agent-workflow-kit] ${message}`), { name: 'DelegationStoreStop', code: DELEGATION_STORE_STOP });
11
+ const stop = delegationStoreStop;
12
+
13
+ export const DELEGATION_STORE_BASENAME = 'agent-workflow-delegation.jsonl';
14
+ const GIT_MAX_BUFFER = 256 * 1024 * 1024;
15
+
16
+ const runGit = (args, cwd, spawn = spawnSync) => spawn('git', args, { cwd, maxBuffer: GIT_MAX_BUFFER, windowsHide: true });
17
+ const readGitLine = (args, cwd, spawn) => {
18
+ const result = runGit(args, cwd, spawn);
19
+ return result.error || result.status !== 0 ? null : result.stdout.toString('utf8').replace(/\r?\n$/, '');
20
+ };
21
+
22
+ export const resolveDelegationStorePath = (cwd, env = process.env) => {
23
+ if (env.AW_DELEGATION_STORE) {
24
+ if (!isAbsolute(env.AW_DELEGATION_STORE)) {
25
+ throw stop(`AW_DELEGATION_STORE must be an ABSOLUTE path (got "${env.AW_DELEGATION_STORE}") — a relative override resolves a different ledger from each worktree/cwd (fail closed)`);
26
+ }
27
+ const normalized = normalize(env.AW_DELEGATION_STORE);
28
+ if (normalized.endsWith(sep) || normalized.endsWith('/')) {
29
+ throw stop(`AW_DELEGATION_STORE must not end with a path separator (got "${env.AW_DELEGATION_STORE}") — a store is a file, not a directory (fail closed)`);
30
+ }
31
+ return normalized;
32
+ }
33
+ if (readGitLine(['rev-parse', '--is-inside-work-tree'], cwd) !== 'true') return null;
34
+ const commonDir = readGitLine(['rev-parse', '--path-format=absolute', '--git-common-dir'], cwd);
35
+ return commonDir == null ? null : join(commonDir, DELEGATION_STORE_BASENAME);
36
+ };
37
+
38
+ const parseDelegationLine = (line, index) => {
39
+ if (line.trim() === '') return { empty: true };
40
+ try {
41
+ const record = JSON.parse(line);
42
+ const verdict = validateDelegationRecord(record);
43
+ return verdict.ok
44
+ ? { record, recordLine: index + 1 }
45
+ : { reason: `line ${index + 1}: ${verdict.reason}` };
46
+ } catch {
47
+ return { reason: `line ${index + 1}: invalid JSON` };
48
+ }
49
+ };
50
+
51
+ export const parseDelegationStoreText = (raw) => {
52
+ const parsed = String(raw).split('\n').map(parseDelegationLine);
53
+ const records = parsed.filter((item) => item.record !== undefined).map((item) => item.record);
54
+ const recordLines = parsed.filter((item) => item.recordLine !== undefined).map((item) => item.recordLine);
55
+ const malformedReasons = parsed.filter((item) => item.reason !== undefined).map((item) => item.reason);
56
+ return { records, recordLines, malformed: malformedReasons.length, malformedReasons };
57
+ };
58
+
59
+ const emptyRead = (outcome) => ({ outcome, records: [], recordLines: [], malformed: 0, malformedReasons: [] });
60
+
61
+ export const readDelegationStore = (path, io = {}) => {
62
+ const read = readRegularFileNoFollow(path, io);
63
+ if (read.outcome === 'absent') return emptyRead('absent');
64
+ if (read.outcome === 'foreign') {
65
+ const reason = `the store is a ${read.className}, not a regular file — refusing to read it (fail closed)`;
66
+ return { ...emptyRead('error'), reason, readError: reason };
67
+ }
68
+ if (read.outcome === 'error') return { ...emptyRead('error'), reason: read.code, readError: read.code };
69
+ const parsed = parseDelegationStoreText(read.content);
70
+ return parsed.malformed > 0
71
+ ? { outcome: 'error', reason: parsed.malformedReasons[0], ...parsed }
72
+ : { outcome: 'ok', ...parsed };
73
+ };
74
+
75
+ const describeGitFailure = (result) => {
76
+ if (result.error) return result.error.code ?? result.error.message;
77
+ const stderr = result.stderr?.toString('utf8').trim() ?? '';
78
+ if (stderr !== '') return stderr;
79
+ if (result.signal) return `signal ${result.signal}`;
80
+ return `exit ${result.status}`;
81
+ };
82
+
83
+ export const readHeadInstant = (cwd, spawn = spawnSync) => {
84
+ const inside = runGit(['rev-parse', '--is-inside-work-tree'], cwd, spawn);
85
+ if (inside.error || inside.status !== 0 || inside.stdout.toString('utf8').trim() !== 'true') {
86
+ return { state: 'error', reason: `cannot resolve the git work tree (${describeGitFailure(inside)})` };
87
+ }
88
+ const head = runGit(['log', '-1', '--format=%ct'], cwd, spawn);
89
+ const raw = head.error || head.status !== 0 ? null : head.stdout.toString('utf8').trim();
90
+ if (raw !== null && /^\d+$/.test(raw)) return { state: 'ok', seconds: Number(raw) };
91
+ const status = runGit(['status', '--porcelain=v2', '--branch', '--untracked-files=no'], cwd, spawn);
92
+ if (!status.error && status.status === 0 && status.stdout.toString('utf8').split(/\r?\n/u).includes('# branch.oid (initial)')) {
93
+ return { state: 'unborn' };
94
+ }
95
+ return { state: 'error', reason: `cannot read HEAD committer time (${describeGitFailure(head)})` };
96
+ };
97
+
98
+ const THREAD_KINDS = ['dispatch', 'return', 'fold', 'degrade'];
99
+
100
+ export const delegationThreadState = (records, nonce) => {
101
+ const thread = records.filter((record) => THREAD_KINDS.includes(record.kind) && record.nonce === nonce);
102
+ const last = thread.at(-1) ?? null;
103
+ const dispatch = thread.find((record) => record.kind === 'dispatch') ?? null;
104
+ const terminal = last !== null && isThreadTerminalRecord(last);
105
+ return {
106
+ records: thread,
107
+ dispatch,
108
+ return: thread.find((record) => record.kind === 'return') ?? null,
109
+ closure: thread.find((record) => record.kind === 'fold' || record.kind === 'degrade') ?? null,
110
+ last,
111
+ terminal,
112
+ open: dispatch !== null && !terminal,
113
+ };
114
+ };
115
+
116
+ const findRetryChainOrigin = (records, current, seen = new Set([current.nonce])) => {
117
+ if (current.retryOf === null) return current;
118
+ const prior = records.find((record) => record.kind === 'dispatch' && record.nonce === current.retryOf);
119
+ if (prior === undefined || seen.has(prior.nonce)) return current;
120
+ return findRetryChainOrigin(records, prior, new Set([...seen, prior.nonce]));
121
+ };
122
+
123
+ const describeClosure = (last) =>
124
+ last.kind === 'fold' ? 'its fold'
125
+ : last.kind === 'degrade' ? 'its recorded degrade'
126
+ : `a terminal return (outcome "${last.outcome}")`;
127
+
128
+ const WAVE_SCOPED_KINDS = ['dispatch', 'observation', 'degrade'];
129
+
130
+ export const delegationSemanticPreflight = ({ records, snapshot, storePath }) => {
131
+ const digest = canonicalDelegationDigest(snapshot);
132
+ if (records.some((record) => canonicalDelegationDigest(record) === digest)) {
133
+ throw stop(`refusing a canonical duplicate: ${storePath} already carries this exact record (${digest.slice(0, 12)}…), however its keys are ordered — a genuine new record carries new content or a new timestamp; nothing was written`);
134
+ }
135
+ const registration = records.find((record) => record.kind === 'pre-registration' && record.waveId === snapshot.waveId);
136
+ if (snapshot.kind === 'pre-registration' && registration !== undefined) {
137
+ throw stop(`refusing a second pre-registration: the wave "${snapshot.waveId}" is already registered and a registration is IMMUTABLE per wave — thresholds a wave was registered under can never move under its own observations; nothing was written`);
138
+ }
139
+ if (WAVE_SCOPED_KINDS.includes(snapshot.kind)) {
140
+ if (registration === undefined) {
141
+ throw stop(`refusing a ${snapshot.kind} that names the UNREGISTERED wave "${snapshot.waveId}" — acceptance is PRE-REGISTERED before the first observation it will count, so the thresholds can never be chosen after the fact; register the wave first; nothing was written`);
142
+ }
143
+ if (!registration.stepClasses.includes(snapshot.stepClass)) {
144
+ throw stop(`refusing a ${snapshot.kind}: step class "${snapshot.stepClass}" is not among the classes wave "${snapshot.waveId}" registered (${registration.stepClasses.join(' | ')}) — the registered set IS the acceptance set; nothing was written`);
145
+ }
146
+ }
147
+ const nonce = typeof snapshot.nonce === 'string' ? snapshot.nonce : null;
148
+ if (nonce === null) return;
149
+ const state = delegationThreadState(records, nonce);
150
+ if (snapshot.kind === 'dispatch') {
151
+ if (state.dispatch !== null) {
152
+ throw stop(`refusing a duplicate dispatch: nonce "${nonce}" already carries a dispatch — a nonce IS the thread identity and is minted once; nothing was written`);
153
+ }
154
+ if (snapshot.retryOf !== null) {
155
+ const prior = delegationThreadState(records, snapshot.retryOf);
156
+ if (prior.dispatch === null) {
157
+ throw stop(`refusing a retry: no dispatch for nonce "${snapshot.retryOf}" is in the store — a retry names the thread it retries; nothing was written`);
158
+ }
159
+ const successor = records.find((record) => record.kind === 'dispatch' && record.retryOf === snapshot.retryOf);
160
+ if (successor !== undefined) {
161
+ throw stop(`refusing a retry: thread "${snapshot.retryOf}" already has the retry successor "${successor.nonce}" — a thread is retried at most ONCE, or the recorded cap would bound only the chain's depth while its branching stayed free; nothing was written`);
162
+ }
163
+ if (!prior.terminal) {
164
+ throw stop(`refusing a retry: thread "${snapshot.retryOf}" is still OPEN — a thread is retried only after it closed (a success or acceptance-failure return stays live until its fold or degrade); nothing was written`);
165
+ }
166
+ if (prior.last.kind === 'fold') {
167
+ throw stop(`refusing a retry: thread "${snapshot.retryOf}" was closed by its fold — folding accepts the attempt into the tree, so a folded thread is never a retry origin whatever its return's outcome was; open a NEW thread instead; nothing was written`);
168
+ }
169
+ if (snapshot.retryIndex !== prior.dispatch.retryIndex + 1) {
170
+ throw stop(`refusing a retry: retryIndex ${snapshot.retryIndex} must be ${prior.dispatch.retryIndex + 1} — a retry increments its origin's index by exactly one, so the chain length is the index; nothing was written`);
171
+ }
172
+ if (snapshot.waveId !== prior.dispatch.waveId) {
173
+ throw stop(`refusing a retry: it names wave "${snapshot.waveId}" but its retry origin "${snapshot.retryOf}" was dispatched in wave "${prior.dispatch.waveId}" — a retry stays in its origin's wave, or one thread would count in two acceptance sets; nothing was written`);
174
+ }
175
+ if (snapshot.stepClass !== prior.dispatch.stepClass) {
176
+ throw stop(`refusing a retry: it declares step class "${snapshot.stepClass}" but its retry origin "${snapshot.retryOf}" was dispatched as "${prior.dispatch.stepClass}" — the pairing key is the step class, so a chain that changes it mid-way would split one attempt's accounting across two classes; nothing was written`);
177
+ }
178
+ const origin = findRetryChainOrigin(records, prior.dispatch);
179
+ if (snapshot.retryIndex > origin.retryCap) {
180
+ throw stop(`refusing a retry: retryIndex ${snapshot.retryIndex} exceeds the retryCap ${origin.retryCap} recorded on the thread's ORIGIN dispatch ("${origin.nonce}") — a fresh contract never manufactures a fresh retry budget; nothing was written`);
181
+ }
182
+ if (prior.return !== null && prior.return.outcome === 'contract-refusal' && snapshot.contractDigest === prior.dispatch.contractDigest) {
183
+ throw stop(`refusing a retry: it retries a contract-refusal thread and must carry a DIFFERENT contractDigest — an unchanged contract would only be refused again, and a retry loop on one contract is exactly what the cap exists to prevent; nothing was written`);
184
+ }
185
+ }
186
+ return;
187
+ }
188
+ if (state.dispatch === null) {
189
+ throw stop(`refusing a ${snapshot.kind}: no dispatch for nonce "${nonce}" is in the store — a thread opens with its dispatch, and a record that binds to nothing is never absorbed; nothing was written`);
190
+ }
191
+ if (snapshot.kind === 'return' && state.return !== null) {
192
+ throw stop(`refusing a second return: nonce "${nonce}" already carries a return (outcome "${state.return.outcome}") — one dispatch answers exactly once, so a stale return never lands; nothing was written`);
193
+ }
194
+ const allowed = allowedSuccessorKinds(state.last);
195
+ if (!allowed.includes(snapshot.kind)) {
196
+ throw stop(state.terminal
197
+ ? `refusing a ${snapshot.kind}: thread "${nonce}" is already closed by ${describeClosure(state.last)} — a closed thread never absorbs another record; nothing was written`
198
+ : `refusing a ${snapshot.kind}: nonce "${nonce}" carries no return to fold — the thread's last record is a ${state.last.kind}, whose only legal successors are ${allowed.join(' | ')}; nothing was written`);
199
+ }
200
+ if (snapshot.kind === 'return') {
201
+ for (const field of ['backend', 'contractDigest', 'preTreeDigest']) {
202
+ if (snapshot[field] !== state.dispatch[field]) {
203
+ throw stop(`refusing a return: ${field} "${snapshot[field]}" does not equal its dispatch's "${state.dispatch[field]}" — a return is bound to the dispatch it answers by nonce AND by identity; nothing was written`);
204
+ }
205
+ }
206
+ if (state.dispatch.baselineClean === false && snapshot.metric.eligible) {
207
+ throw stop(`refusing a return: its dispatch recorded baselineClean:false, so the metric is INELIGIBLE — the uncommitted-state fingerprint is blind to the index↔worktree split, so a dirty baseline cannot attribute bytes to this dispatch (D5); record eligible:false with ineligibleReason "dirty-baseline" (or the stricter reason this return's own fields substantiate); nothing was written`);
208
+ }
209
+ if (state.dispatch.baselineClean !== false && snapshot.metric.ineligibleReason === 'dirty-baseline') {
210
+ throw stop(`refusing a return: it claims ineligibleReason "dirty-baseline" while its dispatch recorded baselineClean:true — the override is unsubstantiated, and only the dispatch decides that fact; nothing was written`);
211
+ }
212
+ }
213
+ if (snapshot.kind === 'degrade') {
214
+ for (const field of ['waveId', 'stepClass']) {
215
+ if (snapshot[field] !== state.dispatch[field]) {
216
+ throw stop(`refusing a degrade: ${field} "${snapshot[field]}" does not equal its dispatch's "${state.dispatch[field]}" — a thread is closed inside the wave and step class it was dispatched in; nothing was written`);
217
+ }
218
+ }
219
+ }
220
+ if (snapshot.kind === 'fold') {
221
+ const target = records.find((record) => canonicalDelegationDigest(record) === snapshot.returnDigest);
222
+ if (target === undefined) {
223
+ throw stop(`refusing a fold: returnDigest ${snapshot.returnDigest.slice(0, 12)}… matches no record in the store — a fold binds an EXISTING return by its canonical digest; nothing was written`);
224
+ }
225
+ if (target.kind !== 'return') {
226
+ throw stop(`refusing a fold: returnDigest resolves to a ${target.kind} record, not a return — a fold folds a return; nothing was written`);
227
+ }
228
+ if (target.nonce !== nonce) {
229
+ throw stop(`refusing a fold: returnDigest resolves to the return of nonce "${target.nonce}", not this fold's "${nonce}" — a fold never reaches across threads; nothing was written`);
230
+ }
231
+ if (snapshot.treeDigestAtFold !== target.postTreeDigest) {
232
+ throw stop(`refusing a fold: treeDigestAtFold ${snapshot.treeDigestAtFold.slice(0, 12)}… does not equal the folded return's postTreeDigest ${target.postTreeDigest.slice(0, 12)}… — the tree moved between the return and the fold, so what was folded is not what was returned; nothing was written`);
233
+ }
234
+ }
235
+ };
236
+
237
+ export const auditDelegationStoreSemantics = ({ records = [], recordLines = [], storePath = '(store)' } = {}) =>
238
+ records.reduce((verdict, record, index) => {
239
+ if (!verdict.ok) return verdict;
240
+ try {
241
+ delegationSemanticPreflight({ records: records.slice(0, index), snapshot: record, storePath });
242
+ return verdict;
243
+ } catch (error) {
244
+ return { ok: false, line: recordLines[index] ?? index + 1, reason: error.message };
245
+ }
246
+ }, { ok: true });
247
+
248
+ export const readDelegationLedger = (cwd, env, deps = {}) => {
249
+ try {
250
+ const {
251
+ resolveStore = resolveDelegationStorePath,
252
+ readStore = readDelegationStore,
253
+ audit = auditDelegationStoreSemantics,
254
+ readHead = readHeadInstant,
255
+ } = deps;
256
+ const storePath = resolveStore(cwd, env);
257
+ if (storePath === null) return { state: 'error', reason: 'the delegation ledger path is unresolvable (fail closed)' };
258
+ const read = readStore(storePath);
259
+ if (read.outcome === 'absent') return { state: 'absent' };
260
+ if (read.outcome === 'error' || read.readError != null || read.malformed > 0) {
261
+ return { state: 'error', reason: read.reason ?? read.readError ?? read.malformedReasons[0] };
262
+ }
263
+ const audited = audit({ records: read.records, recordLines: read.recordLines, storePath });
264
+ if (!audited.ok) return { state: 'error', reason: `line ${audited.line}: ${audited.reason}` };
265
+ return { state: 'ok', records: read.records, head: readHead(cwd) };
266
+ } catch (error) {
267
+ return { state: 'error', reason: error.message };
268
+ }
269
+ };