feat(reports): add Cursor Canvas report route - #49
Merged
Conversation
Render the complete Better Harness report inside Cursor with the public `cursor/canvas` SDK, and surface any workspace-scoped native Context Usage snapshot as the Context Window section of that report. Cursor session scans now project bounded token, category, and item metadata from Cursor's own `context-usage-*.canvas.data.json` files while omitting raw item text, and a missing snapshot stays unobserved instead of being reported as zero usage. `harness render --mode cursor-canvas` writes a deterministic `findings.json`, `canvas.json`, and `report.canvas.tsx` bundle, then validates the split data contract, module boundaries, required report sections, public action bindings, and the final TSX transform. This also moves Cursor's default output route from portable HTML to the host-native Canvas, so the adapter matrix, report routing, skill routing, and bilingual entrypoints are updated to match. The intent, acceptance scenarios, and risks are recorded in docs/specs/2026-08-01-cursor-canvas-report.md. Validated with npm test (1051 passing), npm run pack:verify, and a real Cursor scan followed by a validated cursor-canvas render against an observed native snapshot. Co-authored-by: QoderAI <qoder_ai@qoder.com>
`contextItemLabel` fell back to the native `source.label` even when `safeContextSource` had rejected that source for living outside the workspace, so a file the boundary just excluded could still name a Context Window row. Only an admitted source may label an item now. Report the `cursor-context-usage` source from a materialized snapshot file instead of its parent `canvases` directory, which can exist while holding no evidence. A workspace without a snapshot now reports `exists: false` and raises the normal `missing-optional-root` warning rather than claiming a present source next to an unobserved projection. Drop the unresolvable `parentId` projection and its schema slot, since native parent ids name synthetic group nodes that are absent from `items` and no reader surface consumed the field. Give each Canvas mode its own analyzer companion filename so the Cursor route no longer borrows the Qoder constant. Add `test/cursor-canvas-report.test.mjs` covering the bounded projection, the workspace source boundary, the unobserved and fail-closed paths, the snapshot presence contract, the Context Usage schema bounds, the renderer, the validator, and a `--mode cursor-canvas --validate` run. The label leak surfaced only while writing that coverage. The per-route artifact inventory stays asserted in `templates/reporting/routing.md`, which owns it, because the root Skill sits against a hard 12,000-byte prompt budget. Validated with npm test (1,061 passing), npm run pack:verify, and real Cursor scans of both an observed native snapshot and an unobserved workspace. Co-authored-by: QoderAI <qoder_ai@qoder.com>
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.
Summary
Cursor gains a host-native Canvas report rendered with the public
cursor/canvasSDK. Cursor session scans now project any workspace-scoped native
context-usage-*.canvas.data.jsonsnapshot into a Context Window section of thecomplete report, with raw item text always omitted and a missing snapshot kept
explicitly unobserved rather than reported as zero usage.
Why
linked to an issue.
experience Qoder users already have, plus visibility into what is consuming
their context window — bounded to evidence Cursor itself materialized.
Traceability and Scope
docs/specs/2026-08-01-cursor-canvas-report.md;docs/adrs/directory-structure.mdrecords the newtemplates/reporting/cursor-canvas.mdowner.scripts/session-analysis/platforms/cursor.mjs(Context Usage discovery/projection),
scripts/harness-analysis/(renderer,validator, schema, render routing),
templates/canvas/better-harness-cursor.canvas.tsx,templates/reporting/cursor-canvas.md+routing.md,skills/better-harness/SKILL.md.implementation or template; decoding
store.db; making Canvas generation a sideeffect of every JSON command; refactoring the existing Qoder Canvas template.
Change Type
The bug-fix commit resolves a privacy-boundary leak found while adding the test
coverage for the feature commit:
contextItemLabelfell back to the nativesource.labeleven aftersafeContextSourcehad rejected that source for livingoutside the workspace, so an excluded file could still name a Context Window row.
Test and Review Evidence
npm testmain; +10 new)npm run pack:verifynode --test test/doc-link-graph.test.mjsnode --test test/cursor-canvas-report.test.mjsharness render --mode cursor-canvas --validate --jsonstatus: passacross all 5 Cursor checks; artifacts exactlyfindings.json,canvas.json,report.canvas.tsx; sole importcursor/canvas; TSX transform succeededManual or visual evidence:
dispatched action types (
newComposerChat,openFile,openAgent) were checkedagainst the shipped Cursor SDK type declarations in
Cursor.app/.../agent-sdk/cursor/canvas/*.d.ts, including prop-level names suchas
UsageBar.total/segments/topLeftLabel,Table.emptyMessage, andText.truncate.56,860 / 300,000tokens, 19% full, 7 categories, 119 bounded items; verified noraw
textfield, no absolute-path labels, and noparentIdin the projection.status: unobservedwithexists: falseand the normalmissing-optional-rootwarning.Fluency chart, AI Agent Practice, Context Window, findings, and evidence sections.
Risk and Recovery
from HTML to Canvas, so Cursor runs no longer emit
report.md/report.htmlbydefault. Path handling uses
path.isAbsolute/relative/sepand the existingWindows file-identity helpers, but the runtime verification above was performed on
macOS only; Windows and Linux rest on CI.
scripts/harness-analysis/andtemplates/, both covered bypack:verify.summary.contextUsageis a new additive, optional field in the report contract;existing Qoder, Markdown, and HTML routes are untouched. JSON analysis stays
write-free unless an explicit output option is passed.
removing
cursor-canvasfrom the mode set restores Cursor's HTML route.item.sourceis only retained when the referenced file is inside theworkspace. On the one real snapshot available, 0 of 119 items qualified (its
files belonged to a sibling repository), so the Context Window
openFileactionis proven by unit fixtures rather than by live data.
change; the reader fails closed on malformed snapshots and avoids private
command ids and internal imports.
AI Involvement
evidence-based review pass and directed the follow-up fixes; the automated checks
and real-workspace scans in the table above were executed and their output
inspected. The scope question is deliberately left to human decision and is not
resolved here.
Checklist
AGENTS.md,CONTRIBUTING.md, and the relevant canonical-owner guidance.CHANGELOG.md.CHANGELOG.mdis intentionally left untouched:AGENTS.mdforbids proactivelyediting release metadata. The Cursor default-route switch does warrant a changelog
entry — please confirm the scope decision first, then say the word and I will add it.