@invinite-org/chartlang-adapter-kit 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/CHANGELOG.md +1380 -0
  2. package/LICENSE +21 -0
  3. package/README.md +69 -0
  4. package/dist/base/bufferingAdapter.d.ts +52 -0
  5. package/dist/base/bufferingAdapter.d.ts.map +1 -0
  6. package/dist/base/bufferingAdapter.js +68 -0
  7. package/dist/base/bufferingAdapter.js.map +1 -0
  8. package/dist/base/index.d.ts +3 -0
  9. package/dist/base/index.d.ts.map +1 -0
  10. package/dist/base/index.js +5 -0
  11. package/dist/base/index.js.map +1 -0
  12. package/dist/base/passThroughAdapter.d.ts +49 -0
  13. package/dist/base/passThroughAdapter.d.ts.map +1 -0
  14. package/dist/base/passThroughAdapter.js +61 -0
  15. package/dist/base/passThroughAdapter.js.map +1 -0
  16. package/dist/capabilities/capabilities.d.ts +336 -0
  17. package/dist/capabilities/capabilities.d.ts.map +1 -0
  18. package/dist/capabilities/capabilities.js +616 -0
  19. package/dist/capabilities/capabilities.js.map +1 -0
  20. package/dist/capabilities/index.d.ts +2 -0
  21. package/dist/capabilities/index.d.ts.map +1 -0
  22. package/dist/capabilities/index.js +4 -0
  23. package/dist/capabilities/index.js.map +1 -0
  24. package/dist/defineAdapter.d.ts +74 -0
  25. package/dist/defineAdapter.d.ts.map +1 -0
  26. package/dist/defineAdapter.js +55 -0
  27. package/dist/defineAdapter.js.map +1 -0
  28. package/dist/index.d.ts +12 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +9 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/mocks/index.d.ts +3 -0
  33. package/dist/mocks/index.d.ts.map +1 -0
  34. package/dist/mocks/index.js +4 -0
  35. package/dist/mocks/index.js.map +1 -0
  36. package/dist/mocks/mockCandleSource.d.ts +68 -0
  37. package/dist/mocks/mockCandleSource.d.ts.map +1 -0
  38. package/dist/mocks/mockCandleSource.js +61 -0
  39. package/dist/mocks/mockCandleSource.js.map +1 -0
  40. package/dist/types.d.ts +655 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +4 -0
  43. package/dist/types.js.map +1 -0
  44. package/dist/validation/decodeDrawing.d.ts +29 -0
  45. package/dist/validation/decodeDrawing.d.ts.map +1 -0
  46. package/dist/validation/decodeDrawing.js +35 -0
  47. package/dist/validation/decodeDrawing.js.map +1 -0
  48. package/dist/validation/index.d.ts +4 -0
  49. package/dist/validation/index.d.ts.map +1 -0
  50. package/dist/validation/index.js +5 -0
  51. package/dist/validation/index.js.map +1 -0
  52. package/dist/validation/validateEmission.d.ts +70 -0
  53. package/dist/validation/validateEmission.d.ts.map +1 -0
  54. package/dist/validation/validateEmission.js +1481 -0
  55. package/dist/validation/validateEmission.js.map +1 -0
  56. package/package.json +41 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,1380 @@
