smol-symphony 0.1.0 → 0.2.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.
Files changed (140) hide show
  1. package/AGENTS.md +105 -38
  2. package/PRODUCT.md +2 -1
  3. package/README.md +195 -98
  4. package/SPEC.md +543 -1915
  5. package/WORKFLOW.md +654 -179
  6. package/WORKFLOW.template.md +761 -121
  7. package/dist/acp-bridge.js +324 -0
  8. package/dist/acp-bridge.js.map +1 -0
  9. package/dist/actions/cache.js +191 -0
  10. package/dist/actions/cache.js.map +1 -0
  11. package/dist/actions/effects.js +41 -0
  12. package/dist/actions/effects.js.map +1 -0
  13. package/dist/actions/executor.js +570 -0
  14. package/dist/actions/executor.js.map +1 -0
  15. package/dist/actions/index.js +13 -0
  16. package/dist/actions/index.js.map +1 -0
  17. package/dist/actions/parsing.js +273 -0
  18. package/dist/actions/parsing.js.map +1 -0
  19. package/dist/actions/predicate-env.js +27 -0
  20. package/dist/actions/predicate-env.js.map +1 -0
  21. package/dist/actions/predicates.js +49 -0
  22. package/dist/actions/predicates.js.map +1 -0
  23. package/dist/actions/templating.js +66 -0
  24. package/dist/actions/templating.js.map +1 -0
  25. package/dist/actions/types.js +15 -0
  26. package/dist/actions/types.js.map +1 -0
  27. package/dist/agent/acp.js +232 -63
  28. package/dist/agent/acp.js.map +1 -1
  29. package/dist/agent/adapter-names.js +159 -0
  30. package/dist/agent/adapter-names.js.map +1 -0
  31. package/dist/agent/adapters.js +338 -102
  32. package/dist/agent/adapters.js.map +1 -1
  33. package/dist/agent/credential-extractors.js +342 -0
  34. package/dist/agent/credential-extractors.js.map +1 -0
  35. package/dist/agent/credential-secrets.js +628 -0
  36. package/dist/agent/credential-secrets.js.map +1 -0
  37. package/dist/agent/credential-ticker.js +57 -0
  38. package/dist/agent/credential-ticker.js.map +1 -0
  39. package/dist/agent/gondolin-creds-staging.js +356 -0
  40. package/dist/agent/gondolin-creds-staging.js.map +1 -0
  41. package/dist/agent/gondolin-dispatch.js +375 -0
  42. package/dist/agent/gondolin-dispatch.js.map +1 -0
  43. package/dist/agent/gondolin.js +124 -0
  44. package/dist/agent/gondolin.js.map +1 -0
  45. package/dist/agent/runner-decisions.js +134 -0
  46. package/dist/agent/runner-decisions.js.map +1 -0
  47. package/dist/agent/runner.js +1352 -290
  48. package/dist/agent/runner.js.map +1 -1
  49. package/dist/agent/tool-call-summary.js +102 -0
  50. package/dist/agent/tool-call-summary.js.map +1 -0
  51. package/dist/agent/vm-acp-mapping.js +73 -0
  52. package/dist/agent/vm-acp-mapping.js.map +1 -0
  53. package/dist/agent/vm-guards.js +262 -0
  54. package/dist/agent/vm-guards.js.map +1 -0
  55. package/dist/agent/vm-port.js +22 -0
  56. package/dist/agent/vm-port.js.map +1 -0
  57. package/dist/agent/vm-process-registry.js +79 -0
  58. package/dist/agent/vm-process-registry.js.map +1 -0
  59. package/dist/bin/cli-args.js +105 -0
  60. package/dist/bin/cli-args.js.map +1 -0
  61. package/dist/bin/symphony.js +719 -130
  62. package/dist/bin/symphony.js.map +1 -1
  63. package/dist/errors.js +15 -0
  64. package/dist/errors.js.map +1 -0
  65. package/dist/http-disk.js +135 -0
  66. package/dist/http-disk.js.map +1 -0
  67. package/dist/http-handlers.js +180 -0
  68. package/dist/http-handlers.js.map +1 -0
  69. package/dist/http.js +1476 -764
  70. package/dist/http.js.map +1 -1
  71. package/dist/issues.js +178 -0
  72. package/dist/issues.js.map +1 -0
  73. package/dist/logging.js +163 -5
  74. package/dist/logging.js.map +1 -1
  75. package/dist/mcp.js +391 -163
  76. package/dist/mcp.js.map +1 -1
  77. package/dist/memory.js +85 -0
  78. package/dist/memory.js.map +1 -0
  79. package/dist/orchestrator-decisions.js +331 -0
  80. package/dist/orchestrator-decisions.js.map +1 -0
  81. package/dist/orchestrator.js +1189 -303
  82. package/dist/orchestrator.js.map +1 -1
  83. package/dist/prompt.js +5 -5
  84. package/dist/prompt.js.map +1 -1
  85. package/dist/reconciler/cache.js +65 -0
  86. package/dist/reconciler/cache.js.map +1 -0
  87. package/dist/reconciler/index.js +448 -0
  88. package/dist/reconciler/index.js.map +1 -0
  89. package/dist/reconciler/ledger.js +131 -0
  90. package/dist/reconciler/ledger.js.map +1 -0
  91. package/dist/reconciler/pr-adapters.js +174 -0
  92. package/dist/reconciler/pr-adapters.js.map +1 -0
  93. package/dist/reconciler/pr-decide.js +167 -0
  94. package/dist/reconciler/pr-decide.js.map +1 -0
  95. package/dist/reconciler/pr.js +422 -0
  96. package/dist/reconciler/pr.js.map +1 -0
  97. package/dist/reconciler/types.js +12 -0
  98. package/dist/reconciler/types.js.map +1 -0
  99. package/dist/reconciler/vm.js +243 -0
  100. package/dist/reconciler/vm.js.map +1 -0
  101. package/dist/reconciler/workspace-defaults.js +83 -0
  102. package/dist/reconciler/workspace-defaults.js.map +1 -0
  103. package/dist/reconciler/workspace.js +272 -0
  104. package/dist/reconciler/workspace.js.map +1 -0
  105. package/dist/runlog.js +403 -0
  106. package/dist/runlog.js.map +1 -0
  107. package/dist/scaffold.js +165 -0
  108. package/dist/scaffold.js.map +1 -0
  109. package/dist/trackers/local.js +234 -133
  110. package/dist/trackers/local.js.map +1 -1
  111. package/dist/trackers/types.js +1 -1
  112. package/dist/trackers/types.js.map +1 -1
  113. package/dist/types.js +1 -1
  114. package/dist/util/clock.js +12 -0
  115. package/dist/util/clock.js.map +1 -0
  116. package/dist/util/crypto.js +25 -0
  117. package/dist/util/crypto.js.map +1 -0
  118. package/dist/util/frontmatter.js +70 -0
  119. package/dist/util/frontmatter.js.map +1 -0
  120. package/dist/util/fs-issues.js +22 -0
  121. package/dist/util/fs-issues.js.map +1 -0
  122. package/dist/util/process.js +152 -0
  123. package/dist/util/process.js.map +1 -0
  124. package/dist/util/workspace-key.js +10 -0
  125. package/dist/util/workspace-key.js.map +1 -0
  126. package/dist/workflow-loader.js +147 -0
  127. package/dist/workflow-loader.js.map +1 -0
  128. package/dist/workflow.js +656 -219
  129. package/dist/workflow.js.map +1 -1
  130. package/dist/workspace-types.js +8 -0
  131. package/dist/workspace-types.js.map +1 -0
  132. package/dist/workspace.js +367 -120
  133. package/dist/workspace.js.map +1 -1
  134. package/package.json +14 -6
  135. package/scripts/vm-agent.mjs +211 -0
  136. package/dist/agent/codex.js +0 -439
  137. package/dist/agent/codex.js.map +0 -1
  138. package/dist/agent/smolvm.js +0 -174
  139. package/dist/agent/smolvm.js.map +0 -1
  140. package/scripts/build-vm.sh +0 -67
