@smartmemory/compose 0.2.8-beta → 0.2.10-beta

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.
Files changed (67) hide show
  1. package/bin/compose.js +75 -1
  2. package/contracts/gsd-state.json +140 -0
  3. package/contracts/par-merge-bounce.json +39 -0
  4. package/dist/assets/{App-D3ehVPvi.js → App-CG-2euMe.js} +164 -164
  5. package/dist/assets/{arc-Dmf69iHG.js → arc-7QBWoLra.js} +1 -1
  6. package/dist/assets/{architectureDiagram-3BPJPVTR-xYo993Yw.js → architectureDiagram-3BPJPVTR-CUw-7uLm.js} +1 -1
  7. package/dist/assets/{blockDiagram-GPEHLZMM-UX4EF98O.js → blockDiagram-GPEHLZMM-COU1vmr7.js} +1 -1
  8. package/dist/assets/{c4Diagram-AAUBKEIU-DaP9CGWb.js → c4Diagram-AAUBKEIU-XPO9PSJL.js} +1 -1
  9. package/dist/assets/channel-Bcu04MIK.js +1 -0
  10. package/dist/assets/{chunk-2J33WTMH-CKk_RN3A.js → chunk-2J33WTMH-zMzVB2a6.js} +1 -1
  11. package/dist/assets/{chunk-4BX2VUAB-DboAwYKw.js → chunk-4BX2VUAB-Kke_qcHU.js} +1 -1
  12. package/dist/assets/{chunk-55IACEB6-Dsy9RYvI.js → chunk-55IACEB6-hMeFx5Nh.js} +1 -1
  13. package/dist/assets/{chunk-727SXJPM-fAH0QO9v.js → chunk-727SXJPM-DesUnrEw.js} +1 -1
  14. package/dist/assets/{chunk-AQP2D5EJ-DyZYerFP.js → chunk-AQP2D5EJ-1uGGvkxW.js} +1 -1
  15. package/dist/assets/{chunk-FMBD7UC4-BnboGO5t.js → chunk-FMBD7UC4-DYHv1PcZ.js} +1 -1
  16. package/dist/assets/{chunk-ND2GUHAM-Di9tYXme.js → chunk-ND2GUHAM-D0MENOLX.js} +1 -1
  17. package/dist/assets/{chunk-QZHKN3VN-zRPRlAIL.js → chunk-QZHKN3VN-8nn3HP-N.js} +1 -1
  18. package/dist/assets/classDiagram-4FO5ZUOK-DU4yxldU.js +1 -0
  19. package/dist/assets/classDiagram-v2-Q7XG4LA2-DU4yxldU.js +1 -0
  20. package/dist/assets/{cose-bilkent-S5V4N54A-C7Hqukaf.js → cose-bilkent-S5V4N54A-BoZPVIny.js} +1 -1
  21. package/dist/assets/{dagre-BM42HDAG-B-cR-BjI.js → dagre-BM42HDAG-BgZzdLG9.js} +1 -1
  22. package/dist/assets/{diagram-2AECGRRQ-B6-5onDk.js → diagram-2AECGRRQ-CknAnpSu.js} +1 -1
  23. package/dist/assets/{diagram-5GNKFQAL-DoZZgFAM.js → diagram-5GNKFQAL-CZUEbKim.js} +1 -1
  24. package/dist/assets/{diagram-KO2AKTUF-77jEGlJh.js → diagram-KO2AKTUF-DCs-pLdH.js} +1 -1
  25. package/dist/assets/{diagram-LMA3HP47-D3S7XDRD.js → diagram-LMA3HP47-lRaDjIfM.js} +1 -1
  26. package/dist/assets/{diagram-OG6HWLK6-KbYL9aCY.js → diagram-OG6HWLK6-CIGqmehP.js} +1 -1
  27. package/dist/assets/{erDiagram-TEJ5UH35-DezFbJP-.js → erDiagram-TEJ5UH35-Lx3c2N6F.js} +1 -1
  28. package/dist/assets/{flowDiagram-I6XJVG4X-4x31cK9j.js → flowDiagram-I6XJVG4X-VoluKqSq.js} +1 -1
  29. package/dist/assets/{ganttDiagram-6RSMTGT7-FopfSTyZ.js → ganttDiagram-6RSMTGT7-D7hETiNZ.js} +1 -1
  30. package/dist/assets/{gitGraphDiagram-PVQCEYII-DSiQGKbN.js → gitGraphDiagram-PVQCEYII-DenEcUvY.js} +1 -1
  31. package/dist/assets/{index-ClX6LVAf.js → index-B4dv3acY.js} +2 -2
  32. package/dist/assets/{infoDiagram-5YYISTIA-DE6BqzK_.js → infoDiagram-5YYISTIA-v7cq9Er9.js} +1 -1
  33. package/dist/assets/{ishikawaDiagram-YF4QCWOH-Dml8NwQI.js → ishikawaDiagram-YF4QCWOH-CfCCXt2x.js} +1 -1
  34. package/dist/assets/{journeyDiagram-JHISSGLW-CwWeJgjE.js → journeyDiagram-JHISSGLW-Bbokl_xO.js} +1 -1
  35. package/dist/assets/{kanban-definition-UN3LZRKU-DnG956Wh.js → kanban-definition-UN3LZRKU-DhkOZ2hg.js} +1 -1
  36. package/dist/assets/{linear-CA3N7Rpi.js → linear-bHjluRm2.js} +1 -1
  37. package/dist/assets/{mindmap-definition-RKZ34NQL-CxfIOjLX.js → mindmap-definition-RKZ34NQL-C1bHpoXH.js} +1 -1
  38. package/dist/assets/{pieDiagram-4H26LBE5-O7aIwy1x.js → pieDiagram-4H26LBE5-CZb1i55T.js} +1 -1
  39. package/dist/assets/{quadrantDiagram-W4KKPZXB-CPQ2qq7c.js → quadrantDiagram-W4KKPZXB-o37AwRHB.js} +1 -1
  40. package/dist/assets/{requirementDiagram-4Y6WPE33-C23horL4.js → requirementDiagram-4Y6WPE33-BVErWDzU.js} +1 -1
  41. package/dist/assets/{sankeyDiagram-5OEKKPKP-DPY04kOW.js → sankeyDiagram-5OEKKPKP-BhBK8gHQ.js} +1 -1
  42. package/dist/assets/{sequenceDiagram-3UESZ5HK-BKaTfIvo.js → sequenceDiagram-3UESZ5HK-CsICF23P.js} +1 -1
  43. package/dist/assets/{stateDiagram-AJRCARHV-B9na_6mY.js → stateDiagram-AJRCARHV-TN1AXwim.js} +1 -1
  44. package/dist/assets/stateDiagram-v2-BHNVJYJU-BLR6AkKX.js +1 -0
  45. package/dist/assets/{timeline-definition-PNZ67QCA-BBWPqd7X.js → timeline-definition-PNZ67QCA-DftAajbU.js} +1 -1
  46. package/dist/assets/{vennDiagram-CIIHVFJN-tWqiHsOZ.js → vennDiagram-CIIHVFJN-cFTMstT7.js} +1 -1
  47. package/dist/assets/{wardley-L42UT6IY-DorxG6os.js → wardley-L42UT6IY-DL8CivzO.js} +1 -1
  48. package/dist/assets/{wardleyDiagram-YWT4CUSO-B49f8GzW.js → wardleyDiagram-YWT4CUSO-BDZT1hQj.js} +1 -1
  49. package/dist/assets/{xychartDiagram-2RQKCTM6-BgKSj8Qb.js → xychartDiagram-2RQKCTM6-DQQSkfC4.js} +1 -1
  50. package/dist/index.html +1 -1
  51. package/lib/build.js +140 -17
  52. package/lib/gsd-diff-capture.js +34 -0
  53. package/lib/gsd-events.js +61 -0
  54. package/lib/gsd-headless-config.js +110 -0
  55. package/lib/gsd-milestone-report.js +323 -0
  56. package/lib/gsd-state.js +165 -0
  57. package/lib/gsd-supervisor.js +223 -0
  58. package/lib/gsd-timing.js +89 -0
  59. package/lib/gsd.js +504 -49
  60. package/lib/step-prompt.js +6 -0
  61. package/lib/stratum-mcp-client.js +3 -1
  62. package/package.json +1 -1
  63. package/pipelines/gsd.stratum.yaml +12 -4
  64. package/dist/assets/channel-D_RXsFFT.js +0 -1
  65. package/dist/assets/classDiagram-4FO5ZUOK-K6wdB4ic.js +0 -1
  66. package/dist/assets/classDiagram-v2-Q7XG4LA2-K6wdB4ic.js +0 -1
  67. package/dist/assets/stateDiagram-v2-BHNVJYJU-Cf84VDiH.js +0 -1
