@runcofactor/cli 0.10.0 → 0.13.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 (148) hide show
  1. package/dist/{api-catalog-GFGCLVS4.js → api-catalog-7HLEN7C2.js} +5 -4
  2. package/dist/{api-catalog-GFGCLVS4.js.map → api-catalog-7HLEN7C2.js.map} +1 -1
  3. package/dist/{attachment-SJC7D7FY.js → attachment-OVNQD6Z2.js} +5 -4
  4. package/dist/{attachment-SJC7D7FY.js.map → attachment-OVNQD6Z2.js.map} +1 -1
  5. package/dist/{auth-EN6NQCJY.js → auth-VUHL74FH.js} +3 -2
  6. package/dist/{chunk-IMPZAX6L.js → chunk-5GD47G2D.js} +37 -1
  7. package/dist/{chunk-IMPZAX6L.js.map → chunk-5GD47G2D.js.map} +1 -1
  8. package/dist/{chunk-WOHL4FTB.js → chunk-CQE46HSM.js} +5 -5
  9. package/dist/chunk-CQE46HSM.js.map +1 -0
  10. package/dist/{chunk-52IO7ACJ.js → chunk-CRHPDPRS.js} +39 -3
  11. package/dist/{chunk-52IO7ACJ.js.map → chunk-CRHPDPRS.js.map} +1 -1
  12. package/dist/{chunk-XQXVG7RA.js → chunk-EEN2TZUQ.js} +3 -3
  13. package/dist/{chunk-I4QLI3JO.js → chunk-EJ4U242G.js} +2 -2
  14. package/dist/{chunk-CLAWT7BJ.js → chunk-ENDHMYEJ.js} +3 -3
  15. package/dist/chunk-G4UYGRZR.js +2 -0
  16. package/dist/chunk-JZMJUPZP.js +92 -0
  17. package/dist/chunk-JZMJUPZP.js.map +1 -0
  18. package/dist/{chunk-GKY2KXO5.js → chunk-NL7CVG56.js} +105 -35
  19. package/dist/chunk-NL7CVG56.js.map +1 -0
  20. package/dist/{chunk-A4OFTUVV.js → chunk-PZ4LCN6K.js} +7 -4
  21. package/dist/chunk-PZ4LCN6K.js.map +1 -0
  22. package/dist/{chunk-SJJW77WN.js → chunk-QGI4UCJX.js} +69 -5
  23. package/dist/chunk-QGI4UCJX.js.map +1 -0
  24. package/dist/{chunk-4PNOV7OV.js → chunk-QQVWGWPI.js} +1023 -150
  25. package/dist/chunk-QQVWGWPI.js.map +1 -0
  26. package/dist/{chunk-N7HFALX3.js → chunk-VGTKUOYS.js} +2 -2
  27. package/dist/{chunk-B2VISN2V.js → chunk-YN763NWQ.js} +230 -66
  28. package/dist/chunk-YN763NWQ.js.map +1 -0
  29. package/dist/{chunk-QFDOWABX.js → chunk-Z5LUTUDN.js} +6 -3
  30. package/dist/chunk-Z5LUTUDN.js.map +1 -0
  31. package/dist/{chunk-Q4BO4Z5V.js → chunk-ZTOE3ND3.js} +4 -4
  32. package/dist/{credential-COLTBV6D.js → credential-LO2SEEHU.js} +24 -20
  33. package/dist/credential-LO2SEEHU.js.map +1 -0
  34. package/dist/{credential-template-EEPYFG7R.js → credential-template-ZN7W7734.js} +30 -28
  35. package/dist/credential-template-ZN7W7734.js.map +1 -0
  36. package/dist/{debug-workflow-KOVH5GOP.js → debug-workflow-R3RYYAXK.js} +5 -4
  37. package/dist/{debug-workflow-KOVH5GOP.js.map → debug-workflow-R3RYYAXK.js.map} +1 -1
  38. package/dist/{docs-3SSORHB5.js → docs-DIAFBDXQ.js} +5 -4
  39. package/dist/{docs-3SSORHB5.js.map → docs-DIAFBDXQ.js.map} +1 -1
  40. package/dist/{doctor-OTSIWV6G.js → doctor-NZPUSCRE.js} +9 -5
  41. package/dist/doctor-NZPUSCRE.js.map +1 -0
  42. package/dist/environment-RQVU4P5M.js +221 -0
  43. package/dist/environment-RQVU4P5M.js.map +1 -0
  44. package/dist/{feature-flag-T2LRZME3.js → feature-flag-XG26RURR.js} +72 -10
  45. package/dist/feature-flag-XG26RURR.js.map +1 -0
  46. package/dist/index.js +57 -48
  47. package/dist/index.js.map +1 -1
  48. package/dist/{init-TGWRECT2.js → init-EDNLKGPT.js} +5 -4
  49. package/dist/{init-TGWRECT2.js.map → init-EDNLKGPT.js.map} +1 -1
  50. package/dist/{install-DXSNWZKO.js → install-FYZ3HNZK.js} +7 -7
  51. package/dist/{issues-XIZOPX6T.js → issues-NNRVTJ4L.js} +5 -4
  52. package/dist/{issues-XIZOPX6T.js.map → issues-NNRVTJ4L.js.map} +1 -1
  53. package/dist/{knobs-NBINZIUB.js → knobs-A2BOBH22.js} +5 -4
  54. package/dist/{knobs-NBINZIUB.js.map → knobs-A2BOBH22.js.map} +1 -1
  55. package/dist/{list-3TDHK637.js → list-F33VZ4BJ.js} +5 -4
  56. package/dist/{list-3TDHK637.js.map → list-F33VZ4BJ.js.map} +1 -1
  57. package/dist/{local-HICVD6PU.js → local-DQDTM3XA.js} +7 -6
  58. package/dist/{local-HICVD6PU.js.map → local-DQDTM3XA.js.map} +1 -1
  59. package/dist/{login-UOIUXVUK.js → login-BUMVIB36.js} +5 -4
  60. package/dist/{login-UOIUXVUK.js.map → login-BUMVIB36.js.map} +1 -1
  61. package/dist/{mcp-54IJEOUA.js → mcp-B5Y5CDMQ.js} +23 -24
  62. package/dist/mcp-B5Y5CDMQ.js.map +1 -0
  63. package/dist/{property-UXLOLPTD.js → property-NJ4SRXQG.js} +76 -4
  64. package/dist/property-NJ4SRXQG.js.map +1 -0
  65. package/dist/{psql-BYUUNLGG.js → psql-VBZFCMNR.js} +3 -2
  66. package/dist/{psql-BYUUNLGG.js.map → psql-VBZFCMNR.js.map} +1 -1
  67. package/dist/{record-4TR2UIIS.js → record-WAO36O7G.js} +16 -17
  68. package/dist/record-WAO36O7G.js.map +1 -0
  69. package/dist/{redis-35VBUYW7.js → redis-DHZXHL4S.js} +3 -2
  70. package/dist/{redis-35VBUYW7.js.map → redis-DHZXHL4S.js.map} +1 -1
  71. package/dist/{release-OOLTUBUT.js → release-STJKUKTE.js} +5 -4
  72. package/dist/{release-OOLTUBUT.js.map → release-STJKUKTE.js.map} +1 -1
  73. package/dist/{sandbox-XGSUU6G7.js → sandbox-DWSGLY7B.js} +9 -8
  74. package/dist/{sandbox-XGSUU6G7.js.map → sandbox-DWSGLY7B.js.map} +1 -1
  75. package/dist/{scenarios-GWLJEGHA.js → scenarios-HCIDY725.js} +5 -4
  76. package/dist/{scenarios-GWLJEGHA.js.map → scenarios-HCIDY725.js.map} +1 -1
  77. package/dist/{session-MYFNZPRP.js → session-Y5RHU54D.js} +76 -8
  78. package/dist/session-Y5RHU54D.js.map +1 -0
  79. package/dist/{skill-KM34LDMO.js → skill-23WAHUOB.js} +217 -67
  80. package/dist/skill-23WAHUOB.js.map +1 -0
  81. package/dist/skills/debug-run/SKILL.md +33 -22
  82. package/dist/skills/debug-run/history/1.2.0.md +262 -0
  83. package/dist/skills/debug-run/history/1.3.0.md +273 -0
  84. package/dist/skills/debug-run/version.json +1 -1
  85. package/dist/skills/optimize-cache/SKILL.md +206 -0
  86. package/dist/{spec-RA3CFEKG.js → spec-LM5XHOQ5.js} +54 -25
  87. package/dist/spec-LM5XHOQ5.js.map +1 -0
  88. package/dist/{src-LB4HBTJQ.js → src-22RI5VP2.js} +19 -11
  89. package/dist/src-22RI5VP2.js.map +1 -0
  90. package/dist/src-BGDRMOKC.js +5748 -0
  91. package/dist/src-BGDRMOKC.js.map +1 -0
  92. package/dist/{src-N3TPKGTY.js → src-NCJPUAE6.js} +3 -2
  93. package/dist/{src-UVDX5UP6.js → src-Y3K7UJEF.js} +4 -2
  94. package/dist/{start-PSEQSQA3.js → start-SWY55CZX.js} +5 -4
  95. package/dist/{start-PSEQSQA3.js.map → start-SWY55CZX.js.map} +1 -1
  96. package/dist/{tag-YY6YAABQ.js → tag-YSU2XXPI.js} +6 -5
  97. package/dist/{task-IQC3YR5K.js → task-NLBKLD7D.js} +5 -4
  98. package/dist/{task-IQC3YR5K.js.map → task-NLBKLD7D.js.map} +1 -1
  99. package/dist/{test-suite-DZNJFXDW.js → test-suite-QCPWZ2LS.js} +9 -6
  100. package/dist/{test-suite-DZNJFXDW.js.map → test-suite-QCPWZ2LS.js.map} +1 -1
  101. package/dist/{trace-HXIW7U2D.js → trace-A3ZY6K26.js} +6 -4
  102. package/dist/{triage-ORG45R5W.js → triage-TT4BMDBU.js} +6 -5
  103. package/dist/triage-TT4BMDBU.js.map +1 -0
  104. package/dist/{upgrade-L2VYMP2V.js → upgrade-6ZUFETKX.js} +3 -3
  105. package/dist/{workflow-XYFH5LTP.js → workflow-2CX3ZNOQ.js} +241 -237
  106. package/dist/workflow-2CX3ZNOQ.js.map +1 -0
  107. package/dist/{workflow-cache-NQJQFWMV.js → workflow-cache-GXNE6SU2.js} +140 -7
  108. package/dist/workflow-cache-GXNE6SU2.js.map +1 -0
  109. package/dist/{workflow-debug-BGD4UIXU.js → workflow-debug-PRX3DWGD.js} +8 -7
  110. package/dist/{workflow-debug-BGD4UIXU.js.map → workflow-debug-PRX3DWGD.js.map} +1 -1
  111. package/dist/{workflow-proposals-MTUDX46O.js → workflow-proposals-BW327WGL.js} +5 -4
  112. package/dist/{workflow-proposals-MTUDX46O.js.map → workflow-proposals-BW327WGL.js.map} +1 -1
  113. package/package.json +2 -1
  114. package/dist/chunk-4PNOV7OV.js.map +0 -1
  115. package/dist/chunk-A4OFTUVV.js.map +0 -1
  116. package/dist/chunk-B2VISN2V.js.map +0 -1
  117. package/dist/chunk-GKY2KXO5.js.map +0 -1
  118. package/dist/chunk-JICSYFRJ.js +0 -46
  119. package/dist/chunk-JICSYFRJ.js.map +0 -1
  120. package/dist/chunk-QFDOWABX.js.map +0 -1
  121. package/dist/chunk-SJJW77WN.js.map +0 -1
  122. package/dist/chunk-WOHL4FTB.js.map +0 -1
  123. package/dist/credential-COLTBV6D.js.map +0 -1
  124. package/dist/credential-template-EEPYFG7R.js.map +0 -1
  125. package/dist/doctor-OTSIWV6G.js.map +0 -1
  126. package/dist/feature-flag-T2LRZME3.js.map +0 -1
  127. package/dist/mcp-54IJEOUA.js.map +0 -1
  128. package/dist/property-UXLOLPTD.js.map +0 -1
  129. package/dist/record-4TR2UIIS.js.map +0 -1
  130. package/dist/session-MYFNZPRP.js.map +0 -1
  131. package/dist/skill-KM34LDMO.js.map +0 -1
  132. package/dist/spec-RA3CFEKG.js.map +0 -1
  133. package/dist/src-LB4HBTJQ.js.map +0 -1
  134. package/dist/workflow-XYFH5LTP.js.map +0 -1
  135. package/dist/workflow-cache-NQJQFWMV.js.map +0 -1
  136. /package/dist/{auth-EN6NQCJY.js.map → auth-VUHL74FH.js.map} +0 -0
  137. /package/dist/{chunk-XQXVG7RA.js.map → chunk-EEN2TZUQ.js.map} +0 -0
  138. /package/dist/{chunk-I4QLI3JO.js.map → chunk-EJ4U242G.js.map} +0 -0
  139. /package/dist/{chunk-CLAWT7BJ.js.map → chunk-ENDHMYEJ.js.map} +0 -0
  140. /package/dist/{src-N3TPKGTY.js.map → chunk-G4UYGRZR.js.map} +0 -0
  141. /package/dist/{chunk-N7HFALX3.js.map → chunk-VGTKUOYS.js.map} +0 -0
  142. /package/dist/{chunk-Q4BO4Z5V.js.map → chunk-ZTOE3ND3.js.map} +0 -0
  143. /package/dist/{install-DXSNWZKO.js.map → install-FYZ3HNZK.js.map} +0 -0
  144. /package/dist/{src-UVDX5UP6.js.map → src-NCJPUAE6.js.map} +0 -0
  145. /package/dist/{tag-YY6YAABQ.js.map → src-Y3K7UJEF.js.map} +0 -0
  146. /package/dist/{trace-HXIW7U2D.js.map → tag-YSU2XXPI.js.map} +0 -0
  147. /package/dist/{triage-ORG45R5W.js.map → trace-A3ZY6K26.js.map} +0 -0
  148. /package/dist/{upgrade-L2VYMP2V.js.map → upgrade-6ZUFETKX.js.map} +0 -0
