Debugging instrumentation (@dxos/log pipeline)
SkillWeb & browsingUse when instrumenting code with runtime logs to test a hypothesis — @dxos/log debug lines captured to app.log (browser), test.log (node tests), or test-browser.log (browser tests/storybook), and querying them with query-logs.mjs. Reference for the log-exfiltration pipeline and instrumentation mechanics, not a debugging workflow.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Debugging instrumentation (@dxos/log pipeline) skill
What this skill tells your AI
The instructions your AI receives, as published by dxos/dxos in .agents/skills/debugging/SKILL.md and read by ahel’s review.
Mechanics for hypothesis-testing with runtime logs. The debugging process —
hypotheses, isolation, verification, user interaction — is owned by the calling
skill (debugging-ui for UI bugs) or workflow; this skill is only how to get
signals out of running code cleanly.
The pipeline — how log exfiltration works here
This repo already ships the full pipeline; do not reinvent it.
@dxos/vite-plugin-logis wired intocomposer-app/vite.config.tsand intercepts every browser-side@dxos/logcall via aLogProcessor.- Entries are serialized as NDJSON and POSTed to the plugin's dev-server sink (
/@dxos-plugin-log/sink, not the HMR WebSocket), which appends them topackages/apps/composer-app/app.log. The file is truncated when the dev server starts. - Third-party plugin code hosted inside Composer imports
@dxos/logfrom the host, so its logs land in the sameapp.log. - Query the log with
node scripts/query-logs.mjs packages/apps/composer-app/app.log -q <filter> -g <regex>. See theloggingskill for the full filter syntax (levels,path:level,!exclude,-qOR /-gAND). - Node-side code (tests, CLI, server):
@dxos/logworks identically; setLOG_FILTER=debugfor stdout capture in vitest runs. Node vitest also writes an NDJSON file sink at<package>/test.log(path is printed at run start). - Browser tests (vitest browser mode,
*.browser.test.ts, storybook) have no filesystem, so@dxos/logentries are POSTed to theDxosLogPlugindev-server sink and appended to<package>/test-browser.log(NDJSON, same shape asapp.log/test.log). Both the page realm and worker realms are covered. Filter defaults todebug; override withDX_TEST_LOG_FILTER(orLOG_FILTER). Query it the same way:node scripts/query-logs.mjs <package>/test-browser.log -q debug -g '\[DEBUG H'. This is the primary window into worker-side behavior for worker-framework browser tests. - Composer runs client services in a dedicated worker per tab (a coordinator handles cross-tab exclusivity; there is no long-lived SharedWorker hosting services —
DX_SHARED_WORKERis an opt-in exception). A plain page reload therefore picks up newly instrumented worker-side code; do NOT ask the user to close all tabs first. Worker-side logs land in the sameapp.log(the log plugin handles?worker_file/?sharedworker_fileentries).
Instrumentation rules
Use @dxos/log, not console.log or print
// #region DEBUG
import { log } from '@dxos/log';
log('[DEBUG H1] frobbed check', { frobbed, ts: Date.now() });
// #endregion DEBUG
- Static message first (lowercase phrase, hypothesis tag included). No template-literal interpolation in the message string.
- Structured context second — dynamic values go in the object, never only in the message.
- Tag each line with
[DEBUG H<n>](n = hypothesis number) so instrumentation is greppable and distinct from framework logs. - If the file does not already import
@dxos/log, add the import inside the#region DEBUGblock so it removes cleanly. - Never use
console.log,print, stdout, or stderr. All debug output goes through@dxos/log.
Region markers
ALL instrumentation MUST be wrapped in region blocks for clean removal:
// #region DEBUG (JS/TS/Java/C#/Go/Rust/C/C++)
# #region DEBUG (Python/Ruby/Shell/YAML)
<!-- #region DEBUG --> (HTML/Vue/Svelte)
-- #region DEBUG (Lua)
...instrumentation...
// #endregion DEBUG (matching closer)
Be minimal
Log only what confirms or rules out the hypothesis — variable states, execution
paths, timing, decision points. Prune aggressively; app.log is noisy with
existing framework logs.
Capture cycle
-
Rotate the sink before each reproduction —
app.logonly self-truncates on dev-server restart, so clear it between iterations, but move the previous capture aside rather than destroying it (the run you are about to overwrite may hold the only evidence of an intermittent failure):mv packages/apps/composer-app/app.log "$(mktemp packages/apps/composer-app/app.log.XXXXXX)" && touch packages/apps/composer-app/app.logAlways rotate to a unique destination. A fixed name (
app.log.prev) clobbers the previous capture on the second iteration, which is exactly the evidence loss the rotation exists to prevent;mktempallocates the destination atomically, so back-to-back rotations cannot collide the way a timestamp suffix can.touch, never: >. The dev-server sink appends by path (fs.appendFile), reopening the file per write, so it recreatesapp.logon its own after themv; truncating instead would wipe any lines it already wrote in the window between the two commands.Node tests rotate only between runs. The node file processor holds an open fd (
openSync(path, 'a')), so a rotation during a live run follows the inode — output keeps landing in the rotated file while the newtest.logstays empty. Rotate<package>/test.log(node) or<package>/test-browser.log(browser) with the same unique-destination rule, but do it between runs, and re-run the test yourself each iteration.The log is shared with whoever else is attached to that dev server — never delete a sink you did not create, and if a capture predates your session, keep it.
-
Reproduce (yourself via browser tools whenever possible — see
debugging-ui). -
Check size first (
wc -l), then extract only your lines:node scripts/query-logs.mjs packages/apps/composer-app/app.log -q debug -g '\[DEBUG H'Narrow further as needed:
node scripts/query-logs.mjs packages/apps/composer-app/app.log -q debug -g '\[DEBUG H2' node scripts/query-logs.mjs packages/apps/composer-app/app.log -q 'debug,!rpc.ts' -g '\[DEBUG H'Output columns:
timestamp, level letter,file:line, scope, message, context, error. Thef/nNDJSON fields give file:line;ccarries structured context;ocarries scope.
Cleanup
- Never remove instrumentation before the fix is verified in the reporting environment.
- Once verified: remove all
#region DEBUGblocks and their contents (Grep for#region DEBUGacross touched files). Do not deleteapp.logitself — it's the standard dev log.
Related skills
debugging-ui— the UI debugging process (isolation ladder, verification contract, interaction budget) that decides when to instrument.logging— full@dxos/logreference (levels,dbg, NDJSON shape) andquery-logs.mjsfilter syntax.
Workflow inspiration: doraemonkeys/claude-code-debug-mode (generic HTTP-endpoint version). This repo's adaptation uses the existing @dxos/log → app.log pipeline instead of a bespoke endpoint.
Signals
- GitHub stars
- 520
- Forks
- 49
- Last commit
- Sep 2026
- Hacker News mentions
- 20
Advanced
- Catalog kind
- skill
- Gateway key
debugging-dxos- Source
- github.com/dxos/dxos