Skip to content

Add a diagnosis skill for the search-shape channel, and fix its grouping doc - #6143

Open
habdelra wants to merge 3 commits into
mainfrom
claude/clever-galileo-9qa4r7
Open

habdelra wants to merge 3 commits into
mainfrom
claude/clever-galileo-9qa4r7

Conversation

@habdelra

@habdelra habdelra commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

boxel:search-shape emits one line per _federated-search request saying what the search asked for. boxel:client-perf, boxel:screenshot-perf and the indexer's diagnostics columns each have a skill for reading them; this adds the equivalent for boxel:search-shape, because several of the obvious queries against it return plausible wrong numbers.

The grouping correction

The module header offered correlationId as the set to count distinct shapeHash within. That is wrong: newCorrelationId() runs on every _federated-search fetch, so a line that carries an id carries its own, and the count is always 1.

The sharper version, which this PR settles: no member of the line groups one render.

key groups
correlationId one search — and a live request whose client-telemetry instrument is dormant carries none at all, so those lines share the absence of an id
jobId one indexing job (queue job + reservation), held across the job's whole file sweep — every file, both visit types. Not one render.
correlationIdboxel:client-perf server-request a live browser tab session (session_id) and the person driving it (matrix_user_id)

So "searches per page render" is not answerable from this channel as posed; per index pass and per tab session are. The header states that, and the skill carries it as a trap.

No behavior change: the fields are what they always were, and the correction is to the grouping the header documents.

The skill

.claude/skills/search-shape-diagnosis/SKILL.md, matching the *-diagnosis family. It states the operational consequence of a field and points at the module header for the semantics, rather than restating them.

The traps, each of which produces a believable wrong number:

  • results / total / incomplete are null whenever a cache answered, so avg(results) averages over cache misses only — during a load event, the unrepresentative subset. And the cache=~"none|miss|job-miss" filter is necessary but not sufficient: the live cache records its outcome before the populate runs, so a 408 or an escaping 500 reports a miss with no counts too. Add | status=200 when counting lines rather than unwrapping.
  • total has a second null cause: an incomplete merge, where it would sum only the realms that answered.
  • Nothing groups one render (above).
  • linkMode: 'prerender' is not a synonym for indexing — it derives from x-boxel-during-prerender, which the module, file-extract and command-runner routes raise alongside the indexing render route. Indexing is the subset with a non-null jobId.
  • shapeHash groups across realms and page numbers by design; a shared hash means the same query shape, not the same request.
  • The hash is taken pre-cap, so two shapes differing only past the cut hash differently while their rendered filter can be indistinguishable — group by shapeHash, never by the filter string.

The channel's blind spot, which belonged in the skill: a search the admission gate sheds is answered with a 429 before the handler runs and logged on realm:search-admission instead. Under exactly the saturation this channel is written for, it counts admitted searches and not arriving ones, and the missing lines are the excess. Pair any spike with the shed count.

Worked LogQL for what the channel exists to answer: what a spike is made of, which shapes dominate by volume and by latency, how much of a load the caches absorbed, headless load vs live load, searches per index pass by card type, and one request end to end joined to realm:search-timing. Plus tail-logs invocations, and a section on reconstructing a replay from a shape — including that a replay issuing one query repeatedly measures the live-search cache rather than the index.

Scope

Deferred: no dashboard. The three sibling channels each have one under packages/observability/grafanactl/resources/dashboards/boxel-status/; the recipes here are written for Grafana Explore, and the trap list is what any panel set has to respect.

Also out of scope: wiring the prerender requestId onto the line, which would give the per-render key the channel lacks. That is a change to the telemetry, not to its documentation.

Test plan

Prose and one corrected comment block — no behavior. The skill loads and registers (its description appears in the session skill list). eslint and prettier clean via lint-staged on both files.

🤖 Generated with Claude Code

https://claude.ai/code/session_015T2Z2bbFtqizh57qAW7JFW

