Skip to content

fix(perf): stamp each batched metric with its own timestamp - #1400

Merged
groupthinking merged 2 commits into
mainfrom
claude/clever-heisenberg-8k227t
Aug 7, 2026
Merged

fix(perf): stamp each batched metric with its own timestamp#1400
groupthinking merged 2 commits into
mainfrom
claude/clever-heisenberg-8k227t

Conversation

@groupthinking

Copy link
Copy Markdown
Owner

Canonical issue

Closes #1399

Outcome

PerformanceMonitor.record_metrics becomes the drop-in for serial record_metric that its docstring already claims to be. Each record in a batch is stamped at the moment it is built, instead of all records sharing one timestamp hoisted out of the comprehension.

-            now = datetime.now(timezone.utc)
             records = [
                 PerformanceMetric(
                     ...
-                    timestamp=entry.get("timestamp") or now,
+                    timestamp=entry.get("timestamp") or datetime.now(timezone.utc),
                 )
                 for entry in metrics
             ]

The entry["timestamp"] escape hatch is unchanged: an explicitly supplied timestamp is still honoured, and the clock is read only for entries that omit it.

Scope

Risk

  • Risk level: low
  • Failure mode: one additional datetime.now() per metric. The background monitor records 7 per 30-second cycle, so this is not measurable against the SQLite commit that record_metrics exists to collapse. No schema, query, or API surface changes.
  • Rollback: revert this single commit. Timestamps are consumed only by range queries (WHERE timestamp < ? for retention pruning, WHERE timestamp >= ? for windowed reads); rows written under either implementation remain readable by both.

Verification

Current head f531eeb.

  • Focused teststests/unit/test_performance_monitor.py: 131 passed (128 before, +3).
  • Fail-test — with the source change reverted to shared-now, test_batch_stamps_each_record_with_its_own_now and test_batch_persists_the_per_record_timestamps both FAIL; both pass with the change. The third (test_batch_honours_an_explicitly_supplied_timestamp) passes either way by design — it guards the explicit-timestamp path, which was never broken.
  • Full unit suitetests/unit: 8105 passed, 5 xpassed, 89 subtests, 0 failed (175s).
  • Router teststests/unit/test_v1_router_extended.py included above; record_metrics is the call the report endpoint makes.
  • Lintruff check clean on both changed files.
  • Required CI — pending on this head.
  • Review threads resolved — no threads yet.

On how the tests assert

Asserting that timestamps merely differ would be flaky: wall-clock resolution is coarse enough that several now() calls in a tight loop can legitimately return the same value. The tests instead patch the module clock to walk a known sequence, so the Nth record must carry the Nth instant — true only if the clock is consulted once per record, in order. Running out of instants raises IndexError, which record_metrics swallows, leaving the batch unwritten and failing the buffer assertion; an extra clock read cannot pass silently.

Production evidence

Not applicable — no runtime surface, HTTP contract, or deployed artifact changes. The edit is one expression inside an internal service method; there is no user-visible behaviour to observe in a preview deployment. Evidence is the fail-test above, which demonstrates the tests detect the exact regression being fixed.

Relationship to #1356

#1356 carries this same fix and nothing else that is not already on main. Its other three files landed via #1341, so GitHub's "+428" is measured against a pre-#1341 merge base. #1356 is additionally conflicted (mergeable_state: dirty, two hunks, both these timestamp lines) and blocked on a red agent-completion/truth-gate (invalid_payload — its body is still the unfilled template).

This PR takes the fix cleanly off current main and adds the test coverage #1356 lacks — its serial-vs-batched parity test asserts buffer and deque contents but never timestamps, so the fix it carries is itself unguarded. #1356 can be closed as superseded once this lands.

Agent handoff


Generated by Claude Code

`record_metrics` docstring promises "The observable behaviour is identical
to calling `record_metric` once per entry". It was not: the batch hoisted a
single `now = datetime.now(timezone.utc)` out of the comprehension and gave
every record in the batch that same timestamp, while the serial
`record_metric` stamps each metric at the moment it is recorded.

Nothing tested the timestamp in either direction, so the divergence was free
to persist. It was raised on #1341 (Copilot) and again on #1356, and has now
survived two reviews unfixed.

Consult the clock per record so the batch is a true drop-in. The
`entry["timestamp"]` escape hatch is unchanged: an explicitly supplied
timestamp is still honoured and the clock is only read for entries that
omit it. Cost is one extra clock read per metric — the background monitor
records 7 per 30s cycle, so it is not measurable against the SQLite commit
the batch exists to collapse.

Three tests pin it. Rather than assert timestamps merely differ — wall-clock
resolution is coarse enough that several `now()` calls in a tight loop can
legitimately return the same value — they patch the module clock to walk a
known sequence, so the Nth record must carry the Nth instant. That holds
only if the clock is consulted once per record, in order. Two of the three
fail against the shared-`now` code and pass with this change; the third
guards the explicit-timestamp path, which was never broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019baCDT5aP5Z66pLGBCE2Y6
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
v0-uvai Ready Ready Preview, v0 Aug 7, 2026 5:56pm

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one) (1)
  • [‘architecture-gap’, ‘bug’, ‘ci-cd’, ‘ci/cd’, ‘copilot-rabbit’, ‘documentation’, ‘duplicate’, ‘enhancement’, ‘frontend’, ‘github_actions’, ‘good first issue’, ‘help wanted’, ‘high-priority’, ‘invalid’, ‘javascript’, ‘ml-model’, ‘needs-triage’, ‘pipeline-critical’, ‘placeholder-code’, ‘priority:high’, ‘python’, ‘python:uv’, ‘question’, ‘styling’, ‘tests’, ‘v0’]

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited), Organization UI (inherited)

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 85fcbdc0-7536-4bd2-a4d0-2dc9e4cd5070

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the python label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA fff458c.
Ensure that dependencies are being submitted on PR branches and consider enabling retry-on-snapshot-warnings. See the documentation for more information and troubleshooting advice.

