Verify saved output documents across all chat handoff scenarios

This commit is contained in:
Dotta 2026-09-11 14:40:58 -05:00
parent cb425d35bc
commit 6cd4126108
3 changed files with 27 additions and 25 deletions

View File

@ -431,7 +431,11 @@ impl AcpxSidecarTransport {
} else {
format!(
" stderrCategories={}",
self.stderr_categories.iter().copied().collect::<Vec<_>>().join(",")
self.stderr_categories
.iter()
.copied()
.collect::<Vec<_>>()
.join(",")
)
};
if diagnostics.is_empty() {
@ -444,7 +448,8 @@ impl AcpxSidecarTransport {
fn record_stderr(&mut self, line: &str) {
// Only fixed categories cross this boundary. Raw errors, stack paths,
// identifiers, and credential-bearing strings remain fully redacted.
self.stderr_categories.extend(stderr_diagnostic_categories(line));
self.stderr_categories
.extend(stderr_diagnostic_categories(line));
self.stderr_tail.push(redact_diagnostic(line));
}
@ -639,15 +644,18 @@ fn stderr_diagnostic_categories(value: &str) -> BTreeSet<&'static str> {
("ENOENT", "file_not_found"),
("EACCES", "permission_denied"),
("EPERM", "permission_denied"),
("ACPX_PERSISTED_SESSION_IDENTITY_MISMATCH", "persisted_session_identity_mismatch"),
(
"ACPX_PERSISTED_SESSION_IDENTITY_MISMATCH",
"persisted_session_identity_mismatch",
),
("SESSION_RESUME_REQUIRED", "session_resume_required"),
];
let mut categories: BTreeSet<&'static str> = value
.split(|character: char| !character.is_ascii_alphanumeric() && character != '_')
.filter_map(|token| {
CATEGORIES.iter().find_map(|(known, category)| {
(token == *known).then_some(*category)
})
CATEGORIES
.iter()
.find_map(|(known, category)| (token == *known).then_some(*category))
})
.collect();
if value.contains("triggerUncaughtException") && value.contains("fromPromise") {

View File

@ -106,19 +106,19 @@ describe("chat acceptance contracts", () => {
assertChatTaskHandoff({ ...task, projectId: null }, [run], source),
).toThrow();
});
it("finds a committed descriptive output document without accepting a copied plan or a claim", async () => {
it.each(["project-description", "welcome-note", "output"])("finds committed %s output without accepting a copied plan or a claim", async (key) => {
const output = {
...plan,
id: "description-doc",
issueId: "work",
key: "project-description",
key,
body: "A completed description with CHAT123.",
createdByAgentId: "agent",
};
const get = vi.fn(async (path: string) => {
if (path === "/api/issues/work/documents")
return [{ key: "plan" }, { key: "project-description" }];
if (path === "/api/issues/work/documents/project-description")
return [{ key: "plan" }, { key }];
if (path === `/api/issues/work/documents/${key}`)
return output;
throw new Error(`Unexpected document read: ${path}`);
});

View File

@ -642,22 +642,16 @@ export async function runChatFlow(input: {
acceptedPlan!.latestRevisionId,
);
} else assertChatTaskHandoff(child, taskRuns, issue!);
const output =
caseId === "multi-repository"
? await readChatOutputDocument(api, child.id, marker)
: await api.get<Plan>(`/api/issues/${child.id}/documents/output`);
const output = await readChatOutputDocument(api, child.id, marker);
expect(output.body).toContain(marker);
if (caseId === "multi-repository") {
const outputKey = (output as ChatOutputDocument).key;
await input.evidence("chat-execution-output.json", {
taskId: child.id,
document: output,
revisions: await api.get(
`/api/issues/${child.id}/documents/${encodeURIComponent(outputKey)}/revisions`,
),
executionRunIds: taskRuns.map((run) => run.id),
});
}
await input.evidence("chat-execution-output.json", {
taskId: child.id,
document: output,
revisions: await api.get(
`/api/issues/${child.id}/documents/${encodeURIComponent(output.key)}/revisions`,
),
executionRunIds: taskRuns.map((run) => run.id),
});
expect(
(await comments())
.filter((c) => c.authorAgentId)