fix(agent-claude-code): treat pr-link as re-snapshot noise too
Empirical evidence from production: ao-160's JSONL had the same PR (#1911) written as a `pr-link` entry 33 times in the last 200 lines, alternating with permission-mode/ai-title in the same trio pattern. Internal timestamps showed re-emissions ~1 minute apart (15:24:40, 15:26:27, 15:27:42 — same PR, three writes within ~3 minutes). User-visible bug: ao-139 (last real work 11h ago) and ao-160 (last real work over a day ago) were both showing as `ready` on the dashboard because the file mtime kept moving forward via these pr-link re-snapshots, and pr-link was in the explicit ready/idle bookkeeping case. Fix: move `pr-link` from the bookkeeping case into NOISE_JSONL_TYPES so the cascade skips it and falls through to the AO JSONL pipeline (and ultimately to the stale-native idle fallback for genuinely dormant sessions). Real PR creation is still observable via the assistant message that asked for it and via the gh-tracker. Verified: 200/200 plugin tests pass. The existing pr-link test updated to expect idle (matches new behavior); added comment with the empirical evidence.
This commit is contained in:
parent
8aeffdcd86
commit
4e0e64ab06
|
|
@ -407,9 +407,19 @@ describe("Claude Code Activity Detection", () => {
|
|||
expect((await agent.getActivityState(makeSession()))?.state).toBe("ready");
|
||||
});
|
||||
|
||||
it("returns 'ready' for recent 'pr-link' (bookkeeping)", async () => {
|
||||
writeJsonl([{ type: "pr-link" }]);
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("ready");
|
||||
it("returns 'idle' (not 'ready') for recent 'pr-link' — re-snapshot noise", async () => {
|
||||
// Empirical evidence (ao-160): the SAME PR (#1911) was written as
|
||||
// pr-link 33 times in the last 200 lines, with new timestamps each
|
||||
// time. It's a periodic state snapshot, not a one-shot event.
|
||||
// Treat as noise so dormant sessions don't show as "ready" forever.
|
||||
writeJsonl([
|
||||
{
|
||||
type: "pr-link",
|
||||
prNumber: 1911,
|
||||
prUrl: "https://github.com/owner/repo/pull/1911",
|
||||
},
|
||||
]);
|
||||
expect((await agent.getActivityState(makeSession()))?.state).toBe("idle");
|
||||
});
|
||||
|
||||
it("returns 'ready' for recent 'attachment' (bookkeeping)", async () => {
|
||||
|
|
|
|||
|
|
@ -352,6 +352,14 @@ const NOISE_JSONL_TYPES: ReadonlySet<string> = new Set([
|
|||
"agent-color",
|
||||
"agent-name",
|
||||
"custom-title",
|
||||
// pr-link is also re-snapshot noise — verified on ao-160's JSONL where the
|
||||
// SAME PR (#1911) was written as a `pr-link` entry three times within
|
||||
// minutes (count: 33 pr-link vs 21 user messages in the last 200 lines).
|
||||
// The first emission is real; subsequent re-emissions are state snapshots.
|
||||
// We can't distinguish first vs Nth from the last line alone, so treat
|
||||
// all pr-link as noise. Real PR creation is still observable via the
|
||||
// assistant message and the gh-tracker side.
|
||||
"pr-link",
|
||||
]);
|
||||
|
||||
/**
|
||||
|
|
@ -443,14 +451,14 @@ export async function getClaudeActivityState(
|
|||
case "summary":
|
||||
return { state: ageMs > threshold ? "idle" : "ready", timestamp };
|
||||
|
||||
// Bookkeeping types Claude writes AFTER finishing a turn (file
|
||||
// edits, PR creation, attachment context). Map to ready/idle by
|
||||
// age, same as assistant/summary. Pure UI-noise types
|
||||
// (permission-mode, ai-title, agent-*, custom-title) are handled
|
||||
// separately by the NOISE_JSONL_TYPES check above the switch.
|
||||
// Bookkeeping types Claude writes AFTER a real event (file edits,
|
||||
// attachment context, queue housekeeping, prompt submit). Map to
|
||||
// ready/idle by age, same as assistant/summary. The pure re-snapshot
|
||||
// types (permission-mode, ai-title, agent-*, custom-title, pr-link)
|
||||
// are filtered out earlier by NOISE_JSONL_TYPES — they get written
|
||||
// continuously without indicating activity.
|
||||
case "file-history-snapshot":
|
||||
case "attachment":
|
||||
case "pr-link":
|
||||
case "queue-operation":
|
||||
case "last-prompt":
|
||||
return { state: ageMs > threshold ? "idle" : "ready", timestamp };
|
||||
|
|
|
|||
Loading…
Reference in New Issue