feat(workspace): tell the model about extension tools a live IDE bridge serves - #1291
feat(workspace): tell the model about extension tools a live IDE bridge serves#1291ralphstodomingo wants to merge 1 commit into
Conversation
…ge serves The `## Workspace integrations` section projected only the three warehouse capabilities per connection type, so extension-type tools the engine serves through a live VS Code bridge (the dbt project tools, sql-tools) never reached the model even when they sat in its catalog. The presentation surfaces learned about them in #1236; this is the model-facing half. - `declared()` groups the extension keys under their catalog integration (`Declared.extensions`, optional so the flat lists stay the contract), and the attach outcome carries the groups so precedence never makes a second lookup. - `derive` names an extension tool only when two signals agree: its key materialised in the live catalog AND a bridge for this project is live now. Either missing is silence, not a warning. The tools ride on the `nothing-materialised` snapshot too, since a workspace can serve extension tools and no warehouse capability at all. - `servedExtensions` applies the same reachability filter as the warehouse inventory, at projection time, so an `analyst` is never told about tools it may not call. - The section appends the groups under a fixed intro naming the condition they depend on; a workspace with extension tools and nothing routed gets an extension-only shape that keeps the local-tools sentence. Extension lines are dropped before any warehouse type when the cap is hit. Every snapshot without extension tools renders byte-for-byte what it did before, including all silent disabled states. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GHBUvb843k1R7UAGi8Ya9b
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Thanks for updating your PR! It now meets our contributing guidelines. 👍 |
E2E — live VS Code bridge, real engine, real model turn (2026-09-10)Rig: demo-project bound (server-side) to workspace "Test Workspace" (id 15) on the ralphtest tenant; VS Code (WSL) open on the project with the Altimate extension + Power User for dbt, which writes the bridge sidecar; engine 0.7.1 spawned by this branch's overlay with 1. Token-free probe — the real attach, catalog and render, no modelA scratch script runs the CLI's own
Rendered section (verbatim from the probe): 2. Real model turn — the section reaches the model
The model returned the section byte-for-byte identical to the probe's render above (same 14 extension tools, same intro sentence), then 3. Negative control — same rig, bridge sidecar moved aside for one runWith the sidecar file temporarily renamed (VS Code left running; file restored afterwards, verified), a fresh probe run under a new session id gave:
Not exercised live (covered by unit tests only): the "catalog has the keys but the bridge died mid-session" state, which needs a window closed between engine spawn and the turn — R1 describes the one-step staleness bound for it. |
Issue for this PR
Closes #1263
Type of change
What does this PR do?
The
## Workspace integrationssystem-prompt section projected only the three warehouse capabilities per connection type, so extension-type tools the engine serves through a live VS Code bridge (the dbt project tools, sql-tools) never reached the model — even while they sat in its catalog. #1236 taught the presentation surfaces about them; this is the model-facing half.declared()now also groups the extension keys under their catalog integration (Declared.extensions, optional — the flatkeys/extensionKeyslists remain the contract every existing reader was written against), and the attach outcome carries the groups so precedence never makes a second lookup.derivenames an extension tool only when two signals agree: its key materialised in the live catalog and a bridge for this project is live at this turn (liveBridge, the same read-only mirror fix: list and count extension-type integrations when a live IDE bridge serves them #1236 uses). Either missing is silence, not a warning — a dormant bridge renders exactly what a workspace with no extension tools renders.nothing-materialisedsnapshot too, because a workspace can serve extension tools and no warehouse capability at all; that state now speaks when (and only when) it carries them.servedExtensionsapplies the same reachability filter asservedInventory, at projection time, so ananalystis never told about tools it may not call.datamate_*keys listed. When the cap is hit, extension lines drop before any warehouse type.Claims
EMPTY's shape is untouched unless extension tools are present.liveBridge(projectDirectory)is true for this turn. A dormant bridge, a catalog key no declared group names, or an outcome without groups (an older engine) all render the no-extension section.inertWorkspaceNamebefore reaching the prompt.analystshape renders nothing.nothing-materialisedsnapshot carries the groups (and the bound id) and renders an extension-only section that keeps the local-tools sentence; the same snapshot without a live bridge is silent, as it always was.MAX_SECTION_CHARS.Residuals
liveBridge), not the engine's own connection state. A window closed after this step's snapshot refresh can leave the block one step stale — the same bound the per-step snapshot already has for everything else in the section.liveBridgepath matching usespath.relativeprefix semantics rather than the engine's platform-aware normalisation (inherited from fix: list and count extension-type integrations when a live IDE bridge serves them #1236's R1); a Windows drive-letter case mismatch can under-report a live bridge, never over-report.deriverises 17 → 20 cognitive (complexity appendix below). The three added breaks are the extension gate, the outcome-shape spread, and the bound-id spread on thenothing-materialisedreturn; extracting them would move the branches into a helper, not remove them, so the shape is recorded rather than reshuffled.How did you verify your code works?
bun testover the eight workspace suites (awareness,precedence,engine-overlay,engine-probes,engine-types,datamate-list-integrations,plugin/workspace,engine-install-offer): 312 tests, all green;tsgo --noEmitclean.nothing-materialisedand silent again without the bridge;analystrenders nothing; hostile integration name inert; cap drops extension lines first; the attach outcome carries groups only when the allowlist names any;declared()grouping (name from catalog, id fallback, empty groups dropped) and the exact flat-only shape when no extension integration exists.Screenshots / recordings
Not a UI change. The rendered system-prompt section is asserted verbatim in
awareness.test.ts, and the E2E comment on this PR shows the live render from a real VS Code bridge (probe), the model quoting it back verbatim (one real turn), and the negative control with the bridge removed.Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_01GHBUvb843k1R7UAGi8Ya9b
Appendix — complexity delta (awareness: extension tools via live bridge)
89ddf85656→686b2e559c· only functions this diff touches · advisory, not a gate.deriveL555packages/opencode/src/altimate/workspace/precedence.tssystemSectionL146packages/opencode/src/altimate/workspace/awareness.tsdeclaredL124packages/opencode/src/altimate/workspace/engine-probes.tsextensionsServedL287packages/opencode/src/altimate/workspace/precedence.tsSummary: 13 touched · 3 rose · 1 improved · 6 new (max cognitive 10) · net cognitive Δ +26
ℹ️ How to read these numbers
Cognitive (Sonar spec) counts breaks in linear reading flow — each
if/loop/catch/ternary/boolean-operator switch adds 1, and nesting makes every further break cost more. It approximates how much you must hold in your head to follow the function: 0–5 trivial · 6–10 easy · 11–15 moderate (15 = Sonar's recommended per-function cap) · 16–25 hard to follow · >25 needs decomposition.CCN (cyclomatic) counts independent paths — also the minimum number of test cases for full branch coverage of the function.
Only functions this diff touches are measured, as deltas — pre-existing complexity is not counted against this change. Rising numbers aren't automatically wrong; they're where review attention should go. Test files excluded.