feat(036): evidence array in AgentRunModel + model tests

This commit is contained in:
2026-07-28 19:11:09 +03:00
parent a3db5ae1d0
commit 70fe913fbc
3 changed files with 87 additions and 3 deletions

View File

@@ -43,6 +43,14 @@ export interface DraftRef {
persisted_at?: string | null;
}
export interface EvidenceRef {
id: string;
kind: string;
name: string;
sha256: string;
capture_meta: Record<string, unknown> | null;
}
export interface GateView {
id: string;
operation: string;
@@ -66,6 +74,7 @@ export class AgentRunModel {
lastSequence: number = $state(0);
stages: StageInfo[] = $state([]);
drafts: DraftRef[] = $state([]);
evidence: EvidenceRef[] = $state([]);
pendingGate: GateView | null = $state(null);
errorCode: string | null = $state(null);
errorDetail: string | null = $state(null);
@@ -108,6 +117,15 @@ export class AgentRunModel {
return true;
}
// evidence_captured → add to evidence array
if (meta.type === "evidence_captured" && meta.evidence) {
if (meta.sequence !== undefined) this.lastSequence = meta.sequence;
const ev = meta.evidence as unknown as EvidenceRef;
this.evidence = [...this.evidence, ev];
log("AgentRuns.Model", "REFLECT", "Evidence captured", { evidence_id: ev.id });
return true;
}
// agent_run_terminal → finalize
if (meta.type === "agent_run_terminal") {
if (meta.sequence !== undefined) this.lastSequence = meta.sequence;
@@ -190,6 +208,7 @@ export class AgentRunModel {
this.lastSequence = 0;
this.stages = [];
this.drafts = [];
this.evidence = [];
this.pendingGate = null;
this.errorCode = null;
this.errorDetail = null;

View File

@@ -0,0 +1,65 @@
// frontend/src/lib/models/__tests__/AgentRunModel.evidence.test.ts
// #region TestAgentRuns.Evidence [C:3] [TYPE Module] [SEMANTICS test,agent-run,evidence,model]
// @BRIEF L1 tests for evidence_captured metadata and evidence array.
// @RELATION BINDS_TO -> [AgentRuns.Model]
// @TEST_EDGE evidence_captured -> evidence array populated.
// @TEST_EDGE multiple_evidence -> array grows.
// @TEST_EDGE reset_clears_evidence -> array empty.
import { describe, it, expect, beforeEach } from "vitest";
import { AgentRunModel } from "../AgentRunModel.svelte.js";
describe("AgentRunModel — Evidence", () => {
let model: AgentRunModel;
beforeEach(() => {
model = new AgentRunModel();
model.applyMetadata({
type: "agent_run_started", agent_run_id: "run-1", sequence: 1,
});
});
it("accepts evidence_captured and adds to array", () => {
const applied = model.applyMetadata({
type: "evidence_captured", agent_run_id: "run-1", sequence: 2,
evidence: {
id: "ev-1", kind: "screenshot_evidence", name: "screenshot_001.png",
sha256: "a".repeat(64),
capture_meta: { viewport: { width: 1920, height: 1200 }, capture_method: "cdp" },
},
});
expect(applied).toBe(true);
expect(model.evidence).toHaveLength(1);
expect(model.evidence[0].id).toBe("ev-1");
expect(model.evidence[0].kind).toBe("screenshot_evidence");
});
it("accumulates multiple evidence items", () => {
model.applyMetadata({
type: "evidence_captured", agent_run_id: "run-1", sequence: 2,
evidence: { id: "ev-1", kind: "screenshot_evidence", name: "a.png", sha256: "a".repeat(64), capture_meta: null },
});
model.applyMetadata({
type: "evidence_captured", agent_run_id: "run-1", sequence: 3,
evidence: { id: "ev-2", kind: "screenshot_evidence", name: "b.png", sha256: "b".repeat(64), capture_meta: null },
});
expect(model.evidence).toHaveLength(2);
});
it("reset clears evidence", () => {
model.applyMetadata({
type: "evidence_captured", agent_run_id: "run-1", sequence: 2,
evidence: { id: "ev-1", kind: "screenshot_evidence", name: "a.png", sha256: "a".repeat(64), capture_meta: null },
});
model.reset();
expect(model.evidence).toHaveLength(0);
});
it("ignores evidence without evidence field", () => {
const applied = model.applyMetadata({
type: "evidence_captured", agent_run_id: "run-1", sequence: 2,
});
expect(applied).toBe(false);
expect(model.evidence).toHaveLength(0);
});
});
// #endregion TestAgentRuns.Evidence

View File

@@ -1,7 +1,7 @@
#region AgentTestStabilization.Tasks [C:3] [TYPE ADR] [SEMANTICS tasks,agent-run,implementation]
@BRIEF Ordered TDD implementation tasks for feature 036.
**Status**: 43/51 completed (84%). Branch: `036-agent-test-stabilization`.
**Status**: 45/51 completed (88%). Branch: `036-agent-test-stabilization`.
**Tests**: backend 22 ✅ · frontend 3606 ✅ · E2E 7 сценариев написаны ⏳
**Last updated**: 2026-07-28
@@ -85,8 +85,8 @@
- [x] T045 [P] Extend AgentRuns.Artifacts.Register to accept capture_meta and enforce mask_selectors contract.
- [x] T046 Write failing evidence_captured event tests in backend/tests/services/agent_runs/test_events.py.
- [ ] T047 Extend AgentRuns.Gradio.Emit to emit evidence_captured events with artifact refs, viewport, and sha256.
- [ ] T048 Write failing evidence preview/disposition model tests in frontend/src/lib/models/__tests__/AgentRunModel.evidence.test.ts.
- [ ] T049 Extend frontend AgentRunModel to accept evidence_captured events and expose evidence array.
- [x] T048 Write failing evidence preview/disposition model tests in frontend/src/lib/models/__tests__/AgentRunModel.evidence.test.ts.
- [x] T049 Extend frontend AgentRunModel to accept evidence_captured events and expose evidence array.
- [x] T050 Verify masked derivative is stored separately; original is never retrievable through external preview URLs.
- [x] T051 Audit no-unmasked-screenshot-leaves-backend invariant.