Scanned Files

None

Copy link
Copy Markdown
Owner Author

@coderabbitai review

Auto-review skipped on the label gate again. The required-label list is satisfied by this PR's content (python, tests), but the auto-labeler runs after the PR opens, so CodeRabbit's gate evaluates an unlabelled PR and skips every time. This is the documented retry.


Generated by Claude Code

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Agent Completion Truth Gate: BLOCKED

Reasons: invalid_payload

Machine-readable verdict
{
  "details": {
    "collection_errors": [
      "incomplete_linked_issue_contract",
      "linked_issue_not_agent_task",
      "missing_intent_snapshot",
      "missing_agent_run_id",
      "missing_agent_login"
    ],
    "invalid_fields": [
      "policy.agent_login",
      "policy.run_id"
    ]
  },
  "reasons": [
    "invalid_payload"
  ],
  "verdict": "blocked"
}

Workflow evidence

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

@groupthinking I will review the current changes in #1400. The explicit retry bypasses the initial label-gate timing issue.

⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Owner Author

CI status on f531eeb — one red check, and it is a false positive

agent-completion/truth-gate/pr-1400 is red (invalid_payload) and cannot go green on this PR. Not fixing it here; diagnosis and the one-line fix are filed as #1401.

Why it is red. agentTaskApplicable() arms the gate when the branch matches /^(?:agent|claude|codex|copilot|jules)[/-]/ and any issue is linked:

(issueDispatch || (pullProvenance && Boolean(selectedIssue)))

This branch is claude/… and it closes #1399, so the gate arms — then demands policy.agent_login and policy.run_id, which are only populated from the intent snapshot snapshot-agent-task-intent writes for dispatched agent tasks. #1399 is an ordinary issue, so no snapshot exists and the verdict is permanently invalid_payload. The collector says as much itself: linked_issue_not_agent_task, missing_intent_snapshot.

This is #1377's defect one level down — that fixed arming with no linked issue; this is arming on a linked issue that declares no contract. The fix is Boolean(selectedIssue)declaresAgentContract(selectedIssue).

Why it is not fixed in this PR. Two reasons, both dispositive:

  1. It is a CI-governance change with no relation to metric timestamps, and this PR is deliberately surgical (2 files, one expression + tests).
  2. It would not work anyway — pull_request_target runs the workflow from the base branch, so a fix on this branch cannot affect this PR's own gate run. fix(ci): unblock the merge gate, consolidate its competing fixes, and audit 337 branches #1377 hit exactly this and merged with the same status red.

Not blocking. mergeable_state is unstable, not blocked — the gate is advisory, not a required check. #1377 merged with it red for the same reason.

Everything else on f531eeb

Check Result
lint-python, guards, validate, gitleaks (working tree), dependency-review ✅ pass
Canonical issue and evidence, Agent completion enforcement ✅ pass
Vercel preview ✅ Ready
agent-completion/truth-gate ❌ false positive → #1401

Local: 8105 passed, 5 xpassed, 0 failed on tests/unit; ruff clean.

CodeRabbit has not reviewed this PR. The label gate skipped it (labels are applied by the auto-labeler after open, so the gate sees an unlabelled PR); the explicit @coderabbitai review retry was accepted but then hit the account's review rate limit — next slot in ~51 minutes. Worth noting this skip-on-open pattern is hitting every PR in this repo, #1381 included.


Generated by Claude Code

@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@groupthinking The reported agent-completion/truth-gate failure is outside this PR’s timestamp scope.

The reported pull_request_target behavior means a fix on this branch cannot change this PR’s gate result. Tracking the gate defect in #1401 is the correct separation.

The reported passing checks and focused test results support keeping this PR limited to the timestamp fix. CodeRabbit review remains pending until the review rate limit permits a retry.

🐇


✏️ Learnings added
Learnt from: groupthinking
URL: https://github.com/groupthinking/EventRelay/pull/1400

Timestamp: 2026-08-05T09:47:41.848Z
Learning: In groupthinking/EventRelay, the auto-labeler applies PR labels after the PR opens. CodeRabbit’s required-label gate can therefore skip the initial automatic review because it evaluates the PR before labels such as `python` and `tests` exist. A later explicit review command is required after labels are applied.

Learnt from: groupthinking
URL: https://github.com/groupthinking/EventRelay/pull/1400

Timestamp: 2026-08-05T09:47:41.848Z
Learning: In groupthinking/EventRelay, `agent-completion/truth-gate` can return `invalid_payload` for a `claude/`, `agent/`, `codex/`, `copilot/`, or `jules/` branch that links an ordinary issue without an agent-task intent snapshot. The CI defect is tracked in GitHub issue `#1401`; it is separate from PR `#1400`’s metric timestamp change.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

You are interacting with an AI system.

@groupthinking
groupthinking marked this pull request as ready for review August 7, 2026 17:54
@groupthinking
groupthinking merged commit 9203570 into main Aug 7, 2026
16 of 18 checks passed
@groupthinking
groupthinking deleted the claude/clever-heisenberg-8k227t branch August 7, 2026 17:55
@linear-code

linear-code Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

GRV-341

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf: batched record_metrics gives every metric an identical timestamp, breaking its documented parity with record_metric

2 participants