@@ -0,0 +1,206 @@
1
+ ---
2
+ name: optimize-cache
3
+ description: Build an optimized playbook + deterministic cache for a Canary workflow from a real run's Playwright trace. Materializes the run workspace, works node by node, verifies locators offline against trace-reconstructed DOMs, and commits recipes through the server-validated cache-recipe endpoint. Use when the user asks to optimize a workflow's cache, harvest a run into the cache, or repair a low-hit-rate workflow.
4
+ allowed-tools: Read, Grep, Glob, Bash(canary workflow debug:*), Bash(canary workflow cache:*), Bash(canary workflow runs:*), Bash(jq:*), Bash(diff:*), Bash(sed -n:*), Bash(head:*), Bash(tail:*), Bash(wc:*), Bash(grep:*), Bash(find:*), Bash(ls:*), Bash(cat:*)
5
+ ---
6
+
7
+ <!-- AUTOGENERATED from packages/workflow-cache/. Do not edit by hand. -->
8
+ <!-- After editing the partials, mint a new version: -->
9
+ <!-- bun run --filter=@canaryai/cli bump-skill optimize-cache [patch|minor|major] -->
10
+
11
+ # Cache Optimization Guide
12
+
13
+ You are building the deterministic cache + playbook for a workflow from the
14
+ evidence of a real run — the same procedure the server's async cache-optimizer
15
+ agent follows, driven manually through the canary CLI.
16
+
17
+ ## Materialize the workspace
18
+
19
+ ```bash
20
+ canary workflow debug materialize <runId> --out ./cache-opt-<runId>/
21
+ ```
22
+
23
+ The workspace contains the trace (`browser/trace.zip`, parsed
24
+ `browser/actions.jsonl`), per-step thoughts, timeline JSONLs, the workflow
25
+ definition, and variable lineage.
26
+
27
+ ## CLI tools
28
+
29
+ ```bash
30
+ # Reconstruct one trace action's DOM and capture its substrate-v2:
31
+ canary workflow cache substrate --workspace ./cache-opt-<runId> --action <traceActionIndex> [--phase before|after]
32
+
33
+ # Derive + offline-verify a locator for a ref in that substrate:
34
+ canary workflow cache locator --workspace ./cache-opt-<runId> --action <traceActionIndex> --ref <ref> [--var name=value ...]
35
+
36
+ # Commit a node's recipe (server recomputes the hash and validates):
37
+ canary workflow cache commit <workflowId> --node <nodeId> --recipe <recipe.json>
38
+
39
+ # Or enqueue the server-side optimizer job instead of working manually:
40
+ canary workflow cache optimize <workflowId> [--run <runId>]
41
+ ```
42
+
43
+ ## Methodology — node by node, evidence first
44
+
45
+ You are building the deterministic cache and playbook for a workflow from the
46
+ evidence of a real run: its Playwright trace, the agent's tool calls and
47
+ thoughts, and the run's variable/macro records. Work methodically:
48
+
49
+ 1. **Orient.** Read the workspace summary, the workflow definition
50
+ (`workflow/`), `timeline/*.jsonl`, `variables/lineage.jsonl`, and the
51
+ per-step thoughts (`thoughts/by-step/`). Note each step's `pathNotes`
52
+ summary when present — the live agent recorded its detours there.
53
+ 2. **Pick harvestable nodes.** Only nodes whose step SUCCEEDED are
54
+ harvestable. A node that succeeded in a run that later failed is still
55
+ valid — often the most valuable, since that run won't be green soon.
56
+ Cache-replayed nodes (their trace actions sit under a `Cache Replay:`
57
+ group) carry nothing new — skip them.
58
+ 3. **Per node, reconstruct the true path.** Cross-reference the node's trace
59
+ actions with the agent's tool calls and thoughts. Identify what actually
60
+ accomplished the task versus exploration.
61
+ 4. **Author the most-direct playbook** of core actions (see Core actions).
62
+ The playbook describes the shortest correct path to the node's goal — not
63
+ a transcript of what the agent happened to do.
64
+ 5. **Build and verify each locator** (see Verification bar). Materialize the
65
+ substrate for the action's before-state, derive the locator, verify it
66
+ offline. Iterate or degrade to a gap — never commit an unverified locator.
67
+ 6. **Commit the node, then move on.** One node at a time; do not interleave.
68
+
69
+
70
+ ## Core actions — what becomes a playbook step
71
+
72
+ Every CORE action on the success path becomes a playbook step (cached) or a
73
+ gap. Core actions are:
74
+
75
+ - **Mutations**: click, fill/type, select, check/uncheck, press, hover-to-open,
76
+ navigate, file upload, drag.
77
+ - **Assertions**: explicit verify steps and inline assertions the node declares.
78
+ - **Captures**: values the agent saved for downstream steps (e.g.
79
+ `browser_evaluate` with `saveAs`). These become context extractors on the
80
+ cache row — `declaredOutputs` stay reserved for DOM-captured values.
81
+ - **Non-browser effects**: reading email/inbox, running python, reading or
82
+ verifying downloads. These are invisible in the browser trace — source them
83
+ from the agent's tool-call records and thoughts. They become playbook steps
84
+ whose compiled entry is a **gap** (the replay agent performs them live);
85
+ python steps with a cache label become cached data scripts.
86
+
87
+ Observation tool calls are NOT playbook material: accessibility snapshots,
88
+ screenshots, look-around scrolling, `browser_evaluate` without `saveAs`. They
89
+ informed the agent; they leave no artifact in the playbook.
90
+
91
+ A playbook that silently drops a core action loses functionality — when in
92
+ doubt whether an action was core, check what downstream steps consumed.
93
+
94
+
95
+ ## Replay mechanics — author recipes that replay, not recipes that look right
96
+
97
+ The trace shows RESOLVED values; the recipe must reference their SOURCES so
98
+ future runs work with fresh data:
99
+
100
+ - **`$var.X` tokens** substitute from the variable registry at replay.
101
+ Workflow `inputValues` auto-promote to `$var.{name}` downstream; values an
102
+ agent captured land under the key it saved them as. Keep these tokens
103
+ verbatim in playbook params — never inline the resolved value you saw in
104
+ the trace.
105
+ - **Macros** (`$EMAIL()`, `$UUID()`, `$RANDOM_STRING()`, …) re-roll fresh on
106
+ every run. The run's macro resolution records map resolved values back to
107
+ expressions; supply action args as literals and let the synthesizer attach
108
+ `macroBindings` — do not hand-write macro expressions into action args.
109
+ - **Dynamic locator text** (`dynamicText` / `containsTextDynamic` bindings)
110
+ re-resolves selector text from run state. When a locator can only be
111
+ disambiguated by a run-specific value, bind it to the variable that
112
+ produced the value — never anchor on the literal.
113
+ - **Gaps** are honest: a `gap` compiled entry means "the replay agent performs
114
+ this step live". A gap replays slower but correctly; a bad cached locator
115
+ replays fast and wrong. Prefer the gap.
116
+ - **Volatile identifiers** (seeded entity numbers, ULIDs/UUIDs, timestamps,
117
+ framework-generated class hashes) WILL differ next run even though they
118
+ point at a real element today. Structural prefixes (e.g. a stable
119
+ `__new__` draft-row prefix) are usable; the embedded number is not.
120
+
121
+
122
+ ## Exploration filtering — most-direct path, not a transcript
123
+
124
+ The live agent sometimes explores: opens the wrong section, misclicks,
125
+ scrolls to look around, retries. The playbook is the shortest correct path,
126
+ so filter exploration out — but carefully:
127
+
128
+ - Use the agent's thoughts and the step's `pathNotes` ("accidentally opened X
129
+ before finding A under B") to separate detours from the success path.
130
+ - An action is exploratory if the success path works without it. It is
131
+ LOAD-BEARING if a later action's target only exists/becomes visible because
132
+ of it — dismissing a blocking dialog, expanding a collapsed section,
133
+ switching a tab. Keep those even if the agent stumbled into them.
134
+ - The offline check: if the direct-path locator for step N does not resolve
135
+ against the post-state of step N-1, you dropped something load-bearing.
136
+ Re-examine the actions you filtered.
137
+ - Collapse retries: a fill that was typed twice (cleared, retyped) is one
138
+ step with the final value.
139
+
140
+
141
+ ## Verification bar — never commit an unverified locator
142
+
143
+ Every cached browser action's locator must pass offline verification against
144
+ the trace-reconstructed DOM of the action's before-state:
145
+
146
+ 1. Materialize the substrate for the action (its before-snapshot).
147
+ 2. Derive the locator from the substrate (the deterministic builder owns the
148
+ predicate ladder — do not hand-craft selectors when the builder succeeds).
149
+ 3. Verify: the strategy must resolve to EXACTLY the recorded target element
150
+ (handle identity, count of one) and must not lean on volatile identifier
151
+ tokens.
152
+
153
+ On failure, in order:
154
+ - Re-check the target ref — did you pick the right element in the substrate?
155
+ - Try the builder against a corrected ref or with the run's variable values
156
+ (a row may only be distinguishable via a bound variable).
157
+ - If the element is one of several identical widgets (responsive duplicates,
158
+ repeated list rows with no distinguishing signal), that is IRREDUCIBLE.
159
+ Do not reach for ordinal/nth predicates — ordinals encode layout order,
160
+ not identity. Mark the step as a gap instead.
161
+ - Any other persistent failure: gap, with a reason.
162
+
163
+ A verified-offline locator can still fail live (reconstructed DOMs lack JS
164
+ state and cross-origin frames) — that is what replay healing is for. An
165
+ UNVERIFIED locator must never be committed.
166
+
167
+
168
+ ## Commit rules — one node, one recipe
169
+
170
+ - Only nodes whose step SUCCEEDED in the source run are committable — the
171
+ commit is rejected otherwise (enforced, not advisory). Failed or
172
+ never-reached nodes have no proven success path to cache.
173
+ - One commit per node, after all its steps are resolved (cached or gap).
174
+ - Playbook steps are numbered contiguously from 1; every step maps to exactly
175
+ one cached action or one gap.
176
+ - **Reconcile, don't duplicate, auto-cached fields.** Assertions the node
177
+ declares are carried as the codified assertion on the recipe; captures are
178
+ carried as context extractors — aligned to the new step order. Do not also
179
+ write them as redundant steps, and never drop them.
180
+ - **Lossless tokens.** Every `$var.X` / macro the node's inputs and previous
181
+ recipe depended on must survive into the new recipe (as a token, a variable
182
+ binding, or a macro binding). The commit validates this and rejects recipes
183
+ that inline run-specific values.
184
+ - The node hash is recomputed server-side from the node context with your
185
+ playbook applied; the next run computes the same hash and replays your
186
+ recipe. You never supply a hash.
187
+ - Be honest in summaries: report how many steps are cached versus gaps and
188
+ why. A mostly-gap recipe for a hard node is a fine outcome.
189
+
190
+
191
+ ## Recipe file format
192
+
193
+ `canary workflow cache commit` takes a JSON file:
194
+
195
+ ```json
196
+ {
197
+ "sourceRunId": "<runId>",
198
+ "playbookSteps": [{ "order": 1, "action": "fill", "target": "Name field", "params": { "text": "$var.name" } }],
199
+ "plannedActions": [{ "stepOrder": 1, "toolName": "browser_type", "actionType": "type", "args": { "text": "Acme Corp" }, "selectors": { "role": { "role": "textbox", "name": "Name" } }, "elementDescription": "Name field" }],
200
+ "gaps": [{ "stepOrder": 2, "reason": "reads verification email from inbox" }]
201
+ }
202
+ ```
203
+
204
+ `plannedActions[].args` carry the LITERAL values from the trace — the server
205
+ maps them back to variables/macros from the run's records and rejects recipes
206
+ that drop required tokens.
@@ -12,8 +12,7 @@ import {
12
12
  workflowToSpec,
13
13
  writeNodeIds,
14
14
  writeWorkflowId
15
- } from "./chunk-B2VISN2V.js";
16
- import "./chunk-SJJW77WN.js";
15
+ } from "./chunk-YN763NWQ.js";
17
16
  import {
18
17
  fetchOrExit
19
18
  } from "./chunk-3CZU7XCS.js";
