@sabaiway/agent-workflow-kit 5.11.0 → 5.11.1

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 (59) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/SKILL.md +1 -1
  3. package/capability.json +1 -1
  4. package/package.json +1 -1
  5. package/references/hooks/gate-approve.mjs +13 -2
  6. package/references/hooks/state-block-guard.mjs +14 -2
  7. package/references/scripts/archive-changelog.mjs +14 -3
  8. package/references/scripts/archive-decisions.mjs +14 -3
  9. package/references/scripts/archive-issues.mjs +14 -3
  10. package/references/scripts/check-docs-size.mjs +14 -3
  11. package/references/scripts/migrate-gates.mjs +13 -2
  12. package/tools/ack-write.mjs +3 -3
  13. package/tools/autonomy-doctor.mjs +2 -3
  14. package/tools/bridge-settings.mjs +2 -3
  15. package/tools/cheap-agents.mjs +3 -3
  16. package/tools/commands.mjs +2 -3
  17. package/tools/commit-guard.mjs +3 -3
  18. package/tools/core-evidence.mjs +2 -3
  19. package/tools/coverage-check.mjs +2 -3
  20. package/tools/delegation.mjs +2 -3
  21. package/tools/detect-backends.mjs +2 -3
  22. package/tools/doc-parity.mjs +2 -3
  23. package/tools/family-registry.mjs +3 -3
  24. package/tools/flow-adoption-mint.mjs +70 -0
  25. package/tools/flow-append.mjs +309 -0
  26. package/tools/flow-chain-state.mjs +91 -0
  27. package/tools/flow-check.mjs +2 -3
  28. package/tools/flow-delta-proof.mjs +307 -0
  29. package/tools/flow-record.mjs +1 -1
  30. package/tools/flow-store-read.mjs +3 -3
  31. package/tools/flow-store.mjs +35 -812
  32. package/tools/flow-subset-budget.mjs +81 -0
  33. package/tools/flow-writer.mjs +3 -3
  34. package/tools/gate-hook.mjs +3 -3
  35. package/tools/gates-init.mjs +3 -3
  36. package/tools/grounding.mjs +2 -3
  37. package/tools/hide-footprint.mjs +2 -3
  38. package/tools/inject-methodology.mjs +2 -3
  39. package/tools/lens-region.mjs +2 -3
  40. package/tools/manifest/validate.mjs +2 -3
  41. package/tools/migrate-adr-store.mjs +3 -3
  42. package/tools/path-inventory.mjs +2 -3
  43. package/tools/procedures.mjs +3 -3
  44. package/tools/receipt-deadline.mjs +2 -3
  45. package/tools/recipes.mjs +2 -3
  46. package/tools/recommendations.mjs +3 -3
  47. package/tools/release-scan.mjs +2 -3
  48. package/tools/repo-search.mjs +2 -3
  49. package/tools/review-state.mjs +3 -3
  50. package/tools/run-gates.mjs +2 -3
  51. package/tools/sandbox-masks.mjs +3 -3
  52. package/tools/set-autonomy.mjs +2 -3
  53. package/tools/set-flow.mjs +3 -3
  54. package/tools/set-recipe.mjs +2 -3
  55. package/tools/setup-backends.mjs +3 -3
  56. package/tools/store-append.mjs +2 -2
  57. package/tools/uninstall.mjs +2 -3
  58. package/tools/velocity-profile.mjs +3 -3
  59. package/tools/worktrees.mjs +3 -3
