@akagilnc/pi-workflow-roles 0.1.1783 → 0.1.1792
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/public-cli/main.js +176 -24
- package/dist/reviewer-git-snapshot.js +1 -7
- package/dist/reviewer-workspace.js +11 -30
- package/package.json +1 -1
- package/src/public-cli/explicit-internal.ts +100 -2
- package/src/public-cli/reviewer-run.ts +3 -0
- package/src/public-cli/settlement.ts +16 -2
- package/src/reviewer-git-snapshot.ts +3 -11
- package/src/reviewer-workspace.ts +12 -18
- package/src/role-runtime.ts +23 -1
package/dist/public-cli/main.js
CHANGED
|
@@ -16718,13 +16718,149 @@ var init_invocation = __esm({
|
|
|
16718
16718
|
}
|
|
16719
16719
|
});
|
|
16720
16720
|
|
|
16721
|
+
// src/reviewer-git-snapshot.ts
|
|
16722
|
+
var init_reviewer_git_snapshot = __esm({
|
|
16723
|
+
"src/reviewer-git-snapshot.ts"() {
|
|
16724
|
+
"use strict";
|
|
16725
|
+
}
|
|
16726
|
+
});
|
|
16727
|
+
|
|
16728
|
+
// src/reviewer-preflight-error.ts
|
|
16729
|
+
var init_reviewer_preflight_error = __esm({
|
|
16730
|
+
"src/reviewer-preflight-error.ts"() {
|
|
16731
|
+
"use strict";
|
|
16732
|
+
}
|
|
16733
|
+
});
|
|
16734
|
+
|
|
16735
|
+
// src/reviewer-pinned-git.ts
|
|
16736
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
16737
|
+
import { promisify as promisify2 } from "node:util";
|
|
16738
|
+
var execFileAsync2;
|
|
16739
|
+
var init_reviewer_pinned_git = __esm({
|
|
16740
|
+
"src/reviewer-pinned-git.ts"() {
|
|
16741
|
+
"use strict";
|
|
16742
|
+
init_reviewer_git_snapshot();
|
|
16743
|
+
init_sha256();
|
|
16744
|
+
init_reviewer_preflight_error();
|
|
16745
|
+
execFileAsync2 = promisify2(execFile2);
|
|
16746
|
+
}
|
|
16747
|
+
});
|
|
16748
|
+
|
|
16749
|
+
// src/reviewer-prompt-identity.ts
|
|
16750
|
+
var init_reviewer_prompt_identity = __esm({
|
|
16751
|
+
"src/reviewer-prompt-identity.ts"() {
|
|
16752
|
+
"use strict";
|
|
16753
|
+
}
|
|
16754
|
+
});
|
|
16755
|
+
|
|
16756
|
+
// src/reviewer-scope-prompt.ts
|
|
16757
|
+
var init_reviewer_scope_prompt = __esm({
|
|
16758
|
+
"src/reviewer-scope-prompt.ts"() {
|
|
16759
|
+
"use strict";
|
|
16760
|
+
}
|
|
16761
|
+
});
|
|
16762
|
+
|
|
16763
|
+
// src/reviewer-construction.ts
|
|
16764
|
+
var REVIEWER_CONSTRUCTION_RECIPE, REVIEWER_AXIS_OUTPUT_ADAPTER, REVIEWER_STANDARDS_CONCLUSION_KEYS, REVIEWER_STANDARDS_CONCLUSION_LABELS;
|
|
16765
|
+
var init_reviewer_construction = __esm({
|
|
16766
|
+
"src/reviewer-construction.ts"() {
|
|
16767
|
+
"use strict";
|
|
16768
|
+
init_sha256();
|
|
16769
|
+
init_reviewer_scope_prompt();
|
|
16770
|
+
REVIEWER_CONSTRUCTION_RECIPE = Object.freeze({
|
|
16771
|
+
recipeId: "reviewer-common-bundle",
|
|
16772
|
+
version: 1,
|
|
16773
|
+
runtimeVersion: "1",
|
|
16774
|
+
implementationSha256: sha256Hex("reviewer-common-bundle:v1:direct-text-prompts")
|
|
16775
|
+
});
|
|
16776
|
+
REVIEWER_AXIS_OUTPUT_ADAPTER = Object.freeze({
|
|
16777
|
+
adapterId: "reviewer-axis-output",
|
|
16778
|
+
version: 1,
|
|
16779
|
+
implementationSha256: sha256Hex("reviewer-axis-output:v1:single-axis-verbatim-report+standards-three-priorities")
|
|
16780
|
+
});
|
|
16781
|
+
REVIEWER_STANDARDS_CONCLUSION_KEYS = Object.freeze([
|
|
16782
|
+
"constitutionality",
|
|
16783
|
+
"minimum-necessary-test-cost",
|
|
16784
|
+
"complexity"
|
|
16785
|
+
]);
|
|
16786
|
+
REVIEWER_STANDARDS_CONCLUSION_LABELS = Object.freeze({
|
|
16787
|
+
constitutionality: "constitutionality",
|
|
16788
|
+
"minimum-necessary-test-cost": "minimum-necessary test cost",
|
|
16789
|
+
complexity: "complexity"
|
|
16790
|
+
});
|
|
16791
|
+
}
|
|
16792
|
+
});
|
|
16793
|
+
|
|
16794
|
+
// src/reviewer-dispatch.ts
|
|
16795
|
+
var REVIEWER_PREFLIGHT_VIOLATIONS;
|
|
16796
|
+
var init_reviewer_dispatch = __esm({
|
|
16797
|
+
"src/reviewer-dispatch.ts"() {
|
|
16798
|
+
"use strict";
|
|
16799
|
+
init_reviewer_git_snapshot();
|
|
16800
|
+
init_reviewer_pinned_git();
|
|
16801
|
+
init_reviewer_pinned_git();
|
|
16802
|
+
init_reviewer_prompt_identity();
|
|
16803
|
+
init_sha256();
|
|
16804
|
+
init_reviewer_construction();
|
|
16805
|
+
init_reviewer_preflight_error();
|
|
16806
|
+
init_sha256();
|
|
16807
|
+
init_reviewer_prompt_identity();
|
|
16808
|
+
REVIEWER_PREFLIGHT_VIOLATIONS = ["base-invalid", "range-invalid", "prompt-identity-invalid", "target-drift"];
|
|
16809
|
+
}
|
|
16810
|
+
});
|
|
16811
|
+
|
|
16721
16812
|
// src/public-cli/explicit-internal.ts
|
|
16722
|
-
import { execFile as
|
|
16723
|
-
import { constants } from "node:fs";
|
|
16724
|
-
import { access, realpath as realpath3 } from "node:fs/promises";
|
|
16813
|
+
import { execFile as execFile3, spawn } from "node:child_process";
|
|
16814
|
+
import { constants, writeFileSync } from "node:fs";
|
|
16815
|
+
import { access, readFile as readFile5, realpath as realpath3, unlink } from "node:fs/promises";
|
|
16725
16816
|
import { delimiter as delimiter2, isAbsolute as isAbsolute4, join as join5, resolve as resolve5 } from "node:path";
|
|
16726
16817
|
import { platform } from "node:process";
|
|
16727
|
-
import { promisify as
|
|
16818
|
+
import { promisify as promisify3 } from "node:util";
|
|
16819
|
+
function isReviewerPreflightViolation(value) {
|
|
16820
|
+
return typeof value === "string" && REVIEWER_PREFLIGHT_VIOLATIONS.includes(value);
|
|
16821
|
+
}
|
|
16822
|
+
function reviewerDispatchRejectionPath(runDirectory) {
|
|
16823
|
+
return join5(runDirectory, REVIEWER_DISPATCH_REJECTION_FILE);
|
|
16824
|
+
}
|
|
16825
|
+
async function clearReviewerDispatchRejection(runDirectory) {
|
|
16826
|
+
try {
|
|
16827
|
+
await unlink(reviewerDispatchRejectionPath(runDirectory));
|
|
16828
|
+
} catch (error) {
|
|
16829
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
16830
|
+
return;
|
|
16831
|
+
}
|
|
16832
|
+
throw error;
|
|
16833
|
+
}
|
|
16834
|
+
}
|
|
16835
|
+
async function readReviewerDispatchRejection(runDirectory) {
|
|
16836
|
+
let raw;
|
|
16837
|
+
try {
|
|
16838
|
+
raw = await readFile5(reviewerDispatchRejectionPath(runDirectory), "utf8");
|
|
16839
|
+
} catch (error) {
|
|
16840
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
16841
|
+
return void 0;
|
|
16842
|
+
}
|
|
16843
|
+
throw error;
|
|
16844
|
+
}
|
|
16845
|
+
const parsed = JSON.parse(raw);
|
|
16846
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
16847
|
+
const error = new Error("Reviewer dispatch rejection page must be a JSON object");
|
|
16848
|
+
error.name = "ReviewerDispatchRejectionContractError";
|
|
16849
|
+
throw error;
|
|
16850
|
+
}
|
|
16851
|
+
const record4 = parsed;
|
|
16852
|
+
if (typeof record4.diagnostic !== "string" || record4.diagnostic.trim() === "" || !Array.isArray(record4.violations) || record4.violations.length === 0 || record4.violations.some((value) => !isReviewerPreflightViolation(value))) {
|
|
16853
|
+
const error = new Error("Reviewer dispatch rejection page has unusable required fields");
|
|
16854
|
+
error.name = "ReviewerDispatchRejectionContractError";
|
|
16855
|
+
throw error;
|
|
16856
|
+
}
|
|
16857
|
+
return {
|
|
16858
|
+
cause: "activation",
|
|
16859
|
+
diagnostic: record4.diagnostic,
|
|
16860
|
+
identity: { name: "ReviewerDispatchRejectionError" },
|
|
16861
|
+
details: { violations: Object.freeze([...record4.violations]) }
|
|
16862
|
+
};
|
|
16863
|
+
}
|
|
16728
16864
|
function resolveInternalRoleEntrypoint(packageRoot2) {
|
|
16729
16865
|
return join5(packageRoot2, INTERNAL_ROLE_ENTRYPOINT_RELATIVE);
|
|
16730
16866
|
}
|
|
@@ -16765,7 +16901,7 @@ async function resolveSelectedPi(command, cwd, env) {
|
|
|
16765
16901
|
}
|
|
16766
16902
|
async function selectedPiIdentity(command, cwd, env) {
|
|
16767
16903
|
const executable = await resolveSelectedPi(command, cwd, env);
|
|
16768
|
-
const { stdout } = await
|
|
16904
|
+
const { stdout } = await execFileAsync3(executable, ["--version"], {
|
|
16769
16905
|
cwd,
|
|
16770
16906
|
env,
|
|
16771
16907
|
encoding: "utf8"
|
|
@@ -16802,13 +16938,15 @@ async function runExplicitInternalActivation(options) {
|
|
|
16802
16938
|
env
|
|
16803
16939
|
});
|
|
16804
16940
|
}
|
|
16805
|
-
var
|
|
16941
|
+
var REVIEWER_DISPATCH_REJECTION_FILE, execFileAsync3, defaultExplicitInternalPiRunner;
|
|
16806
16942
|
var init_explicit_internal = __esm({
|
|
16807
16943
|
"src/public-cli/explicit-internal.ts"() {
|
|
16808
16944
|
"use strict";
|
|
16809
16945
|
init_invocation();
|
|
16810
16946
|
init_registry2();
|
|
16811
|
-
|
|
16947
|
+
init_reviewer_dispatch();
|
|
16948
|
+
REVIEWER_DISPATCH_REJECTION_FILE = "typed-known-failure.json";
|
|
16949
|
+
execFileAsync3 = promisify3(execFile3);
|
|
16812
16950
|
defaultExplicitInternalPiRunner = async (args, options) => {
|
|
16813
16951
|
const command = options.env.PI_BINARY ?? "pi";
|
|
16814
16952
|
const piIdentity = await selectedPiIdentity(command, options.cwd, options.env);
|
|
@@ -16863,7 +17001,7 @@ var init_explicit_internal = __esm({
|
|
|
16863
17001
|
|
|
16864
17002
|
// src/package-resources/method-skill.ts
|
|
16865
17003
|
import { createHash as createHash3 } from "node:crypto";
|
|
16866
|
-
import { readFile as
|
|
17004
|
+
import { readFile as readFile6, realpath as realpath4 } from "node:fs/promises";
|
|
16867
17005
|
import { join as join6 } from "node:path";
|
|
16868
17006
|
function gitBlobOid(bytes) {
|
|
16869
17007
|
const body = typeof bytes === "string" ? Buffer.from(bytes, "utf8") : Buffer.from(bytes);
|
|
@@ -16982,7 +17120,7 @@ async function loadPackagedMethodSkillMaterial(packageRoot2, name) {
|
|
|
16982
17120
|
const provenancePath = join6(rootDirectory, "provenance.json");
|
|
16983
17121
|
let provenanceRaw;
|
|
16984
17122
|
try {
|
|
16985
|
-
provenanceRaw = await
|
|
17123
|
+
provenanceRaw = await readFile6(provenancePath, "utf8");
|
|
16986
17124
|
} catch (error) {
|
|
16987
17125
|
throw new PackagedMethodSkillUnavailableError(name, provenancePath, error);
|
|
16988
17126
|
}
|
|
@@ -16999,7 +17137,7 @@ async function loadPackagedMethodSkillMaterial(packageRoot2, name) {
|
|
|
16999
17137
|
const absolute = join6(rootDirectory, rel);
|
|
17000
17138
|
let bytes;
|
|
17001
17139
|
try {
|
|
17002
|
-
bytes = await
|
|
17140
|
+
bytes = await readFile6(absolute);
|
|
17003
17141
|
} catch (error) {
|
|
17004
17142
|
throw new PackagedMethodSkillUnavailableError(name, absolute, error);
|
|
17005
17143
|
}
|
|
@@ -17015,7 +17153,7 @@ async function loadPackagedMethodSkillMaterial(packageRoot2, name) {
|
|
|
17015
17153
|
let raw;
|
|
17016
17154
|
try {
|
|
17017
17155
|
skillPath = await realpath4(skillPathConfigured);
|
|
17018
|
-
raw = await
|
|
17156
|
+
raw = await readFile6(skillPath, "utf8");
|
|
17019
17157
|
} catch (error) {
|
|
17020
17158
|
throw new PackagedMethodSkillUnavailableError(name, skillPathConfigured, error);
|
|
17021
17159
|
}
|
|
@@ -17130,7 +17268,7 @@ var init_public_run_credentials = __esm({
|
|
|
17130
17268
|
});
|
|
17131
17269
|
|
|
17132
17270
|
// src/public-cli/run-lifecycle.ts
|
|
17133
|
-
import { lstat as lstat2, open, readdir as readdir2, readFile as
|
|
17271
|
+
import { lstat as lstat2, open, readdir as readdir2, readFile as readFile7, unlink as unlink2, writeFile as writeFile3 } from "node:fs/promises";
|
|
17134
17272
|
import { join as join7 } from "node:path";
|
|
17135
17273
|
function isV1ResumableProvider(provider) {
|
|
17136
17274
|
return V1_RESUMABLE_PROVIDERS.includes(provider);
|
|
@@ -17140,7 +17278,7 @@ function typedProviderHttpPath(runDirectory) {
|
|
|
17140
17278
|
}
|
|
17141
17279
|
async function clearTypedProviderHttpObservation(runDirectory) {
|
|
17142
17280
|
try {
|
|
17143
|
-
await
|
|
17281
|
+
await unlink2(typedProviderHttpPath(runDirectory));
|
|
17144
17282
|
} catch (error) {
|
|
17145
17283
|
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
17146
17284
|
return;
|
|
@@ -17151,7 +17289,7 @@ async function clearTypedProviderHttpObservation(runDirectory) {
|
|
|
17151
17289
|
async function readTypedHttp429Observation(runDirectory) {
|
|
17152
17290
|
try {
|
|
17153
17291
|
const raw = JSON.parse(
|
|
17154
|
-
await
|
|
17292
|
+
await readFile7(typedProviderHttpPath(runDirectory), "utf8")
|
|
17155
17293
|
);
|
|
17156
17294
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
17157
17295
|
return void 0;
|
|
@@ -17184,7 +17322,7 @@ async function writeRoleRunState(runDirectory, record4) {
|
|
|
17184
17322
|
async function readRoleRunState(runDirectory) {
|
|
17185
17323
|
try {
|
|
17186
17324
|
const raw = JSON.parse(
|
|
17187
|
-
await
|
|
17325
|
+
await readFile7(join7(runDirectory, RUN_STATE_FILE), "utf8")
|
|
17188
17326
|
);
|
|
17189
17327
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
17190
17328
|
return void 0;
|
|
@@ -17308,7 +17446,7 @@ async function acquireRunWriterLease(runDirectory) {
|
|
|
17308
17446
|
`, "utf8");
|
|
17309
17447
|
} catch (error) {
|
|
17310
17448
|
await handle.close().catch(() => void 0);
|
|
17311
|
-
await
|
|
17449
|
+
await unlink2(lockPath).catch(() => void 0);
|
|
17312
17450
|
throw error;
|
|
17313
17451
|
}
|
|
17314
17452
|
let released = false;
|
|
@@ -17318,7 +17456,7 @@ async function acquireRunWriterLease(runDirectory) {
|
|
|
17318
17456
|
if (released) return;
|
|
17319
17457
|
released = true;
|
|
17320
17458
|
await handle.close().catch(() => void 0);
|
|
17321
|
-
await
|
|
17459
|
+
await unlink2(lockPath).catch(() => void 0);
|
|
17322
17460
|
}
|
|
17323
17461
|
};
|
|
17324
17462
|
} catch (error) {
|
|
@@ -17387,7 +17525,7 @@ async function loadResumableRunRecord(home, runId) {
|
|
|
17387
17525
|
let derived;
|
|
17388
17526
|
try {
|
|
17389
17527
|
const raw = JSON.parse(
|
|
17390
|
-
await
|
|
17528
|
+
await readFile7(run.admittedRequestPath, "utf8")
|
|
17391
17529
|
);
|
|
17392
17530
|
if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
|
|
17393
17531
|
const record4 = raw;
|
|
@@ -18293,7 +18431,7 @@ var init_terminal = __esm({
|
|
|
18293
18431
|
|
|
18294
18432
|
// src/public-cli/settlement.ts
|
|
18295
18433
|
import { randomUUID } from "node:crypto";
|
|
18296
|
-
import { lstat as lstat3, mkdir as mkdir3, open as open2, readFile as
|
|
18434
|
+
import { lstat as lstat3, mkdir as mkdir3, open as open2, readFile as readFile8, readdir as readdir3, writeFile as writeFile4 } from "node:fs/promises";
|
|
18297
18435
|
import { dirname as dirname6, join as join8 } from "node:path";
|
|
18298
18436
|
function isChildDiagnosticFloodLine(line2) {
|
|
18299
18437
|
if (/^at\s+/.test(line2)) return true;
|
|
@@ -18349,7 +18487,7 @@ function presentStructuralRejection(error, io) {
|
|
|
18349
18487
|
}
|
|
18350
18488
|
async function inspectJudgeSession(sessionFile) {
|
|
18351
18489
|
try {
|
|
18352
|
-
await
|
|
18490
|
+
await readFile8(sessionFile, "utf8");
|
|
18353
18491
|
return { state: "present" };
|
|
18354
18492
|
} catch (error) {
|
|
18355
18493
|
if (isMissingPathError2(error)) return { state: "missing" };
|
|
@@ -18385,8 +18523,7 @@ function classifyPostAdmissionFailure(input) {
|
|
|
18385
18523
|
return {
|
|
18386
18524
|
cause: error.knownCause,
|
|
18387
18525
|
diagnostic: error.message || error.name || "unrecognized exception",
|
|
18388
|
-
identity
|
|
18389
|
-
...error.details === void 0 ? {} : { details: error.details }
|
|
18526
|
+
identity
|
|
18390
18527
|
};
|
|
18391
18528
|
}
|
|
18392
18529
|
if (error instanceof Error) {
|
|
@@ -18490,7 +18627,7 @@ function sessionReadFailure(error, fallbackMessage) {
|
|
|
18490
18627
|
return failed;
|
|
18491
18628
|
}
|
|
18492
18629
|
async function readBoundSessionEntries(sessionFile) {
|
|
18493
|
-
const text = await
|
|
18630
|
+
const text = await readFile8(sessionFile, "utf8");
|
|
18494
18631
|
const entries = [];
|
|
18495
18632
|
for (const line2 of text.trim().split("\n").filter(Boolean)) {
|
|
18496
18633
|
try {
|
|
@@ -18678,6 +18815,19 @@ function typedFailedTerminatingToolKnownFailure(entries) {
|
|
|
18678
18815
|
}
|
|
18679
18816
|
async function resolveAuditedRunnerKnownFailure(input) {
|
|
18680
18817
|
if (input.runner !== void 0) return input.runner;
|
|
18818
|
+
if (input.runDirectory !== void 0) {
|
|
18819
|
+
try {
|
|
18820
|
+
const rejection = await readReviewerDispatchRejection(input.runDirectory);
|
|
18821
|
+
if (rejection !== void 0) return rejection;
|
|
18822
|
+
} catch (error) {
|
|
18823
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
18824
|
+
return {
|
|
18825
|
+
cause: "activation",
|
|
18826
|
+
identity: thrownIdentity(failure),
|
|
18827
|
+
diagnostic: failure.message || failure.name
|
|
18828
|
+
};
|
|
18829
|
+
}
|
|
18830
|
+
}
|
|
18681
18831
|
try {
|
|
18682
18832
|
const auditorFailure = await readBoundAuditorKnownFailure(input.sessionFile);
|
|
18683
18833
|
if (auditorFailure !== void 0) return auditorFailure;
|
|
@@ -22897,6 +23047,7 @@ async function dispatchAdmittedReviewer(input) {
|
|
|
22897
23047
|
}
|
|
22898
23048
|
await markRunRunning(admitted.runDirectory);
|
|
22899
23049
|
await clearTypedProviderHttpObservation(admitted.runDirectory);
|
|
23050
|
+
await clearReviewerDispatchRejection(admitted.runDirectory);
|
|
22900
23051
|
const childEnv = {
|
|
22901
23052
|
...process.env,
|
|
22902
23053
|
HOME: env.home,
|
|
@@ -22991,7 +23142,8 @@ async function dispatchAdmittedReviewer(input) {
|
|
|
22991
23142
|
const knownFailure = await resolveAuditedRunnerKnownFailure({
|
|
22992
23143
|
runner: result2.knownFailure,
|
|
22993
23144
|
sessionFile: admitted.sessionFile,
|
|
22994
|
-
credential: credentialFailure
|
|
23145
|
+
credential: credentialFailure,
|
|
23146
|
+
runDirectory: admitted.runDirectory
|
|
22995
23147
|
});
|
|
22996
23148
|
return await presentControlledFailure7(
|
|
22997
23149
|
admitted,
|
|
@@ -28,11 +28,5 @@ export function immutableReviewerRefs(refs) {
|
|
|
28
28
|
}
|
|
29
29
|
export function sameReviewerPinnedTarget(actual, expected) {
|
|
30
30
|
return actual.repositoryRoot === expected.repositoryRoot && actual.objectFormat === expected.objectFormat &&
|
|
31
|
-
actual.targetHead === expected.targetHead
|
|
32
|
-
}
|
|
33
|
-
export function sameReviewerRefs(actual, expected) {
|
|
34
|
-
const actualEntries = Object.entries(actual).sort(([left], [right]) => left.localeCompare(right));
|
|
35
|
-
const expectedEntries = Object.entries(expected).sort(([left], [right]) => left.localeCompare(right));
|
|
36
|
-
return actualEntries.length === expectedEntries.length && actualEntries.every(([name, value], index) => name === expectedEntries[index]?.[0] && value.objectId === expectedEntries[index]?.[1].objectId &&
|
|
37
|
-
value.peeledCommitId === expectedEntries[index]?.[1].peeledCommitId);
|
|
31
|
+
actual.targetHead === expected.targetHead;
|
|
38
32
|
}
|
|
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
|
|
|
2
2
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { sameReviewerPinnedTarget } from "./reviewer-git-snapshot.js";
|
|
6
6
|
export class ReviewerProcessError extends Error {
|
|
7
7
|
command;
|
|
8
8
|
args;
|
|
@@ -44,20 +44,13 @@ async function runCommand(command, args, options = {}) {
|
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
46
|
async function git(cwd, args, signal, allowedCodes) { return runCommand("git", ["-C", cwd, ...args], { ...(signal === undefined ? {} : { signal }), ...(allowedCodes === undefined ? {} : { allowedCodes }) }); }
|
|
47
|
-
async function readRefs(cwd, signal) { return parseReviewerRefSnapshot((await git(cwd, reviewerRefSnapshotArgs(), signal)).stdout.trim()); }
|
|
48
47
|
async function verifySnapshot(cwd, snapshot, signal) {
|
|
49
48
|
if ((await git(cwd, ["rev-parse", "--show-object-format"], signal)).stdout.trim() !== snapshot.objectFormat)
|
|
50
49
|
throw new Error("Review clone object format does not match the pinned session snapshot");
|
|
51
50
|
const head = (await git(cwd, ["rev-parse", "HEAD^{commit}"], signal)).stdout.trim();
|
|
52
51
|
if (head !== snapshot.targetHead)
|
|
53
52
|
throw new Error(`Review clone target mismatch: expected ${snapshot.targetHead}, got ${head}`);
|
|
54
|
-
|
|
55
|
-
throw new Error("Review clone ref map does not match the pinned session snapshot");
|
|
56
|
-
for (const entry of Object.values(snapshot.refs)) {
|
|
57
|
-
await git(cwd, ["cat-file", "-e", `${entry.objectId}^{object}`], signal);
|
|
58
|
-
if (entry.peeledCommitId !== null)
|
|
59
|
-
await git(cwd, ["cat-file", "-e", `${entry.peeledCommitId}^{commit}`], signal);
|
|
60
|
-
}
|
|
53
|
+
await git(cwd, ["cat-file", "-e", `${snapshot.targetHead}^{commit}`], signal);
|
|
61
54
|
}
|
|
62
55
|
function workspaceError(error, failure, disposition, target) {
|
|
63
56
|
const wrapped = error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
@@ -68,31 +61,20 @@ async function prepareSnapshot(accepted, signal, dependencies) {
|
|
|
68
61
|
try {
|
|
69
62
|
dependencies.fault?.("snapshot.head");
|
|
70
63
|
const objectFormat = (await git(accepted.repositoryRoot, ["rev-parse", "--show-object-format"], signal)).stdout.trim();
|
|
71
|
-
if (objectFormat !== accepted.objectFormat)
|
|
72
|
-
throw new Error("Accepted Reviewer object format no longer matches the repository");
|
|
73
64
|
const targetHead = (await git(accepted.repositoryRoot, ["rev-parse", "HEAD^{commit}"], signal)).stdout.trim();
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
throw new Error("Accepted Reviewer target/ref identity no longer matches the repository");
|
|
65
|
+
if (!sameReviewerPinnedTarget({ repositoryRoot: accepted.repositoryRoot, objectFormat: objectFormat, targetHead }, accepted))
|
|
66
|
+
throw new Error("Accepted Reviewer target identity no longer matches the repository");
|
|
67
|
+
await git(accepted.repositoryRoot, ["cat-file", "-e", `${targetHead}^{commit}`], signal);
|
|
78
68
|
dependencies.fault?.("mirror.before-create");
|
|
79
69
|
mirrorRoot = await mkdtemp(join(tmpdir(), "ak-reviewer-snapshot-"));
|
|
80
70
|
const mirrorPath = join(mirrorRoot, "repository.git");
|
|
81
71
|
dependencies.fault?.("mirror.create");
|
|
82
|
-
await runCommand("git", ["
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
await git(mirrorPath, ["fetch", "--no-tags", accepted.repositoryRoot, targetHead], signal);
|
|
86
|
-
await git(mirrorPath, ["update-ref", "refs/ak-reviewer/target", targetHead], signal);
|
|
87
|
-
}
|
|
72
|
+
await runCommand("git", ["init", "--bare", `--object-format=${accepted.objectFormat}`, mirrorPath], signal === undefined ? {} : { signal });
|
|
73
|
+
await git(mirrorPath, ["fetch", "--no-tags", accepted.repositoryRoot, targetHead], signal);
|
|
74
|
+
await git(mirrorPath, ["update-ref", "refs/ak-reviewer/target", targetHead], signal);
|
|
88
75
|
dependencies.fault?.("mirror.verify");
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const objects = Object.values(refs).flatMap(e => e.peeledCommitId === null ? [e.objectId] : [e.objectId, e.peeledCommitId]);
|
|
92
|
-
for (const object of new Set([targetHead, ...objects]))
|
|
93
|
-
await git(mirrorPath, ["cat-file", "-e", `${object}^{object}`], signal);
|
|
94
|
-
await git(mirrorPath, ["config", "--remove-section", "remote.origin"], signal, [0, 5, 128]);
|
|
95
|
-
return { ...accepted, targetHead, refs, mirrorRoot, mirrorPath };
|
|
76
|
+
await git(mirrorPath, ["cat-file", "-e", `${targetHead}^{commit}`], signal);
|
|
77
|
+
return { ...accepted, targetHead, mirrorRoot, mirrorPath };
|
|
96
78
|
}
|
|
97
79
|
catch (error) {
|
|
98
80
|
throw workspaceError(error, "snapshot", mirrorRoot === undefined ? "not-created" : { retained: mirrorRoot }, accepted);
|
|
@@ -107,8 +89,7 @@ async function prepareClone(snapshot, signal, dependencies) {
|
|
|
107
89
|
dependencies.fault?.("workspace.init");
|
|
108
90
|
await git(workspace, ["init", `--object-format=${snapshot.objectFormat}`, "--initial-branch=ak-reviewer-unborn"], signal);
|
|
109
91
|
dependencies.fault?.("workspace.fetch");
|
|
110
|
-
await git(workspace, ["fetch", "--no-tags",
|
|
111
|
-
await git(workspace, ["config", "--remove-section", "remote.origin"], signal, [0, 5, 128]);
|
|
92
|
+
await git(workspace, ["fetch", "--no-tags", snapshot.mirrorPath, snapshot.targetHead], signal);
|
|
112
93
|
await git(workspace, ["checkout", "--detach", snapshot.targetHead], signal);
|
|
113
94
|
dependencies.fault?.("workspace.verify");
|
|
114
95
|
await verifySnapshot(workspace, snapshot, signal);
|
package/package.json
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* this adapter (or an intentional developer `pi -e`) crosses that boundary.
|
|
5
5
|
*/
|
|
6
6
|
import { execFile, spawn } from "node:child_process";
|
|
7
|
-
import { constants } from "node:fs";
|
|
8
|
-
import { access, realpath } from "node:fs/promises";
|
|
7
|
+
import { constants, writeFileSync } from "node:fs";
|
|
8
|
+
import { access, readFile, realpath, unlink } from "node:fs/promises";
|
|
9
9
|
import { delimiter, isAbsolute, join, resolve } from "node:path";
|
|
10
10
|
import { platform } from "node:process";
|
|
11
11
|
import { promisify } from "node:util";
|
|
@@ -16,8 +16,106 @@ import {
|
|
|
16
16
|
recordLaunchedRolePackageIdentity,
|
|
17
17
|
} from "./invocation.ts";
|
|
18
18
|
import { INTERNAL_ROLE_ENTRYPOINT_RELATIVE } from "./registry.ts";
|
|
19
|
+
import {
|
|
20
|
+
REVIEWER_PREFLIGHT_VIOLATIONS,
|
|
21
|
+
type ReviewerPreflightViolation,
|
|
22
|
+
} from "../reviewer-dispatch.ts";
|
|
19
23
|
import type { ControlledFailureCause } from "./terminal.ts";
|
|
20
24
|
|
|
25
|
+
/** Durable Reviewer-rejection child→parent page under AK_ROLE_RUN_DIR. */
|
|
26
|
+
const REVIEWER_DISPATCH_REJECTION_FILE = "typed-known-failure.json";
|
|
27
|
+
|
|
28
|
+
function isReviewerPreflightViolation(value: unknown): value is ReviewerPreflightViolation {
|
|
29
|
+
return (
|
|
30
|
+
typeof value === "string" &&
|
|
31
|
+
(REVIEWER_PREFLIGHT_VIOLATIONS as readonly string[]).includes(value)
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function reviewerDispatchRejectionPath(runDirectory: string): string {
|
|
36
|
+
return join(runDirectory, REVIEWER_DISPATCH_REJECTION_FILE);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Clear any prior attempt's Reviewer rejection page so resume/retry cannot
|
|
41
|
+
* inherit a stale knownFailure.details.
|
|
42
|
+
*/
|
|
43
|
+
export async function clearReviewerDispatchRejection(runDirectory: string): Promise<void> {
|
|
44
|
+
try {
|
|
45
|
+
await unlink(reviewerDispatchRejectionPath(runDirectory));
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (
|
|
48
|
+
error instanceof Error &&
|
|
49
|
+
"code" in error &&
|
|
50
|
+
(error as { code?: unknown }).code === "ENOENT"
|
|
51
|
+
) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Synchronous durable write for Reviewer dispatch rejection (child process exit is sync).
|
|
60
|
+
* Parent public CLI recovers via readReviewerDispatchRejection into knownFailure.
|
|
61
|
+
*/
|
|
62
|
+
export function recordReviewerDispatchRejectionSync(
|
|
63
|
+
runDirectory: string,
|
|
64
|
+
rejection: Readonly<{
|
|
65
|
+
diagnostic: string;
|
|
66
|
+
violations: readonly ReviewerPreflightViolation[];
|
|
67
|
+
}>,
|
|
68
|
+
): void {
|
|
69
|
+
writeFileSync(
|
|
70
|
+
reviewerDispatchRejectionPath(runDirectory),
|
|
71
|
+
`${JSON.stringify(rejection)}\n`,
|
|
72
|
+
"utf8",
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Recover a child-written Reviewer dispatch rejection through a fixed mapping. */
|
|
77
|
+
export async function readReviewerDispatchRejection(
|
|
78
|
+
runDirectory: string,
|
|
79
|
+
): Promise<ExplicitInternalKnownFailure | undefined> {
|
|
80
|
+
let raw: string;
|
|
81
|
+
try {
|
|
82
|
+
raw = await readFile(reviewerDispatchRejectionPath(runDirectory), "utf8");
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (
|
|
85
|
+
error instanceof Error &&
|
|
86
|
+
"code" in error &&
|
|
87
|
+
(error as { code?: unknown }).code === "ENOENT"
|
|
88
|
+
) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
const parsed: unknown = JSON.parse(raw);
|
|
94
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
95
|
+
const error = new Error("Reviewer dispatch rejection page must be a JSON object");
|
|
96
|
+
error.name = "ReviewerDispatchRejectionContractError";
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
const record = parsed as Record<string, unknown>;
|
|
100
|
+
if (
|
|
101
|
+
typeof record.diagnostic !== "string" ||
|
|
102
|
+
record.diagnostic.trim() === "" ||
|
|
103
|
+
!Array.isArray(record.violations) ||
|
|
104
|
+
record.violations.length === 0 ||
|
|
105
|
+
record.violations.some((value) => !isReviewerPreflightViolation(value))
|
|
106
|
+
) {
|
|
107
|
+
const error = new Error("Reviewer dispatch rejection page has unusable required fields");
|
|
108
|
+
error.name = "ReviewerDispatchRejectionContractError";
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
cause: "activation",
|
|
113
|
+
diagnostic: record.diagnostic,
|
|
114
|
+
identity: { name: "ReviewerDispatchRejectionError" },
|
|
115
|
+
details: { violations: Object.freeze([...record.violations]) },
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
21
119
|
export function resolveInternalRoleEntrypoint(packageRoot: string): string {
|
|
22
120
|
return join(packageRoot, INTERNAL_ROLE_ENTRYPOINT_RELATIVE);
|
|
23
121
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
type PackagedMethodSkillProvenance,
|
|
15
15
|
} from "../package-resources/method-skill.ts";
|
|
16
16
|
import {
|
|
17
|
+
clearReviewerDispatchRejection,
|
|
17
18
|
runExplicitInternalActivation,
|
|
18
19
|
type ExplicitInternalKnownFailure,
|
|
19
20
|
type ExplicitInternalPiRunner,
|
|
@@ -267,6 +268,7 @@ async function dispatchAdmittedReviewer(input: {
|
|
|
267
268
|
}
|
|
268
269
|
await markRunRunning(admitted.runDirectory);
|
|
269
270
|
await clearTypedProviderHttpObservation(admitted.runDirectory);
|
|
271
|
+
await clearReviewerDispatchRejection(admitted.runDirectory);
|
|
270
272
|
|
|
271
273
|
const childEnv: NodeJS.ProcessEnv = {
|
|
272
274
|
...process.env,
|
|
@@ -369,6 +371,7 @@ async function dispatchAdmittedReviewer(input: {
|
|
|
369
371
|
runner: result.knownFailure,
|
|
370
372
|
sessionFile: admitted.sessionFile,
|
|
371
373
|
credential: credentialFailure,
|
|
374
|
+
runDirectory: admitted.runDirectory,
|
|
372
375
|
});
|
|
373
376
|
return await presentControlledFailure(
|
|
374
377
|
admitted,
|
|
@@ -12,7 +12,7 @@ import { AUDITOR_SOUL_ROLES } from "../auditor-soul.ts";
|
|
|
12
12
|
import { DOCTOR_AUDIT_TOOL_NAME } from "../doctor-auditor.ts";
|
|
13
13
|
import { JUDGE_AUDIT_TOOL_NAME } from "../judge-auditor.ts";
|
|
14
14
|
import { REVIEWER_AUDIT_TOOL_NAME } from "../reviewer-auditor.ts";
|
|
15
|
-
import { knownFailureFromProviderStop, type ExplicitInternalKnownFailure } from "./explicit-internal.ts";
|
|
15
|
+
import { knownFailureFromProviderStop, type ExplicitInternalKnownFailure, readReviewerDispatchRejection } from "./explicit-internal.ts";
|
|
16
16
|
import {
|
|
17
17
|
AUDITOR_COMPLIANCE_FAILURE_ENTRY_TYPE,
|
|
18
18
|
AUDITOR_PARENT_ATTEMPT_BINDING_ENTRY_TYPE,
|
|
@@ -338,7 +338,6 @@ export function classifyPostAdmissionFailure(input: {
|
|
|
338
338
|
cause: error.knownCause,
|
|
339
339
|
diagnostic: error.message || error.name || "unrecognized exception",
|
|
340
340
|
identity,
|
|
341
|
-
...(error.details === undefined ? {} : { details: error.details }),
|
|
342
341
|
};
|
|
343
342
|
}
|
|
344
343
|
if (error instanceof Error) {
|
|
@@ -783,8 +782,23 @@ export async function resolveAuditedRunnerKnownFailure(input: {
|
|
|
783
782
|
runner: ExplicitInternalKnownFailure | undefined;
|
|
784
783
|
sessionFile: string;
|
|
785
784
|
credential: ExplicitInternalKnownFailure | undefined;
|
|
785
|
+
/** Reviewer only: recover child-written rejection page into knownFailure.details. */
|
|
786
|
+
runDirectory?: string;
|
|
786
787
|
}): Promise<ExplicitInternalKnownFailure | undefined> {
|
|
787
788
|
if (input.runner !== undefined) return input.runner;
|
|
789
|
+
if (input.runDirectory !== undefined) {
|
|
790
|
+
try {
|
|
791
|
+
const rejection = await readReviewerDispatchRejection(input.runDirectory);
|
|
792
|
+
if (rejection !== undefined) return rejection;
|
|
793
|
+
} catch (error) {
|
|
794
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
795
|
+
return {
|
|
796
|
+
cause: "activation",
|
|
797
|
+
identity: thrownIdentity(failure),
|
|
798
|
+
diagnostic: failure.message || failure.name,
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
}
|
|
788
802
|
// Bound auditor evidence outranks a parent failure that the auditor path itself
|
|
789
803
|
// caused (retention EISDIR race). A typed terminating-tool host failure is next:
|
|
790
804
|
// it outranks provider/credential and nonzero fallbacks, but not its recorded cause.
|
|
@@ -37,17 +37,9 @@ export function immutableReviewerRefs(refs: ReviewerRefMap): ReviewerRefMap {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export function sameReviewerPinnedTarget(
|
|
40
|
-
actual: Readonly<{ repositoryRoot: string; objectFormat: "sha1" | "sha256"; targetHead: string; refs
|
|
41
|
-
expected: Readonly<{ repositoryRoot: string; objectFormat: "sha1" | "sha256"; targetHead: string; refs
|
|
40
|
+
actual: Readonly<{ repositoryRoot: string; objectFormat: "sha1" | "sha256"; targetHead: string; refs?: ReviewerRefMap }>,
|
|
41
|
+
expected: Readonly<{ repositoryRoot: string; objectFormat: "sha1" | "sha256"; targetHead: string; refs?: ReviewerRefMap }>,
|
|
42
42
|
): boolean {
|
|
43
43
|
return actual.repositoryRoot === expected.repositoryRoot && actual.objectFormat === expected.objectFormat &&
|
|
44
|
-
actual.targetHead === expected.targetHead
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function sameReviewerRefs(actual: ReviewerRefMap, expected: ReviewerRefMap): boolean {
|
|
48
|
-
const actualEntries = Object.entries(actual).sort(([left], [right]) => left.localeCompare(right));
|
|
49
|
-
const expectedEntries = Object.entries(expected).sort(([left], [right]) => left.localeCompare(right));
|
|
50
|
-
return actualEntries.length === expectedEntries.length && actualEntries.every(([name, value], index) =>
|
|
51
|
-
name === expectedEntries[index]?.[0] && value.objectId === expectedEntries[index]?.[1].objectId &&
|
|
52
|
-
value.peeledCommitId === expectedEntries[index]?.[1].peeledCommitId);
|
|
44
|
+
actual.targetHead === expected.targetHead;
|
|
53
45
|
}
|
|
@@ -3,11 +3,11 @@ import { mkdtemp, rm } from "node:fs/promises";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { sameReviewerPinnedTarget } from "./reviewer-git-snapshot.ts";
|
|
7
7
|
import type { ReviewerTargetSnapshot, ReviewerWorkspaceDisposition } from "./reviewer-execution-ledger.ts";
|
|
8
8
|
|
|
9
9
|
export type ReviewerWorkspaceFaultPoint =
|
|
10
|
-
| "snapshot.head"
|
|
10
|
+
| "snapshot.head"
|
|
11
11
|
| "mirror.before-create" | "mirror.create" | "mirror.verify"
|
|
12
12
|
| "workspace.before-create" | "workspace.init" | "workspace.fetch" | "workspace.verify";
|
|
13
13
|
export type ReviewerWorkspaceDependencies = Readonly<{ fault?(operation: ReviewerWorkspaceFaultPoint): void }>;
|
|
@@ -40,13 +40,11 @@ async function runCommand(command: string, args: string[], options: { cwd?: stri
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
async function git(cwd: string, args: string[], signal?: AbortSignal, allowedCodes?: readonly number[]) { return runCommand("git", ["-C", cwd, ...args], { ...(signal === undefined ? {} : { signal }), ...(allowedCodes === undefined ? {} : { allowedCodes }) }); }
|
|
43
|
-
async function readRefs(cwd: string, signal?: AbortSignal): Promise<Record<string, ReviewerRefEntry>> { return parseReviewerRefSnapshot((await git(cwd, reviewerRefSnapshotArgs(), signal)).stdout.trim()); }
|
|
44
43
|
async function verifySnapshot(cwd: string, snapshot: ReviewerTargetSnapshot, signal?: AbortSignal) {
|
|
45
44
|
if ((await git(cwd, ["rev-parse", "--show-object-format"], signal)).stdout.trim() !== snapshot.objectFormat) throw new Error("Review clone object format does not match the pinned session snapshot");
|
|
46
45
|
const head = (await git(cwd, ["rev-parse", "HEAD^{commit}"], signal)).stdout.trim();
|
|
47
46
|
if (head !== snapshot.targetHead) throw new Error(`Review clone target mismatch: expected ${snapshot.targetHead}, got ${head}`);
|
|
48
|
-
|
|
49
|
-
for (const entry of Object.values(snapshot.refs)) { await git(cwd, ["cat-file", "-e", `${entry.objectId}^{object}`], signal); if (entry.peeledCommitId !== null) await git(cwd, ["cat-file", "-e", `${entry.peeledCommitId}^{commit}`], signal); }
|
|
47
|
+
await git(cwd, ["cat-file", "-e", `${snapshot.targetHead}^{commit}`], signal);
|
|
50
48
|
}
|
|
51
49
|
function workspaceError(error: unknown, failure: "snapshot" | "workspace", disposition: ReviewerWorkspaceDisposition, target: ReviewerTargetSnapshot): ReviewerWorkspaceError {
|
|
52
50
|
const wrapped = error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
@@ -57,19 +55,15 @@ async function prepareSnapshot(accepted: ReviewerTargetSnapshot, signal: AbortSi
|
|
|
57
55
|
try {
|
|
58
56
|
dependencies.fault?.("snapshot.head");
|
|
59
57
|
const objectFormat = (await git(accepted.repositoryRoot, ["rev-parse", "--show-object-format"], signal)).stdout.trim();
|
|
60
|
-
if (objectFormat !== accepted.objectFormat) throw new Error("Accepted Reviewer object format no longer matches the repository");
|
|
61
58
|
const targetHead = (await git(accepted.repositoryRoot, ["rev-parse", "HEAD^{commit}"], signal)).stdout.trim();
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
if (!sameReviewerPinnedTarget({ repositoryRoot: accepted.repositoryRoot, objectFormat: objectFormat as "sha1" | "sha256", targetHead }, accepted)) throw new Error("Accepted Reviewer target identity no longer matches the repository");
|
|
60
|
+
await git(accepted.repositoryRoot, ["cat-file", "-e", `${targetHead}^{commit}`], signal);
|
|
64
61
|
dependencies.fault?.("mirror.before-create"); mirrorRoot = await mkdtemp(join(tmpdir(), "ak-reviewer-snapshot-")); const mirrorPath = join(mirrorRoot, "repository.git");
|
|
65
|
-
dependencies.fault?.("mirror.create"); await runCommand("git", ["
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
dependencies.fault?.("mirror.verify");
|
|
69
|
-
|
|
70
|
-
for (const object of new Set([targetHead, ...objects])) await git(mirrorPath, ["cat-file", "-e", `${object}^{object}`], signal);
|
|
71
|
-
await git(mirrorPath, ["config", "--remove-section", "remote.origin"], signal, [0, 5, 128]);
|
|
72
|
-
return { ...accepted, targetHead, refs, mirrorRoot, mirrorPath };
|
|
62
|
+
dependencies.fault?.("mirror.create"); await runCommand("git", ["init", "--bare", `--object-format=${accepted.objectFormat}`, mirrorPath], signal === undefined ? {} : { signal });
|
|
63
|
+
await git(mirrorPath, ["fetch", "--no-tags", accepted.repositoryRoot, targetHead], signal);
|
|
64
|
+
await git(mirrorPath, ["update-ref", "refs/ak-reviewer/target", targetHead], signal);
|
|
65
|
+
dependencies.fault?.("mirror.verify"); await git(mirrorPath, ["cat-file", "-e", `${targetHead}^{commit}`], signal);
|
|
66
|
+
return { ...accepted, targetHead, mirrorRoot, mirrorPath };
|
|
73
67
|
} catch (error) { throw workspaceError(error, "snapshot", mirrorRoot === undefined ? "not-created" : { retained: mirrorRoot }, accepted); }
|
|
74
68
|
}
|
|
75
69
|
async function prepareClone(snapshot: GitSnapshot, signal: AbortSignal | undefined, dependencies: ReviewerWorkspaceDependencies): Promise<string> {
|
|
@@ -77,8 +71,8 @@ async function prepareClone(snapshot: GitSnapshot, signal: AbortSignal | undefin
|
|
|
77
71
|
try {
|
|
78
72
|
dependencies.fault?.("workspace.before-create"); workspace = await mkdtemp(join(tmpdir(), "ak-reviewer-leg-"));
|
|
79
73
|
dependencies.fault?.("workspace.init"); await git(workspace, ["init", `--object-format=${snapshot.objectFormat}`, "--initial-branch=ak-reviewer-unborn"], signal);
|
|
80
|
-
dependencies.fault?.("workspace.fetch"); await git(workspace, ["fetch", "--no-tags",
|
|
81
|
-
await git(workspace, ["
|
|
74
|
+
dependencies.fault?.("workspace.fetch"); await git(workspace, ["fetch", "--no-tags", snapshot.mirrorPath, snapshot.targetHead], signal);
|
|
75
|
+
await git(workspace, ["checkout", "--detach", snapshot.targetHead], signal);
|
|
82
76
|
dependencies.fault?.("workspace.verify"); await verifySnapshot(workspace, snapshot, signal); return workspace;
|
|
83
77
|
} catch (error) { throw workspaceError(error, "workspace", workspace === undefined ? "not-created" : { retained: workspace }, target); }
|
|
84
78
|
}
|
package/src/role-runtime.ts
CHANGED
|
@@ -44,6 +44,10 @@ import {
|
|
|
44
44
|
resolveLifecycleInvocationPrincipal,
|
|
45
45
|
} from "./navigator-invocation-identity.ts";
|
|
46
46
|
import { recordTypedProviderHttpStatus } from "./public-cli/run-lifecycle.ts";
|
|
47
|
+
import {
|
|
48
|
+
ExplicitInternalActivationError,
|
|
49
|
+
recordReviewerDispatchRejectionSync,
|
|
50
|
+
} from "./public-cli/explicit-internal.ts";
|
|
47
51
|
import { NAVIGATOR_POST_ROLE_GRACE_MS, raceNavigatorGrace } from "./public-cli/settlement.ts";
|
|
48
52
|
import { PACKAGED_ROLE_REGISTRY, packagedRoleMetadata, packagedRoleOutputTool, packagedRolePhaseFlag, type PackagedRole } from "./packaged-role-registry.ts";
|
|
49
53
|
import { isAuditEscalationProjection } from "./audit-escalation.ts";
|
|
@@ -223,7 +227,25 @@ function activationStage(role: PackagedRole, runtime: ActivationRuntime): { id:
|
|
|
223
227
|
case "reviewer": return { id: "load-install-and-dispatch", run: async () => {
|
|
224
228
|
const activation = await runtime.reviewer.activate(runtime.context);
|
|
225
229
|
const result = await activation.dispatcher.dispatch(activation.fixedBaseRevision, { context: runtime.context });
|
|
226
|
-
if (result.status !== "accepted")
|
|
230
|
+
if (result.status !== "accepted") {
|
|
231
|
+
const rejection = new ExplicitInternalActivationError(
|
|
232
|
+
`Fixed Reviewer dispatch was not accepted: ${result.status}: ${result.diagnostic}`,
|
|
233
|
+
{
|
|
234
|
+
knownCause: "activation",
|
|
235
|
+
name: "ReviewerDispatchRejectionError",
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
// Pi stderr cannot carry the structured violations. Do not mask a durable
|
|
239
|
+
// write failure: its infrastructure cause is truer than the unwritten page.
|
|
240
|
+
const runDir = process.env.AK_ROLE_RUN_DIR;
|
|
241
|
+
if (typeof runDir === "string" && runDir.trim() !== "") {
|
|
242
|
+
recordReviewerDispatchRejectionSync(runDir, {
|
|
243
|
+
diagnostic: rejection.message,
|
|
244
|
+
violations: result.violations,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
throw rejection;
|
|
248
|
+
}
|
|
227
249
|
} };
|
|
228
250
|
case "collector": return { id: "load-and-install", run: async () => runtime.collector.activate(runtime.context, runtime.event) };
|
|
229
251
|
case "doctor": return { id: "load-and-install", run: async () => runtime.doctor.activate() };
|