Skip to content

feat(ai-engineer): split the sleep counter into foreground blocks, deferred watchers and unattributed Codex#2282

Merged
devantler merged 5 commits into
mainfrom
claude/telemetry-sleep-classification
Jul 19, 2026
Merged

feat(ai-engineer): split the sleep counter into foreground blocks, deferred watchers and unattributed Codex#2282
devantler merged 5 commits into
mainfrom
claude/telemetry-sleep-classification

Conversation

@devantler

@devantler devantler commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

🤖 Generated by the Daily AI Engineer

Why

We have been trying to tell whether the agents busy-wait, and the number we were using could not answer that. It counted a legitimate background watcher exactly the same as a genuine block — and the improver's own runs create several watchers every tick, so the measurement was polluted by the thing reading it. Yesterday a falling total was read as progress when the per-session rate had actually gone up.

What

Splits the sleep count into three: real foreground blocks, compliant background watchers, and Codex's sleeps, which we honestly cannot attribute because that runtime exposes no background flag at all. Rates are now reported per session with the denominator stated.

Running it on live data immediately changed the picture: of 340 sleeps, only 101 are actual foreground blocks — and Codex, previously assumed near-zero, accounts for 195 of them, the largest share. The prior conclusion that this was a Claude-only problem does not survive the sharper measurement.

This only sharpens what we measure — nothing was narrowed or removed.

Fixes #2283

…ferred watchers and unattributed Codex

The raw sleep total could not answer the question it was used to answer. It
scored a contract-COMPLIANT backgrounded watcher identically to a foreground
busy-wait, and the improver's own runs arm several watchers per tick — so the
metric polluted itself with the noise of the component reading it.

Classification is structural (`run_in_background` on the tool call), so prose
cannot fake it; the key is omitted when false, so absence reads as foreground.
Codex is a STATED GAP, not a zero: 767 live exec_command calls carried
`yield_time_ms` (an output-read timeout) and zero carried any background flag,
so that runtime exposes no surface to classify. Its sleeps are counted but not
attributed.

One jq expression with a class filter rather than a second extractor — a
parallel copy is how detector parity broke six times on the redactor — plus a
sum check that warns when the classes stop summing to the total.

This SHARPENS the measurement and removes nothing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

@codex review for measurement correctness: whether the class filter and the total can drift apart, whether the run_in_background absence-means-foreground reading is sound, and whether any guard here is vacuous.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f75ed95fad

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/scripts/agent-telemetry.sh Outdated
Comment thread .claude/scripts/agent-telemetry.sh Outdated
Comment thread .claude/scripts/agent-telemetry.sh
…ally, exclude never-ran calls

Three review findings, all valid:

1. The labels claimed a compliance verdict the data does not support.
   run_in_background is LAUNCH MODE, not intent: the contract permits a
   foreground bare sleep as a local timer for a process the agent started, and
   a backgrounded sleep can still be a redundant poll. Labels are now neutral
   and the note says plainly that a foreground count is a candidate, not a
   violation, and a background count is not an exoneration.

2. Four independent scans of a LIVE corpus (the sibling writes transcripts
   while we read) could observe four different states and fire the drift
   warning spuriously. Each file is now snapshotted once and every class comes
   from that copy; the total is the SUM of the classes, so drift is impossible
   by construction rather than merely detected. The drift warning is dropped
   with it — over a derived sum it would be a vacuous guard.

3. A hook-blocked or permission-denied call never ran, so counting it as a
   foreground block reports waiting that never happened.

Note on (3): keying the exclusion on is_error was too broad and the suite
caught it. A TIMED-OUT sleep also carries is_error:true, but it ran — and it is
the most expensive block in the corpus, so suppressing it would have hidden the
worst case while claiming to measure it. The exclusion matches the two
never-ran shapes explicitly, with a guard pinning the timeout case.

111/111 tests; the two new guards RED-proven by ablation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

All three findings fixed in 5edf384 (replies on each thread). CI green on both ubuntu and macos legs; 111/111 tests with the two new guards RED-proven by ablation.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5edf384647

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/scripts/agent-telemetry.sh Outdated
Comment thread .claude/scripts/agent-telemetry.sh Outdated
Comment thread .claude/scripts/agent-telemetry.sh Outdated
Comment thread .claude/scripts/agent-telemetry.sh Outdated
…regex, halve the parse passes

Four review findings, all valid:

P1 — the snapshot directory holds an UNREDACTED transcript copy and extracted
command text, either of which can carry inline credentials, and it was not in
the signal traps. A scheduler SIGTERM could leave that material in /tmp. It is
now registered with both traps BEFORE it is populated, and deregistered when
removed.