@@ -0,0 +1,81 @@
1
+ // flow-subset-budget.mjs — the Decision-7/8 counting-context budget (Plan 4): the two caps, the ONE
2
+ // budget walk both consumers share (subsetAttemptState), the exhaustion remedy prose, and the
3
+ // under-lock gate the append lane runs. Split out of flow-store.mjs unchanged (baseline-practices
4
+ // tranche 2); the facade re-exports everything here except subsetAttemptGate, which crosses to
5
+ // flow-append.mjs alone and stays off the store's public surface.
6
+ //
7
+ // PURE over read results, beside flow-chain-state.mjs at the bottom of the store's five leaves: no
8
+ // store IO, no git, no fs, and the record vocabulary is its only tools sibling.
9
+
10
+ import { flowRecordKey, subsetFoldBatchDigest } from './flow-record.mjs';
11
+
12
+ // The waste bound is the CAP, not the prose (Decision 8): a counting context allows at most
13
+ // THREE red attempts (two blind + one diagnosed) on its own; past that, no diagnosis reopens it —
14
+ // only a recorded FRESH-EYES consult verdict does, one further attempt per consult.
15
+ export const SUBSET_ATTEMPT_MAX_REDS = 3;
16
+
17
+ // Past the SECOND red every further attempt at the key rides a recorded diagnosis — the
18
+ // obligation keys on REDS, never on the attempt index (a green history stays blind-legal).
19
+ export const SUBSET_ATTEMPT_DIAGNOSIS_REDS = 2;
20
+
21
+ // subsetAttemptState(records, probe) → { attempts, nextIndex, reds, credits, exhausted } — the
22
+ // ONE Decision-7/8 budget walk both consumers share (the locked gate below re-runs it under the
23
+ // lock; run-gates' pre-gate check reads it lock-free). The exhaustion ladder is PERMIT-based
24
+ // and foldBatch-GLOBAL (round-3 disposition): red counts stay per key, but permits and their
25
+ // consumption span EVERY subsetDigest of the round context — one consult verdict is exactly
26
+ // ONE further attempt across the whole foldBatch, whichever subset spends it. Consult identity
27
+ // {backend, nonce} is tracked STORE-WIDE before any relevance filtering, so a replay from
28
+ // another round (or any seen identity — pre-exhaustion or spent) never credits; a credit is
29
+ // granted only for a NEW identity whose {planId, cycle, stepId, round} digests to this
30
+ // foldBatch while SOME key of the foldBatch is base-exhausted at that point in raw order.
31
+ // EVERY attempt recorded past its own key's base budget consumes one credit, whatever its
32
+ // status; a tampered store that drove credits negative stays exhausted (fail closed).
33
+ export const subsetAttemptState = (records, probe) => {
34
+ const key = flowRecordKey({ kind: 'subset-attempt', ...probe });
35
+ const attempts = [];
36
+ const seenConsults = new Set();
37
+ const redsByKey = new Map();
38
+ let credits = 0;
39
+ const someKeyExhausted = () => [...redsByKey.values()].some((n) => n >= SUBSET_ATTEMPT_MAX_REDS);
40
+ for (const r of records) {
41
+ if (r.kind === 'subset-attempt' && r.foldBatch === probe.foldBatch) {
42
+ const rKey = flowRecordKey(r);
43
+ const priorReds = redsByKey.get(rKey) ?? 0;
44
+ if (priorReds >= SUBSET_ATTEMPT_MAX_REDS) credits -= 1;
45
+ if (r.status === 'red') redsByKey.set(rKey, priorReds + 1);
46
+ if (rKey === key) attempts.push(r);
47
+ } else if (r.kind === 'consult-attestation') {
48
+ const identity = JSON.stringify([r.backend, r.nonce]);
49
+ const relevant = subsetFoldBatchDigest({ planId: r.planId, cycle: r.cycle, stepId: r.stepId, round: r.round }) === probe.foldBatch;
50
+ if (relevant && !seenConsults.has(identity) && someKeyExhausted()) credits += 1;
51
+ seenConsults.add(identity);
52
+ }
53
+ }
54
+ const reds = redsByKey.get(key) ?? 0;
55
+ return {
56
+ attempts,
57
+ nextIndex: attempts.reduce((m, r) => Math.max(m, r.attemptIndex), 0) + 1,
58
+ reds,
59
+ credits,
60
+ exhausted: reds >= SUBSET_ATTEMPT_MAX_REDS && credits <= 0,
61
+ };
62
+ };
63
+
64
+ export const subsetExhaustionRemedy = 'the fresh-eyes lane reopens it (Decision 8 — never a human wait-state): dispatch a MANDATORY grounded bridge consult (a different model) carrying the full attempt/diagnosis trail; its recorded consult-attestation at this round context reopens exactly ONE further diagnosed attempt. Otherwise park the stuck work with its trail and switch to independent work; a fresh context opens with the next round (new foldBatch) or a declared pregateExclude change (new subsetDigest)';
65
+
66
+ // The under-lock rules the factory does NOT already enforce itself: the exhaustion ladder and
67
+ // the byte-distinct diagnosis (blind thrashing refuses; a NEW hypothesis proceeds). The
68
+ // monotonic index and the reds-based diagnosis REQUIREMENT live in the factory alone — it is
69
+ // the ONLY entry for this kind (the generic lane refuses it by name, round-9 fold), computes
70
+ // the index from the SAME locked snapshot this gate sees, and throws its own named stops first.
71
+ export const subsetAttemptGate = (records, snapshot) => {
72
+ const { attempts, reds, exhausted } = subsetAttemptState(records, snapshot);
73
+ if (exhausted) {
74
+ return { ok: false, reason: `this counting context already holds ${reds} red attempts — EXHAUSTED (two blind + one diagnosed, Decision 8) and no diagnosis reopens it; ${subsetExhaustionRemedy}` };
75
+ }
76
+ const prior = attempts.find((r) => r.attemptIndex === snapshot.attemptIndex - 1);
77
+ if (typeof snapshot.diagnosis === 'string' && prior != null && prior.diagnosis === snapshot.diagnosis) {
78
+ return { ok: false, reason: "the diagnosis is byte-identical to the prior attempt's — a diagnosed continuation states a NEW hypothesis (Decision 8); blind thrashing refuses" };
79
+ }
80
+ return { ok: true };
81
+ };
@@ -48,7 +48,7 @@
48
48
 
49
49
  import { readFileSync, lstatSync } from 'node:fs';
