@letta-ai/letta-code 0.30.26 → 0.30.28
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/agent-presets.js +17 -17
- package/dist/agent-presets.js.map +1 -1
- package/dist/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/types/agent/turn-recovery-policy.d.ts +33 -0
- package/dist/types/agent/turn-recovery-policy.d.ts.map +1 -1
- package/dist/types/tools/impl/apply-patch.d.ts.map +1 -1
- package/dist/types/tools/secret-substitution.d.ts.map +1 -1
- package/dist/types/types/loop-status-protocol.d.ts +17 -0
- package/dist/types/types/loop-status-protocol.d.ts.map +1 -0
- package/dist/types/types/protocol_v2.d.ts +2 -19
- package/dist/types/types/protocol_v2.d.ts.map +1 -1
- package/dist/types/websocket/listener/inbound-queue.d.ts +5 -0
- package/dist/types/websocket/listener/inbound-queue.d.ts.map +1 -0
- package/dist/types/websocket/listener/protocol-outbound-routing.d.ts +9 -0
- package/dist/types/websocket/listener/protocol-outbound-routing.d.ts.map +1 -0
- package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
- package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
- package/dist/types/websocket/listener/turn-correlation.d.ts +10 -0
- package/dist/types/websocket/listener/turn-correlation.d.ts.map +1 -0
- package/dist/types/websocket/listener/types.d.ts +4 -0
- package/dist/types/websocket/listener/types.d.ts.map +1 -1
- package/letta.js +545 -120
- package/package.json +1 -1
- package/scripts/claude-watch/agent-watch.ts +622 -0
- package/scripts/claude-watch/docs-snapshot.test.ts +259 -0
- package/scripts/claude-watch/docs-snapshot.ts +672 -0
- package/scripts/claude-watch/fixtures/historical-replays.json +52 -0
- package/scripts/claude-watch/github.ts +137 -0
- package/scripts/claude-watch/release-analysis.test.ts +235 -0
- package/scripts/claude-watch/release-analysis.ts +297 -0
- package/scripts/claude-watch/release-source.test.ts +179 -0
- package/scripts/claude-watch/release-source.ts +369 -0
- package/scripts/claude-watch/runtime-observations.ts +98 -0
- package/scripts/claude-watch/runtime-probe.test.ts +576 -0
- package/scripts/claude-watch/runtime-probe.ts +911 -0
- package/scripts/claude-watch/runtime-sandbox.ts +170 -0
- package/scripts/claude-watch/state-branch.test.ts +211 -0
- package/scripts/claude-watch/state-branch.ts +316 -0
- package/scripts/claude-watch/tracker.test.ts +148 -0
- package/scripts/claude-watch/tracker.ts +325 -0
- package/scripts/claude-watch/types.ts +186 -0
- package/scripts/claude-watch/update-tracker.ts +201 -0
- package/scripts/codex-watch/agent-watch.ts +2 -2
- package/scripts/codex-watch/release-analysis.ts +14 -2
- package/scripts/codex-watch/tracker.ts +1 -3
- package/scripts/run-unit-tests.cjs +2 -0
- package/scripts/source-file-size-baseline.json +6 -5
- package/skills/creating-mods/references/commands.md +1 -1
- package/skills/creating-mods/references/ui.md +1 -1
- package/skills/customizing-commands/SKILL.md +1 -1
- package/skills/initializing-memory/SKILL.md +7 -7
- package/skills/self-configuration/SKILL.md +4 -4
- package/scripts/codex-watch/check-release.ts +0 -128
- package/scripts/codex-watch/render-issue.ts +0 -273
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "read-tab-prefix",
|
|
4
|
+
"previous_version": "2.1.227",
|
|
5
|
+
"current_version": "2.1.228",
|
|
6
|
+
"previous_tools": ["Read"],
|
|
7
|
+
"current_tools": ["Read"],
|
|
8
|
+
"probe_name": "read-lines-9-10-tab-prefix",
|
|
9
|
+
"previous_result": " 9→line9\n10→line10",
|
|
10
|
+
"current_result": "9\tline9\n10\tline10",
|
|
11
|
+
"expected_verdict": "tool contract review needed",
|
|
12
|
+
"local_surfaces": ["src/tools/impl/read.ts", "src/tools/read.test.ts"]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": "task-metadata-delete-contract",
|
|
16
|
+
"previous_version": "2.1.19",
|
|
17
|
+
"current_version": "2.1.20",
|
|
18
|
+
"previous_tools": ["TaskCreate", "TaskGet", "TaskList", "TaskUpdate"],
|
|
19
|
+
"current_tools": ["TaskCreate", "TaskGet", "TaskList", "TaskUpdate"],
|
|
20
|
+
"probe_name": "task-metadata-delete-contract",
|
|
21
|
+
"previous_result": "metadata values must be strings; deleted task remains retrievable",
|
|
22
|
+
"current_result": "metadata accepts arbitrary values; null deletes a key; deleted task is absent",
|
|
23
|
+
"expected_verdict": "tool contract review needed",
|
|
24
|
+
"local_surfaces": [
|
|
25
|
+
"src/tools/schemas/TaskUpdate.json",
|
|
26
|
+
"src/tools/impl/task-update.ts",
|
|
27
|
+
"src/tools/impl/tasks/store.ts"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": "task-tool-transition",
|
|
32
|
+
"previous_version": "2.1.82",
|
|
33
|
+
"current_version": "2.1.83",
|
|
34
|
+
"previous_tools": ["LS", "MultiEdit", "Read", "TaskOutput", "TodoWrite"],
|
|
35
|
+
"current_tools": [
|
|
36
|
+
"Read",
|
|
37
|
+
"TaskCreate",
|
|
38
|
+
"TaskGet",
|
|
39
|
+
"TaskList",
|
|
40
|
+
"TaskUpdate"
|
|
41
|
+
],
|
|
42
|
+
"probe_name": "task-metadata-delete-contract",
|
|
43
|
+
"previous_result": "legacy task surface",
|
|
44
|
+
"current_result": "task family advertised; TaskOutput deprecated in favor of Read output path",
|
|
45
|
+
"expected_verdict": "tool surface review needed",
|
|
46
|
+
"local_surfaces": [
|
|
47
|
+
"src/agent/subagents/builtin/fork.md",
|
|
48
|
+
"src/agent/subagents/builtin/general-purpose.md",
|
|
49
|
+
"src/tools/descriptions/TaskOutput.md"
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
]
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
|
|
6
|
+
export function runGh(args: string[], input?: string): string {
|
|
7
|
+
const result = spawnSync("gh", args, {
|
|
8
|
+
encoding: "utf8",
|
|
9
|
+
input,
|
|
10
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
11
|
+
stdio: input ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"],
|
|
12
|
+
});
|
|
13
|
+
if (result.status !== 0) {
|
|
14
|
+
throw new Error(`gh ${args.join(" ")} failed:\n${result.stderr}`);
|
|
15
|
+
}
|
|
16
|
+
return result.stdout;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function ghJson<T>(args: string[]): T {
|
|
20
|
+
return JSON.parse(runGh(args)) as T;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function ensureLabel(repo: string, label: string): void {
|
|
24
|
+
const result = spawnSync(
|
|
25
|
+
"gh",
|
|
26
|
+
[
|
|
27
|
+
"label",
|
|
28
|
+
"create",
|
|
29
|
+
label,
|
|
30
|
+
"--repo",
|
|
31
|
+
repo,
|
|
32
|
+
"--description",
|
|
33
|
+
"Automated Claude Code upstream monitoring",
|
|
34
|
+
"--color",
|
|
35
|
+
"7E57C2",
|
|
36
|
+
],
|
|
37
|
+
{ encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] },
|
|
38
|
+
);
|
|
39
|
+
if (result.status !== 0 && !result.stderr.includes("already exists")) {
|
|
40
|
+
throw new Error(`gh label create ${label} failed:\n${result.stderr}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function findIssueByExactTitle(
|
|
45
|
+
repo: string,
|
|
46
|
+
title: string,
|
|
47
|
+
): { number: number; url: string; body: string } | null {
|
|
48
|
+
const issues = ghJson<Array<{ number: number; title: string; url: string }>>([
|
|
49
|
+
"issue",
|
|
50
|
+
"list",
|
|
51
|
+
"--repo",
|
|
52
|
+
repo,
|
|
53
|
+
"--state",
|
|
54
|
+
"all",
|
|
55
|
+
"--search",
|
|
56
|
+
`${title} in:title`,
|
|
57
|
+
"--limit",
|
|
58
|
+
"20",
|
|
59
|
+
"--json",
|
|
60
|
+
"number,title,url",
|
|
61
|
+
]);
|
|
62
|
+
const issue = issues.find((candidate) => candidate.title === title);
|
|
63
|
+
if (!issue) return null;
|
|
64
|
+
return { ...issue, body: getIssueBody(repo, issue.number) };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function createIssue(
|
|
68
|
+
repo: string,
|
|
69
|
+
title: string,
|
|
70
|
+
body: string,
|
|
71
|
+
label: string,
|
|
72
|
+
): { number: number; url: string; body: string } {
|
|
73
|
+
const bodyFile = writeTemp(body, "claude-watch-issue");
|
|
74
|
+
try {
|
|
75
|
+
const url = runGh([
|
|
76
|
+
"issue",
|
|
77
|
+
"create",
|
|
78
|
+
"--repo",
|
|
79
|
+
repo,
|
|
80
|
+
"--title",
|
|
81
|
+
title,
|
|
82
|
+
"--body-file",
|
|
83
|
+
bodyFile,
|
|
84
|
+
"--label",
|
|
85
|
+
label,
|
|
86
|
+
]).trim();
|
|
87
|
+
const issue = ghJson<{ number: number; url: string; body: string | null }>([
|
|
88
|
+
"issue",
|
|
89
|
+
"view",
|
|
90
|
+
url,
|
|
91
|
+
"--repo",
|
|
92
|
+
repo,
|
|
93
|
+
"--json",
|
|
94
|
+
"number,url,body",
|
|
95
|
+
]);
|
|
96
|
+
return { ...issue, body: issue.body ?? "" };
|
|
97
|
+
} finally {
|
|
98
|
+
rmSync(bodyFile, { force: true });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function getIssueBody(repo: string, issue: number): string {
|
|
103
|
+
return (
|
|
104
|
+
ghJson<{ body: string | null }>([
|
|
105
|
+
"issue",
|
|
106
|
+
"view",
|
|
107
|
+
String(issue),
|
|
108
|
+
"--repo",
|
|
109
|
+
repo,
|
|
110
|
+
"--json",
|
|
111
|
+
"body",
|
|
112
|
+
]).body ?? ""
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function editIssueBody(repo: string, issue: number, body: string): void {
|
|
117
|
+
const bodyFile = writeTemp(body, "claude-watch-tracker");
|
|
118
|
+
try {
|
|
119
|
+
runGh([
|
|
120
|
+
"issue",
|
|
121
|
+
"edit",
|
|
122
|
+
String(issue),
|
|
123
|
+
"--repo",
|
|
124
|
+
repo,
|
|
125
|
+
"--body-file",
|
|
126
|
+
bodyFile,
|
|
127
|
+
]);
|
|
128
|
+
} finally {
|
|
129
|
+
rmSync(bodyFile, { force: true });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function writeTemp(body: string, prefix: string): string {
|
|
134
|
+
const path = join(tmpdir(), `${prefix}-${process.pid}-${Date.now()}.md`);
|
|
135
|
+
writeFileSync(path, body);
|
|
136
|
+
return path;
|
|
137
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
analyzeClaudeCandidate,
|
|
6
|
+
buildClaudeCandidateId,
|
|
7
|
+
} from "./release-analysis.ts";
|
|
8
|
+
import type {
|
|
9
|
+
ClaudeDocsSnapshot,
|
|
10
|
+
ClaudeProbeObservation,
|
|
11
|
+
ClaudeReleaseCandidate,
|
|
12
|
+
ClaudeRuntimeSnapshot,
|
|
13
|
+
ClaudeWatchStateSnapshot,
|
|
14
|
+
ClaudeWatchVerdict,
|
|
15
|
+
} from "./types.ts";
|
|
16
|
+
|
|
17
|
+
interface ReplayFixture {
|
|
18
|
+
id: string;
|
|
19
|
+
previous_version: string;
|
|
20
|
+
current_version: string;
|
|
21
|
+
previous_tools: string[];
|
|
22
|
+
current_tools: string[];
|
|
23
|
+
probe_name: string;
|
|
24
|
+
previous_result: string;
|
|
25
|
+
current_result: string;
|
|
26
|
+
expected_verdict: ClaudeWatchVerdict;
|
|
27
|
+
local_surfaces: string[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const fixtures = JSON.parse(
|
|
31
|
+
readFileSync(
|
|
32
|
+
join(import.meta.dir, "fixtures", "historical-replays.json"),
|
|
33
|
+
"utf8",
|
|
34
|
+
),
|
|
35
|
+
) as ReplayFixture[];
|
|
36
|
+
|
|
37
|
+
function docs(digest = "docs-stable"): ClaudeDocsSnapshot {
|
|
38
|
+
return {
|
|
39
|
+
index_url: "https://code.claude.com/docs/llms.txt",
|
|
40
|
+
index_hash: "index",
|
|
41
|
+
digest,
|
|
42
|
+
full_scan: true,
|
|
43
|
+
scanned_at: "2026-08-01T00:00:00Z",
|
|
44
|
+
pages: {},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function probe(name: string, result: string): ClaudeProbeObservation {
|
|
49
|
+
return {
|
|
50
|
+
name,
|
|
51
|
+
status: "passed",
|
|
52
|
+
attempts: 1,
|
|
53
|
+
assertions: { observed_result: result },
|
|
54
|
+
tool_calls: [],
|
|
55
|
+
tool_results: [result],
|
|
56
|
+
filesystem_changes: [],
|
|
57
|
+
error: null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function runtime(
|
|
62
|
+
version: string,
|
|
63
|
+
tools: string[],
|
|
64
|
+
observation: ClaudeProbeObservation,
|
|
65
|
+
): ClaudeRuntimeSnapshot {
|
|
66
|
+
return {
|
|
67
|
+
version,
|
|
68
|
+
version_output: `${version} (Claude Code)`,
|
|
69
|
+
help_text: "--permission-mode <mode>",
|
|
70
|
+
help_hash: "help",
|
|
71
|
+
doctor: { exit_code: 0, summary: "No installation issues found." },
|
|
72
|
+
auto_mode_defaults: {},
|
|
73
|
+
init: {
|
|
74
|
+
tools,
|
|
75
|
+
model: "claude-test",
|
|
76
|
+
capabilities: null,
|
|
77
|
+
stable_fields: {},
|
|
78
|
+
},
|
|
79
|
+
event_inventory: ["assistant", "system/init", "user"],
|
|
80
|
+
probes: [observation],
|
|
81
|
+
digest: `${version}-runtime`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function candidate(version: string): ClaudeReleaseCandidate {
|
|
86
|
+
return {
|
|
87
|
+
version,
|
|
88
|
+
published_at: "2026-08-01T00:00:00Z",
|
|
89
|
+
integrity: `sha512-${version}`,
|
|
90
|
+
tarball_url: `https://registry.npmjs.org/${version}.tgz`,
|
|
91
|
+
release_url: `https://github.com/anthropics/claude-code/releases/tag/v${version}`,
|
|
92
|
+
release_notes_md: "Observable Claude Code harness behavior changed.",
|
|
93
|
+
release_published_at: "2026-08-01T00:01:00Z",
|
|
94
|
+
dist_tags: { latest: version, stable: version, next: null },
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function previousState(
|
|
99
|
+
version: string,
|
|
100
|
+
previousRuntime: ClaudeRuntimeSnapshot,
|
|
101
|
+
): ClaudeWatchStateSnapshot {
|
|
102
|
+
return {
|
|
103
|
+
schema_version: 1,
|
|
104
|
+
candidate_id: buildClaudeCandidateId(
|
|
105
|
+
version,
|
|
106
|
+
docs().digest,
|
|
107
|
+
previousRuntime.digest,
|
|
108
|
+
),
|
|
109
|
+
package_version: version,
|
|
110
|
+
npm_integrity: `sha512-${version}`,
|
|
111
|
+
npm_published_at: "2026-08-01T00:00:00Z",
|
|
112
|
+
release_url: `https://github.com/anthropics/claude-code/releases/tag/v${version}`,
|
|
113
|
+
release_notes_md: "previous",
|
|
114
|
+
release_published_at: "2026-08-01T00:00:00Z",
|
|
115
|
+
dist_tags: { latest: version, stable: version, next: null },
|
|
116
|
+
docs: docs(),
|
|
117
|
+
runtime: previousRuntime,
|
|
118
|
+
fetched_at: "2026-08-01T00:00:00Z",
|
|
119
|
+
workflow_run_url: "https://example.test/actions/1",
|
|
120
|
+
state_commit_parent: null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
describe("historical Claude parity routing fixtures", () => {
|
|
125
|
+
for (const fixture of fixtures) {
|
|
126
|
+
test(`${fixture.id} routes captured evidence to semantic review`, () => {
|
|
127
|
+
const oldRuntime = runtime(
|
|
128
|
+
fixture.previous_version,
|
|
129
|
+
fixture.previous_tools,
|
|
130
|
+
probe(fixture.probe_name, fixture.previous_result),
|
|
131
|
+
);
|
|
132
|
+
const currentRuntime = runtime(
|
|
133
|
+
fixture.current_version,
|
|
134
|
+
fixture.current_tools,
|
|
135
|
+
probe(fixture.probe_name, fixture.current_result),
|
|
136
|
+
);
|
|
137
|
+
const analysis = analyzeClaudeCandidate({
|
|
138
|
+
candidate: candidate(fixture.current_version),
|
|
139
|
+
previous: previousState(fixture.previous_version, oldRuntime),
|
|
140
|
+
currentDocs: docs(),
|
|
141
|
+
currentRuntime,
|
|
142
|
+
workflowRunUrl: "https://example.test/actions/2",
|
|
143
|
+
stateBaseSha: "state-parent",
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(analysis.verdict).toBe(fixture.expected_verdict);
|
|
147
|
+
expect(fixture.local_surfaces.length).toBeGreaterThan(0);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
describe("Claude verdict routing", () => {
|
|
153
|
+
test("bootstrap requires a current-vs-local audit", () => {
|
|
154
|
+
const currentRuntime = runtime(
|
|
155
|
+
"2.1.237",
|
|
156
|
+
["Read"],
|
|
157
|
+
probe("read-lines-9-10-tab-prefix", "9\tline9\n10\tline10"),
|
|
158
|
+
);
|
|
159
|
+
const analysis = analyzeClaudeCandidate({
|
|
160
|
+
candidate: candidate("2.1.237"),
|
|
161
|
+
previous: null,
|
|
162
|
+
currentDocs: docs(),
|
|
163
|
+
currentRuntime,
|
|
164
|
+
workflowRunUrl: "https://example.test/actions/bootstrap",
|
|
165
|
+
stateBaseSha: null,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
expect(analysis.verdict).toBe("manual review required");
|
|
169
|
+
expect(analysis.runtime_snapshot?.init?.tools).toEqual(["Read"]);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("inconclusive behavioral probes require manual review", () => {
|
|
173
|
+
const old = runtime(
|
|
174
|
+
"2.1.236",
|
|
175
|
+
["Read"],
|
|
176
|
+
probe("read-lines-9-10-tab-prefix", "old"),
|
|
177
|
+
);
|
|
178
|
+
const inconclusive = runtime("2.1.237", ["Read"], {
|
|
179
|
+
...probe("read-lines-9-10-tab-prefix", ""),
|
|
180
|
+
status: "inconclusive",
|
|
181
|
+
error: "model did not call Read",
|
|
182
|
+
});
|
|
183
|
+
const analysis = analyzeClaudeCandidate({
|
|
184
|
+
candidate: candidate("2.1.237"),
|
|
185
|
+
previous: previousState("2.1.236", old),
|
|
186
|
+
currentDocs: docs(),
|
|
187
|
+
currentRuntime: inconclusive,
|
|
188
|
+
workflowRunUrl: "https://example.test/actions/3",
|
|
189
|
+
stateBaseSha: "parent",
|
|
190
|
+
});
|
|
191
|
+
expect(analysis.verdict).toBe("manual review required");
|
|
192
|
+
expect(analysis.verdict_reasons[0]).toContain("inconclusive");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("an unwatched docs-only digest change still receives semantic review", () => {
|
|
196
|
+
const stableRuntime = runtime(
|
|
197
|
+
"2.1.237",
|
|
198
|
+
["Read"],
|
|
199
|
+
probe("read-lines-9-10-tab-prefix", "stable"),
|
|
200
|
+
);
|
|
201
|
+
const previous = previousState("2.1.237", stableRuntime);
|
|
202
|
+
previous.docs = {
|
|
203
|
+
...docs("old-docs"),
|
|
204
|
+
pages: {
|
|
205
|
+
"https://code.claude.com/docs/en/billing.md": {
|
|
206
|
+
url: "https://code.claude.com/docs/en/billing.md",
|
|
207
|
+
hash: "old",
|
|
208
|
+
watched: false,
|
|
209
|
+
source_path: null,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
const currentDocs = {
|
|
214
|
+
...docs("new-docs"),
|
|
215
|
+
pages: {
|
|
216
|
+
"https://code.claude.com/docs/en/billing.md": {
|
|
217
|
+
url: "https://code.claude.com/docs/en/billing.md",
|
|
218
|
+
hash: "new",
|
|
219
|
+
watched: false,
|
|
220
|
+
source_path: null,
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
const analysis = analyzeClaudeCandidate({
|
|
225
|
+
candidate: candidate("2.1.237"),
|
|
226
|
+
previous,
|
|
227
|
+
currentDocs,
|
|
228
|
+
currentRuntime: stableRuntime,
|
|
229
|
+
workflowRunUrl: "https://example.test/actions/4",
|
|
230
|
+
stateBaseSha: "parent",
|
|
231
|
+
});
|
|
232
|
+
expect(analysis.verdict).toBe("harness behavior review needed");
|
|
233
|
+
expect(analysis.verdict_reasons[0]).toContain("unwatched");
|
|
234
|
+
});
|
|
235
|
+
});
|