@@ -21,7 +20,15 @@ import {
21
20
  getArgValue,
22
21
  hasFlag,
23
22
  resolveAppUrl
24
- } from "./chunk-A4OFTUVV.js";
23
+ } from "./chunk-PZ4LCN6K.js";
24
+ import {
25
+ isSpecFile,
26
+ readableSpecDirs,
27
+ specDir,
28
+ specExtension,
29
+ splitSpecExtension
30
+ } from "./chunk-JZMJUPZP.js";
31
+ import "./chunk-QGI4UCJX.js";
25
32
  import "./chunk-VKVL7WBN.js";
26
33
 
27
34
  // src/spec/index.ts
@@ -175,7 +182,7 @@ async function resolvePropertyNameMap(ctx, names) {
175
182
  var UnsupportedFieldEditError = class extends Error {
176
183
  constructor(fields) {
177
184
  super(
178
- `Editing ${fields.join(", ")} via .canary.md is not supported yet. These fields require name \u2192 id resolution that lands in a follow-up. Edit them in the Canary UI for now; the rest of your changes will still apply.`
185
+ `Editing ${fields.join(", ")} via a spec file is not supported yet. These fields require name \u2192 id resolution that lands in a follow-up. Edit them in the Canary UI for now; the rest of your changes will still apply.`
179
186
  );
180
187
  this.name = "UnsupportedFieldEditError";
181
188
  }
@@ -289,21 +296,19 @@ function printSpecHelp() {
289
296
  " validate <file> Parse + lint the spec offline",
290
297
  " compile <file> Validate, then create the workflow",
291
298
  " diff <file> Preview what `compile` would change (no writes)",
292
- " pull <workflow-id> Reverse: workflow \u2192 round-trippable .canary.md",
299
+ ` pull <workflow-id> Reverse: workflow \u2192 round-trippable ${specExtension}`,
293
300
  " pull --all Sync a directory of specs down from all workflows",
294
301
  "",
295
302
  "Compile flags:",
296
- " --publish Create as published instead of draft",
297
- " --run-and-publish After create, trigger a test run; publish if green",
298
303
  " --allow-duplicate Fresh compile: create even when a same-named workflow exists",
299
304
  " --create-with-duplicate-title",
300
305
  " Alias for --allow-duplicate",
301
306
  " --json Machine-readable output",
302
307
  "",
303
308
  "Pull flags:",
304
- " --output <path|dir> File (single) or directory (--all); default canary/specs/",
309
+ ` --output <path|dir> File (single) or directory (--all); default ${specDir}/`,
305
310
  " --all Sync every workflow into the directory (add new, update matching by workflowId)",
306
- " --status <s> With --all, filter workflows by status (draft|published|\u2026)",
311
+ " --status <s> With --all, filter workflows by status (draft|approved|\u2026)",
307
312
  " --force Overwrite an existing file (single pull)"
308
313
  ].join("\n")
309
314
  );
@@ -473,7 +478,7 @@ async function handlePull(argv, apiUrl, token) {
473
478
  console.error(`Error: workflow ${workflowId} not found (or no permission).`);
474
479
  process.exit(1);
475
480
  }
476
- const outPath = getArgValue(argv, "--output") ?? path.join("canary", "specs", `${slugify(live.name)}.canary.md`);
481
+ const outPath = getArgValue(argv, "--output") ?? path.join(specDir, `${slugify(live.name)}${specExtension}`);
477
482
  const preserved = await readPreservedFrontmatter(outPath);
478
483
  const sidecarPath = await fetchAndWriteApiSequenceSidecar(
479
484
  apiUrl,
@@ -524,10 +529,12 @@ async function handlePull(argv, apiUrl, token) {
524
529
  }
525
530
  async function handlePullAll(argv, apiUrl, token) {
526
531
  const json = hasFlag(argv, "--json");
527
- const dir = getArgValue(argv, "--output") ?? argv.find((a) => !a.startsWith("-")) ?? path.join("canary", "specs");
532
+ const explicitDir = getArgValue(argv, "--output") ?? argv.find((a) => !a.startsWith("-"));
533
+ const dir = explicitDir ?? specDir;
528
534
  const status = getArgValue(argv, "--status");
529
535
  const workflows = await fetchWorkflowList(apiUrl, token, status);
530
- const byWorkflowId = await indexSpecFiles(dir);
536
+ const indexDirs = explicitDir ? [explicitDir] : readableSpecDirs();
537
+ const byWorkflowId = await mergeSpecRoots(indexDirs);
531
538
  const usedNames = new Set([...byWorkflowId.values()].map((p) => path.basename(p)));
532
539
  const results = [];
533
540
  for (const wf of workflows) {
@@ -651,7 +658,7 @@ async function indexSpecFiles(dir) {
651
658
  }
652
659
  const ID_RE = /^workflowId:\s*([0-9a-f-]{36})\s*$/im;
653
660
  for (const rel of entries) {
654
- if (!rel.endsWith(".canary.md")) continue;
661
+ if (!isSpecFile(rel)) continue;
655
662
  const full = path.join(dir, rel);
656
663
  try {
657
664
  const src = await fs.readFile(full, "utf-8");
@@ -662,9 +669,18 @@ async function indexSpecFiles(dir) {
662
669
  }
663
670
  return map;
664
671
  }
672
+ async function mergeSpecRoots(roots) {
673
+ const byWorkflowId = /* @__PURE__ */ new Map();
674
+ for (const root of roots) {
675
+ for (const [id, file] of await indexSpecFiles(root)) {
676
+ if (!byWorkflowId.has(id)) byWorkflowId.set(id, file);
677
+ }
678
+ }
679
+ return byWorkflowId;
680
+ }
665
681
  function uniqueName(slug, workflowId, used) {
666
- let name = `${slug}.canary.md`;
667
- if (used.has(name)) name = `${slug}-${workflowId.slice(0, 8)}.canary.md`;
682
+ let name = `${slug}${specExtension}`;
683
+ if (used.has(name)) name = `${slug}-${workflowId.slice(0, 8)}${specExtension}`;
668
684
  return name;
669
685
  }
670
686
  async function runFreshCompile(opts) {
@@ -699,7 +715,13 @@ async function runFreshCompile(opts) {
699
715
  }
700
716
  process.exit(1);
701
717
  }
702
- const status = hasFlag(argv, "--publish") ? "published" : emitted.status ?? "draft";
718
+ if (hasFlag(argv, "--publish")) {
719
+ console.error(
720
+ "Error: --publish was removed. Compile the spec, then approve it (canary workflow approve <id> --yes) and promote it (canary workflow promote <id>)."
721
+ );
722
+ process.exit(1);
723
+ }
724
+ const status = emitted.status ?? "draft";
703
725
  const payload = {
704
726
  name: emitted.name,
705
727
  description: emitted.description,
@@ -769,8 +791,10 @@ async function runFreshCompile(opts) {
769
791
  for (const w of body.warnings) console.log(` \u26A0 ${w}`);
770
792
  }
771
793
  if (hasFlag(argv, "--run-and-publish")) {
772
- console.log("\n\u2139 --run-and-publish for spec compile is not wired in this MVP. Run:");
773
- console.log(` canary workflow run ${body.workflow.id} --publish-on-pass`);
794
+ console.log("\n\u2139 --run-and-publish was removed. Run, approve, then promote:");
795
+ console.log(` canary workflow run ${body.workflow.id}`);
796
+ console.log(` canary workflow approve ${body.workflow.id} --yes`);
797
+ console.log(` canary workflow promote ${body.workflow.id}`);
774
798
  }
775
799
  }
776
800
  var DUPLICATE_WORKFLOW_TITLE_ALLOW_FLAGS = [
@@ -823,10 +847,11 @@ async function fetchFreshCompileDuplicateWorkflows(opts) {
823
847
  return exactWorkflowTitleMatches(body.data ?? [], opts.workflowName);
824
848
  }
825
849
  function stampedSpecPath(file) {
826
- if (file.endsWith(".canary.md")) {
827
- return `${file.slice(0, -".canary.md".length)}.stamped.canary.md`;
850
+ const split = splitSpecExtension(file);
851
+ if (split) {
852
+ return `${split.base}.stamped${split.ext}`;
828
853
  }
829
- return `${file}.stamped.canary.md`;
854
+ return `${file}.stamped${specExtension}`;
830
855
  }
831
856
  async function runRecompile(opts) {
832
857
  const { argv, apiUrl, token, file, emitted } = opts;
@@ -881,9 +906,9 @@ async function runRecompile(opts) {
881
906
  if (diff.nameDrift) {
882
907
  console.log(` \u270E renamed: "${diff.nameDrift.from}" \u2192 "${diff.nameDrift.to}"`);
883
908
  }
884
- if (live.status === "published") {
909
+ if (live.promotion === "promoted") {
885
910
  console.log(
886
- ` \u26A0 workflow is published \u2014 changes land on draft state; next publish captures them.`
911
+ ` \u26A0 workflow is promoted \u2014 suites run the live definition, so these changes take effect on the next suite run.`
887
912
  );
888
913
  }
889
914
  if (diff.duplicateSpecIds.length > 0) {
@@ -945,7 +970,11 @@ export {
945
970
  handleCompile,
946
971
  handleSpec,
947
972
  handleValidate,
973
+ indexSpecFiles,
948
974
  lintSpec,
949
- parseSpec
975
+ mergeSpecRoots,
976
+ parseSpec,
977
+ stampedSpecPath,
978
+ uniqueName
950
979
  };
951
- //# sourceMappingURL=spec-RA3CFEKG.js.map
980
+ //# sourceMappingURL=spec-LM5XHOQ5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/spec/index.ts","../src/spec/sync.ts"],"sourcesContent":["/**\n * `canary spec` subcommand entry points.\n *\n * Subcommands:\n * canary spec validate <file> — parse + lint, exit non-zero on errors\n * canary spec compile <file> — validate, then POST to /workflows/create-with-nodes\n * canary spec pull <id> — reverse: workflow → spec (TODO)\n * canary spec diff <file> <id> — what would compile change? (TODO)\n *\n * @module spec\n */\n\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport process from 'node:process';\nimport { getArgValue, hasFlag, resolveAppUrl } from '../auth.js';\nimport { fetchOrExit } from '../api-fetch.js';\nimport {\n isSpecFile,\n readableSpecDirs,\n specDir,\n specExtension,\n splitSpecExtension,\n} from '../brand.js';\nimport {\n ApiSequenceFileSchema,\n diffWorkflow,\n emitSpec,\n extractPreservedFrontmatter,\n hasAnyChange,\n lintSpec,\n parseSpec,\n summarize,\n workflowToSpec,\n writeNodeIds,\n writeWorkflowId,\n type DiffResult,\n type LiveWorkflow,\n type PullWorkflow,\n type PullOptions,\n type SpecAst,\n type SpecIssue,\n} from '@chatsdet/canary-spec';\nimport { applyDiff } from './sync.js';\n\nexport async function handleSpec(argv: string[], apiUrl: string, token: string): Promise<void> {\n const sub = argv[0];\n switch (sub) {\n case 'validate':\n await handleValidate(argv.slice(1));\n return;\n case 'compile':\n await handleCompile(argv.slice(1), apiUrl, token);\n return;\n case 'pull':\n await handlePull(argv.slice(1), apiUrl, token);\n return;\n case 'diff':\n await handleDiff(argv.slice(1), apiUrl, token);\n return;\n default:\n printSpecHelp();\n process.exit(sub ? 1 : 0);\n }\n}\n\nfunction printSpecHelp(): void {\n console.log(\n [\n 'Usage: canary spec <subcommand> <file>',\n '',\n 'Subcommands:',\n ' validate <file> Parse + lint the spec offline',\n ' compile <file> Validate, then create the workflow',\n ' diff <file> Preview what `compile` would change (no writes)',\n ` pull <workflow-id> Reverse: workflow → round-trippable ${specExtension}`,\n ' pull --all Sync a directory of specs down from all workflows',\n '',\n 'Compile flags:',\n ' --allow-duplicate Fresh compile: create even when a same-named workflow exists',\n ' --create-with-duplicate-title',\n ' Alias for --allow-duplicate',\n ' --json Machine-readable output',\n '',\n 'Pull flags:',\n ` --output <path|dir> File (single) or directory (--all); default ${specDir}/`,\n ' --all Sync every workflow into the directory (add new, update matching by workflowId)',\n ' --status <s> With --all, filter workflows by status (draft|approved|…)',\n ' --force Overwrite an existing file (single pull)',\n ].join('\\n')\n );\n}\n\nasync function handleValidate(argv: string[]): Promise<void> {\n const file = argv[0];\n if (!file) {\n console.error('Error: canary spec validate <file>');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n\n const result = await loadAndValidate(file);\n if (json) {\n console.log(\n JSON.stringify(\n {\n ok: result.ok,\n issues: result.issues,\n ...(result.ok ? { workflowName: result.ast.frontmatter.name } : {}),\n },\n null,\n 2\n )\n );\n process.exit(result.ok ? 0 : 1);\n }\n printIssues(result.issues, file);\n if (!result.ok) {\n process.exit(1);\n }\n console.log(`✓ ${file} validates`);\n}\n\nasync function handleCompile(argv: string[], apiUrl: string, token: string): Promise<void> {\n const file = argv[0];\n if (!file) {\n console.error('Error: canary spec compile <file>');\n process.exit(1);\n }\n const result = await loadAndValidate(file);\n printIssues(result.issues, file);\n if (!result.ok) {\n process.exit(1);\n }\n\n const ast = result.ast;\n // Append a default end node only on a fresh create; on recompile (spec\n // already bound to a workflow) suppress it so an end-less workflow doesn't\n // gain a phantom node on every round-trip.\n const emitted = emitSpec(ast, { appendDefaultEnd: !ast.frontmatter.workflowId });\n\n // Load any api_sequence sidecar referenced by a node. Multiple nodes pointing\n // at the same path share one sidecar (DB enforces one api_sequence row per\n // workflow); divergent paths produce a clear error before any state changes.\n try {\n emitted.apiSequenceConfig = await loadApiSequenceSidecar(emitted.nodes, file);\n } catch (err) {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n // Branch: recompile (frontmatter has `workflowId`) vs fresh create.\n if (emitted.workflowId) {\n await runRecompile({ argv, apiUrl, token, file, emitted });\n return;\n }\n\n await runFreshCompile({ argv, apiUrl, token, file, emitted });\n}\n\n/**\n * Resolve and load the api_sequence sidecar(s) referenced by spec nodes.\n *\n * The DB enforces one api_sequence row per workflow, so a single workflow may\n * carry many `nodeType: 'api_sequence'` nodes but they all point at the same\n * sidecar. We accept multiple identical paths but reject divergent ones with\n * a clear error before any state mutation.\n */\nasync function loadApiSequenceSidecar(\n nodes: ReadonlyArray<{ nodeType: string; configJson: Record<string, unknown> }>,\n specFile: string\n): Promise<unknown> {\n const refs = nodes\n .filter((n) => n.nodeType === 'api_sequence')\n .map((n) => n.configJson['__apiSequenceSidecar'])\n .filter((p): p is string => typeof p === 'string' && p.length > 0);\n if (refs.length === 0) return undefined;\n const unique = Array.from(new Set(refs));\n if (unique.length > 1) {\n throw new Error(\n `Spec has divergent api_sequence sidecar paths: ${unique\n .map((p) => `\"${p}\"`)\n .join(', ')}. The DB stores one config per workflow — pick a single path.`\n );\n }\n const sidecarPath = path.resolve(path.dirname(specFile), unique[0]);\n let raw: string;\n try {\n raw = await fs.readFile(sidecarPath, 'utf-8');\n } catch (err) {\n throw new Error(\n `Could not read api_sequence sidecar at ${sidecarPath}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n const yaml = await import('yaml');\n let parsed: unknown;\n try {\n parsed = yaml.parse(raw);\n } catch (err) {\n throw new Error(\n `Sidecar YAML at ${sidecarPath} is invalid: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n const validated = ApiSequenceFileSchema.safeParse(parsed);\n if (!validated.success) {\n throw new Error(\n `Sidecar at ${sidecarPath} doesn't match the api_sequence shape: ${validated.error.message}`\n );\n }\n return validated.data;\n}\n\nasync function handleDiff(argv: string[], apiUrl: string, token: string): Promise<void> {\n const file = argv[0];\n if (!file) {\n console.error('Error: canary spec diff <file>');\n process.exit(1);\n }\n const result = await loadAndValidate(file);\n printIssues(result.issues, file);\n if (!result.ok) process.exit(1);\n\n const emitted = emitSpec(result.ast, { appendDefaultEnd: false });\n if (!emitted.workflowId) {\n console.log(\n `ℹ ${file} has no workflowId in frontmatter — \\`canary spec compile\\` would create a new workflow.`\n );\n console.log(` Nodes that would be created: ${emitted.nodes.length}`);\n return;\n }\n\n const liveRes = await fetchOrExit(`${apiUrl}/workflows/${emitted.workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!liveRes.ok) {\n console.error(`Error: failed to fetch workflow ${emitted.workflowId} (${liveRes.status})`);\n process.exit(1);\n }\n const liveBody = (await liveRes.json().catch(() => ({}))) as { workflow?: LiveWorkflow };\n const live = liveBody.workflow;\n if (!live) {\n console.error(`Error: workflow ${emitted.workflowId} not found.`);\n process.exit(1);\n }\n\n let diff: DiffResult;\n try {\n diff = diffWorkflow(emitted, live);\n } catch (err) {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n if (hasFlag(argv, '--json')) {\n console.log(JSON.stringify({ ok: true, summary: summarize(diff), diff }, null, 2));\n return;\n }\n\n if (!hasAnyChange(diff)) {\n console.log(`✓ ${file} — in sync with workflow ${emitted.workflowId}`);\n return;\n }\n const s = summarize(diff);\n console.log(`\\nPreview for \"${live.name}\" (${emitted.workflowId}):`);\n console.log(\n ` +${s.created} create · ~${s.updated} update · -${s.deleted} delete · ↻ ${s.moved} move`\n );\n if (diff.nameDrift) console.log(` ✎ rename: \"${diff.nameDrift.from}\" → \"${diff.nameDrift.to}\"`);\n const fieldKeys = Object.keys(diff.workflowFieldChanges).filter((k) => k !== 'name');\n if (fieldKeys.length > 0) console.log(` ✎ workflow fields: ${fieldKeys.join(', ')}`);\n console.log(`\\nRun \\`canary spec compile ${file}\\` to apply.`);\n}\n\nasync function handlePull(argv: string[], apiUrl: string, token: string): Promise<void> {\n if (hasFlag(argv, '--all')) {\n await handlePullAll(argv, apiUrl, token);\n return;\n }\n\n const workflowId = argv.find((a) => !a.startsWith('-'));\n if (!workflowId) {\n console.error('Error: canary spec pull <workflow-id> [--output <path>] [--force]');\n console.error(' canary spec pull --all [--output <dir>] [--status <s>] [--force]');\n process.exit(1);\n }\n const json = hasFlag(argv, '--json');\n const force = hasFlag(argv, '--force');\n\n const live = await fetchWorkflow(apiUrl, token, workflowId);\n if (!live) {\n console.error(`Error: workflow ${workflowId} not found (or no permission).`);\n process.exit(1);\n }\n\n const outPath =\n getArgValue(argv, '--output') ??\n path.join(specDir, `${slugify(live.name)}${specExtension}`);\n\n // Preserve spec-source-only frontmatter from the prior file (requirementRef,\n // scenario skipReason). These have no live equivalent, so a naive overwrite\n // would silently drop the author's notes on re-pull.\n const preserved = await readPreservedFrontmatter(outPath);\n // When the workflow has an api_sequence config, fetch it + write a sidecar\n // file alongside the spec. Pull annotates each api_sequence node in the live\n // workflow with `__apiSequenceSidecar` so `workflowToSpec` can emit the\n // `- apiSequence:` bullet. The sidecar path lives next to the spec under a\n // sibling `sequences/` directory by convention.\n const sidecarPath = await fetchAndWriteApiSequenceSidecar(\n apiUrl,\n token,\n workflowId,\n live,\n outPath\n );\n if (sidecarPath) {\n for (const node of live.nodes) {\n if (node.nodeType === 'api_sequence') {\n node.configJson = {\n ...node.configJson,\n __apiSequenceSidecar: path.relative(path.dirname(outPath), sidecarPath),\n };\n }\n }\n }\n const source = workflowToSpec(live, preserved);\n\n // Validate the round-trip so a malformed pull surfaces immediately.\n const parsed = parseSpec(source);\n const lintIssues = parsed.ok ? lintSpec(parsed.ast) : [];\n const parseIssues = parsed.ok ? [] : parsed.issues;\n const issues = [...parseIssues, ...lintIssues];\n\n if (!force) {\n try {\n await fs.access(outPath);\n console.error(\n `Error: ${outPath} already exists. Use --output <path> or --force to overwrite.`\n );\n process.exit(1);\n } catch {\n // file does not exist — good.\n }\n }\n\n await fs.mkdir(path.dirname(outPath), { recursive: true });\n await fs.writeFile(outPath, source, 'utf-8');\n\n if (json) {\n console.log(JSON.stringify({ ok: true, file: outPath, workflowId, issues }, null, 2));\n return;\n }\n\n console.log(`\\n✓ Pulled \"${live.name}\" → ${outPath}`);\n console.log(` Nodes: ${live.nodes.length}`);\n console.log(` Edit the file, then \\`canary spec compile ${outPath}\\` to push changes back.`);\n if (issues.length > 0) {\n console.log('\\n Notes:');\n printIssues(issues, outPath);\n }\n}\n\n/**\n * `canary spec pull --all` — sync a directory of spec files (`.cofactor.md`,\n * legacy `.canary.md`) down from the live workflows. Existing files are matched\n * to a workflow by their `workflowId:` frontmatter and rewritten in place;\n * workflows with no matching file get a fresh `<slug>` spec in the active\n * brand's extension. Files are only written when their\n * content actually changes, so an already-synced repo produces no churn.\n */\nasync function handlePullAll(argv: string[], apiUrl: string, token: string): Promise<void> {\n const json = hasFlag(argv, '--json');\n const explicitDir = getArgValue(argv, '--output') ?? argv.find((a) => !a.startsWith('-'));\n // New files default to the active brand's dir (e.g. `cofactor/specs`).\n const dir = explicitDir ?? specDir;\n const status = getArgValue(argv, '--status');\n\n const workflows = await fetchWorkflowList(apiUrl, token, status);\n // Map existing files by their declared workflowId so re-pulls land in place.\n // With no explicit dir, scan every recognized spec root so an existing\n // `canary/specs` repo is matched and rewritten in place rather than\n // duplicated under the new default `cofactor/specs`.\n const indexDirs = explicitDir ? [explicitDir] : readableSpecDirs();\n const byWorkflowId = await mergeSpecRoots(indexDirs);\n const usedNames = new Set<string>([...byWorkflowId.values()].map((p) => path.basename(p)));\n\n const results: Array<{\n id: string;\n name: string;\n file: string;\n action: 'added' | 'synced' | 'unchanged' | 'failed';\n note?: string;\n }> = [];\n\n for (const wf of workflows) {\n const live = await fetchWorkflow(apiUrl, token, wf.id);\n if (!live) {\n results.push({ id: wf.id, name: wf.name, file: '', action: 'failed', note: 'fetch failed' });\n continue;\n }\n const existing = byWorkflowId.get(wf.id);\n let target: string;\n let isNew = false;\n if (existing) {\n target = existing;\n } else {\n target = path.join(dir, uniqueName(slugify(live.name), wf.id, usedNames));\n usedNames.add(path.basename(target));\n isNew = true;\n }\n\n let prior: string | null = null;\n try {\n prior = await fs.readFile(target, 'utf-8');\n } catch {\n // no prior file\n }\n // Spec-source-only fields (requirementRef, scenario skipReason) have no\n // server-side representation — lift them from the prior file before\n // emitting, so re-pull doesn't drop them.\n const preserved = prior ? extractPreservedFrontmatter(prior) : {};\n const source = workflowToSpec(live, preserved);\n if (prior === source) {\n results.push({ id: wf.id, name: live.name, file: target, action: 'unchanged' });\n continue;\n }\n await fs.mkdir(path.dirname(target), { recursive: true });\n await fs.writeFile(target, source, 'utf-8');\n results.push({ id: wf.id, name: live.name, file: target, action: isNew ? 'added' : 'synced' });\n }\n\n if (json) {\n console.log(JSON.stringify({ ok: true, dir, results }, null, 2));\n return;\n }\n const count = (a: string) => results.filter((r) => r.action === a).length;\n console.log(`\\n✓ Synced ${results.length} workflow(s) → ${dir}`);\n console.log(\n ` +${count('added')} added · ~${count('synced')} synced · =${count('unchanged')} unchanged${count('failed') ? ` · ✗${count('failed')} failed` : ''}`\n );\n for (const r of results) {\n if (r.action === 'unchanged') continue;\n const mark = r.action === 'added' ? '+' : r.action === 'synced' ? '~' : '✗';\n console.log(\n ` ${mark} ${path.relative(process.cwd(), r.file) || r.file}${r.note ? ` (${r.note})` : ''}`\n );\n }\n}\n\n/** Fetch the full live workflow (with nodes + edges) for the diff/pull path. */\n/**\n * Fetch the workflow's api_sequence config (if any) and write it to a sidecar\n * YAML alongside the spec. Returns the absolute path to the sidecar, or null\n * when the workflow doesn't have an api_sequence row.\n *\n * The sidecar filename is derived from the workflow slug and lands in a\n * sibling `sequences/` directory: `<spec-dir>/sequences/<slug>.yaml`.\n * Re-pull overwrites the file in place.\n */\nasync function fetchAndWriteApiSequenceSidecar(\n apiUrl: string,\n token: string,\n workflowId: string,\n live: PullWorkflow,\n outPath: string\n): Promise<string | null> {\n // Skip when the workflow has no api_sequence nodes.\n if (!live.nodes.some((n) => n.nodeType === 'api_sequence')) return null;\n const res = await fetchOrExit(`${apiUrl}/api-sequences/by-workflow/${workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!res.ok) {\n // Workflow may have api_sequence nodes but no row yet (e.g. legacy state);\n // leave the sidecar off so the round-trip stays graceful.\n return null;\n }\n const body = (await res.json().catch(() => ({}))) as { data?: { config?: unknown } };\n const config = body.data?.config;\n if (!config) return null;\n\n const sequencesDir = path.join(path.dirname(outPath), 'sequences');\n const sidecarSlug = slugify(live.name);\n const sidecarPath = path.join(sequencesDir, `${sidecarSlug}.yaml`);\n await fs.mkdir(sequencesDir, { recursive: true });\n const yaml = await import('yaml');\n await fs.writeFile(sidecarPath, yaml.stringify(config), 'utf-8');\n return sidecarPath;\n}\n\n/**\n * Read spec-source-only frontmatter from the prior file at `target` (if any).\n * Returns an empty `PullOptions` when no prior file exists.\n */\nasync function readPreservedFrontmatter(target: string): Promise<PullOptions> {\n try {\n const prior = await fs.readFile(target, 'utf-8');\n return extractPreservedFrontmatter(prior);\n } catch {\n return {};\n }\n}\n\nasync function fetchWorkflow(\n apiUrl: string,\n token: string,\n workflowId: string\n): Promise<PullWorkflow | null> {\n const res = await fetchOrExit(`${apiUrl}/workflows/${workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!res.ok) return null;\n const body = (await res.json().catch(() => ({}))) as { workflow?: PullWorkflow };\n return body.workflow ?? null;\n}\n\n/** List workflows (paginated), optionally filtered by status. */\nasync function fetchWorkflowList(\n apiUrl: string,\n token: string,\n status: string | undefined\n): Promise<Array<{ id: string; name: string }>> {\n const out: Array<{ id: string; name: string }> = [];\n let page = 1;\n for (;;) {\n const params = new URLSearchParams({ page: String(page), pageSize: '100' });\n if (status) params.set('status', status);\n const res = await fetchOrExit(`${apiUrl}/workflows?${params.toString()}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!res.ok) {\n console.error(`Error: failed to list workflows (${res.status})`);\n process.exit(1);\n }\n const body = (await res.json().catch(() => ({}))) as {\n data?: Array<{ id: string; name: string }>;\n pagination?: { page: number; totalPages: number };\n };\n for (const w of body.data ?? []) out.push({ id: w.id, name: w.name });\n const pg = body.pagination;\n if (!pg || pg.page >= pg.totalPages || (body.data ?? []).length === 0) break;\n page += 1;\n }\n return out;\n}\n\n/** Build a map of `workflowId` → spec file path across a directory tree. */\nexport async function indexSpecFiles(dir: string): Promise<Map<string, string>> {\n const map = new Map<string, string>();\n let entries: string[];\n try {\n entries = await fs.readdir(dir, { recursive: true });\n } catch {\n return map; // directory doesn't exist yet\n }\n const ID_RE = /^workflowId:\\s*([0-9a-f-]{36})\\s*$/im;\n for (const rel of entries) {\n if (!isSpecFile(rel)) continue;\n const full = path.join(dir, rel);\n try {\n const src = await fs.readFile(full, 'utf-8');\n const m = ID_RE.exec(src);\n if (m && !map.has(m[1])) map.set(m[1], full);\n } catch {\n // unreadable — skip\n }\n }\n return map;\n}\n\n/**\n * Index several spec roots and merge into one `workflowId` → path map.\n * Roots are processed in order and the FIRST occurrence of a `workflowId` wins,\n * so callers pass roots highest-precedence-first (see {@link readableSpecDirs}).\n * This is what keeps a `pull --all` from duplicating an existing legacy\n * `canary/specs` file under the new default `cofactor/specs`.\n */\nexport async function mergeSpecRoots(roots: string[]): Promise<Map<string, string>> {\n const byWorkflowId = new Map<string, string>();\n for (const root of roots) {\n for (const [id, file] of await indexSpecFiles(root)) {\n if (!byWorkflowId.has(id)) byWorkflowId.set(id, file);\n }\n }\n return byWorkflowId;\n}\n\n/** Pick a collision-free filename, disambiguating with a short id when needed. */\nexport function uniqueName(slug: string, workflowId: string, used: Set<string>): string {\n let name = `${slug}${specExtension}`;\n if (used.has(name)) name = `${slug}-${workflowId.slice(0, 8)}${specExtension}`;\n return name;\n}\n\nasync function runFreshCompile(opts: {\n argv: string[];\n apiUrl: string;\n token: string;\n file: string;\n emitted: ReturnType<typeof emitSpec>;\n}): Promise<void> {\n const { argv, apiUrl, token, file, emitted } = opts;\n\n const duplicateMatches = await fetchFreshCompileDuplicateWorkflows({\n argv,\n apiUrl,\n token,\n workflowName: emitted.name,\n });\n if (duplicateMatches.length > 0) {\n const message = formatDuplicateWorkflowTitleWarning({\n file,\n workflowName: emitted.name,\n matches: duplicateMatches,\n });\n if (hasFlag(argv, '--json')) {\n console.log(\n JSON.stringify(\n {\n ok: false,\n error: 'duplicate_workflow_title',\n message,\n workflows: duplicateMatches,\n },\n null,\n 2\n )\n );\n } else {\n console.error(message);\n }\n process.exit(1);\n }\n\n // `--publish` was removed with the approval/promotion lifecycle split — approval\n // is an explicit human act (canary workflow approve) and suite inclusion is a\n // separate promote step. Flows always compile as drafts.\n if (hasFlag(argv, '--publish')) {\n console.error(\n 'Error: --publish was removed. Compile the spec, then approve it ' +\n '(canary workflow approve <id> --yes) and promote it (canary workflow promote <id>).'\n );\n process.exit(1);\n }\n const status = emitted.status ?? 'draft';\n\n const payload: Record<string, unknown> = {\n name: emitted.name,\n description: emitted.description,\n preflight: emitted.preflight,\n cleanup: emitted.cleanup,\n status,\n flowType: emitted.flowType,\n createdVia: 'cli-spec',\n // The route only knows about (nodeType, configJson) — strip our meta.\n nodes: emitted.nodes.map(({ nodeType, configJson }) => ({ nodeType, configJson })),\n ...(emitted.inputsJson.length > 0 ? { inputsJson: emitted.inputsJson } : {}),\n ...(emitted.scenariosJson.length > 0 ? { scenariosJson: emitted.scenariosJson } : {}),\n };\n\n const res = await fetchOrExit(`${apiUrl}/workflows/create-with-nodes`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n },\n token,\n body: JSON.stringify(payload),\n });\n\n const body = (await res.json().catch(() => ({}))) as {\n ok: boolean;\n error?: string;\n warnings?: string[];\n workflow?: { id: string; name: string; status: string };\n nodes?: Array<{ id: string; nodeType: string; orderIndex: number }>;\n };\n\n if (!res.ok || !body.ok || !body.workflow) {\n console.error(`Error: ${body.error ?? `${res.status} ${res.statusText}`}`);\n process.exit(1);\n }\n\n // Write workflowId back to frontmatter + @id comments to each lead-in line.\n // Pair each emitted node with its sourceLine and the corresponding new\n // server-side node UUID (the route returns nodes in orderIndex order).\n const writebackPairs: Array<{ sourceLine: number; nodeId: string }> = [];\n if (body.nodes && body.nodes.length === emitted.nodes.length) {\n for (let i = 0; i < emitted.nodes.length; i++) {\n const sourceLine = emitted.nodes[i].sourceLine;\n if (sourceLine === undefined) continue;\n writebackPairs.push({ sourceLine, nodeId: body.nodes[i].id });\n }\n }\n\n try {\n const source = await fs.readFile(file, 'utf-8');\n const withWorkflowId = writeWorkflowId(source, body.workflow.id);\n // writeWorkflowId may insert a new frontmatter line, shifting all subsequent\n // line numbers by +1. Shift sourceLine accordingly so node @id markers land\n // on the lead-in (not on the blank line before it).\n const lineShift = withWorkflowId.split('\\n').length - source.split('\\n').length;\n const shiftedPairs = writebackPairs.map((p) => ({\n ...p,\n sourceLine: p.sourceLine + lineShift,\n }));\n const next = writeNodeIds(withWorkflowId, shiftedPairs);\n if (next !== source) {\n await fs.writeFile(file, next, 'utf-8');\n }\n } catch (err) {\n console.warn(\n `⚠ Workflow created, but failed to write IDs back to ${file}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n\n if (hasFlag(argv, '--json')) {\n console.log(JSON.stringify({ ok: true, workflow: body.workflow, writebackPairs }, null, 2));\n return;\n }\n\n const appUrl = resolveAppUrl(apiUrl);\n console.log(`\\n✓ Spec compiled: ${body.workflow.name}`);\n console.log(` ID: ${body.workflow.id}`);\n console.log(` Status: ${body.workflow.status}`);\n console.log(` Nodes: ${body.nodes?.length ?? 0}`);\n console.log(` URL: ${appUrl}/flows/${body.workflow.id}`);\n console.log(` → wrote workflowId + @id markers back to ${file}`);\n if (body.warnings && body.warnings.length > 0) {\n console.log('\\n Warnings:');\n for (const w of body.warnings) console.log(` ⚠ ${w}`);\n }\n\n if (hasFlag(argv, '--run-and-publish')) {\n console.log('\\nℹ --run-and-publish was removed. Run, approve, then promote:');\n console.log(` canary workflow run ${body.workflow.id}`);\n console.log(` canary workflow approve ${body.workflow.id} --yes`);\n console.log(` canary workflow promote ${body.workflow.id}`);\n }\n}\n\nexport const DUPLICATE_WORKFLOW_TITLE_ALLOW_FLAGS = [\n '--allow-duplicate',\n '--create-with-duplicate-title',\n] as const;\n\nexport type WorkflowTitleMatch = {\n id: string;\n name: string;\n status?: string;\n flowType?: string;\n};\n\nexport function allowsDuplicateWorkflowTitle(argv: string[]): boolean {\n return DUPLICATE_WORKFLOW_TITLE_ALLOW_FLAGS.some((flag) => hasFlag(argv, flag));\n}\n\nexport function exactWorkflowTitleMatches(\n workflows: ReadonlyArray<WorkflowTitleMatch>,\n workflowName: string\n): WorkflowTitleMatch[] {\n return workflows.filter((workflow) => workflow.name === workflowName);\n}\n\nexport function formatDuplicateWorkflowTitleWarning(opts: {\n file: string;\n workflowName: string;\n matches: ReadonlyArray<WorkflowTitleMatch>;\n}): string {\n const first = opts.matches[0];\n const stampedFile = stampedSpecPath(opts.file);\n const workflowLines = opts.matches\n .map((workflow) => {\n const details = [workflow.status, workflow.flowType].filter(Boolean).join(', ');\n return ` - ${workflow.id}${details ? ` (${details})` : ''}`;\n })\n .join('\\n');\n return [\n `Error: fresh spec compile would create a duplicate workflow named \"${opts.workflowName}\".`,\n '',\n 'Existing workflow(s):',\n workflowLines,\n '',\n 'To update an existing workflow instead, pull a stamped spec, re-apply your edits, then compile it:',\n ` canary spec pull ${first.id} --output ${stampedFile}`,\n ` canary spec compile ${stampedFile}`,\n '',\n 'To intentionally create another workflow with the same title, re-run with:',\n ` canary spec compile ${opts.file} --allow-duplicate`,\n ].join('\\n');\n}\n\nasync function fetchFreshCompileDuplicateWorkflows(opts: {\n argv: string[];\n apiUrl: string;\n token: string;\n workflowName: string;\n}): Promise<WorkflowTitleMatch[]> {\n if (allowsDuplicateWorkflowTitle(opts.argv)) return [];\n const params = new URLSearchParams({\n search: opts.workflowName,\n pageSize: '50',\n });\n const res = await fetchOrExit(`${opts.apiUrl}/workflows?${params.toString()}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${opts.token}` },\n token: opts.token,\n });\n if (!res.ok) {\n console.error(`Error: failed to check for existing workflow titles (${res.status})`);\n process.exit(1);\n }\n const body = (await res.json().catch(() => ({}))) as {\n data?: WorkflowTitleMatch[];\n };\n return exactWorkflowTitleMatches(body.data ?? [], opts.workflowName);\n}\n\nexport function stampedSpecPath(file: string): string {\n const split = splitSpecExtension(file);\n if (split) {\n return `${split.base}.stamped${split.ext}`;\n }\n return `${file}.stamped${specExtension}`;\n}\n\nasync function runRecompile(opts: {\n argv: string[];\n apiUrl: string;\n token: string;\n file: string;\n emitted: ReturnType<typeof emitSpec>;\n}): Promise<void> {\n const { argv, apiUrl, token, file, emitted } = opts;\n const workflowId = emitted.workflowId!;\n\n // 1. Fetch live workflow. GET /workflows/:id returns the workflow with\n // nodes + edges via getWorkflowWithDetails.\n const liveRes = await fetchOrExit(`${apiUrl}/workflows/${workflowId}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n token,\n });\n if (!liveRes.ok) {\n console.error(`Error: failed to fetch workflow ${workflowId} (${liveRes.status})`);\n process.exit(1);\n }\n const liveBody = (await liveRes.json().catch(() => ({}))) as {\n workflow?: LiveWorkflow & { snapshotVersion?: number };\n };\n const live = liveBody.workflow;\n if (!live) {\n console.error(`Error: workflow ${workflowId} not found (or no permission).`);\n process.exit(1);\n }\n\n // 2. Compute the diff.\n let diff: DiffResult;\n try {\n diff = diffWorkflow(emitted, live);\n } catch (err) {\n console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n\n if (!hasAnyChange(diff)) {\n console.log(`✓ ${file} — no changes (workflow ${workflowId} already in sync)`);\n return;\n }\n\n // 3. Apply.\n const ctx = { apiUrl, token, workflowId };\n const sync = await applyDiff(ctx, diff, emitted, live);\n\n // 4. Write back any new @id comments.\n if (sync.created.length > 0) {\n try {\n const source = await fs.readFile(file, 'utf-8');\n const next = writeNodeIds(source, sync.created);\n if (next !== source) await fs.writeFile(file, next, 'utf-8');\n } catch (err) {\n console.warn(\n `⚠ Recompile applied, but failed to write new @id markers to ${file}: ${err instanceof Error ? err.message : String(err)}`\n );\n }\n }\n\n // 5. Summary.\n const s = summarize(diff);\n const appUrl = resolveAppUrl(apiUrl);\n console.log(`\\n✓ Recompiled \"${live.name}\"`);\n console.log(\n ` +${s.created} created · ~${s.updated} updated · -${s.deleted} deleted · ↻ ${s.moved} moved`\n );\n console.log(` URL: ${appUrl}/flows/${workflowId}`);\n if (diff.nameDrift) {\n console.log(` ✎ renamed: \"${diff.nameDrift.from}\" → \"${diff.nameDrift.to}\"`);\n }\n if (live.promotion === 'promoted') {\n console.log(\n ` ⚠ workflow is promoted — suites run the live definition, so these changes take effect on the next suite run.`\n );\n }\n if (diff.duplicateSpecIds.length > 0) {\n console.log(\n ` ⚠ duplicate @id in spec (treated as fresh creates): ${diff.duplicateSpecIds.join(', ')}`\n );\n }\n if (diff.orphanedSpecIds.length > 0) {\n console.log(\n ` ⚠ stale @id in spec (no matching node — rebound to new): ${diff.orphanedSpecIds.join(', ')}`\n );\n }\n\n if (hasFlag(argv, '--json')) {\n console.log(JSON.stringify({ ok: true, workflowId, summary: s, diff }, null, 2));\n }\n}\n\ntype LoadResult =\n | { ok: true; ast: SpecAst; issues: SpecIssue[] }\n | { ok: false; issues: SpecIssue[] };\n\nasync function loadAndValidate(file: string): Promise<LoadResult> {\n let source: string;\n try {\n source = await fs.readFile(file, 'utf-8');\n } catch (err) {\n return {\n ok: false,\n issues: [\n {\n severity: 'error',\n message: `Could not read ${file}: ${err instanceof Error ? err.message : String(err)}`,\n },\n ],\n };\n }\n const parsed = parseSpec(source);\n if (!parsed.ok) return { ok: false, issues: parsed.issues };\n const lintIssues = lintSpec(parsed.ast);\n const allIssues = [...lintIssues];\n const hasError = allIssues.some((i) => i.severity === 'error');\n if (hasError) return { ok: false, issues: allIssues };\n return { ok: true, ast: parsed.ast, issues: allIssues };\n}\n\n/** Kebab-case slug for the default pull filename. */\nfunction slugify(name: string): string {\n const slug = name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 60);\n return slug || 'workflow';\n}\n\nfunction printIssues(issues: SpecIssue[], file: string): void {\n if (issues.length === 0) return;\n for (const i of issues) {\n const prefix = i.severity === 'error' ? '✗ error' : '⚠ warn';\n const loc = i.line ? `${file}:${i.line}` : file;\n console.error(`${prefix} ${loc}: ${i.message}`);\n if (i.hint) console.error(` hint: ${i.hint}`);\n }\n}\n\nexport { handleValidate, handleCompile };\n\n// Used by tests.\nexport { emitSpec, lintSpec, parseSpec };\n","/**\n * Apply a DiffResult to a live workflow via the existing REST endpoints\n * (`POST /workflows/:id/nodes`, `PUT /workflows/:id/nodes/:nodeId`,\n * `POST /workflows/:id/nodes/bulk-delete`, edge routes, `PUT /workflows/:id`).\n *\n * Returns the set of `(sourceLine → newNodeId)` pairs the writeback step\n * needs to stamp the spec file with fresh `@id` comments.\n *\n * @module spec/sync\n */\n\nimport { fetchOrExit } from '../api-fetch.js';\nimport {\n reconcileLinearEdges,\n type DiffResult,\n type EmittedWorkflow,\n type LiveEdge,\n type LiveWorkflow,\n type SpecOp,\n type WorkflowFieldChanges,\n} from '@chatsdet/canary-spec';\n\nexport type SyncContext = {\n apiUrl: string;\n token: string;\n workflowId: string;\n};\n\nexport type SyncResult = {\n /** New nodes that were created — caller uses these to write `@id` back to the spec. */\n created: Array<{ sourceLine: number; nodeId: string }>;\n};\n\nexport async function applyDiff(\n ctx: SyncContext,\n diff: DiffResult,\n emitted: EmittedWorkflow,\n live: LiveWorkflow,\n): Promise<SyncResult> {\n // 1. Bulk-delete removed nodes (reconnect: true so adjacent edges heal).\n const deletes = diff.ops.filter((o): o is Extract<SpecOp, { kind: 'delete' }> => o.kind === 'delete');\n if (deletes.length > 0) {\n await postJson(ctx, `/workflows/${ctx.workflowId}/nodes/bulk-delete`, {\n nodeIds: deletes.map((o) => o.nodeId),\n reconnect: true,\n });\n }\n\n // 2. Create new nodes. The route assigns final orderIndex, but we send the\n // desired orderIndex so it lands at the right position. We need final IDs\n // back for both writeback and downstream edge reconciliation.\n const creates = diff.ops.filter((o): o is Extract<SpecOp, { kind: 'create' }> => o.kind === 'create');\n const createdIds = new Map<number, string>(); // specIndex → new node UUID\n const writebackPairs: SyncResult['created'] = [];\n for (const op of creates) {\n const res = await postJson<{ ok?: boolean; node?: { id: string }; error?: string }>(\n ctx,\n `/workflows/${ctx.workflowId}/nodes`,\n {\n nodeType: op.nodeType,\n configJson: op.configJson,\n orderIndex: op.orderIndex,\n },\n );\n const created = res.node;\n if (!created) {\n throw new Error(\n `Failed to create node at orderIndex ${op.orderIndex}: ${res.error ?? JSON.stringify(res)}`,\n );\n }\n createdIds.set(op.specIndex, created.id);\n if (op.sourceLine !== undefined) {\n writebackPairs.push({ sourceLine: op.sourceLine, nodeId: created.id });\n }\n }\n\n // 3. Update existing nodes whose config or orderIndex changed.\n const updates = diff.ops.filter((o): o is Extract<SpecOp, { kind: 'update' }> => o.kind === 'update');\n for (const op of updates) {\n if (!op.changes.configChanged && !op.changes.orderChanged) continue;\n await putJson(ctx, `/workflows/${ctx.workflowId}/nodes/${op.nodeId}`, {\n ...(op.changes.configChanged ? { configJson: op.configJson } : {}),\n ...(op.changes.orderChanged ? { orderIndex: op.orderIndex } : {}),\n });\n }\n\n // 4. Edge reconciliation. Spec authors a single linear default chain; we\n // re-derive the expected edges from final node order and DELETE+POST to\n // converge. Non-default edges (condition then/else) are left alone.\n if (diff.ops.length > 0) {\n const finalNodeIds = computeFinalNodeOrder(diff, live, createdIds);\n const fresh = await getJson<{ workflow?: { edges?: LiveEdge[] } }>(\n ctx,\n `/workflows/${ctx.workflowId}`,\n );\n const liveEdges = fresh.workflow?.edges ?? live.edges ?? [];\n const { ops: edgeOps } = reconcileLinearEdges(finalNodeIds, liveEdges);\n for (const op of edgeOps) {\n if (op.kind === 'delete') {\n await deleteJson(ctx, `/workflows/${ctx.workflowId}/edges/${op.edgeId}`);\n } else {\n await postJson(ctx, `/workflows/${ctx.workflowId}/edges`, {\n sourceNodeId: op.sourceNodeId,\n targetNodeId: op.targetNodeId,\n edgeType: 'default',\n });\n }\n }\n }\n\n // 5. Workflow-level fields. Some fields require name → id resolution against\n // org/property scope (tags, environments, sandboxTemplate); those land in\n // a follow-up PR. Surface a clear error rather than silently dropping the\n // edit, so the author isn't surprised by missing changes.\n rejectUnsupportedFieldEdits(diff.workflowFieldChanges);\n if (Object.keys(diff.workflowFieldChanges).length > 0) {\n const payload = await resolveFieldUpdatePayload(ctx, diff.workflowFieldChanges);\n await putJson(ctx, `/workflows/${ctx.workflowId}`, payload);\n }\n\n // 6. Optional api_sequence DSL upsert. The spec carries the full\n // ApiSequenceConfig in a YAML sidecar referenced from any api_sequence\n // node; here we POST it to `/api-sequences/save`, which upserts the\n // workflow-scoped row keyed by workflowId. Skipping when the sidecar is\n // absent leaves any existing live config untouched (no implicit clear).\n if (emitted.apiSequenceConfig) {\n await postJson(ctx, `/api-sequences/save`, {\n workflowId: ctx.workflowId,\n config: emitted.apiSequenceConfig,\n });\n }\n\n return { created: writebackPairs };\n}\n\n/**\n * Look up a teardown workflow by name in the org. Matches on (name, flowType\n * === 'teardown'); throws when missing so the author gets a clear \"compile the\n * teardown spec first\" message instead of a silent FK clear.\n */\nasync function resolveTeardownWorkflowId(\n ctx: SyncContext,\n specName: string,\n): Promise<string> {\n const res = await getJson<{\n ok?: boolean;\n data?: Array<{ id: string; name: string; flowType: string }>;\n }>(\n ctx,\n `/workflows?search=${encodeURIComponent(specName)}&flowType=teardown&pageSize=50`,\n );\n const match = (res.data ?? []).find(\n (w) => w.name === specName && w.flowType === 'teardown',\n );\n if (!match) {\n throw new Error(\n `Teardown workflow \"${specName}\" not found (flowType=teardown). ` +\n `Compile the teardown spec first so the seed can link to it by name.`,\n );\n }\n return match.id;\n}\n\n/**\n * Map name-form workflow fields to their wire shape. Currently only teardown\n * and seedOutputs need lookup; tags/environments/sandboxTemplate are gated by\n * `rejectUnsupportedFieldEdits` above and never reach here.\n */\nasync function resolveFieldUpdatePayload(\n ctx: SyncContext,\n changes: WorkflowFieldChanges,\n): Promise<Record<string, unknown>> {\n const payload = fieldUpdatePayload(changes);\n // teardownSpec carries the name; the API expects teardownWorkflowId.\n if ('teardownSpec' in payload) {\n const specName = payload.teardownSpec;\n delete payload.teardownSpec;\n if (specName === null) {\n payload.teardownWorkflowId = null;\n } else if (typeof specName === 'string') {\n payload.teardownWorkflowId = await resolveTeardownWorkflowId(ctx, specName);\n }\n }\n // teardownSnapshotVersion: 'latest' → null on the wire (DB convention).\n if ('teardownSnapshotVersion' in payload) {\n if (payload.teardownSnapshotVersion === 'latest') payload.teardownSnapshotVersion = null;\n }\n // seedOutputs: rename to `seedOutputsJson` and resolve propertyName → id.\n // The route's normalizeSeedOutputs accepts entries with propertyId set;\n // we ferry across credentialOutput/attributes verbatim per the diff layer.\n if ('seedOutputs' in payload) {\n const inputRows = payload.seedOutputs as\n | Array<{\n dataKey: string;\n label?: string;\n description?: string;\n propertyName?: string;\n credentialOutput?: unknown;\n attributes?: unknown;\n }>\n | undefined;\n delete payload.seedOutputs;\n if (inputRows) {\n const propIds = await resolvePropertyNameMap(\n ctx,\n Array.from(new Set(inputRows.map((r) => r.propertyName).filter((p): p is string => !!p))),\n );\n payload.seedOutputsJson = inputRows.map((r) => {\n const propertyId = r.propertyName ? propIds.get(r.propertyName) : undefined;\n if (r.propertyName && !propertyId) {\n throw new Error(\n `Declared output \"${r.dataKey}\": property \"${r.propertyName}\" not found in this org.`,\n );\n }\n return {\n dataKey: r.dataKey,\n name: r.dataKey,\n displayLabel: r.label ?? r.dataKey,\n ...(r.description ? { description: r.description } : {}),\n ...(propertyId ? { propertyId } : {}),\n ...(r.credentialOutput !== undefined ? { credentialOutput: r.credentialOutput } : {}),\n ...(r.attributes !== undefined ? { attributes: r.attributes } : {}),\n };\n });\n }\n }\n return payload;\n}\n\n/**\n * Look up property ids by name in a single round trip. Used for seed-output\n * declarations where the spec author writes `property: <name>` and sync needs\n * the propertyId for the wire payload.\n */\nasync function resolvePropertyNameMap(\n ctx: SyncContext,\n names: string[],\n): Promise<Map<string, string>> {\n const out = new Map<string, string>();\n if (names.length === 0) return out;\n const res = await getJson<{\n ok?: boolean;\n data?: Array<{ id: string; name: string }>;\n properties?: Array<{ id: string; name: string }>;\n }>(ctx, `/org/properties`);\n const all = res.data ?? res.properties ?? [];\n for (const want of names) {\n const match = all.find((p) => p.name === want);\n if (match) out.set(want, match.id);\n }\n return out;\n}\n\nexport class UnsupportedFieldEditError extends Error {\n constructor(fields: string[]) {\n super(\n `Editing ${fields.join(', ')} via a spec file is not supported yet. ` +\n `These fields require name → id resolution that lands in a follow-up. ` +\n `Edit them in the Canary UI for now; the rest of your changes will still apply.`,\n );\n this.name = 'UnsupportedFieldEditError';\n }\n}\n\nexport function rejectUnsupportedFieldEdits(changes: WorkflowFieldChanges): void {\n const unsupported = (['tags', 'environmentConstraints', 'sandboxTemplate'] as const).filter(\n (k) => changes[k] !== undefined,\n );\n if (unsupported.length > 0) throw new UnsupportedFieldEditError(unsupported);\n}\n\n/**\n * Build the final ordered list of node UUIDs after applying the diff —\n * positions correspond to the spec's order, IDs resolved from either the live\n * workflow (for UPDATE) or the new-node map (for CREATE).\n */\nfunction computeFinalNodeOrder(\n diff: DiffResult,\n live: LiveWorkflow,\n createdIds: Map<number, string>,\n): string[] {\n const liveById = new Map(live.nodes.map((n) => [n.id, n] as const));\n const total = diff.ops.filter((o) => o.kind !== 'delete').length;\n const result: string[] = Array.from({ length: total });\n for (const op of diff.ops) {\n if (op.kind === 'create') {\n const id = createdIds.get(op.specIndex);\n if (!id) throw new Error(`Missing created node id for specIndex ${op.specIndex}`);\n result[op.specIndex] = id;\n } else if (op.kind === 'update') {\n if (!liveById.has(op.nodeId)) {\n throw new Error(`UPDATE references nodeId ${op.nodeId} not in live workflow`);\n }\n result[op.specIndex] = op.nodeId;\n }\n }\n return result;\n}\n\nfunction fieldUpdatePayload(changes: WorkflowFieldChanges): Record<string, unknown> {\n // PUT /workflows/:id accepts each field independently; pass through only\n // what changed. Scenario `skipReason` is spec-source-only — the API's\n // `normalizeScenarios` strips it, but stripping here keeps the wire payload\n // honest and the round-trip test deterministic.\n const payload: Record<string, unknown> = {};\n for (const k of Object.keys(changes) as (keyof WorkflowFieldChanges)[]) {\n if (k === 'scenariosJson' && Array.isArray(changes.scenariosJson)) {\n payload.scenariosJson = (changes.scenariosJson as Array<Record<string, unknown>>).map((s) => {\n const { skipReason: _skip, ...rest } = s;\n return rest;\n });\n } else {\n payload[k] = changes[k];\n }\n }\n return payload;\n}\n\nasync function postJson<T = unknown>(\n ctx: SyncContext,\n path: string,\n body: unknown,\n): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n 'Content-Type': 'application/json',\n },\n token: ctx.token,\n body: JSON.stringify(body),\n });\n return (await res.json()) as T;\n}\n\nasync function putJson<T = unknown>(\n ctx: SyncContext,\n path: string,\n body: unknown,\n): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'PUT',\n headers: {\n Authorization: `Bearer ${ctx.token}`,\n 'Content-Type': 'application/json',\n },\n token: ctx.token,\n body: JSON.stringify(body),\n });\n return (await res.json()) as T;\n}\n\nasync function getJson<T = unknown>(ctx: SyncContext, path: string): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'GET',\n headers: { Authorization: `Bearer ${ctx.token}` },\n token: ctx.token,\n });\n return (await res.json()) as T;\n}\n\nasync function deleteJson<T = unknown>(ctx: SyncContext, path: string): Promise<T> {\n const res = await fetchOrExit(`${ctx.apiUrl}${path}`, {\n method: 'DELETE',\n headers: { Authorization: `Bearer ${ctx.token}` },\n token: ctx.token,\n });\n return (await res.json()) as T;\n}\n"],"mappings":"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","names":["path"]}