50
50
  import { join, dirname } from 'node:path';
51
- import { pathToFileURL, fileURLToPath } from 'node:url';
51
+ import { fileURLToPath } from 'node:url';
52
52
  import { createHash, randomBytes } from 'node:crypto';
53
53
  import {
54
54
  FLOW_SCHEMA_VERSION, CHAIN_KIND, canonicalFlowDigest, flowTreeIdentity,
@@ -59,6 +59,7 @@ import {
59
59
  appendFlowRecord, appendFlowRecordWithPreflight, mintAdoption, readPlanFrontmatterId,
60
60
  resolveRecordReference, priorChainTerminal,
61
61
  } from './flow-store.mjs';
62
+ import { isDirectRun } from './direct-run.mjs';
62
63
  import { readFileBytesNoFollow, gitLine } from './flow-store-read.mjs';
63
64
  import {
64
65
  resolveBase, computeTreeFingerprint, lexicalRepoRelative,
@@ -1256,8 +1257,7 @@ export const main = (argv, ctx = {}) => {
1256
1257
  }
1257
1258
  };
1258
1259
 
1259
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
1260
- if (isDirectRun) {
1260
+ if (isDirectRun(import.meta.url)) {
1261
1261
  const r = main(process.argv.slice(2));
1262
1262
  if (r.stdout) console.log(r.stdout);
1263
1263
  if (r.stderr) console.error(r.stderr);
@@ -41,7 +41,7 @@
41
41
 
42
42
  import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
43
43
  import { dirname, join, resolve } from 'node:path';
44
- import { fileURLToPath, pathToFileURL } from 'node:url';
44
+ import { fileURLToPath } from 'node:url';
45
45
  import {
46
46
  CLAUDE_DIR,
47
47
  EXPECTED_WORKFLOW_VERSION,
@@ -54,6 +54,7 @@ import {
54
54
  resolveEffectiveMode,
55
55
  } from './velocity-profile.mjs';
56
56
  import { assertDocsAiDeployment, writeDocsAiFileAtomic } from './atomic-write.mjs';
57
+ import { isDirectRun } from './direct-run.mjs';
57
58
  import { shellQuoteArg } from './review-state.mjs';
58
59
 
59
60
  const HERE = dirname(fileURLToPath(import.meta.url));
@@ -572,5 +573,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
572
573
  }
573
574
  };
574
575
 
575
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
576
- if (isDirectRun) process.exit(main(process.argv.slice(2)));
576
+ if (isDirectRun(import.meta.url)) process.exit(main(process.argv.slice(2)));
@@ -58,7 +58,8 @@
58
58
 
59
59
  import { existsSync, lstatSync, readFileSync } from 'node:fs';
60
60
  import { join, resolve, dirname } from 'node:path';
61
- import { fileURLToPath, pathToFileURL } from 'node:url';
61
+ import { fileURLToPath } from 'node:url';
62
+ import { isDirectRun } from './direct-run.mjs';
62
63
  import { discoverGateCandidates, EXPECTED_WORKFLOW_VERSION } from './velocity-profile.mjs';
63
64
  import { GATES_REL, validateDeclaration } from './run-gates.mjs';
64
65
  import { canonicalCheckerGates, coverageDeclarationDefects, isKitOwnedCheckerGate } from './gates-declaration.mjs';
@@ -795,5 +796,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
795
796
  }
796
797
  };
797
798
 
798
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
799
- if (isDirectRun) process.exit(main(process.argv.slice(2)));
799
+ if (isDirectRun(import.meta.url)) process.exit(main(process.argv.slice(2)));
@@ -31,8 +31,8 @@
31
31
  import { readFileSync, writeFileSync, lstatSync, realpathSync } from 'node:fs';
32
32
  import { resolve, join, relative, isAbsolute, dirname, basename, sep } from 'node:path';
33
33
  import { tmpdir } from 'node:os';
34
- import { pathToFileURL } from 'node:url';
35
34
  import { spawnSync } from 'node:child_process';
35
+ import { isDirectRun } from './direct-run.mjs';
36
36
  import { fail } from './orchestration-config.mjs';
37
37
  import { readRegularFileNoFollow } from './fs-read-nofollow.mjs';
38
38
  // (f) --autonomy (AD-044 Plan 3): the effective per-project autonomy policy for the facts payload.
@@ -466,8 +466,7 @@ export const main = (argv, ctx = {}) => {
466
466
  }
467
467
  };
468
468
 
