tickmarkr 2.7.1 → 2.7.2
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/gates/baseline.js +6 -2
- package/dist/gates/run-gates.js +3 -7
- package/dist/gates/test-manifest.d.ts +3 -0
- package/dist/gates/test-manifest.js +14 -9
- package/dist/run/daemon.d.ts +14 -1
- package/dist/run/daemon.js +19 -10
- package/package.json +1 -1
package/dist/gates/baseline.js
CHANGED
|
@@ -7,7 +7,7 @@ import { basename, join } from "node:path";
|
|
|
7
7
|
import { DEFAULT_EVIDENCE_QUOTA_BYTES } from "../config/config.js";
|
|
8
8
|
import { dependencyLinkRefusal, DEFAULT_SHELL_TIMEOUT_MS, describeCapacity, sameCapacity, sh } from "../run/git.js";
|
|
9
9
|
import { executionSignal } from "../run/execution-budget.js";
|
|
10
|
-
import { isVitestTestCommand, manifestFileCount } from "./test-manifest.js";
|
|
10
|
+
import { isVitestTestCommand, manifestChildEnv, manifestFileCount } from "./test-manifest.js";
|
|
11
11
|
export const EVIDENCE_TAIL_BYTES = 16 * 1024;
|
|
12
12
|
/** The default per-run quota; `gates.evidenceQuotaBytes` in config overrides it (OBS-1140). */
|
|
13
13
|
export const EVIDENCE_RUN_QUOTA_BYTES = DEFAULT_EVIDENCE_QUOTA_BYTES;
|
|
@@ -726,7 +726,11 @@ export async function captureBaseline(cwd, commands, opts = {}) {
|
|
|
726
726
|
continue;
|
|
727
727
|
}
|
|
728
728
|
const evidence = beginGateEvidence(cwd, name, cmd, opts.evidence);
|
|
729
|
-
|
|
729
|
+
// Queue row 104 (D-1644): a manifested test command is captured under the test gate's own runner environment
|
|
730
|
+
// (node_modules/.bin first), so a bare repo-local runner resolves here exactly as it does in the gate — built on the
|
|
731
|
+
// caller's own evidence environment when it supplies one (D-1659), never in place of it.
|
|
732
|
+
const env = (name === "test" || name === "tipTest") && isVitestTestCommand(cmd, cwd) ? manifestChildEnv(cwd, opts.evidence?.env) : opts.evidence?.env;
|
|
733
|
+
const r = await sh(cmd, cwd, CAPTURE_CEILING_MS, { env, onReceipt: receipt => { evidence.observe(receipt); opts.onReceipt?.(receipt); } });
|
|
730
734
|
(base.evidenceReceipts ??= {})[name] = evidence.finish(r.stdout, r.stderr);
|
|
731
735
|
// ponytail: strip the executing cwd so repo-root capture and worktree compare fingerprint identically; /private-vs-/tmp symlink variance is out of scope
|
|
732
736
|
// ponytail: a capture that was itself killed records the ceiling as its "measurement", which
|
package/dist/gates/run-gates.js
CHANGED
|
@@ -15,11 +15,11 @@ import { disallowedBy, observedSeat } from "../route/preference.js";
|
|
|
15
15
|
import { marginalCostRank } from "../route/router.js";
|
|
16
16
|
import { carriedAuthorVendors, gateReviewerFloor, pickReviewer, reviewGate } from "./review.js";
|
|
17
17
|
import { scopeGate } from "./scope.js";
|
|
18
|
-
import { discoverTestManifest, evaluateManifestedTest, isVitestTestCommand,
|
|
18
|
+
import { discoverTestManifest, evaluateManifestedTest, isVitestTestCommand, manifestChildEnv } from "./test-manifest.js";
|
|
19
19
|
import { RUNNER_INFRA_DIAGNOSTIC_RE, timeoutShaped } from "./timeout-shaped.js";
|
|
20
20
|
import { executionSignal } from "../run/execution-budget.js";
|
|
21
21
|
import { failureDisposition } from "../run/recovery.js";
|
|
22
|
-
import { dependencyLinkRefusal,
|
|
22
|
+
import { dependencyLinkRefusal, preserveWorktree, producerFields, readCapacity, shGit, resolvedCapacity, sameCapacity, verificationProtocol } from "../run/git.js";
|
|
23
23
|
import { withJudgeInvocationEvidence } from "../run/journal.js";
|
|
24
24
|
import { computeVerificationIdentity, verificationIdentityKey, formatReusedRow, getVerdictStore, isInfraResult, resolveStateDir, reusedIdentity, } from "./cache.js";
|
|
25
25
|
const productionLoadProvider = () => loadavg()[0] ?? 0;
|
|
@@ -285,11 +285,7 @@ export function diagnosticAdmission(baseline, selected) {
|
|
|
285
285
|
* discovery receives (test-manifest.ts manifestEnvironment), so it compares with the one a verdict
|
|
286
286
|
* certified. Undefined when the runner cannot list — an unlisted manifest certifies nothing. */
|
|
287
287
|
async function listFullManifest(cmd, worktree) {
|
|
288
|
-
const env =
|
|
289
|
-
[VITEST_CACHE_ENV]: worktreeVitestCache(worktree),
|
|
290
|
-
[FORK_CAP_ENV]: String(resolvedCapacity().forkCap), [SUITE_PARENT_ENV]: String(process.pid) };
|
|
291
|
-
for (const key of [...ROUTING_ENV_SEAMS, "VITEST", "TEST", "VITEST_WORKER_ID", "VITEST_POOL_ID"])
|
|
292
|
-
delete env[key];
|
|
288
|
+
const env = manifestChildEnv(worktree);
|
|
293
289
|
const dir = mkdtempSync(join(tmpdir(), "tickmarkr-full-manifest-"));
|
|
294
290
|
try {
|
|
295
291
|
return (await discoverTestManifest(cmd, worktree, { dir, nonce: randomUUID(), env })).files;
|
|
@@ -206,6 +206,9 @@ export interface ManifestGateOutcome {
|
|
|
206
206
|
exitCode: number;
|
|
207
207
|
reportPath: string;
|
|
208
208
|
}
|
|
209
|
+
/** Queue row 104 (D-1644): THE child environment of a manifested test command — its listing and run here, the full
|
|
210
|
+
* manifest re-listing (run-gates.ts) and its baseline capture (baseline.ts) — so every one resolves the same runner. */
|
|
211
|
+
export declare function manifestChildEnv(cwd: string, base?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
|
|
209
212
|
export interface DiscoveredManifest {
|
|
210
213
|
files: string[];
|
|
211
214
|
listing: string;
|
|
@@ -504,17 +504,22 @@ export function runManifestedTest(cmd, cwd, opts) {
|
|
|
504
504
|
* `npm_config_ignore_scripts` from the environment over every npmrc); the verdict records it so a
|
|
505
505
|
* verdict measured with `pretest` hooks is never compared to one without. */
|
|
506
506
|
function manifestEnvironment(cwd) {
|
|
507
|
-
const env =
|
|
507
|
+
const env = manifestBaseEnv(cwd);
|
|
508
|
+
return { env: scrubbedManifestEnv(env), verification: verificationProtocol(env, cwd) };
|
|
509
|
+
}
|
|
510
|
+
function manifestBaseEnv(cwd, base = process.env) {
|
|
511
|
+
return { ...base, PATH: `${join(cwd, "node_modules/.bin")}:${base.PATH ?? ""}`,
|
|
508
512
|
[VITEST_CACHE_ENV]: worktreeVitestCache(cwd),
|
|
509
513
|
[FORK_CAP_ENV]: String(resolvedCapacity().forkCap), [SUITE_PARENT_ENV]: String(process.pid) };
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
514
|
+
}
|
|
515
|
+
// A gate can itself be tested under Vitest. Do not give the child the outer worker identity.
|
|
516
|
+
const OUTER_RUNNER_KEYS = new Set([...ROUTING_ENV_SEAMS, "VITEST", "TEST", "VITEST_WORKER_ID", "VITEST_POOL_ID"]);
|
|
517
|
+
const scrubbedManifestEnv = (env) => Object.fromEntries(Object.entries(env).filter(([key]) => !OUTER_RUNNER_KEYS.has(key)));
|
|
518
|
+
/** Queue row 104 (D-1644): THE child environment of a manifested test command — its listing and run here, the full
|
|
519
|
+
* manifest re-listing (run-gates.ts) and its baseline capture (baseline.ts) — so every one resolves the same runner. */
|
|
520
|
+
export function manifestChildEnv(cwd, base) {
|
|
521
|
+
// built on `base` when a caller supplies its own environment (captureBaseline's evidence env), else on process.env
|
|
522
|
+
return scrubbedManifestEnv(manifestBaseEnv(cwd, base));
|
|
518
523
|
}
|
|
519
524
|
/** OBS-1044: THE discovery seam — the files the runner would collect under this exact invocation,
|
|
520
525
|
* from its own listing and never from a stdout summary. The gate and the baseline capture share it,
|
package/dist/run/daemon.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { type DriverChoice } from "../drivers/index.js";
|
|
|
4
4
|
import { type ExecutorDriver, type Slot } from "../drivers/types.js";
|
|
5
5
|
import { type Baseline, type BaselineProvenance, type GateEvidenceOptions } from "../gates/baseline.js";
|
|
6
6
|
import type { GateResult } from "../gates/types.js";
|
|
7
|
-
import { type RunGraph } from "../graph/schema.js";
|
|
7
|
+
import { type RunGraph, type Task } from "../graph/schema.js";
|
|
8
8
|
import { type PreserveProducer } from "./git.js";
|
|
9
9
|
import { Journal, type JournalEvent } from "./journal.js";
|
|
10
10
|
export declare function closeLiveSlot(liveSlots: Set<Slot>, driver: Pick<ExecutorDriver, "close">, slot: Slot): Promise<void>;
|
|
@@ -144,6 +144,19 @@ export declare const gateSatisfied: (g: GateResult) => boolean;
|
|
|
144
144
|
* preference (ranked last, never excluded) — every review-pool-demotion except the launch-caused ones, which
|
|
145
145
|
* never demote a seat (live or replayed). plan.ts previews a resume with this same fold. */
|
|
146
146
|
export declare function replayedReviewerDemotions(events: readonly JournalEvent[]): Set<string>;
|
|
147
|
+
/** C: a journaled test red whose failing-file attribution is nonempty, trustworthy and complete (repair
|
|
148
|
+
* selection's own rule: a behavioral red on a real subject from a full suite or a screen that names
|
|
149
|
+
* every failure), POSITIVELY certified complete by its runner, uncontradicted by the files its own text
|
|
150
|
+
* names, and wholly outside a declared files[]. Empty files[] is unrestricted and never qualifies;
|
|
151
|
+
* every other shape keeps its replay. */
|
|
152
|
+
export declare function outsideScopeRed(t: Task, taskId: string, data: Record<string, unknown>, commit: string): boolean;
|
|
153
|
+
/** OBS-1106 residual, D-607: what ONE isolated rerun of a timeout-shaped red's attributed `files` says about that
|
|
154
|
+
* red. Lifted unchanged out of adjudicateInfraShapedRed (queue row 102) so its closed outcome table runs on this
|
|
155
|
+
* function in-process. Pure: it reads only its arguments; the caller classifies the rerun first. */
|
|
156
|
+
export declare function reobservationOutcome(files: readonly string[], rerun: GateResult): {
|
|
157
|
+
outcome: "reproduced" | "passed" | "ambiguous";
|
|
158
|
+
selection: string[] | undefined;
|
|
159
|
+
};
|
|
147
160
|
/**
|
|
148
161
|
* T4 (OBS-265): the journal with the review objections a round did NOT hinge on removed. Judge and
|
|
149
162
|
* review are now launched together, so a round can journal a failed review that the serial walk would
|
package/dist/run/daemon.js
CHANGED
|
@@ -409,7 +409,7 @@ const RUNNER_CERTIFICATE_RE = /runner-level diagnostic: never-started ([^;\s]+);
|
|
|
409
409
|
* every failure), POSITIVELY certified complete by its runner, uncontradicted by the files its own text
|
|
410
410
|
* names, and wholly outside a declared files[]. Empty files[] is unrestricted and never qualifies;
|
|
411
411
|
* every other shape keeps its replay. */
|
|
412
|
-
function outsideScopeRed(t, taskId, data, commit) {
|
|
412
|
+
export function outsideScopeRed(t, taskId, data, commit) {
|
|
413
413
|
if (!t.files.length || data.gate !== "test" || data.pass !== false || data.skipped === true || data.commit !== commit
|
|
414
414
|
// a failed producer — an infra row, a dirty-tree refusal, a recovery the runner could not finish —
|
|
415
415
|
// never attributes anything, whatever files it lists
|
|
@@ -456,6 +456,21 @@ const observedSelection = (g) => {
|
|
|
456
456
|
/** D-607: on the manifest path only the reporter's own attribution counts — a `FAIL path` line inside
|
|
457
457
|
* captured stdout is text, not attribution. Headline lexing is for runners without a reporter. */
|
|
458
458
|
const rerunFailingFiles = (g) => metaFiles(g.meta?.manifest) ? [...new Set(metaFiles(g.meta?.failingFiles) ?? [])].sort() : attributedFailingFiles(g);
|
|
459
|
+
/** OBS-1106 residual, D-607: what ONE isolated rerun of a timeout-shaped red's attributed `files` says about that
|
|
460
|
+
* red. Lifted unchanged out of adjudicateInfraShapedRed (queue row 102) so its closed outcome table runs on this
|
|
461
|
+
* function in-process. Pure: it reads only its arguments; the caller classifies the rerun first. */
|
|
462
|
+
export function reobservationOutcome(files, rerun) {
|
|
463
|
+
// Reproduced only by failure evidence the rerun ATTRIBUTES to one of exactly these files, from an
|
|
464
|
+
// OBSERVED selection that was exactly these files (D-607): a path merely printed in output, a
|
|
465
|
+
// positional filter that also collected another file, or a runner that ignored the file
|
|
466
|
+
// arguments and reported no manifest of its selection, says nothing about the original red.
|
|
467
|
+
const failing = rerunFailingFiles(rerun);
|
|
468
|
+
const selection = observedSelection(rerun);
|
|
469
|
+
const isolated = selection !== undefined && selection.length === files.length
|
|
470
|
+
&& files.every((file) => selection.includes(file)) && failing.every((file) => files.includes(file));
|
|
471
|
+
const outcome = rerun.pass ? "passed" : isolated && !isInfraResult(rerun) && failing.length ? "reproduced" : "ambiguous";
|
|
472
|
+
return { outcome, selection };
|
|
473
|
+
}
|
|
459
474
|
// v1.85 T3: the gates whose failure IS a deterministic measurement — a machine re-ran a command over a
|
|
460
475
|
// tree and printed the same bytes. Those are the failures the fingerprint cap governs (the ruling names
|
|
461
476
|
// it a "deterministic-gate" cap): a third identical answer to a question already answered twice is the
|
|
@@ -3080,15 +3095,9 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
3080
3095
|
rerun = await withCommandContext(t.id, () => reobserveTestFiles(wt, commands.test, baseline, files, journal.dir))
|
|
3081
3096
|
.catch((error) => ({ gate: "test", pass: false, details: `infra: rerun failed: ${error instanceof Error ? error.message : String(error)}`, meta: { infra: true } }));
|
|
3082
3097
|
classifyInfraResult(rerun);
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
// arguments and reported no manifest of its selection, says nothing about the original red.
|
|
3087
|
-
const failing = rerunFailingFiles(rerun);
|
|
3088
|
-
selection = observedSelection(rerun);
|
|
3089
|
-
const isolated = selection !== undefined && selection.length === files.length
|
|
3090
|
-
&& files.every((file) => selection.includes(file)) && failing.every((file) => files.includes(file));
|
|
3091
|
-
outcome = rerun.pass ? "passed" : isolated && !isInfraResult(rerun) && failing.length ? "reproduced" : "ambiguous";
|
|
3098
|
+
const judged = reobservationOutcome(files, rerun);
|
|
3099
|
+
outcome = judged.outcome;
|
|
3100
|
+
selection = judged.selection;
|
|
3092
3101
|
}
|
|
3093
3102
|
journal.append("gate-reobserved", t.id, {
|
|
3094
3103
|
gate: "test", attempt, commit: subject, fingerprint, ...(observation ? { observation } : {}), files, outcome,
|