Skip to content

Sequence diagram primary + directional transfer chord + compose parity - #295

Closed
pedapudi wants to merge 13 commits into
mainfrom
design/sequence-compose-parity
Closed

pedapudi wants to merge 13 commits into
mainfrom
design/sequence-compose-parity

Conversation

@pedapudi

Copy link
Copy Markdown
Owner

Redraws the harmonograf console's sequence story in the zicato design language with the corrected framing: the sequence view shows how the agents interact — who said what to whom, when.

Task 1 — vertical sequence diagram is now the recommended view

sequence.html is restructured so Option B (the vertical staff) is the RECOMMENDED, primary view, redrawn to read unambiguously as an agent-interaction sequence diagram: one vertical lifeline per agent (neutral, with a name chip), time flowing down, each message a horizontal connector from sender to receiver with an explicit arrowhead — solid for a call, dashed for a return — and the label above in the KIND hue. Activations hug the lifelines; the delegation opens a child band on the tester lifeline and returns; the approval is a gate; the failed check is the only red. Messages use a minimum-row-gap layout so a near-coincident call+return never overprint. The horizontal score becomes the explicit Gantt-companion alternative; the UML frame stays as the before.

Task 2 — directional transfer chord

Figure 11 in grammar.html (mountChord) is rebuilt so direction is obvious three redundant ways at once:

  1. each agent's arc sector splits into a solid outbound half and a dashed inbound half;
  2. each ribbon is an asymmetric teardrop — pinched at the source, broad at the target;
  3. a source→target hue gradient plus a small arrowhead landing on the receiver's arc.

"Who talks to whom AND in which direction" is legible at a glance, and it stays legible even on belafonte-night where the ANSI kind hues collapse (direction carried by treatment, not hue). The same chord is incorporated into the sequence view as the communication-topology summary beside the time-ordered diagram.

Task 3 — information parity across the three compose proposals

Audited what each proposal surfaced from the session model and closed every gap so no proposal is missing what the others expose:

  • A · observatory — sequence view now renders the corrected vertical diagram + topology chord (was the flat score).
  • B · score — main is the corrected sequence diagram; the margin gains the topology chord, intervention ladder, plan reel (already had drift, interventions, heartbeat, fingerprint, DAG, strata in main).
  • C · mission control — gains the sequence diagram, topology chord, plan DAG, plan strata, and judge heartbeat (already had gantt, drift, ladder, reel, fingerprints).
  • The span inspector is now reachable in every proposal (A docks it in the rail drawer; B & C float it as an overlay), plus a ?span= deep-link.

Constraints / verification

Preserves the zicato design language, the 16-theme ANSI-sourced categorical system, and the KIND=hue / STATUS=treatment grammar. Standalone HTML/CSS/vanilla-JS, no build, no network. Theme switch re-skins without a re-render; _embed.js ?only/?bare/?theme still works (smoke-tested on the 3 restructured sequence sections). Verified headless across monokai, paper, dracula, belafonte-night, and solarized-dark.

pedapudi and others added 13 commits June 7, 2026 20:09
Serve the built console SPA from the web port alongside gRPC-Web and the
health probes so one harmonograf-server process hands a browser the whole
console — no separate static host or frontend dev server.