469
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
470
- if (isDirectRun) {
469
+ if (isDirectRun(import.meta.url)) {
471
470
  const r = main(process.argv.slice(2));
472
471
  // Exact writes + a natural exit: console.log would append a stray newline to the
473
472
  // byte-exact facts payload, and process.exit() can truncate large piped stdout before Node
@@ -24,8 +24,8 @@ import {
24
24
  } from 'node:fs';
25
25
  import { join, resolve, relative, isAbsolute, basename } from 'node:path';
26
26
  import { execFileSync } from 'node:child_process';
27
- import { pathToFileURL } from 'node:url';
28
27
  import os from 'node:os';
28
+ import { isDirectRun } from './direct-run.mjs';
29
29
  import {
30
30
  KIT_OWN_PATHS, KNOWN_FOOTPRINT, FOOTPRINT_STOP, stop,
31
31
  normalizeSlashes, isDirPattern, patternToProbe, expandGlob, matchesKnownGlob,
@@ -584,5 +584,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
584
584
  }
585
585
  };
586
586
 
587
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
588
- if (isDirectRun) process.exit(main(process.argv.slice(2)));
587
+ if (isDirectRun(import.meta.url)) process.exit(main(process.argv.slice(2)));
@@ -26,6 +26,7 @@
26
26
  // ONLY when its content normalize-matches a KNOWN PRIOR canonical fragment (drift-guarded, append-only
27
27
  // per descriptor). A customized slot never matches → preserved verbatim + a read-only upgrade advisory.
28
28
 
29
+ import { isDirectRun } from './direct-run.mjs';
29
30
  import { normalizeCanonical } from './orchestration-config.mjs';
30
31
 
31
32
  export const START_MARKER = '<!-- workflow:methodology:start -->';
@@ -599,9 +600,7 @@ export const runCli = async (argv, deps = {}) => {
599
600
  }
600
601
  };
601
602
 
602
- const { pathToFileURL } = await import('node:url');
603
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
604
- if (isDirectRun) {
603
+ if (isDirectRun(import.meta.url)) {
605
604
  const { code, stdout, stderr } = await runCli(process.argv.slice(2));
606
605
  if (stdout) process.stdout.write(stdout);
607
606
  if (stderr) process.stderr.write(stderr);
@@ -27,6 +27,7 @@
27
27
  //
28
28
  // Pure string functions (fs only in the CLI); dependency-free, Node >= 22.
29
29
 
30
+ import { isDirectRun } from './direct-run.mjs';
30
31
  import { normalizeCanonical } from './orchestration-config.mjs';
31
32
 
32
33
  // The deployed-heading matcher (prefix, like the historical extractLensBlock: robust to a future
@@ -386,6 +387,4 @@ export const runCli = async (argv, deps = {}) => {
386
387
  return 0;
387
388
  };
388
389
 
389
- const { pathToFileURL } = await import('node:url');
390
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
391
- if (isDirectRun) process.exitCode = await runCli(process.argv.slice(2));
390
+ if (isDirectRun(import.meta.url)) process.exitCode = await runCli(process.argv.slice(2));
@@ -20,7 +20,7 @@
20
20
 
21
21
  import { readFileSync, existsSync } from 'node:fs';
22
22
  import { resolve, join, isAbsolute } from 'node:path';
23
- import { pathToFileURL } from 'node:url';
23
+ import { isDirectRun } from '../direct-run.mjs';
24
24
 
25
25
  export const FAMILY = 'agent-workflow';
26
26
  export const SUPPORTED_SCHEMA = 1;
@@ -765,5 +765,4 @@ const main = (argv) => {
765
765
  if (strict && notValid > 0) process.exit(1);
766
766
  };
767
767
 
768
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
769
- if (isDirectRun) main(process.argv.slice(2));
768
+ if (isDirectRun(import.meta.url)) main(process.argv.slice(2));
@@ -33,7 +33,7 @@
33
33
 
34
34
  import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSync, chmodSync } from 'node:fs';
35
35
  import { join, resolve, dirname, relative, isAbsolute } from 'node:path';
36
- import { fileURLToPath, pathToFileURL } from 'node:url';
36
+ import { fileURLToPath } from 'node:url';
37
37
  import { spawnSync } from 'node:child_process';
38
38
  import { tmpdir } from 'node:os';
39
39
  import { writeContainedFileAtomic, assertDocsAiDeployment } from './atomic-write.mjs';
@@ -47,6 +47,7 @@ import {
47
47
  defaultRegenerateIndex,
48
48
  runCli as runArchiveDecisions,
49
49
  } from '../references/scripts/archive-decisions.mjs';
50
+ import { isDirectRun } from './direct-run.mjs';
50
51
  import { surveyAdrLayoutStrict, ADR_LAYOUT_PATHS } from './family-registry.mjs';
51
52
 
52
53
  const HERE = dirname(fileURLToPath(import.meta.url));
@@ -463,5 +464,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
463
464
  }
464
465
  };
465
466
 
466
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
467
- if (isDirectRun) process.exitCode = main();
467
+ if (isDirectRun(import.meta.url)) process.exitCode = main();
@@ -46,7 +46,7 @@
46
46
 
47
47
  import { openSync, fstatSync, readSync, closeSync, opendirSync, realpathSync, lstatSync, constants } from 'node:fs';
48
48
  import { resolve, relative, isAbsolute, sep, dirname } from 'node:path';
49
- import { pathToFileURL } from 'node:url';
49
+ import { isDirectRun } from './direct-run.mjs';
50
50
 
