feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META - #224

Merged
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render
Jul 29, 2026
Merged

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META#224
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

What

The Run Inspector's native pulse plot (#66) was hardwired to ZOH stairs, so a linear-spline pulse drew as wrong stairs. AMICODE_PULSE_META now carries an optional interp=zoh|linear|cubic field and the plot branches per mode: stairs (zero-order hold), polyline through linear-spline knots, smooth curve through cubic knots.

Contract decisions

  • interp= is a TRAILING optional field (after bounds=). Trailing so the opencode fork's tail-capture mirror regex (problems.ts, labels=([^\n]*)$) keeps matching without a coordinated change. Emitters: append it at the end of the line.
  • Unknown values coerce to zoh without dropping the meta. A future interp kind degrades an old client to stairs — never to NO_DATA. (Deliberate: strict-grammar fragility is what NO_DATA'd the cloud path this week.)
  • No interp=zoh: every existing run.log renders exactly as before (verified against a real pre-change run dir).

Render details

  • Path math extracted to DOM-free media/ui/components/pulsepath.ts, unit-tested: mode-correct crosshair mapping (interval midpoints under zoh, knots under splines), duration (n·dt vs (n−1)·dt), single-knot degenerate holds.
  • Cubic is knot-faithful, not coefficient-faithful — the record carries values only, so the curve is a Catmull-Rom through the true knots. Faithful cubic needs knot derivatives carried in the line format (flagged to the cloud-delivery side; offered as a format extension).
  • Warming hint no longer promises "~1–2 min": a cloud run warms ~6 min and then streams in ~15 s sidecar sync bursts; the copy now stays true for both. The render path was already burst-safe (wholesale path swap per record, 5 Hz coalescing throttle).
  • Dev harness gains interp/knot-count switches (pnpm run dev:pulseplot) for eyeballing all three modes; 12 knots makes them unmistakable.

Tested

  • 787 unit tests pass incl. 12 new (grammar: interp present/absent/unknown; geometry: all three paths, cursor mapping, durations). Typecheck + repertoire lint clean.
  • Real-pipeline eyeball: a real x-gate transmon run dir stamped interp=linear renders as a polyline in the inspector (F=0.99993 intact), interp=cubic as a smooth curve, and the untouched original as stairs — full state matrix incl. the no-pulse-data empty state.

Out of scope

Cloud delivery of AMICODE_PULSE lines (remote_executor forwarder + runner capture) — owned separately; this PR defines the contract the relay serves. Mock-replay acceptance pending the shared playback harness.

🤖 Generated with Claude Code

kateebonnerand others added 3 commits July 28, 2026 21:05
…META (#66)
AMICODE_PULSE_META gains a TRAILING optional interp= field: zoh (default),
linear, cubic. Trailing so the opencode fork's tail-capture mirror keeps
matching; unknown values coerce to zoh so a future interp kind degrades an
old client to stairs, never to NO_DATA.
The plot branches per mode: stairs (zero-order hold, as before), polyline
through linear-spline knots, Catmull-Rom curve through cubic knots. Path
math extracted to a DOM-free pulsepath.ts (unit-tested): mode-correct
crosshair mapping (interval midpoints vs knots), duration (n·dt vs
(n-1)·dt), single-knot degenerate holds.
Cubic is knot-faithful, not coefficient-faithful — the record carries no
derivatives; flagged for a line-format extension on the delivery side.
Warming hint drops its ~1–2 min promise: a cloud run warms ~6 min and then
streams in ~15 s sidecar bursts, and the copy now stays true for both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip zoh/linear/cubic live on the same synthetic knots (12 knots makes the
three modes unmistakable; 50 is fixture scale). Redraws immediately on
switch while playing. Dev-only (.vscodeignore'd).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erp=
Review pass on #224 caught this: tolerating ONLY interp= recreates the
strict-grammar fragility one field into the future — the planned cubic
derivatives extension (or any new field) would strand THIS client on
NO_DATA, the exact failure class the design notes call out. Both
AMICODE_PULSE_META and AMICODE_PULSE now accept an open tail of key=value
fields (unknown fields ignored; non-key=value tails still reject as
corruption). Replayed all 2258 records across the full local run corpus:
zero drops, legacy metas all read zoh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
ContributorAuthor

Thorough re-test pass:

  • Full suite: 788 passed (37 on the pulse grammar/geometry files, incl. 4 new future-field tests). Typecheck, webview bundle, repertoire lint all clean.
  • Real-corpus replay: every AMICODE_PULSE* line from all ~50 run dirs on a real machine through the new parser — 30 metas, 2,258 records, 0 dropped; all legacy metas correctly default to zoh.
  • Adversarial review caught one real gap, fixed in the follow-up commit: the parser tolerated only interp= as a trailing field, so the next format extension (e.g. the planned d= cubic derivatives) would have stranded this client on NO_DATA — the exact failure class this PR exists to prevent. Both pulse lines now accept an open key=value tail; genuine corruption still rejects.
  • Live verification in the extension dev host: real x-gate run stamped interp=linear renders as a polyline (F=0.99993 intact), interp=cubic as a smooth curve, untouched run as stairs, and a pulse-free run shows the no-data hint.

@jack-champagne

Copy link
Copy Markdown
Member

Locking the pulse-derivative line format so the emit + render sides match 👇

Emitter (cloud/solve output) now emits, order-agnostic:

  • AMICODE_PULSE_META … interp=zoh|linear|cubic — feeds your type-aware mode select. (Heads up: your parser coerces missing interp → zoh, so a linear/cubic pulse without this field was drawing as stairs — now it's set.)
  • AMICODE_PULSE … a=<knots> d=<du knots>d= is the first-derivative (du) at each knot, same layout as a= (comma-sep knots, ;-sep drives), appended as an open-tail field. Emitted only when the trajectory has a :du component — ZOH has none, so it's omitted there (your tail treats absent = Catmull-Rom fallback ✅).

For faithful cubic, du is all you need — value + slope → Hermite. ddu is not needed for the plot (that's the smoothness-reg term; only matters for a quintic/C² render). So don't wait on it.

Your follow-up (consume side): parse d= out of the tail into PulseRecord, and swap cubicPath's Catmull-Rom tangents (p(k+1)−p(k−1))/6 for the provided d[k] (Hermite). Linear/ZOH paths unchanged.

I'm generating a real cubic solve sample (interp=cubic + d=) right now so you can build the Hermite render against actual solver derivatives, not synthetic — will point you at it shortly (~/repos/harmoniqs/plans/run-inspector-mock/).

One thing to sanity-check when you wire it: this assumes the cubic curve is cubic-Hermite in (value, du). If Piccolissimo's cubic basis actually leans on ddu for the segment shape, du-only Hermite won't be pixel-faithful and we'd add a dd= field — worth an eyeball against the solver's own dense pulse.

@jack-champagne
jack-champagne merged commit 153042c into mainJul 29, 2026
5 checks passed
@jack-champagne
jack-champagne deleted the kate/pulseplot-interp-render branch July 29, 2026 02:37
@jack-champagne

Copy link
Copy Markdown
Member

Correction on the sample I promised: bumping the integrator'sspline_order to 3 doesn't make the control cubic (bit-identical optimum → still LinearSplinePulse). So the sample at ~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is interp=linear + d=<du>, not cubic.

Still useful: the d= plumbing works end-to-end and the values are real (they're exactly the linear segment slopes, e.g. d[0] = (a[1]−a[0])/dt), so you can build + wire your Hermite render against it — flip interp=cubic on your end to exercise the curve code against real derivatives. Just don't treat it as ground-truth for cubic fidelity — a genuinely cubic pulse needs a cubic pulse constructor in the solve, which isn't wired yet (chasing that separately).

@jack-champagne

Copy link
Copy Markdown
Member

Real cubic sample is up now (thanks Jack — the fix was CubicSplinePulse(values, derivatives, times); the pulse type sets the spline order, the integrator kwarg is overridden by it).

~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is now a genuine cubic solve:

  • AMICODE_PULSE_META … interp=cubic
  • d= on all 61 lines, and the derivatives are independent now (e.g. d[0]=0, 0.0588, −0.178…) — NOT the linear slopes from before (−2.70…). So it actually curves the segments.
  • result.toml: integrator = spline_cubic, fidelity 0.99999957.

So you can build + validate the Hermite render against ground-truth cubic derivatives. ./playback_run.sh sample-run to animate it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@kateebonner@jack-champagne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META - #224

Merged
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render
Jul 29, 2026
Merged

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META#224
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

What

The Run Inspector's native pulse plot (#66) was hardwired to ZOH stairs, so a linear-spline pulse drew as wrong stairs. AMICODE_PULSE_META now carries an optional interp=zoh|linear|cubic field and the plot branches per mode: stairs (zero-order hold), polyline through linear-spline knots, smooth curve through cubic knots.

Contract decisions

  • interp= is a TRAILING optional field (after bounds=). Trailing so the opencode fork's tail-capture mirror regex (problems.ts, labels=([^\n]*)$) keeps matching without a coordinated change. Emitters: append it at the end of the line.
  • Unknown values coerce to zoh without dropping the meta. A future interp kind degrades an old client to stairs — never to NO_DATA. (Deliberate: strict-grammar fragility is what NO_DATA'd the cloud path this week.)
  • No interp=zoh: every existing run.log renders exactly as before (verified against a real pre-change run dir).

Render details

  • Path math extracted to DOM-free media/ui/components/pulsepath.ts, unit-tested: mode-correct crosshair mapping (interval midpoints under zoh, knots under splines), duration (n·dt vs (n−1)·dt), single-knot degenerate holds.
  • Cubic is knot-faithful, not coefficient-faithful — the record carries values only, so the curve is a Catmull-Rom through the true knots. Faithful cubic needs knot derivatives carried in the line format (flagged to the cloud-delivery side; offered as a format extension).
  • Warming hint no longer promises "~1–2 min": a cloud run warms ~6 min and then streams in ~15 s sidecar sync bursts; the copy now stays true for both. The render path was already burst-safe (wholesale path swap per record, 5 Hz coalescing throttle).
  • Dev harness gains interp/knot-count switches (pnpm run dev:pulseplot) for eyeballing all three modes; 12 knots makes them unmistakable.

Tested

  • 787 unit tests pass incl. 12 new (grammar: interp present/absent/unknown; geometry: all three paths, cursor mapping, durations). Typecheck + repertoire lint clean.
  • Real-pipeline eyeball: a real x-gate transmon run dir stamped interp=linear renders as a polyline in the inspector (F=0.99993 intact), interp=cubic as a smooth curve, and the untouched original as stairs — full state matrix incl. the no-pulse-data empty state.

Out of scope

Cloud delivery of AMICODE_PULSE lines (remote_executor forwarder + runner capture) — owned separately; this PR defines the contract the relay serves. Mock-replay acceptance pending the shared playback harness.

🤖 Generated with Claude Code

kateebonnerand others added 3 commits July 28, 2026 21:05
…META (#66)
AMICODE_PULSE_META gains a TRAILING optional interp= field: zoh (default),
linear, cubic. Trailing so the opencode fork's tail-capture mirror keeps
matching; unknown values coerce to zoh so a future interp kind degrades an
old client to stairs, never to NO_DATA.
The plot branches per mode: stairs (zero-order hold, as before), polyline
through linear-spline knots, Catmull-Rom curve through cubic knots. Path
math extracted to a DOM-free pulsepath.ts (unit-tested): mode-correct
crosshair mapping (interval midpoints vs knots), duration (n·dt vs
(n-1)·dt), single-knot degenerate holds.
Cubic is knot-faithful, not coefficient-faithful — the record carries no
derivatives; flagged for a line-format extension on the delivery side.
Warming hint drops its ~1–2 min promise: a cloud run warms ~6 min and then
streams in ~15 s sidecar bursts, and the copy now stays true for both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip zoh/linear/cubic live on the same synthetic knots (12 knots makes the
three modes unmistakable; 50 is fixture scale). Redraws immediately on
switch while playing. Dev-only (.vscodeignore'd).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erp=
Review pass on #224 caught this: tolerating ONLY interp= recreates the
strict-grammar fragility one field into the future — the planned cubic
derivatives extension (or any new field) would strand THIS client on
NO_DATA, the exact failure class the design notes call out. Both
AMICODE_PULSE_META and AMICODE_PULSE now accept an open tail of key=value
fields (unknown fields ignored; non-key=value tails still reject as
corruption). Replayed all 2258 records across the full local run corpus:
zero drops, legacy metas all read zoh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
ContributorAuthor

Thorough re-test pass:

  • Full suite: 788 passed (37 on the pulse grammar/geometry files, incl. 4 new future-field tests). Typecheck, webview bundle, repertoire lint all clean.
  • Real-corpus replay: every AMICODE_PULSE* line from all ~50 run dirs on a real machine through the new parser — 30 metas, 2,258 records, 0 dropped; all legacy metas correctly default to zoh.
  • Adversarial review caught one real gap, fixed in the follow-up commit: the parser tolerated only interp= as a trailing field, so the next format extension (e.g. the planned d= cubic derivatives) would have stranded this client on NO_DATA — the exact failure class this PR exists to prevent. Both pulse lines now accept an open key=value tail; genuine corruption still rejects.
  • Live verification in the extension dev host: real x-gate run stamped interp=linear renders as a polyline (F=0.99993 intact), interp=cubic as a smooth curve, untouched run as stairs, and a pulse-free run shows the no-data hint.

@jack-champagne

Copy link
Copy Markdown
Member

Locking the pulse-derivative line format so the emit + render sides match 👇

Emitter (cloud/solve output) now emits, order-agnostic:

  • AMICODE_PULSE_META … interp=zoh|linear|cubic — feeds your type-aware mode select. (Heads up: your parser coerces missing interp → zoh, so a linear/cubic pulse without this field was drawing as stairs — now it's set.)
  • AMICODE_PULSE … a=<knots> d=<du knots>d= is the first-derivative (du) at each knot, same layout as a= (comma-sep knots, ;-sep drives), appended as an open-tail field. Emitted only when the trajectory has a :du component — ZOH has none, so it's omitted there (your tail treats absent = Catmull-Rom fallback ✅).

For faithful cubic, du is all you need — value + slope → Hermite. ddu is not needed for the plot (that's the smoothness-reg term; only matters for a quintic/C² render). So don't wait on it.

Your follow-up (consume side): parse d= out of the tail into PulseRecord, and swap cubicPath's Catmull-Rom tangents (p(k+1)−p(k−1))/6 for the provided d[k] (Hermite). Linear/ZOH paths unchanged.

I'm generating a real cubic solve sample (interp=cubic + d=) right now so you can build the Hermite render against actual solver derivatives, not synthetic — will point you at it shortly (~/repos/harmoniqs/plans/run-inspector-mock/).

One thing to sanity-check when you wire it: this assumes the cubic curve is cubic-Hermite in (value, du). If Piccolissimo's cubic basis actually leans on ddu for the segment shape, du-only Hermite won't be pixel-faithful and we'd add a dd= field — worth an eyeball against the solver's own dense pulse.

@jack-champagne
jack-champagne merged commit 153042c into mainJul 29, 2026
5 checks passed
@jack-champagne
jack-champagne deleted the kate/pulseplot-interp-render branch July 29, 2026 02:37
@jack-champagne

Copy link
Copy Markdown
Member

Correction on the sample I promised: bumping the integrator'sspline_order to 3 doesn't make the control cubic (bit-identical optimum → still LinearSplinePulse). So the sample at ~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is interp=linear + d=<du>, not cubic.

Still useful: the d= plumbing works end-to-end and the values are real (they're exactly the linear segment slopes, e.g. d[0] = (a[1]−a[0])/dt), so you can build + wire your Hermite render against it — flip interp=cubic on your end to exercise the curve code against real derivatives. Just don't treat it as ground-truth for cubic fidelity — a genuinely cubic pulse needs a cubic pulse constructor in the solve, which isn't wired yet (chasing that separately).

@jack-champagne

Copy link
Copy Markdown
Member

Real cubic sample is up now (thanks Jack — the fix was CubicSplinePulse(values, derivatives, times); the pulse type sets the spline order, the integrator kwarg is overridden by it).

~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is now a genuine cubic solve:

  • AMICODE_PULSE_META … interp=cubic
  • d= on all 61 lines, and the derivatives are independent now (e.g. d[0]=0, 0.0588, −0.178…) — NOT the linear slopes from before (−2.70…). So it actually curves the segments.
  • result.toml: integrator = spline_cubic, fidelity 0.99999957.

So you can build + validate the Hermite render against ground-truth cubic derivatives. ./playback_run.sh sample-run to animate it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@kateebonner@jack-champagne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META - #224

Merged
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render
Jul 29, 2026
Merged

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META#224
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

What

The Run Inspector's native pulse plot (#66) was hardwired to ZOH stairs, so a linear-spline pulse drew as wrong stairs. AMICODE_PULSE_META now carries an optional interp=zoh|linear|cubic field and the plot branches per mode: stairs (zero-order hold), polyline through linear-spline knots, smooth curve through cubic knots.

Contract decisions

  • interp= is a TRAILING optional field (after bounds=). Trailing so the opencode fork's tail-capture mirror regex (problems.ts, labels=([^\n]*)$) keeps matching without a coordinated change. Emitters: append it at the end of the line.
  • Unknown values coerce to zoh without dropping the meta. A future interp kind degrades an old client to stairs — never to NO_DATA. (Deliberate: strict-grammar fragility is what NO_DATA'd the cloud path this week.)
  • No interp=zoh: every existing run.log renders exactly as before (verified against a real pre-change run dir).

Render details

  • Path math extracted to DOM-free media/ui/components/pulsepath.ts, unit-tested: mode-correct crosshair mapping (interval midpoints under zoh, knots under splines), duration (n·dt vs (n−1)·dt), single-knot degenerate holds.
  • Cubic is knot-faithful, not coefficient-faithful — the record carries values only, so the curve is a Catmull-Rom through the true knots. Faithful cubic needs knot derivatives carried in the line format (flagged to the cloud-delivery side; offered as a format extension).
  • Warming hint no longer promises "~1–2 min": a cloud run warms ~6 min and then streams in ~15 s sidecar sync bursts; the copy now stays true for both. The render path was already burst-safe (wholesale path swap per record, 5 Hz coalescing throttle).
  • Dev harness gains interp/knot-count switches (pnpm run dev:pulseplot) for eyeballing all three modes; 12 knots makes them unmistakable.

Tested

  • 787 unit tests pass incl. 12 new (grammar: interp present/absent/unknown; geometry: all three paths, cursor mapping, durations). Typecheck + repertoire lint clean.
  • Real-pipeline eyeball: a real x-gate transmon run dir stamped interp=linear renders as a polyline in the inspector (F=0.99993 intact), interp=cubic as a smooth curve, and the untouched original as stairs — full state matrix incl. the no-pulse-data empty state.

Out of scope

Cloud delivery of AMICODE_PULSE lines (remote_executor forwarder + runner capture) — owned separately; this PR defines the contract the relay serves. Mock-replay acceptance pending the shared playback harness.

🤖 Generated with Claude Code

kateebonnerand others added 3 commits July 28, 2026 21:05
…META (#66)
AMICODE_PULSE_META gains a TRAILING optional interp= field: zoh (default),
linear, cubic. Trailing so the opencode fork's tail-capture mirror keeps
matching; unknown values coerce to zoh so a future interp kind degrades an
old client to stairs, never to NO_DATA.
The plot branches per mode: stairs (zero-order hold, as before), polyline
through linear-spline knots, Catmull-Rom curve through cubic knots. Path
math extracted to a DOM-free pulsepath.ts (unit-tested): mode-correct
crosshair mapping (interval midpoints vs knots), duration (n·dt vs
(n-1)·dt), single-knot degenerate holds.
Cubic is knot-faithful, not coefficient-faithful — the record carries no
derivatives; flagged for a line-format extension on the delivery side.
Warming hint drops its ~1–2 min promise: a cloud run warms ~6 min and then
streams in ~15 s sidecar bursts, and the copy now stays true for both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip zoh/linear/cubic live on the same synthetic knots (12 knots makes the
three modes unmistakable; 50 is fixture scale). Redraws immediately on
switch while playing. Dev-only (.vscodeignore'd).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erp=
Review pass on #224 caught this: tolerating ONLY interp= recreates the
strict-grammar fragility one field into the future — the planned cubic
derivatives extension (or any new field) would strand THIS client on
NO_DATA, the exact failure class the design notes call out. Both
AMICODE_PULSE_META and AMICODE_PULSE now accept an open tail of key=value
fields (unknown fields ignored; non-key=value tails still reject as
corruption). Replayed all 2258 records across the full local run corpus:
zero drops, legacy metas all read zoh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
ContributorAuthor

Thorough re-test pass:

  • Full suite: 788 passed (37 on the pulse grammar/geometry files, incl. 4 new future-field tests). Typecheck, webview bundle, repertoire lint all clean.
  • Real-corpus replay: every AMICODE_PULSE* line from all ~50 run dirs on a real machine through the new parser — 30 metas, 2,258 records, 0 dropped; all legacy metas correctly default to zoh.
  • Adversarial review caught one real gap, fixed in the follow-up commit: the parser tolerated only interp= as a trailing field, so the next format extension (e.g. the planned d= cubic derivatives) would have stranded this client on NO_DATA — the exact failure class this PR exists to prevent. Both pulse lines now accept an open key=value tail; genuine corruption still rejects.
  • Live verification in the extension dev host: real x-gate run stamped interp=linear renders as a polyline (F=0.99993 intact), interp=cubic as a smooth curve, untouched run as stairs, and a pulse-free run shows the no-data hint.

@jack-champagne

Copy link
Copy Markdown
Member

Locking the pulse-derivative line format so the emit + render sides match 👇

Emitter (cloud/solve output) now emits, order-agnostic:

  • AMICODE_PULSE_META … interp=zoh|linear|cubic — feeds your type-aware mode select. (Heads up: your parser coerces missing interp → zoh, so a linear/cubic pulse without this field was drawing as stairs — now it's set.)
  • AMICODE_PULSE … a=<knots> d=<du knots>d= is the first-derivative (du) at each knot, same layout as a= (comma-sep knots, ;-sep drives), appended as an open-tail field. Emitted only when the trajectory has a :du component — ZOH has none, so it's omitted there (your tail treats absent = Catmull-Rom fallback ✅).

For faithful cubic, du is all you need — value + slope → Hermite. ddu is not needed for the plot (that's the smoothness-reg term; only matters for a quintic/C² render). So don't wait on it.

Your follow-up (consume side): parse d= out of the tail into PulseRecord, and swap cubicPath's Catmull-Rom tangents (p(k+1)−p(k−1))/6 for the provided d[k] (Hermite). Linear/ZOH paths unchanged.

I'm generating a real cubic solve sample (interp=cubic + d=) right now so you can build the Hermite render against actual solver derivatives, not synthetic — will point you at it shortly (~/repos/harmoniqs/plans/run-inspector-mock/).

One thing to sanity-check when you wire it: this assumes the cubic curve is cubic-Hermite in (value, du). If Piccolissimo's cubic basis actually leans on ddu for the segment shape, du-only Hermite won't be pixel-faithful and we'd add a dd= field — worth an eyeball against the solver's own dense pulse.

@jack-champagne
jack-champagne merged commit 153042c into mainJul 29, 2026
5 checks passed
@jack-champagne
jack-champagne deleted the kate/pulseplot-interp-render branch July 29, 2026 02:37
@jack-champagne

Copy link
Copy Markdown
Member

Correction on the sample I promised: bumping the integrator'sspline_order to 3 doesn't make the control cubic (bit-identical optimum → still LinearSplinePulse). So the sample at ~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is interp=linear + d=<du>, not cubic.

Still useful: the d= plumbing works end-to-end and the values are real (they're exactly the linear segment slopes, e.g. d[0] = (a[1]−a[0])/dt), so you can build + wire your Hermite render against it — flip interp=cubic on your end to exercise the curve code against real derivatives. Just don't treat it as ground-truth for cubic fidelity — a genuinely cubic pulse needs a cubic pulse constructor in the solve, which isn't wired yet (chasing that separately).

@jack-champagne

Copy link
Copy Markdown
Member

Real cubic sample is up now (thanks Jack — the fix was CubicSplinePulse(values, derivatives, times); the pulse type sets the spline order, the integrator kwarg is overridden by it).

~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is now a genuine cubic solve:

  • AMICODE_PULSE_META … interp=cubic
  • d= on all 61 lines, and the derivatives are independent now (e.g. d[0]=0, 0.0588, −0.178…) — NOT the linear slopes from before (−2.70…). So it actually curves the segments.
  • result.toml: integrator = spline_cubic, fidelity 0.99999957.

So you can build + validate the Hermite render against ground-truth cubic derivatives. ./playback_run.sh sample-run to animate it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@kateebonner@jack-champagne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META - #224

Merged
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render
Jul 29, 2026
Merged

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META#224
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

What

The Run Inspector's native pulse plot (#66) was hardwired to ZOH stairs, so a linear-spline pulse drew as wrong stairs. AMICODE_PULSE_META now carries an optional interp=zoh|linear|cubic field and the plot branches per mode: stairs (zero-order hold), polyline through linear-spline knots, smooth curve through cubic knots.

Contract decisions

  • interp= is a TRAILING optional field (after bounds=). Trailing so the opencode fork's tail-capture mirror regex (problems.ts, labels=([^\n]*)$) keeps matching without a coordinated change. Emitters: append it at the end of the line.
  • Unknown values coerce to zoh without dropping the meta. A future interp kind degrades an old client to stairs — never to NO_DATA. (Deliberate: strict-grammar fragility is what NO_DATA'd the cloud path this week.)
  • No interp=zoh: every existing run.log renders exactly as before (verified against a real pre-change run dir).

Render details

  • Path math extracted to DOM-free media/ui/components/pulsepath.ts, unit-tested: mode-correct crosshair mapping (interval midpoints under zoh, knots under splines), duration (n·dt vs (n−1)·dt), single-knot degenerate holds.
  • Cubic is knot-faithful, not coefficient-faithful — the record carries values only, so the curve is a Catmull-Rom through the true knots. Faithful cubic needs knot derivatives carried in the line format (flagged to the cloud-delivery side; offered as a format extension).
  • Warming hint no longer promises "~1–2 min": a cloud run warms ~6 min and then streams in ~15 s sidecar sync bursts; the copy now stays true for both. The render path was already burst-safe (wholesale path swap per record, 5 Hz coalescing throttle).
  • Dev harness gains interp/knot-count switches (pnpm run dev:pulseplot) for eyeballing all three modes; 12 knots makes them unmistakable.

Tested

  • 787 unit tests pass incl. 12 new (grammar: interp present/absent/unknown; geometry: all three paths, cursor mapping, durations). Typecheck + repertoire lint clean.
  • Real-pipeline eyeball: a real x-gate transmon run dir stamped interp=linear renders as a polyline in the inspector (F=0.99993 intact), interp=cubic as a smooth curve, and the untouched original as stairs — full state matrix incl. the no-pulse-data empty state.

Out of scope

Cloud delivery of AMICODE_PULSE lines (remote_executor forwarder + runner capture) — owned separately; this PR defines the contract the relay serves. Mock-replay acceptance pending the shared playback harness.

🤖 Generated with Claude Code

kateebonnerand others added 3 commits July 28, 2026 21:05
…META (#66)
AMICODE_PULSE_META gains a TRAILING optional interp= field: zoh (default),
linear, cubic. Trailing so the opencode fork's tail-capture mirror keeps
matching; unknown values coerce to zoh so a future interp kind degrades an
old client to stairs, never to NO_DATA.
The plot branches per mode: stairs (zero-order hold, as before), polyline
through linear-spline knots, Catmull-Rom curve through cubic knots. Path
math extracted to a DOM-free pulsepath.ts (unit-tested): mode-correct
crosshair mapping (interval midpoints vs knots), duration (n·dt vs
(n-1)·dt), single-knot degenerate holds.
Cubic is knot-faithful, not coefficient-faithful — the record carries no
derivatives; flagged for a line-format extension on the delivery side.
Warming hint drops its ~1–2 min promise: a cloud run warms ~6 min and then
streams in ~15 s sidecar bursts, and the copy now stays true for both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip zoh/linear/cubic live on the same synthetic knots (12 knots makes the
three modes unmistakable; 50 is fixture scale). Redraws immediately on
switch while playing. Dev-only (.vscodeignore'd).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erp=
Review pass on #224 caught this: tolerating ONLY interp= recreates the
strict-grammar fragility one field into the future — the planned cubic
derivatives extension (or any new field) would strand THIS client on
NO_DATA, the exact failure class the design notes call out. Both
AMICODE_PULSE_META and AMICODE_PULSE now accept an open tail of key=value
fields (unknown fields ignored; non-key=value tails still reject as
corruption). Replayed all 2258 records across the full local run corpus:
zero drops, legacy metas all read zoh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
ContributorAuthor

Thorough re-test pass:

  • Full suite: 788 passed (37 on the pulse grammar/geometry files, incl. 4 new future-field tests). Typecheck, webview bundle, repertoire lint all clean.
  • Real-corpus replay: every AMICODE_PULSE* line from all ~50 run dirs on a real machine through the new parser — 30 metas, 2,258 records, 0 dropped; all legacy metas correctly default to zoh.
  • Adversarial review caught one real gap, fixed in the follow-up commit: the parser tolerated only interp= as a trailing field, so the next format extension (e.g. the planned d= cubic derivatives) would have stranded this client on NO_DATA — the exact failure class this PR exists to prevent. Both pulse lines now accept an open key=value tail; genuine corruption still rejects.
  • Live verification in the extension dev host: real x-gate run stamped interp=linear renders as a polyline (F=0.99993 intact), interp=cubic as a smooth curve, untouched run as stairs, and a pulse-free run shows the no-data hint.

@jack-champagne

Copy link
Copy Markdown
Member

Locking the pulse-derivative line format so the emit + render sides match 👇

Emitter (cloud/solve output) now emits, order-agnostic:

  • AMICODE_PULSE_META … interp=zoh|linear|cubic — feeds your type-aware mode select. (Heads up: your parser coerces missing interp → zoh, so a linear/cubic pulse without this field was drawing as stairs — now it's set.)
  • AMICODE_PULSE … a=<knots> d=<du knots>d= is the first-derivative (du) at each knot, same layout as a= (comma-sep knots, ;-sep drives), appended as an open-tail field. Emitted only when the trajectory has a :du component — ZOH has none, so it's omitted there (your tail treats absent = Catmull-Rom fallback ✅).

For faithful cubic, du is all you need — value + slope → Hermite. ddu is not needed for the plot (that's the smoothness-reg term; only matters for a quintic/C² render). So don't wait on it.

Your follow-up (consume side): parse d= out of the tail into PulseRecord, and swap cubicPath's Catmull-Rom tangents (p(k+1)−p(k−1))/6 for the provided d[k] (Hermite). Linear/ZOH paths unchanged.

I'm generating a real cubic solve sample (interp=cubic + d=) right now so you can build the Hermite render against actual solver derivatives, not synthetic — will point you at it shortly (~/repos/harmoniqs/plans/run-inspector-mock/).

One thing to sanity-check when you wire it: this assumes the cubic curve is cubic-Hermite in (value, du). If Piccolissimo's cubic basis actually leans on ddu for the segment shape, du-only Hermite won't be pixel-faithful and we'd add a dd= field — worth an eyeball against the solver's own dense pulse.

@jack-champagne
jack-champagne merged commit 153042c into mainJul 29, 2026
5 checks passed
@jack-champagne
jack-champagne deleted the kate/pulseplot-interp-render branch July 29, 2026 02:37
@jack-champagne

Copy link
Copy Markdown
Member

Correction on the sample I promised: bumping the integrator'sspline_order to 3 doesn't make the control cubic (bit-identical optimum → still LinearSplinePulse). So the sample at ~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is interp=linear + d=<du>, not cubic.

Still useful: the d= plumbing works end-to-end and the values are real (they're exactly the linear segment slopes, e.g. d[0] = (a[1]−a[0])/dt), so you can build + wire your Hermite render against it — flip interp=cubic on your end to exercise the curve code against real derivatives. Just don't treat it as ground-truth for cubic fidelity — a genuinely cubic pulse needs a cubic pulse constructor in the solve, which isn't wired yet (chasing that separately).

@jack-champagne

Copy link
Copy Markdown
Member

Real cubic sample is up now (thanks Jack — the fix was CubicSplinePulse(values, derivatives, times); the pulse type sets the spline order, the integrator kwarg is overridden by it).

~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is now a genuine cubic solve:

  • AMICODE_PULSE_META … interp=cubic
  • d= on all 61 lines, and the derivatives are independent now (e.g. d[0]=0, 0.0588, −0.178…) — NOT the linear slopes from before (−2.70…). So it actually curves the segments.
  • result.toml: integrator = spline_cubic, fidelity 0.99999957.

So you can build + validate the Hermite render against ground-truth cubic derivatives. ./playback_run.sh sample-run to animate it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@kateebonner@jack-champagne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META - #224

Merged
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render
Jul 29, 2026
Merged

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META#224
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

What

The Run Inspector's native pulse plot (#66) was hardwired to ZOH stairs, so a linear-spline pulse drew as wrong stairs. AMICODE_PULSE_META now carries an optional interp=zoh|linear|cubic field and the plot branches per mode: stairs (zero-order hold), polyline through linear-spline knots, smooth curve through cubic knots.

Contract decisions

  • interp= is a TRAILING optional field (after bounds=). Trailing so the opencode fork's tail-capture mirror regex (problems.ts, labels=([^\n]*)$) keeps matching without a coordinated change. Emitters: append it at the end of the line.
  • Unknown values coerce to zoh without dropping the meta. A future interp kind degrades an old client to stairs — never to NO_DATA. (Deliberate: strict-grammar fragility is what NO_DATA'd the cloud path this week.)
  • No interp=zoh: every existing run.log renders exactly as before (verified against a real pre-change run dir).

Render details

  • Path math extracted to DOM-free media/ui/components/pulsepath.ts, unit-tested: mode-correct crosshair mapping (interval midpoints under zoh, knots under splines), duration (n·dt vs (n−1)·dt), single-knot degenerate holds.
  • Cubic is knot-faithful, not coefficient-faithful — the record carries values only, so the curve is a Catmull-Rom through the true knots. Faithful cubic needs knot derivatives carried in the line format (flagged to the cloud-delivery side; offered as a format extension).
  • Warming hint no longer promises "~1–2 min": a cloud run warms ~6 min and then streams in ~15 s sidecar sync bursts; the copy now stays true for both. The render path was already burst-safe (wholesale path swap per record, 5 Hz coalescing throttle).
  • Dev harness gains interp/knot-count switches (pnpm run dev:pulseplot) for eyeballing all three modes; 12 knots makes them unmistakable.

Tested

  • 787 unit tests pass incl. 12 new (grammar: interp present/absent/unknown; geometry: all three paths, cursor mapping, durations). Typecheck + repertoire lint clean.
  • Real-pipeline eyeball: a real x-gate transmon run dir stamped interp=linear renders as a polyline in the inspector (F=0.99993 intact), interp=cubic as a smooth curve, and the untouched original as stairs — full state matrix incl. the no-pulse-data empty state.

Out of scope

Cloud delivery of AMICODE_PULSE lines (remote_executor forwarder + runner capture) — owned separately; this PR defines the contract the relay serves. Mock-replay acceptance pending the shared playback harness.

🤖 Generated with Claude Code

kateebonnerand others added 3 commits July 28, 2026 21:05
…META (#66)
AMICODE_PULSE_META gains a TRAILING optional interp= field: zoh (default),
linear, cubic. Trailing so the opencode fork's tail-capture mirror keeps
matching; unknown values coerce to zoh so a future interp kind degrades an
old client to stairs, never to NO_DATA.
The plot branches per mode: stairs (zero-order hold, as before), polyline
through linear-spline knots, Catmull-Rom curve through cubic knots. Path
math extracted to a DOM-free pulsepath.ts (unit-tested): mode-correct
crosshair mapping (interval midpoints vs knots), duration (n·dt vs
(n-1)·dt), single-knot degenerate holds.
Cubic is knot-faithful, not coefficient-faithful — the record carries no
derivatives; flagged for a line-format extension on the delivery side.
Warming hint drops its ~1–2 min promise: a cloud run warms ~6 min and then
streams in ~15 s sidecar bursts, and the copy now stays true for both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip zoh/linear/cubic live on the same synthetic knots (12 knots makes the
three modes unmistakable; 50 is fixture scale). Redraws immediately on
switch while playing. Dev-only (.vscodeignore'd).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erp=
Review pass on #224 caught this: tolerating ONLY interp= recreates the
strict-grammar fragility one field into the future — the planned cubic
derivatives extension (or any new field) would strand THIS client on
NO_DATA, the exact failure class the design notes call out. Both
AMICODE_PULSE_META and AMICODE_PULSE now accept an open tail of key=value
fields (unknown fields ignored; non-key=value tails still reject as
corruption). Replayed all 2258 records across the full local run corpus:
zero drops, legacy metas all read zoh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
ContributorAuthor

Thorough re-test pass:

  • Full suite: 788 passed (37 on the pulse grammar/geometry files, incl. 4 new future-field tests). Typecheck, webview bundle, repertoire lint all clean.
  • Real-corpus replay: every AMICODE_PULSE* line from all ~50 run dirs on a real machine through the new parser — 30 metas, 2,258 records, 0 dropped; all legacy metas correctly default to zoh.
  • Adversarial review caught one real gap, fixed in the follow-up commit: the parser tolerated only interp= as a trailing field, so the next format extension (e.g. the planned d= cubic derivatives) would have stranded this client on NO_DATA — the exact failure class this PR exists to prevent. Both pulse lines now accept an open key=value tail; genuine corruption still rejects.
  • Live verification in the extension dev host: real x-gate run stamped interp=linear renders as a polyline (F=0.99993 intact), interp=cubic as a smooth curve, untouched run as stairs, and a pulse-free run shows the no-data hint.

@jack-champagne

Copy link
Copy Markdown
Member

Locking the pulse-derivative line format so the emit + render sides match 👇

Emitter (cloud/solve output) now emits, order-agnostic:

  • AMICODE_PULSE_META … interp=zoh|linear|cubic — feeds your type-aware mode select. (Heads up: your parser coerces missing interp → zoh, so a linear/cubic pulse without this field was drawing as stairs — now it's set.)
  • AMICODE_PULSE … a=<knots> d=<du knots>d= is the first-derivative (du) at each knot, same layout as a= (comma-sep knots, ;-sep drives), appended as an open-tail field. Emitted only when the trajectory has a :du component — ZOH has none, so it's omitted there (your tail treats absent = Catmull-Rom fallback ✅).

For faithful cubic, du is all you need — value + slope → Hermite. ddu is not needed for the plot (that's the smoothness-reg term; only matters for a quintic/C² render). So don't wait on it.

Your follow-up (consume side): parse d= out of the tail into PulseRecord, and swap cubicPath's Catmull-Rom tangents (p(k+1)−p(k−1))/6 for the provided d[k] (Hermite). Linear/ZOH paths unchanged.

I'm generating a real cubic solve sample (interp=cubic + d=) right now so you can build the Hermite render against actual solver derivatives, not synthetic — will point you at it shortly (~/repos/harmoniqs/plans/run-inspector-mock/).

One thing to sanity-check when you wire it: this assumes the cubic curve is cubic-Hermite in (value, du). If Piccolissimo's cubic basis actually leans on ddu for the segment shape, du-only Hermite won't be pixel-faithful and we'd add a dd= field — worth an eyeball against the solver's own dense pulse.

@jack-champagne
jack-champagne merged commit 153042c into mainJul 29, 2026
5 checks passed
@jack-champagne
jack-champagne deleted the kate/pulseplot-interp-render branch July 29, 2026 02:37
@jack-champagne

Copy link
Copy Markdown
Member

Correction on the sample I promised: bumping the integrator'sspline_order to 3 doesn't make the control cubic (bit-identical optimum → still LinearSplinePulse). So the sample at ~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is interp=linear + d=<du>, not cubic.

Still useful: the d= plumbing works end-to-end and the values are real (they're exactly the linear segment slopes, e.g. d[0] = (a[1]−a[0])/dt), so you can build + wire your Hermite render against it — flip interp=cubic on your end to exercise the curve code against real derivatives. Just don't treat it as ground-truth for cubic fidelity — a genuinely cubic pulse needs a cubic pulse constructor in the solve, which isn't wired yet (chasing that separately).

@jack-champagne

Copy link
Copy Markdown
Member

Real cubic sample is up now (thanks Jack — the fix was CubicSplinePulse(values, derivatives, times); the pulse type sets the spline order, the integrator kwarg is overridden by it).

~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is now a genuine cubic solve:

  • AMICODE_PULSE_META … interp=cubic
  • d= on all 61 lines, and the derivatives are independent now (e.g. d[0]=0, 0.0588, −0.178…) — NOT the linear slopes from before (−2.70…). So it actually curves the segments.
  • result.toml: integrator = spline_cubic, fidelity 0.99999957.

So you can build + validate the Hermite render against ground-truth cubic derivatives. ./playback_run.sh sample-run to animate it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@kateebonner@jack-champagne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META - #224

Merged
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render
Jul 29, 2026
Merged

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META#224
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

What

The Run Inspector's native pulse plot (#66) was hardwired to ZOH stairs, so a linear-spline pulse drew as wrong stairs. AMICODE_PULSE_META now carries an optional interp=zoh|linear|cubic field and the plot branches per mode: stairs (zero-order hold), polyline through linear-spline knots, smooth curve through cubic knots.

Contract decisions

  • interp= is a TRAILING optional field (after bounds=). Trailing so the opencode fork's tail-capture mirror regex (problems.ts, labels=([^\n]*)$) keeps matching without a coordinated change. Emitters: append it at the end of the line.
  • Unknown values coerce to zoh without dropping the meta. A future interp kind degrades an old client to stairs — never to NO_DATA. (Deliberate: strict-grammar fragility is what NO_DATA'd the cloud path this week.)
  • No interp=zoh: every existing run.log renders exactly as before (verified against a real pre-change run dir).

Render details

  • Path math extracted to DOM-free media/ui/components/pulsepath.ts, unit-tested: mode-correct crosshair mapping (interval midpoints under zoh, knots under splines), duration (n·dt vs (n−1)·dt), single-knot degenerate holds.
  • Cubic is knot-faithful, not coefficient-faithful — the record carries values only, so the curve is a Catmull-Rom through the true knots. Faithful cubic needs knot derivatives carried in the line format (flagged to the cloud-delivery side; offered as a format extension).
  • Warming hint no longer promises "~1–2 min": a cloud run warms ~6 min and then streams in ~15 s sidecar sync bursts; the copy now stays true for both. The render path was already burst-safe (wholesale path swap per record, 5 Hz coalescing throttle).
  • Dev harness gains interp/knot-count switches (pnpm run dev:pulseplot) for eyeballing all three modes; 12 knots makes them unmistakable.

Tested

  • 787 unit tests pass incl. 12 new (grammar: interp present/absent/unknown; geometry: all three paths, cursor mapping, durations). Typecheck + repertoire lint clean.
  • Real-pipeline eyeball: a real x-gate transmon run dir stamped interp=linear renders as a polyline in the inspector (F=0.99993 intact), interp=cubic as a smooth curve, and the untouched original as stairs — full state matrix incl. the no-pulse-data empty state.

Out of scope

Cloud delivery of AMICODE_PULSE lines (remote_executor forwarder + runner capture) — owned separately; this PR defines the contract the relay serves. Mock-replay acceptance pending the shared playback harness.

🤖 Generated with Claude Code

kateebonnerand others added 3 commits July 28, 2026 21:05
…META (#66)
AMICODE_PULSE_META gains a TRAILING optional interp= field: zoh (default),
linear, cubic. Trailing so the opencode fork's tail-capture mirror keeps
matching; unknown values coerce to zoh so a future interp kind degrades an
old client to stairs, never to NO_DATA.
The plot branches per mode: stairs (zero-order hold, as before), polyline
through linear-spline knots, Catmull-Rom curve through cubic knots. Path
math extracted to a DOM-free pulsepath.ts (unit-tested): mode-correct
crosshair mapping (interval midpoints vs knots), duration (n·dt vs
(n-1)·dt), single-knot degenerate holds.
Cubic is knot-faithful, not coefficient-faithful — the record carries no
derivatives; flagged for a line-format extension on the delivery side.
Warming hint drops its ~1–2 min promise: a cloud run warms ~6 min and then
streams in ~15 s sidecar bursts, and the copy now stays true for both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip zoh/linear/cubic live on the same synthetic knots (12 knots makes the
three modes unmistakable; 50 is fixture scale). Redraws immediately on
switch while playing. Dev-only (.vscodeignore'd).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erp=
Review pass on #224 caught this: tolerating ONLY interp= recreates the
strict-grammar fragility one field into the future — the planned cubic
derivatives extension (or any new field) would strand THIS client on
NO_DATA, the exact failure class the design notes call out. Both
AMICODE_PULSE_META and AMICODE_PULSE now accept an open tail of key=value
fields (unknown fields ignored; non-key=value tails still reject as
corruption). Replayed all 2258 records across the full local run corpus:
zero drops, legacy metas all read zoh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
ContributorAuthor

Thorough re-test pass:

  • Full suite: 788 passed (37 on the pulse grammar/geometry files, incl. 4 new future-field tests). Typecheck, webview bundle, repertoire lint all clean.
  • Real-corpus replay: every AMICODE_PULSE* line from all ~50 run dirs on a real machine through the new parser — 30 metas, 2,258 records, 0 dropped; all legacy metas correctly default to zoh.
  • Adversarial review caught one real gap, fixed in the follow-up commit: the parser tolerated only interp= as a trailing field, so the next format extension (e.g. the planned d= cubic derivatives) would have stranded this client on NO_DATA — the exact failure class this PR exists to prevent. Both pulse lines now accept an open key=value tail; genuine corruption still rejects.
  • Live verification in the extension dev host: real x-gate run stamped interp=linear renders as a polyline (F=0.99993 intact), interp=cubic as a smooth curve, untouched run as stairs, and a pulse-free run shows the no-data hint.

@jack-champagne

Copy link
Copy Markdown
Member

Locking the pulse-derivative line format so the emit + render sides match 👇

Emitter (cloud/solve output) now emits, order-agnostic:

  • AMICODE_PULSE_META … interp=zoh|linear|cubic — feeds your type-aware mode select. (Heads up: your parser coerces missing interp → zoh, so a linear/cubic pulse without this field was drawing as stairs — now it's set.)
  • AMICODE_PULSE … a=<knots> d=<du knots>d= is the first-derivative (du) at each knot, same layout as a= (comma-sep knots, ;-sep drives), appended as an open-tail field. Emitted only when the trajectory has a :du component — ZOH has none, so it's omitted there (your tail treats absent = Catmull-Rom fallback ✅).

For faithful cubic, du is all you need — value + slope → Hermite. ddu is not needed for the plot (that's the smoothness-reg term; only matters for a quintic/C² render). So don't wait on it.

Your follow-up (consume side): parse d= out of the tail into PulseRecord, and swap cubicPath's Catmull-Rom tangents (p(k+1)−p(k−1))/6 for the provided d[k] (Hermite). Linear/ZOH paths unchanged.

I'm generating a real cubic solve sample (interp=cubic + d=) right now so you can build the Hermite render against actual solver derivatives, not synthetic — will point you at it shortly (~/repos/harmoniqs/plans/run-inspector-mock/).

One thing to sanity-check when you wire it: this assumes the cubic curve is cubic-Hermite in (value, du). If Piccolissimo's cubic basis actually leans on ddu for the segment shape, du-only Hermite won't be pixel-faithful and we'd add a dd= field — worth an eyeball against the solver's own dense pulse.

@jack-champagne
jack-champagne merged commit 153042c into mainJul 29, 2026
5 checks passed
@jack-champagne
jack-champagne deleted the kate/pulseplot-interp-render branch July 29, 2026 02:37
@jack-champagne

Copy link
Copy Markdown
Member

Correction on the sample I promised: bumping the integrator'sspline_order to 3 doesn't make the control cubic (bit-identical optimum → still LinearSplinePulse). So the sample at ~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is interp=linear + d=<du>, not cubic.

Still useful: the d= plumbing works end-to-end and the values are real (they're exactly the linear segment slopes, e.g. d[0] = (a[1]−a[0])/dt), so you can build + wire your Hermite render against it — flip interp=cubic on your end to exercise the curve code against real derivatives. Just don't treat it as ground-truth for cubic fidelity — a genuinely cubic pulse needs a cubic pulse constructor in the solve, which isn't wired yet (chasing that separately).

@jack-champagne

Copy link
Copy Markdown
Member

Real cubic sample is up now (thanks Jack — the fix was CubicSplinePulse(values, derivatives, times); the pulse type sets the spline order, the integrator kwarg is overridden by it).

~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is now a genuine cubic solve:

  • AMICODE_PULSE_META … interp=cubic
  • d= on all 61 lines, and the derivatives are independent now (e.g. d[0]=0, 0.0588, −0.178…) — NOT the linear slopes from before (−2.70…). So it actually curves the segments.
  • result.toml: integrator = spline_cubic, fidelity 0.99999957.

So you can build + validate the Hermite render against ground-truth cubic derivatives. ./playback_run.sh sample-run to animate it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@kateebonner@jack-champagne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META - #224

Merged
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render
Jul 29, 2026
Merged

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META#224
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

What

The Run Inspector's native pulse plot (#66) was hardwired to ZOH stairs, so a linear-spline pulse drew as wrong stairs. AMICODE_PULSE_META now carries an optional interp=zoh|linear|cubic field and the plot branches per mode: stairs (zero-order hold), polyline through linear-spline knots, smooth curve through cubic knots.

Contract decisions

  • interp= is a TRAILING optional field (after bounds=). Trailing so the opencode fork's tail-capture mirror regex (problems.ts, labels=([^\n]*)$) keeps matching without a coordinated change. Emitters: append it at the end of the line.
  • Unknown values coerce to zoh without dropping the meta. A future interp kind degrades an old client to stairs — never to NO_DATA. (Deliberate: strict-grammar fragility is what NO_DATA'd the cloud path this week.)
  • No interp=zoh: every existing run.log renders exactly as before (verified against a real pre-change run dir).

Render details

  • Path math extracted to DOM-free media/ui/components/pulsepath.ts, unit-tested: mode-correct crosshair mapping (interval midpoints under zoh, knots under splines), duration (n·dt vs (n−1)·dt), single-knot degenerate holds.
  • Cubic is knot-faithful, not coefficient-faithful — the record carries values only, so the curve is a Catmull-Rom through the true knots. Faithful cubic needs knot derivatives carried in the line format (flagged to the cloud-delivery side; offered as a format extension).
  • Warming hint no longer promises "~1–2 min": a cloud run warms ~6 min and then streams in ~15 s sidecar sync bursts; the copy now stays true for both. The render path was already burst-safe (wholesale path swap per record, 5 Hz coalescing throttle).
  • Dev harness gains interp/knot-count switches (pnpm run dev:pulseplot) for eyeballing all three modes; 12 knots makes them unmistakable.

Tested

  • 787 unit tests pass incl. 12 new (grammar: interp present/absent/unknown; geometry: all three paths, cursor mapping, durations). Typecheck + repertoire lint clean.
  • Real-pipeline eyeball: a real x-gate transmon run dir stamped interp=linear renders as a polyline in the inspector (F=0.99993 intact), interp=cubic as a smooth curve, and the untouched original as stairs — full state matrix incl. the no-pulse-data empty state.

Out of scope

Cloud delivery of AMICODE_PULSE lines (remote_executor forwarder + runner capture) — owned separately; this PR defines the contract the relay serves. Mock-replay acceptance pending the shared playback harness.

🤖 Generated with Claude Code

kateebonnerand others added 3 commits July 28, 2026 21:05
…META (#66)
AMICODE_PULSE_META gains a TRAILING optional interp= field: zoh (default),
linear, cubic. Trailing so the opencode fork's tail-capture mirror keeps
matching; unknown values coerce to zoh so a future interp kind degrades an
old client to stairs, never to NO_DATA.
The plot branches per mode: stairs (zero-order hold, as before), polyline
through linear-spline knots, Catmull-Rom curve through cubic knots. Path
math extracted to a DOM-free pulsepath.ts (unit-tested): mode-correct
crosshair mapping (interval midpoints vs knots), duration (n·dt vs
(n-1)·dt), single-knot degenerate holds.
Cubic is knot-faithful, not coefficient-faithful — the record carries no
derivatives; flagged for a line-format extension on the delivery side.
Warming hint drops its ~1–2 min promise: a cloud run warms ~6 min and then
streams in ~15 s sidecar bursts, and the copy now stays true for both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip zoh/linear/cubic live on the same synthetic knots (12 knots makes the
three modes unmistakable; 50 is fixture scale). Redraws immediately on
switch while playing. Dev-only (.vscodeignore'd).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erp=
Review pass on #224 caught this: tolerating ONLY interp= recreates the
strict-grammar fragility one field into the future — the planned cubic
derivatives extension (or any new field) would strand THIS client on
NO_DATA, the exact failure class the design notes call out. Both
AMICODE_PULSE_META and AMICODE_PULSE now accept an open tail of key=value
fields (unknown fields ignored; non-key=value tails still reject as
corruption). Replayed all 2258 records across the full local run corpus:
zero drops, legacy metas all read zoh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
ContributorAuthor

Thorough re-test pass:

  • Full suite: 788 passed (37 on the pulse grammar/geometry files, incl. 4 new future-field tests). Typecheck, webview bundle, repertoire lint all clean.
  • Real-corpus replay: every AMICODE_PULSE* line from all ~50 run dirs on a real machine through the new parser — 30 metas, 2,258 records, 0 dropped; all legacy metas correctly default to zoh.
  • Adversarial review caught one real gap, fixed in the follow-up commit: the parser tolerated only interp= as a trailing field, so the next format extension (e.g. the planned d= cubic derivatives) would have stranded this client on NO_DATA — the exact failure class this PR exists to prevent. Both pulse lines now accept an open key=value tail; genuine corruption still rejects.
  • Live verification in the extension dev host: real x-gate run stamped interp=linear renders as a polyline (F=0.99993 intact), interp=cubic as a smooth curve, untouched run as stairs, and a pulse-free run shows the no-data hint.

@jack-champagne

Copy link
Copy Markdown
Member

Locking the pulse-derivative line format so the emit + render sides match 👇

Emitter (cloud/solve output) now emits, order-agnostic:

  • AMICODE_PULSE_META … interp=zoh|linear|cubic — feeds your type-aware mode select. (Heads up: your parser coerces missing interp → zoh, so a linear/cubic pulse without this field was drawing as stairs — now it's set.)
  • AMICODE_PULSE … a=<knots> d=<du knots>d= is the first-derivative (du) at each knot, same layout as a= (comma-sep knots, ;-sep drives), appended as an open-tail field. Emitted only when the trajectory has a :du component — ZOH has none, so it's omitted there (your tail treats absent = Catmull-Rom fallback ✅).

For faithful cubic, du is all you need — value + slope → Hermite. ddu is not needed for the plot (that's the smoothness-reg term; only matters for a quintic/C² render). So don't wait on it.

Your follow-up (consume side): parse d= out of the tail into PulseRecord, and swap cubicPath's Catmull-Rom tangents (p(k+1)−p(k−1))/6 for the provided d[k] (Hermite). Linear/ZOH paths unchanged.

I'm generating a real cubic solve sample (interp=cubic + d=) right now so you can build the Hermite render against actual solver derivatives, not synthetic — will point you at it shortly (~/repos/harmoniqs/plans/run-inspector-mock/).

One thing to sanity-check when you wire it: this assumes the cubic curve is cubic-Hermite in (value, du). If Piccolissimo's cubic basis actually leans on ddu for the segment shape, du-only Hermite won't be pixel-faithful and we'd add a dd= field — worth an eyeball against the solver's own dense pulse.

@jack-champagne
jack-champagne merged commit 153042c into mainJul 29, 2026
5 checks passed
@jack-champagne
jack-champagne deleted the kate/pulseplot-interp-render branch July 29, 2026 02:37
@jack-champagne

Copy link
Copy Markdown
Member

Correction on the sample I promised: bumping the integrator'sspline_order to 3 doesn't make the control cubic (bit-identical optimum → still LinearSplinePulse). So the sample at ~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is interp=linear + d=<du>, not cubic.

Still useful: the d= plumbing works end-to-end and the values are real (they're exactly the linear segment slopes, e.g. d[0] = (a[1]−a[0])/dt), so you can build + wire your Hermite render against it — flip interp=cubic on your end to exercise the curve code against real derivatives. Just don't treat it as ground-truth for cubic fidelity — a genuinely cubic pulse needs a cubic pulse constructor in the solve, which isn't wired yet (chasing that separately).

@jack-champagne

Copy link
Copy Markdown
Member

Real cubic sample is up now (thanks Jack — the fix was CubicSplinePulse(values, derivatives, times); the pulse type sets the spline order, the integrator kwarg is overridden by it).

~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is now a genuine cubic solve:

  • AMICODE_PULSE_META … interp=cubic
  • d= on all 61 lines, and the derivatives are independent now (e.g. d[0]=0, 0.0588, −0.178…) — NOT the linear slopes from before (−2.70…). So it actually curves the segments.
  • result.toml: integrator = spline_cubic, fidelity 0.99999957.

So you can build + validate the Hermite render against ground-truth cubic derivatives. ./playback_run.sh sample-run to animate it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@kateebonner@jack-champagne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META - #224

Merged
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render
Jul 29, 2026
Merged

feat(extension): type-aware pulse plot — interp=zoh|linear|cubic in AMICODE_PULSE_META#224
jack-champagne merged 3 commits into
mainfrom
kate/pulseplot-interp-render

Conversation

@kateebonner

Copy link
Copy Markdown
Contributor

What

The Run Inspector's native pulse plot (#66) was hardwired to ZOH stairs, so a linear-spline pulse drew as wrong stairs. AMICODE_PULSE_META now carries an optional interp=zoh|linear|cubic field and the plot branches per mode: stairs (zero-order hold), polyline through linear-spline knots, smooth curve through cubic knots.

Contract decisions

  • interp= is a TRAILING optional field (after bounds=). Trailing so the opencode fork's tail-capture mirror regex (problems.ts, labels=([^\n]*)$) keeps matching without a coordinated change. Emitters: append it at the end of the line.
  • Unknown values coerce to zoh without dropping the meta. A future interp kind degrades an old client to stairs — never to NO_DATA. (Deliberate: strict-grammar fragility is what NO_DATA'd the cloud path this week.)
  • No interp=zoh: every existing run.log renders exactly as before (verified against a real pre-change run dir).

Render details

  • Path math extracted to DOM-free media/ui/components/pulsepath.ts, unit-tested: mode-correct crosshair mapping (interval midpoints under zoh, knots under splines), duration (n·dt vs (n−1)·dt), single-knot degenerate holds.
  • Cubic is knot-faithful, not coefficient-faithful — the record carries values only, so the curve is a Catmull-Rom through the true knots. Faithful cubic needs knot derivatives carried in the line format (flagged to the cloud-delivery side; offered as a format extension).
  • Warming hint no longer promises "~1–2 min": a cloud run warms ~6 min and then streams in ~15 s sidecar sync bursts; the copy now stays true for both. The render path was already burst-safe (wholesale path swap per record, 5 Hz coalescing throttle).
  • Dev harness gains interp/knot-count switches (pnpm run dev:pulseplot) for eyeballing all three modes; 12 knots makes them unmistakable.

Tested

  • 787 unit tests pass incl. 12 new (grammar: interp present/absent/unknown; geometry: all three paths, cursor mapping, durations). Typecheck + repertoire lint clean.
  • Real-pipeline eyeball: a real x-gate transmon run dir stamped interp=linear renders as a polyline in the inspector (F=0.99993 intact), interp=cubic as a smooth curve, and the untouched original as stairs — full state matrix incl. the no-pulse-data empty state.

Out of scope

Cloud delivery of AMICODE_PULSE lines (remote_executor forwarder + runner capture) — owned separately; this PR defines the contract the relay serves. Mock-replay acceptance pending the shared playback harness.

🤖 Generated with Claude Code

kateebonnerand others added 3 commits July 28, 2026 21:05
…META (#66)
AMICODE_PULSE_META gains a TRAILING optional interp= field: zoh (default),
linear, cubic. Trailing so the opencode fork's tail-capture mirror keeps
matching; unknown values coerce to zoh so a future interp kind degrades an
old client to stairs, never to NO_DATA.
The plot branches per mode: stairs (zero-order hold, as before), polyline
through linear-spline knots, Catmull-Rom curve through cubic knots. Path
math extracted to a DOM-free pulsepath.ts (unit-tested): mode-correct
crosshair mapping (interval midpoints vs knots), duration (n·dt vs
(n-1)·dt), single-knot degenerate holds.
Cubic is knot-faithful, not coefficient-faithful — the record carries no
derivatives; flagged for a line-format extension on the delivery side.
Warming hint drops its ~1–2 min promise: a cloud run warms ~6 min and then
streams in ~15 s sidecar bursts, and the copy now stays true for both.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip zoh/linear/cubic live on the same synthetic knots (12 knots makes the
three modes unmistakable; 50 is fixture scale). Redraws immediately on
switch while playing. Dev-only (.vscodeignore'd).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erp=
Review pass on #224 caught this: tolerating ONLY interp= recreates the
strict-grammar fragility one field into the future — the planned cubic
derivatives extension (or any new field) would strand THIS client on
NO_DATA, the exact failure class the design notes call out. Both
AMICODE_PULSE_META and AMICODE_PULSE now accept an open tail of key=value
fields (unknown fields ignored; non-key=value tails still reject as
corruption). Replayed all 2258 records across the full local run corpus:
zero drops, legacy metas all read zoh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kateebonner

Copy link
Copy Markdown
ContributorAuthor

Thorough re-test pass:

  • Full suite: 788 passed (37 on the pulse grammar/geometry files, incl. 4 new future-field tests). Typecheck, webview bundle, repertoire lint all clean.
  • Real-corpus replay: every AMICODE_PULSE* line from all ~50 run dirs on a real machine through the new parser — 30 metas, 2,258 records, 0 dropped; all legacy metas correctly default to zoh.
  • Adversarial review caught one real gap, fixed in the follow-up commit: the parser tolerated only interp= as a trailing field, so the next format extension (e.g. the planned d= cubic derivatives) would have stranded this client on NO_DATA — the exact failure class this PR exists to prevent. Both pulse lines now accept an open key=value tail; genuine corruption still rejects.
  • Live verification in the extension dev host: real x-gate run stamped interp=linear renders as a polyline (F=0.99993 intact), interp=cubic as a smooth curve, untouched run as stairs, and a pulse-free run shows the no-data hint.

@jack-champagne

Copy link
Copy Markdown
Member

Locking the pulse-derivative line format so the emit + render sides match 👇

Emitter (cloud/solve output) now emits, order-agnostic:

  • AMICODE_PULSE_META … interp=zoh|linear|cubic — feeds your type-aware mode select. (Heads up: your parser coerces missing interp → zoh, so a linear/cubic pulse without this field was drawing as stairs — now it's set.)
  • AMICODE_PULSE … a=<knots> d=<du knots>d= is the first-derivative (du) at each knot, same layout as a= (comma-sep knots, ;-sep drives), appended as an open-tail field. Emitted only when the trajectory has a :du component — ZOH has none, so it's omitted there (your tail treats absent = Catmull-Rom fallback ✅).

For faithful cubic, du is all you need — value + slope → Hermite. ddu is not needed for the plot (that's the smoothness-reg term; only matters for a quintic/C² render). So don't wait on it.

Your follow-up (consume side): parse d= out of the tail into PulseRecord, and swap cubicPath's Catmull-Rom tangents (p(k+1)−p(k−1))/6 for the provided d[k] (Hermite). Linear/ZOH paths unchanged.

I'm generating a real cubic solve sample (interp=cubic + d=) right now so you can build the Hermite render against actual solver derivatives, not synthetic — will point you at it shortly (~/repos/harmoniqs/plans/run-inspector-mock/).

One thing to sanity-check when you wire it: this assumes the cubic curve is cubic-Hermite in (value, du). If Piccolissimo's cubic basis actually leans on ddu for the segment shape, du-only Hermite won't be pixel-faithful and we'd add a dd= field — worth an eyeball against the solver's own dense pulse.

@jack-champagne
jack-champagne merged commit 153042c into mainJul 29, 2026
5 checks passed
@jack-champagne
jack-champagne deleted the kate/pulseplot-interp-render branch July 29, 2026 02:37
@jack-champagne

Copy link
Copy Markdown
Member

Correction on the sample I promised: bumping the integrator'sspline_order to 3 doesn't make the control cubic (bit-identical optimum → still LinearSplinePulse). So the sample at ~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is interp=linear + d=<du>, not cubic.

Still useful: the d= plumbing works end-to-end and the values are real (they're exactly the linear segment slopes, e.g. d[0] = (a[1]−a[0])/dt), so you can build + wire your Hermite render against it — flip interp=cubic on your end to exercise the curve code against real derivatives. Just don't treat it as ground-truth for cubic fidelity — a genuinely cubic pulse needs a cubic pulse constructor in the solve, which isn't wired yet (chasing that separately).

@jack-champagne

Copy link
Copy Markdown
Member

Real cubic sample is up now (thanks Jack — the fix was CubicSplinePulse(values, derivatives, times); the pulse type sets the spline order, the integrator kwarg is overridden by it).

~/repos/harmoniqs/plans/run-inspector-mock/sample-run/ is now a genuine cubic solve:

  • AMICODE_PULSE_META … interp=cubic
  • d= on all 61 lines, and the derivatives are independent now (e.g. d[0]=0, 0.0588, −0.178…) — NOT the linear slopes from before (−2.70…). So it actually curves the segments.
  • result.toml: integrator = spline_cubic, fidelity 0.99999957.

So you can build + validate the Hermite render against ground-truth cubic derivatives. ./playback_run.sh sample-run to animate it.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@kateebonner@jack-champagne