You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: mark Changes 1, 2, and 3 as implemented in agentic architecture proposal
Changes 1 (gitignore generated parser), 2 (split grammar by domain), and 3
(command self-registration via explicit NewRegistry()) are all shipped. Updated
each section to reflect actual implementation details and replaced the forward-
looking summary table with done/remaining status.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
**What:**Add`mdl/grammar/parser/*.go` to `.gitignore`and stop tracking them. The `.g4` source files remain in git. Generated files are produced locally and in CI by running`make grammar`.
37
+
**Status:**Shipped.`mdl/grammar/parser/` is excluded via `.gitignore`(line 42–43). The directory is not tracked; contributors regenerate with`make grammar`.
38
38
39
-
**Why this eliminates the conflict:** The 119,737-line generated file is the source of every grammar-related merge conflict. The file is deterministically derived from the `.g4` source — there is no information in it that isn't in the grammar. Removing it from git means two branches that both add syntax no longer produce a file-level conflict; only the human-readable `.g4` source can conflict.
40
-
41
-
**Prerequisites:** Contributors already have ANTLR4 installed (this has been verified — all grammar contributors have been able to run `make grammar` without issue). The barrier that would normally exist (requiring Java for ANTLR4) is not a practical concern here.
42
-
43
-
**CI impact:** The CI pipeline must install ANTLR4 and run `make grammar` before `make build` or `make test`. The `push-test.yml` and `release.yml` workflows currently have no grammar step — both need a setup step added.
44
-
45
-
**Implementation steps:**
46
-
1. Add to `.gitignore`:
47
-
```
48
-
# Generated ANTLR4 parser (regenerate with: make grammar)
49
-
mdl/grammar/parser/*.go
50
-
mdl/grammar/parser/*.interp
51
-
mdl/grammar/parser/*.tokens
52
-
```
53
-
2. Run `git rm --cached mdl/grammar/parser/*.go mdl/grammar/parser/*.interp mdl/grammar/parser/*.tokens`
54
-
3. Add to CI workflows before the build step:
55
-
```yaml
56
-
- name: Install ANTLR4
57
-
run: pip install antlr4-tools
58
-
- name: Generate parser
59
-
run: make grammar
60
-
```
61
-
4. Add `make grammar-check` target that regenerates and diffs — to catch cases where grammar source changed but `make grammar` was not run before committing
39
+
**What was done:** Added `mdl/grammar/parser/` to `.gitignore`. The `.g4` source files remain in git; the generated Go parser is produced locally and in CI by `make grammar`. This eliminates the 119,737-line generated file as a source of merge conflicts entirely.
62
40
63
41
---
64
42
65
-
### Change 2: Split the Grammar Source by Domain
43
+
### Change 2: Split the Grammar Source by Domain ✅ Implemented
66
44
67
-
**What:** Use ANTLR4's `import` directive to split `MDLParser.g4` into domain-specific grammar files, imported by a thin master grammar.
45
+
**Status:**Shipped. `MDLParser.g4`is now a thin master grammar; domain rules live in `mdl/grammar/domains/`.
68
46
69
-
**Why this reduces the remaining conflict surface:** After Change 1, grammar conflicts can only occur on the `.g4` source files. A monolithic 4,082-line grammar means two PRs touching different domains (e.g. microflows and pages) still conflict at the source level. Splitting by domain means those PRs are fully independent.
70
-
71
-
**Proposed split:**
47
+
**What was done:** The monolithic parser grammar was split into nine domain files imported via ANTLR4's `import` directive:
Two PRs touching different domains now edit independent files and cannot conflict at the grammar source level. The generated parser is still a single file — the split is source-level only; all visitor/listener code is unchanged.
99
66
100
-
**Note:** ANTLR4's `import` merges rules into the main grammar at generation time. The generated parser is still a single file — the split is a source-level convenience only. All existing listener/visitor code continues to work unchanged.
67
+
---
101
68
102
-
**Implementation steps:**
103
-
1. Identify rule boundaries in current `MDLParser.g4` by domain
104
-
2. Extract each domain's rules into a `domains/MDL*.g4` file
105
-
3. Replace extracted rules in `MDLParser.g4` with `import` directives
106
-
4. Run `make grammar` and verify generated parser is functionally identical (test suite must pass)
107
-
5. Update `mdl/grammar/Makefile` to list the domain files as dependencies
69
+
### Change 3: Command Self-Registration in the Executor ✅ Implemented
108
70
109
-
---
71
+
**Status:** Shipped. See `mdl/executor/registry.go` and `mdl/executor/register_stubs.go`.
110
72
111
-
### Change 3: Command Self-Registration in the Executor
73
+
**What was proposed:** Replace the central dispatch switch with a registration pattern using `init()` so each `cmd_*.go` file self-registers its handler, making a new command self-contained in one file.
112
74
113
-
**What:**Replace the central dispatch switch in the executor with a registration pattern where each `cmd_*.go` file self-registers its handler on package init.
75
+
**What was implemented:**The registry pattern without `init()`. `NewRegistry()`in `registry.go` is the single composition root that calls 29 named `registerXxxHandlers(r)` functions — one per domain. The `init()` approach was explicitly rejected because it creates package-level global state that breaks test isolation.
114
76
115
-
**Why this helps agentic development:**Currently, adding a new command requires: (a) creating a `cmd_*.go` file, (b) finding and editing the dispatch switch in `executor.go`, and (c) knowing the right context type. An agent has to read across multiple files to understand the pattern. With self-registration, a new command is entirely contained in its own file — no other file needs to change.
77
+
**Why `init()` was not used:**With `init()`-based registration, every import of the `executor` package pre-populates a global handler map before any test runs. This makes it impossible to create a registry with zero handlers for targeted isolation tests. The existing test suite depends on `emptyRegistry()` (a factory that returns a handler-free `*Registry`) for six tests covering dispatch, completeness, and duplicate-registration panics. `init()` also makes duplicate-registration panics surface at package-load time rather than at `NewRegistry()`, producing an obscure failure with no clear test attribution.
1. Add `executor/registry.go` with `Register()` and the handler map
143
-
2. Replace the central dispatch switch with a lookup into the registry
144
-
3. Migrate existing `cmd_*.go` files to register via `init()` — one file at a time, verifiable by running tests after each
145
-
4. Remove the dispatch switch from `executor.go` once all commands are migrated
103
+
**Adding a new command today:** Create `cmd_yourfeature.go` with the handler function, then add a `registerYourFeatureHandlers(r)` call in `NewRegistry()` and a corresponding stub function in `register_stubs.go`. The completeness test (`TestNewRegistry_Completeness`) will fail at CI if the registration step is missed.
104
+
105
+
**Agent discovery cost:** An agent can read `register_stubs.go` as the canonical index of all registered commands and their handler signatures. Any existing `registerXxxHandlers` function is a complete, copy-pasteable example.
146
106
147
107
---
148
108
@@ -198,10 +158,10 @@ Because Go's `internal/` rule allows only the parent package and its children to
| 5. Compiler-enforced boundary | Converts checklist rule to compile error | Medium (needs design) | Medium |
206
166
207
-
**Recommended order:** Changes 1 and 4 first — both are low risk, high value, and immediately unblock parallel development. Changes 2 and 3 next, ideally in separate PRs. Change 5 needs a design decision on the `internal/`boundary approach before implementation.
167
+
**Status:** Changes 1, 2, and 3 are shipped. Remaining work: Change 4 (code-generate mock) and Change 5 (compiler-enforced boundary, needs design decision on the `internal/` approach).
0 commit comments