D5: fold cursor, windsurf, antigravity onto the engine + data/vendors config - #99
Merged
Conversation
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
marked this pull request as ready for review
September 1, 2026 14:36
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.
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 itsdata/vendors/<agent>.jsonentry, which now carries the dialect's words and note strings verbatim (docs/design/dialect-families.mdD5). Wire behaviour is unchanged: the D1 golden fixtures pass byte-for-byte, never regenerated.Two stages, mirroring D3/D4:
toolCall.args.X,workspacePaths[0], top-leveledits[].new_string),stringify,empty_object_events, per-gatewords_atoverrides,flag_note/flag_note_defaulttemplates,hook_entry.also_wires, thecursor/flat_entrieshook wrappers withgroupnesting, and a G1 block reroute for an escalate degraded from a transform where the entry names that degradation. windsurf'spre_mcp_tool_usewiring moves out ofentry_extra(which_hook_dictwould copy into entries verbatim — W41's landmine) into first-classalso_wires; its second blocking pre_tool gate joins_EXTRA_GATE_NAMES, recounted by executing the dispatcher against the frozen pre_tool payload._hook_json.pysplits by activity (_payload.py: claims/parse) to stay in the 300-line budget. The three entries are regenerated bytools/recount_vendor_config.py --write, never by hand; the other nine entries are byte-identical under the same run._cursor.py/_windsurf.py/_antigravity.pyfamily modules,_familydispatch byfamily, module deletions, andbundle()composing engine + family module + inlinedVENDORliteral 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 awire_eventsinverse fallback for an entry with noeventsmap, 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 publicparse()→UNKNOWN.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
MATRIXuntouched; windsurf'spre_mcp_tool_usegate maps topre_tool, already matrix block-capable, and is derived by executing the dispatcher, not assertedMATRIXrow carries averifiedrecord — no matrix rows changedtests/fixtures/golden/*.json), which were captured from the pre-fold adapters and are unchanged in this PRChecks
pytest -qpasses (1348 passed, 4 skipped; golden fixtures byte-for-byte, 12-agent bundler subprocess replay green)ruff check .andruff format --check .passtests/check_stdlib_only.pyOK)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
words_at.Stop.allowto"halt", windsurf's escalate note, and cursor'shonours_transformeach fails the golden-fixture replay; a benign key-reorder of a whole entry still passes. These mutations fail correctly — necessary, not sufficient.""where antigravity's oldraw.get("error") or NonereturnedNonefor a present-but-emptyerror; and cursor's content chain falls through past an emptyeditsjoin where the old_content_ofstopped. Flagging in case either matters to a reviewer more than it did to the suite._g1's escalate reroute is data-triggered (escalate_from_transformpresent + gate honours escalate); no existing F1/F2 entry has that combination, which the byte-identical fixtures confirm.Generated by Claude Code