@@ -0,0 +1,273 @@
1
+ // Parse a per-state `actions:` block into typed records (issue 36).
2
+ //
3
+ // Lifts the YAML shape into the closed `WorkflowAction` union, validating
4
+ // each kind's required fields up-front so a missing `head:` on
5
+ // `create_pr_if_missing` (or a misspelled `kind:`) fails at workflow load
6
+ // instead of at the first dispatch into the terminal state.
7
+ import { WorkflowError } from '../errors.js';
8
+ const KNOWN_KINDS = [
9
+ 'push_branch',
10
+ 'create_pr_if_missing',
11
+ 'ensure_branch',
12
+ 'checkout',
13
+ 'merge',
14
+ 'delete_branch',
15
+ 'run_in_vm',
16
+ 'propose_followup',
17
+ ];
18
+ function asString(v) {
19
+ return typeof v === 'string' ? v : null;
20
+ }
21
+ function asInt(v) {
22
+ if (typeof v === 'number' && Number.isFinite(v))
23
+ return Math.trunc(v);
24
+ if (typeof v === 'string' && /^-?\d+$/.test(v))
25
+ return parseInt(v, 10);
26
+ return null;
27
+ }
28
+ function expectString(stateName, idx, kind, key, v) {
29
+ const s = asString(v);
30
+ if (s === null || s.length === 0) {
31
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (${kind}): "${key}" must be a non-empty string`);
32
+ }
33
+ return s;
34
+ }
35
+ function parsePredicate(stateName, idx, raw) {
36
+ if (raw === undefined || raw === null)
37
+ return null;
38
+ if (typeof raw === 'string')
39
+ return raw;
40
+ if (typeof raw === 'object' && !Array.isArray(raw)) {
41
+ const m = raw;
42
+ if (typeof m.branch_exists === 'string')
43
+ return { branch_exists: m.branch_exists };
44
+ if (typeof m.file_present === 'string')
45
+ return { file_present: m.file_present };
46
+ }
47
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] "if" must be a "$var" string, {branch_exists: <ref>}, or {file_present: <path>}`);
48
+ }
49
+ function parseErrorPolicy(stateName, idx, raw) {
50
+ if (raw === undefined || raw === null)
51
+ return undefined;
52
+ if (typeof raw !== 'object' || Array.isArray(raw)) {
53
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] "on_error" must be a map`);
54
+ }
55
+ const m = raw;
56
+ const out = {};
57
+ if (m.retry !== undefined) {
58
+ if (typeof m.retry !== 'object' || m.retry === null || Array.isArray(m.retry)) {
59
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] "on_error.retry" must be a map`);
60
+ }
61
+ const r = m.retry;
62
+ const count = asInt(r.count);
63
+ const backoff = asInt(r.backoff_ms);
64
+ if (count === null || count < 0) {
65
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] "on_error.retry.count" must be a non-negative integer`);
66
+ }
67
+ if (backoff === null || backoff < 0) {
68
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] "on_error.retry.backoff_ms" must be a non-negative integer`);
69
+ }
70
+ out.retry = { count, backoff_ms: backoff };
71
+ }
72
+ if (m.then !== undefined) {
73
+ if (m.then === 'abort') {
74
+ out.then = 'abort';
75
+ }
76
+ else if (typeof m.then === 'object' &&
77
+ !Array.isArray(m.then) &&
78
+ typeof m.then.route_to === 'string') {
79
+ out.then = { route_to: m.then.route_to };
80
+ }
81
+ else {
82
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] "on_error.then" must be "abort" or {route_to: <state>}`);
83
+ }
84
+ }
85
+ return out;
86
+ }
87
+ function parseAction(stateName, idx, raw) {
88
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw)) {
89
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] must be a map with a "kind" field`);
90
+ }
91
+ const m = raw;
92
+ const kind = asString(m.kind);
93
+ if (!kind || !KNOWN_KINDS.includes(kind)) {
94
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] "kind" must be one of: ${KNOWN_KINDS.join(', ')} (got: ${String(m.kind)})`);
95
+ }
96
+ const base = {
97
+ name: asString(m.name) ?? undefined,
98
+ if: parsePredicate(stateName, idx, m.if),
99
+ on_error: parseErrorPolicy(stateName, idx, m.on_error),
100
+ };
101
+ // Strip undefined to keep parsed records clean (deep-equal in tests).
102
+ const cleanBase = {};
103
+ if (base.name !== undefined)
104
+ cleanBase.name = base.name;
105
+ if (base.if !== null)
106
+ cleanBase.if = base.if;
107
+ if (base.on_error !== undefined)
108
+ cleanBase.on_error = base.on_error;
109
+ switch (kind) {
110
+ case 'push_branch':
111
+ return {
112
+ kind: 'push_branch',
113
+ remote: expectString(stateName, idx, kind, 'remote', m.remote),
114
+ ref: expectString(stateName, idx, kind, 'ref', m.ref),
115
+ ...cleanBase,
116
+ };
117
+ case 'create_pr_if_missing':
118
+ return {
119
+ kind: 'create_pr_if_missing',
120
+ base: expectString(stateName, idx, kind, 'base', m.base),
121
+ head: expectString(stateName, idx, kind, 'head', m.head),
122
+ title_from: expectString(stateName, idx, kind, 'title_from', m.title_from),
123
+ body_from: expectString(stateName, idx, kind, 'body_from', m.body_from),
124
+ ...cleanBase,
125
+ };
126
+ case 'ensure_branch':
127
+ return {
128
+ kind: 'ensure_branch',
129
+ name: expectString(stateName, idx, kind, 'name', m.name),
130
+ seed_from: asString(m.seed_from) ?? undefined,
131
+ ...cleanBase,
132
+ };
133
+ case 'checkout':
134
+ return {
135
+ kind: 'checkout',
136
+ ref: expectString(stateName, idx, kind, 'ref', m.ref),
137
+ ...cleanBase,
138
+ };
139
+ case 'merge': {
140
+ const onConflictRaw = m.on_conflict;
141
+ let onConflict;
142
+ if (onConflictRaw === 'abort') {
143
+ onConflict = 'abort';
144
+ }
145
+ else if (typeof onConflictRaw === 'object' &&
146
+ onConflictRaw !== null &&
147
+ !Array.isArray(onConflictRaw) &&
148
+ typeof onConflictRaw.route_to === 'string') {
149
+ onConflict = { route_to: onConflictRaw.route_to };
150
+ }
151
+ else {
152
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (merge): "on_conflict" must be "abort" or {route_to: <state>}`);
153
+ }
154
+ return {
155
+ kind: 'merge',
156
+ source: expectString(stateName, idx, kind, 'source', m.source),
157
+ target: expectString(stateName, idx, kind, 'target', m.target),
158
+ on_conflict: onConflict,
159
+ ...cleanBase,
160
+ };
161
+ }
162
+ case 'delete_branch': {
163
+ const scope = asString(m.scope);
164
+ if (scope !== 'local' && scope !== 'remote' && scope !== 'both') {
165
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (delete_branch): "scope" must be one of local|remote|both`);
166
+ }
167
+ const remote = asString(m.remote) ?? undefined;
168
+ if ((scope === 'remote' || scope === 'both') && !remote) {
169
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (delete_branch): "remote" is required when scope includes "remote"`);
170
+ }
171
+ return {
172
+ kind: 'delete_branch',
173
+ name: expectString(stateName, idx, kind, 'name', m.name),
174
+ scope,
175
+ remote,
176
+ ...cleanBase,
177
+ };
178
+ }
179
+ case 'run_in_vm': {
180
+ const name = asString(m.name);
181
+ if (!name || name.length === 0) {
182
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (run_in_vm): "name" is required (used by cache + rerun CLI)`);
183
+ }
184
+ // Names are used as a path segment in the cache layout
185
+ // (`<root>/actions/run_in_vm/<name>/<hash>/result.json`). Restrict to a
186
+ // safe identifier set so `--check=<name>` is unambiguous and no name
187
+ // can escape the namespace (e.g. via `..`). The cache layer also
188
+ // defensively encodes unsafe chars, but rejecting at parse time keeps
189
+ // the CLI surface honest.
190
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) {
191
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (run_in_vm): "name" must match /^[A-Za-z0-9][A-Za-z0-9._-]*$/ (got: ${JSON.stringify(name)})`);
192
+ }
193
+ const cmdRaw = m.cmd;
194
+ if (!Array.isArray(cmdRaw) || cmdRaw.length === 0 || !cmdRaw.every((s) => typeof s === 'string')) {
195
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (run_in_vm): "cmd" must be a non-empty list of strings`);
196
+ }
197
+ const envRaw = m.env;
198
+ let env;
199
+ if (envRaw !== undefined) {
200
+ if (typeof envRaw !== 'object' || envRaw === null || Array.isArray(envRaw)) {
201
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (run_in_vm): "env" must be a map of string→string`);
202
+ }
203
+ env = {};
204
+ for (const [k, v] of Object.entries(envRaw)) {
205
+ if (typeof v !== 'string') {
206
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (run_in_vm): env["${k}"] must be a string`);
207
+ }
208
+ env[k] = v;
209
+ }
210
+ }
211
+ const timeout = m.timeout === undefined ? undefined : asInt(m.timeout);
212
+ if (m.timeout !== undefined && (timeout === null || timeout === undefined || timeout <= 0)) {
213
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (run_in_vm): "timeout" must be a positive integer (seconds)`);
214
+ }
215
+ const action = {
216
+ kind: 'run_in_vm',
217
+ name,
218
+ cmd: cmdRaw,
219
+ ...cleanBase,
220
+ };
221
+ if (env !== undefined)
222
+ action.env = env;
223
+ if (timeout !== undefined && timeout !== null)
224
+ action.timeout = timeout;
225
+ return action;
226
+ }
227
+ case 'propose_followup': {
228
+ const labelsRaw = m.labels;
229
+ let labels;
230
+ if (labelsRaw !== undefined) {
231
+ if (!Array.isArray(labelsRaw) || !labelsRaw.every((s) => typeof s === 'string')) {
232
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (propose_followup): "labels" must be a list of strings`);
233
+ }
234
+ labels = labelsRaw;
235
+ }
236
+ const priority = m.priority === undefined ? undefined : asInt(m.priority);
237
+ if (m.priority !== undefined && priority === null) {
238
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions[${idx}] (propose_followup): "priority" must be an integer`);
239
+ }
240
+ const action = {
241
+ kind: 'propose_followup',
242
+ title: expectString(stateName, idx, kind, 'title', m.title),
243
+ ...cleanBase,
244
+ };
245
+ const body = asString(m.body);
246
+ if (body !== null)
247
+ action.body = body;
248
+ if (labels !== undefined)
249
+ action.labels = labels;
250
+ if (priority !== undefined && priority !== null)
251
+ action.priority = priority;
252
+ return action;
253
+ }
254
+ default: {
255
+ // Exhaustiveness check at the type level — adding a new kind to the union
256
+ // without extending this switch is a TypeScript error.
257
+ const _exhaustive = kind;
258
+ void _exhaustive;
259
+ throw new WorkflowError('workflow_parse_error', `unreachable action kind: ${String(kind)}`);
260
+ }
261
+ }
262
+ }
263
+ export function parseActionsBlock(stateName, raw) {
264
+ if (raw === undefined)
265
+ return undefined;
266
+ if (raw === null)
267
+ return [];
268
+ if (!Array.isArray(raw)) {
269
+ throw new WorkflowError('workflow_parse_error', `state "${stateName}": actions must be a list of typed action records`);
270
+ }
271
+ return raw.map((entry, idx) => parseAction(stateName, idx, entry));
272
+ }
273
+ //# sourceMappingURL=parsing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsing.js","sourceRoot":"","sources":["../../src/actions/parsing.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,0EAA0E;AAC1E,+DAA+D;AAC/D,0EAA0E;AAC1E,4DAA4D;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAQ7C,MAAM,WAAW,GAAkC;IACjD,aAAa;IACb,sBAAsB;IACtB,eAAe;IACf,UAAU;IACV,OAAO;IACP,eAAe;IACf,WAAW;IACX,kBAAkB;CACV,CAAC;AAEX,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1C,CAAC;AAED,SAAS,KAAK,CAAC,CAAU;IACvB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACtE,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACvE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,YAAY,CAAC,SAAiB,EAAE,GAAW,EAAE,IAAY,EAAE,GAAW,EAAE,CAAU;IACzF,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,MAAM,IAAI,OAAO,GAAG,8BAA8B,CACvF,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,cAAc,CAAC,SAAiB,EAAE,GAAW,EAAE,GAAY;IAClE,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD,MAAM,CAAC,GAAG,GAA8B,CAAC;QACzC,IAAI,OAAO,CAAC,CAAC,aAAa,KAAK,QAAQ;YAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC;QACnF,IAAI,OAAO,CAAC,CAAC,YAAY,KAAK,QAAQ;YAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC;IAClF,CAAC;IACD,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,mFAAmF,CACxH,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,SAAiB,EAAE,GAAW,EAAE,GAAY;IACpE,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACxD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClD,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,4BAA4B,CACjE,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,IAAI,CAAC,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC1B,IAAI,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,kCAAkC,CACvE,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,CAAC,KAAgC,CAAC;QAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QACpC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,yDAAyD,CAC9F,CAAC;QACJ,CAAC;QACD,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,8DAA8D,CACnG,CAAC;QACJ,CAAC;QACD,GAAG,CAAC,KAAK,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;IAC7C,CAAC;IACD,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,GAAG,OAAO,CAAC;QACrB,CAAC;aAAM,IACL,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;YAC1B,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;YACtB,OAAQ,CAAC,CAAC,IAAgC,CAAC,QAAQ,KAAK,QAAQ,EAChE,CAAC;YACD,GAAG,CAAC,IAAI,GAAG,EAAE,QAAQ,EAAG,CAAC,CAAC,IAA+B,CAAC,QAAS,EAAE,CAAC;QACxE,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,0DAA0D,CAC/F,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,SAAiB,EAAE,GAAW,EAAE,GAAY;IAC/D,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,qCAAqC,CAC1E,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,IAAI,IAAI,CAAE,WAAiC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,4BAA4B,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAClH,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG;QACX,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,SAAS;QACnC,EAAE,EAAE,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC;QACxC,QAAQ,EAAE,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,QAAQ,CAAC;KACvD,CAAC;IACF,sEAAsE;IACtE,MAAM,SAAS,GAA4B,EAAE,CAAC;IAC9C,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;QAAE,SAAS,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACxD,IAAI,IAAI,CAAC,EAAE,KAAK,IAAI;QAAE,SAAS,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;IAC7C,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;QAAE,SAAS,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IAEpE,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,aAAa;YAChB,OAAO;gBACL,IAAI,EAAE,aAAa;gBACnB,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;gBAC9D,GAAG,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC;gBACrD,GAAG,SAAS;aACb,CAAC;QACJ,KAAK,sBAAsB;YACzB,OAAO;gBACL,IAAI,EAAE,sBAAsB;gBAC5B,IAAI,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC;gBACxD,IAAI,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC;gBACxD,UAAU,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC,UAAU,CAAC;gBAC1E,SAAS,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,SAAS,CAAC;gBACvE,GAAG,SAAS;aACb,CAAC;QACJ,KAAK,eAAe;YAClB,OAAO;gBACL,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC;gBACxD,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,SAAS;gBAC7C,GAAG,SAAS;aACK,CAAC;QACtB,KAAK,UAAU;YACb,OAAO;gBACL,IAAI,EAAE,UAAU;gBAChB,GAAG,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC;gBACrD,GAAG,SAAS;aACb,CAAC;QACJ,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,aAAa,GAAG,CAAC,CAAC,WAAW,CAAC;YACpC,IAAI,UAA0C,CAAC;YAC/C,IAAI,aAAa,KAAK,OAAO,EAAE,CAAC;gBAC9B,UAAU,GAAG,OAAO,CAAC;YACvB,CAAC;iBAAM,IACL,OAAO,aAAa,KAAK,QAAQ;gBACjC,aAAa,KAAK,IAAI;gBACtB,CAAC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;gBAC7B,OAAQ,aAAyC,CAAC,QAAQ,KAAK,QAAQ,EACvE,CAAC;gBACD,UAAU,GAAG,EAAE,QAAQ,EAAG,aAAwC,CAAC,QAAS,EAAE,CAAC;YACjF,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,iEAAiE,CACtG,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;gBAC9D,MAAM,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;gBAC9D,WAAW,EAAE,UAAU;gBACvB,GAAG,SAAS;aACb,CAAC;QACJ,CAAC;QACD,KAAK,eAAe,CAAC,CAAC,CAAC;YACrB,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;gBAChE,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,6DAA6D,CAClG,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;YAC/C,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACxD,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,sEAAsE,CAC3G,CAAC;YACJ,CAAC;YACD,OAAO;gBACL,IAAI,EAAE,eAAe;gBACrB,IAAI,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC;gBACxD,KAAK;gBACL,MAAM;gBACN,GAAG,SAAS;aACK,CAAC;QACtB,CAAC;QACD,KAAK,WAAW,CAAC,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,+DAA+D,CACpG,CAAC;YACJ,CAAC;YACD,uDAAuD;YACvD,wEAAwE;YACxE,qEAAqE;YACrE,iEAAiE;YACjE,sEAAsE;YACtE,0BAA0B;YAC1B,IAAI,CAAC,8BAA8B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/C,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,yEAAyE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CACrI,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC;YACrB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;gBACjG,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,0DAA0D,CAC/F,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC;YACrB,IAAI,GAAuC,CAAC;YAC5C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC3E,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,qDAAqD,CAC1F,CAAC;gBACJ,CAAC;gBACD,GAAG,GAAG,EAAE,CAAC;gBACT,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAiC,CAAC,EAAE,CAAC;oBACvE,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;wBAC1B,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,uBAAuB,CAAC,qBAAqB,CAClF,CAAC;oBACJ,CAAC;oBACD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;gBACb,CAAC;YACH,CAAC;YACD,MAAM,OAAO,GACX,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;YACzD,IAAI,CAAC,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,CAAC,EAAE,CAAC;gBAC3F,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,+DAA+D,CACpG,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAuC;gBACjD,IAAI,EAAE,WAAW;gBACjB,IAAI;gBACJ,GAAG,EAAE,MAAkB;gBACvB,GAAG,SAAS;aACb,CAAC;YACF,IAAI,GAAG,KAAK,SAAS;gBAAE,MAAM,CAAC,GAAG,GAAG,GAAG,CAAC;YACxC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI;gBAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;YACxE,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,KAAK,kBAAkB,CAAC,CAAC,CAAC;YACxB,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;YAC3B,IAAI,MAA4B,CAAC;YACjC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,EAAE,CAAC;oBAChF,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,0DAA0D,CAC/F,CAAC;gBACJ,CAAC;gBACD,MAAM,GAAG,SAAqB,CAAC;YACjC,CAAC;YACD,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC1E,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBAClD,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,cAAc,GAAG,qDAAqD,CAC1F,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAA+C;gBACzD,IAAI,EAAE,kBAAkB;gBACxB,KAAK,EAAE,YAAY,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC;gBAC3D,GAAG,SAAS;aACb,CAAC;YACF,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,IAAI,KAAK,IAAI;gBAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;YACtC,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC;YACjD,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI;gBAAE,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC5E,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,CAAC,CAAC,CAAC;YACR,0EAA0E;YAC1E,uDAAuD;YACvD,MAAM,WAAW,GAAU,IAAa,CAAC;YACzC,KAAK,WAAW,CAAC;YACjB,MAAM,IAAI,aAAa,CAAC,sBAAsB,EAAE,4BAA4B,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9F,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,SAAiB,EAAE,GAAY;IAC/D,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,CAAC;IAC5B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,aAAa,CACrB,sBAAsB,EACtB,UAAU,SAAS,mDAAmD,CACvE,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,WAAW,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,27 @@
1
+ // Production-side adapter for `PredicateEnv` (issue 96). Backs
2
+ // `branchExists` with `git rev-parse --verify --quiet` (via the unified
3
+ // `runProcess`) and `pathExists` with `fs.stat`. Lives outside the
4
+ // functional-core lint group so `src/actions/predicates.ts` can stay free of
5
+ // `node:fs/promises` and `runProcess` direct imports; the runner threads
6
+ // `defaultPredicateEnv` into the executor.
7
+ import { stat } from 'node:fs/promises';
8
+ import { runProcess } from '../util/process.js';
9
+ export const defaultPredicateEnv = {
10
+ async branchExists(ref, workspacePath) {
11
+ // `--verify --quiet` keeps both streams empty on the happy path; the
12
+ // tiny default byte clamp is plenty and `appendErrorToStderr: false`
13
+ // skips stderr decoration for the missing-ref case (the common one).
14
+ const r = await runProcess('git', ['rev-parse', '--verify', '--quiet', `refs/heads/${ref}`], { cwd: workspacePath, appendErrorToStderr: false });
15
+ return r.exit_code === 0;
16
+ },
17
+ async pathExists(absPath) {
18
+ try {
19
+ const st = await stat(absPath);
20
+ return st.isFile() || st.isDirectory();
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ },
26
+ };
27
+ //# sourceMappingURL=predicate-env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predicate-env.js","sourceRoot":"","sources":["../../src/actions/predicate-env.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,wEAAwE;AACxE,mEAAmE;AACnE,6EAA6E;AAC7E,yEAAyE;AACzE,2CAA2C;AAE3C,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,MAAM,CAAC,MAAM,mBAAmB,GAAiB;IAC/C,KAAK,CAAC,YAAY,CAAC,GAAG,EAAE,aAAa;QACnC,qEAAqE;QACrE,qEAAqE;QACrE,qEAAqE;QACrE,MAAM,CAAC,GAAG,MAAM,UAAU,CACxB,KAAK,EACL,CAAC,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,cAAc,GAAG,EAAE,CAAC,EACzD,EAAE,GAAG,EAAE,aAAa,EAAE,mBAAmB,EAAE,KAAK,EAAE,CACnD,CAAC;QACF,OAAO,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC;IAC3B,CAAC;IACD,KAAK,CAAC,UAAU,CAAC,OAAO;QACtB,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/B,OAAO,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACzC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,49 @@
1
+ // `if:` predicate evaluation (issue 36).
2
+ //
3
+ // Three predicate shapes — env-var-truthy, branch-exists, file-present.
4
+ // These are the three predicates the hook shell actually exercised across
5
+ // today's WORKFLOW.md; the issue body explicitly caps the predicate set
6
+ // there: "If users want more, they've outgrown declarative."
7
+ import path from 'node:path';
8
+ import { renderTemplate } from './templating.js';
9
+ /**
10
+ * Evaluate a predicate against the context. `null`/undefined → always true
11
+ * (no `if:` → run unconditionally).
12
+ *
13
+ * The string form `"$var"` is a truthiness check on the named context field
14
+ * — `if: $repo` matches the issue body's example exactly. A bare literal
15
+ * (`if: yes`) is treated as truthy; the literal-false case `if: ""` /
16
+ * `if: null` falls through to "always".
17
+ *
18
+ * IO is routed through the injected `PredicateEnv` so the evaluator core
19
+ * stays pure (no `node:fs` / `runProcess` imports). String-truthy predicates
20
+ * never touch the env.
21
+ */
22
+ export async function evaluatePredicate(predicate, ctx, workspacePath, env) {
23
+ if (predicate === null || predicate === undefined)
24
+ return true;
25
+ if (typeof predicate === 'string') {
26
+ // Templates may already have been rendered by the executor's renderTree
27
+ // pass before evaluatePredicate is called; re-rendering an already-rendered
28
+ // empty string is a no-op. An empty (or empty-after-render) predicate is
29
+ // explicitly false — the intent of `if: $repo` with $repo unset is "skip
30
+ // when the var is empty," not "always run".
31
+ const expanded = renderTemplate(predicate, ctx).trim();
32
+ return expanded.length > 0;
33
+ }
34
+ if ('branch_exists' in predicate) {
35
+ const ref = renderTemplate(predicate.branch_exists, ctx).trim();
36
+ if (ref.length === 0)
37
+ return false;
38
+ return env.branchExists(ref, workspacePath);
39
+ }
40
+ if ('file_present' in predicate) {
41
+ const file = renderTemplate(predicate.file_present, ctx).trim();
42
+ if (file.length === 0)
43
+ return false;
44
+ const abs = path.isAbsolute(file) ? file : path.join(workspacePath, file);
45
+ return env.pathExists(abs);
46
+ }
47
+ return false;
48
+ }
49
+ //# sourceMappingURL=predicates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"predicates.js","sourceRoot":"","sources":["../../src/actions/predicates.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,wEAAwE;AACxE,0EAA0E;AAC1E,wEAAwE;AACxE,6DAA6D;AAE7D,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEjD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,SAAsC,EACtC,GAAkB,EAClB,aAAqB,EACrB,GAAiB;IAEjB,IAAI,SAAS,KAAK,IAAI,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC/D,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAClC,wEAAwE;QACxE,4EAA4E;QAC5E,yEAAyE;QACzE,yEAAyE;QACzE,4CAA4C;QAC5C,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACvD,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,CAAC;IACD,IAAI,eAAe,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,cAAc,CAAC,SAAS,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAChE,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACnC,OAAO,GAAG,CAAC,YAAY,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,cAAc,IAAI,SAAS,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,cAAc,CAAC,SAAS,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAChE,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACpC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;QAC1E,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,66 @@
1
+ // $-substitution against a fixed ActionContext namespace (issue 36).
2
+ //
3
+ // The issue body explicitly says "Mustache or `$`-substitution with a fixed
4
+ // namespace is enough; Bazel's Starlark is overkill." We pick `$identifier`
5
+ // over `${{ identifier }}` because the Done after_run already uses
6
+ // `$SYMPHONY_*` env-var shapes; sharing the prefix style keeps cognitive
7
+ // load low for operators converting hooks to actions.
8
+ //
9
+ // Behavior:
10
+ // • `$name` expands to the named ActionContext field.
11
+ // • An unknown `$name` throws — silent expansion to "" was the most common
12
+ // hook-shell bug (typo'd `$SYMPHONY_PR_TITTLE` is the canonical example).
13
+ // • Literal `$` followed by a non-word char is left alone.
14
+ // • `\$name` escapes the substitution (literal `$name` in output).
15
+ export class TemplateError extends Error {
16
+ name;
17
+ constructor(name, message) {
18
+ super(message);
19
+ this.name = name;
20
+ this.name = 'TemplateError';
21
+ }
22
+ }
23
+ const VAR_RE = /(\\?)\$([A-Za-z_][A-Za-z0-9_]*)/g;
24
+ /**
25
+ * Substitute `$name` references in `template` against the ActionContext.
26
+ * Throws TemplateError when a referenced variable is not in the fixed
27
+ * namespace — silent "" expansion masks operator typos.
28
+ */
29
+ export function renderTemplate(template, ctx) {
30
+ if (typeof template !== 'string') {
31
+ return template;
32
+ }
33
+ return template.replace(VAR_RE, (full, escape, name) => {
34
+ if (escape === '\\')
35
+ return `$${name}`;
36
+ if (!(name in ctx)) {
37
+ throw new TemplateError(name, `unknown template variable "$${name}" (available: ${Object.keys(ctx).join(', ')})`);
38
+ }
39
+ const v = ctx[name];
40
+ if (v === null || v === undefined)
41
+ return '';
42
+ return String(v);
43
+ });
44
+ }
45
+ /**
46
+ * Render every string field of an object. Used to render an action's
47
+ * declarative fields in one pass before execution. Recurses into arrays and
48
+ * plain objects; primitives other than strings (numbers, booleans) pass
49
+ * through unchanged.
50
+ */
51
+ export function renderTree(value, ctx) {
52
+ if (typeof value === 'string')
53
+ return renderTemplate(value, ctx);
54
+ if (Array.isArray(value)) {
55
+ return value.map((v) => renderTree(v, ctx));
56
+ }
57
+ if (value && typeof value === 'object') {
58
+ const out = {};
59
+ for (const [k, v] of Object.entries(value)) {
60
+ out[k] = renderTree(v, ctx);
61
+ }
62
+ return out;
63
+ }
64
+ return value;
65
+ }
66
+ //# sourceMappingURL=templating.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templating.js","sourceRoot":"","sources":["../../src/actions/templating.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,mEAAmE;AACnE,yEAAyE;AACzE,sDAAsD;AACtD,EAAE;AACF,YAAY;AACZ,wDAAwD;AACxD,6EAA6E;AAC7E,8EAA8E;AAC9E,6DAA6D;AAC7D,qEAAqE;AAIrE,MAAM,OAAO,aAAc,SAAQ,KAAK;IACnB;IAAnB,YAAmB,IAAY,EAAE,OAAe;QAC9C,KAAK,CAAC,OAAO,CAAC,CAAC;QADE,SAAI,GAAJ,IAAI,CAAQ;QAE7B,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;IAC9B,CAAC;CACF;AAED,MAAM,MAAM,GAAG,kCAAkC,CAAC;AAElD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,GAAkB;IACjE,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAc,EAAE,IAAY,EAAE,EAAE;QACrE,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,IAAI,IAAI,EAAE,CAAC;QACvC,IAAI,CAAC,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;YACnB,MAAM,IAAI,aAAa,CACrB,IAAI,EACJ,+BAA+B,IAAI,iBAAiB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CACnF,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,GAAI,GAA0C,CAAC,IAAI,CAAC,CAAC;QAC5D,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QAC7C,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAI,KAAQ,EAAE,GAAkB;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,cAAc,CAAC,KAAK,EAAE,GAAG,CAAiB,CAAC;IACjF,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAiB,CAAC;IAC9D,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,GAAmB,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,15 @@
1
+ // Workflow action records (issue 36 — reconciler v2 / typed action DAG).
2
+ //
3
+ // Per-state `actions:` blocks declare a closed set of typed records that the
4
+ // orchestrator runs in place of an `after_run` shell hook. Each kind has a
5
+ // fixed schema; the only escape hatch for arbitrary commands is `run_in_vm`,
6
+ // which is content-hash cached so identical (workspace, cmd, env) tuples
7
+ // don't re-execute.
8
+ //
9
+ // Action records are pure data; the executor (src/actions/executor.ts) is the
10
+ // one place that knows how to apply each kind. This mirrors the reconciler's
11
+ // `KillSessionAction`/`RemoveWorkspaceAction`/… pattern (records, not closures)
12
+ // so the snapshot machinery and the dashboard can render per-action state
13
+ // uniformly across both surfaces.
14
+ export {};
15
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/actions/types.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,oBAAoB;AACpB,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,gFAAgF;AAChF,0EAA0E;AAC1E,kCAAkC"}