51
51
  import {
52
52
  UsageError,
@@ -512,5 +512,4 @@ const emitResult = (r) => {
512
512
  process.exitCode = r.code;
513
513
  };
514
514
 
515
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
516
- if (isDirectRun) emitResult(main(process.argv.slice(2)));
515
+ if (isDirectRun(import.meta.url)) emitResult(main(process.argv.slice(2)));
@@ -20,11 +20,12 @@
20
20
  import { readFileSync, lstatSync } from 'node:fs';
21
21
  import { homedir } from 'node:os';
22
22
  import { join, dirname } from 'node:path';
23
- import { pathToFileURL, fileURLToPath } from 'node:url';
23
+ import { fileURLToPath } from 'node:url';
24
24
  import { detectBackends, wrapperCmdFor, wrapperContractFor } from './detect-backends.mjs';
25
25
  // The host-level bridge-settings registry (manifest-as-source) + its allowed-value labels. READ-ONLY
26
26
  // core only — never the writer — so this read-only advisor never imports the atomic-write core.
27
27
  import { loadRegistry, allowedLabel } from './bridge-settings-read.mjs';
28
+ import { isDirectRun } from './direct-run.mjs';
28
29
  import { ACTIVITIES, resolveActivityRecipe, planRecipe } from './recipes.mjs';
29
30
  import { resolveEngineDir, readEngineFragment, PROCEDURES_FRAGMENT_REL } from './engine-source.mjs';
30
31
  // The plan-in-flight detector (AD-038) — imported from the plan-files.mjs LEAF (read-only fs by
@@ -586,8 +587,7 @@ export const main = (argv, ctx = {}) => {
586
587
  }
587
588
  };
588
589
 
