Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/LANDMINES.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,14 @@ fix is ever reverted or bypassed. Referenced from CLAUDE.md.
one real scenario (CLI drops `assistant`/`user` entirely) this tripwire
exists to catch. Stayed a named allowlist. Run `npm run check` (now
includes `schema-drift:check`) before trusting any future change here.
**Recurred 2026-11-06:** simulating the tripwire over 30 days of real
transcripts, five Claude sessions (CLI v2.1.299-310) false-tripped, worst
streak 43, on `api-request`, `api-request-blob`,
`artifact-autoreact-ledger`, `queue-operation` and `system` lines. Codex
peaked at 19 of 20 on `event_msg`. All six added to the recognized list;
worst streak is now 12 (Claude) and 4 (Codex). Before adding a type,
replay recent `~/.claude/projects` and `~/.codex/sessions` files through
the allowlist and print envelope types only, never message content.

- **Copy-paste out of a Claude Code pane silently breaks long commands
(found 2026-09-27).** Two distinct bugs were conflated live before the
Expand Down
12 changes: 12 additions & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7054,3 +7054,15 @@ their screenshots), reported the mid-drag text selection fixed above, asked for
the view pills, and accepted the phase. Not individually confirmed from the
manual list: release outside the window (3), Finder file drop alongside board
drags (5), external rename during a drag (6).

### Schema-drift side-channel allowlist (2026-11-06)

Automated: `npm run check` pins the six new types as recognized and the
2026-09-12 types as still flagged. Offline replay over 30 days of real
transcripts (161 Claude, 80 Codex files, envelope types only): 0 sessions
reach the threshold, down from 5.

Manual, installed app after `npm run reinstall`: run a long Claude session
with subagents and confirm no "transcript format doesn't match" banner
appears. Not yet run; the banner isn't persisted, so the old false trip was
only reproduced by replay.
12 changes: 12 additions & 0 deletions scripts/decision-integrity-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ for (const line of [
]) {
assert.equal(transcriptEnvelopeType(line), "unrecognized", `${line} must be flagged, not silently ignored`);
}
// Side-channel lines that false-tripped the tripwire on CLI v2.1.299-310 and
// nearly did on Codex (2026-11-06).
for (const type of [
"api-request",
"api-request-blob",
"artifact-autoreact-ledger",
"queue-operation",
"system",
"event_msg",
]) {
assert.equal(transcriptEnvelopeType(JSON.stringify({ type })), "recognized", `${type} is a known side channel`);
}
assert.equal(transcriptEnvelopeType("not json"), "unparseable");
assert.equal(transcriptEnvelopeType('{"type":"assistant"'), "unparseable", "a partial line mid-flush is not drift");

Expand Down
12 changes: 6 additions & 6 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions src/lib/decisions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ const assistantBuf = new Map<string, PendingAssistant>(); // session_id -> pendi
// allowlist, updated by hand each time a new benign side-channel type
// ships (see decision-integrity-check.ts's regression test, which pins the
// 2026-09-12 types as still-must-flag — never add those here).
//
// Recurrence 2026-11-06: simulated over 30 days of real transcripts, five
// Claude sessions (CLI v2.1.299-310) false-tripped on api-request,
// api-request-blob, artifact-autoreact-ledger, queue-operation and system
// lines; Codex peaked at 19 of 20 on event_msg. With these added the worst
// streak is 12 (Claude) and 4 (Codex).
const SCHEMA_DRIFT_THRESHOLD = 20;
const unrecognizedStreak = new Map<string, number>(); // session_id -> consecutive unrecognized-envelope lines
const driftWarned = new Set<string>(); // session_id already warned — fire once, not per line
Expand Down Expand Up @@ -79,6 +85,12 @@ export function transcriptEnvelopeType(line: string): "recognized" | "unrecogniz
obj.type === "pi_message" ||
obj.type === "deepseek_message" ||
obj.type === "attachment" ||
obj.type === "api-request" ||
obj.type === "api-request-blob" ||
obj.type === "artifact-autoreact-ledger" ||
obj.type === "queue-operation" ||
obj.type === "system" ||
obj.type === "event_msg" ||
isAntigravityEnvelope(obj)
? "recognized"
: "unrecognized";
Expand Down
Loading