@open-tomato/rafa 0.32.0 → 0.33.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.
@@ -10,11 +10,12 @@ import {
10
10
  activeOutputMode,
11
11
  configFilePath,
12
12
  createTextOutput,
13
+ hubContextOf,
13
14
  isStampableStub,
14
15
  parseConfigText,
15
16
  readRafaBlocks,
16
17
  resolveConfig
17
- } from "./index-9aph6ft9.js";
18
+ } from "./index-dv0ycmp8.js";
18
19
  import {
19
20
  bless,
20
21
  merge
@@ -22,21 +23,23 @@ import {
22
23
  import {
23
24
  CommandExit,
24
25
  commandSpelling
25
- } from "./index-mt6hpsry.js";
26
+ } from "./index-3mzw3kzh.js";
26
27
  import {
27
28
  BUSY_TIMEOUT_MAX_MS,
28
29
  BUSY_TIMEOUT_MIN_MS,
29
30
  RAFA_INSTINCTS_PATH,
30
31
  STORE_BACKENDS,
31
32
  SYNC_STRATEGIES,
33
+ TEST_SCOPES,
32
34
  bundledBinDirectory,
33
35
  describeValue,
34
36
  isBusyTimeoutMs,
37
+ isGitHubLogin,
35
38
  isMapping,
36
39
  messageOf,
37
40
  resolveInstinctScopes,
38
41
  tierExists
39
- } from "./index-rs79dq9e.js";
42
+ } from "./index-fzhd3d4y.js";
40
43
  import {
41
44
  actionHash,
42
45
  triggerKey
@@ -249,7 +252,7 @@ import { Database } from "bun:sqlite";
249
252
 
250
253
  // package.json
251
254
  var name = "@open-tomato/rafa";
252
- var version = "0.32.0";
255
+ var version = "0.33.0";
253
256
 
254
257
  // src/cli/version.ts
255
258
  var RAFA_VERSION = version;
@@ -271,17 +274,7 @@ import {
271
274
  rmSync as rmSync3,
272
275
  writeFileSync as writeFileSync3
273
276
  } from "fs";
274
- import { basename, dirname as dirname5, isAbsolute as isAbsolute4, join as join7 } from "path";
275
-
276
- // src/next/hop-record.ts
277
- import { randomUUID as randomUUID2 } from "crypto";
278
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
279
- import { dirname as dirname4, join as join6 } from "path";
280
-
281
- // src/project/position.ts
282
- import { randomUUID } from "crypto";
283
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "fs";
284
- import { dirname as dirname3, join as join5 } from "path";
277
+ import { dirname as dirname5, join as join7 } from "path";
285
278
 
286
279
  // src/project/scope.ts
287
280
  import { existsSync as existsSync3, realpathSync as realpathSync2 } from "fs";
@@ -403,16 +396,20 @@ function selfAndAncestors(path) {
403
396
  const parent = dirname2(path);
404
397
  return parent === path ? [path] : [path, ...selfAndAncestors(parent)];
405
398
  }
406
- function walkUp(realStart, realHome, fs) {
399
+ var GIT_MARKER = ".git";
400
+ function walkUp(dirs, realHome, fs, stopAtTopLevel) {
407
401
  let passedUserConfig = false;
408
- for (const dir of selfAndAncestors(realStart)) {
409
- if (!fs.exists(configFilePath(dir)))
410
- continue;
411
- if (dir !== realHome)
412
- return { root: dir, passedUserConfig };
413
- passedUserConfig = true;
402
+ for (const dir of dirs) {
403
+ if (fs.exists(configFilePath(dir))) {
404
+ if (dir !== realHome)
405
+ return { root: dir, passedUserConfig, topLevel: null };
406
+ passedUserConfig = true;
407
+ }
408
+ if (stopAtTopLevel && fs.exists(join4(dir, GIT_MARKER))) {
409
+ return { root: null, passedUserConfig, topLevel: dir };
410
+ }
414
411
  }
415
- return { root: null, passedUserConfig };
412
+ return { root: null, passedUserConfig, topLevel: null };
416
413
  }
417
414
  function mainCheckoutAt(realStart, mainCheckout) {
418
415
  try {
@@ -430,6 +427,13 @@ function fallBack(realStart, realHome, fs, mainCheckout) {
430
427
  }
431
428
  return fs.exists(configFilePath(main)) ? { root: main, probed: main } : { root: null, probed: main };
432
429
  }
430
+ function walkAbove(lower, realHome, fs) {
431
+ if (lower.topLevel === null)
432
+ return lower;
433
+ const above = selfAndAncestors(lower.topLevel).slice(1);
434
+ const upper = walkUp(above, realHome, fs, false);
435
+ return { ...upper, passedUserConfig: lower.passedUserConfig || upper.passedUserConfig };
436
+ }
433
437
  function resolveScope(start, seams) {
434
438
  const { home, fs = DISK_FILE_SYSTEM, mainCheckout = mainCheckoutOf } = seams;
435
439
  requireAbsolute("start directory", start);
@@ -437,16 +441,29 @@ function resolveScope(start, seams) {
437
441
  const user = scopeAt(home);
438
442
  const realStart = realStartOf(start, fs);
439
443
  const realHome = realHomeOf(home, fs);
440
- const walk = walkUp(realStart, realHome, fs);
441
- const fallback = walk.root === null ? fallBack(realStart, realHome, fs, mainCheckout) : { root: walk.root, probed: null };
442
- if (fallback.root === null) {
444
+ const lower = walkUp(selfAndAncestors(realStart), realHome, fs, true);
445
+ const fallback = lower.root === null ? fallBack(realStart, realHome, fs, mainCheckout) : { root: lower.root, probed: null };
446
+ const walk = fallback.root === null ? walkAbove(lower, realHome, fs) : lower;
447
+ const root = fallback.root ?? walk.root;
448
+ if (root === null) {
443
449
  const passed = walk.passedUserConfig ? user.configFile : null;
444
450
  return { found: false, start, home, user, hint: initHint(start, passed, fallback.probed) };
445
451
  }
446
- return { found: true, root: fallback.root, home, project: scopeAt(fallback.root), user };
452
+ return { found: true, root, home, project: scopeAt(root), user };
447
453
  }
448
454
 
455
+ // src/loop/session-record-parse.ts
456
+ import { basename, isAbsolute as isAbsolute4 } from "path";
457
+
458
+ // src/next/hop-record.ts
459
+ import { randomUUID as randomUUID2 } from "crypto";
460
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
461
+ import { dirname as dirname4, join as join6 } from "path";
462
+
449
463
  // src/project/position.ts
464
+ import { randomUUID } from "crypto";
465
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, renameSync, rmSync, writeFileSync } from "fs";
466
+ import { dirname as dirname3, join as join5 } from "path";
450
467
  var POSITION_FILE = "position.json";
451
468
  function positionFilePath(root) {
452
469
  return join5(scopeAt(root).dir, POSITION_FILE);
@@ -625,11 +642,13 @@ function staleAgainst(record, position) {
625
642
  return position.home.board !== record.home.board || position.home.epic !== record.home.epic;
626
643
  }
627
644
 
628
- // src/loop/sessions.ts
645
+ // src/loop/session-record-parse.ts
629
646
  var SESSION_STATES = Object.freeze(["running", "paused", "stopped", "done"]);
630
- var RUNS_DIR = "runs";
647
+ var SESSION_PHASES = Object.freeze(["task", "wrap-up", "pull-request", "ci", "repair"]);
648
+ var DEFAULT_PHASE = "task";
631
649
  var RECORD_EXTENSION = ".json";
632
650
  var SESSION_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
651
+ var SESSION_STEP_KINDS = Object.freeze(["baseline", "task", "stage", "pre-wrap-up"]);
633
652
 
634
653
  class SessionRecordError extends Error {
635
654
  file;
@@ -639,40 +658,9 @@ class SessionRecordError extends Error {
639
658
  this.file = file;
640
659
  }
641
660
  }
642
-
643
- class SessionConflictError extends Error {
644
- conflicts;
645
- constructor(conflicts) {
646
- const named = conflicts.map((conflict) => `${conflict.record.sessionId} (${conflict.reason})`);
647
- super(`the plan's sessions refuse the run: ${named.join(", ")}`);
648
- this.name = "SessionConflictError";
649
- this.conflicts = conflicts;
650
- }
651
- }
652
-
653
- class SessionStateError extends Error {
654
- file;
655
- state;
656
- constructor(file, state, onlyFrom) {
657
- const acted = onlyFrom.length === 0 ? "no state" : onlyFrom.join(" or ");
658
- super(`session record ${file}: stores ${state}, and the change acts on ${acted} alone`);
659
- this.name = "SessionStateError";
660
- this.file = file;
661
- this.state = state;
662
- }
663
- }
664
661
  function isSessionId(value) {
665
662
  return SESSION_ID_PATTERN.test(value);
666
663
  }
667
- function runsDir(root) {
668
- return join7(scopeAt(root).dir, RUNS_DIR);
669
- }
670
- function sessionFilePath(root, sessionId) {
671
- if (!isSessionId(sessionId)) {
672
- throw new Error(`session record: unusable session id ${JSON.stringify(sessionId)}`);
673
- }
674
- return join7(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
675
- }
676
664
  function describeValue2(value) {
677
665
  return value === undefined ? "missing" : JSON.stringify(value) ?? String(value);
678
666
  }
@@ -710,6 +698,63 @@ function worktreeProblem(fields) {
710
698
  const worktree = field(fields, "worktree");
711
699
  return isText(worktree) && isAbsolute4(worktree) ? null : `worktree is ${describeValue2(worktree)}, expected an absolute path`;
712
700
  }
701
+ function isTextList(value) {
702
+ return Array.isArray(value) && value.every(isText);
703
+ }
704
+ function isFailure(value) {
705
+ return isObject(value) && isText(field(value, "file")) && isText(field(value, "name"));
706
+ }
707
+ function isFailureList(value) {
708
+ return Array.isArray(value) && value.every(isFailure);
709
+ }
710
+ function failureKey(failure) {
711
+ return JSON.stringify([failure.file, failure.name]);
712
+ }
713
+ function stepFailuresProblem(step, at) {
714
+ const failures = field(step, "failures");
715
+ const newFailures = field(step, "newFailures");
716
+ if (!isFailureList(failures))
717
+ return `${at}.failures is ${describeValue2(failures)}, expected a list of failing tests`;
718
+ if (!isFailureList(newFailures)) {
719
+ return `${at}.newFailures is ${describeValue2(newFailures)}, expected a list of failing tests`;
720
+ }
721
+ const known = new Set(failures.map(failureKey));
722
+ const stray = newFailures.find((failure) => !known.has(failureKey(failure)));
723
+ return stray === undefined ? null : `${at}.newFailures names ${describeValue2(stray)}, which ${at}.failures does not`;
724
+ }
725
+ function stepInterruptedProblem(step, at) {
726
+ if (!Object.hasOwn(step, "interrupted"))
727
+ return null;
728
+ const interrupted = field(step, "interrupted");
729
+ return interrupted === true ? null : `${at}.interrupted is ${describeValue2(interrupted)}, expected true or no key`;
730
+ }
731
+ function stepProblems(step, at) {
732
+ if (!isObject(step))
733
+ return [`${at} is ${describeValue2(step)}, expected a step`];
734
+ const kind = field(step, "kind");
735
+ const scope = field(step, "scope");
736
+ const command = field(step, "command");
737
+ const exitCode = field(step, "exitCode");
738
+ const summary = field(step, "summary");
739
+ const problems = [
740
+ SESSION_STEP_KINDS.includes(kind) ? null : `${at}.kind is ${describeValue2(kind)}, expected one of ${SESSION_STEP_KINDS.join(", ")}`,
741
+ TEST_SCOPES.includes(scope) || isTextList(scope) ? null : `${at}.scope is ${describeValue2(scope)}, expected one of ${TEST_SCOPES.join(", ")} or a list of paths`,
742
+ isTextList(command) && command.length > 0 ? null : `${at}.command is ${describeValue2(command)}, expected a non-empty list of arguments`,
743
+ typeof exitCode === "number" && Number.isSafeInteger(exitCode) ? null : `${at}.exitCode is ${describeValue2(exitCode)}, expected a whole number`,
744
+ summary === null || typeof summary === "string" ? null : `${at}.summary is ${describeValue2(summary)}, expected null or a string`,
745
+ stepFailuresProblem(step, at),
746
+ stepInterruptedProblem(step, at)
747
+ ];
748
+ return problems.filter((problem) => problem !== null);
749
+ }
750
+ function stepsProblems(fields) {
751
+ if (!Object.hasOwn(fields, "steps"))
752
+ return [];
753
+ const steps = field(fields, "steps");
754
+ if (!Array.isArray(steps))
755
+ return [`steps is ${describeValue2(steps)}, expected a list of steps`];
756
+ return steps.flatMap((step, index) => stepProblems(step, `steps[${index}]`));
757
+ }
713
758
  function sessionIdProblem(sessionId, file) {
714
759
  if (typeof sessionId !== "string" || !isSessionId(sessionId)) {
715
760
  return `sessionId is ${describeValue2(sessionId)}, expected a plain file name`;
@@ -735,14 +780,41 @@ function recordProblems(fields, file) {
735
780
  SESSION_STATES.includes(state) ? null : `state is ${describeValue2(state)}, expected one of ${SESSION_STATES.join(", ")}`,
736
781
  taskProblem(field(fields, "task")),
737
782
  hopProblem(fields),
738
- worktreeProblem(fields)
783
+ worktreeProblem(fields),
784
+ ...stepsProblems(fields)
739
785
  ];
740
786
  return problems.filter((problem) => problem !== null);
741
787
  }
788
+ function isSessionPhase(value) {
789
+ return SESSION_PHASES.includes(value);
790
+ }
791
+ function phaseEntry(phase) {
792
+ return isSessionPhase(phase) ? { phase } : {};
793
+ }
742
794
  function freezeHop(value) {
743
795
  const hop2 = asHopRecord(value);
744
796
  return Object.freeze({ ...hop2, home: Object.freeze(hop2.home), from: Object.freeze(hop2.from) });
745
797
  }
798
+ function freezeFailures(failures) {
799
+ return Object.freeze(failures.map((failure) => Object.freeze({ file: failure.file, name: failure.name })));
800
+ }
801
+ function freezeStep(step) {
802
+ return Object.freeze({
803
+ kind: step.kind,
804
+ scope: typeof step.scope === "string" ? step.scope : Object.freeze([...step.scope]),
805
+ command: Object.freeze([...step.command]),
806
+ exitCode: step.exitCode,
807
+ summary: step.summary,
808
+ failures: freezeFailures(step.failures),
809
+ newFailures: freezeFailures(step.newFailures),
810
+ ...step.interrupted === true ? { interrupted: true } : {}
811
+ });
812
+ }
813
+ function stepsEntry(steps) {
814
+ if (!Array.isArray(steps) || steps.length === 0)
815
+ return {};
816
+ return { steps: Object.freeze(steps.map(freezeStep)) };
817
+ }
746
818
  function freezeRecord(fields) {
747
819
  const task = field(fields, "task");
748
820
  const hop2 = field(fields, "hop");
@@ -756,10 +828,18 @@ function freezeRecord(fields) {
756
828
  startedAt: field(fields, "startedAt"),
757
829
  state: field(fields, "state"),
758
830
  task: isObject(task) ? Object.freeze({ line: field(task, "line"), text: field(task, "text") }) : null,
831
+ ...phaseEntry(field(fields, "phase")),
759
832
  ...hop2 === undefined ? {} : { hop: freezeHop(hop2) },
760
- ...worktree === undefined ? {} : { worktree }
833
+ ...worktree === undefined ? {} : { worktree },
834
+ ...stepsEntry(field(fields, "steps"))
761
835
  });
762
836
  }
837
+ function sessionSteps(record) {
838
+ return record.steps ?? [];
839
+ }
840
+ function sessionPhase(record) {
841
+ return record.phase ?? DEFAULT_PHASE;
842
+ }
763
843
  function parseSessionRecord(text, file) {
764
844
  let parsed;
765
845
  try {
@@ -774,6 +854,40 @@ function parseSessionRecord(text, file) {
774
854
  throw new SessionRecordError(file, problems.join("; "));
775
855
  return freezeRecord(parsed);
776
856
  }
857
+
858
+ // src/loop/sessions.ts
859
+ var RUNS_DIR = "runs";
860
+
861
+ class SessionConflictError extends Error {
862
+ conflicts;
863
+ constructor(conflicts) {
864
+ const named = conflicts.map((conflict) => `${conflict.record.sessionId} (${conflict.reason})`);
865
+ super(`the plan's sessions refuse the run: ${named.join(", ")}`);
866
+ this.name = "SessionConflictError";
867
+ this.conflicts = conflicts;
868
+ }
869
+ }
870
+
871
+ class SessionStateError extends Error {
872
+ file;
873
+ state;
874
+ constructor(file, state, onlyFrom) {
875
+ const acted = onlyFrom.length === 0 ? "no state" : onlyFrom.join(" or ");
876
+ super(`session record ${file}: stores ${state}, and the change acts on ${acted} alone`);
877
+ this.name = "SessionStateError";
878
+ this.file = file;
879
+ this.state = state;
880
+ }
881
+ }
882
+ function runsDir(root) {
883
+ return join7(scopeAt(root).dir, RUNS_DIR);
884
+ }
885
+ function sessionFilePath(root, sessionId) {
886
+ if (!isSessionId(sessionId)) {
887
+ throw new Error(`session record: unusable session id ${JSON.stringify(sessionId)}`);
888
+ }
889
+ return join7(runsDir(root), `${sessionId}${RECORD_EXTENSION}`);
890
+ }
777
891
  function errorCode(error) {
778
892
  return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" ? error.code : null;
779
893
  }
@@ -870,10 +984,15 @@ function updateSession(root, sessionId, change) {
870
984
  if (change.onlyFrom !== undefined && !change.onlyFrom.includes(stored.state)) {
871
985
  throw new SessionStateError(file, stored.state, change.onlyFrom);
872
986
  }
987
+ const appended = change.appendStep === undefined ? [] : stepProblems(change.appendStep, `steps[${sessionSteps(stored).length}]`);
988
+ if (appended.length > 0)
989
+ throw new SessionRecordError(file, `not written: ${appended.join("; ")}`);
873
990
  const record = freezeRecord({
874
991
  ...stored,
875
992
  state: change.state ?? stored.state,
876
- task: change.task === undefined ? stored.task : change.task
993
+ task: change.task === undefined ? stored.task : change.task,
994
+ phase: change.phase ?? stored.phase,
995
+ steps: change.appendStep === undefined ? sessionSteps(stored) : [...sessionSteps(stored), change.appendStep]
877
996
  });
878
997
  writeRecordFile(file, record, false);
879
998
  return record;
@@ -2865,7 +2984,7 @@ var LIVE2 = "live";
2865
2984
  function asidePaths2(path, first, stamp) {
2866
2985
  return { parallelPath: `${path}.migrate-${stamp}`, backupPath: `${path}.before-${first}-${stamp}.bak` };
2867
2986
  }
2868
- function liveLoop(path, options, nothingDone = "Nothing was migrated.") {
2987
+ function liveLoop(path, options, nothingDone = "Nothing was migrated.", ownSessionId = null) {
2869
2988
  const root = storeProjectRoot(path);
2870
2989
  if (root === null)
2871
2990
  return;
@@ -2875,7 +2994,7 @@ function liveLoop(path, options, nothingDone = "Nothing was migrated.") {
2875
2994
  } catch (error) {
2876
2995
  throw new RebuildRefusal(`the loop's run records cannot be read, so a live loop on ${path} cannot be ruled out` + ` (${messageOf(error)}). ${nothingDone}`);
2877
2996
  }
2878
- return records.find((record) => record.state === "running" || record.state === "paused");
2997
+ return records.find((record) => (record.state === "running" || record.state === "paused") && record.sessionId !== ownSessionId);
2879
2998
  }
2880
2999
  function refuseLiveLoop2(path, breaking, options) {
2881
3000
  const loop = liveLoop(path, options);
@@ -3011,8 +3130,8 @@ function refuseOtherProject(options) {
3011
3130
  }
3012
3131
  return other?.storeId ?? null;
3013
3132
  }
3014
- function refuseLiveLoop3(path, options) {
3015
- const loop = liveLoop(path, options, NOTHING_MERGED);
3133
+ function refuseLiveLoop3(path, options, sessionId) {
3134
+ const loop = liveLoop(path, options, NOTHING_MERGED, sessionId);
3016
3135
  if (loop === undefined)
3017
3136
  return;
3018
3137
  throw new MergeRefusal("live-loop", `REFUSED \u2014 loop ${loop.sessionId} (pid ${String(loop.pid)}, plan ${loop.planStub ?? loop.plan}) is running on this store,` + ` and swapping the merged store in under it would lose what it writes. ${NOTHING_MERGED}` + " Finish or stop that loop, then run it again.");
@@ -3103,7 +3222,7 @@ function refuseUnmergeable(options, migrations) {
3103
3222
  const otherStore = refuseOtherProject(options);
3104
3223
  if (!options.dryRun) {
3105
3224
  refuseUnownedDevelopmentWrite(path, [...writeNames(localPlan), MERGES_TABLE], options);
3106
- refuseLiveLoop3(path, options);
3225
+ refuseLiveLoop3(path, options, options.sessionId ?? null);
3107
3226
  }
3108
3227
  return { otherPlan, otherStore };
3109
3228
  }
@@ -3171,10 +3290,11 @@ function createFileSync(options) {
3171
3290
  const copied = copyEffortStore({ source, target });
3172
3291
  return Object.freeze({ status: "pushed", path: join12(copied.directory, SQLITE_STORE_FILE_NAME) });
3173
3292
  };
3174
- const pull = async ({ from, dryRun }) => {
3293
+ const pull = async ({ from, dryRun, sessionId = null }) => {
3175
3294
  const otherPath = resolve5(repoRoot, requirePath("from", from));
3176
3295
  const path = join12(storeDir(), SQLITE_STORE_FILE_NAME);
3177
- const merge2 = mergeStore({ ...mergeSeams(options), path, otherPath, backend, dryRun, stamp: fileStamp(now()), now });
3296
+ const stamp = fileStamp(now());
3297
+ const merge2 = mergeStore({ ...mergeSeams(options), path, otherPath, backend, dryRun, stamp, now, sessionId });
3178
3298
  return Object.freeze({ status: "pulled", merge: merge2 });
3179
3299
  };
3180
3300
  return Object.freeze({ kind: "file", push, pull });
@@ -3218,12 +3338,19 @@ class SyncModuleMissing extends Error {
3218
3338
  function isModuleStrategy(kind) {
3219
3339
  return MODULE_SYNC_STRATEGIES.includes(kind);
3220
3340
  }
3341
+ function contextOf(repoRoot, config) {
3342
+ const hub = hubContextOf(config);
3343
+ return {
3344
+ repoRoot,
3345
+ ...config.store === undefined ? {} : { store: config.store },
3346
+ ...hub === undefined ? {} : { hub }
3347
+ };
3348
+ }
3221
3349
  function selectSync(repoRoot, config, registry = CORE_ADAPTER_REGISTRY) {
3222
3350
  const kind = config.effortSync;
3223
3351
  const adapter = typeof kind === "string" ? registry.find("sync", kind) : undefined;
3224
- if (adapter !== undefined) {
3225
- return adapter.create(config.store === undefined ? { repoRoot } : { repoRoot, store: config.store });
3226
- }
3352
+ if (adapter !== undefined)
3353
+ return adapter.create(contextOf(repoRoot, config));
3227
3354
  const registered = registry.kinds("sync");
3228
3355
  if (isModuleStrategy(kind))
3229
3356
  throw new SyncModuleMissing(kind, registered);
@@ -4335,6 +4462,34 @@ function createLocalLearning(options) {
4335
4462
  return Object.freeze(learning);
4336
4463
  }
4337
4464
 
4465
+ // src/adapters/output/events.ts
4466
+ var EVENT_PREFIX = "rafa\xB7 ";
4467
+ var KIND_COLUMN = 17;
4468
+ function padKind(kind) {
4469
+ return `${kind} `.padEnd(KIND_COLUMN);
4470
+ }
4471
+ function oneLine(message) {
4472
+ return message.split(`
4473
+ `).map((part) => part.trim()).filter((part) => part !== "").join(" ");
4474
+ }
4475
+ function createEventsOutput({ stream }) {
4476
+ const writeLine = (line) => {
4477
+ stream.write(`${EVENT_PREFIX}${line}
4478
+ `);
4479
+ };
4480
+ const error = (message) => {
4481
+ writeLine(`${padKind("error")}${oneLine(message)}`);
4482
+ };
4483
+ const silent = () => {};
4484
+ const emit = (event) => {
4485
+ if (event.type === "event")
4486
+ writeLine(oneLine(event.summary));
4487
+ if (event.type === "log" && event.level === "error")
4488
+ error(event.message);
4489
+ };
4490
+ return Object.freeze({ info: silent, warn: silent, debug: silent, error, emit, result: silent });
4491
+ }
4492
+
4338
4493
  // src/adapters/output/json.ts
4339
4494
  var SECOND_RESULT_REFUSAL = "json output: refused a second terminal result event; a command emits exactly one";
4340
4495
  function createJsonOutput({
@@ -4643,7 +4798,7 @@ function guardSpend() {
4643
4798
  }
4644
4799
  async function spawnClaude(args, prompt, options = {}) {
4645
4800
  guardSpend();
4646
- if (activeOutputMode() === "json") {
4801
+ if (activeOutputMode() !== "text") {
4647
4802
  const { exitCode } = await spawnCaptured(args, prompt, options);
4648
4803
  return exitCode;
4649
4804
  }
@@ -4677,22 +4832,22 @@ function writeWholeLines(text, write) {
4677
4832
  }
4678
4833
  async function teeToOperator(stream) {
4679
4834
  const decoder = new TextDecoder;
4680
- const jsonOutput = activeOutputMode() === "json" ? activeOutput() : null;
4835
+ const routed = activeOutputMode() === "text" ? null : activeOutput();
4681
4836
  const writeLine = (line) => {
4682
- jsonOutput?.info(line);
4837
+ routed?.info(line);
4683
4838
  };
4684
4839
  let text = "";
4685
4840
  let pending = "";
4686
4841
  for await (const chunk of stream) {
4687
- if (jsonOutput === null)
4842
+ if (routed === null)
4688
4843
  process.stdout.write(chunk);
4689
4844
  const decoded = decoder.decode(chunk, { stream: true });
4690
4845
  text += decoded;
4691
- if (jsonOutput !== null)
4846
+ if (routed !== null)
4692
4847
  pending = writeWholeLines(pending + decoded, writeLine);
4693
4848
  }
4694
4849
  const tail = decoder.decode();
4695
- if (jsonOutput !== null) {
4850
+ if (routed !== null) {
4696
4851
  const rest = writeWholeLines(pending + tail, writeLine);
4697
4852
  if (rest !== "")
4698
4853
  writeLine(rest);
@@ -5513,6 +5668,12 @@ var CORE_ADAPTERS = [
5513
5668
  portVersion: PORT_VERSIONS.output,
5514
5669
  create: ({ stream = process.stdout }) => createJsonOutput({ stream })
5515
5670
  },
5671
+ {
5672
+ port: "output",
5673
+ kind: "events",
5674
+ portVersion: PORT_VERSIONS.output,
5675
+ create: ({ stream = process.stdout }) => createEventsOutput({ stream })
5676
+ },
5516
5677
  {
5517
5678
  port: "tracker",
5518
5679
  kind: "local",
@@ -5579,6 +5740,358 @@ var CORE_ADAPTERS = [
5579
5740
  ];
5580
5741
  var CORE_ADAPTER_REGISTRY = createAdapterRegistry(CORE_ADAPTERS);
5581
5742
 
5743
+ // src/board/trust.ts
5744
+ var REPO_PATH = "{owner}/{repo}";
5745
+ var TRUSTED_PERMISSIONS = ["admin", "maintain", "write"];
5746
+ var TRUST_REFUSAL_EXIT = 2;
5747
+ var REMEDY = Object.freeze({
5748
+ issue: "a member must open the spec",
5749
+ "pull request": "a member must open the pull request"
5750
+ });
5751
+ var UNREPORTED = "the lookup wrote nothing";
5752
+ function failureDetail(reading) {
5753
+ const detail = reading.permission?.detail ?? "";
5754
+ return detail === "" ? UNREPORTED : detail;
5755
+ }
5756
+ function isTrustedPermission(value) {
5757
+ return TRUSTED_PERMISSIONS.some((permission) => permission === value);
5758
+ }
5759
+ function stringOrNull(value) {
5760
+ return typeof value === "string" ? value : null;
5761
+ }
5762
+ function lookupFailed(login, detail) {
5763
+ return { login, permission: null, roleName: null, detail };
5764
+ }
5765
+ function detailOf2(result, command) {
5766
+ const written = result.stderr.trim() || result.stdout.trim();
5767
+ return written === "" ? `${command} failed and wrote nothing` : `${command} failed: ${written}`;
5768
+ }
5769
+ function createGhPermissions(options) {
5770
+ const { gh } = options;
5771
+ return async (login) => {
5772
+ if (!isGitHubLogin(login)) {
5773
+ return lookupFailed(login, `${describeValue(login)} is not a GitHub login, so no permission was read`);
5774
+ }
5775
+ const path = `repos/${REPO_PATH}/collaborators/${login}/permission`;
5776
+ const command = `gh api ${path}`;
5777
+ const result = await gh(["api", path]);
5778
+ if (!result.ok)
5779
+ return lookupFailed(login, detailOf2(result, command));
5780
+ let payload;
5781
+ try {
5782
+ payload = JSON.parse(result.stdout);
5783
+ } catch (error) {
5784
+ return lookupFailed(login, `${command} wrote output that is not JSON: ${messageOf(error)}`);
5785
+ }
5786
+ if (!isMapping(payload)) {
5787
+ return lookupFailed(login, `${command} answered ${describeValue(payload)}, expected a mapping`);
5788
+ }
5789
+ const permission = stringOrNull(payload["permission"]);
5790
+ const roleName = stringOrNull(payload["role_name"]);
5791
+ return permission === null && roleName === null ? lookupFailed(login, `${command} answered neither a permission nor a role_name`) : { login, permission, roleName, detail: "" };
5792
+ };
5793
+ }
5794
+ async function readAuthorTrust(options) {
5795
+ const { login, permissions, trustedAuthors } = options;
5796
+ if (!isGitHubLogin(login)) {
5797
+ const detail = `${describeValue(login)} is not a GitHub login, so no permission was read`;
5798
+ return {
5799
+ login,
5800
+ trusted: false,
5801
+ source: null,
5802
+ refusal: "lookup-failed",
5803
+ permission: lookupFailed(login, detail)
5804
+ };
5805
+ }
5806
+ const wanted = login.toLowerCase();
5807
+ const listed2 = trustedAuthors.some((author) => author.toLowerCase() === wanted);
5808
+ if (listed2) {
5809
+ return { login, trusted: true, source: "allow-list", refusal: null, permission: null };
5810
+ }
5811
+ const permission = await permissions(login);
5812
+ if (permission.detail !== "") {
5813
+ return { login, trusted: false, source: null, refusal: "lookup-failed", permission };
5814
+ }
5815
+ const trusted = isTrustedPermission(permission.permission) || isTrustedPermission(permission.roleName);
5816
+ return {
5817
+ login,
5818
+ trusted,
5819
+ source: trusted ? "permission" : null,
5820
+ refusal: trusted ? null : "no-write-access",
5821
+ permission
5822
+ };
5823
+ }
5824
+ function ghBoardTrust(options) {
5825
+ return {
5826
+ permissions: createGhPermissions({ gh: options.gh }),
5827
+ trustedAuthors: options.trustedAuthors,
5828
+ repo: options.repo
5829
+ };
5830
+ }
5831
+ function readBoardTrust(trust, login) {
5832
+ return readAuthorTrust({
5833
+ login,
5834
+ permissions: trust.permissions,
5835
+ trustedAuthors: trust.trustedAuthors
5836
+ });
5837
+ }
5838
+ async function requireTrustedBoardAuthor(item, trust, login) {
5839
+ const reading = await readBoardTrust(trust, login);
5840
+ requireTrustedAuthor({ kind: item.kind, number: item.number, repo: trust.repo }, reading);
5841
+ return reading;
5842
+ }
5843
+ function trustRefusalClause(repo, reading) {
5844
+ if (reading.trusted) {
5845
+ throw new TypeError(`board trust: ${reading.login} is trusted, and has no refusal to name`);
5846
+ }
5847
+ return reading.refusal === "lookup-failed" ? `whose write access to ${repo} could not be read (${failureDetail(reading)})` : `who has no write access to ${repo}`;
5848
+ }
5849
+ function trustRefusalMessage(item, reading) {
5850
+ const because = trustRefusalClause(item.repo, reading);
5851
+ const opened = `${item.kind} #${String(item.number)} was opened by ${reading.login}`;
5852
+ return `${opened}, ${because}; ${REMEDY[item.kind]}`;
5853
+ }
5854
+ function requireTrustedAuthor(item, reading) {
5855
+ if (reading.trusted)
5856
+ return;
5857
+ throw new CommandExit(TRUST_REFUSAL_EXIT, trustRefusalMessage(item, reading));
5858
+ }
5859
+ // src/effort/sync/wire.ts
5860
+ import { existsSync as existsSync14, mkdtempSync as mkdtempSync2, rmSync as rmSync7 } from "fs";
5861
+ import { tmpdir as tmpdir4 } from "os";
5862
+ import { join as join17 } from "path";
5863
+ import { Database as Database6, SQLiteError as SQLiteError3 } from "bun:sqlite";
5864
+ var WIRE_FORMAT = "rafa-effort-wire";
5865
+ var WIRE_VERSION = 1;
5866
+
5867
+ class WireFormatError extends Error {
5868
+ name = "WireFormatError";
5869
+ }
5870
+
5871
+ class WireExportRefusal extends Error {
5872
+ name = "WireExportRefusal";
5873
+ }
5874
+ var MERGED_TABLES = Object.entries(MERGE_RULES).filter(([, rule]) => rule.scope === "merged").map(([table]) => table);
5875
+ var ORIGIN_COLUMNS = ["origin_store", "origin_seq"];
5876
+ function isRecord4(value) {
5877
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5878
+ }
5879
+ function isCount(value) {
5880
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
5881
+ }
5882
+ function isMergedTable(name2) {
5883
+ return MERGED_TABLES.includes(name2);
5884
+ }
5885
+ function checkCursor(value) {
5886
+ if (!isRecord4(value))
5887
+ throw new WireFormatError("wire: cursor is not an object");
5888
+ const entries = Object.entries(value).map(([table, seq]) => {
5889
+ if (!isMergedTable(table))
5890
+ throw new WireFormatError(`wire: cursor names ${JSON.stringify(table)}, which is no merged table`);
5891
+ if (!isCount(seq))
5892
+ throw new WireFormatError(`wire: cursor for ${table} is ${JSON.stringify(seq)}, expected a whole number of 0 or more`);
5893
+ return [table, seq];
5894
+ });
5895
+ return Object.freeze(Object.fromEntries(entries));
5896
+ }
5897
+ function checkRow(table, value, index) {
5898
+ const where = `wire: ${table} row ${String(index)}`;
5899
+ if (!isRecord4(value))
5900
+ throw new WireFormatError(`${where} is not an object`);
5901
+ for (const [column, cell] of Object.entries(value)) {
5902
+ const carried = cell === null || typeof cell === "string" || typeof cell === "number" && Number.isFinite(cell);
5903
+ if (!carried)
5904
+ throw new WireFormatError(`${where} holds ${JSON.stringify(cell) ?? typeof cell} in ${column}, expected a string, a number or null`);
5905
+ }
5906
+ const seq = value.seq;
5907
+ if (!isCount(seq) || seq === 0)
5908
+ throw new WireFormatError(`${where} has seq ${JSON.stringify(seq) ?? "undefined"}, expected a whole number above 0`);
5909
+ return Object.freeze({ ...value });
5910
+ }
5911
+ function checkMigrations(value) {
5912
+ if (!Array.isArray(value))
5913
+ throw new WireFormatError("wire: migrations is not a list");
5914
+ const ids = value.map((id) => {
5915
+ if (typeof id !== "string" || id === "")
5916
+ throw new WireFormatError(`wire: migration id ${JSON.stringify(id)} is not a name`);
5917
+ return id;
5918
+ });
5919
+ const twice = ids.filter((id, index) => ids.indexOf(id) !== index);
5920
+ if (twice.length > 0)
5921
+ throw new WireFormatError(`wire: migrations name ${twice.join(", ")} more than once`);
5922
+ return Object.freeze(ids);
5923
+ }
5924
+ function checkTables(value) {
5925
+ if (!isRecord4(value))
5926
+ throw new WireFormatError("wire: tables is not an object");
5927
+ const entries = Object.entries(value).map(([table, rows]) => {
5928
+ if (!isMergedTable(table))
5929
+ throw new WireFormatError(`wire: tables names ${JSON.stringify(table)}, which is no merged table`);
5930
+ if (!Array.isArray(rows))
5931
+ throw new WireFormatError(`wire: tables.${table} is not a list`);
5932
+ return [table, Object.freeze(rows.map((row, index) => checkRow(table, row, index)))];
5933
+ });
5934
+ return Object.freeze(Object.fromEntries(entries));
5935
+ }
5936
+ function checkPayload(value) {
5937
+ if (!isRecord4(value))
5938
+ throw new WireFormatError("wire: the payload is not an object");
5939
+ if (value.format !== WIRE_FORMAT)
5940
+ throw new WireFormatError(`wire: format is ${JSON.stringify(value.format) ?? "undefined"}, expected ${JSON.stringify(WIRE_FORMAT)}`);
5941
+ if (value.version !== WIRE_VERSION)
5942
+ throw new WireFormatError(`wire: version is ${JSON.stringify(value.version) ?? "undefined"}; this rafa reads version ${String(WIRE_VERSION)}`);
5943
+ return Object.freeze({
5944
+ format: WIRE_FORMAT,
5945
+ version: WIRE_VERSION,
5946
+ migrations: checkMigrations(value.migrations),
5947
+ tables: checkTables(value.tables),
5948
+ cursor: checkCursor(value.cursor)
5949
+ });
5950
+ }
5951
+ function encodeWirePayload(payload) {
5952
+ return JSON.stringify(payload);
5953
+ }
5954
+ function decodeWirePayload(text) {
5955
+ let parsed;
5956
+ try {
5957
+ parsed = JSON.parse(text);
5958
+ } catch (error) {
5959
+ const reason = error instanceof Error ? error.message : String(error);
5960
+ throw new WireFormatError(`wire: the payload is not JSON (${reason})`);
5961
+ }
5962
+ return checkPayload(parsed);
5963
+ }
5964
+ function readLoggedIds(db) {
5965
+ const hasLog = db.query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(MIGRATION_LOG_TABLE) !== null;
5966
+ if (!hasLog)
5967
+ return null;
5968
+ return db.query(`SELECT id FROM ${MIGRATION_LOG_TABLE} ORDER BY seq`).all().map(({ id }) => id);
5969
+ }
5970
+ function columnsOf(db, table) {
5971
+ return db.query(`PRAGMA table_info(${quoted(table)})`).all().map(({ name: name2 }) => name2);
5972
+ }
5973
+ function wireValue(value, where) {
5974
+ if (typeof value === "bigint") {
5975
+ const number = Number(value);
5976
+ if (Number.isSafeInteger(number))
5977
+ return number;
5978
+ throw new WireExportRefusal(`wire: ${where} holds ${String(value)}, past the whole numbers JSON carries exactly`);
5979
+ }
5980
+ if (value instanceof Uint8Array)
5981
+ throw new WireExportRefusal(`wire: ${where} holds a BLOB, which the wire does not carry`);
5982
+ return value;
5983
+ }
5984
+ function readRows(db, table, columns, after, path) {
5985
+ const named = columns.map(quoted).join(", ");
5986
+ return db.query(`SELECT ${named} FROM ${quoted(table)} WHERE seq > ? ORDER BY seq`).all(after).map((row) => Object.freeze(Object.fromEntries(Object.entries(row).map(([column, value]) => [
5987
+ column,
5988
+ wireValue(value, `${path} ${table}.${column} at seq ${String(row.seq)}`)
5989
+ ]))));
5990
+ }
5991
+ function readPayload(db, path, cursor) {
5992
+ const migrations = readLoggedIds(db);
5993
+ if (migrations === null) {
5994
+ throw new WireExportRefusal(`wire: ${path} has no migration log; any rafa command that writes the store brings it forward`);
5995
+ }
5996
+ const tables = {};
5997
+ const next = { ...cursor };
5998
+ for (const table of MERGED_TABLES) {
5999
+ const columns = columnsOf(db, table);
6000
+ if (columns.length === 0)
6001
+ continue;
6002
+ const missing = ORIGIN_COLUMNS.filter((column) => !columns.includes(column));
6003
+ if (missing.length > 0) {
6004
+ throw new WireExportRefusal(`wire: ${path} holds no ${missing.map((column) => `${table}.${column}`).join(", ")}, so its rows carry no origin pair`);
6005
+ }
6006
+ const rows = readRows(db, table, columns, cursor[table] ?? 0, path);
6007
+ tables[table] = Object.freeze(rows);
6008
+ const last = rows[rows.length - 1];
6009
+ next[table] = last === undefined ? cursor[table] ?? 0 : Number(last.seq);
6010
+ }
6011
+ return Object.freeze({
6012
+ format: WIRE_FORMAT,
6013
+ version: WIRE_VERSION,
6014
+ migrations: Object.freeze([...migrations]),
6015
+ tables: Object.freeze(tables),
6016
+ cursor: Object.freeze(next)
6017
+ });
6018
+ }
6019
+ function exportWirePayload(options) {
6020
+ const { path } = options;
6021
+ const cursor = checkCursor(options.cursor ?? {});
6022
+ guardTestProcess(path);
6023
+ if (!existsSync14(path))
6024
+ throw new WireExportRefusal(`wire: ${path} is not there`);
6025
+ const db = new Database6(path, { readonly: true, safeIntegers: true });
6026
+ try {
6027
+ db.run(`PRAGMA busy_timeout = ${String(activeStoreSettings().busyTimeoutMs)}`);
6028
+ return db.transaction(() => readPayload(db, path, cursor))();
6029
+ } finally {
6030
+ db.close();
6031
+ }
6032
+ }
6033
+ function namedMigrations(ids, catalogue) {
6034
+ const known = new Set(catalogue.map(({ id }) => id));
6035
+ const unknown = ids.filter((id) => !known.has(id));
6036
+ if (unknown.length > 0) {
6037
+ throw new WireFormatError(`wire: the payload's store holds migration ${unknown.join(", ")}, which this rafa does not know; update rafa to take its rows`);
6038
+ }
6039
+ return catalogue.filter(({ id }) => ids.includes(id));
6040
+ }
6041
+ function insertRows(db, table, rows) {
6042
+ const columns = columnsOf(db, table);
6043
+ if (columns.length === 0)
6044
+ throw new WireFormatError(`wire: the payload carries ${table}, which its migrations do not make`);
6045
+ const statement = db.query(`INSERT INTO ${quoted(table)} (${columns.map(quoted).join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`);
6046
+ const expected = [...columns].sort().join(",");
6047
+ rows.forEach((row, index) => {
6048
+ const where = `wire: ${table} row ${String(index)}`;
6049
+ const held = Object.keys(row);
6050
+ if ([...held].sort().join(",") !== expected) {
6051
+ throw new WireFormatError(`${where} holds columns ${held.join(", ")}; its migrations make ${columns.join(", ")}`);
6052
+ }
6053
+ try {
6054
+ statement.run(...columns.map((column) => row[column] ?? null));
6055
+ } catch (error) {
6056
+ if (!(error instanceof SQLiteError3))
6057
+ throw error;
6058
+ throw new WireFormatError(`${where} (seq ${String(row.seq)}) is refused by the table: ${error.message}`);
6059
+ }
6060
+ });
6061
+ }
6062
+ function buildStore(path, payload, options) {
6063
+ const migrations = namedMigrations(payload.migrations, options.migrations ?? SQLITE_MIGRATIONS);
6064
+ const db = new Database6(path, { create: true, readwrite: true });
6065
+ try {
6066
+ bringForward(db, path, "write", "open", {
6067
+ migrations,
6068
+ appliedBy: appliedByName(options.identity ?? readRuntimeIdentity()),
6069
+ now: options.now,
6070
+ builtAside: true
6071
+ });
6072
+ db.transaction(() => {
6073
+ for (const [table, rows] of Object.entries(payload.tables))
6074
+ insertRows(db, table, rows);
6075
+ })();
6076
+ } finally {
6077
+ db.close();
6078
+ }
6079
+ }
6080
+ function materialiseWirePayload(payload, options = {}) {
6081
+ const checked = checkPayload(payload);
6082
+ const directory = mkdtempSync2(join17(options.tempDir ?? tmpdir4(), "rafa-wire-"));
6083
+ const release = () => rmSync7(directory, { recursive: true, force: true });
6084
+ const path = join17(directory, SQLITE_STORE_FILE_NAME);
6085
+ try {
6086
+ guardTestProcess(path);
6087
+ buildStore(path, checked, options);
6088
+ } catch (error) {
6089
+ release();
6090
+ throw error;
6091
+ }
6092
+ return Object.freeze({ directory, path, release });
6093
+ }
6094
+
5582
6095
  // src/effort/store/index.ts
5583
6096
  function describeValue4(value) {
5584
6097
  if (typeof value === "string")
@@ -5596,4 +6109,4 @@ function selectEffortStore(repoRoot, config) {
5596
6109
  return adapter.create({ repoRoot });
5597
6110
  }
5598
6111
 
5599
- export { EFFORT_STORE_DIR, effortStoreFileName, EFFORT_DIR_VARIABLE, effortStoreDir, guardTestProcess, EFFORT_KEY_PROJECTIONS, openNdjsonStore, name, version, RAFA_VERSION, versionLine, createGitRunner, gitSaid, NOT_A_REPOSITORY, mainCheckoutOf, SCOPE_DIR, INIT_COMMAND, DISK_FILE_SYSTEM, ScopeError, scopeAt, initHint, selfAndAncestors, resolveScope, POSITION_FILE, positionFilePath, readPositionFile, writePositionFile, positionAt, rehome, hop, goHome, HOP_FILE, hopFilePath, readHopRecord, writeHopRecord, staleAgainst, SessionRecordError, SessionConflictError, SessionStateError, isSessionId, runsDir, sessionFilePath, errorCode, isPidAlive, readSessions, readSession, beginSession, updateSession, DEVELOPMENT_NEXT_STEP, DEVELOPMENT_MARK, DevelopmentBuildRefusedError, refuseUnownedDevelopmentWrite, LEGACY_GATE_OPEN, LEGACY_GATE_CLOSED, SQLITE_MIGRATIONS, sqliteCatalogue, planSchema, SchemaRefusedError, readStoreSchema, writeNames, ACROSS_STORES_ORDER, STAMPED_COLUMNS, stampedValues, activeStoreSettings, normalizeRemote, gitRemoteUrl, readStoreMeta, SQLITE_STORE_FILE_NAME, sqliteStorePath, LONE_SURROGATE, withSqliteStore, writeSqliteStore, openSqliteStore, RebuildRefusal, readConfigFile, loadConfig, plansDirAt, requireProject, resolvePlansDir, resolveProjectConfig, countTasks, formatCounts, checkbox, planFileName, stubOfPlanFile, isFile, issueLine, plural, expectNoArgument, expectOneArgument, expectAtMostOneArgument, readSwitch, fileStamp, fix_schema_default, EffortCopyRefusal, EffortCopyFailure, copyEffortStore, minutesBetween, readCommitLog, UnionSchemaMismatch, migrateStore, MOVE_TO_SQLITE, MergeRefusal, mergeStore, createFileSync, SyncModuleMissing, selectSync, SKILL_FILE, MARKDOWN_EXTENSION, scanLayout, declaredNameIssue, readFrontmatterDocument, readFrontmatter, renderFrontmatter, mergeFrontmatter, writeFrontmatter, ACTION_HEADING, CAUSE_HEADING, parseInstinct, writeInstinct, readScopes, allRecords, findRecords, instinctProject, toHeldRecords, sessionSpawnEnv, getClaudeUsagePercent, checkUsage, CLAUDE_BIN, claudeArgs, interruptClaudeSessions, spawnClaude, runClaude, spawnClaudeCaptured, runClaudeCaptured, planFilePath, ClaudePlannerError, ISSUE_TYPES, ISSUE_PRIORITIES, ISSUE_STATES, GITHUB_LABELS, createGhRunner, typeOfLabels, moduleOfLabels, createLocalTracker, PORT_VERSIONS, createAdapterRegistry, CORE_ADAPTER_REGISTRY, selectEffortStore };
6112
+ export { EFFORT_STORE_DIR, effortStoreFileName, EFFORT_DIR_VARIABLE, effortStoreDir, guardTestProcess, EFFORT_KEY_PROJECTIONS, openNdjsonStore, name, version, RAFA_VERSION, versionLine, createGitRunner, gitSaid, NOT_A_REPOSITORY, mainCheckoutOf, SCOPE_DIR, INIT_COMMAND, DISK_FILE_SYSTEM, ScopeError, scopeAt, initHint, selfAndAncestors, resolveScope, POSITION_FILE, positionFilePath, readPositionFile, writePositionFile, positionAt, rehome, hop, goHome, HOP_FILE, hopFilePath, readHopRecord, writeHopRecord, staleAgainst, SessionRecordError, isSessionId, sessionPhase, SessionConflictError, SessionStateError, runsDir, sessionFilePath, errorCode, isPidAlive, readSessions, readSession, beginSession, updateSession, DEVELOPMENT_NEXT_STEP, DEVELOPMENT_MARK, DevelopmentBuildRefusedError, refuseUnownedDevelopmentWrite, LEGACY_GATE_OPEN, LEGACY_GATE_CLOSED, SQLITE_MIGRATIONS, sqliteCatalogue, planSchema, SchemaRefusedError, readStoreSchema, writeNames, ACROSS_STORES_ORDER, STAMPED_COLUMNS, stampedValues, activeStoreSettings, normalizeRemote, gitRemoteUrl, readStoreMeta, SQLITE_STORE_FILE_NAME, sqliteStorePath, LONE_SURROGATE, withSqliteStore, writeSqliteStore, openSqliteStore, RebuildRefusal, readConfigFile, loadConfig, plansDirAt, requireProject, resolvePlansDir, resolveProjectConfig, countTasks, formatCounts, checkbox, planFileName, stubOfPlanFile, isFile, issueLine, plural, expectNoArgument, expectOneArgument, expectAtMostOneArgument, readSwitch, fileStamp, fix_schema_default, EffortCopyRefusal, EffortCopyFailure, copyEffortStore, minutesBetween, readCommitLog, UnionSchemaMismatch, migrateStore, MOVE_TO_SQLITE, MergeRefusal, mergeStore, createFileSync, SyncModuleMissing, selectSync, SKILL_FILE, MARKDOWN_EXTENSION, scanLayout, declaredNameIssue, readFrontmatterDocument, readFrontmatter, renderFrontmatter, mergeFrontmatter, writeFrontmatter, ACTION_HEADING, CAUSE_HEADING, parseInstinct, writeInstinct, readScopes, allRecords, findRecords, instinctProject, toHeldRecords, padKind, oneLine, sessionSpawnEnv, getClaudeUsagePercent, checkUsage, CLAUDE_BIN, claudeArgs, interruptClaudeSessions, spawnClaude, runClaude, spawnClaudeCaptured, runClaudeCaptured, planFilePath, ClaudePlannerError, ISSUE_TYPES, ISSUE_PRIORITIES, ISSUE_STATES, GITHUB_LABELS, createGhRunner, typeOfLabels, moduleOfLabels, createLocalTracker, PORT_VERSIONS, createAdapterRegistry, CORE_ADAPTER_REGISTRY, TRUSTED_PERMISSIONS, createGhPermissions, readAuthorTrust, ghBoardTrust, readBoardTrust, requireTrustedBoardAuthor, trustRefusalClause, requireTrustedAuthor, WIRE_FORMAT, WIRE_VERSION, WireFormatError, WireExportRefusal, encodeWirePayload, decodeWirePayload, exportWirePayload, materialiseWirePayload, selectEffortStore };