589
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
590
- if (isDirectRun) {
590
+ if (isDirectRun(import.meta.url)) {
591
591
  const r = main(process.argv.slice(2));
592
592
  if (r.stdout) console.log(r.stdout);
593
593
  if (r.stderr) console.error(r.stderr);
@@ -21,9 +21,9 @@
21
21
  // Node >= 22. No side effects on import (the isDirectRun idiom).
22
22
 
23
23
  import { join, dirname } from 'node:path';
24
- import { pathToFileURL } from 'node:url';
25
24
  import { createHash } from 'node:crypto';
26
25
  import { resolveReceiptsPath } from './core-evidence.mjs';
26
+ import { isDirectRun } from './direct-run.mjs';
27
27
  import { SAFE_NONCE_RE, findingManifestBasename, decodeFindingManifest } from './flow-record.mjs';
28
28
  import { readFileBytesNoFollow } from './flow-store-read.mjs';
29
29
 
@@ -254,8 +254,7 @@ export const main = async (argv, ctx = {}) => {
254
254
  }
255
255
  };
256
256
 
257
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
258
- if (isDirectRun) {
257
+ if (isDirectRun(import.meta.url)) {
259
258
  main(process.argv.slice(2)).then((r) => {
260
259
  if (r.stdout) process.stdout.write(r.stdout.endsWith('\n') ? r.stdout : `${r.stdout}\n`);
261
260
  if (r.stderr) process.stderr.write(r.stderr.endsWith('\n') ? r.stderr : `${r.stderr}\n`);
package/tools/recipes.mjs CHANGED
@@ -16,10 +16,10 @@
16
16
 
17
17
  import { existsSync, readFileSync } from 'node:fs';
18
18
  import { dirname, join, resolve } from 'node:path';
19
- import { pathToFileURL } from 'node:url';
20
19
  // The host-level bridge-settings snapshot (fact-only, best-effort). READ-ONLY core only — never the
21
20
  // writer — so this read-only advisor never pulls in the atomic-write core.
22
21
  import { settingsSnapshot, DEFAULT_BUNDLE_ROOT } from './bridge-settings-read.mjs';
22
+ import { isDirectRun } from './direct-run.mjs';
23
23
  import {
24
24
  detectBackends,
25
25
  wrapperCmdFor,
@@ -596,11 +596,10 @@ exclusive. Detection only — never writes, never commits, never runs a subscrip
596
596
  return 0;
597
597
  };
598
598
 
599
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
600
599
  // Natural exit via process.exitCode — never process.exit inside the async main (it would drop buffered
601
600
  // stdio writes on piped stderr), and never a TOP-LEVEL await here: orchestration-config.mjs statically
602
601
  // imports this module, so awaiting the dynamic import during our own evaluation would deadlock the cycle.
603
- if (isDirectRun) {
602
+ if (isDirectRun(import.meta.url)) {
604
603
  main(process.argv.slice(2)).then(
605
604
  (code) => {
606
605
  process.exitCode = code ?? 0;
@@ -35,7 +35,7 @@ import { readFileSync, readdirSync, lstatSync, existsSync } from 'node:fs';
35
35
  import { createHash } from 'node:crypto';
36
36
  import { homedir } from 'node:os';
37
37
  import { dirname, join, resolve } from 'node:path';
38
- import { fileURLToPath, pathToFileURL } from 'node:url';
38
+ import { fileURLToPath } from 'node:url';
39
39
  import {
40
40
  preflightVelocityProfile,
41
41
  planVelocityProfile,
@@ -51,6 +51,7 @@ import {
51
51
  import { loadAutonomy, isSparseSeedConfig, AUTONOMY_REL } from './autonomy-config.mjs';
52
52
  import { deriveDoctorPlan } from './autonomy-doctor.mjs';
53
53
  import { detectBackends, findOnPath } from './detect-backends.mjs';
54
+ import { isDirectRun } from './direct-run.mjs';
54
55
  import { ACTIVITIES, resolveActivityRecipe } from './recipes.mjs';
55
56
  import { surveyFamily, surveyGateHook, surveyAdrLayoutStrict } from './family-registry.mjs';
56
57
  import { probeSandboxMasks, needsMasksApply } from './sandbox-masks.mjs';
@@ -1534,8 +1535,7 @@ export const main = (argv, ctx = {}) => {
1534
1535
  }
1535
1536
  };
1536
1537
 
1537
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
1538
- if (isDirectRun) {
1538
+ if (isDirectRun(import.meta.url)) {
1539
1539
  const r = main(process.argv.slice(2));
1540
1540
  if (r.stdout) process.stdout.write(r.stdout.endsWith('\n') ? r.stdout : `${r.stdout}\n`);
1541
1541
  if (r.stderr) process.stderr.write(r.stderr.endsWith('\n') ? r.stderr : `${r.stderr}\n`);
@@ -12,7 +12,7 @@
12
12
 
13
13
  import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
14
14
  import { join, extname, basename, resolve } from 'node:path';
15
- import { pathToFileURL } from 'node:url';
15
+ import { isDirectRun } from './direct-run.mjs';
16
16
 
17
17
  export const ALLOWLIST = [
18
18
  'codex-cli-bridge',
@@ -274,5 +274,4 @@ export const main = (argv) => {
274
274
  return reportOnly ? 0 : 1;
275
275
  };
276
276
 
277
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
278
- if (isDirectRun) process.exitCode = main(process.argv.slice(2));
277
+ if (isDirectRun(import.meta.url)) process.exitCode = main(process.argv.slice(2));
@@ -50,7 +50,7 @@
50
50
  import { openSync, fstatSync, readSync, closeSync, opendirSync, realpathSync, lstatSync, constants } from 'node:fs';
51
51
  import { createHash } from 'node:crypto';
52
52
  import { join, resolve, relative, isAbsolute, sep } from 'node:path';
53
- import { pathToFileURL } from 'node:url';
53
+ import { isDirectRun } from './direct-run.mjs';
54
54
 
55
55
  export const EXIT_OK = 0;
56
56
  export const EXIT_ERROR = 1;
@@ -590,5 +590,4 @@ const emitResult = (r) => {
590
590
  process.exitCode = r.code;
591
591
  };
592
592
 
593
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
594
- if (isDirectRun) emitResult(main(process.argv.slice(2)));
593
+ if (isDirectRun(import.meta.url)) emitResult(main(process.argv.slice(2)));
@@ -90,10 +90,11 @@
90
90
 
91
91
  import { lstatSync, readFileSync } from 'node:fs';
92
92
  import { join, dirname } from 'node:path';
93
- import { pathToFileURL, fileURLToPath } from 'node:url';
93
+ import { fileURLToPath } from 'node:url';
94
94
  import { spawnSync } from 'node:child_process';
95
95
  import { createHash } from 'node:crypto';
96
96
  import { detectBackends, READY } from './detect-backends.mjs';
97
+ import { isDirectRun } from './direct-run.mjs';
97
98
  import { resolveActivityRecipe, DISPLAY_ALIASES, requiredBackendsForConfiguredRecipe } from './recipes.mjs';
98
99
  import { CONFIG_REL, fail, loadConfig } from './orchestration-config.mjs';
99
100
  import { resolveFlowStorePath, readFlowStore, deriveFlowOwner, readPlanFrontmatterId } from './flow-store.mjs';
@@ -819,8 +820,7 @@ const emitResult = (r) => {
819
820
  process.exitCode = r.code;
820
821
  };
821
822
 
822
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
823
- if (isDirectRun) {
823
+ if (isDirectRun(import.meta.url)) {
824
824
  const argv = process.argv.slice(2);
825
825
  if (argv.includes('--await')) mainAwait(argv).then(emitResult);
826
826
  else emitResult(main(argv));
@@ -32,8 +32,8 @@
32
32
  import { readFileSync, lstatSync, unlinkSync } from 'node:fs';
33
33
  import { join } from 'node:path';
34
34
  import { spawnSync } from 'node:child_process';
35
- import { pathToFileURL } from 'node:url';
36
35
  import { createHash, randomUUID } from 'node:crypto';
36
+ import { isDirectRun } from './direct-run.mjs';
37
37
  import { computeTreeFingerprint } from './review-state.mjs';
38
38
  import { CONFIG_REL, loadConfig } from './orchestration-config.mjs';
39
39
  // The D3(a) final receipt rides the core-evidence SOLE WRITER (the sole-writer boundary — this
@@ -942,5 +942,4 @@ export const runCli = (argv, deps = {}) => {
942
942
  }
943
943
  };
944
944
 
945
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
946
- if (isDirectRun) process.exitCode = runCli(process.argv.slice(2));
945
+ if (isDirectRun(import.meta.url)) process.exitCode = runCli(process.argv.slice(2));
@@ -36,8 +36,9 @@
36
36
 
37
37
  import { lstatSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
38
38
  import { join, resolve, dirname } from 'node:path';
39
- import { fileURLToPath, pathToFileURL } from 'node:url';
39
+ import { fileURLToPath } from 'node:url';
40
40
  import { spawnSync } from 'node:child_process';
41
+ import { isDirectRun } from './direct-run.mjs';
41
42
  import { fail } from './orchestration-config.mjs';
42
43
  import { isNeverCommittableStat, shellQuoteArg } from './review-state.mjs';
43
44
  import { assertContainedRealPath } from './fs-safe.mjs';
@@ -366,5 +367,4 @@ const emitResult = (r) => {
366
367
  process.exitCode = r.code;
367
368
  };
368
369
 
369
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
370
- if (isDirectRun) emitResult(main(process.argv.slice(2)));
370
+ if (isDirectRun(import.meta.url)) emitResult(main(process.argv.slice(2)));
@@ -22,7 +22,6 @@
22
22
  // Dependency-free, Node >= 22. No side effects on import (the isDirectRun idiom).
23
23
 
24
24
  import { readFileSync, lstatSync } from 'node:fs';
25
- import { pathToFileURL } from 'node:url';
26
25
  import {
27
26
  AUTONOMY_REL,
28
27
  fail,
@@ -35,6 +34,7 @@ import {
35
34
  AUTONOMY_README,
36
35
  } from './autonomy-config.mjs';
37
36
  import { writeAutonomy as writeAutonomyFs } from './autonomy-write.mjs';
37
+ import { isDirectRun } from './direct-run.mjs';
38
38
 
39
39
  // ── argument parsing (usage errors → exit 2) ────────────────────────────────────────
40
40
 
@@ -186,8 +186,7 @@ export const main = (argv, ctx = {}) => {
186
186
  }
187
187
  };
188
188
 
189
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
190
- if (isDirectRun) {
189
+ if (isDirectRun(import.meta.url)) {
191
190
  const r = main(process.argv.slice(2));
192
191
  if (r.stdout) console.log(r.stdout);
193
192
  if (r.stderr) console.error(r.stderr);
@@ -30,7 +30,7 @@
30
30
 
31
31
  import { readFileSync, lstatSync } from 'node:fs';
32
32
  import { join, dirname, resolve } from 'node:path';
33
- import { pathToFileURL, fileURLToPath } from 'node:url';
33
+ import { fileURLToPath } from 'node:url';
34
34
  import { spawnSync } from 'node:child_process';
35
35
  import {
36
36
  CONFIG_REL,
@@ -44,6 +44,7 @@ import {
44
44
  FLOW_CANDIDATE_CLASSES,
45
45
  FLOW_SCHEMA_1_FIXTURE,
46
46
  } from './orchestration-config.mjs';
47
+ import { isDirectRun } from './direct-run.mjs';
47
48
  import { writeConfig as writeConfigFs } from './orchestration-write.mjs';
48
49
  import { loadDeclaration } from './run-gates.mjs';
49
50
  import { compareSemver } from './semver-lite.mjs';
@@ -456,8 +457,7 @@ export const main = (argv, ctx = {}) => {
456
457
  }
457
458
  };
458
459
 
459
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
460
- if (isDirectRun) {
460
+ if (isDirectRun(import.meta.url)) {
461
461
  const r = main(process.argv.slice(2));
462
462
  if (r.stdout) console.log(r.stdout);
463
463
  if (r.stderr) console.error(r.stderr);
@@ -23,8 +23,8 @@
23
23
 
24
24
  import { readFileSync, lstatSync } from 'node:fs';
25
25
  import { homedir } from 'node:os';
26
- import { pathToFileURL } from 'node:url';
27
26
  import { detectBackends } from './detect-backends.mjs';
27
+ import { isDirectRun } from './direct-run.mjs';
28
28
  import { resolveActivityRecipe, composeActiveRecipeLine } from './recipes.mjs';
29
29
  import { loadAutonomy, resolveAutonomy } from './autonomy-config.mjs';
30
30
  import {
@@ -230,8 +230,7 @@ export const main = (argv, ctx = {}) => {
230
230
  }
231
231
  };
232
232
 
233
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
234
- if (isDirectRun) {
233
+ if (isDirectRun(import.meta.url)) {
235
234
  const r = main(process.argv.slice(2));
236
235
  if (r.stdout) console.log(r.stdout);
237
236
  if (r.stderr) console.error(r.stderr);
@@ -28,9 +28,10 @@ import {
28
28
  chmodSync, readFileSync, realpathSync,
29
29
  } from 'node:fs';
30
30
  import { join, resolve, relative, dirname, isAbsolute } from 'node:path';
31
- import { fileURLToPath, pathToFileURL } from 'node:url';
31
+ import { fileURLToPath } from 'node:url';
32
32
  import os from 'node:os';
33
33
  import { KNOWN_BACKENDS, detectBackend, detectBackends, resolveDir, guideFor, READY } from './detect-backends.mjs';
34
+ import { isDirectRun } from './direct-run.mjs';
34
35
  import { copyTreeRefresh, linkManaged, isReadonlyWriteBoundary } from './fs-safe.mjs';
35
36
  import {
36
37
  READONLY_RERUN_HINT, WRAPPER_MODE, scanBundleOwnedDrift, scanWrapperParity, parityVerdict,
@@ -821,5 +822,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
821
822
  return worst;
822
823
  };
823
824
 
824
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
825
- if (isDirectRun) process.exit(main(process.argv.slice(2)));
825
+ if (isDirectRun(import.meta.url)) process.exit(main(process.argv.slice(2)));
@@ -1,6 +1,6 @@
1
1
  // store-append.mjs — the PARAMETERIZED lock/CAS serialized-append leaf (delegation Plan 1, Phase 2,
2
- // D12). Extracted VERBATIM from flow-store.mjs, which is now its first caller; the delegation store
3
- // (dispatch-store.mjs) is the second. No CLI, no side effects on import.
2
+ // D12). Extracted VERBATIM from the flow store, whose write door flow-append.mjs is now its first
3
+ // caller; the delegation store (dispatch-store.mjs) is the second. No CLI, no side effects on import.
4
4
  //
5
5
  // Why a leaf rather than an import of the flow appender: `appendFlowRecordWithPreflight` hardwires
6
6
  // the AW_FLOW_STORE seam and validateFlowRecord, so a second store could only reuse it by pretending
@@ -24,8 +24,8 @@
24
24
 
25
25
  import { existsSync, statSync, lstatSync, readlinkSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
26
26
  import { join, resolve, dirname, basename, isAbsolute } from 'node:path';
27
- import { pathToFileURL } from 'node:url';
28
27
  import os from 'node:os';
28
+ import { isDirectRun } from './direct-run.mjs';
29
29
  import { surveyFamily, surveyProject, FAMILY_MEMBERS, classifyMember, OK } from './family-registry.mjs';
30
30
  import { removeTreeManaged, unlinkManaged, MANAGED_LINK_CONFLICT } from './fs-safe.mjs';
31
31
  import { deriveLinks } from './setup-backends.mjs';
@@ -606,8 +606,7 @@ const main = (argv) => {
606
606
  }
607
607
  };
608
608
 
609
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
610
- if (isDirectRun) {
609
+ if (isDirectRun(import.meta.url)) {
611
610
  try {
612
611
  main(process.argv.slice(2));
613
612
  } catch (err) {
@@ -1,7 +1,7 @@
1
1
  import { existsSync, lstatSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
2
  import { basename, dirname, join, relative, resolve } from 'node:path';
3
3
  import { homedir, tmpdir } from 'node:os';
4
- import { fileURLToPath, pathToFileURL } from 'node:url';
4
+ import { fileURLToPath } from 'node:url';
5
5
  // The --autonomy render reads the per-project autonomy policy through the read-only autonomy core
6
6
  // (AD-044). This file is the family's one .claude/settings.json writer, so the policy render lives here;
7
7
  // it never imports the policy fs-writer (autonomy-write.mjs) — the render owns the settings file, not
@@ -10,6 +10,7 @@ import { AUTONOMY_REL, loadAutonomy, resolveAutonomy, COMMAND_REDLINES } from '.
10
10
  // The bridge-wrappers tier's placement probe (AD-044 Plan 4, Decision 2): a tier entry derives ONLY
11
11
  // for a PLACED bridge wrapper — findOnPath is the same read-only PATH scan the backend detector uses.
12
12
  import { findOnPath } from './detect-backends.mjs';
13
+ import { isDirectRun } from './direct-run.mjs';
13
14
  import { compareSemver } from './semver-lite.mjs';
14
15
  // The declared-path resolution + segment containment the allowWrite degrade shares with the
15
16
  // advisor's worktrees-dir convergence lane — ONE leaf, so the two readings cannot drift.
@@ -1820,5 +1821,4 @@ export const main = (argv = process.argv.slice(2), deps = {}) => {
1820
1821
  }
1821
1822
  };
1822
1823
 
1823
- export const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
1824
- if (isDirectRun) process.exit(main(process.argv.slice(2)));
1824
+ if (isDirectRun(import.meta.url)) process.exit(main(process.argv.slice(2)));