Skip to content

Commit 5c6c9aa

Browse files
D4 stage 2: fold gemini_cli, tabnine, junie, grok onto the flat_decision engine
The four F2 modules are deleted; adapters.get() binds each to the D3 engine over its data/vendors entry, and bundle() composes engine + inlined VENDOR literal for them. Golden fixtures byte-for-byte unchanged; the 12-agent bundler subprocess replay is green. The tabnine source-grep vocabulary test is retired per design §5, its intent already re-homed as test_vocabulary_basis_is_unverified_only_for_tabnine. junie's PermissionRequest alias gate joins _EXTRA_GATE_NAMES; the transitional gemini fields override in tools/recount is removed now that the entry is the source. Signed-off-by: Open Coder AI <250249270+open-coder-ai@users.noreply.github.com>
1 parent 0bd581b commit 5c6c9aa

10 files changed

Lines changed: 43 additions & 495 deletions

File tree

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,32 @@ versioning: [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
3030
chains (demonstrated, not merely asserted).
3131

3232
### Changed
33+
- **The four `flat_decision` adapters are folded onto the same engine**
34+
(`docs/design/dialect-families.md` D4). `gemini_cli`, `tabnine`, `junie` and `grok` are
35+
no longer hand-written modules: the D3 engine executes their `data/vendors/<agent>.json`
36+
entries, which now carry the G1 renderer data (verdict `words`, `degrade_notes` verbatim
37+
from the deleted adapters, `reason_defaults`). The golden wire fixtures pass
38+
**byte-for-byte unchanged**. The devices F2 needed are all word/grammar-table
39+
extensions, each an opt-in schema field: an escalate word path in the G1 renderer, the
40+
`hook_specific_tool_input` (gemini) and `top_level_updated_input` (junie) transform
41+
bodies, template degrade notes filling (reason, wire event) slots (gemini's
42+
ask-degradation), per-gate reason defaults and allow-silence and allow-body context
43+
(junie's Stop/`additionalContext`), a reverse-map fallback for payloads carrying no wire
44+
event name (tabnine's hand-built events), a `tool_input` envelope chain (grok's
45+
`toolInput`), write-gated content (gemini), and literal hook-entry extras (tabnine's
46+
`name`). junie's `PermissionRequest` alias gate is recounted explicitly via
47+
`_EXTRA_GATE_NAMES`, same as devin's. `bundle()` now composes engine + inlined `VENDOR`
48+
literal for these four too (measured: gemini_cli 690, tabnine 664, junie 664, grok 664
49+
lines, from 458/409/429/439 — real numbers in the design doc §7). Two respond()-direct
50+
behaviours the events table cannot express did not survive the fold: tabnine no longer
51+
answers the undocumented `BeforeModel`/`AfterModel` wire names (they map to no canonical
52+
event, and the dispatcher never reached them), and gemini_cli no longer honours a
53+
rewrite at gates whose recounted capability is deny-only — both unreachable through
54+
`handle()`, so the wire through the dispatcher is unchanged.
55+
`test_the_unverified_vocabulary_is_still_only_tabnine` is retired exactly as §5's table
56+
prescribed; its intent lives on as
57+
`test_vendor_config.py::test_vocabulary_basis_is_unverified_only_for_tabnine`, a config
58+
derivation instead of a source grep.
3359
- **Four `hook_json` adapters are now one engine plus their vendor config entries**
3460
(`docs/design/dialect-families.md` D3). `claude_code`, `codex_cli`, `kimi_code` and
3561
`devin` are no longer hand-written modules: `adapters/_hook_json.py` (marker claims,

docs/design/dialect-families.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,17 @@ first, D3/D4 swap cleanly — they share only D1+D2.
321321
§4 payoff side held: the four deleted adapter modules were 624 lines; the engine that
322322
replaces them (`_hook_json` + `_hook_entry` + `_probes` + `_family`) is ~390 shared
323323
across the family.
324+
- **[v]** D4's F2 numbers, measured at its base (main `b9ae0a8`, where the pre-fold
325+
bundles stood at gemini_cli 458 / tabnine 409 / junie 429 / grok 439): gemini_cli 690,
326+
tabnine 664, junie 664, grok 664 — +51% to +62%, above both the §4 estimate (470–500)
327+
and D3's +32–44%. The overshoot is composition, not dialect: an engine bundle inlines
328+
the whole shared engine, G2 and the F1-only devices included — the trade D3 already
329+
made, and the bundle-composition wave (D6) owns any trimming. The source-tree payoff
330+
held again: the four deleted modules were 465 lines, replaced by no new engine module
331+
at all — the D3 engine absorbed F2 with ~40 lines of word/grammar-table devices
332+
(escalate word path, two transform-body variants, template notes, per-gate reason
333+
defaults, allow-body context/silence, a reverse-map wire fallback, a tool-input
334+
envelope chain, write-gated content, literal hook-entry extras).
324335
- **[v]** (was [h]) The `reject_probes: ["looks_like_claude_code"]` device (named engine
325336
predicates referenced from config) is the narrowest crack in the code/config line;
326337
if D2 finds more than ~3 named probes are needed, that is evidence the line is drawn

src/agentseam/adapters/__init__.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,19 @@
66
from . import (
77
antigravity,
88
cursor,
9-
gemini_cli,
10-
grok,
11-
junie,
12-
tabnine,
139
vscode_copilot,
1410
windsurf,
1511
)
1612
from ._family import bind
1713

18-
#: hook_json vendors driven by engine + data/vendors entry (dialect-families.md D3);
14+
#: hook_json (D3) and flat_decision (D4) vendors driven by engine + data/vendors entry;
1915
#: vscode_copilot stays a dialect module -- its three-path claims() and memory-tool
2016
#: branching are beyond what the flat config may carry (§3.1).
21-
_CONFIG_DRIVEN = ("claude_code", "codex_cli", "devin", "kimi_code")
17+
_CONFIG_DRIVEN = ("claude_code", "codex_cli", "devin", "gemini_cli", "grok", "junie", "kimi_code", "tabnine")
2218

2319
ADAPTERS = {
2420
antigravity.AGENT: antigravity,
2521
cursor.AGENT: cursor,
26-
gemini_cli.AGENT: gemini_cli,
27-
grok.AGENT: grok,
28-
junie.AGENT: junie,
29-
tabnine.AGENT: tabnine,
3022
vscode_copilot.AGENT: vscode_copilot,
3123
windsurf.AGENT: windsurf,
3224
}

src/agentseam/adapters/_hook_json.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""The F1 `hook_json` family engine: claims/parse/respond driven by a vendor config entry.
1+
"""The F1 `hook_json` / F2 `flat_decision` engine: claims/parse/respond driven by a vendor config entry.
22
33
Every function takes the vendor's `data/vendors/<agent>.json` entry as its first argument;
44
`_family.bind()` closes them over one entry, and a bundle inlines this module next to a
@@ -211,7 +211,9 @@ def _g1(v, gate, decision, wire, name):
211211
if decision.reason:
212212
out["reason"] = decision.reason
213213
return _json.dumps(out), 0
214-
return _json.dumps({"hookSpecificOutput": {"hookEventName": name, "updatedInput": decision.updated_input}}), 0
214+
return _json.dumps(
215+
{"hookSpecificOutput": {"hookEventName": name, "updatedInput": decision.updated_input}}
216+
), 0
215217
if decision.outcome == ESCALATE and gate["honours_escalate"] and "escalate" in words:
216218
reason = decision.reason or _default_for(v, decision, True, wire)
217219
return _json.dumps({"decision": words["escalate"], "reason": reason}), 0

src/agentseam/adapters/gemini_cli.py

Lines changed: 0 additions & 120 deletions
This file was deleted.

src/agentseam/adapters/grok.py

Lines changed: 0 additions & 124 deletions
This file was deleted.

0 commit comments

Comments
 (0)