tickmarkr 2.6.7 → 2.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/beat.d.ts +3 -3
- package/dist/cli/commands/beat.js +39 -19
- package/dist/cli/commands/fleet.js +2 -0
- package/dist/cli/commands/plan.js +16 -18
- package/dist/cli/commands/verify.js +4 -2
- package/dist/cli/help.js +5 -2
- package/dist/drivers/herdr.js +2 -2
- package/dist/drivers/orca.d.ts +12 -4
- package/dist/drivers/orca.js +200 -39
- package/dist/gates/evidence.js +3 -3
- package/dist/gates/review.d.ts +1 -1
- package/dist/gates/review.js +62 -13
- package/dist/gates/run-gates.d.ts +3 -0
- package/dist/gates/run-gates.js +115 -20
- package/dist/gates/test-manifest.d.ts +36 -2
- package/dist/gates/test-manifest.js +55 -10
- package/dist/run/beat-lifecycle.d.ts +109 -24
- package/dist/run/beat-lifecycle.js +1173 -216
- package/dist/run/daemon.d.ts +4 -0
- package/dist/run/daemon.js +111 -33
- package/dist/run/git-trust.d.ts +78 -3
- package/dist/run/git-trust.js +175 -19
- package/dist/run/git.d.ts +24 -3
- package/dist/run/git.js +140 -21
- package/dist/run/merge.js +2 -2
- package/dist/run/verification-job.d.ts +2 -0
- package/dist/run/verification-job.js +32 -10
- package/dist/tui/ink/fleet-app.d.ts +7 -2
- package/dist/tui/ink/fleet-app.js +56 -14
- package/package.json +1 -1
- package/skills/tickmarkr-auto/SKILL.md +58 -14
- package/skills/tickmarkr-loop/SKILL.md +65 -14
- package/skills/tickmarkr-overseer/SKILL.md +97 -28
- package/skills/tickmarkr-overseer/scripts/watch-journal.sh +9 -7
package/dist/run/git.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CommandReceiptAttribution, ShellReceipt } from "./protocol.js";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { ROUTING_ENV_SEAMS } from "../route/router.js";
|
|
4
|
+
import { type OwnGitCapability } from "./git-trust.js";
|
|
4
5
|
export { ROUTING_ENV_SEAMS };
|
|
5
6
|
export declare const FORK_CAP_ENV = "VITEST_MAX_FORKS";
|
|
6
7
|
export declare const SUITE_PARENT_ENV = "TICKMARKR_SUITE_PARENT";
|
|
@@ -168,12 +169,32 @@ export interface ShellOptions {
|
|
|
168
169
|
/** Called once per invocation (1-based), including each pre-spawn retry. */
|
|
169
170
|
receiptAttribution?: (invocation: number) => CommandReceiptAttribution;
|
|
170
171
|
}
|
|
171
|
-
/** Shared command seam, including invocation-bound manifested runners.
|
|
172
|
+
/** Shared command seam, including invocation-bound manifested runners. A PAYLOAD entry: its children keep the
|
|
173
|
+
* operator's git config (hooks, fsmonitor) and get no checkout pin — ownership follows the entry, never `login`
|
|
174
|
+
* or the command text. tickmarkr's own git goes through shGit/shGitOk. */
|
|
172
175
|
export declare function shell(cmd: string, cwd: string, timeoutMs: number, login?: boolean, options?: ShellOptions): Promise<ShResult>;
|
|
176
|
+
/**
|
|
177
|
+
* v2.6.8 T2: what an OWN-GIT caller declares at its call site — never inferred from the command text. Every own-git
|
|
178
|
+
* child in a linked checkout keeps the path pin and the ref-store pin; `createsWorktree` is the worktree capability the
|
|
179
|
+
* branch-creating call sites declare, and addDetachedWorktree is the detached one (see OwnGitCapability and ownGitPin).
|
|
180
|
+
*/
|
|
181
|
+
export type OwnGitOptions = OwnGitCapability;
|
|
173
182
|
export declare function sh(cmd: string, cwd: string, timeoutMs?: number, options?: ShellOptions): Promise<ShResult>;
|
|
174
|
-
export declare function shGit(cmd: string, cwd: string, timeoutMs?: number): Promise<ShResult>;
|
|
183
|
+
export declare function shGit(cmd: string, cwd: string, timeoutMs?: number, own?: OwnGitOptions): Promise<ShResult>;
|
|
175
184
|
export declare function shOk(cmd: string, cwd: string): Promise<string>;
|
|
176
|
-
export declare function shGitOk(cmd: string, cwd: string): Promise<string>;
|
|
185
|
+
export declare function shGitOk(cmd: string, cwd: string, own?: OwnGitOptions): Promise<string>;
|
|
186
|
+
/**
|
|
187
|
+
* v2.6.8 T2: the DETACHED worktree capability, declared by calling it (the daemon's baseline recapture and standalone
|
|
188
|
+
* verify's base worktree). Under the ref-store pin git stubs a detached add's new HEAD as `ref: refs/heads/.invalid`
|
|
189
|
+
* and checks out nothing, and no own-git step ever runs unpinned instead: the commit-ish is resolved to an object id in
|
|
190
|
+
* `cwd`'s checkout (HEAD and branch names read the trusted store), the checkout is added `--no-checkout --detach` at
|
|
191
|
+
* that id (its ref store pinned to the trusted common directory, see DetachedAddCapability), and then — pinned to the
|
|
192
|
+
* NEW checkout's own authority — its HEAD is written as that id, git's `refs/heads/.invalid` stub ref is deleted and the
|
|
193
|
+
* checkout is populated.
|
|
194
|
+
* A step raced by a commondir change is refused by name before the next one runs, and what the add created is rolled
|
|
195
|
+
* back through the trusted common directory derived here once (dropWorktree), even while that commondir stays hostile.
|
|
196
|
+
*/
|
|
197
|
+
export declare function addDetachedWorktree(cwd: string, dir: string, commitish: string, timeoutMs?: number): Promise<ShResult>;
|
|
177
198
|
export declare function npmDependencyManifestChanged(cwd: string, baselineRef: string): Promise<boolean>;
|
|
178
199
|
export declare function npmDependencyInstallCommand(cwd: string): string;
|
|
179
200
|
export declare function gitHead(cwd: string): Promise<string>;
|
package/dist/run/git.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { observeVerificationReceipt, observeVerificationSpawn, verificationJobEnvironment } from "./verification-job.js";
|
|
2
|
-
import { executionSignal } from "./execution-budget.js";
|
|
2
|
+
import { executionSignal, withoutExecutionBudget } from "./execution-budget.js";
|
|
3
3
|
import { commandLeaseEnvironment, repositoryLeaseEnvironment, registerRepositoryChild, withCommandLease } from "./lease.js";
|
|
4
4
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5
5
|
import { execFile, execFileSync, spawn } from "node:child_process";
|
|
@@ -11,7 +11,7 @@ import { shq } from "../adapters/types.js";
|
|
|
11
11
|
import { tickmarkrDir } from "../graph/graph.js";
|
|
12
12
|
import { ROUTING_ENV_SEAMS } from "../route/router.js";
|
|
13
13
|
import { titleEnvironment } from "./title-environment.js";
|
|
14
|
-
import { assertGitTrust, protectedGitEnv, trustedCommonDir } from "./git-trust.js";
|
|
14
|
+
import { assertGitTrust, GitTrustRefusal, ownGitPin, protectedGitEnv, trustedCommonDir } from "./git-trust.js";
|
|
15
15
|
export { ROUTING_ENV_SEAMS };
|
|
16
16
|
// OBS-110: gate/baseline/tip-verify children are vitest suites; without a fork cap, concurrent
|
|
17
17
|
// full-suite gate runs fork a worker-per-core pool per worktree and saturate the operator box.
|
|
@@ -259,8 +259,13 @@ export const SPAWN_RETRY_BACKOFF_MS = 50;
|
|
|
259
259
|
// RULING-P99-28: SHIP — every gate user can hit the inherited-pipe hang at this product seam.
|
|
260
260
|
/** Give a normally-exited shell's descendants this long to close their inherited pipes themselves. */
|
|
261
261
|
const SHELL_REAP_GRACE_MS = 2000;
|
|
262
|
-
/** Shared command seam, including invocation-bound manifested runners.
|
|
262
|
+
/** Shared command seam, including invocation-bound manifested runners. A PAYLOAD entry: its children keep the
|
|
263
|
+
* operator's git config (hooks, fsmonitor) and get no checkout pin — ownership follows the entry, never `login`
|
|
264
|
+
* or the command text. tickmarkr's own git goes through shGit/shGitOk. */
|
|
263
265
|
export function shell(cmd, cwd, timeoutMs, login = false, options = {}) {
|
|
266
|
+
return shellEntry(cmd, cwd, timeoutMs, login, options, undefined);
|
|
267
|
+
}
|
|
268
|
+
function shellEntry(cmd, cwd, timeoutMs, login, options, ownGit) {
|
|
264
269
|
const inherited = executionSignal();
|
|
265
270
|
const signal = inherited && options.signal ? AbortSignal.any([inherited, options.signal]) : inherited ?? options.signal;
|
|
266
271
|
let attribution;
|
|
@@ -305,22 +310,24 @@ export function shell(cmd, cwd, timeoutMs, login = false, options = {}) {
|
|
|
305
310
|
};
|
|
306
311
|
begin(1);
|
|
307
312
|
checkAbort();
|
|
308
|
-
return withCommandLease(cmd, () => executeShell(cmd, cwd, timeoutMs, login, { ...options, signal }, { begin, emit, checkAbort }))
|
|
313
|
+
return withCommandLease(cmd, () => executeShell(cmd, cwd, timeoutMs, login, { ...options, signal }, { begin, emit, checkAbort }, ownGit))
|
|
309
314
|
.catch((error) => {
|
|
310
315
|
if (signal?.aborted)
|
|
311
316
|
emit({ outcome: "cancelled", exitCode: null, signal: null });
|
|
312
317
|
throw error;
|
|
313
318
|
});
|
|
314
319
|
}
|
|
315
|
-
function executeShell(cmd, cwd, timeoutMs, login, options, observation) {
|
|
320
|
+
function executeShell(cmd, cwd, timeoutMs, login, options, observation, ownGit) {
|
|
316
321
|
observation.checkAbort();
|
|
317
322
|
// OBS-74: scrub tickmarkr's own routing env seams from every child — a daemon carrying
|
|
318
323
|
// TICKMARKR_QUALITY leaked it into baseline/gate/tip-verify children, turning a dogfood
|
|
319
324
|
// repo's route() tests red inside the gates. Scrub a copy at this one choke point so
|
|
320
325
|
// children are hermetic by construction; the daemon's own process.env stays unchanged.
|
|
321
|
-
// v2.6.7 T4 (E1): git children run with core.fsmonitor=false and
|
|
322
|
-
// repository or inherited GIT_CONFIG_* say;
|
|
323
|
-
|
|
326
|
+
// v2.6.7 T4 (E1) / v2.6.8 T2: tickmarkr's OWN git children (shGit/shGitOk) run with core.fsmonitor=false and
|
|
327
|
+
// an inert hooksPath whatever the repository or inherited GIT_CONFIG_* say; payload children keep the
|
|
328
|
+
// operator's config. Every other parent, capability and explicit value survives.
|
|
329
|
+
const payloadEnv = verificationJobEnvironment(repositoryLeaseEnvironment(commandLeaseEnvironment(options.env ?? process.env)));
|
|
330
|
+
const env = ownGit ? protectedGitEnv(payloadEnv) : payloadEnv;
|
|
324
331
|
for (const k of ROUTING_ENV_SEAMS)
|
|
325
332
|
delete env[k];
|
|
326
333
|
// A run freezes the operator override and cores at startup; admission can lower the round cap.
|
|
@@ -345,6 +352,8 @@ function executeShell(cmd, cwd, timeoutMs, login, options, observation) {
|
|
|
345
352
|
// under. `Number` of an unparseable export is NaN, which every reader treats as malformed and
|
|
346
353
|
// therefore fails closed — the honest direction when the cap in play cannot be stated.
|
|
347
354
|
const capacity = { forkCap: Number(env[FORK_CAP_ENV]), cores: resolvedCapacity().cores };
|
|
355
|
+
// v2.6.8 T2: the pin's post-exit recheck for the attempt that actually ran its child, and its ref view's release.
|
|
356
|
+
let recheck, release, unview, store;
|
|
348
357
|
const attempt = () => new Promise((resolve) => {
|
|
349
358
|
const startedAt = Date.now();
|
|
350
359
|
// detached: bash gets its own process group so a timeout can kill the whole tree —
|
|
@@ -353,15 +362,24 @@ function executeShell(cmd, cwd, timeoutMs, login, options, observation) {
|
|
|
353
362
|
let p;
|
|
354
363
|
// v2.6.7 T4: the trust check runs before EVERY spawn attempt (retries included) and caches nothing — a
|
|
355
364
|
// seat can rewrite its gitdir's commondir/config.worktree between any two calls. A refusal names the file.
|
|
365
|
+
// v2.6.8 T2 (P3): an own-git attempt in a linked checkout also pins its child (config, objects and the ref store)
|
|
366
|
+
// to the authority that check derived, and its result is refused when commondir changed before the child exited;
|
|
367
|
+
// payloads get no pin. The command text never selects a pin.
|
|
368
|
+
let childEnv = env;
|
|
369
|
+
recheck = undefined;
|
|
370
|
+
unview = undefined;
|
|
371
|
+
store = undefined;
|
|
356
372
|
try {
|
|
357
|
-
assertGitTrust(cwd);
|
|
373
|
+
const pin = ownGit ? ownGitPin(cwd, env, ownGit) : (assertGitTrust(cwd), undefined);
|
|
374
|
+
if (pin)
|
|
375
|
+
({ env: childEnv, recheck, release, unview, store } = pin);
|
|
358
376
|
}
|
|
359
377
|
catch (error) {
|
|
360
378
|
observation.emit({ outcome: "spawn-failed", exitCode: null, signal: null, error: String(error) });
|
|
361
379
|
throw error;
|
|
362
380
|
}
|
|
363
381
|
try {
|
|
364
|
-
p = (spawnChild ?? spawn)("bash", [login ? "-lc" : "-c", cmd], { cwd, env, stdio: ["ignore", "pipe", "pipe"], detached: true });
|
|
382
|
+
p = (spawnChild ?? spawn)("bash", [login ? "-lc" : "-c", cmd], { cwd, env: childEnv, stdio: ["ignore", "pipe", "pipe"], detached: true });
|
|
365
383
|
}
|
|
366
384
|
catch (error) {
|
|
367
385
|
observation.emit({ outcome: "spawn-failed", exitCode: null, signal: null, error: String(error) });
|
|
@@ -521,9 +539,29 @@ function executeShell(cmd, cwd, timeoutMs, login, options, observation) {
|
|
|
521
539
|
if (n > 1)
|
|
522
540
|
observation.begin(n);
|
|
523
541
|
observation.checkAbort();
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
542
|
+
let r;
|
|
543
|
+
try {
|
|
544
|
+
r = await attempt();
|
|
545
|
+
}
|
|
546
|
+
finally {
|
|
547
|
+
release?.();
|
|
548
|
+
release = undefined;
|
|
549
|
+
}
|
|
550
|
+
if (!("refused" in r)) {
|
|
551
|
+
try {
|
|
552
|
+
recheck?.();
|
|
553
|
+
}
|
|
554
|
+
catch (error) {
|
|
555
|
+
// v2.6.8 T2: a raced worktree add already registered what it created in the trusted store; it is removed through
|
|
556
|
+
// the trusted common directory this attempt derived, even while the caller's commondir stays hostile and even
|
|
557
|
+
// when the execution budget expired meanwhile; a failed rollback rides on the refusal, never swallowed
|
|
558
|
+
const rollback = ownGit && "rollback" in ownGit ? ownGit.rollback : undefined;
|
|
559
|
+
if (rollback && store)
|
|
560
|
+
await dropWorktree(store, rollback, timeoutMs).catch((failure) => { throw rollbackFailed(error, failure); });
|
|
561
|
+
throw error;
|
|
562
|
+
}
|
|
563
|
+
return unview ? { ...r, stdout: unview(r.stdout), stderr: unview(r.stderr) } : r;
|
|
564
|
+
}
|
|
527
565
|
// Bounded, and the bound is what makes a persisting shortage a REPORTED failure rather than a
|
|
528
566
|
// wedged daemon: past it the caller gets the refusal's own text under exit 127, as before.
|
|
529
567
|
if (n >= SPAWN_ATTEMPT_LIMIT) {
|
|
@@ -537,8 +575,10 @@ export function sh(cmd, cwd, timeoutMs = DEFAULT_SHELL_TIMEOUT_MS, options = {})
|
|
|
537
575
|
return shell(cmd, cwd, timeoutMs, true, options);
|
|
538
576
|
}
|
|
539
577
|
// Git plumbing never needs an operator profile; skip login-shell startup and its side effects.
|
|
540
|
-
|
|
541
|
-
|
|
578
|
+
// v2.6.8 T2: the explicit OWN-GIT entry — forced inert hooks/fsmonitor and, in a linked checkout, the trusted-path and
|
|
579
|
+
// ref-store pin.
|
|
580
|
+
export function shGit(cmd, cwd, timeoutMs = DEFAULT_SHELL_TIMEOUT_MS, own = {}) {
|
|
581
|
+
return shellEntry(cmd, cwd, timeoutMs, false, {}, own);
|
|
542
582
|
}
|
|
543
583
|
export async function shOk(cmd, cwd) {
|
|
544
584
|
const r = await sh(cmd, cwd);
|
|
@@ -546,12 +586,88 @@ export async function shOk(cmd, cwd) {
|
|
|
546
586
|
throw new Error(`command failed (${r.code}): ${cmd}\n${r.stderr || r.stdout}`);
|
|
547
587
|
return r.stdout;
|
|
548
588
|
}
|
|
549
|
-
export async function shGitOk(cmd, cwd) {
|
|
550
|
-
const r = await shGit(cmd, cwd);
|
|
589
|
+
export async function shGitOk(cmd, cwd, own = {}) {
|
|
590
|
+
const r = await shGit(cmd, cwd, DEFAULT_SHELL_TIMEOUT_MS, own);
|
|
551
591
|
if (r.code !== 0)
|
|
552
592
|
throw new Error(`command failed (${r.code}): ${cmd}\n${r.stderr || r.stdout}`);
|
|
553
593
|
return r.stdout;
|
|
554
594
|
}
|
|
595
|
+
/**
|
|
596
|
+
* v2.6.8 T2: the DETACHED worktree capability, declared by calling it (the daemon's baseline recapture and standalone
|
|
597
|
+
* verify's base worktree). Under the ref-store pin git stubs a detached add's new HEAD as `ref: refs/heads/.invalid`
|
|
598
|
+
* and checks out nothing, and no own-git step ever runs unpinned instead: the commit-ish is resolved to an object id in
|
|
599
|
+
* `cwd`'s checkout (HEAD and branch names read the trusted store), the checkout is added `--no-checkout --detach` at
|
|
600
|
+
* that id (its ref store pinned to the trusted common directory, see DetachedAddCapability), and then — pinned to the
|
|
601
|
+
* NEW checkout's own authority — its HEAD is written as that id, git's `refs/heads/.invalid` stub ref is deleted and the
|
|
602
|
+
* checkout is populated.
|
|
603
|
+
* A step raced by a commondir change is refused by name before the next one runs, and what the add created is rolled
|
|
604
|
+
* back through the trusted common directory derived here once (dropWorktree), even while that commondir stays hostile.
|
|
605
|
+
*/
|
|
606
|
+
export async function addDetachedWorktree(cwd, dir, commitish, timeoutMs = DEFAULT_SHELL_TIMEOUT_MS) {
|
|
607
|
+
// the authority this operation keeps, rollback included: derived once from the trusted layout, never a commondir
|
|
608
|
+
const store = trustedCommonDir(cwd);
|
|
609
|
+
const resolved = await shGit(`git rev-parse --verify --end-of-options ${shq(`${commitish}^{commit}`)}`, cwd, timeoutMs);
|
|
610
|
+
if (resolved.code !== 0)
|
|
611
|
+
return resolved;
|
|
612
|
+
const oid = resolved.stdout.trim();
|
|
613
|
+
// under a ref payload git also resolves its new-HEAD stub into a shared refs/heads/.invalid; it is deleted again
|
|
614
|
+
const rollback = { dir, branch: { name: ".invalid" } };
|
|
615
|
+
const added = await shellEntry(`git worktree add --no-checkout --detach ${shq(dir)} ${shq(oid)}`, cwd, timeoutMs, false, {}, { createsWorktree: "detached", rollback });
|
|
616
|
+
if (added.code !== 0)
|
|
617
|
+
return added;
|
|
618
|
+
let repaired;
|
|
619
|
+
try {
|
|
620
|
+
repaired = await shGit(`git update-ref --no-deref HEAD ${shq(oid)} && git update-ref -d refs/heads/.invalid && git reset -q --hard --no-recurse-submodules`, dir, timeoutMs);
|
|
621
|
+
}
|
|
622
|
+
catch (error) {
|
|
623
|
+
await dropWorktree(store, rollback, timeoutMs).catch((failure) => { throw rollbackFailed(error, failure); });
|
|
624
|
+
throw error;
|
|
625
|
+
}
|
|
626
|
+
if (repaired.code !== 0)
|
|
627
|
+
await dropWorktree(store, rollback, timeoutMs);
|
|
628
|
+
return repaired;
|
|
629
|
+
}
|
|
630
|
+
/** A failed rollback is never swallowed: it rides on the error the operation already throws (a refusal stays one). */
|
|
631
|
+
function rollbackFailed(error, failure) {
|
|
632
|
+
const why = `; its rollback failed, so what it created may remain: ${failure instanceof Error ? failure.message : String(failure)}`;
|
|
633
|
+
return error instanceof GitTrustRefusal ? new GitTrustRefusal(error.path, `${error.detail}${why}`, { cause: failure })
|
|
634
|
+
: new AggregateError([error, failure], `${error instanceof Error ? error.message : String(error)}${why}`);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Removes a checkout `dir` and its registration — and, when named, a branch the add created (deleted) or moved
|
|
638
|
+
* (restored to `prior`) — through `store`, a trusted common directory derived from the layout (TrustedStoreCapability):
|
|
639
|
+
* nothing here reads the caller's checkout, so it completes while that checkout's commondir is hostile. Cleanup only, so
|
|
640
|
+
* it runs outside any execution budget (an expired one would refuse every step and leave what the add created), each
|
|
641
|
+
* step bounded by `timeoutMs`; every step is tried, and any that failed is thrown, never swallowed.
|
|
642
|
+
*/
|
|
643
|
+
async function dropWorktree(store, { dir, branch }, timeoutMs = DEFAULT_SHELL_TIMEOUT_MS) {
|
|
644
|
+
const failed = await withoutExecutionBudget(async () => {
|
|
645
|
+
const run = async (cmd) => {
|
|
646
|
+
try {
|
|
647
|
+
const r = await shellEntry(cmd, store, timeoutMs, false, {}, { store });
|
|
648
|
+
return r.code === 0 ? undefined : `${cmd} (${r.code}): ${(r.stderr || r.stdout).trim()}`;
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
return `${cmd}: ${String(error)}`;
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
await run(`git worktree remove --force ${shq(dir)}`); // best-effort; the steps below decide
|
|
655
|
+
const steps = [`rm -rf ${shq(dir)}`, "git worktree prune"];
|
|
656
|
+
if (branch) {
|
|
657
|
+
const ref = shq(`refs/heads/${branch.name}`);
|
|
658
|
+
steps.push(branch.prior ? `git update-ref ${ref} ${shq(branch.prior)}` : `git update-ref -d ${ref}`);
|
|
659
|
+
}
|
|
660
|
+
const out = [];
|
|
661
|
+
for (const cmd of steps) {
|
|
662
|
+
const f = await run(cmd);
|
|
663
|
+
if (f)
|
|
664
|
+
out.push(f);
|
|
665
|
+
}
|
|
666
|
+
return out;
|
|
667
|
+
});
|
|
668
|
+
if (failed.length)
|
|
669
|
+
throw new Error(`cleanup of ${dir} through ${store} failed: ${failed.join("; ")}`);
|
|
670
|
+
}
|
|
555
671
|
const NPM_DEPENDENCY_MANIFESTS = ["package.json", "package-lock.json", "npm-shrinkwrap.json"];
|
|
556
672
|
// OBS-126: compare the whole attempt (committed, staged, unstaged, or newly added manifest) with
|
|
557
673
|
// the task's integration-tip baseline. A committed package.json change is invisible to `git status`,
|
|
@@ -705,7 +821,10 @@ export async function createWorktree(repo, branch, baseRef) {
|
|
|
705
821
|
const dir = join(tickmarkrDir(repo), WORKTREES_DIR, sanitize(branch));
|
|
706
822
|
if (existsSync(dir))
|
|
707
823
|
await removeWorktree(repo, dir);
|
|
708
|
-
|
|
824
|
+
// the worktree capability: a branch add, whose symbolic HEAD keeps the ref-store pin (see OwnGitCapability); a raced
|
|
825
|
+
// add is rolled back, its branch restored to the value read here or deleted when it is new
|
|
826
|
+
const prior = (await shGit(`git rev-parse --verify -q ${shq(`refs/heads/${branch}`)}`, repo)).stdout.trim() || undefined;
|
|
827
|
+
await shGitOk(`git worktree add -B ${shq(branch)} ${shq(dir)} ${shq(baseRef)}`, repo, { createsWorktree: "branch", rollback: { dir, branch: { name: branch, prior } } });
|
|
709
828
|
linkNodeModules(repo, dir);
|
|
710
829
|
return dir;
|
|
711
830
|
}
|
|
@@ -906,10 +1025,10 @@ export async function changeRepresented(cwd, commit, dest = "HEAD") {
|
|
|
906
1025
|
const tree = await shGit(`git rev-parse ${shq(`${dest}^{tree}`)}`, cwd);
|
|
907
1026
|
return tree.code === 0 && merged.stdout.split("\n")[0].trim() === tree.stdout.trim();
|
|
908
1027
|
}
|
|
1028
|
+
// v2.6.8 T2: through the trusted common directory derived once from `repo`'s layout, never `repo`'s mutable commondir,
|
|
1029
|
+
// so a checkout is removed even while that commondir stays hostile (see dropWorktree)
|
|
909
1030
|
export async function removeWorktree(repo, dir) {
|
|
910
|
-
await
|
|
911
|
-
await shGit(`rm -rf ${shq(dir)}`, repo);
|
|
912
|
-
await shGit("git worktree prune", repo);
|
|
1031
|
+
await dropWorktree(trustedCommonDir(repo), { dir });
|
|
913
1032
|
}
|
|
914
1033
|
export const REFS_PREFLIGHT_PREFIX = "refs/tickmarkr/preflight";
|
|
915
1034
|
export const REFS_PROBE_REMEDY = "run from the main repository or a full clone; a sandbox that denies writes under that path cannot host a run";
|
package/dist/run/merge.js
CHANGED
|
@@ -39,10 +39,10 @@ export async function ensureIntegration(repo, branch, baseRef) {
|
|
|
39
39
|
if (!existsSync(join(dir, ".git"))) {
|
|
40
40
|
const exists = (await shGit(`git rev-parse --verify refs/heads/${shq(branch)}`, repo)).code === 0;
|
|
41
41
|
if (exists) {
|
|
42
|
-
await shGitOk(`git worktree add ${shq(dir)} ${shq(branch)}`, repo);
|
|
42
|
+
await shGitOk(`git worktree add ${shq(dir)} ${shq(branch)}`, repo, { createsWorktree: "branch", rollback: { dir } });
|
|
43
43
|
}
|
|
44
44
|
else {
|
|
45
|
-
await shGitOk(`git worktree add -b ${shq(branch)} ${shq(dir)} ${shq(baseRef)}`, repo);
|
|
45
|
+
await shGitOk(`git worktree add -b ${shq(branch)} ${shq(dir)} ${shq(baseRef)}`, repo, { createsWorktree: "branch", rollback: { dir, branch: { name: branch } } });
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
linkNodeModules(repo, dir);
|
|
@@ -38,6 +38,8 @@ export interface VerificationJobReport {
|
|
|
38
38
|
phases: VerificationPhase[];
|
|
39
39
|
}
|
|
40
40
|
export declare const VERIFICATION_JOB_TOKEN_ENV = "TICKMARKR_VERIFICATION_JOB_TOKEN";
|
|
41
|
+
/** v2.6.8 T2: a child carries only THIS process's local job id; outside a local job an inherited (foreign) token
|
|
42
|
+
* is dropped, never forwarded — a nested verify mints and stamps its own. */
|
|
41
43
|
export declare function verificationJobEnvironment(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
42
44
|
export declare const setVerificationJobClockForTests: (now: () => number) => void;
|
|
43
45
|
export declare const resetVerificationJobClockForTests: () => void;
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
-
import { execFileSync } from "node:child_process";
|
|
3
2
|
import { randomUUID } from "node:crypto";
|
|
4
3
|
import { renameSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
-
import {
|
|
4
|
+
import { shGit } from "./git.js";
|
|
6
5
|
import { executionSignal } from "./execution-budget.js";
|
|
7
|
-
import { hasRepositoryLeaseOwnership, REPOSITORY_LEASE_TOKEN_ENV, reentrantHolder, repositoryLeasePath, reapRepositoryChildren, withCommandLease, withFreshCommandLease, withRepositoryLease } from "./lease.js";
|
|
6
|
+
import { hasRepositoryLeaseOwnership, readHolder, REPOSITORY_LEASE_TOKEN_ENV, reentrantHolder, repositoryLeasePath, reapRepositoryChildren, withCommandLease, withFreshCommandLease, withRepositoryLease } from "./lease.js";
|
|
8
7
|
const jobs = new AsyncLocalStorage();
|
|
9
8
|
export const VERIFICATION_JOB_TOKEN_ENV = "TICKMARKR_VERIFICATION_JOB_TOKEN";
|
|
9
|
+
/** v2.6.8 T2: a child carries only THIS process's local job id; outside a local job an inherited (foreign) token
|
|
10
|
+
* is dropped, never forwarded — a nested verify mints and stamps its own. */
|
|
10
11
|
export function verificationJobEnvironment(env) {
|
|
11
12
|
const job = jobs.getStore();
|
|
12
|
-
|
|
13
|
+
const child = { ...env };
|
|
14
|
+
if (job)
|
|
15
|
+
child[VERIFICATION_JOB_TOKEN_ENV] = job.report.id;
|
|
16
|
+
else
|
|
17
|
+
delete child[VERIFICATION_JOB_TOKEN_ENV];
|
|
18
|
+
return child;
|
|
13
19
|
}
|
|
14
20
|
let clock = () => Date.now();
|
|
15
21
|
export const setVerificationJobClockForTests = (now) => { clock = now; };
|
|
@@ -60,9 +66,11 @@ export async function withVerificationJob(command, cwd, reportPath, evidence, ru
|
|
|
60
66
|
const signal = executionSignal();
|
|
61
67
|
let commit = evidence?.subjectCommit ?? null;
|
|
62
68
|
if (!commit) {
|
|
69
|
+
// v2.6.8 T2: the job-identity HEAD read is tickmarkr's own git — pinned in a linked checkout, refused when raced
|
|
63
70
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
const r = await shGit("git rev-parse HEAD", cwd);
|
|
72
|
+
if (r.code === 0)
|
|
73
|
+
commit = r.stdout.trim();
|
|
66
74
|
}
|
|
67
75
|
catch { /* unknown remains explicit */ }
|
|
68
76
|
}
|
|
@@ -91,9 +99,10 @@ export async function withVerificationJob(command, cwd, reportPath, evidence, ru
|
|
|
91
99
|
report.reservationReused = hasRepositoryLeaseOwnership() && !jobs.getStore();
|
|
92
100
|
// A process descendant may share its ancestor's scheduler capability only after the file
|
|
93
101
|
// generation and strict ancestry have been verified. A same-pid sibling always queues fresh.
|
|
94
|
-
const
|
|
95
|
-
|
|
102
|
+
const leasePath = await repositoryLeasePath(cwd);
|
|
103
|
+
const inherited = !hasRepositoryLeaseOwnership() && await reentrantHolder(leasePath, process.env[REPOSITORY_LEASE_TOKEN_ENV]);
|
|
96
104
|
const reserveCommand = report.reservationReused || inherited ? withCommandLease : withFreshCommandLease;
|
|
105
|
+
let owner; // the live holder the reservation's in-process owner reaps at release
|
|
97
106
|
outcome = await reserveCommand(command, () => withRepositoryLease(cwd, () => {
|
|
98
107
|
signal?.throwIfAborted();
|
|
99
108
|
report.admittedAt = clock();
|
|
@@ -104,14 +113,27 @@ export async function withVerificationJob(command, cwd, reportPath, evidence, ru
|
|
|
104
113
|
return await run();
|
|
105
114
|
}
|
|
106
115
|
finally {
|
|
107
|
-
|
|
116
|
+
try {
|
|
117
|
+
await reapRepositoryChildren({ pid: process.pid, cwd, at: report.queuedAt, token: report.id }, 20, VERIFICATION_JOB_TOKEN_ENV);
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
// v2.6.8 T2: a nested verify records its shells' birth-checked groups in the reservation FILE, never in the
|
|
121
|
+
// in-process owner's holder, which its release reaps. Carry them back to that owner — this job's own
|
|
122
|
+
// reservation or a reused enclosing one — the same in-memory merge registerRepositoryChild makes, even
|
|
123
|
+
// when the job census above failed (its error still propagates).
|
|
124
|
+
const recorded = owner?.pid === process.pid ? readHolder(leasePath) : undefined;
|
|
125
|
+
if (owner && recorded?.token === owner.token) {
|
|
126
|
+
owner.roots = [...new Set([...(owner.roots ?? []), ...(recorded.roots ?? [])])];
|
|
127
|
+
owner.rootBirths = { ...owner.rootBirths, ...recorded.rootBirths };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
108
130
|
report.reapedAt = clock();
|
|
109
131
|
job.persist();
|
|
110
132
|
}
|
|
111
133
|
}));
|
|
112
134
|
}, { isolated: true, protectOrphans: true, independent: !!jobs.getStore(), signal, pollMs: 20,
|
|
113
135
|
onWait: holder => { report.waitingOn = { ...holder }; job.persist(); },
|
|
114
|
-
onAdmission: holder => { report.repositoryAdmittedAt = clock(); report.reservation = { ...holder }; job.persist(); },
|
|
136
|
+
onAdmission: holder => { owner = holder; report.repositoryAdmittedAt = clock(); report.reservation = { ...holder }; job.persist(); },
|
|
115
137
|
onRelease: () => { report.reapedAt ??= clock(); report.repositoryReleasedAt = clock(); job.persist(); } }));
|
|
116
138
|
report.releasedAt = clock();
|
|
117
139
|
report.state = signal?.aborted ? "cancelled" : outcome.pass ? "completed" : "failed";
|
|
@@ -157,7 +157,7 @@ export type FleetStagedRouting = {
|
|
|
157
157
|
ok: false;
|
|
158
158
|
error: string;
|
|
159
159
|
};
|
|
160
|
-
export declare function FleetApp({ ageMs, agents, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, reloadGuard, stagedRouting, holderOf, entry, initialJudge, judgeSeats, viewRows, frameColumns, frameRows, ...initialDenyProps }: InitialDenyProps & {
|
|
160
|
+
export declare function FleetApp({ ageMs, agents, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, layerStamp, reloadGuard, stagedRouting, holderOf, entry, initialJudge, judgeSeats, viewRows, frameColumns, frameRows, ...initialDenyProps }: InitialDenyProps & {
|
|
161
161
|
ageMs: number | null;
|
|
162
162
|
agents: AgentCli[];
|
|
163
163
|
/** OBS-1046: adapter ids and adapter:model keys routing.allow leaves out of the fleet */
|
|
@@ -181,6 +181,9 @@ export declare function FleetApp({ ageMs, agents, initialAllowOut, initialEffort
|
|
|
181
181
|
/** OBS-1052(3): advisory review no-verdict history lines — display only, never a routing input */
|
|
182
182
|
reviewAdvisories?: string[];
|
|
183
183
|
reviewOverlay: (state: FleetEditorState) => FleetOverlayReview;
|
|
184
|
+
/** B-KEYS r2 (D-1446): the loaded overlay layers' identity (fleet: both overlay files' bytes) — the rows
|
|
185
|
+
* derive from them, so a layer written during the session re-derives every row; read once per render */
|
|
186
|
+
layerStamp?: () => string;
|
|
184
187
|
reloadGuard: (bytes: string) => string | null;
|
|
185
188
|
/** the routing policy the staged deny sets load as (fleet: the config loader over the candidate
|
|
186
189
|
* bytes); absent ⇒ the four staged sets alone, with no allowlist */
|
|
@@ -199,7 +202,7 @@ export declare function FleetApp({ ageMs, agents, initialAllowOut, initialEffort
|
|
|
199
202
|
/** terminal height — set only on a real TTY so the footer sticks to the bottom */
|
|
200
203
|
frameRows?: number;
|
|
201
204
|
}): import("react").JSX.Element;
|
|
202
|
-
export declare function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, reloadGuard, stagedRouting, holderOf, entry, initialJudge, judgeSeats, initialInput, input, output, debug, ...initialDenyProps }: InitialDenyProps & {
|
|
205
|
+
export declare function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut, initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories, reviewOverlay, layerStamp, reloadGuard, stagedRouting, holderOf, entry, initialJudge, judgeSeats, initialInput, input, output, debug, ...initialDenyProps }: InitialDenyProps & {
|
|
203
206
|
ageMs: number | null;
|
|
204
207
|
adapters: WorkerAdapter[];
|
|
205
208
|
health: Record<string, AuthHealth>;
|
|
@@ -224,6 +227,8 @@ export declare function runFleetInkEditor({ ageMs, adapters, health, initialAllo
|
|
|
224
227
|
/** OBS-1052(3): advisory review no-verdict history lines — display only, never a routing input */
|
|
225
228
|
reviewAdvisories?: string[];
|
|
226
229
|
reviewOverlay: (state: FleetEditorState) => FleetOverlayReview;
|
|
230
|
+
/** B-KEYS r2: the loaded overlay layers' identity (fleet: both overlay files' bytes), read once per render */
|
|
231
|
+
layerStamp?: () => string;
|
|
227
232
|
reloadGuard: (bytes: string) => string | null;
|
|
228
233
|
/** the routing policy the staged deny sets load as (fleet: the config loader over the candidate
|
|
229
234
|
* bytes); absent ⇒ the four staged sets alone, with no allowlist */
|
|
@@ -49,7 +49,7 @@ const REACH_CHOICES = [
|
|
|
49
49
|
{ id: "out-workers", label: "out · workers", gloss: "workers skip it; judge/review/consult unaffected" },
|
|
50
50
|
{ id: "out-all", label: "out · all seats", gloss: "no seat routes here" },
|
|
51
51
|
];
|
|
52
|
-
export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts = {}, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories = [], reviewOverlay, reloadGuard, stagedRouting, holderOf, entry = "probe", initialJudge = "", judgeSeats = [], viewRows = Number.POSITIVE_INFINITY, frameColumns = 100, frameRows, ...initialDenyProps }) {
|
|
52
|
+
export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts = {}, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories = [], reviewOverlay, layerStamp, reloadGuard, stagedRouting, holderOf, entry = "probe", initialJudge = "", judgeSeats = [], viewRows = Number.POSITIVE_INFINITY, frameColumns = 100, frameRows, ...initialDenyProps }) {
|
|
53
53
|
const { exit } = useApp();
|
|
54
54
|
const [, setRevision] = useState(0);
|
|
55
55
|
// the props are the fleet command's contract, one per schema-enumerated scope (initialDeny…),
|
|
@@ -137,21 +137,51 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
137
137
|
// the STAGED policy (fleet loads the candidate bytes; see stagedRouting) and the recorded
|
|
138
138
|
// identity — never literal membership of the staged sets, which missed identity, bare-model and
|
|
139
139
|
// family entries and could not tell an allowlist exclusion from a deny.
|
|
140
|
-
|
|
140
|
+
// B-KEYS (D-1338): the staged CONTENT every row and the staged policy derive from — the deny sets,
|
|
141
|
+
// the allow complement, classifications, efforts and seeds, read raw off ui (the provenance a
|
|
142
|
+
// review folds is a pure function of them). Computed per call, never per render: a handler
|
|
143
|
+
// mutates ui and reads rows in the same tick, before bump.
|
|
144
|
+
const stagedKey = () => JSON.stringify([
|
|
145
|
+
DENY_SCOPES.map((scope) => [...ui[scope.key]].sort()),
|
|
146
|
+
[...ui.allowOut].sort(),
|
|
147
|
+
ui.classifications,
|
|
148
|
+
ui.efforts,
|
|
149
|
+
ui.seededAllowOut,
|
|
150
|
+
]);
|
|
151
|
+
// ponytail: both memos live in refs keyed on stagedKey — a render-local memo reset on every keypress,
|
|
152
|
+
// so each cursor move reloaded the policy and re-derived ~800 rows' reach (B-KEYS). A cursor move,
|
|
153
|
+
// a filter, showAll or an overlay never recompute a row; any staged edit changes the key and
|
|
154
|
+
// recomputes every group. Ceiling: one JSON.stringify of the staged sets per lookup (µs); a dirty
|
|
155
|
+
// counter bumped at every mutation site if a fleet ever outgrows it.
|
|
156
|
+
const routingMemo = useRef(null);
|
|
141
157
|
const stagedPolicy = () => {
|
|
158
|
+
const key = memoKey();
|
|
159
|
+
const memo = routingMemo.current;
|
|
160
|
+
if (memo?.key === key)
|
|
161
|
+
return memo.policy;
|
|
142
162
|
const deny = stagedDeny();
|
|
143
163
|
const stage = stagedMetadata();
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
? stagedRouting(deny, stage)
|
|
150
|
-
: { ok: true, routing: { deny: denyBlockFrom(denyLists()) } },
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
return routingMemo.policy;
|
|
164
|
+
const policy = stagedRouting
|
|
165
|
+
? stagedRouting(deny, stage)
|
|
166
|
+
: { ok: true, routing: { deny: denyBlockFrom(denyLists()) } };
|
|
167
|
+
routingMemo.current = { key, policy };
|
|
168
|
+
return policy;
|
|
154
169
|
};
|
|
170
|
+
const rowsMemo = useRef({ key: "", byAdapter: new Map() });
|
|
171
|
+
// B-KEYS r2 (D-1446): staged content alone does not identify a row — the rows also derive from the loaded
|
|
172
|
+
// overlay layers (stagedRouting and holderOf read both files) and the props they read. Once per render a
|
|
173
|
+
// generation moves when the layer bytes or one of those props changes, and both memos key on it beside the
|
|
174
|
+
// staged content: a layer written mid-session shows on the next frame, as on the base, while a cursor move
|
|
175
|
+
// over unchanged layers still derives nothing. Cost: one read of each overlay per frame (the B2 previewCfg key).
|
|
176
|
+
const layers = useRef({ stamp: "", inputs: [], gen: 0 });
|
|
177
|
+
{
|
|
178
|
+
const stamp = layerStamp?.() ?? "";
|
|
179
|
+
const inputs = [modelGroups, stagedRouting, holderOf];
|
|
180
|
+
if (stamp !== layers.current.stamp || inputs.some((input, at) => input !== layers.current.inputs[at])) {
|
|
181
|
+
layers.current = { stamp, inputs, gen: layers.current.gen + 1 };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const memoKey = () => `${layers.current.gen}\u0000${stagedKey()}`;
|
|
155
185
|
// OBS-1099 add.1: the browser's deny discovery ranges over the schema-enumerated scopes (the
|
|
156
186
|
// route collector names its four paths by hand, so a scope the schema gains would never render
|
|
157
187
|
// or lift here). A flat scope reaches every seat; a nested one only the seat its block names
|
|
@@ -321,7 +351,19 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
321
351
|
const found = members.map(coveringFor).filter((covering) => covering !== undefined);
|
|
322
352
|
return found.find((covering) => covering.shared) ?? found[0];
|
|
323
353
|
};
|
|
354
|
+
// B-KEYS: one derivation per group per staged state — the memo above hands every later pass
|
|
355
|
+
// (visibleCount per rail row, modelRows, the hidden and folded counts, a handler's lookup) the
|
|
356
|
+
// same rows; the returned array is read, never mutated, by every caller.
|
|
324
357
|
const groupRows = (group) => {
|
|
358
|
+
const key = memoKey();
|
|
359
|
+
if (rowsMemo.current.key !== key)
|
|
360
|
+
rowsMemo.current = { key, byAdapter: new Map() };
|
|
361
|
+
const memo = rowsMemo.current.byAdapter;
|
|
362
|
+
const rows = memo.get(group.adapter) ?? deriveGroupRows(group);
|
|
363
|
+
memo.set(group.adapter, rows);
|
|
364
|
+
return rows;
|
|
365
|
+
};
|
|
366
|
+
const deriveGroupRows = (group) => {
|
|
325
367
|
const rows = group.rows.map((row) => {
|
|
326
368
|
const members = membersOf(group.adapter, row);
|
|
327
369
|
const staged = ui.classifications.find((classification) => classification.adapter === group.adapter
|
|
@@ -2180,7 +2222,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
2180
2222
|
? _jsx(Text, { color: INK.warn, children: ` ! ${clip(ui.notice, width - 4)}` })
|
|
2181
2223
|
: details.map((line) => _jsx(Text, { dimColor: true, children: ` ${clip(line, width - 2)}` }, line)), _jsx(KeyBar, { binds: keyBinds() })] }));
|
|
2182
2224
|
}
|
|
2183
|
-
export async function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut = [], initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories = [], reviewOverlay, reloadGuard, stagedRouting, holderOf, entry = "probe", initialJudge = "", judgeSeats = [], initialInput = [], input, output, debug = false, ...initialDenyProps }) {
|
|
2225
|
+
export async function runFleetInkEditor({ ageMs, adapters, health, initialAllowOut = [], initialEfforts, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewAdvisories = [], reviewOverlay, layerStamp, reloadGuard, stagedRouting, holderOf, entry = "probe", initialJudge = "", judgeSeats = [], initialInput = [], input, output, debug = false, ...initialDenyProps }) {
|
|
2184
2226
|
const agents = adapters.flatMap((adapter) => {
|
|
2185
2227
|
const state = health[adapter.id];
|
|
2186
2228
|
if (!state?.installed)
|
|
@@ -2204,7 +2246,7 @@ export async function runFleetInkEditor({ ageMs, adapters, health, initialAllowO
|
|
|
2204
2246
|
// rebuilt from output.columns/rows so a pane resize re-renders at the new size. Same root
|
|
2205
2247
|
// element type on every rerender ⇒ React keeps the component instance, so staged edits,
|
|
2206
2248
|
// cursor, and overlays all survive the resize.
|
|
2207
|
-
const editorElement = () => (_jsx(FleetApp, { ageMs: ageMs, agents: agents, ...initialDenyProps, initialAllowOut: initialAllowOut, initialEfforts: initialEfforts, modelGroups: declaredModelGroups, initialMode: initialMode, modeOptions: modeOptions, initialMap: initialMap, modePreview: modePreview, shapeRows: shapeRows, candidatesForShape: candidatesForShape, preferOptionsForShape: preferOptionsForShape, initialSteering: initialSteering, steeringOptionsFor: steeringOptionsFor, reviewAdvisories: reviewAdvisories, reviewOverlay: reviewOverlay, reloadGuard: reloadGuard, stagedRouting: stagedRouting, holderOf: holderOf, entry: entry, initialJudge: initialJudge, judgeSeats: judgeSeats,
|
|
2249
|
+
const editorElement = () => (_jsx(FleetApp, { ageMs: ageMs, agents: agents, ...initialDenyProps, initialAllowOut: initialAllowOut, initialEfforts: initialEfforts, modelGroups: declaredModelGroups, initialMode: initialMode, modeOptions: modeOptions, initialMap: initialMap, modePreview: modePreview, shapeRows: shapeRows, candidatesForShape: candidatesForShape, preferOptionsForShape: preferOptionsForShape, initialSteering: initialSteering, steeringOptionsFor: steeringOptionsFor, reviewAdvisories: reviewAdvisories, reviewOverlay: reviewOverlay, layerStamp: layerStamp, reloadGuard: reloadGuard, stagedRouting: stagedRouting, holderOf: holderOf, entry: entry, initialJudge: initialJudge, judgeSeats: judgeSeats,
|
|
2208
2250
|
// v1.90.9: cap every list to the terminal minus chrome so the cursor and header can
|
|
2209
2251
|
// never scroll off-screen (omp's 218-model list did exactly that pre-cap).
|
|
2210
2252
|
// OBS-523: chrome is ≤12 rows worst case (header, borders, padding, scope+search,
|