Skip to content

D5: fold cursor, windsurf, antigravity onto the engine + data/vendors config - #99

Merged
open-coder-ai merged 2 commits into
mainfrom
w42/d5-singleton-folds
Sep 1, 2026
Merged

D5: fold cursor, windsurf, antigravity onto the engine + data/vendors config#99
open-coder-ai merged 2 commits into
mainfrom
w42/d5-singleton-folds

Conversation

@open-coder-ai

Copy link
Copy Markdown
Owner

What this changes

The three singleton dialect modules (cursor.py, windsurf.py, antigravity.py) are deleted; each vendor is now a small family module — shape-inferred claims/event naming stays code per design §3.3, plus cursor's G4 and windsurf's G5 renderers — bound to the shared accessor and its data/vendors/<agent>.json entry, which now carries the dialect's words and note strings verbatim (docs/design/dialect-families.md D5). Wire behaviour is unchanged: the D1 golden fixtures pass byte-for-byte, never regenerated.

Two stages, mirroring D3/D4:

  1. Stage 1 — schema + engine devices + recount carry the singleton dialect data. All new engine devices are opt-in config keys absent from every F1/F2 entry: a generic dotted-path field accessor (toolCall.args.X, workspacePaths[0], top-level edits[].new_string), stringify, empty_object_events, per-gate words_at overrides, flag_note/flag_note_default templates, hook_entry.also_wires, the cursor/flat_entries hook wrappers with group nesting, and a G1 block reroute for an escalate degraded from a transform where the entry names that degradation. windsurf's pre_mcp_tool_use wiring moves out of entry_extra (which _hook_dict would copy into entries verbatim — W41's landmine) into first-class also_wires; its second blocking pre_tool gate joins _EXTRA_GATE_NAMES, recounted by executing the dispatcher against the frozen pre_tool payload. _hook_json.py splits by activity (_payload.py: claims/parse) to stay in the 300-line budget. The three entries are regenerated by tools/recount_vendor_config.py --write, never by hand; the other nine entries are byte-identical under the same run.
  2. Stage 2 — the fold: _cursor.py / _windsurf.py / _antigravity.py family modules, _family dispatch by family, module deletions, and bundle() composing engine + family module + inlined VENDOR literal via one binding template for all eleven config-driven vendors. vscode_copilot stays a dialect module (W38's recorded design feedback; folding it is an owner decision).

The config/code line (§3.1) held with no new named probe: both known hard spots folded inside it — windsurf's two-key pre_tool fan-out is wiring data (also_wires), antigravity's inline event mapping is a wire_events inverse fallback for an entry with no events map, and the sizing clause was not needed.

Test intent survives everywhere; two assertions re-homed with their intent intact:

  • test_invocation_events_are_unmapped... asserted on the deleted module's _infer_event; it now asserts the same fact through public parse()UNKNOWN.
  • the git-hook test edited adapters/windsurf.py; it now edits the vendor entry, where that dialect string lives.

Measured bundle line counts (base 38023f4 → this PR; recorded in design doc §7): cursor 524 → 883, windsurf 424 → 814, antigravity 442 → 796 (+69% to +92% — the engine-bundle composition trade D3/D4 already made; D6 owns trimming). Source tree: 472 deleted adapter lines replaced by 219 family-module lines, with every word-shaped fact in config.

Claim check

  • No capability claim is widened without a mechanism behind it — MATRIX untouched; windsurf's pre_mcp_tool_use gate maps to pre_tool, already matrix block-capable, and is derived by executing the dispatcher, not asserted
  • Any new/changed MATRIX row carries a verified record — no matrix rows changed
  • Payload shapes come from a primary source — no new payload shapes; every wire fact is replayed from the D1 golden fixtures (tests/fixtures/golden/*.json), which were captured from the pre-fold adapters and are unchanged in this PR

Checks

  • pytest -q passes (1348 passed, 4 skipped; golden fixtures byte-for-byte, 12-agent bundler subprocess replay green)
  • ruff check . and ruff format --check . pass
  • Runtime path is still stdlib-only (tests/check_stdlib_only.py OK)
  • Commits are signed off (git commit -s)

Also run locally: python examples/generate.py --check (up to date), cd docs/assets && python gen_brand_assets.py --check (current), tools/validate_vendor_config.py (all entries ok).

Notes for the reviewer

  • Mutation checks on the new devices (worker-protocol discipline, run locally, not committed): flipping antigravity's words_at.Stop.allow to "halt", windsurf's escalate note, and cursor's honours_transform each fails the golden-fixture replay; a benign key-reorder of a whole entry still passes. These mutations fail correctly — necessary, not sufficient.
  • Two accepted parse-level nuances, invisible on the wire and to every test: the engine's chain accessor returns "" where antigravity's old raw.get("error") or None returned None for a present-but-empty error; and cursor's content chain falls through past an empty edits join where the old _content_of stopped. Flagging in case either matters to a reviewer more than it did to the suite.
  • _g1's escalate reroute is data-triggered (escalate_from_transform present + gate honours escalate); no existing F1/F2 entry has that combination, which the byte-identical fixtures confirm.

Generated by Claude Code

The three singleton entries (cursor, windsurf, antigravity) gain their
renderer data via tools/recount: verdict words, degrade notes verbatim from
the adapters they will replace, flag-note templates, reason defaults. The
engine gains the opt-in devices those dialects need: a generic dotted-path
field accessor (nested keys, [0] index, top-level [] joins), a stringify
fields device, empty_object_events, per-gate words_at overrides, a block
reroute for an escalate degraded from a transform where the entry names that
degradation, pre-resolved wire names on hj_parse/hj_respond for the
shape-inferred families, and a wire_events fallback for an entry with no
events map. hook_entry_config learns the cursor wrapper (version +
failClosed from answer_events), flat_entries, group nesting, and also_wires
-- windsurf's extra pre_mcp_tool_use wiring moves out of entry_extra, which
_hook_dict would otherwise copy into entries verbatim. windsurf's second
blocking pre_tool wire name joins _EXTRA_GATE_NAMES, recounted by executing
the dispatcher against the frozen pre_tool payload. _hook_json.py is split
by activity to stay in the review budget: _payload.py now holds the
claims/parse side. All devices are opt-in config keys absent from every
F1/F2 entry; full suite green with golden fixtures byte-identical.

Signed-off-by: Claude <noreply@anthropic.com>
The three singleton modules are deleted. Each becomes a small family
module -- shape-inferred claims and event naming stay code per design §3.3,
plus cursor's G4 permission-object renderer and windsurf's G5 exit-code
renderer -- bound by _family to the shared accessor and its data/vendors
entry; antigravity's verdicts are the engine's G1 over its word tables, its
own module just the three-line shape functions. bundle() composes engine +
family module + inlined VENDOR literal for them, one binding template for
all eleven config-driven vendors. Golden fixtures byte-for-byte unchanged;
the 12-agent bundler subprocess replay is green. The antigravity
_infer_event assertion re-homes onto parse()'s public UNKNOWN, and the git
hook test edits the vendor entry instead of the deleted module, since the
dialect now lives there. tools/recount drops the generic AST chain walker
and the singleton field overrides -- every config-driven entry echoes its
own chains, and vscode_copilot keeps its cited override as the one dialect
module left.

Signed-off-by: Claude <noreply@anthropic.com>
@open-coder-ai
open-coder-ai marked this pull request as ready for review September 1, 2026 14:36
@open-coder-ai
open-coder-ai merged commit 6838d69 into main Sep 1, 2026
14 checks passed
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.

2 participants