agent-dealer 1.3.0 → 1.3.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/bundle/server/dist/adapters/git-worktree.js +38 -1
- package/bundle/server/dist/adapters/github.js +50 -0
- package/bundle/server/dist/adapters/github.test.js +89 -1
- package/bundle/server/dist/coordinator/admission.test.js +115 -0
- package/bundle/server/dist/coordinator/auto-merge.js +46 -9
- package/bundle/server/dist/coordinator/commands.js +89 -9
- package/bundle/server/dist/coordinator/commands.test.js +228 -2
- package/bundle/server/dist/coordinator/developer-effect.js +36 -0
- package/bundle/server/dist/coordinator/operator-verification.integration.test.js +102 -1
- package/bundle/server/dist/coordinator/product-scope-contract.test.js +249 -0
- package/bundle/server/dist/coordinator/prompts.js +57 -2
- package/bundle/server/dist/coordinator/prompts.test.js +236 -2
- package/bundle/server/dist/coordinator/reviewer-browser-probe.js +1083 -0
- package/bundle/server/dist/coordinator/reviewer-browser-probe.test.js +780 -0
- package/bundle/server/dist/coordinator/reviewer-effect.js +30 -1
- package/bundle/server/dist/coordinator/reviewer-effect.test.js +54 -0
- package/bundle/server/dist/coordinator/reviewer-visual-evidence.js +594 -0
- package/bundle/server/dist/coordinator/reviewer-visual-evidence.test.js +496 -0
- package/bundle/server/dist/coordinator/source-reload.js +44 -9
- package/bundle/server/dist/coordinator/visual-qa-collection.test.js +619 -0
- package/bundle/server/dist/coordinator/visual-qa.js +630 -0
- package/bundle/server/dist/coordinator/visual-qa.test.js +493 -0
- package/bundle/server/dist/coordinator/worker-loop.js +1 -1
- package/bundle/server/dist/paths.js +24 -0
- package/bundle/server/dist/repository/admission-settings.js +5 -5
- package/bundle/server/dist/repository/admission-settings.test.js +71 -15
- package/bundle/server/dist/repository/issues.js +8 -1
- package/bundle/server/dist/repository/issues.test.js +34 -0
- package/bundle/server/dist/routes/issues-execution-contract.test.js +293 -0
- package/bundle/server/dist/routes/issues-source-attachments.test.js +277 -0
- package/bundle/server/dist/routes/issues.js +23 -8
- package/bundle/server/dist/routes/issues.test.js +80 -0
- package/bundle/server/dist/routes/queue.js +1 -1
- package/bundle/server/dist/routes/queue.test.js +55 -20
- package/bundle/server/dist/runners/process-registry.js +1 -1
- package/bundle/server/package.json +2 -2
- package/bundle/server/static-ui/assets/index-DQ5ivcyg.js +64 -0
- package/bundle/server/static-ui/index.html +1 -1
- package/bundle/shared/dist/queue-entries.d.ts +5 -4
- package/bundle/shared/dist/queue-entries.js +5 -4
- package/bundle/shared/package.json +1 -1
- package/package.json +1 -1
- package/templates/prod.env.example +5 -2
- package/bundle/server/static-ui/assets/index-Bm74Vq_X.js +0 -64
|
@@ -28,10 +28,12 @@ import { parseProfileSnapshot, roleCeiling } from "@agent-dealer/shared";
|
|
|
28
28
|
import { getTaskSnapshot } from "./commands.js";
|
|
29
29
|
import { extractOperatorCriteria } from "./operator-criteria.js";
|
|
30
30
|
import { buildReviewerPrompt, formatDiffForPrompt, TOTAL_DIFF_LIMIT } from "./prompts.js";
|
|
31
|
+
import { cleanupStagedVisualArtifacts, resolveReviewerVisualEvidence, toReviewerVisualEvidenceInput, } from "./reviewer-visual-evidence.js";
|
|
32
|
+
import { readLatestVisualQa } from "./visual-qa.js";
|
|
31
33
|
import { guidanceForNextSession } from "./guidance.js";
|
|
32
34
|
import { realReviewerSpawn, reviewerSessionLogPath } from "./spawn.js";
|
|
33
35
|
import { parseReviewerResult, normalizeReviewerResult, INCOMPLETE_REVIEW_FINGERPRINT, ReviewerResult as ReviewerResultSchema, } from "./reviewer-result.js";
|
|
34
|
-
import { createRoleWorktree, safeRemoveWorktree, isWorktreeClean, mergeBase, fetchRef, diffShas, } from "../adapters/git-worktree.js";
|
|
36
|
+
import { createRoleWorktree, safeRemoveWorktree, isWorktreeClean, listChangedFiles, mergeBase, fetchRef, diffShas, } from "../adapters/git-worktree.js";
|
|
35
37
|
import { ensureIssueRepoCheckout, roleWorktreePathForResolution, resolveCheckoutBaseBranch, } from "../adapters/managed-repo.js";
|
|
36
38
|
import { prepareWorkerDeckConnection, releaseWorkerDeckConnection } from "../adapters/agent-deck-bind.js";
|
|
37
39
|
import { realGithubAdapter } from "../adapters/github.js";
|
|
@@ -104,6 +106,14 @@ const EVENT_FOR_VERDICT = {
|
|
|
104
106
|
};
|
|
105
107
|
/** Best-effort: the worker never writes, so a non-clean checkout here is unexpected, not valuable work. */
|
|
106
108
|
async function bestEffortRemove(repo, worktreePath) {
|
|
109
|
+
try {
|
|
110
|
+
// NOT-384: staged visual evidence goes first, so no residue survives the
|
|
111
|
+
// attempt even when the worktree removal below fails.
|
|
112
|
+
cleanupStagedVisualArtifacts(worktreePath);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// same courtesy as below — staging cleanup must never fail the attempt
|
|
116
|
+
}
|
|
107
117
|
try {
|
|
108
118
|
await safeRemoveWorktree({ repo, path: worktreePath, role: "reviewer" });
|
|
109
119
|
}
|
|
@@ -335,12 +345,29 @@ export async function runReviewerEffect(ctx, deps = defaultDeps) {
|
|
|
335
345
|
const baseSha = await mergeBase({ repo: worktreePath, base: `origin/${baseBranch}`, head: headSha });
|
|
336
346
|
const diff = await diffShas({ worktreePath, baseSha, headSha });
|
|
337
347
|
const { truncated: diffTruncated, omittedPaths } = formatDiffForPrompt(diff);
|
|
348
|
+
// NOT-384: SHA-bound CI visual evidence for this head (never throws —
|
|
349
|
+
// every failure mode resolves to `missing` with a reason, and a missing
|
|
350
|
+
// or failed capture never counts as a visual pass).
|
|
351
|
+
const changedFiles = await listChangedFiles({ worktreePath, baseSha, headSha }).catch(() => null);
|
|
352
|
+
const visualEvidence = toReviewerVisualEvidenceInput(await resolveReviewerVisualEvidence({
|
|
353
|
+
issueId: issue.id,
|
|
354
|
+
workerSessionId: sessionId,
|
|
355
|
+
worktreePath,
|
|
356
|
+
baseSha,
|
|
357
|
+
headSha,
|
|
358
|
+
changedFiles,
|
|
359
|
+
github: deps.github,
|
|
360
|
+
fetcher: deps.visualEvidenceFetcher ?? null,
|
|
361
|
+
}));
|
|
338
362
|
const openFindings = listFindingsForIssue(issue.id).filter((f) => f.status === "open" || f.status === "recurring");
|
|
339
363
|
const guidance = guidanceForNextSession(issue.id, sessionId);
|
|
340
364
|
// NOT-314: the frozen snapshot's operator criteria — the reviewer must not
|
|
341
365
|
// treat missing operator evidence as a defect (Dealer gates the merge), but
|
|
342
366
|
// the probe and doc must exist. Rendered only when non-empty.
|
|
343
367
|
const operatorCriteria = extractOperatorCriteria(taskSnapshot.acceptanceCriteria);
|
|
368
|
+
// NOT-381: the latest coordinator-validated visual receipt (or loud
|
|
369
|
+
// rejection), SHA-checked against the pinned head inside the prompt section.
|
|
370
|
+
const visualQaRecord = readLatestVisualQa(issue.id);
|
|
344
371
|
const prompt = buildReviewerPrompt({
|
|
345
372
|
taskSnapshot,
|
|
346
373
|
round: workItem.round,
|
|
@@ -354,6 +381,8 @@ export async function runReviewerEffect(ctx, deps = defaultDeps) {
|
|
|
354
381
|
deckId: snapshot?.deckId ?? null,
|
|
355
382
|
guidance: guidance.length ? guidance : undefined,
|
|
356
383
|
operatorCriteria: operatorCriteria.length > 0 ? operatorCriteria : undefined,
|
|
384
|
+
visualEvidence,
|
|
385
|
+
visualQa: visualQaRecord ? { record: visualQaRecord, pinnedHeadSha: headSha } : undefined,
|
|
357
386
|
});
|
|
358
387
|
// NOT-83 review finding — see developer-effect.ts's identical check for the full
|
|
359
388
|
// rationale: re-verify this item is still leased right before the real spawn, since an
|
|
@@ -728,3 +728,57 @@ test("NOT-225 regression: a non-zero reviewer exit keeps its session_failed beha
|
|
|
728
728
|
const crashReason = outcome.reason ?? "";
|
|
729
729
|
assert.ok(!crashReason.includes("could not start"), "a spawned-then-crashed session is not a setup failure");
|
|
730
730
|
});
|
|
731
|
+
test("NOT-384: available visual evidence stages CI artifacts under the reviewer worktree and cites them", async () => {
|
|
732
|
+
const { isWorktreeClean } = await import("../adapters/git-worktree.js");
|
|
733
|
+
const { reviewerVisualStagingDir } = await import("./reviewer-visual-evidence.js");
|
|
734
|
+
const issueId = await makeIssue();
|
|
735
|
+
const github = fakeGithub();
|
|
736
|
+
const uiSpawn = async (input) => {
|
|
737
|
+
fs.mkdirSync(path.join(input.cwd, "apps", "web", "src"), { recursive: true });
|
|
738
|
+
fs.writeFileSync(path.join(input.cwd, "apps", "web", "src", "app.tsx"), "export const App = () => null;\n");
|
|
739
|
+
git(input.cwd, "add", ".");
|
|
740
|
+
git(input.cwd, "-c", "user.email=agent@test", "-c", "user.name=Agent", "commit", "-q", "-m", "ui change");
|
|
741
|
+
return { exitCode: 0, transcript: "Implementation conclusion: changed the web app.", logPath: "/dev/null", timedOut: false };
|
|
742
|
+
};
|
|
743
|
+
registerEffectHandler("developer", (ctx) => runDeveloperEffect(ctx, { deckCallTool: okDeckCallTool, spawn: uiSpawn, github }));
|
|
744
|
+
startWorkflow(issueId);
|
|
745
|
+
await pump(1);
|
|
746
|
+
assert.equal(getIssue(issueId).status, "reviewing", "test setup: UI change did not reach reviewing");
|
|
747
|
+
let seenPrompt = "";
|
|
748
|
+
let seenCwd = "";
|
|
749
|
+
let cleanDuringSpawn = null;
|
|
750
|
+
const fetcher = {
|
|
751
|
+
async findRunForHead({ headSha }) {
|
|
752
|
+
return { databaseId: 4242, headSha, conclusion: "success", status: "completed" };
|
|
753
|
+
},
|
|
754
|
+
async downloadArtifact({ name, destDir }) {
|
|
755
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
756
|
+
fs.writeFileSync(path.join(destDir, "issues-home-1440x900.png"), `png-${name}`);
|
|
757
|
+
fs.writeFileSync(path.join(destDir, "issues-home-390x800.png"), `png-${name}`);
|
|
758
|
+
if (name === "ui-diff")
|
|
759
|
+
fs.writeFileSync(path.join(destDir, "SUMMARY.md"), "## UI diff\n\nhead: `h`\n");
|
|
760
|
+
},
|
|
761
|
+
};
|
|
762
|
+
const capturingSpawn = async (input) => {
|
|
763
|
+
seenPrompt = input.prompt;
|
|
764
|
+
seenCwd = input.cwd;
|
|
765
|
+
cleanDuringSpawn = await isWorktreeClean(input.cwd);
|
|
766
|
+
return verdictSpawn({ verdict: "approved" })(input);
|
|
767
|
+
};
|
|
768
|
+
registerEffectHandler("reviewer", (ctx) => runReviewerEffect(ctx, { deckCallTool: okDeckCallTool, spawn: capturingSpawn, github, visualEvidenceFetcher: fetcher }));
|
|
769
|
+
await pump(1);
|
|
770
|
+
const issue = getIssue(issueId);
|
|
771
|
+
assert.equal(issue.status, "final_review");
|
|
772
|
+
const stagingDir = reviewerVisualStagingDir(seenCwd);
|
|
773
|
+
assert.ok(stagingDir.startsWith(seenCwd + path.sep), "staging dir is inside the reviewer worktree");
|
|
774
|
+
assert.ok(seenPrompt.includes(stagingDir), "reviewer input must name the staged directory");
|
|
775
|
+
assert.ok(seenPrompt.includes(issue.headSha), "reviewer input must cite the head SHA");
|
|
776
|
+
assert.ok(seenPrompt.includes("## UI diff"), "reviewer input must embed the diff summary");
|
|
777
|
+
assert.match(seenPrompt, /1440x900 and 390x800/);
|
|
778
|
+
assert.equal(cleanDuringSpawn, true, "the worktree reports clean with staged evidence present");
|
|
779
|
+
const visual = listArtifactsForIssue(issueId).find((a) => a.kind === "visual_evidence");
|
|
780
|
+
assert.ok(visual, "expected a recorded visual_evidence artifact");
|
|
781
|
+
assert.equal(JSON.parse(visual.contentJson).state, "available");
|
|
782
|
+
assert.equal(fs.existsSync(stagingDir), false, "the staging directory is gone after the attempt");
|
|
783
|
+
assert.equal(fs.existsSync(seenCwd), false, "the reviewer worktree is removed after the attempt");
|
|
784
|
+
});
|