…ing doc

The channel carries what a search load was made of, but reading it has
traps that produce plausible wrong numbers: the result counts are null
whenever a cache answered, so a naive average is an average over cache
misses only; `total` is additionally null on an incomplete merge; and
`shapeHash` groups across realms and page numbers by design.

The largest trap was in the module doc itself, which offered
`correlationId` as the key to count distinct shapes within. That id is
minted per search — `newCorrelationId()` runs on every `_federated-search`
fetch — so every line has its own and the count is always 1. A render's
searches group by `jobId` for prerender traffic; live browser traffic has
no render key on this channel and reaches a tab session only by joining
`correlationId` to the `server-request` event on `boxel:client-perf`. The
module header says that now.

The skill carries the traps, the join map, and worked LogQL for the
questions this channel exists to answer — what a spike was made of, which
shapes dominate by volume and by latency, how much of a load the caches
absorbed, prerender load vs live load, and reconstructing a replay from a
shape. It defers to the module header for field semantics rather than
restating them, so the two cannot drift.

No dashboard ships with this; the recipes are written for Grafana Explore
and note what the panels should be.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015T2Z2bbFtqizh57qAW7JFW
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 15, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-15T23:31:39.049767Z 951f919 PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 951f91934b

ℹ️ 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/skills/search-shape-diagnosis/SKILL.md Outdated
Comment thread .claude/skills/search-shape-diagnosis/SKILL.md Outdated
`jobId` is the indexing job a search ran under — queue job plus
reservation, held across the job's whole file sweep — so it groups an
entire index pass, every file and both visit types, rather than one
render. Calling it per-visit repeated the error this skill exists to
prevent: a key offered for a grouping it does not perform. No member of
the line groups one render, so "searches per page render" is not
answerable here; per index pass and per tab session are.

`linkMode: 'prerender'` is not a synonym for indexing. It derives from
`x-boxel-during-prerender`, which the module, file-extract and
command-runner routes raise alongside the indexing render route, so the
bucket is all headless traffic. Indexing is the subset carrying a
non-null `jobId`.

A correlation id rides only an instrumented request. Lines from a live
request whose client-telemetry instrument is dormant share the absence of
an id rather than each carrying their own, so a filter on it silently
lumps them together.

The admission gate is the channel's blind spot and belonged in it: an
over-ceiling search is shed with a 429 before the handler runs and logged
on `realm:search-admission` instead. Under the saturation this channel is
written for, it counts admitted searches and not arriving ones, and the
missing lines are the excess.

Also: the cache-outcome filter is necessary but not sufficient for
counting lines, since the live cache records its outcome before the
populate runs and a 408 or 500 reports a miss with no counts; the
truncation trap's heading stated the converse of its own warning; and the
sections that restated the module header now carry the operational
consequence and point at the header for the semantics, since the pair had
already drifted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015T2Z2bbFtqizh57qAW7JFW
@habdelra
habdelra requested a review from a team September 15, 2026 23:46

Copy link
Copy Markdown
Contributor Author

[Claude Code 🤖] Host Tests (3, 16) failed on ac3bf7bd. It is not this branch's, and the reason is structural rather than circumstantial: the branch changes no executable code at all.

The diff is two files. One is .claude/skills/search-shape-diagnosis/SKILL.md, which no bundle includes. The other is packages/runtime-common/search-shape.ts, where every added and removed line is a comment:

$ git diff origin/main...HEAD -- packages/runtime-common/search-shape.ts \
    | grep -E '^[+-]' | grep -vE '^[+-]{3}' | grep -vE '^[+-]\s*//' | grep -vE '^[+-]\s*$'
(no output)

So there is no mechanism by which this branch reaches a host test. Nothing to port and nothing to fix here.

The re-run is not spent: rerun_failed_jobs refuses with 403 This workflow is already running while the other shards are still in flight. It gets spent once the run completes, and this branch stays watched until it is green.


Generated by Claude Code

