@snipcodeit/mgw 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,412 @@
1
+ <purpose>
2
+ Shared state management for MGW commands. Handles .mgw/ directory initialization,
3
+ issue state read/write, cross-ref management, and staleness detection against GitHub.
4
+ Every command that touches .mgw/ state references this file.
5
+ </purpose>
6
+
7
+ ## Directory Structure
8
+
9
+ `.mgw/` lives at repo root, is gitignored, and is local-only per developer.
10
+
11
+ ```
12
+ .mgw/
13
+ config.json # User prefs (github username, default filters)
14
+ active/ # In-progress issue pipelines
15
+ <number>-<slug>.json
16
+ completed/ # Archived after PR merged/issue closed
17
+ cross-refs.json # Bidirectional issue/PR/branch links
18
+ ```
19
+
20
+ ## validate_and_load
21
+
22
+ Single entry point for state initialization. Every command that touches .mgw/ calls
23
+ this pattern at startup. It ensures directory structure, gitignore entries, cross-refs,
24
+ and runs a lightweight staleness check.
25
+
26
+ ```bash
27
+ REPO_ROOT=$(git rev-parse --show-toplevel)
28
+ MGW_DIR="${REPO_ROOT}/.mgw"
29
+
30
+ # 1. Ensure directory structure
31
+ mkdir -p "${MGW_DIR}/active" "${MGW_DIR}/completed"
32
+
33
+ # 2. Ensure gitignore entries
34
+ for ENTRY in ".mgw/" ".worktrees/"; do
35
+ if ! grep -q "^\\${ENTRY}\$" "${REPO_ROOT}/.gitignore" 2>/dev/null; then
36
+ echo "${ENTRY}" >> "${REPO_ROOT}/.gitignore"
37
+ fi
38
+ done
39
+
40
+ # 3. Initialize cross-refs if missing
41
+ if [ ! -f "${MGW_DIR}/cross-refs.json" ]; then
42
+ echo '{"links":[]}' > "${MGW_DIR}/cross-refs.json"
43
+ fi
44
+
45
+ # 4. Migrate project.json schema (idempotent — adds new fields without overwriting)
46
+ # This ensures all commands see gsd_milestone_id, gsd_state, active_gsd_milestone, etc.
47
+ # Non-blocking: if migration fails for any reason, continue silently.
48
+ if [ -f "${MGW_DIR}/project.json" ]; then
49
+ node -e "
50
+ const { migrateProjectState } = require('./lib/state.cjs');
51
+ try { migrateProjectState(); } catch(e) { /* non-blocking */ }
52
+ " 2>/dev/null || true
53
+ fi
54
+
55
+ # 5. Run staleness check (see Staleness Detection below)
56
+ # Only if active issues exist — skip for commands that don't need it (e.g., init, help)
57
+ if ls "${MGW_DIR}/active/"*.json 1>/dev/null 2>&1; then
58
+ check_staleness "${MGW_DIR}"
59
+ fi
60
+ ```
61
+
62
+ ## Staleness Detection
63
+
64
+ Lightweight check comparing GitHub `updatedAt` timestamps with local state file
65
+ modification times. Runs on every MGW command that touches state. Non-blocking:
66
+ if the check fails (network error, API limit), log a warning and continue.
67
+
68
+ ### Per-Issue Check
69
+ For commands that operate on a specific issue:
70
+
71
+ ```bash
72
+ # Non-blocking: if check fails, continue with warning
73
+ check_issue_staleness() {
74
+ local ISSUE_NUMBER="$1"
75
+ local STATE_FILE="$2"
76
+
77
+ # Get GitHub's last update timestamp
78
+ GH_UPDATED=$(gh issue view "$ISSUE_NUMBER" --json updatedAt -q .updatedAt 2>/dev/null)
79
+ if [ -z "$GH_UPDATED" ]; then
80
+ return 0 # Can't check — don't block
81
+ fi
82
+
83
+ # Compare with local state file modification time
84
+ LOCAL_MTIME=$(stat -c %Y "$STATE_FILE" 2>/dev/null || echo "0")
85
+ GH_EPOCH=$(date -d "$GH_UPDATED" +%s 2>/dev/null || echo "0")
86
+
87
+ if [ "$GH_EPOCH" -gt "$LOCAL_MTIME" ]; then
88
+ echo "MGW: Stale state detected for #${ISSUE_NUMBER} — auto-syncing..."
89
+ # Re-fetch issue data and update local state
90
+ FRESH_DATA=$(gh issue view "$ISSUE_NUMBER" --json number,title,body,labels,assignees,state,comments,url,milestone 2>/dev/null)
91
+ if [ -n "$FRESH_DATA" ]; then
92
+ # Update the issue section of the state file (preserve pipeline_stage, triage, etc.)
93
+ # Implementation: read state JSON, update .issue fields, write back
94
+ touch "$STATE_FILE" # Update mtime to prevent re-triggering
95
+ fi
96
+ return 1 # Stale — caller can react
97
+ fi
98
+ return 0 # Fresh
99
+ }
100
+ ```
101
+
102
+ ### Batch Check (Milestone-Level)
103
+ For commands that check across all active issues in a single API call:
104
+
105
+ ```bash
106
+ # Single GraphQL call for all open issues — use for milestone operations
107
+ check_batch_staleness() {
108
+ local MGW_DIR="$1"
109
+
110
+ OWNER=$(gh repo view --json owner -q .owner.login 2>/dev/null)
111
+ REPO=$(gh repo view --json name -q .name 2>/dev/null)
112
+
113
+ if [ -z "$OWNER" ] || [ -z "$REPO" ]; then
114
+ return 0 # Can't check — don't block
115
+ fi
116
+
117
+ ISSUES_JSON=$(gh api graphql -f query='
118
+ query($owner: String!, $repo: String!) {
119
+ repository(owner: $owner, name: $repo) {
120
+ issues(first: 50, states: OPEN) {
121
+ nodes { number updatedAt }
122
+ }
123
+ }
124
+ }
125
+ ' -f owner="$OWNER" -f repo="$REPO" --jq '.data.repository.issues.nodes' 2>/dev/null)
126
+
127
+ if [ -z "$ISSUES_JSON" ]; then
128
+ return 0 # Can't check — don't block
129
+ fi
130
+
131
+ # Compare each issue's updatedAt with local state file mtime
132
+ # Flag any stale issues for sync
133
+ echo "$ISSUES_JSON"
134
+ }
135
+ ```
136
+
137
+ ### Staleness Behavior
138
+ - When stale state is detected: **auto-sync with notice** — sync automatically, print one line telling the user it happened, do not block
139
+ - When the check fails (network, API limit, error): **continue silently** — never let staleness detection prevent command execution
140
+ - Scope: covers **both milestone-level and issue-level state**
141
+
142
+ ## Comment Tracking
143
+
144
+ Comment tracking detects new comments posted on an issue between triage and execution.
145
+ The `triage.last_comment_count` and `triage.last_comment_at` fields are populated during
146
+ triage (issue.md) and checked before GSD execution begins (run.md).
147
+
148
+ ### Comment Tracking Fields
149
+
150
+ | Field | Type | Set By | Description |
151
+ |-------|------|--------|-------------|
152
+ | `triage.last_comment_count` | number | issue.md | Total comment count at triage time |
153
+ | `triage.last_comment_at` | string\|null | issue.md | ISO timestamp of most recent comment at triage time |
154
+
155
+ ### Pre-Flight Comment Check
156
+
157
+ Before GSD execution starts, run.md compares current comment count against the stored
158
+ count. If new comments are detected, they are classified before proceeding:
159
+
160
+ ```bash
161
+ # Fetch current comment state from GitHub
162
+ CURRENT_COMMENTS=$(gh issue view $ISSUE_NUMBER --json comments --jq '.comments | length')
163
+ STORED_COMMENTS="${triage.last_comment_count}"
164
+
165
+ if [ "$CURRENT_COMMENTS" -gt "$STORED_COMMENTS" ]; then
166
+ # New comments detected — fetch and classify
167
+ NEW_COMMENT_BODIES=$(gh issue view $ISSUE_NUMBER --json comments \
168
+ --jq "[.comments[-$(($CURRENT_COMMENTS - $STORED_COMMENTS)):]] | .[].body")
169
+ fi
170
+ ```
171
+
172
+ ### Comment Classification
173
+
174
+ New comments are classified by a general-purpose agent into one of three categories:
175
+
176
+ | Classification | Meaning | Pipeline Action |
177
+ |---------------|---------|-----------------|
178
+ | **material** | Changes scope, requirements, or acceptance criteria | Flag for re-triage — update state, re-run triage analysis |
179
+ | **informational** | Status update, +1, question, acknowledgment | Log and continue — no pipeline impact |
180
+ | **blocking** | Explicit "don't work on this yet", "hold", "wait" | Pause pipeline — set pipeline_stage to "blocked" |
181
+
182
+ The classification agent receives the new comment bodies and the issue context, and returns
183
+ a JSON result:
184
+
185
+ ```json
186
+ {
187
+ "classification": "material|informational|blocking",
188
+ "reasoning": "Brief explanation",
189
+ "new_requirements": ["list of new requirements if material"],
190
+ "blocking_reason": "reason if blocking"
191
+ }
192
+ ```
193
+
194
+ ### Comment Delta in Drift Detection
195
+
196
+ sync.md includes comment delta as a drift signal. If the current comment count differs
197
+ from `triage.last_comment_count`, the issue is flagged as having unreviewed comments
198
+ in the sync report.
199
+
200
+ ## Issue State Schema
201
+
202
+ File: `.mgw/active/<number>-<slug>.json`
203
+
204
+ ```json
205
+ {
206
+ "issue": {
207
+ "number": 42,
208
+ "title": "Short title",
209
+ "url": "https://github.com/owner/repo/issues/42",
210
+ "labels": ["bug"],
211
+ "assignee": "username"
212
+ },
213
+ "triage": {
214
+ "scope": { "files": 0, "systems": [] },
215
+ "validity": "pending|confirmed|invalid",
216
+ "security_notes": "",
217
+ "conflicts": [],
218
+ "last_comment_count": 0,
219
+ "last_comment_at": null,
220
+ "gate_result": {
221
+ "status": "passed|blocked",
222
+ "blockers": [],
223
+ "warnings": [],
224
+ "missing_fields": []
225
+ }
226
+ },
227
+ "gsd_route": null,
228
+ "gsd_artifacts": { "type": null, "path": null },
229
+ "pipeline_stage": "new|triaged|needs-info|needs-security-review|discussing|approved|planning|diagnosing|executing|verifying|pr-created|done|failed|blocked",
230
+ "comments_posted": [],
231
+ "linked_pr": null,
232
+ "linked_issues": [],
233
+ "linked_branches": []
234
+ }
235
+ ```
236
+
237
+ ## Stage Flow Diagram
238
+
239
+ ```
240
+ new --> triaged (triage passes all gates)
241
+ new --> needs-info (validity=invalid OR insufficient detail)
242
+ new --> needs-security-review (security=high)
243
+
244
+ needs-info --> triaged (re-triage after info provided)
245
+ needs-security-review --> triaged (re-triage after security ack)
246
+
247
+ triaged --> discussing (new-milestone route, large scope)
248
+ triaged --> approved (discussion complete, ready for execution)
249
+ triaged --> planning (direct route, skip discussion)
250
+ triaged --> diagnosing (gsd:diagnose-issues route)
251
+
252
+ discussing --> approved (stakeholder approval)
253
+ approved --> planning
254
+
255
+ planning --> executing
256
+ diagnosing --> planning (root cause found, proceeding to fix)
257
+ diagnosing --> blocked (investigation inconclusive)
258
+ executing --> verifying
259
+ verifying --> pr-created
260
+ pr-created --> done
261
+
262
+ Any stage --> blocked (blocking comment detected)
263
+ blocked --> triaged (re-triage after blocker resolved)
264
+ Any stage --> failed (unrecoverable error)
265
+ ```
266
+
267
+ ## Slug Generation
268
+
269
+ Use gsd-tools for consistent slug generation:
270
+ ```bash
271
+ SLUG=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs generate-slug "${issue_title}" --raw)
272
+ SLUG="${SLUG:0:40}" # gsd-tools doesn't truncate; MGW enforces 40-char limit
273
+ ```
274
+
275
+ ## Timestamps
276
+
277
+ Use gsd-tools for ISO timestamp generation:
278
+ ```bash
279
+ TIMESTAMP=$(node ~/.claude/get-shit-done/bin/gsd-tools.cjs current-timestamp --raw)
280
+ ```
281
+
282
+ ## Cross-Refs Schema
283
+
284
+ File: `.mgw/cross-refs.json`
285
+
286
+ ```json
287
+ {
288
+ "links": [
289
+ { "a": "issue:42", "b": "issue:43", "type": "related", "created": "2026-02-24T10:00:00Z" },
290
+ { "a": "issue:42", "b": "pr:15", "type": "implements", "created": "2026-02-24T12:00:00Z" },
291
+ { "a": "issue:42", "b": "branch:fix/auth-42", "type": "tracks", "created": "2026-02-24T10:00:00Z" }
292
+ ]
293
+ }
294
+ ```
295
+
296
+ ### Link Types
297
+ | a | b | type |
298
+ |---|---|------|
299
+ | issue | issue | related |
300
+ | issue | pr | implements |
301
+ | issue | branch | tracks |
302
+ | pr | branch | tracks |
303
+
304
+ ## Project State (project.json)
305
+
306
+ File: `.mgw/project.json` — Created by `/mgw:project`, read/updated by `/mgw:milestone` and `/mgw:next`.
307
+
308
+ ### Read Project State
309
+ ```bash
310
+ MGW_DIR="${REPO_ROOT}/.mgw"
311
+ PROJECT_JSON=$(cat "${MGW_DIR}/project.json" 2>/dev/null)
312
+ if [ -z "$PROJECT_JSON" ]; then
313
+ echo "No project initialized. Run /mgw:project first."
314
+ exit 1
315
+ fi
316
+
317
+ # Resolve active milestone index — supports both new schema (active_gsd_milestone string)
318
+ # and legacy schema (current_milestone 1-indexed integer).
319
+ ACTIVE_IDX=$(node -e "
320
+ const { loadProjectState, resolveActiveMilestoneIndex } = require('./lib/state.cjs');
321
+ const state = loadProjectState();
322
+ console.log(resolveActiveMilestoneIndex(state));
323
+ ")
324
+ CURRENT_MILESTONE=$((ACTIVE_IDX + 1)) # 1-indexed for display/legacy compat
325
+ ```
326
+
327
+ ### Read Milestone Issues
328
+ ```bash
329
+ MILESTONE_ISSUES=$(node -e "
330
+ const { loadProjectState, resolveActiveMilestoneIndex } = require('./lib/state.cjs');
331
+ const state = loadProjectState();
332
+ const idx = resolveActiveMilestoneIndex(state);
333
+ if (idx < 0) { console.error('No active milestone'); process.exit(1); }
334
+ console.log(JSON.stringify(state.milestones[idx].issues || [], null, 2));
335
+ ")
336
+ ```
337
+
338
+ ### Update Issue Pipeline Stage
339
+ Used after each `/mgw:run` completion to checkpoint progress.
340
+ ```bash
341
+ node -e "
342
+ const { loadProjectState, resolveActiveMilestoneIndex, writeProjectState } = require('./lib/state.cjs');
343
+ const state = loadProjectState();
344
+ const idx = resolveActiveMilestoneIndex(state);
345
+ if (idx < 0) { console.error('No active milestone'); process.exit(1); }
346
+ const issue = (state.milestones[idx].issues || []).find(i => i.github_number === ${ISSUE_NUMBER});
347
+ if (issue) { issue.pipeline_stage = '${NEW_STAGE}'; }
348
+ writeProjectState(state);
349
+ "
350
+ ```
351
+
352
+ Valid stages: `new`, `triaged`, `planning`, `diagnosing`, `executing`, `verifying`, `pr-created`, `done`, `failed`, `blocked`.
353
+
354
+ ### Advance Current Milestone
355
+ Used after milestone completion to move pointer to next milestone.
356
+ ```bash
357
+ node -e "
358
+ const { loadProjectState, resolveActiveMilestoneIndex, writeProjectState } = require('./lib/state.cjs');
359
+ const state = loadProjectState();
360
+ const currentIdx = resolveActiveMilestoneIndex(state);
361
+ const next = (state.milestones || [])[currentIdx + 1];
362
+ if (next) {
363
+ state.active_gsd_milestone = next.gsd_milestone_id || null;
364
+ if (!state.active_gsd_milestone) {
365
+ state.current_milestone = currentIdx + 2; // legacy fallback
366
+ }
367
+ } else {
368
+ state.active_gsd_milestone = null;
369
+ state.current_milestone = currentIdx + 2; // past end, signals completion
370
+ }
371
+ writeProjectState(state);
372
+ "
373
+ ```
374
+
375
+ Only advance if ALL issues in current milestone completed successfully.
376
+
377
+ ### Phase Map Usage
378
+
379
+ The `phase_map` in project.json maps GSD phase numbers to their metadata. This is the
380
+ bridge between MGW's issue tracking and GSD's phase-based execution:
381
+
382
+ ```json
383
+ {
384
+ "phase_map": {
385
+ "1": {"milestone_index": 0, "gsd_route": "plan-phase", "name": "Core Data Models"},
386
+ "2": {"milestone_index": 0, "gsd_route": "plan-phase", "name": "API Endpoints"},
387
+ "3": {"milestone_index": 1, "gsd_route": "plan-phase", "name": "Frontend Components"}
388
+ }
389
+ }
390
+ ```
391
+
392
+ Each issue in project.json has a `phase_number` field that indexes into this map.
393
+ When `/mgw:run` picks up an issue, it reads the `phase_number` to determine which
394
+ GSD phase directory (`.planning/phases/{NN}-{slug}/`) to operate in.
395
+
396
+ Issues created outside of `/mgw:project` (e.g., manually filed bugs) will not have
397
+ a `phase_number`. In this case, `/mgw:run` falls back to the quick pipeline.
398
+
399
+ ## Consumers
400
+
401
+ | Pattern | Referenced By |
402
+ |---------|-------------|
403
+ | validate_and_load | init.md, issue.md, run.md, update.md, link.md, pr.md, sync.md, milestone.md, ask.md |
404
+ | Per-issue staleness | run.md, issue.md, update.md |
405
+ | Batch staleness | sync.md (full reconciliation), milestone.md |
406
+ | Comment tracking | issue.md (populate), run.md (pre-flight check), sync.md (drift detection) |
407
+ | Issue state schema | issue.md, run.md, update.md, sync.md |
408
+ | Cross-refs schema | link.md, run.md, pr.md, sync.md |
409
+ | Slug generation | issue.md, run.md |
410
+ | Project state | milestone.md, next.md, ask.md |
411
+ | Gate result schema | issue.md (populate), run.md (validate) |
412
+ | Board status sync | board-sync.md (utility), issue.md (triage transitions), run.md (pipeline transitions) |
@@ -0,0 +1,144 @@
1
+ <purpose>
2
+ Delegation boundary rule for MGW commands. Provides a mechanical check and review
3
+ checklist to enforce that MGW orchestrates but never codes. Any developer reviewing
4
+ or writing an MGW command can apply this rule to get a clear yes/no answer.
5
+ </purpose>
6
+
7
+ ## The Delegation Boundary Rule
8
+
9
+ **MGW orchestrates. MGW never codes.**
10
+
11
+ MGW's job is to connect GitHub issues to GSD's execution engine and manage the
12
+ pipeline state between them. MGW reads state, writes state, talks to GitHub, and
13
+ spawns agents. It never reads application code, writes application code, or makes
14
+ implementation decisions.
15
+
16
+ ## Mechanical Check
17
+
18
+ For any logic in an MGW command, ask:
19
+
20
+ > "If GSD improved this tomorrow, would MGW automatically benefit?"
21
+
22
+ - **YES** -- Logic is correctly delegated. It lives in a Task() agent or in GSD itself, and MGW references the result.
23
+ - **NO** -- Logic is misplaced in MGW. It should be moved into a Task() agent that MGW spawns.
24
+
25
+ ### Examples
26
+
27
+ | Logic | Answer | Reasoning |
28
+ |-------|--------|-----------|
29
+ | "Fetch issue from GitHub" | N/A | This is GitHub API, not GSD. MGW may do this directly. |
30
+ | "Analyze which files are affected" | YES | GSD's executor reads code. If GSD got better at analysis, MGW benefits because it spawns an analysis agent. |
31
+ | "Parse the issue body for triage" | NO (violation) | If MGW inlines body parsing, GSD improvements don't help. Spawn a triage agent instead. |
32
+ | "Spawn a planner agent" | YES | The planner is a GSD agent. Improvements flow through. |
33
+ | "Write .mgw/active/42-fix.json" | N/A | State management is MGW's domain, not GSD's. |
34
+
35
+ ## What MGW May Do Directly (Allowlist)
36
+
37
+ These operations are within MGW's boundary:
38
+
39
+ ```
40
+ - Read/write .mgw/ state files (JSON)
41
+ - Read/write GitHub metadata (via gh CLI — patterns in workflows/github.md)
42
+ - Parse command arguments ($ARGUMENTS)
43
+ - Display user-facing output (banners, tables, prompts, reports)
44
+ - Spawn Task() agents (via templates in workflows/gsd.md)
45
+ - Call gsd-tools.cjs for utilities (slugs, timestamps, model resolution)
46
+ - Initialize state (via validate_and_load in workflows/state.md)
47
+ - Manage worktrees (git worktree add/remove)
48
+ - Check/set git branches (git checkout, git push)
49
+ ```
50
+
51
+ ## What MGW Must NEVER Do Directly (Denylist)
52
+
53
+ These operations cross the delegation boundary:
54
+
55
+ ```
56
+ - Read application source code (even for "quick" scope analysis)
57
+ - Write application source code
58
+ - Make architecture or implementation decisions
59
+ - Analyze code for scope, security, or conflicts
60
+ - Generate PR descriptions from code analysis (not from GSD artifacts)
61
+ - Choose libraries or implementation patterns
62
+ - Run application tests or interpret test results
63
+ - Any operation that requires understanding the project's implementation
64
+ ```
65
+
66
+ **If you find yourself needing to do something on the denylist:** Spawn an agent for it.
67
+
68
+ ## Review Checklist
69
+
70
+ For each block of logic in an MGW command, check:
71
+
72
+ - [ ] Does it read/write .mgw/ state? -- **ALLOWED**
73
+ - [ ] Does it call gh CLI? -- **ALLOWED** (should match a pattern in workflows/github.md)
74
+ - [ ] Does it spawn a Task()? -- **ALLOWED** (must match template in workflows/gsd.md, must include CLAUDE.md injection)
75
+ - [ ] Does it call gsd-tools.cjs? -- **ALLOWED**
76
+ - [ ] Does it display output to the user? -- **ALLOWED**
77
+ - [ ] Does it manage git worktrees/branches? -- **ALLOWED**
78
+ - [ ] Does it parse $ARGUMENTS? -- **ALLOWED**
79
+ - [ ] Does it read application code? -- **VIOLATION** — spawn an analysis agent
80
+ - [ ] Does it write application code? -- **VIOLATION** — spawn an executor agent
81
+ - [ ] Does it make implementation decisions? -- **VIOLATION** — spawn a planner agent
82
+ - [ ] Does it analyze code for scope/security? -- **VIOLATION** — spawn a triage agent
83
+ - [ ] Does it generate content from code analysis? -- **VIOLATION** — spawn a content agent
84
+
85
+ ## Applying the Rule: Concrete Example
86
+
87
+ **Before (issue.md inline analysis — VIOLATION):**
88
+ ```
89
+ # BAD: MGW reading code directly
90
+ Search the codebase for files related to "${issue_title}"
91
+ grep -r "auth" src/
92
+ # Then MGW decides: "3 files affected, medium scope"
93
+ ```
94
+
95
+ **After (issue.md delegated analysis — CORRECT):**
96
+ ```
97
+ # GOOD: MGW spawns an agent that reads code
98
+ Task(
99
+ prompt="
100
+ <files_to_read>
101
+ - ./CLAUDE.md (Project instructions)
102
+ - .agents/skills/ (Project skills)
103
+ </files_to_read>
104
+
105
+ Analyze GitHub issue #${ISSUE_NUMBER} against this codebase.
106
+ Search for affected files and systems.
107
+ Return: scope (files, systems, size), validity, security, conflicts.
108
+ ",
109
+ subagent_type="general-purpose",
110
+ description="Triage issue #${ISSUE_NUMBER}"
111
+ )
112
+ ```
113
+
114
+ The agent reads code and returns structured results. MGW reads the structured results,
115
+ writes them to .mgw/active/, and presents them to the user. MGW never touched the code.
116
+
117
+ ## When In Doubt
118
+
119
+ If you're unsure whether a piece of logic belongs in MGW or in an agent:
120
+
121
+ 1. Apply the mechanical check: "If GSD improved this, would MGW benefit?"
122
+ 2. Check if it's on the allowlist (state, GitHub, display, spawn, utilities)
123
+ 3. If neither gives a clear answer, default to **spawning an agent**
124
+
125
+ Over-delegation (spawning agents for trivial work) wastes tokens but doesn't break anything.
126
+ Under-delegation (MGW reading code) violates the architecture and creates maintenance debt.
127
+
128
+ ## Consumers
129
+
130
+ This rule applies to ALL MGW commands:
131
+
132
+ | Command | Key Boundary Points |
133
+ |---------|-------------------|
134
+ | run.md | Spawns planner, executor, verifier, comment classifier — never reads code |
135
+ | issue.md | Spawns analysis agent — never analyzes code itself |
136
+ | review.md | Spawns comment classification agent — reads comments, not code |
137
+ | pr.md | Spawns PR body builder — never reads code for description |
138
+ | ask.md | Spawns classification agent — never analyzes code itself |
139
+ | sync.md | Reads state + GitHub API only — never reads code |
140
+ | update.md | Reads state only — posts comments |
141
+ | link.md | Reads/writes cross-refs only |
142
+ | init.md | Creates structure + GitHub labels only |
143
+ | issues.md | Reads GitHub API only |
144
+ | help.md | Display only |