package/bin/compose.js CHANGED
@@ -1968,11 +1968,55 @@ if (cmd === 'build') {
1968
1968
  // compose gsd <feature-code> — runs the per-task fresh-context dispatch
1969
1969
  // pipeline (pipelines/gsd.stratum.yaml). Hard-requires existing
1970
1970
  // docs/features/<code>/blueprint.md with a parseable Boundary Map.
1971
+
1972
+ // COMP-GSD-6: `compose gsd query <feature>` — instant read-only JSON snapshot
1973
+ // (no LLM/server/Stratum). For status pollers + CI dashboards.
1974
+ if (args[0] === 'query') {
1975
+ const qCode = args.find((a, i) => i > 0 && !a.startsWith('-'))
1976
+ if (!qCode) {
1977
+ console.error('Usage: compose gsd query <feature-code> [--cwd <path>]')
1978
+ process.exit(1)
1979
+ }
1980
+ const { root: qRoot } = resolveCwdWithWorkspace(args.slice(1))
1981
+ const qCwdIdx = args.indexOf('--cwd')
1982
+ const qCwd = qCwdIdx !== -1 ? resolve(args[qCwdIdx + 1]) : qRoot
1983
+ const { buildGsdQuery } = await import('../lib/gsd-state.js')
1984
+ const snapshot = buildGsdQuery(qCwd, qCode)
1985
+ console.log(JSON.stringify(snapshot, null, 2))
1986
+ process.exit(snapshot.status === 'absent' ? 3 : 0)
1987
+ }
1988
+
1989
+ // COMP-GSD-7: `compose gsd report <feature>` — (re)generate the milestone HTML
1990
+ // report from persisted run artifacts (auto-generated on completion; this is
1991
+ // the retroactive/archival path). Writes docs/gsd-reports/<feature>.html.
1992
+ if (args[0] === 'report') {
1993
+ const rCwdIdx = args.indexOf('--cwd')
1994
+ const rCwdValIdx = rCwdIdx !== -1 ? rCwdIdx + 1 : -1
1995
+ const rCode = args.find((a, i) => i > 0 && i !== rCwdValIdx && !a.startsWith('-'))
1996
+ if (!rCode) {
1997
+ console.error('Usage: compose gsd report <feature-code> [--cwd <path>]')
1998
+ process.exit(1)
1999
+ }
2000
+ const { root: rRoot } = resolveCwdWithWorkspace(args.slice(1))
2001
+ const rCwd = rCwdIdx !== -1 ? resolve(args[rCwdIdx + 1]) : rRoot
2002
+ const { generateGsdMilestoneReport } = await import('../lib/gsd-milestone-report.js')
2003
+ const r = generateGsdMilestoneReport(rCode, rCwd)
2004
+ if (!r.ok) {
2005
+ console.error(`gsd report: ${r.error}`)
2006
+ process.exit(1)
2007
+ }
2008
+ console.log(`Milestone report written: ${r.path}`)
2009
+ process.exit(0)
2010
+ }
2011
+
1971
2012
  const gsdCode = args.find(a => !a.startsWith('-'))
1972
2013
  const gsdResume = args.includes('--resume')
1973
2014
  const gsdResetBudget = args.includes('--reset-budget')
2015
+ const gsdHeadless = args.includes('--headless')
1974
2016
  if (!gsdCode) {
1975
- console.error('Usage: compose gsd <feature-code> [--resume] [--reset-budget]')
2017
+ console.error('Usage: compose gsd <feature-code> [--resume] [--reset-budget] [--headless]')
2018
+ console.error(' compose gsd query <feature-code> (instant JSON status snapshot)')
2019
+ console.error(' compose gsd report <feature-code> (generate milestone HTML report)')
1976
2020
  console.error('')
1977
2021
  console.error('Runs the per-task fresh-context dispatch pipeline (COMP-GSD-2).')
1978
2022
  console.error('Hard-requires docs/features/<code>/blueprint.md with a valid Boundary Map.')
@@ -1984,12 +2028,35 @@ if (cmd === 'build') {
1984
2028
  console.error(' from .compose/gsd/<code>/pause.json (skips completed tasks).')
1985
2029
  console.error(' --reset-budget Clear the feature\'s cumulative budget ledger before running')
1986
2030
  console.error(' (use after raising or removing a spent gsd.budget.cumulative cap).')
2031
+ console.error(' --headless Unattended supervisor (COMP-GSD-6): auto-resume on crash/stuck')
2032
+ console.error(' with backoff + crash recovery. Policy: gsd.headless.* in compose.json.')
1987
2033
  console.error(' --cwd <path> Working directory (defaults to current)')
1988
2034
  process.exit(1)
1989
2035
  }
1990
2036
  const { root: gsdCwd } = resolveCwdWithWorkspace(args)
1991
2037
  const cwdIdx = args.indexOf('--cwd')
1992
2038
  const gsdAgentCwd = cwdIdx !== -1 ? resolve(args[cwdIdx + 1]) : gsdCwd
2039
+
2040
+ // COMP-GSD-6: --headless hands off to the supervisor, which spawns plain
2041
+ // `compose gsd` children and auto-resumes per policy. Exit 0 on completion,
2042
+ // non-zero on a terminal stop (failed/fatal/budget/stuck-exhausted/aborted).
2043
+ if (gsdHeadless) {
2044
+ const { runGsdHeadless } = await import('../lib/gsd-supervisor.js')
2045
+ try {
2046
+ const r = await runGsdHeadless(gsdCode, { cwd: gsdAgentCwd, resume: gsdResume })
2047
+ if (r.ok) {
2048
+ console.log(`gsd headless: complete after ${r.attempts} attempt(s).`)
2049
+ process.exit(0)
2050
+ }
2051
+ console.error(`gsd headless: stopped with status "${r.status}" after ${r.attempts} attempt(s).`)
2052
+ console.error(`Query: compose gsd query ${gsdCode}`)
2053
+ process.exit(r.status === 'failed' || r.status === 'fatal' ? 1 : 2)
2054
+ } catch (err) {
2055
+ console.error(`gsd headless failed: ${err.message}`)
2056
+ process.exit(1)
2057
+ }
2058
+ }
2059
+
1993
2060
  const { runGsd } = await import('../lib/gsd.js')
1994
2061
  try {
1995
2062
  if (gsdResetBudget) {
@@ -2020,6 +2087,13 @@ if (cmd === 'build') {
2020
2087
  }
2021
2088
  process.exit(2)
2022
2089
  }
2090
+ if (result.status !== 'complete') {
2091
+ // COMP-GSD-6: any non-complete terminal (e.g. a stratum 'killed') is a
2092
+ // failure, not success — don't print "complete" or exit 0.
2093
+ console.error(`gsd ${result.status}: run ended without completing.`)
2094
+ console.error(`Inspect: compose gsd query ${gsdCode}`)
2095
+ process.exit(1)
2096
+ }
2023
2097
  console.log(`gsd complete: ${result.blackboardEntries} task results captured.`)
2024
2098
  } catch (err) {
2025
2099
  console.error(`gsd failed: ${err.message}`)
@@ -0,0 +1,140 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gsd-state.json",
4
+ "_source": "COMP-GSD-6",
5
+ "_also": "COMP-GSD-4",
6
+ "_roadmap": "COMP-GSD-6",
7
+ "title": "GsdStateContracts",
8
+ "description": "COMP-GSD-6 run-state artifacts. `state` is the continuously-flushed checkpoint at .compose/gsd/<feature>/state.json — written pre-plan (phase:planning, flowId:null), updated with flowId after stratum.plan, heartbeat-bumped per task event, marked resumeReady once decomposedTasks is populated, and terminally flushed (complete|stuck|budget|failed). A hard crash leaves status:running + a dead pid, which readers derive as 'crashed'. `query` is the read-only envelope emitted by `compose gsd query` — it adds the reader-only derived statuses 'crashed' and 'absent' to the runner taxonomy, plus the advisory heartbeatStale flag. decomposedTasks stores FULL enriched task objects (id/depends_on/description), not ids, so crash-recovery can synthesize a resume graph (it reuses the gsd-stuck.json#/definitions/pause decomposedTasks shape).",
9
+ "definitions": {
10
+ "state": {
11
+ "type": "object",
12
+ "title": "GsdRunState",
13
+ "description": "The continuously-flushed run checkpoint. Status is what the RUNNER writes; readers derive 'crashed'/'absent' from this via deriveRunStatus (lib/gsd-state.js).",
14
+ "required": ["feature", "pid", "mode", "phase", "status", "heartbeatAt"],
15
+ "additionalProperties": true,
16
+ "properties": {
17
+ "feature": {
18
+ "type": "string",
19
+ "description": "Feature code the gsd run is building (e.g. COMP-GSD-6)."
20
+ },
21
+ "flowId": {
22
+ "type": ["string", "null"],
23
+ "description": "Stratum flow id. null in the pre-plan 'planning' checkpoint; set after stratum.plan returns."
24
+ },
25
+ "pid": {
26
+ "type": "integer",
27
+ "description": "OS pid of the running process. The authoritative crash signal: status:running + a dead pid (process.kill(pid,0) ESRCH) ⇒ crashed."
28
+ },
29
+ "mode": {
30
+ "type": "string",
31
+ "const": "gsd",
32
+ "description": "Run mode. Always 'gsd'."
33
+ },
34
+ "phase": {
35
+ "type": "string",
36
+ "enum": ["planning", "decompose", "execute", "ship", "done"],
37
+ "description": "Coarse lifecycle phase. 'planning' is the pre-plan checkpoint (before stratum.plan); the failed-vs-fatal boundary is whether this checkpoint exists."
38
+ },
39
+ "status": {
40
+ "type": "string",
41
+ "enum": ["running", "complete", "stuck", "budget", "failed"],
42
+ "description": "Runner-written status. 'failed' = an orderly exception AFTER the planning checkpoint (the dispatch-try catch). 'running' persisted with a dead pid is derived as 'crashed' by readers (never self-written)."
43
+ },
44
+ "startedAt": {
45
+ "type": "string",
46
+ "format": "date-time",
47
+ "description": "ISO-8601 timestamp the run started."
48
+ },
49
+ "heartbeatAt": {
50
+ "type": "string",
51
+ "format": "date-time",
52
+ "description": "ISO-8601 timestamp bumped on every flush AND from the per-task push-event path. A stale heartbeat on a LIVE pid is advisory only (heartbeatStale) — never a crash verdict."
53
+ },
54
+ "headless": {
55
+ "type": "boolean",
56
+ "description": "True if launched under the --headless supervisor."
57
+ },
58
+ "attempt": {
59
+ "type": "integer",
60
+ "minimum": 1,
61
+ "description": "Supervisor attempt counter (resume generation)."
62
+ },
63
+ "resumeReady": {
64
+ "type": "boolean",
65
+ "description": "True once decomposedTasks is flushed post-decompose. Gates the supervisor crash branch: --resume when true, fresh restart when false (crashed during plan/decompose — nothing merged yet)."
66
+ },
67
+ "decomposedTasks": {
68
+ "type": "array",
69
+ "description": "Full enriched task objects (id/depends_on/description, NOT bare ids), so the crash bridge can synthesize a resume graph. Empty until decompose completes.",
70
+ "items": { "type": "object" }
71
+ },
72
+ "completedTaskIds": {
73
+ "type": "array",
74
+ "description": "Task ids whose validated result is already merged/in the blackboard. Resume skips these.",
75
+ "items": { "type": "string" }
76
+ },
77
+ "lastTaskId": {
78
+ "type": ["string", "null"],
79
+ "description": "Most recently completed task id (progress hint)."
80
+ },
81
+ "lastStepId": {
82
+ "type": ["string", "null"],
83
+ "description": "Most recent stratum step id."
84
+ },
85
+ "budget": {
86
+ "type": "object",
87
+ "description": "Optional mirror of the live flow budget {caps,consumed} for query burn reporting.",
88
+ "additionalProperties": true
89
+ }
90
+ }
91
+ },
92
+ "query": {
93
+ "type": "object",
94
+ "title": "GsdQuerySnapshot",
95
+ "description": "Read-only envelope emitted by `compose gsd query <feature>`. Synthesized with a fixed source precedence (state.json → pause.json → budget.json → absent). One status vocabulary across query/supervisor/tests.",
96
+ "required": ["feature", "status"],
97
+ "additionalProperties": true,
98
+ "properties": {
99
+ "feature": { "type": "string", "description": "Feature code queried." },
100
+ "status": {
101
+ "type": "string",
102
+ "enum": ["running", "crashed", "complete", "stuck", "budget", "failed", "absent"],
103
+ "description": "Derived status. 'crashed' (running+dead-pid) and 'absent' (no run state anywhere) are reader-only; the rest mirror the runner taxonomy."
104
+ },
105
+ "phase": {
106
+ "type": ["string", "null"],
107
+ "enum": ["planning", "decompose", "execute", "ship", "done", null]
108
+ },
109
+ "heartbeatStale": {
110
+ "type": "boolean",
111
+ "description": "Advisory: status is 'running' on a live pid but the heartbeat exceeded the stale threshold (run may be wedged). Never implies crashed."
112
+ },
113
+ "progress": {
114
+ "type": "object",
115
+ "description": "Task progress.",
116
+ "properties": {
117
+ "completed": { "type": "integer", "minimum": 0 },
118
+ "total": { "type": "integer", "minimum": 0 }
119
+ },
120
+ "additionalProperties": false
121
+ },
122
+ "resumeReady": {
123
+ "type": "boolean",
124
+ "description": "Whether a crash here would --resume (true) or restart fresh (false)."
125
+ },
126
+ "pid": { "type": ["integer", "null"] },
127
+ "flowId": { "type": ["string", "null"] },
128
+ "pause": {
129
+ "type": ["object", "null"],
130
+ "description": "When paused: the pause kind + detail (from pause.json)."
131
+ },
132
+ "budget": {
133
+ "type": ["object", "null"],
134
+ "description": "Cumulative/flow budget info when available (from state.json or budget.json)."
135
+ },
136
+ "heartbeatAt": { "type": ["string", "null"], "format": "date-time" }
137
+ }
138
+ }
139
+ }
140
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "par-merge-bounce.json",
4
+ "_source": "docs/features/COMP-PAR-MERGE-QUEUE/design.md",
5
+ "_roadmap": "COMP-PAR-MERGE-QUEUE",
6
+ "title": "ParMergeBounce",
7
+ "description": "A single bounce record produced by parallel_dispatch's post-dispatch merge gate. Unifies both failure kinds (a failed pre-merge verify gate, or a merge conflict during diff apply) into one structured contract. It is surfaced on the ensure_failed.bounced_tasks[] envelope AND persisted onto the failed task's state so that when the step is re-dispatched, Stratum's ParallelExecutor._render_prompt injects the failure context into the re-run task's prompt (server-side, because the server re-resolves the task list on each re-dispatch). Convention: _source = design doc origin, _roadmap = feature code.",
8
+ "type": "object",
9
+ "required": ["task_id", "reason", "files", "excerpt"],
10
+ "properties": {
11
+ "task_id": {
12
+ "type": "string",
13
+ "description": "Which task to bounce / re-dispatch."
14
+ },
15
+ "reason": {
16
+ "type": "string",
17
+ "enum": ["gate_failed", "merge_conflict"],
18
+ "description": "Why the task bounced. 'gate_failed' = a pre_merge_verify command exited non-zero in the task worktree; 'merge_conflict' = git apply --check/apply failed when merging the task diff onto base."
19
+ },
20
+ "files": {
21
+ "type": "array",
22
+ "items": { "type": "string" },
23
+ "description": "Files involved. gate_failed: from `git diff --name-only HEAD` in the worktree. merge_conflict: parsed from the git-apply error, falling back to the task's owned files."
24
+ },
25
+ "command": {
26
+ "type": ["string", "null"],
27
+ "description": "The gate command that failed, e.g. 'pnpm build'. Populated for gate_failed only; null for merge_conflict."
28
+ },
29
+ "exit_code": {
30
+ "type": ["integer", "null"],
31
+ "description": "Exit code of the failed gate command. Populated for gate_failed only; null for merge_conflict."
32
+ },
33
+ "excerpt": {
34
+ "type": "string",
35
+ "description": "Bounded (~2KB) tail of the gate stdout+stderr (gate_failed) OR the git-apply error text (merge_conflict). Bounded to avoid unbounded prompt growth and to limit credential leakage; do not echo environment."
36
+ }
37
+ },
38
+ "additionalProperties": true
39
+ }