Code Blue: read the error without resizing, and a read-only skill that investigates it - #745
Merged
AllTerrainDeveloper merged 1 commit intoSep 3, 2026
Conversation
…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
enabled auto-merge (squash)
September 3, 2026 09:47
AllTerrainDeveloper
deleted the
feat/code-blue-readable-errors-and-debugging-skill
branch
September 3, 2026 09:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
<os-code block copy wrap>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.openstation_code_blue_search_urlEvery 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
unknownrather 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 onwp_abilities_api_init, plus a system-prompt appendix that turns them into a method.list_log_issuescount: 400. Carriessignature, severity, message, file/line, andorigin({ kind, slug, name, version }). No traces: the list is triage.get_log_issueread_source_excerptget_site_contextThat 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
readonlyand 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.mdread first; these reach agents too, ceilinged at the invoker's capabilities.read_source_excerptis bounded to files the current log names — an entry'sfile, 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, andwp-config.php/.envrefused outright — a parse error insidewp-config.phpdoes 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 ismcp.public.tests/phpunit/tests/aiDebuggingAbilities.phppins every refusal, the read-only property, the absent MCP exposure, and the gate.<os-code>wrap— long lines fold instead of scrolling sidewaysblockthe 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— thecodeelement 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-betaregistered its settings-tab script atadmin_enqueue_scriptspriority 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_wrongnotice, 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_scriptcall found" — there was one, just later), and the five PHP docblock examples plus seven inhooks-reference.mdtaught 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.logfor 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 examplecomponents-reference.md—<os-code>'swrap🤖 Generated with Claude Code