1
+ # @invinite-org/chartlang-adapter-kit
2
+
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 4d44a9c: Add a `"history-then-stream"` mode to `mockCandleSource` plus a `streamTail` option (default `1`, clamped to `[0, bars.length]`). The new mode emits a single warm-up history batch containing every bar except the trailing `streamTail` bars, then yields one `close` event per remaining bar. Lets a consumer paint a chart instantly from history and still receive a few per-bar ticks afterwards — the missing combination for the React demo pane, the conformance scenarios, and any "live editor" UI. The existing `"history"` and `"stream"` modes are unchanged.
8
+
9
+ ### Patch Changes
10
+
11
+ - d1de692: Fix end-user-blocking Node-ESM packaging bug. Every published `dist/index.js` previously failed to load under Node's strict ESM resolver because `tsc` had been configured with `moduleResolution: "Bundler"` and emitted relative specifiers verbatim, so `dist/index.js` carried `from "./api"` (extensionless) and Node rejected the resolution. Workspace consumers never saw this because tsx / vitest / Vite resolve loosely, but `npm install @invinite-org/chartlang-compiler` followed by `import` failed immediately for any Node consumer, and `examples/react-demo/vite.config.ts`'s server-side compile plugin broke at dev-config-load time.
12
+
13
+ This release switches `tsconfig.base.json` to `module: "NodeNext"` / `moduleResolution: "NodeNext"`, and rewrites every relative import / export / dynamic-import / `typeof import("…")` specifier across all packages' source to carry an explicit `.js` (or `/index.js`) suffix. The new resolution mode also surfaces this bug class as a compile error rather than runtime breakage, so it cannot regress.
14
+
15
+ No behavioural change for runtime consumers — the rewritten specifiers resolve to the same TypeScript sources at build time and the same `dist/<path>.js` files at consumer-load time.
16
+
17
+ - Updated dependencies [d1de692]
18
+ - Updated dependencies [98599b2]
19
+ - @invinite-org/chartlang-core@1.0.1
20
+
21
+ ## 1.0.0
22
+
23
+ ### Major Changes
24
+
25
+ - chartlang `1.0.0` -- the `apiVersion: 1` standard.
26
+
27
+ - `apiVersion: 1` frozen: compiler accepts only the frozen language
28
+ version; `STATEFUL_PRIMITIVES` locked at 172 entries by exact
29
+ name-set; every shipping export `@stable`; pre-1.0 deprecations
30
+ removed (`PHASE_1_SCENARIOS`).
31
+ - Canonical language spec published (`docs/spec/`): grammar,
32
+ semantics, manifest, emissions, versioning -- self-contained for
33
+ alternate implementations. The `v1.0.0` tag is the frozen spec
34
+ snapshot.
35
+ - Public conformance reports: `pnpm conformance --report` emits
36
+ `CONFORMANCE.md` + `conformance-report.json`; canvas2d reference
37
+ report published and drift-gated.
38
+ - Adapter-author path proven end-to-end: scaffolded adapters ship a
39
+ wired conformance test; full writing-an-adapter tutorial +
40
+ Lightweight Charts porting walkthrough.
41
+ - Pine migration guide finalised with a pattern-coverage matrix
42
+ audited against the top ~50 Pine scripts.
43
+
44
+ ### Minor Changes
45
+
46
+ - d14a034: Add phase 5 server alerts, multi-timeframe request handling, runtime persistence, QuickJS hosting, expanded plot and table rendering, color helpers, alert conditions, and volume profile primitives.
47
+
48
+ ### Patch Changes
49
+
50
+ - Freeze `apiVersion: 1`: release-grade compiler diagnostics for version
51
+ mismatches, an exact name-set lock on the 172-entry `STATEFUL_PRIMITIVES`
52
+ registry, and freeze-contract documentation on pinned surfaces. No behavioural
53
+ change: the structural check already enforced `apiVersion: 1`.
54
+ - Pre-1.0 surface cleanup: remove the deprecated `PHASE_1_SCENARIOS`
55
+ alias (use `ALL_SCENARIOS`) and promote every shipping export from
56
+ `@experimental` to `@stable` ahead of the `apiVersion: 1` freeze.
57
+ - Updated dependencies [d14a034]
58
+ - Updated dependencies [3cfff10]
59
+ - Updated dependencies [3cfff10]
60
+ - Updated dependencies [3cfff10]
61
+ - Updated dependencies [3cfff10]
62
+ - Updated dependencies
63
+ - Updated dependencies
64
+ - Updated dependencies
65
+ - @invinite-org/chartlang-core@1.0.0
66
+
67
+ ## 0.5.0
68
+
69
+ ### Phase 5
70
+
71
+ #### Minor Changes
72
+
73
+ - Ship Phase 5 `defineAlertCondition`, compiler manifest extraction, runtime `signal()` emissions, adapter validation, and conformance coverage per PLAN §11.2.
74
+ - Add `draw.table` with `TableCell`/`TablePosition` types, runtime emission,
75
+ viewport-anchored canvas2d rendering, and conformance coverage per PLAN §10.2.
76
+ - Add Phase 5 plot kinds, runtime emission dispatch, validation, conformance scenarios, and canvas2d reference renderers.
77
+ - Add the Phase 5 `runtime.log.*` and `runtime.error()` surface, log emissions, runtime halt diagnostics, and conformance coverage.
78
+ - Add the PLAN.md §6.9 persistent runtime snapshot store, warm-start restore flow, close/dispose snapshot saves, and snapshot diagnostics.
79
+ - Replace the Phase 4 `request.security` NaN-only path with real
80
+ multi-timeframe secondary stream alignment per PLAN.md §6.8 and §7.2.
81
+ Adapters can route tagged `CandleEvent.streamKey` candles, the worker
82
+ host dispatches them through `ScriptRunner.push`, conformance includes
83
+ MTF scenarios, and the private canvas2d reference adapter now declares
84
+ `multiTimeframe: true`.
85
+ - Add `ta.fixedRangeVolumeProfile`, completing the Phase 5 volume-profile set
86
+ from PLAN §9.2 and §10.1.1 with fixed `[from, to]` anchors, frozen post-range
87
+ histograms, and `fixed-range-inverted` diagnostics. Ported from invinite
88
+ commit `3234c8c0c3f9880d9d1e3a3ee63ebd55ddd535f4`.
89
+ - Port `ta.sessionVolumeProfile` from invinite commit 3234c8c0c3f9880d9d1e3a3ee63ebd55ddd535f4, adding the PLAN §9.2 horizontal-histogram session volume-profile primitive, PLAN §4.8 syminfo-session fallback diagnostics, and compiler/runtime registration.
90
+
91
+ #### Patch Changes
92
+
93
+ - Updated dependencies
94
+ - Updated dependencies
95
+ - Updated dependencies
96
+ - Updated dependencies
97
+ - Updated dependencies
98
+ - Updated dependencies
99
+ - Updated dependencies
100
+ - Updated dependencies
101
+ - Updated dependencies
102
+ - Updated dependencies
103
+ - Updated dependencies
104
+ - @invinite-org/chartlang-core@0.5.0
105
+
106
+ ## 0.4.0
107
+
108
+ ### Minor Changes
109
+
110
+ - 3f3ce38: Replace the Phase-0 placeholder with the Phase-1 adapter contract:
111
+ `Adapter` / `Capabilities` / `CandleEvent` types and the §7.3 emission
112
+ shapes, capability builders (`capabilities.line()` / `.allLines()` /
113
+ `.alerts(...)` / `.union(...)`), `defineAdapter` factory, hand-rolled
114
+ `validateEmission` (no `zod` / `valibot` dependency) covering every
115
+ Phase-1 emission and meta walker, `decodeDrawing` Phase-1 stub,
116
+ `mockCandleSource` for test playback, and `PassThroughAdapter` /
117
+ `BufferingAdapter` base classes for runtime + conformance fixtures.
118
+ - 38fb475: Phase 2 — `0.2` full indicator parity.
119
+
120
+ - 81 new `ta.*` primitives (6 cross-functional + 75 §9.2 ports);
121
+ `TA_REGISTRY` cardinality 9 -> 90; `STATEFUL_PRIMITIVES`
122
+ cardinality 12 -> 93.
123
+ - 5 new chained-MA helpers + 5 new stats/volatility helpers in
124
+ `packages/runtime/src/ta/lib/`.
125
+ - 6 new `PlotKind`s (histogram, bars, area, filled-band, label,
126
+ marker) + canvas2d renderers + `validateEmission` arms.
127
+ - `Bar` extended with `hl2` / `hlc3` / `ohlc4` / `hlcc4` derived
128
+ source fields — runtime already pre-computes on `BarView`.
129
+ - `Scenario` extended with `inlineSource?: string` so Phase-2
130
+ scenarios stay self-contained without bloating
131
+ `examples/scripts/`.
132
+ - `STATEFUL_PRIMITIVES` shape widened from `ReadonlySet<string>`
133
+ to `ReadonlySet<{ name: string; slot: boolean }>` to support
134
+ `ta.nz` (the only stateless `ta.*`).
135
+ - Universal `opts.offset` honoured on every `ta.*` primitive
136
+ (Phase-1 backfill in Task 29).
137
+ - `chartlang docs` subcommand generates
138
+ `docs/primitives/ta/<id>.md` per primitive.
139
+ - `PHASE_2_INDICATORS` + `PHASE_5_DEFERRED` inventories exported
140
+ from `@invinite-org/chartlang-conformance` and pinned by
141
+ `phase2Coverage.test.ts` (Task 30).
142
+ - 100% coverage maintained across every published package.
143
+ - `apiVersion: 1` script header unchanged; Phase 2 is additive
144
+ at runtime.
145
+
146
+ - 38fb475: Phase-2 Task 1 — three foundational widenings every subsequent
147
+ Phase-2 port depends on:
148
+
149
+ 1. **`PlotKind` expansion (3 → 9).** Adds `histogram`, `bars`,
150
+ `area`, `filled-band`, `label`, `marker` per PLAN.md §7.3. The
151
+ `PlotStyle` discriminated union in
152
+ `@invinite-org/chartlang-adapter-kit` extends in lockstep; the
153
+ `validateEmission` switch grows matching arms with per-kind
154
+ payload rules; the `capabilities` builder gains `histogram()` /
155
+ `bars()` / `area()` / `filledBand()` / `label()` / `marker()` /
156
+ `allPhase2Plots()`. The canvas2d reference adapter ships six new
157
+ pure-on-`RenderCtx` renderers (`render/histogram.ts`, `bars.ts`,
158
+ `area.ts`, `filledBand.ts`, `label.ts`, `marker.ts`) and flips
159
+ `CANVAS2D_CAPABILITIES.plots` to `capabilities.allPhase2Plots()`
160
+ (9 kinds). `RenderCtx` + `MockCanvas2DContext` extend with
161
+ `fillText`, `globalAlpha`, `font`, `textAlign`, `textBaseline`.
162
+
163
+ 2. **`Bar` derived sources.** Extends the script-facing `Bar`
164
+ (`packages/core/src/types.ts`) with the four pre-computed derived
165
+ sources `hl2` / `hlc3` / `ohlc4` / `hlcc4`. The runtime's
166
+ `BarView` (`packages/runtime/src/streamState.ts`) already
167
+ populates these on every close — Phase 2 surfaces them so authors
168
+ can write `ta.cci(bar.hlc3, 20)` like Pine. No runtime change.
169
+
170
+ 3. **`Scenario.inlineSource`.** Extends the conformance `Scenario`
171
+ type (`packages/conformance/src/runConformanceSuite.ts`) with an
172
+ optional `inlineSource?: string` field that is mutually exclusive
173
+ with the existing `scriptPath?: string`. `runConformanceSuite`
174
+ writes the inline source to the existing `.cache/` tmp file and
175
+ compiles + imports it exactly like the `scriptPath` branch, with
176
+ a virtual `<inline:${id}>.chart.ts` `sourcePath` so callsite-id
177
+ injection produces stable, pinnable slot ids. Phase-2 ports use
178
+ this to carry their `defineIndicator` source inline rather than
179
+ spawning 80+ files in `examples/scripts/`.
180
+
181
+ The new `PLOT_KIND_COVERAGE_SCENARIO` exercises the `inlineSource`
182
+ path + the wider capability surface end-to-end (one inline
183
+ `plot(bar.close)` + `hline(50)` script; asserts no
184
+ `unsupported-plot-kind` and no `malformed-emission` diagnostics
185
+ fire). Per-port Phase-2 tasks (Tasks 21+) each add their own
186
+ scenario asserting the specific new kind's drained emissions once
187
+ the runtime acquires the matching emission path.
188
+
189
+ No runtime / host-worker source-level changes in this task —
190
+ `BarView` already carries the four derived fields, and the
191
+ `PlotKind` expansion is additive at every consumer.
192
+
193
+ - b0d296b: Phase 3 closeout — `0.3` "Full Drawing Parity".
194
+
195
+ 61 drawing kinds across 13 categories ship under `draw.*` with the
196
+ full §22.10 set per kind (impl + property + golden + bench + JSDoc
197
+
198
+ - conformance scenario + auto-generated docs page). 5-bucket
199
+ `DrawingCounts` budget, per-kind capability gating, `DrawingHandle`
200
+ across-bar stability, real-impl `validateEmission` + `decodeDrawing`,
201
+ `drawing-hash` conformance assertion variant, 13 category + 1
202
+ umbrella capability builders, canvas2d reference adapter renders
203
+ every kind, `defineDrawing` constructor for interactive tools.
204
+
205
+ Final cardinalities: `STATEFUL_PRIMITIVES.size === 154` (93 Phase-2
206
+
207
+ - 61 Phase-3 `draw.*` entries); `DRAWING_KINDS.length === 61`.
208
+
209
+ Per-bucket kind tally pinned by `bucketFor` (6 + 5 + 6 + 25 + 19 = 61):
210
+
211
+ - `lines` (6): `line`, `horizontal-line`, `horizontal-ray`,
212
+ `vertical-line`, `cross-line`, `trend-angle`.
213
+ - `boxes` (5): `rectangle`, `rotated-rectangle`, `triangle`,
214
+ `circle`, `ellipse`.
215
+ - `labels` (6): `marker`, `text`, `arrow`, `arrow-marker`,
216
+ `arrow-mark-up`, `arrow-mark-down`.
217
+ - `polylines` (25): `polyline`, `path`, `arc`, `curve`,
218
+ `double-curve`, `pen`, `highlighter`, `brush`,
219
+ `trend-channel`, `flat-top-bottom`, `disjoint-channel`,
220
+ `regression-trend`, `pitchfork`, `pitchfan`, `xabcd-pattern`,
221
+ `cypher-pattern`, `head-and-shoulders`, `abcd-pattern`,
222
+ `triangle-pattern`, `three-drives-pattern`,
223
+ `elliott-impulse-wave`, `elliott-correction-wave`,
224
+ `elliott-triangle-wave`, `elliott-double-combo`,
225
+ `elliott-triple-combo`.
226
+ - `other` (19): 10 `fib-*` + 4 `gann-*` + 3 cycles
227
+ (`cyclic-lines`, `time-cycles`, `sine-line`) + 2 containers
228
+ (`group`, `frame`).
229
+
230
+ Conformance scenarios: 61 per-kind + 12 task bundles +
231
+ `drawAll61` + `drawBudgetOverflow` + `drawUnsupportedKind` = **76**.
232
+ Docs: 61 auto-generated `docs/primitives/draw/<kind>.md` pages +
233
+ 1 hand-written `index.md`.
234
+
235
+ Variant collapses pinned in Task 1 (carried forward unchanged):
236
+
237
+ - `pitchfork.variant: "standard" | "schiff" | "modified-schiff" | "inside"`
238
+ collapses the 4 invinite pitchfork tools.
239
+ - `line.{extendLeft, extendRight}` collapses the `ray` /
240
+ `extended-line` tools.
241
+ - `cypherPattern` ships as a `defineDrawing`-only kind (no
242
+ standalone interactive tool).
243
+
244
+ Compiler: `callsiteIdInjection` recognises every `draw.*` callable
245
+ via the widened 154-entry `STATEFUL_PRIMITIVES`;
246
+ `statefulCallInLoop` flags `draw.*` in unbounded loops with the
247
+ existing `stateful-call-inside-loop` error.
248
+
249
+ Bench thresholds (re-verified post-Phase-3 on Apple-silicon):
250
+
251
+ - `pushDrawing.bench.test.ts` — 10 000 line drawings under 2 000ms
252
+ wall-clock (`ceil(median × 3)` per §22.10; no drift across
253
+ Tasks 4–18 — the budget/validate path is independent of
254
+ per-kind canvas renderers). `pnpm bench:ci` median ~180ms.
255
+ - The Phase-2 ta / ringBuffer / seriesView / onBarClose /
256
+ plot / hline bench thresholds were bumped from the
257
+ `200/250/300/400/500/600ms` solo-run pins to a uniform `1500ms`
258
+ (3000ms for plot + hline) to absorb the parallel-worker
259
+ scheduling overhead during workspace `pnpm test` (665 test
260
+ files in parallel). Solo `pnpm bench:ci` medians remain in the
261
+ 10–200ms range — well under both old and new thresholds — so
262
+ this is a noise-floor adjustment, not a perf-regression
263
+ accommodation.
264
+
265
+ `apiVersion: 1` script header unchanged; Phase 3 is additive at
266
+ runtime.
267
+
268
+ - b0d296b: Phase 3 Task 10 — Channels (`trendChannel` / `flatTopBottom` /
269
+ `disjointChannel` / `regressionTrend`).
270
+
271
+ - **adapter-kit** — 4 new per-kind validators (`validateTrendChannelState`,
272
+ `validateFlatTopBottomState`, `validateDisjointChannelState`,
273
+ `validateRegressionTrendState`) + 1 file-local style helper
274
+ (`validateRegressionTrendOpts` with the
275
+ `close|open|high|low|hl2|hlc3|ohlc4|hlcc4` source whitelist). The
276
+ `regression-trend` validator enforces `anchors[0].time <
277
+ anchors[1].time` and `stdevMultiplier >= 0`.
278
+ - **runtime** — 4 new emit functions under
279
+ `packages/runtime/src/emit/draw/channels/` wired into `DRAW_NAMESPACE`.
280
+ `regressionTrend` carries the 4-arg form
281
+ `(slotId, a: WorldPoint, b: WorldPoint, opts?)`. The Phase-2
282
+ `linearRegression` + `LinearRegressionFrame` helper graduates to the
283
+ public runtime surface so consumer adapters can compute the OLS fit
284
+ without duplicating math.
285
+ - **canvas2d-adapter** — 4 new renderers + dispatch wiring. The
286
+ `regression-trend` renderer strokes a placeholder anchor-to-anchor
287
+ line; the actual OLS fit + σ bands require bar-buffer access not
288
+ exposed by the current `Viewport` (see
289
+ `tasks/phase-3-drawing-parity/10-channels.plan.md` §3). `trendChannel`
290
+ / `flatTopBottom` / `disjointChannel` are stroke-only (no fill polygon
291
+ between rails — see plan §5).
292
+ - **conformance** — 5 new scenarios (4 per-kind + 1
293
+ `drawChannelsAll` bundle) with pinned `drawing-hash` assertions.
294
+
295
+ See `tasks/phase-3-drawing-parity/10-channels.plan.md` for the full
296
+ audit + divergence flags.
297
+
298
+ - b0d296b: Phase 3 Task 11 — Fibonacci A (`fibRetracement` / `fibTrendExtension`
299
+ / `fibChannel` / `fibTimeZone` / `fibWedge`).
300
+
301
+ - **core** — `DrawNamespace` flattened: the four sub-namespace types
302
+ (`FibSubNamespace`, `GannSubNamespace`, `ElliottSubNamespace`,
303
+ `PatternSubNamespace`) are removed; every kind now lives as a flat
304
+ method directly on `DrawNamespace` matching the canonical
305
+ `STATEFUL_PRIMITIVES` names (`draw.fibRetracement(...)`,
306
+ `draw.gannBox(...)`, `draw.elliottImpulseWave(...)`,
307
+ `draw.xabcdPattern(...)`, etc.). The throwing-stub `draw` Proxy
308
+ drops the sub-namespace branch. Script authors use the flat
309
+ Pine/invinite-parity surface; the compiler resolves callsites
310
+ through its existing 2-segment property-access path. The 30
311
+ not-yet-ported method signatures (Tasks 12–18 fib-B / gann /
312
+ pitchfork / pattern / elliott / cycle / container kinds) are
313
+ declared as flat stubs so Tasks 12–18 only need to extend the
314
+ runtime `KIND_IMPLS` map. **BREAKING** for any consumer that
315
+ referenced `draw.fib.retracement(...)` or one of the four
316
+ sub-namespace types — none currently exist outside Phase-3 work.
317
+ - **adapter-kit** — 5 new per-kind validators
318
+ (`validateFibRetracementState`, `validateFibTrendExtensionState`,
319
+ `validateFibChannelState`, `validateFibTimeZoneState`,
320
+ `validateFibWedgeState`) + 1 file-local style helper
321
+ (`validateFibOpts`) covering FibOpts (`levels` finite-array,
322
+ `showLabels` / `color` / `extendLeft` / `extendRight`).
323
+ - **runtime** — 5 new emit functions under
324
+ `packages/runtime/src/emit/draw/fibA/` wired into `DRAW_NAMESPACE`
325
+ as flat methods. `fibRetracement` / `fibTimeZone` use the 4-arg
326
+ form `(slotId, a, b, opts?)`; the other 3 use the 3-arg
327
+ `(slotId, anchors, opts?)` form. No new sub-namespace wiring.
328
+ - **canvas2d-adapter** — 5 new renderers reusing Task-4's
329
+ `FIB_LEVELS` + `formatLevel` and Task-5's `extendLineSegment` for
330
+ the `fib-retracement` viewport extension. Default colour
331
+ `"#facc15"` (warm yellow) per invinite's fib-tool palette.
332
+ - **conformance** — 6 new scenarios (5 per-kind + 1
333
+ `drawFibA` bundle) with pinned `drawing-hash` assertions.
334
+ Conformance + scenarios test-capability fixtures grow `other`
335
+ bucket from 0 to 100 and add the 5 fib-A kebab kinds.
336
+
337
+ Divergences flagged in `tasks/phase-3-drawing-parity/11-fibonacci-a.plan.md`:
338
+
339
+ - `fib-time-zone` uses the canonical ratio array (`FIB_LEVELS`),
340
+ NOT the integer Fibonacci sequence; `fibSequence.ts` helper is
341
+ NOT created (Task-1 reshape follow-up).
342
+ - `fib-wedge` rays are drawn with a fixed length
343
+ `max(pxWidth, pxHeight) * 2` rather than via a directional
344
+ `extendLineSegment` variant.
345
+ - Per-kind property / golden test files deferred to the pragmatic
346
+ 1-file-per-emit + 1-file-per-renderer set, mirroring Tasks 5–10.
347
+
348
+ See `tasks/phase-3-drawing-parity/11-fibonacci-a.plan.md` for the
349
+ full audit + divergence list.
350
+
351
+ - b0d296b: Phase 3 Task 12 — Fibonacci B (`fibSpeedFan` / `fibSpeedArcs` /
352
+ `fibSpiral` / `fibCircles` / `fibTrendTime`).
353
+
354
+ - **adapter-kit** — 5 new per-kind validators
355
+ (`validateFibSpeedFanState`, `validateFibSpeedArcsState`,
356
+ `validateFibSpiralState`, `validateFibCirclesState`,
357
+ `validateFibTrendTimeState`), reusing Task-11's `validateFibOpts`
358
+ style helper. The permissive-default test fixture moves from
359
+ `fib-speed-fan` to `gann-box` (Task 13's first kind, still
360
+ unported).
361
+ - **runtime** — 5 new emit functions under
362
+ `packages/runtime/src/emit/draw/fibB/` wired into the
363
+ `DRAW_NAMESPACE` `KIND_IMPLS` map as flat methods. Four use the
364
+ 4-arg form `(slotId, a, b, opts?)`; `fibTrendTime` uses the 3-arg
365
+ `(slotId, anchors, opts?)`. Fall-through-stub fixture in
366
+ `namespace.test.ts` / `primitives.test.ts` /
367
+ `buildComputeContext.test.ts` moves from `fibSpeedFan` to
368
+ `gannBox`.
369
+ - **canvas2d-adapter** — 5 new renderers reusing Task-4's
370
+ `FIB_LEVELS` + `formatLevel`. `fibSpiral` additionally reuses
371
+ `sampleCubic` for the chained quarter-Bezier approximation of the
372
+ golden spiral. Default colour `"#facc15"` per invinite's fib-tool
373
+ palette.
374
+ - **conformance** — 5 new per-kind scenarios + 1 bundle
375
+ (`drawFibAll.scenario.ts` covering all 10 fib kinds, superseding
376
+ Task 11's `drawFibA.scenario.ts` which is deleted). Conformance +
377
+ scenarios test-capability fixtures switch from the explicit
378
+ fib-A kebab list to `capabilities.allFibDrawings()` (covers all
379
+ 10 kinds). All 6 hashes pinned against the deterministic-run
380
+ actuals.
381
+
382
+ Divergences flagged in `tasks/phase-3-drawing-parity/12-fibonacci-b.plan.md`:
383
+
384
+ - `fibSpiral` is clockwise-only — invinite's `counterClockwise`
385
+ flag is deferred (Task-1 reshape follow-up; landed `FibSpiralState`
386
+ - `FibOpts` don't carry the field).
387
+ - `fibSpeedArcs` is full-circle only — invinite's half-disk variant
388
+ is deferred (Phase-3-deferred UX nuance).
389
+ - `fibCircles` + `fibTrendTime` use the ratio array (`FIB_LEVELS`),
390
+ NOT the integer Fibonacci sequence. Same precedent as Task-11's
391
+ `fib-time-zone`.
392
+ - `gen-docs` regeneration for the 5 new kinds deferred to Task 21
393
+ (the existing `chartlang docs` command only walks `ta.*`; the
394
+ `draw.*` walker extension is an explicit Task-21 deliverable).
395
+ - Per-kind property / golden test files deferred to the pragmatic
396
+ 1-file-per-emit + 1-file-per-renderer set, mirroring Tasks 5–11.
397
+
398
+ See `tasks/phase-3-drawing-parity/12-fibonacci-b.plan.md` for the
399
+ full audit + divergence list.
400
+
401
+ - b0d296b: Phase 3 Task 13 — Gann (`gannBox` / `gannSquareFixed` / `gannSquare` /
402
+ `gannFan`).
403
+
404
+ - **adapter-kit** — 4 new per-kind validators
405
+ (`validateGannBoxState`, `validateGannSquareFixedState`,
406
+ `validateGannSquareState`, `validateGannFanState`), reusing
407
+ Task-5's `validateLineDrawStyle` style helper. The
408
+ permissive-default test fixture moves from `gann-box` to
409
+ `pitchfork` (Task 14's first kind, still unported).
410
+ - **runtime** — 4 new emit functions under
411
+ `packages/runtime/src/emit/draw/gann/` wired into the
412
+ `DRAW_NAMESPACE` `KIND_IMPLS` map as flat methods. Three use the
413
+ 4-arg form `(slotId, a, b, opts?)`; `gannSquareFixed` uses the
414
+ 3-arg `(slotId, anchor, opts?)`. Fall-through-stub fixture in
415
+ `namespace.test.ts` / `primitives.test.ts` /
416
+ `buildComputeContext.test.ts` moves from `gannBox` to `pitchfork`.
417
+ - **canvas2d-adapter** — 4 new renderers + a shared `gannLevels.ts`
418
+ helper exporting `GANN_LEVELS` (`[0, 0.25, 0.5, 0.75, 1]`),
419
+ `GANN_FAN_RATIOS` (9-entry tuple covering 1×1, 1×2, …, 8×1),
420
+ `GANN_FAN_LABELS`, and `formatGannRatio`. Default colour
421
+ `"#a855f7"` (purple/violet, mirroring invinite's gann-tool
422
+ palette).
423
+ - **conformance** — 4 new per-kind scenarios + 1 bundle
424
+ (`drawGannAll.scenario.ts` covering all 4 gann kinds).
425
+ Conformance + scenarios test-capability fixtures widen
426
+ `drawings` with `capabilities.allGannDrawings()`. All 5 hashes
427
+ pinned against the deterministic-run actuals.
428
+
429
+ Divergences flagged in `tasks/phase-3-drawing-parity/13-gann.plan.md`:
430
+
431
+ - `gannBox.levels` custom override deferred — landed `GannBoxState`
432
+ carries only `style: LineDrawStyle`. Renderer uses the shared
433
+ `GANN_LEVELS` constant only (Task-1 reshape follow-up).
434
+ - `gannSquareFixed.sizePrice` custom override deferred — landed
435
+ `GannSquareFixedState` carries only `anchor + style`. Renderer
436
+ uses a fixed `80px` side (Task-1 reshape follow-up).
437
+ - `gannSquare.ratio` custom override deferred — landed
438
+ `GannSquareState` carries only `anchors + style`. Renderer uses
439
+ canvas-space `max(|dx|, |dy|)` (1×1 default, Task-1 reshape
440
+ follow-up).
441
+ - `gannFan.showLabels` flag deferred — `LineDrawStyle` has no
442
+ `showLabels` field. Phase-3 pins unlabeled rays (Task-1 reshape
443
+ follow-up).
444
+ - `gen-docs` regeneration for the 4 new kinds deferred to Task 21
445
+ (the existing `chartlang docs` command only walks `ta.*`; the
446
+ `draw.*` walker extension is an explicit Task-21 deliverable).
447
+ - Per-kind property / golden test files deferred to the pragmatic
448
+ 1-file-per-emit + 1-file-per-renderer set, mirroring Tasks 5–12.
449
+
450
+ See `tasks/phase-3-drawing-parity/13-gann.plan.md` for the full
451
+ audit + divergence list.
452
+
453
+ - b0d296b: Phase 3 Task 14 — Pitchforks (`pitchfork` / `pitchfan`). The
454
+ `pitchfork` kind collapses the four invinite tools (`standard` /
455
+ `schiff` / `modifiedSchiff` / `inside`) into one kind with a
456
+ `variant` discriminator per PLAN.md §3.1.
457
+
458
+ - **adapter-kit** — 2 new per-kind validators
459
+ (`validatePitchforkState`, `validatePitchfanState`), reusing
460
+ Task-2's `validateAnchorTriple` + Task-5's `validateLineDrawStyle`
461
+ helpers. `validatePitchforkState` also pins the 4-entry variant
462
+ enum (`standard | schiff | modifiedSchiff | inside`). The
463
+ permissive-default test fixture moves from `pitchfork` to
464
+ `xabcd-pattern` (Task 15's first kind, still unported).
465
+ - **runtime** — 2 new emit functions under
466
+ `packages/runtime/src/emit/draw/pitchforks/` wired into the
467
+ `DRAW_NAMESPACE` `KIND_IMPLS` map as flat methods. Both use the
468
+ 3-arg form `(slotId, anchors, opts?)`. `pitchfork` accepts
469
+ `opts: LineDrawStyle & { variant? }` — the impl destructures
470
+ `variant` (defaulting to `"standard"`), strips it from the
471
+ style payload, and builds the `PitchforkState`. Fall-through-stub
472
+ fixture in `namespace.test.ts` / `primitives.test.ts` /
473
+ `buildComputeContext.test.ts` moves from `pitchfork` to
474
+ `xabcdPattern`.
475
+ - **canvas2d-adapter** — 2 new renderers + a shared
476
+ `pitchforkGeom.ts` helper exporting `medianOriginFor(variant, a,
477
+ b, c)` and `medianTargetFor(variant, a, b, c)` (per-variant
478
+ median-rail endpoints in canvas space). Default colour
479
+ `"#ec4899"` (pink/magenta, mirroring invinite's pitchfork-tool
480
+ palette family). The pitchfork renderer emits 3 strokes per
481
+ emission (median + 2 parallel handles through `b` and `c`); the
482
+ pitchfan renderer emits 3 rays from `a` through `b`, `mid(b, c)`,
483
+ `c`.
484
+ - **conformance** — 2 new per-kind scenarios + 1 bundle
485
+ (`drawPitchforksAll.scenario.ts` covering 4 pitchfork variants +
486
+ 1 pitchfan = 5 emissions). Conformance + scenarios + index
487
+ test-capability fixtures widen `drawings` with
488
+ `capabilities.allPitchforkDrawings()`. All 3 hashes pinned
489
+ against the deterministic-run actuals.
490
+
491
+ Divergences flagged in
492
+ `tasks/phase-3-drawing-parity/14-pitchforks.plan.md`:
493
+
494
+ - `extendLeft` / `extendRight` flags from invinite's
495
+ `PitchforkDrawing` not on landed `PitchforkState`. Phase-3 pins
496
+ the default extend-forward behaviour for each rail (Task-1
497
+ reshape follow-up).
498
+ - Per-instance `levels` array not on landed state. Phase-3 renders
499
+ the median + 2 parallel-handle pattern only — no per-level
500
+ offsets (Task-1 reshape follow-up).
501
+ - `medianColor` / `medianLineStyle` / `medianStrokeWidthPx` not on
502
+ landed state. Phase-3 paints the median with the same
503
+ `LineDrawStyle` as the handles (Task-1 reshape follow-up).
504
+ - `gen-docs` regeneration for the 2 new kinds deferred to Task 21
505
+ (the existing `chartlang docs` command only walks `ta.*`; the
506
+ `draw.*` walker extension is an explicit Task-21 deliverable).
507
+ - Per-kind property / golden test files deferred to the pragmatic
508
+ 1-file-per-emit + 1-file-per-renderer set, mirroring Tasks 5–13.
509
+
510
+ See `tasks/phase-3-drawing-parity/14-pitchforks.plan.md` for the
511
+ full audit + divergence list.
512
+
513
+ - b0d296b: Phase 3 Task 15 — Harmonic Patterns (`xabcdPattern` / `cypherPattern`
514
+ / `headAndShoulders` / `abcdPattern` / `trianglePattern` /
515
+ `threeDrivesPattern`). All 6 kinds map to the `polylines` bucket and
516
+ ship as flat methods (`draw.<kind>(...)`) per the Task-11 Option-C
517
+ decision.
518
+
519
+ - **adapter-kit** — 6 new per-kind validators
520
+ (`validateXabcdPatternState`, `validateCypherPatternState`,
521
+ `validateHeadAndShouldersState`, `validateAbcdPatternState`,
522
+ `validateTrianglePatternState`,
523
+ `validateThreeDrivesPatternState`) plus a new
524
+ `validateAnchorHept` helper covering the 7-anchor
525
+ `three-drives-pattern` shape. All 6 validators reuse Task-5's
526
+ `validateLineDrawStyle` and Task-2's per-anchor-arity helpers.
527
+ The permissive-default test fixture moves from `xabcd-pattern`
528
+ → `elliott-impulse-wave` (Task 16's first kind, still unported).
529
+ - **runtime** — 6 new emit functions under
530
+ `packages/runtime/src/emit/draw/patterns/` wired into the
531
+ `DRAW_NAMESPACE` `KIND_IMPLS` map as flat methods. Each uses the
532
+ 3-arg form `(slotId, anchors, opts?)` with the dual-overload
533
+ pattern. Fall-through-stub fixture in `namespace.test.ts` /
534
+ `primitives.test.ts` / `buildComputeContext.test.ts` moves from
535
+ `xabcdPattern` to `elliottImpulseWave`.
536
+ - **canvas2d-adapter** — 6 new renderers plus a shared
537
+ `namedPolyline.ts` helper exporting `renderNamedPolyline(ctx,
538
+ points, labels, style)` — strokes an open polyline through the
539
+ pre-projected canvas-space points and fills one text label
540
+ above each anchor (textAlign `center` + textBaseline `bottom`,
541
+ 6 px above the anchor). Default colour `#f59e0b` (amber/orange,
542
+ matching invinite's pattern-tool palette family).
543
+ `headAndShoulders` adds a neckline stroke between the two
544
+ trough anchors (`anchors[1]` → `anchors[3]`), totalling 2
545
+ strokes per emission; the other 5 kinds emit 1 polyline stroke - N point labels.
546
+ - **conformance** — 6 new per-kind scenarios + 1 bundle
547
+ (`drawPatternsAll.scenario.ts` covering all 6 kinds = 6
548
+ emissions). Conformance + scenarios + index test-capability
549
+ fixtures widen `drawings` with
550
+ `capabilities.allPatternDrawings()`. All 7 hashes pinned
551
+ against the deterministic-run actuals.
552
+
553
+ **Provenance carve-out — `cypherPattern`.** Per the team-lead
554
+ brief + PLAN.md §3.1, `cypher-pattern` has no standalone invinite
555
+ tool — only the y-doc-bridge type. The runtime emit
556
+ (`packages/runtime/src/emit/draw/patterns/cypherPattern.ts`) and
557
+ the canvas2d renderer
558
+ (`examples/canvas2d-adapter/src/render/draw/cypherPattern.ts`)
559
+ both cite **only** `invinite/shared/trading-chart-collab-yjs/y-doc-bridge.ts`
560
+ in their relicense headers (no `*-tool.ts` line). The UI surface
561
+ for cypher lives in `defineDrawing` (Task 20).
562
+
563
+ Divergences flagged in
564
+ `tasks/phase-3-drawing-parity/15-patterns.plan.md`:
565
+
566
+ - **`headAndShoulders` is 5-anchor on the landed state** (Task 1's
567
+ `HeadAndShouldersState.anchors: AnchorQuint`), not the 7-anchor
568
+ invinite shape (`start, leftShoulder, leftTrough, head,
569
+ rightTrough, rightShoulder, end`). The renderer treats the 5
570
+ anchors as `[LS, LL, H, RL, RS]` and strokes a neckline between
571
+ the two trough anchors only (no start/end projection). Flagged
572
+ as a Task-1 reshape follow-up.
573
+ - **`trianglePattern` is 3-anchor on the landed state**
574
+ (`TrianglePatternState.anchors: AnchorTriple`), not the 4-anchor
575
+ invinite shape (`a, b, c, d`). The renderer treats the 3 anchors
576
+ as `[apex, baseHigh, baseLow]` matching the landed type's
577
+ `@anchors` annotation. Flagged as a Task-1 reshape follow-up.
578
+ Distinct from `draw.triangle` (Task 6), a solid-shape primitive
579
+ with `ShapeStyle` — `draw.trianglePattern` is a harmonic-pattern
580
+ outline with `LineDrawStyle`. JSDoc cross-references the
581
+ distinction.
582
+ - `gen-docs` regeneration for the 6 new kinds deferred to Task 21
583
+ (the existing `chartlang docs` command only walks `ta.*`; the
584
+ `draw.*` walker extension is an explicit Task-21 deliverable).
585
+ - Per-kind property / golden test files deferred to the pragmatic
586
+ 1-file-per-emit + 1-file-per-renderer set, mirroring Tasks 5–14.
587
+
588
+ See `tasks/phase-3-drawing-parity/15-patterns.plan.md` for the
589
+ full audit + divergence list.
590
+
591
+ - b0d296b: Phase 3 Task 16 — Elliott Waves (`elliottImpulseWave` /
592
+ `elliottCorrectionWave` / `elliottTriangleWave` / `elliottDoubleCombo`
593
+ / `elliottTripleCombo`). All 5 kinds map to the `polylines` bucket
594
+ and ship as flat methods (`draw.<kind>(...)`) per the Task-11
595
+ Option-C decision.
596
+
597
+ - **adapter-kit** — 5 new per-kind validators
598
+ (`validateElliottImpulseWaveState`,
599
+ `validateElliottCorrectionWaveState`,
600
+ `validateElliottTriangleWaveState`,
601
+ `validateElliottDoubleComboState`,
602
+ `validateElliottTripleComboState`) plus a new
603
+ `validateOptionalLabels(v, path, expectedCount)` helper that
604
+ validates the optional script-author `state.labels` override
605
+ (when present: array of strings whose length exactly matches the
606
+ per-kind anchor count). All 5 validators reuse Task-5's
607
+ `validateLineDrawStyle` and Task-2/15's
608
+ `validateAnchorTriple` / `validateAnchorQuint` /
609
+ `validateAnchorHept`. The permissive-default test fixture moves
610
+ from `elliott-impulse-wave` → `cyclic-lines` (Task 17's first
611
+ kind, still unported).
612
+ - **runtime** — 5 new emit functions under
613
+ `packages/runtime/src/emit/draw/elliott/` wired into the
614
+ `DRAW_NAMESPACE` `KIND_IMPLS` map as flat methods. Each uses the
615
+ 3-arg form `(slotId, anchors, opts?)` with the dual-overload
616
+ pattern. The runtime widens `opts` to
617
+ `LineDrawStyle & { labels?: ReadonlyArray<string> }` — the impl
618
+ destructures `labels` from `opts`, strips it from the style
619
+ payload, and stores it on `state.labels` only when present
620
+ (preserving the optional field's `undefined` state when omitted
621
+ so emission hashes stay stable). Fall-through-stub fixture in
622
+ `namespace.test.ts` / `primitives.test.ts` /
623
+ `buildComputeContext.test.ts` moves from `elliottImpulseWave` to
624
+ `cyclicLines`.
625
+ - **canvas2d-adapter** — 5 new renderers reusing Task-15's
626
+ `renderNamedPolyline` helper. Default colour `#14b8a6` (teal —
627
+ free palette slot distinct from blue/yellow/purple/pink/amber).
628
+ Each renderer honours the optional `state.labels` override when
629
+ present and its length matches the anchor count (defensive
630
+ fallback to the per-kind default `LABELS` constant). Per-kind
631
+ default labels: impulse `["1","2","3","4","5"]`, correction
632
+ `["A","B","C"]`, triangle `["a","b","c","d","e"]`, double-combo
633
+ `["S","W","x1","X","x2","Yi","Y"]`, triple-combo
634
+ `["S","W","X1","Y","X2","Zi","Z"]`. Dispatch test's describe
635
+ label bumps from "Task-16+ stubs" to "Task-17+ stubs".
636
+ - **conformance** — 5 new per-kind scenarios + 1 bundle
637
+ (`drawElliottAll.scenario.ts` covering all 5 kinds = 5
638
+ emissions). Conformance + scenarios + index test-capability
639
+ fixtures widen `drawings` with `capabilities.allElliottDrawings()`.
640
+ All 6 hashes pinned against the deterministic-run actuals.
641
+
642
+ Divergences flagged in
643
+ `tasks/phase-3-drawing-parity/16-elliott.plan.md`:
644
+
645
+ - **`WaveDegree` enum + label-decoration helper NOT on landed state**
646
+ (Task 1's `Elliott*State` shapes carry no `degree` field — they
647
+ carry an optional `labels?: ReadonlyArray<string>` field instead,
648
+ letting the script author override the per-kind default labels
649
+ directly). The 9-level `WaveDegree` enum + the
650
+ `elliottLabels.ts` decoration helper are dropped from Phase 3.
651
+ Flagged as a Task-1 reshape follow-up.
652
+ - **`elliottImpulseWave` is 5-anchor on the landed state** (Task 1's
653
+ `ElliottImpulseWaveState.anchors: AnchorQuint`), not the 6-anchor
654
+ invinite shape. The renderer treats the 5 anchors as the wave1End
655
+ → wave5End pivots and strokes 4 connecting legs. Same precedent
656
+ for `elliottCorrectionWave` (landed 3-anchor vs invinite 4),
657
+ `elliottTriangleWave` (landed 5-anchor vs invinite 6), and
658
+ `elliottTripleCombo` (landed 7-anchor vs invinite 10). All
659
+ flagged as Task-1 reshape follow-ups.
660
+ - `gen-docs` regeneration for the 5 new kinds deferred to Task 21
661
+ (the existing `chartlang docs` command only walks `ta.*`; the
662
+ `draw.*` walker extension is an explicit Task-21 deliverable).
663
+ - Per-kind property / golden test files deferred to the pragmatic
664
+ 1-file-per-emit + 1-file-per-renderer set, mirroring Tasks 5–15.
665
+
666
+ See `tasks/phase-3-drawing-parity/16-elliott.plan.md` for the full
667
+ audit + divergence list.
668
+
669
+ - b0d296b: Phase 3 Task 17 — Cycles (`cyclicLines` / `timeCycles` / `sineLine`).
670
+ All 3 kinds map to the `other` bucket and ship as flat methods
671
+ (`draw.<kind>(a, b, opts?)`) per the Task-11 Option-C decision.
672
+
673
+ - **adapter-kit** — 3 new per-kind validators
674
+ (`validateCyclicLinesState`, `validateTimeCyclesState`,
675
+ `validateSineLineState`). All 3 reuse Task-2's `validateAnchorPair`
676
+ - Task-5's `validateLineDrawStyle`; no new helpers needed (cycle
677
+ states carry no `labels` field, so Task-16's
678
+ `validateOptionalLabels` is not consumed). The permissive-default
679
+ test fixture moves from `cyclic-lines` → `group` (Task 18's first
680
+ kind, still unported).
681
+ - **runtime** — 3 new emit functions under
682
+ `packages/runtime/src/emit/draw/cycles/` wired into the
683
+ `DRAW_NAMESPACE` `KIND_IMPLS` map as flat methods. Each uses the
684
+ 4-arg dual-overload form `(slotId, a, b, opts?)` mirroring `line`
685
+ (the script-author surface is the 3-arg `(a, b, opts?)`; the
686
+ compiler injects the leading slot id). State is assembled as
687
+ `anchors: [a, b]`. Fall-through-stub fixture in
688
+ `namespace.test.ts` / `primitives.test.ts` /
689
+ `buildComputeContext.test.ts` moves from `cyclicLines` to `group`.
690
+ - **canvas2d-adapter** — 3 new renderers reusing Task-4's
691
+ `worldPointToCanvas` + Phase-1 `dashPattern`. Default colour
692
+ `#0ea5e9` (sky blue — free palette slot distinct from
693
+ blue/yellow/purple/pink/amber/teal/green/red used by prior port
694
+ tasks). Per-kind geometry:
695
+
696
+ - `cyclicLines` — repeated full-height vertical strokes at
697
+ `fromX + n * periodPx` for n ∈ [0, viewport+overscan/periodPx],
698
+ capped at 256 iterations. Skips silently on degenerate period.
699
+ - `timeCycles` — concentric upper-half arcs centred at the
700
+ midpoint of `(from, to)` on the `from.price` baseline, radius =
701
+ `|toX − fromX| / 2`. Arcs tile across the viewport at multiples
702
+ of the diameter (64 per side). Skips silently on degenerate
703
+ diameter.
704
+ - `sineLine` — sampled sinusoidal polyline. Half-period =
705
+ `|toX − fromX|` (full period doubled). Baseline = midpoint of
706
+ `(fromY, toY)`. Amplitude = `|fromY − toY| / 2`. 32 samples per
707
+ full period; wave starts at the `from` extreme (peak vs trough
708
+ flipped by `fromPx.y < toPx.y` — mirrors invinite's
709
+ `extremeIsPeak` flag). Skips silently on degenerate half-period.
710
+
711
+ Dispatch test's describe labels bump from "Tasks 5–15 shipped" to
712
+ "Tasks 5–17 shipped" and "Task-17+ stubs" to "Task-18+ stubs".
713
+
714
+ - **conformance** — 3 new per-kind scenarios + 1 bundle
715
+ (`drawCyclesAll.scenario.ts` covering all 3 kinds = 3 emissions).
716
+ Conformance + scenarios + index test-capability fixtures widen
717
+ `drawings` with `capabilities.allCycleDrawings()`. All 4 hashes
718
+ pinned against the deterministic-run actuals:
719
+ `drawCyclicLines` = `975166fe…aae16`,
720
+ `drawTimeCycles` = `1bdaca36…d88c0`,
721
+ `drawSineLine` = `9f88b689…3ba8`,
722
+ `drawCyclesAll` = `ef46754f…cc80b`.
723
+
724
+ Divergences flagged in
725
+ `tasks/phase-3-drawing-parity/17-cycles.plan.md`:
726
+
727
+ - **`SineLineState.period: number` field NOT on landed state**
728
+ (Task 1's `SineLineState` carries only `anchors` + `style` —
729
+ the renderer derives the half-period from `|to.time − from.time|`,
730
+ matching invinite's tool source). The explicit `period: number`
731
+ field is dropped from Phase 3; flagged as a Task-1 reshape
732
+ follow-up.
733
+ - **`TimeCyclesState.style.fill` / `fillAlpha` NOT on landed state**
734
+ (Task 1's `TimeCyclesState` uses `LineDrawStyle`, not
735
+ `ShapeStyle`). The renderer strokes the arcs only — invinite's
736
+ tool source DOES fill the half-circles. Flagged as a Task-1
737
+ reshape follow-up.
738
+ - **`to.time > from.time` reject NOT enforced** — Phase-3 renderer
739
+ no-ops silently on degenerate input, matching every other Phase-3
740
+ drawing port (gann / fib / elliott all silently no-op on
741
+ collapsed anchors). The validator accepts reversed anchors per
742
+ `validateAnchorPair`'s finite-only contract.
743
+ - `gen-docs` regeneration for the 3 new kinds deferred to Task 21
744
+ (the existing `chartlang docs` command only walks `ta.*`; the
745
+ `draw.*` walker extension is an explicit Task-21 deliverable).
746
+ - Per-kind property / golden test files deferred to the pragmatic
747
+ 1-file-per-emit + 1-file-per-renderer set, mirroring Tasks 5–16.
748
+
749
+ See `tasks/phase-3-drawing-parity/17-cycles.plan.md` for the full
750
+ audit + divergence list.
751
+
752
+ - b0d296b: Phase 3 Task 18 — Containers (`group` / `frame`). The FINAL per-port
753
+ task: after this lands all 61 `DrawingKind`s have real validator /
754
+ emit / renderer / dispatch arms. Both kinds map to the `other`
755
+ bucket and ship as flat methods (`draw.group(childHandleIds)` /
756
+ `draw.frame(a, b, opts?)`) per the Task-11 Option-C decision.
757
+
758
+ - **adapter-kit** — 2 new per-kind validators (`validateGroupState`,
759
+ `validateFrameState`) + 2 tiny shared helpers
760
+ (`validateOptionalChildHandleIds`, `validateFrameOpts`). `group`
761
+ pins `childHandleIds.length ≤ 100`; `frame` reuses Task-2's
762
+ `validateAnchorPair`, accepts degenerate anchors (silent no-op at
763
+ the renderer per the rest of Phase-3's degenerate-input
764
+ precedent). The permissive-default test fixture
765
+ (`validateEmission.test.ts:1516`) flips from
766
+ `permissively-accepts` to a rejecting `validateGroupState`
767
+ assertion + a new gate-only test that asserts unknown kinds drop
768
+ with `unsupported-drawing-kind` upstream. After Task 18 every
769
+ `DrawingKind` has a real validator arm — the
770
+ `default: return { ok: true };` arm in `validateStateByKind` is
771
+ removed; TS's exhaustiveness check now catches a future
772
+ `DrawingKind` addition without a validator.
773
+ - **runtime** — 2 new emit functions under
774
+ `packages/runtime/src/emit/draw/containers/` wired into the
775
+ `DRAW_NAMESPACE` `KIND_IMPLS` map as flat methods. `group` is a
776
+ 2-arg dual-overload `(slotId, childHandleIds)`; `frame` is a 4-arg
777
+ dual-overload `(slotId, a, b, opts?)` mirroring `line`. After Task
778
+ 18 `IMPL_KIND_NAMES.size === 61`; the Proxy's else-branch
779
+ fall-through to core's throwing-stub is dead code on the
780
+ `DrawNamespace` type surface — kept as defence-in-depth for
781
+ property access outside that type. The pre-Task-18
782
+ "still-stubbed" assertions in `namespace.test.ts` /
783
+ `primitives.test.ts` / `buildComputeContext.test.ts` are replaced
784
+ with a positive cardinality sweep that asserts every
785
+ `DrawingKind` resolves to a real runtime impl that throws the
786
+ in-step-only sentinel (NOT the core stub sentinel).
787
+ - **canvas2d-adapter** — 1 real renderer (`renderFrame`) + 1 pure
788
+ no-op renderer (`renderGroup`). `renderFrame` strokes a closed
789
+ 4-corner rectangle defaulting to slate `#64748b`, optionally
790
+ paints a `fillRect` background when `style.bgColor` is set, and
791
+ optionally paints a `fillText` label inset 6 px from the top-left
792
+ when `style.label` is set. Degenerate anchors (zero width or zero
793
+ height in canvas space) silently no-op. `renderGroup` is a pure
794
+ no-op for Phase 3 — the visible bounding-box envelope around
795
+ grouped drawings is a Phase-4 follow-up tied to
796
+ `Viewport.drawingsById` plumbing (Viewport currently exposes only
797
+ `xMin/xMax/yMin/yMax/pxWidth/pxHeight`). `drawingDispatch`'s
798
+ `// Containers (Task 18)` arms flip from `return;` no-ops to
799
+ `return renderGroup(...)` / `return renderFrame(...)`. The
800
+ `drawingDispatch.test.ts` describe labels bump:
801
+ `Task-18+ stubs` → `'group' no-op + exhaustiveness`;
802
+ `Tasks 5–17 shipped` → `Tasks 5–18 shipped`.
803
+ - **conformance** — 2 new per-kind scenarios (`drawGroup`,
804
+ `drawFrame`) + 1 bundle (`drawContainersAll`, 2 emissions).
805
+ Pinned `drawing-hash` assertions for each:
806
+ - `draw-group`:
807
+ `6e32e387543ef421d1e53c1c15612cc32a814c85c2d969ad86d9f47b8d0359a2`
808
+ - `draw-frame`:
809
+ `4b54e0b6e75ad40904e0f70ac5b34067afa6c1237d43060823889f04b86d900b`
810
+ - `draw-containers-all`:
811
+ `e6ba183dfc04145a5126e6ea75a4cb7117694adc13eea84853239c68810e91fe`
812
+ `TEST_CAPABILITIES.drawings` widens with
813
+ `...capBuilders.allContainerDrawings()`; the `ALL_SCENARIOS`
814
+ `toEqual` array (in `scenarios.test.ts` and `index.test.ts`)
815
+ appends the 3 new scenarios under
816
+ `// Phase 3 Task 18 — Containers.`.
817
+
818
+ ### Divergences from spec (`tasks/phase-3-drawing-parity/18-containers.md`)
819
+
820
+ 1. **Spec § Runtime Notes says `draw.group(children:
821
+ ReadonlyArray<DrawingHandle>)` accepts handle objects.** Landed
822
+ core surface takes `ReadonlyArray<string>` (handle ids) directly
823
+ — the runtime impl uses the landed shape so the wire payload is
824
+ 1:1 with what the script passes. Documented in `draw.group`'s
825
+ JSDoc with the canonical `draw.group([a.id, b.id])` pattern.
826
+ 2. **Spec § Renderer Notes says `group` renders a dashed bounding
827
+ box derived from children's `view.drawingsById.get(childId).state`
828
+ extrema.** Landed `Viewport` exposes no `drawingsById` field;
829
+ adding it is a foundation-level Viewport change beyond a per-port
830
+ task. Phase 3 ships `renderGroup` as a pure no-op (children
831
+ render themselves per `GroupState`'s metadata contract);
832
+ bounding-box envelope deferred to Phase 4.
833
+ 3. **Spec § Kinds Landed says `group.style: { lineWidth?; color? }`
834
+ for the boundary box.** Landed `GroupState` has no `style` field
835
+ (only `childHandleIds` + optional `meta`). Use the landed shape;
836
+ the boundary-box style lands with the Phase-4 renderer rework.
837
+ 4. **Spec § Tests says degenerate `frame` anchors are a warning
838
+ diagnostic.** Landed `validateAnchorPair` only enforces finite
839
+ `time`/`price`; degenerate frames pass validation and the
840
+ renderer silently no-ops on `width === 0 || height === 0`. This
841
+ matches the rest of Phase 3's "no-op on degenerate input"
842
+ precedent (gann/fib/elliott/cycles).
843
+ 5. **Per-kind property tests skipped** — same Tasks 5–17 precedent.
844
+ The per-kind validator describe arms cover happy + wrong-shape
845
+ per kind; the `childHandleIds.length ≤ 100` cap is exercised
846
+ directly in the group describe block.
847
+
848
+ ### Open / deferred
849
+
850
+ - `GroupState` boundary-box style + `view.drawingsById` plumbing for
851
+ the visible group envelope land in Phase 4 (Divergence §2 + §3).
852
+ - `gen-docs` regeneration for `docs/primitives/draw/{group,frame}.md`
853
+ defers to Task 21 (same precedent as Tasks 11–17 — the
854
+ draw-namespace docs walker is Task 21's deliverable).
855
+ - Workspace-wide gates (`pnpm typecheck`, `pnpm test` at the root)
856
+ defer to Task 22's phase closeout. Per-package gates
857
+ (adapter-kit / runtime / canvas2d / conformance) all green and
858
+ 100% coverage held.
859
+
860
+ - b0d296b: Phase-3 Task 2 — adapter-kit drawing surface.
861
+
862
+ Widens `DrawingKind` from the Phase-1 `"line"` placeholder to the full
863
+ 61-entry kebab-case union (re-export of
864
+ `@invinite-org/chartlang-core`'s `DrawingKind`). Narrows
865
+ `DrawingEmission.state` from `unknown` to the typed `DrawingState`
866
+ discriminated union. Adapter code that wrote `drawingKind: "line"`
867
+ still compiles.
868
+
869
+ Replaces the Phase-1 unconditional-fail `validateDrawingEmission` with
870
+ a per-kind dispatch:
871
+
872
+ - Unknown `drawingKind` → `unsupported-drawing-kind`.
873
+ - Malformed payloads of a known kind → `malformed-emission`.
874
+ - The 6 Lines/Rays validators land in this PR (`line`,
875
+ `horizontal-line`, `horizontal-ray`, `vertical-line`,
876
+ `cross-line`, `trend-angle`). Tasks 6–18 ADD their kind
877
+ validators to the dispatch as ports land (per PLAN.md §22.10).
878
+ - Validates `handleId` / `op` / `bar` / `time` /
879
+ `state.kind === drawingKind` / `name`/`visible` meta for every
880
+ kind.
881
+
882
+ Replaces the Phase-1 `decodeDrawing` stub (always returned `null`)
883
+ with the real implementation: returns the typed `DrawingState` for
884
+ emissions that pass `validateEmission`, `null` otherwise.
885
+
886
+ Extends `capabilities.*` with the Phase-3 builder set:
887
+
888
+ - **61 per-kind builders** (`drawLine()`, `drawHorizontalLine()`,
889
+ `drawFibRetracement()`, `drawElliottImpulseWave()`, …) — each
890
+ returns a single-element `ReadonlySet<DrawingKind>` for opt-in
891
+ precision.
892
+ - **13 category-group builders** matching PLAN.md §10.2:
893
+ `allLineDrawings()` (6), `allBoxDrawings()` (8),
894
+ `allCurveDrawings()` (3), `allFreehandDrawings()` (3),
895
+ `allAnnotationDrawings()` (5), `allChannelDrawings()` (4),
896
+ `allFibDrawings()` (10), `allGannDrawings()` (4),
897
+ `allPitchforkDrawings()` (2), `allPatternDrawings()` (6),
898
+ `allElliottDrawings()` (5), `allCycleDrawings()` (3),
899
+ `allContainerDrawings()` (2). The 13 categories are pairwise
900
+ disjoint and sum to 61.
901
+ - **`allPhase3Drawings()`** — the umbrella set of every kind.
902
+ Adapters that support the full surface (canvas2d in Task 4)
903
+ declare this as their `Capabilities.drawings`.
904
+
905
+ Re-exports `bucketFor` + `KIND_BUCKET` + `type DrawingBucket` from
906
+ core via the adapter-kit barrel. Adapter authors that want to
907
+ pre-budget against the canonical kind → bucket map can import them
908
+ directly from `@invinite-org/chartlang-adapter-kit`.
909
+
910
+ No runtime behaviour change — the runtime still doesn't emit
911
+ drawings. Phase-2 plot dispatch + meta walker + Phase-1 alert /
912
+ diagnostic dispatches are unchanged. 100% coverage on
913
+ `packages/adapter-kit` preserved.
914
+
915
+ - b0d296b: Phase-3 Task 6 — second per-port task. Lands the 4 straight-edged
916
+ box-family drawing kinds (`rectangle`, `rotatedRectangle`, `triangle`,
917
+ `polyline`) per PLAN.md §10 and §22.10. Behaviour ports from invinite
918
+ commit `078f41fe2569d659d5aba726da8bcb5d3e2ced02`:
919
+ `tools/rectangle-tool.ts`, `tools/rotated-rectangle-tool.ts`,
920
+ `tools/triangle-tool.ts`, `tools/polyline-tool.ts`, and the matching
921
+ `y-doc-bridge.ts` `DrawingMetadata` variants.
922
+
923
+ `@invinite-org/chartlang-adapter-kit` adds per-kind state validators
924
+ for the 4 box-A kinds — `validateRectangleState`,
925
+ `validateRotatedRectangleState`, `validateTriangleState`,
926
+ `validatePolylineState` — wired into the existing
927
+ `validateStateByKind` dispatch. New file-local helpers
928
+ `validateAnchorTriple` / `validateAnchorQuad` /
929
+ `validateAnchorVariable(min, max)` / `validateShapeStyle` cover the
930
+ anchor cardinalities and the `ShapeStyle` payload bag. `polyline`
931
+ pins `3 ≤ anchors.length ≤ 20` (mirrors invinite's 20-point cap).
932
+ Wire shape is stricter than before — payloads previously passing the
933
+ permissive default arm now reject with `malformed-emission`.
934
+
935
+ `@invinite-org/chartlang-runtime` ships 4 new `draw.<kind>(...)` emit
936
+ functions under `src/emit/draw/boxes/` and extends the
937
+ `DRAW_NAMESPACE` swap-seam at `src/emit/draw/namespace.ts`. Each impl
938
+ uses the dual-overload pattern (`(...)` script-facing throw +
939
+ `(slotId, ...)` compiler-injected) mirroring Task 5 / `plot` /
940
+ `alert`. Returns a `DrawingHandle` per PLAN.md §10.3.
941
+
942
+ `chartlang-example-canvas2d-adapter` ships 4 new renderers under
943
+ `src/render/draw/` plus a shared `shapeStyle.ts` helper exporting
944
+ `applyShapeStyle(ctx, style): AppliedShapeStyle` — sets stroke /
945
+ lineWidth / dash and returns the resolved fill payload so the
946
+ renderer can wrap `ctx.fill()` in a `globalAlpha` bracket. The
947
+ `drawingDispatch` switch flips the 4 box-A arms from no-op stubs to
948
+ real `renderXxx(ctx, e, view)` calls; exhaustiveness is preserved.
949
+ Fill defaults to no-op, stroke defaults to `"#000000"`, lineWidth
950
+ defaults to `1`. Rectangle is rendered as a closed 4-corner polygon
951
+ (no `strokeRect` in the structural `RenderCtx`); rotatedRectangle
952
+ walks the four world anchors directly (no canvas matrix ops);
953
+ triangle walks 3 vertices; polyline auto-closes via `closePath()`.
954
+
955
+ `@invinite-org/chartlang-conformance` ships 5 new scenarios under
956
+ `src/scenarios/` — 4 per-kind (`drawRectangle`, `drawRotatedRectangle`,
957
+ `drawTriangle`, `drawPolyline`) and 1 bundle (`drawBoxesA`). All five
958
+ use `inlineSource` against the bundled 10 000-bar `goldenBars.json`
959
+ fixture with anchor times pulled from `bars[0]` / `bars[500]` /
960
+ `bars[1000]`. The `TEST_CAPABILITIES` bag in
961
+ `runConformanceSuite.test.ts` + `scenarios.test.ts` widens to include
962
+ `allBoxDrawings()` plus `boxes: 100` / `polylines: 100` budgets so
963
+ the new scenarios reach `pushDrawing`'s happy path. The 5 new
964
+ scenarios extend `ALL_SCENARIOS` (now 96 entries) and the public
965
+ re-export surface.
966
+
967
+ No core edits — the `DrawingState` variants and `DrawNamespace`
968
+ signatures Task 1 shipped are the canonical shape and Task 6 wires
969
+ real impls to them.
970
+
971
+ Deviations from spec, flagged for review:
972
+
973
+ - Spec's `rotatedRectangle` "3 anchors (a, b, widthOffset)"
974
+ ergonomics — Task 1's `AnchorQuad` (4 corners) is the persisted
975
+ shape. Callers supply the 4 corners directly; the
976
+ (a, b, widthOffset) reshape belongs to Task 20's `defineDrawing`
977
+ if it remains a hard requirement.
978
+ - Spec's `polyline` `ShapeStyle` + auto-close — Task 1 ships
979
+ `LineDrawStyle` (no fill). Renderer strokes the closed path; fill
980
+ would require widening the variant in a follow-up.
981
+ - Per-kind §22.10 5-file test set deferred to pragmatic 1-file set
982
+ (mirrors Task 5) — Task 3's `pushDrawing.*` and `handle.*` suite
983
+ covers the underlying infra exhaustively.
984
+ - `gen-docs` doc-page generation deferred to Task 21 (mirrors Task 5).
985
+
986
+ - b0d296b: Phase-3 Task 7 — third per-port task. Lands the 4 curved-edge /
987
+ single-anchor box-family drawing kinds (`circle`, `ellipse`, `path`,
988
+ `marker`) per PLAN.md §10 and §22.10. Behaviour ports from invinite
989
+ commit `078f41fe2569d659d5aba726da8bcb5d3e2ced02`:
990
+ `tools/circle-tool.ts`, `tools/ellipse-tool.ts`, `tools/path-tool.ts`,
991
+ `tools/marker-tool.ts`, and the matching `y-doc-bridge.ts` variants.
992
+
993
+ `@invinite-org/chartlang-adapter-kit` adds per-kind state validators
994
+ for the 4 box-B kinds — `validateCircleState`, `validateEllipseState`,
995
+ `validatePathState`, `validateMarkerState` — wired into the existing
996
+ `validateStateByKind` dispatch. New file-local helpers
997
+ `validatePathOpts` (LineDrawStyle + optional `closed: boolean`) and
998
+ `validateTextOpts` (color / size / halign / valign / bgColor enums)
999
+ cover the path / marker style bags. `path` pins
1000
+ `2 ≤ anchors.length ≤ 20` (mirrors invinite's 20-point cap and is
1001
+ narrower than `polyline`'s 3..20 because path supports a 2-point
1002
+ segment with optional caps). Wire shape is stricter than before —
1003
+ payloads previously passing the permissive default arm now reject
1004
+ with `malformed-emission`.
1005
+
1006
+ `@invinite-org/chartlang-runtime` ships 4 new `draw.<kind>(...)` emit
1007
+ functions under `src/emit/draw/boxes/` and extends the
1008
+ `DRAW_NAMESPACE` swap-seam at `src/emit/draw/namespace.ts`. Each impl
1009
+ uses the dual-overload pattern Tasks 5 + 6 pinned. `draw.marker`
1010
+ splits its `opts` bag — top-level `text` / `value` land on
1011
+ `MarkerState` while the remaining `TextOpts` fields nest under
1012
+ `state.style`.
1013
+
1014
+ `chartlang-example-canvas2d-adapter` ships 4 new renderers under
1015
+ `src/render/draw/`. `renderCircle` derives the radius in canvas-pixel
1016
+ space from `|edge - centre|` (matches invinite's circle-tool) and
1017
+ issues a single `ctx.arc(...)`. `renderEllipse` paints a 64-segment
1018
+ polyline approximation (Phase-1 `RenderCtx` exposes `arc(...)` but
1019
+ not `ellipse(...)` — a polyline keeps the renderer pure on the
1020
+ existing structural surface without widening it). `renderPath` paints
1021
+ an OPEN polyline (no `closePath` by default; `style.closed === true`
1022
+ toggles closure). `renderMarker` projects the anchor + paints
1023
+ `text` (when set) via `ctx.fillText` with `TextOpts`-derived font +
1024
+ alignment. Empty / undefined text is a pure no-op — icon-glyph
1025
+ painting belongs to Task 20's `defineDrawing` follow-up. The
1026
+ `drawingDispatch` switch flips the 4 box-B arms from no-op stubs to
1027
+ real `renderXxx(ctx, e, view)` calls; exhaustiveness is preserved.
1028
+
1029
+ `@invinite-org/chartlang-conformance` ships 4 new per-kind scenarios
1030
+ under `src/scenarios/` (`drawCircle`, `drawEllipse`, `drawPath`,
1031
+ `drawMarker`). Per README §22.10 the Task-6 `drawBoxesA.scenario.ts`
1032
+ is REPLACED (deleted) by the wider `drawBoxesAll.scenario.ts`
1033
+ covering all 8 box kinds across Tasks 6 + 7 (rectangle /
1034
+ rotated-rectangle / triangle / polyline / circle / ellipse / path /
1035
+ marker). All five new scenarios use `inlineSource` against the
1036
+ bundled 10 000-bar `goldenBars.json` fixture with anchor times pulled
1037
+ from `bars[0]` / `bars[500]` / `bars[1000]`. The `TEST_CAPABILITIES`
1038
+ bag in `runConformanceSuite.test.ts` + `scenarios.test.ts` bumps
1039
+ `labels` budget from 0 to 100 to host the marker scenario (marker
1040
+ maps to the `labels` bucket). The 4 + 1 new scenarios extend
1041
+ `ALL_SCENARIOS` and the public re-export surface; `DRAW_BOXES_A_SCENARIO`
1042
+ is removed from the public surface (downstream consumers move to
1043
+ `DRAW_BOXES_ALL_SCENARIO`).
1044
+
1045
+ No core edits — the `DrawingState` variants and `DrawNamespace`
1046
+ signatures Task 1 shipped are the canonical shape and Task 7 wires
1047
+ real impls to them.
1048
+
1049
+ Deviations from spec, flagged for review:
1050
+
1051
+ - `MarkerState` shape divergence — task spec's `markerKind` (`emoji` /
1052
+ `icon`) discriminator + `value: string` + `MAX_LENGTH = 32` + icon
1053
+ registry NOT implemented. Uses Task 1's landed
1054
+ `{ anchor, text?, value?, style: TextOpts }` shape (anchor not
1055
+ from/to pair; value is a number; no discriminator). Re-shaping
1056
+ belongs to a follow-up that widens core; mid-phase Task-1 reshapes
1057
+ cascade through the `DrawingState` union + adapter-kit decoder +
1058
+ Task-6 permissive-default tests.
1059
+ - `Ellipse` rendered as 64-segment polyline approximation because
1060
+ `RenderCtx` exposes `arc(...)` but not `ellipse(...)`. Widening
1061
+ the structural type would touch Phase-1's `RenderCtx`; the
1062
+ polyline path stays on the existing surface.
1063
+ - Per-kind §22.10 5-file test set deferred to pragmatic 1-file set
1064
+ (mirrors Tasks 5 + 6) — Task 3's `pushDrawing.*` and `handle.*`
1065
+ suite covers the underlying infra exhaustively.
1066
+ - `gen-docs` doc-page generation deferred to Task 21 (mirrors Tasks
1067
+ 5 + 6).
1068
+
1069
+ - b0d296b: Phase-3 Task 8 — fourth per-port task. Lands the 6 curve + freehand
1070
+ drawing kinds (`arc`, `curve`, `doubleCurve`, `pen`, `highlighter`,
1071
+ `brush`) per PLAN.md §10 and §22.10. Behaviour ports from invinite
1072
+ commit `078f41fe2569d659d5aba726da8bcb5d3e2ced02`:
1073
+ `tools/arc-tool.ts`, `tools/curve-tool.ts`,
1074
+ `tools/double-curve-tool.ts`, `tools/pen-tool.ts`,
1075
+ `tools/highlighter-tool.ts`, `tools/brush-tool.ts`, and the matching
1076
+ `y-doc-bridge.ts` variants (`ArcDrawing`, `CurveDrawing`,
1077
+ `DoubleCurveDrawing`, `PenDrawing`, `HighlighterDrawing`,
1078
+ `BrushDrawing`). All 6 kinds map to the `polylines` bucket.
1079
+
1080
+ `@invinite-org/chartlang-adapter-kit` adds per-kind state validators
1081
+ for the 6 curve + freehand kinds — `validateArcState`,
1082
+ `validateCurveState`, `validateDoubleCurveState`, `validatePenState`,
1083
+ `validateHighlighterState`, `validateBrushState` — wired into the
1084
+ existing `validateStateByKind` dispatch. Three new file-local helpers
1085
+ land alongside: `validateAnchorQuint` (5-tuple for `double-curve`),
1086
+ `validateHighlighterStyle` (required `color: string` + required
1087
+ `alpha ∈ [0, 1]`), and `validateBrushStyle` (required `stroke` + `fill`
1088
+ colour strings). Freehand kinds pin `2 ≤ anchors.length ≤ 500`
1089
+ (matches invinite's stroke cap; broader than the 2..20 path cap).
1090
+ Wire shape is stricter than before — payloads previously passing the
1091
+ permissive default arm now reject with `malformed-emission`.
1092
+
1093
+ `@invinite-org/chartlang-runtime` ships 6 new `draw.<kind>(...)` emit
1094
+ functions under `src/emit/draw/curves/` and extends the
1095
+ `DRAW_NAMESPACE` swap-seam at `src/emit/draw/namespace.ts`. Each impl
1096
+ uses the dual-overload pattern Tasks 5–7 pinned. `draw.highlighter`
1097
+ and `draw.brush` differ from the other emit fns — their `opts`
1098
+ parameter is REQUIRED on the script-facing overload (no `?` because
1099
+ `HighlighterStyle` / `BrushStyle` carry required fields).
1100
+
1101
+ `chartlang-example-canvas2d-adapter` ships 6 new renderers under
1102
+ `src/render/draw/`. The 3 curve renderers (`renderArc`, `renderCurve`,
1103
+ `renderDoubleCurve`) sample the curve via Task 4's `sampleQuadratic` /
1104
+ `sampleCubic` helpers at `CURVE_SAMPLES = 32` segments and stroke as a
1105
+ polyline — the structural `RenderCtx` exposes neither
1106
+ `quadraticCurveTo` nor `bezierCurveTo`, so this keeps the renderer
1107
+ pure on the Phase-1 surface (mirrors Task 7's `ellipse` 64-segment
1108
+ polyline approximation). `renderArc` derives the Bezier control point
1109
+ from `apex` via inverse-quadratic interpolation so the curve passes
1110
+ through `apex` at `t = 0.5`; `renderCurve` uses `anchors[1]` as the
1111
+ Bezier control directly (curve does NOT pass through control);
1112
+ `renderDoubleCurve` paints a single cubic from `anchors[0]` to
1113
+ `anchors[4]` with off-curve controls `anchors[1]` / `anchors[3]` (the
1114
+ middle stitch anchor `anchors[2]` is preserved in state but unused by
1115
+ the current render path — flagged for future split-rendering). The 3
1116
+ freehand renderers paint polylines: `renderPen` strokes open;
1117
+ `renderHighlighter` wraps the stroke in a `globalAlpha` set/reset
1118
+ bracket (default 6 px line width); `renderBrush` paints
1119
+ fill-then-stroke with `closePath` for a closed filled region. The
1120
+ `drawingDispatch` switch flips the 6 arms from no-op stubs to real
1121
+ `renderXxx(ctx, e, view)` calls; exhaustiveness is preserved.
1122
+
1123
+ `@invinite-org/chartlang-conformance` ships 6 new per-kind scenarios
1124
+ under `src/scenarios/` (`drawArc`, `drawCurve`, `drawDoubleCurve`,
1125
+ `drawPen`, `drawHighlighter`, `drawBrush`) plus one bundle scenario
1126
+ `drawCurvesAndFreehandAll` that emits one drawing per curve + freehand
1127
+ kind on the first bar (per README §22.10 Task 8 collapses both
1128
+ categories into ONE bundle). All seven scenarios use `inlineSource`
1129
+ against the bundled 10 000-bar `goldenBars.json` fixture with anchor
1130
+ times pulled from `bars[0]` / `bars[500]` / `bars[1000]` (plus
1131
+ `bars[1500]` for the 4-point freehand strokes). The `TEST_CAPABILITIES`
1132
+ bags in `runConformanceSuite.test.ts` + `scenarios/scenarios.test.ts`
1133
+ extend the `drawings` set with `allCurveDrawings()` +
1134
+ `allFreehandDrawings()`; the existing `polylines: 100` bucket budget
1135
+ covers the bundle scenarios with headroom. `ALL_SCENARIOS` extends
1136
+ additively.
1137
+
1138
+ No core edits — the `DrawingState` variants and `DrawNamespace`
1139
+ signatures Task 1 shipped are the canonical shape and Task 8 wires
1140
+ real impls to them.
1141
+
1142
+ Deviations from spec, flagged for review:
1143
+
1144
+ - `PressurePoint` type widening NOT applied — Task 1's `PenState`
1145
+ shape (`anchors: ReadonlyArray<WorldPoint>`) preserved per Tasks
1146
+ 6/7 precedent of not reshaping Task-1 mid-phase. Adapter-level
1147
+ pressure-driven stroke-width variance is a follow-up concern.
1148
+ - `freehand.ts` smoothing helper NOT created. Per-renderer inline
1149
+ polyline loops suffice for Phase-3 deterministic `drawing-hash`
1150
+ assertions. If pressure-driven smoothing lands later, the helper
1151
+ can ship then.
1152
+ - `double-curve` middle anchor (`anchors[2]`, the stitch point) is
1153
+ preserved in state but currently unused by the renderer (single
1154
+ cubic from `anchors[0]` to `anchors[4]` with controls `[1]` / `[3]`).
1155
+ Future split-rendering can stitch two cubics through `mid`.
1156
+ - `arc` / `curve` / `doubleCurve` fill-path NOT rendered.
1157
+ `LineDrawStyle` has no fill fields; invinite's tools do support
1158
+ fill on these kinds. Widening to support fill is a Task-1 reshape
1159
+ and out of scope.
1160
+ - Bezier rendered as 32-segment polyline approximation because
1161
+ `RenderCtx` exposes `arc(...)` but not `quadraticCurveTo` /
1162
+ `bezierCurveTo`. Mirrors Task 7's `ellipse` 64-segment approach;
1163
+ widening would touch Phase-1 surface.
1164
+ - Per-kind §22.10 5-file test set deferred to pragmatic 1-file set
1165
+ (mirrors Tasks 5–7) — Task 3's `pushDrawing.*` and `handle.*`
1166
+ suite covers the underlying infra exhaustively.
1167
+ - `gen-docs` doc-page generation deferred to Task 21 (mirrors Tasks
1168
+ 5–7).
1169
+
1170
+ - b0d296b: Phase-3 Task 9 — fifth per-port task. Lands the 5 annotation drawing
1171
+ kinds (`text`, `arrow`, `arrowMarker`, `arrowMarkUp`, `arrowMarkDown`)
1172
+ per PLAN.md §10 and §22.10. Behaviour ports from invinite commit
1173
+ `078f41fe2569d659d5aba726da8bcb5d3e2ced02`: `tools/text-tool.ts`,
1174
+ `tools/arrow-tool.ts`, `tools/arrow-marker-tool.ts`,
1175
+ `tools/arrow-mark-up-tool.ts`, `tools/arrow-mark-down-tool.ts`, and the
1176
+ matching `y-doc-bridge.ts` variants (`TextDrawing`, `ArrowDrawing`,
1177
+ `ArrowMarkerDrawing`, `ArrowMarkUpDrawing`, `ArrowMarkDownDrawing`).
1178
+ All 5 kinds map to the `labels` bucket.
1179
+
1180
+ `@invinite-org/chartlang-adapter-kit` adds per-kind state validators
1181
+ for the 5 annotation kinds — `validateTextState`, `validateArrowState`,
1182
+ `validateArrowMarkerState`, `validateArrowMarkUpState`,
1183
+ `validateArrowMarkDownState` — wired into the existing
1184
+ `validateStateByKind` dispatch. Two new file-local style helpers land
1185
+ alongside: `validateArrowOpts` (`LineDrawStyle` + optional string
1186
+ `label`) and `validateArrowMarkerOpts` (optional `color` + optional
1187
+ `text`). `text.body` is validated through `walkMeta` (catches
1188
+ non-JsonValue payloads like bigint / function / symbol) and then
1189
+ pinned as a non-empty string with `TEXT_BODY_MAX_LENGTH = 256` (longer
1190
+ than the 128 cap on plot labels — annotation strings carry short
1191
+ rationales like "Inverse Head and Shoulders Confirmed"). Wire shape
1192
+ is stricter than before — payloads previously passing the permissive
1193
+ default arm now reject with `malformed-emission`.
1194
+
1195
+ `@invinite-org/chartlang-runtime` ships 5 new `draw.<kind>(...)` emit
1196
+ functions under `src/emit/draw/annotations/` and extends the
1197
+ `DRAW_NAMESPACE` swap-seam at `src/emit/draw/namespace.ts`. Each impl
1198
+ uses the dual-overload pattern Tasks 5–8 pinned. `draw.text` is the
1199
+ first emit fn with three script-facing arguments (`anchor`, `body`,
1200
+ `opts?`); the compiler-injected form is `(slotId, anchor, body,
1201
+ opts?)` and the impl signature carries four arguments.
1202
+
1203
+ `chartlang-example-canvas2d-adapter` ships 5 new renderers under
1204
+ `src/render/draw/` plus three new shared helpers: `arrowhead.ts`
1205
+ (`drawArrowhead(ctx, from, to, size?)` — filled triangular arrowhead
1206
+ at `to` pointing along the shaft direction; used by `arrow` +
1207
+ `arrowMarker`), `chevron.ts` (`drawChevron(ctx, at, direction, color,
1208
+ baseWidth?, height?)` — filled up/down triangle glyph; used by
1209
+ `arrowMarkUp` + `arrowMarkDown`), and `textStyle.ts` (`SIZE_TO_PX` /
1210
+ `HALIGN_TO_TEXTALIGN` / `VALIGN_TO_TEXTBASELINE` maps +
1211
+ `resolveTextOpts(opts)` helper that turns a `TextOpts` bag into the
1212
+ four canvas text-state values). The Task-7 `marker.ts` renderer is
1213
+ refactored to consume `textStyle.ts` for the same maps — its call
1214
+ sequence is preserved exactly so `marker.test.ts` continues to pass
1215
+ unchanged. Default colours follow invinite's paint-time defaults:
1216
+ `#3b82f6` (toolbar blue) for `arrowMarker`, `#22c55e` (green) for
1217
+ `arrowMarkUp`, `#ef4444` (red) for `arrowMarkDown`. The `drawingDispatch`
1218
+ switch flips the 5 arms from no-op stubs to real `renderXxx(ctx, e,
1219
+ view)` calls; exhaustiveness is preserved.
1220
+
1221
+ `@invinite-org/chartlang-conformance` ships 5 new per-kind scenarios
1222
+ under `src/scenarios/` (`drawText`, `drawArrow`, `drawArrowMarker`,
1223
+ `drawArrowMarkUp`, `drawArrowMarkDown`) plus one bundle scenario
1224
+ `drawAnnotationsAll` that emits one drawing per annotation kind on
1225
+ the first bar (per README §22.10 Task 9 collapses the category into
1226
+ ONE bundle). All six scenarios use `inlineSource` against the bundled
1227
+ 10 000-bar `goldenBars.json` fixture with anchor times pulled from
1228
+ `bars[0]` / `bars[500]` / `bars[1000]`. The `TEST_CAPABILITIES` bags
1229
+ in `runConformanceSuite.test.ts` + `scenarios/scenarios.test.ts`
1230
+ extend the `drawings` set with `allAnnotationDrawings()`; the existing
1231
+ `labels: 100` bucket budget (added when Task 7's `marker` scenario
1232
+ landed) covers the bundle scenarios with headroom. `ALL_SCENARIOS`
1233
+ extends additively.
1234
+
1235
+ No core edits — the `DrawingState` variants and `DrawNamespace`
1236
+ signatures Task 1 shipped are the canonical shape and Task 9 wires
1237
+ real impls to them.
1238
+
1239
+ Deviations from spec, flagged for review:
1240
+
1241
+ - `text.bgColor` background-rectangle paint NOT rendered. The
1242
+ structural `RenderCtx` exposes neither `measureText` nor a
1243
+ background-rect path; widening would touch the Phase-1 structural
1244
+ type. The `bgColor` field is preserved on the wire (validator
1245
+ accepts string) but the canvas2d renderer does not paint a
1246
+ background rect. Mirror Task 7's `marker` precedent.
1247
+ - `ArrowOpts.label` rotation NOT rendered. `RenderCtx` has no
1248
+ `rotate / translate / save / restore`. Label paints un-rotated at
1249
+ the shaft midpoint with `textAlign = "center"` /
1250
+ `textBaseline = "bottom"`. Pure on the Phase-1 surface.
1251
+ - `ArrowMarkerState` ↔ spec shape delta. Task 1's core landed
1252
+ `ArrowMarkerState` with single `anchor: WorldPoint`; the spec
1253
+ README §13 says `2 (from, to)`. Per Tasks 6/7's "don't reshape
1254
+ Task-1 mid-phase" precedent, Task 9 uses the single-anchor form
1255
+ and the renderer paints a self-contained glyph (dot + stub line +
1256
+ arrowhead + optional text) at the anchor — a "annotation lives
1257
+ here" marker that fits in ~24px. Reshape can ship in a follow-up.
1258
+ - `marker.ts` refactor crosses Task 7 boundary by ~5 lines to
1259
+ consume the new shared `textStyle.ts` helper. The call sequence is
1260
+ preserved exactly; `marker.test.ts` continues to pass without
1261
+ modifications.
1262
+ - Per-kind §22.10 5-file test set deferred to pragmatic 1-file set
1263
+ (mirrors Tasks 5–8) — Task 3's `pushDrawing.*` and `handle.*`
1264
+ suite covers the underlying infra exhaustively.
1265
+ - `gen-docs` doc-page generation deferred to Task 21 (mirrors Tasks
1266
+ 5–8).
1267
+
1268
+ - Phase 4 - Editor + Inputs + Timeframes + Tier-1 Pine parity.
1269
+ Adds: input._ builders, state._ / state.tick.\* slots,
1270
+ barstate / syminfo / timeframe views, request.security typed
1271
+ surface (NaN fallback), defineIndicator overrides,
1272
+ Capabilities triad (intervals / multiTimeframe / subPanes /
1273
+ symInfoFields / maxDrawingsPerScript / alertConditions / logs),
1274
+ language-service hover registry + LSP-style API, CodeMirror 6
1275
+ editor shell + /react sub-export, Inputs UI ViewModel + React
1276
+ form. See tasks/phase-4-editor-tier1/README.md.
1277
+ - Add Phase 4 capability builders for timeframes, panes, syminfo fields, drawing budgets, alert conditions, and logs.
1278
+ - Wire runtime `barstate`, `syminfo`, and `timeframe` views, and add optional adapter symbol metadata for `syminfo` population.
1279
+ - Resolve runtime `input.*` overrides at mount, add adapter input resolver wiring, and audit universal `ta.*` offset support.
1280
+
1281
+ ### Patch Changes
1282
+
1283
+ - b0d296b: Phase-3 Task 1 — `draw.*` type surface foundation.
1284
+
1285
+ Adds the canonical Phase-3 type surface to `@invinite-org/chartlang-core`:
1286
+
1287
+ - `DrawingKind` — 61-entry kebab-case discriminated union (lines /
1288
+ boxes / curves / freehand / annotations / channels / fib / gann /
1289
+ pitchforks / patterns / elliott / cycles / containers). The
1290
+ kebab-case wire format is the source-of-truth; the camelCase
1291
+ TypeScript surface (`draw.horizontalLine`, `draw.fibRetracement`,
1292
+ …) is pinned via the `KIND_CAMELCASE` / `KIND_KEBABCASE` bijection.
1293
+ - `DRAWING_KINDS` — iterable form of `DrawingKind` in canonical
1294
+ declaration order.
1295
+ - `WorldPoint` + `AnchorPair` / `AnchorTriple` / `AnchorQuad` /
1296
+ `AnchorQuint` / `AnchorHept` helpers.
1297
+ - `DrawingState` — discriminated union with one variant per kind.
1298
+ Geometry + style fields only; collab-only fields (Yjs ids,
1299
+ layerIds, intervals, parentGroupId/FrameId, createdAt, authorId)
1300
+ from the invinite source are stripped per PLAN.md §10.4. Variants
1301
+ are minimal shells in this task; Tasks 5–18 refine per-category
1302
+ payloads.
1303
+ - Per-kind style bag types: `LineDrawStyle`, `ShapeStyle`,
1304
+ `HighlighterStyle`, `BrushStyle`, `TextOpts`, `ArrowOpts`,
1305
+ `ArrowMarkerOpts`, `PathOpts`, `FibOpts`, `RegressionTrendOpts`,
1306
+ `FrameOpts`.
1307
+ - `DrawingHandle` — script-facing handle returned by every
1308
+ `draw.<kind>(...)` call. Impl lives in the runtime (Task 3).
1309
+ - `DrawNamespace` + `FibSubNamespace` / `GannSubNamespace` /
1310
+ `ElliottSubNamespace` / `PatternSubNamespace` — the type the
1311
+ runtime swaps the throwing-stub `draw` Proxy for at boot. The
1312
+ stub mirrors the `plot` / `hline` / `alert` pattern from
1313
+ `plot/plot.ts`.
1314
+ - `DrawingBucket` + `KIND_BUCKET` + `bucketFor(kind)` — canonical
1315
+ kind → bucket map (`lines` / `labels` / `boxes` / `polylines` /
1316
+ `other`). Consumed by the runtime budget enforcer (Task 3) and
1317
+ by adapters that pre-budget.
1318
+ - `DrawingCounts` — moved here from `@invinite-org/chartlang-adapter-kit`
1319
+ so `ScriptManifest.maxDrawings?: DrawingCounts` and
1320
+ `Capabilities.maxDrawingsPerScript` pin the same shape without
1321
+ introducing a `core → adapter-kit` dependency cycle. The
1322
+ `adapter-kit` `DrawingCounts` export is now a type re-export of
1323
+ the core declaration — no public-surface drift, no consumer-visible
1324
+ change.
1325
+ - `ScriptManifest.maxDrawings?: DrawingCounts` + matching
1326
+ `DefineIndicatorOpts.maxDrawings?: DrawingCounts` propagation.
1327
+
1328
+ Extends `STATEFUL_PRIMITIVES` by 61 `draw.<camelKind>` entries (all
1329
+ `slot: true`). Cardinality grows from **93 → 154**. The new entries
1330
+ follow the canonical `DRAWING_KINDS` order. The compiler's
1331
+ `callsiteIdInjection` + `statefulCallInLoop` passes pick them up by
1332
+ name automatically.
1333
+
1334
+ No runtime behavior change in this task — `draw` is a throwing-stub
1335
+ Proxy until Task 3 wires the runtime emit infra. Phase-3 downstream
1336
+ tasks (2–22) all import from this surface.
1337
+
1338
+ - Updated dependencies [3f3ce38]
1339
+ - Updated dependencies [38fb475]
1340
+ - Updated dependencies [38fb475]
1341
+ - Updated dependencies [38fb475]
1342
+ - Updated dependencies [38fb475]
1343
+ - Updated dependencies [38fb475]
1344
+ - Updated dependencies [38fb475]
1345
+ - Updated dependencies [38fb475]
1346
+ - Updated dependencies [38fb475]
1347
+ - Updated dependencies [38fb475]
1348
+ - Updated dependencies [38fb475]
1349
+ - Updated dependencies [38fb475]
1350
+ - Updated dependencies [38fb475]
1351
+ - Updated dependencies [38fb475]
1352
+ - Updated dependencies [38fb475]
1353
+ - Updated dependencies [38fb475]
1354
+ - Updated dependencies [38fb475]
1355
+ - Updated dependencies [38fb475]
1356
+ - Updated dependencies [38fb475]
1357
+ - Updated dependencies [38fb475]
1358
+ - Updated dependencies [38fb475]
1359
+ - Updated dependencies [38fb475]
1360
+ - Updated dependencies [38fb475]
1361
+ - Updated dependencies [38fb475]
1362
+ - Updated dependencies [38fb475]
1363
+ - Updated dependencies [38fb475]
1364
+ - Updated dependencies [38fb475]
1365
+ - Updated dependencies [38fb475]
1366
+ - Updated dependencies [38fb475]
1367
+ - Updated dependencies [b0d296b]
1368
+ - Updated dependencies [b0d296b]
1369
+ - Updated dependencies [b0d296b]
1370
+ - Updated dependencies [b0d296b]
1371
+ - Updated dependencies [b0d296b]
1372
+ - Updated dependencies
1373
+ - Updated dependencies
1374
+ - Updated dependencies
1375
+ - Updated dependencies
1376
+ - Updated dependencies
1377
+ - Updated dependencies
1378
+ - Updated dependencies
1379
+ - Updated dependencies
1380
+ - @invinite-org/chartlang-core@0.4.0