- static_site.py wraps the gRPC-Web/health ASGI app: GET/HEAD for non-API
  paths serve index.html + real files from the web root, with an index.html
  SPA fallback for unknown paths (e.g. #/session/<id> deep links). gRPC-Web
  content-type requests, gRPC service-method paths, and health routes always
  pass through untouched. The bundle is located via --web-root, then the
  packaged _console dir (importlib.resources), then ../frontend/dist; a clear
  warning + graceful degradation when none is found.
- Runtime endpoint config: the served index.html gets a
  window.__HARMONOGRAF_API__ global injected (derived from Host/X-Forwarded-*
  or --public-base-url), and transport.ts now resolves the gRPC-Web base URL
  runtime-global → VITE env → compiled-in default, so one built bundle works
  behind any host/port without a rebuild.
- App.tsx parses #/session/<id> on load and hashchange and selects that
  session in the UI store (eagerly, so it survives ListSessions auto-select),
  via a small sessionRoute helper.
- Packaging: make console builds the frontend into
  harmonograf_server/_console/; pyproject ships it in the wheel via the
  hatchling artifacts glob, located at runtime through importlib.resources.

Adds server + frontend tests; documents the approach in server/README.md.
Follow-up fixes from PR #294 review:

- frontend: apply the #/session/<id> deep link once per target instead of
  re-firing on every ListSessions poll. Previously a user who deep-linked to
  session A and then picked session B (which updates the store, not the hash)
  was dragged back to A on the next poll. Adds a regression test.
- server: cache index.html at startup (immutable build artifact) instead of a
  blocking disk read per request on the event loop shared with gRPC-Web
  streaming; off-load asset reads via asyncio.to_thread.
- server: send Cache-Control: immutable for content-hashed /assets/*; keep
  index.html no-cache.
- server: a missing file-like path (*.js/*.svg) now returns 404 instead of the
  SPA index.html fallback (avoids MIME-mismatch errors; also fixes favicon.svg).
- server: emit the injected window.__HARMONOGRAF_API__ via json.dumps (+ </
  guard) instead of hand-rolled escaping.
- server: HEAD responses carry headers + Content-Length but no body.
- tests: add coverage for _safe_join traversal, 404-for-missing-asset, asset
  cache header, HEAD no-body, and injection escaping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ional transfer chord, compose parity

Make the agent-interaction sequence diagram the recommended sequence view:
one vertical lifeline per agent, time down, each message a horizontal
connector with an explicit arrowhead (call solid, return dashed). The
horizontal score becomes the Gantt-companion alternative. Pair the diagram
with a directional transfer chord that encodes who-initiates-vs-who-receives
three redundant ways (split outbound/inbound arc sectors, asymmetric
source-pinched/target-broad ribbons, source-to-target hue + landing
arrowhead). Update figure 11 in the grammar to the directional version.

Close information parity across the three composed-console proposals: every
piece of session information surfaced in any one proposal is now reachable in
all three (sequence diagram, topology chord, plan DAG/strata/reel, drift,
heartbeat, ladder, and the span inspector — floated as an overlay in B and C,
plus a ?span= deep-link). Verified headless across monokai/paper/dracula/
belafonte-night/solarized-dark; theme switch re-skins without re-render.
…e-track stack

Compose the instruments view around two coordinated reading stacks.

Plan stack — the plan reel now sits directly above the plan DAG, sharing
horizontal alignment, and each reel revision is a button that drives the DAG:
clicking a version redraws the DAG for that version's task set (added tasks in
the accent "new" treatment, tasks dropped since the prior revision ghosted and
dashed like the existing live ghost). A `tid` on each DAG node bridges it to the
strata task-ids so a revision's `has`/`added` set reframes the graph. The click
is local UI state — it repaints only the DAG host + the reel's active marks, not
the whole console (digest-gated, no flash). Defaults to the latest revision;
`?ver=` deep-links one.

Time-track stack — the judge heartbeat is folded INTO the drift seismograph as
one instrument on a shared time axis: the drift traces as before, with judge
firings overlaid (on-task ticks in the judge KIND hue, warnings/criticals in the
STATUS treatment). The standalone heartbeat panel is gone from the view (still a
small figure for the inspector). The intervention ladder sits directly below the
seismograph and shares its exact x-domain (padL/padR/width/T via one trackGeom
helper), so an intervention at time t lines up vertically under the drift reading
at t, with a single shared bottom axis.

Preserves the 16-theme ANSI system and KIND=hue / STATUS=treatment grammar;
standalone HTML/CSS/JS, no build; theme switch re-skins without a re-render.
Verified headless across monokai, paper, dracula, belafonte-night and all three
plan revisions.
…, span-connecting gantt edges

- arrowheads (chord, gantt edges, sequence diagram) → small SOLID triangles
- topology chords → thin gradient strokes (width min(4, 0.9+√count·1.1),
  non-scaling) instead of filled teardrops; arc caps 4.5px non-scaling
- gantt transfer/delegation edges connect span-end → span-start with a
  landing arrowhead
- judge heartbeat derived from the drift so its marks align with the
  seismograph excursions

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s the multi-plan prefix

The header's "Plan N · " prefix (#195, written for a merged-trajectory model)
was superseded when the plan picker (#196) landed: the picker scopes the view
model to one plan and conveys plan identity via its chip bar, so a header
prefix is redundant. #196 left #195's two prefix-tests stale → they failed.

Header now stays a plain "rev N of M" (planPrefix = ''); the two stale tests
assert no-prefix while keeping their rev-math coverage (denominator reads the
highest revisionIndex, not the array length). Fixes the 2 pre-existing
TrajectoryView.headerMath failures → full suite 740/740.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port the zicato-language UI study into the real React app as a second,
toggle-able console. Default uiMode is 'md3' so production is unchanged.

- uiStore: persisted uiMode ('md3'|'zicato', default 'md3') + setUiMode/
  toggleUiMode, and a scoped zicatoTheme (16 study themes via
  data-zicato-theme, never touches the MD3 :root theme).
- App.tsx branches on uiMode → ZicatoConsole vs Shell (#/stress unaffected);
  toggle controls in the MD3 AppBar (⧉) and the zicato topbar.
- ZicatoConsole shell (topbar/strip/rail/main/inspector/transport) + the two
  rail views (gantt, instruments); reuses the MD3 SessionPicker (⌘K).
- adapter.ts: useZicatoSession maps the live SessionStore + plan hooks into
  the study's render inputs (reactive; graceful EMPTY_SESSION fallback).
- Six figures ported to React: GanttZ, ChordZ, SeismographZ/JudgeHeartbeatZ,
  LadderZ, PlanZ (reel→DAG), SequenceZ, FingerprintZ; svgUtils + token CSS.
- zicato.smoke.test: console mounts against an empty store; token/agent
  mappers verified.

Build/lint/tests green (740/740). NOT yet verified against a live session —
see docs/zicato-frontend-port.md for the full state + resume guide.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eak)

The MD3 global rule `.hg-rail { grid-area: rail }` leaked onto the zicato
console's rail (same class name). The zicato grid (.zk-app-body: 84px / 1fr /
auto) defines no named `rail` area, so the inherited grid-area pulled the rail
out of placement and collapsed .zk-main into the 84px first column — the gantt /
heartbeat / instruments rendered ~48px wide and "didn't scale to the screen".
Reset `grid-area: auto` on `.zk-root .hg-rail` so auto-placement restores it as
the left column and main fills the rest.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e by sub-type

- GanttZ: greedy interval packing (MD3 packLanes) assigns each agent's
  concurrent spans to distinct sub-lanes; the agent row grows to fit its lane
  count, so overlapping spans no longer render on top of each other.
- adapter.gfClassForSpan: goldfive's own llm spans (goal_derive / refine /
  judge_* / plan) carry no `goldfive.category` attribute and all fell through
  to one llm-call hue. Infer the sub-type from the span name (goldfive actor
  only) so judge / refine / plan / reflective get distinct hues. Work spans are
  unaffected (still coloured by KIND).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ing)

The figures draw into a fixed viewBox (0 0 940 H) at width:100%, so on a wide
pane the whole SVG upscaled ~2× — text, bars and strokes all looked too large.
New <Fig> wrapper measures the container (ResizeObserver) and passes that as the
viewBox width, so 1 viewBox unit = 1 CSS px (no upscaling) while the figure still
fills the pane. Threaded a W prop into GanttZ + PlanZ (its reel scales with W; the
DAG's fixed 4-column x is scaled by W/940). Heartbeat / sequence / chord /
seismograph / ladder already took W — now fed the measured width. Falls back to a
fixed width when ResizeObserver is absent (jsdom), so the smoke test still passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…gantt

- ganttViewport.ts: GanttView window type + zoomView/panView/fitView/isFit helpers.
- GanttZ: optional view/onViewChange props; X maps the visible window; plot clipped
  so a zoomed view never spills the lane gutter; wheel zooms toward the cursor,
  pointer-drag pans (click-vs-drag threshold keeps span selection). No view → renders
  exactly as before (eff = fitView(T)).
- MinimapZ: full-timeline overview (one row per agent, span rects, the viewport box),
  click/drag to seek — the zicato-language port of MD3 Gantt/Minimap.tsx.
- GanttViewZ: owns the viewport state + a − / + / fit toolbar; wires GanttZ + MinimapZ.
- zicato.css: .zk-gantt-head / .zk-zoom / .zk-minimap styles.
- Fig.tsx: un-export useMeasuredWidth (keep the file component-only → clears the
  react-refresh/only-export-components lint error from the prior scaling commit).

Build + lint clean (pre-existing MD3 warning aside); zicato smoke 6/6.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s, reasoning detail + floating drawer

- adapter: ZSpan.hasReasoning/reasoning (via lib/thinking detection) + a ZSteer
  list (buildSteers: goldfive corrections → the agent/task they steered, from the
  refineAttempts/drift registries, severity-tagged).
- GanttZ: render a 🧠 glyph on reasoning spans; draw severity-coloured steering
  arrows (goldfive lane → steered agent), clipped + zoom-window-aware; click →
  floating drawer (useSteerSelect, no-op outside the console provider).
- svgUtils.steerColor; FloatingDrawerZ (port of TrajectoryFloatingDrawer:
  reasoning chain-of-thought + steering trigger/target detail); steerContext
  (SteerSelectContext + useSteerSelect, kept in its own module for fast-refresh).
- ZicatoConsole: reasoning block in the inspector + the floating drawer + provider.
- tests: zicato smoke 6→15 (reasoning/steer derivation); new GanttZ.zoom.test
  (the zoom viewport actually moves spans / fit restores — guards the prior fix).

Build + lint clean; 17 zicato tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e orchestration)

Emits a plan through 4 revisions (add / supersede / drop tasks, with revision
reasons/kinds) to a running harmonograf server via harmonograf_client directly —
no Runner/planner/LLM — so the console's plan reel + DAG can be exercised against
real revision evolution. Run: HARMONOGRAF_SERVER=127.0.0.1:7531 uv run --extra
orchestration python examples/standalone_observability/replan_scenario.py

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@pedapudi

Copy link
Copy Markdown
Owner Author

Merged directly into main as d4c4bcb. The branch was rebased onto #294 (portable console-UI serving), dropping the two now-superseded console-UI commits and replaying the zicato-console work on top — no conflicts, all tests green. Closing this PR and deleting the branch; the work lives on main.

@pedapudi pedapudi closed this Jun 16, 2026
@pedapudi
pedapudi deleted the design/sequence-compose-parity branch June 16, 2026 06:46
Sign up for free to 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.

1 participant