@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Host Test Results

  1 files  ±    0    1 suites  ±0   10m 35s ⏱️ - 2h 27m 3s
270 tests  - 4 559  270 ✅  - 4 544  0 💤  - 14  0 ❌ ±0 
270 runs   - 4 574  270 ✅  - 4 558  0 💤  - 14  0 ❌  - 1 

Results for commit 43e9ed4. ± Comparison against earlier commit ac3bf7b.

Realm Server Test Results

    1 files  ±0    226 suites  ±0   1h 10m 20s ⏱️ - 9m 53s
3 109 tests ±0  3 109 ✅ ±0  0 💤 ±0  0 ❌ ±0 
3 155 runs  ±0  3 155 ✅ ±0  0 💤 ±0  0 ❌ ±0 

Results for commit 43e9ed4. ± Comparison against earlier commit ac3bf7b.

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

Copy link
Copy Markdown
Contributor Author

[Claude Code 🤖] Base merged. Head is 43e9ed4c, and CI runs against it.

Host Tests (3, 16) failed on ac3bf7bd. Shard 3 is the one that owns packages/host/tests/acceptance/operator-mode-acceptance-test.gts: the duration-weighted pack in packages/host/tests/helpers/shard-modules.ts is deterministic, so replaying selectShardModules over the import.meta.glob set and tests/test-module-timings.json names the shard for any file. Both across this merge:

shard 3:  ./acceptance/operator-mode-acceptance-test.gts
shard 11: ./integration/tools/patch-fields-test.gts

That file's failure mode is the deep link arriving logged out. settled() does not span a matrix sign-in: the Sign In button runs an ember-concurrency task, and ember-concurrency 5 ships no @ember/test-waiters integration, so the task is invisible; the matrix client is built without a fetchFn, so its boot traffic carries neither the fetcher waiter nor realm:session-login, leaving gaps between the held stretches. click() returns into one of those gaps and the modal assertion reads a DOM still rendering <Auth />. The fix is a waiter held in MatrixService across the credential exchange and the whole boot, on the flaky-profile-deep-link-login branch — not something this branch can carry or needs to.

Nothing here reaches a host test. The diff is one skill file plus comments:

$ git diff origin/main...HEAD --stat
 .claude/skills/search-shape-diagnosis/SKILL.md | 169 +++++++++++++++++++++++++
 packages/runtime-common/search-shape.ts        |  18 ++-

$ git diff origin/main...HEAD -- packages/runtime-common/search-shape.ts \
    | grep -E '^[+-]' | grep -vE '^[+-]{3}' | grep -vE '^[+-]\s*//' | grep -vE '^[+-]\s*$'
(no output)

The re-run allowance is unspent.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

[Claude Code 🤖] Host Tests (3, 16) passes on re-run, and all 64 check runs on 43e9ed4c are green. The deep-link-after-login flake is intermittent rather than runner-determined: the same shard, same commit, same runner class, opposite outcome.

One red remains, and it is a re-run artifact rather than a visual change: percy/-cardstack-host reports 54 missing snapshots found.

PERCY_PARALLEL_NONCE is ${{ github.run_id }}-${{ github.run_attempt }}, so each attempt opens its own Percy build. Re-running failed jobs re-executes only the shard that failed — the other fifteen are carried forward with their original timestamps and upload nothing — so the new build receives one shard's snapshots and counts the rest as missing. Shard 3's own start time is the tell: it ran alone, well after the others had finished.

This falls out of any partial re-run of the host workflow, not out of anything in this branch, and no further re-run of failed jobs can clear it: attempt 3 would mint another nonce and repeat the shape. Only a whole-workflow re-run, where all sixteen shards upload under one nonce, produces a complete set. That costs a full suite pass, which is a poor trade for a branch whose diff is one skill file plus comments and which renders nothing.

It is not a required check — the PR is mergeable — so this stands as is unless someone wants the badge cleared.


Generated by Claude Code

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.

2 participants