Skip to content

Code Blue: read the error without resizing, and a read-only skill that investigates it - #745

Merged
AllTerrainDeveloper merged 1 commit into
trunkfrom
feat/code-blue-readable-errors-and-debugging-skill
Sep 3, 2026
Merged

Code Blue: read the error without resizing, and a read-only skill that investigates it#745
AllTerrainDeveloper merged 1 commit into
trunkfrom
feat/code-blue-readable-errors-and-debugging-skill

Conversation

@AllTerrainDeveloper

@AllTerrainDeveloper AllTerrainDeveloper commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Reading an error shouldn't cost a resize

Code Blue truncated every message to one line, so reading a fatal meant dragging the window out to the length of the longest line. And the text couldn't be selected — the row that expands an issue is a <button>, and every engine suppresses text selection inside a form control.

Messages and stack traces now wrap. Copying moved to buttons, which is the honest answer when the click target has to stay the whole row (an error list is scanned; asking for aim at a chevron is asking for misses). The detail panel carries:

  • the full message, wrapping, in a <os-code block copy wrap>
  • the file path, flattened to plain mono text with just the copy affordance
  • the stack trace, wrapping, with copy
  • Copy report — paste-ready Markdown: heading, message, file:line, source, occurrence count, first/last seen, the whole environment row, trace in a fence. The thing that otherwise gets retyped by hand into a GitHub issue.
  • Search the web — filterable via the new openstation_code_blue_search_url

Every log line already said whose code failed

…and the window threw it away. Paths are now classified once, in log-reader.php — plugin / must-use / theme / core — and ride on every entry. It shows as a badge in the row and a Source row in the detail, with the plugin's real name and version resolved server-side.

Deliberately conservative: a path that isn't clearly under the content directory or clearly inside core answers unknown rather than guessing, because a wrong attribution sends someone into the wrong codebase.

The error-investigation skill

includes/ai-copilot/abilities-debugging.php — four read-only WordPress Abilities on wp_abilities_api_init, plus a system-prompt appendix that turns them into a method.

Ability Returns
list_log_issues The log as distinct issues, not raw lines — a fatal that fired 400 times is one entry with count: 400. Carries signature, severity, message, file/line, and origin ({ kind, slug, name, version }). No traces: the list is triage.
get_log_issue One issue by signature, stack trace included.
read_source_excerpt Numbered source lines around a line the log named.
get_site_context WP + PHP versions, debug constants, environment type, active theme, every active plugin with its version. No credentials.

That set is chosen so a model can reach a real conclusion: what's failing → the trace → the actual code at that line → the versions it's running on. Most WordPress fatals are a version story, and that story is nowhere in the log.

It proposes; it never repairs

Structural, not prompted: all four are readonly and no writing counterpart exists, so a model handed the whole set can describe a patch and has no route to apply one. The appendix says so in words too, because a model that doesn't know it can't edit files tends to answer as though it had — and asks for the fix as a diff or a precise "in FILE, line N, replace X with Y", flagging when a change carries risk (a plugin edit an update will overwrite, a theme with no child theme).

Security

docs/agents-security.md read first; these reach agents too, ceilinged at the invoker's capabilities.

read_source_excerpt is bounded to files the current log names — an entry's file, or a path inside a stack trace. A path only becomes readable because something already failed there, and the log is readable to exactly the same people, so the tool can never widen what the caller can see. Without that bound it's a general file-read tool wearing a debugging label, reachable through whatever text the model happens to be reading. Behind it: realpath() before the root prefix test, a source-extension allowlist, and wp-config.php / .env refused outright — a parse error inside wp-config.php does name it, and that file is the database password.

Gate is Code Blue's own (site management + Developer mode, moved by openstation_code_blue_user_can_use): a user who can't open the log window can't read the log through an assistant either. None of the four is mcp.public.

tests/phpunit/tests/aiDebuggingAbilities.php pins every refusal, the read-only property, the absent MCP exposure, and the gate.

<os-code>

  • wrap — long lines fold instead of scrolling sideways
  • copy button sized as a real control — 24px square with its own hit area, hover background and tooltip, 8px gap; on block the snippet reserves its corner so it can never land on the first line. Dimmed-but-visible rather than hover-only: a copy affordance nobody can see is one nobody uses.
  • --os-ui-code-block-max-block-size — the code element scrolls, not the host. A scrolling host carried the copy button away with the content.