The never-ran shapes were a SECOND hand-maintained copy of a list the safety
detector already had, so a permission-DENIED sleep still counted as a
foreground launch while a hook-blocked one did not. Both now read one
NEVER_RAN_RE constant — two copies of a shape list is exactly how detector
parity broke six times on the redactor.

Denial ids are resolved once per file instead of once per class, cutting six
full jq passes per transcript to two.

The report note claimed every errored result was excluded, which contradicted
both the code and the timeout regression test. Narrowed to the never-ran cases.

On the signal-cleanup test: an end-to-end kill test was attempted twice and was
VACUOUS both times (it passed with SNAP deliberately removed from the traps).
Replaced with a deterministic structural assertion, with the residual gap
stated in the test rather than hidden behind a green tick.

114/114; the new guards RED-proven by ablation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

All four findings fixed in c2b612f (replies on each thread), including the P1 snapshot-cleanup gap. CI green at this head; 114/114 tests with the new guards RED-proven by ablation.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c2b612f662

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/scripts/agent-telemetry.sh Outdated
Comment thread .claude/skills/agent-improvement/SKILL.md Outdated
Comment thread .claude/scripts/agent-telemetry.sh Outdated
Comment thread .claude/scripts/agent-telemetry.sh Outdated
…e tagged pass

Round 3 raised four findings and three clustered on the snapshot added in round
2 — which is the signal that the snapshot was the defect, not something to
harden. Removing it closes all three at once and leaves the script smaller.

The P1 was unfixable in place: `main` is the left side of `main | redact`, so it
runs in a pipeline subshell and a trap set there never fires when the scheduler
signals the top-level PID. The snapshot existed only to keep the class counts
consistent — a single tagged pass gives that for free, writes no unredacted copy
to disk, and needs no cleanup at all. Four transcript traversals become one.

Each line of a command carries a control-character class tag, so multi-line
commands keep their structure and untrusted command text cannot forge a tag and
move itself between classes (guarded).

Also: the never-ran match is now anchored to the harness denial envelope, so an
executed command that fails while printing "approval denied for tool" in its own
output is no longer treated as never-run and silently removed from every class;
and the skill's guidance no longer calls a background launch "compliant", which
contradicted the report's own warning and would have produced exactly the false
verdict that warning exists to prevent.

Classification now lives in one function, so there is no second classifier to
drift from the first.

115/115; live runtime 21.5s -> 17.8s.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

All four fixed in af7531e — the snapshot is deleted rather than hardened, since three of the four findings clustered on it. One tagged pass now does the classification; no unredacted copy touches disk. CI green at this head; 115/115.

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: af7531e4ae

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .claude/scripts/agent-telemetry.sh Outdated
…enial envelope

A tool_result carries `content` either as a plain string or as the
array-of-text-blocks shape. Anchoring the never-ran match (previous commit)
without normalising meant `tostring` produced `[{"type":"text"...` for the array
form, so the pattern never reached the denial text and a call that never ran was
counted as an executed foreground launch.

Normalised the same way the safety detector already does. Every denial fixture
in the suite used string content, which is why anchoring looked correct — the
array shape is now covered explicitly, RED-proven by ablation.

115/115.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

Fixed in 6902821 — content is normalised before the anchored denial match, covering the array-of-text-blocks shape my fixtures had all missed. CI green at this head; 115/115.

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 690282122b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@devantler

Copy link
Copy Markdown
Contributor Author

🤖 Generated by the Daily AI Engineer

Readiness — self-promoting.

  • Programmatically tested: 115/115. Six guards RED-proven by ablation (background classification, class-sum, blocked-call exclusion, timed-out-still-counts, shared denial shapes, content normalisation). Green on both ubuntu and macos legs.
  • Reviewed: green Codex review at 690282122b = current head. 13 findings across 4 rounds, all fixed-or-refuted, all 12 threads resolved. CodeRabbit never reviewed this PR (Codex lane), so no pre-merge summary exists — the documented lane-choice consequence, not a gap.
  • Tried and evaluated as a user: ran the miner against the live corpus after every round and read the actual output, not just the tests. It reports 354 sleeps as 113 foreground / 46 background / 195 Codex-unattributed, with per-session rates and stated denominators. Runtime went 21.5s to 17.8s once the snapshot was removed. The result it produces is the point of the change: the previous conclusion that busy-waiting was a Claude-instance problem does not survive the sharper measurement.

@devantler
devantler marked this pull request as ready for review July 19, 2026 23:55
@devantler
devantler merged commit c2f1a62 into main Jul 19, 2026
24 checks passed
@devantler
devantler deleted the claude/telemetry-sleep-classification branch July 19, 2026 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sleep telemetry cannot distinguish a compliant background watcher from a foreground block

1 participant