Also here, and separable

Unrelated to the above — happy to split it out if you'd rather.

openstation-beta registered its settings-tab script at admin_enqueue_scripts priority 10, the same priority the shell harvests its payload at, so plugin load order decided whether the handle existed yet. It also skipped registration entirely inside chromeless iframes — which is where the payload is harvested after a plugin activation. Result: the _doing_it_wrong notice, and a Beta tab that disappeared until the next reload. Priority 5 now, and registration no longer skipped.

The notice text was misleading too ("no wp_register_script call found" — there was one, just later), and the five PHP docblock examples plus seven in hooks-reference.md taught the same trap. All now show priority 5.

Testing

npm run build, typecheck, lint (0 errors), lint:php (PHPCS clean), 5577 JS tests, 2809 PHPUnit tests — green on both single-site and multisite.

Not yet exercised by hand on a live install; worth a pass on a real debug.log for the badge rendering and the Copilot turn.

Docs

  • hooks-reference.md — the skill's abilities table, its gating and refusal list, openstation_code_blue_search_url, priority-5 in every script-handle example
  • components-reference.md<os-code>'s wrap

🤖 Generated with Claude Code

Open WordPress Playground Preview

…t investigates it

Reading an error meant dragging the window out to the length of the
longest line, and the one thing anyone wants out of an error log — the
text — could not be selected, because the row that expands an issue is
a button and every engine suppresses selection inside one.

Messages and stack traces now wrap. Copying moved to buttons, which is
the honest answer when the click target has to stay the whole row: the
detail panel carries the file path, the trace, and a paste-ready
Markdown report (message, source, counts, first/last seen, the whole
environment) behind copy buttons, plus a filterable "Search the web"
link.

Every log line already said whose code failed, and the window threw it
away. Paths are now classified once in log-reader.php — plugin,
must-use, theme, core — and rendered as a badge in the row and a Source
row in the detail. Deliberately conservative: anything unclear answers
`unknown` rather than sending someone into the wrong codebase.

The error-investigation skill (includes/ai-copilot/abilities-debugging.php)
is four read-only WordPress Abilities plus a system-prompt appendix:
list_log_issues (the log as distinct issues, not raw lines),
get_log_issue (one issue with its trace), read_source_excerpt (the code
at the line that failed) and get_site_context (versions, debug flags,
active plugins). Enough to reach a real conclusion — most WordPress
fatals are a version story, and that story is nowhere in the log.

It proposes; it never repairs. That is structural rather than prompted:
all four are readonly and no writing counterpart exists, so a model
handed the set can describe a patch and has no route to apply one.

read_source_excerpt is bounded to files the CURRENT log names. A path
only becomes readable because something already failed there, and the
log is readable to exactly the same people, so the tool can never widen
what the caller can see. Behind that: realpath() before the root prefix
test, a source-extension allowlist, and wp-config.php / .env refused
outright — a parse error inside wp-config.php does name it, and that
file is the database password.

os-code gains `wrap`, a copy button sized as a real control with the
snippet reserving its corner, and --os-ui-code-block-max-block-size so
the code element scrolls rather than the host (a scrolling host carried
the copy button away with the content).

Also unrelated, and separable if you would rather it went on its own:
openstation-beta registered its settings-tab script at
admin_enqueue_scripts priority 10, the same priority the shell harvests
its payload at, so plugin load order decided whether the handle existed
— and it skipped registration entirely inside chromeless iframes, which
is where the payload is harvested after a plugin activation. Hence the
_doing_it_wrong notice, and a Beta tab that vanished until reload.
Priority 5 now, and registration no longer skipped. The notice text was
misleading ("no wp_register_script call found" — there was one, just
later) and the five PHP docblock examples plus seven in hooks-reference
taught the same trap; all now show priority 5.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@AllTerrainDeveloper
AllTerrainDeveloper enabled auto-merge (squash) September 3, 2026 09:47
@AllTerrainDeveloper
AllTerrainDeveloper merged commit 5fc1b54 into trunk Sep 3, 2026
5 checks passed
@AllTerrainDeveloper
AllTerrainDeveloper deleted the feat/code-blue-readable-errors-and-debugging-skill branch September 3, 2026 09:49
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.

1 participant