Skip to content

Dash-books: delete fixture mode + Phase 1 (catalog-query model, operator-config connection, CLI DX) - #7

Merged
aaltshuler merged 9 commits into
mainfrom
dash-books-phase1
Jun 24, 2026
Merged

Dash-books: delete fixture mode + Phase 1 (catalog-query model, operator-config connection, CLI DX)#7
aaltshuler merged 9 commits into
mainfrom
dash-books-phase1

Conversation

@aaltshuler

@aaltshuler aaltshuler commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Implements dash-books-canon.md through Phase 1 (read-only). Five reviewable commits: a fixture-deletion baseline, then Phase 1A–1D.

Baseline — delete fixture mode + canon (4431942)

  • Remove @modernrelay/notebook-fixture and all fixture-mode wiring; the notebook runs only against omnigraph-server via the SDK (no in-memory mock).
  • Add dash-books-canon.md (concept, modules, current state, target shape, phased ledger).

Phase 1A — query model (5f94d13)

Consistency over canon (§4.1): cells bind to server-owned catalog queries by name.

  • query.refog.queries.invoke (the default); query.rawGq → ad-hoc og.query (gated escape hatch).
  • Delete the client-side compiler — translate.ts read path, the ego planner, the nodes/path/ego DSL.
  • Strict v1 schema (rejects stale keys — internal tool, no version negotiation).
  • Author 3 catalog .gq queries + register them in the demo cluster; migrate company-server.notebook.yaml to ref.

Phase 1B — operator-config connection (7fb956c)

  • New Node-only @modernrelay/notebook-client/node resolver: ~/.omnigraph/config.yaml + credentials (0600), named servers, keyed-token chain (OMNIGRAPH_TOKEN_<SERVER> → credentials → OMNIGRAPH_BEARER_TOKEN), profiles. Wired into CLI + TUI; web stays on the view proxy.

Phase 1C — CLI DX (7977709)

  • --version, per-command --help, render --watch, omnigraph-notebook bin alias.

Phase 1D — docs (451d251)

  • CLAUDE / AGENTS / README refreshed to the ref/rawGq + operator-config model.

Verification

  • pnpm -r build / typecheck / test green — 56 tests.
  • Offline CLI (validate/catalog/schema/--version/--help) smoke-tested.

Deferred (tracked in the canon ledger)

  • Live-catalog notebook validate (ref/param checks via og.queries.list()).
  • view/tui live-reload --watch (needs websocket / Ink-reconciler work).
  • Phase 2 (typed result envelopes + auto tier) gated on the omnigraph-side "declared query outputs" feature (§4.3, option a).

🤖 Generated with Claude Code

Greptile Summary

This PR implements Phase 1 of dash-books-canon.md: it removes fixture mode entirely, migrates the cell query model from a client-side compiled DSL to server-owned catalog queries (query.refog.queries.invoke) with a capability-gated raw .gq escape hatch (query.rawGq), adds the Node-only operator-config resolver (~/.omnigraph/config.yaml + credentials), and delivers CLI DX improvements (--version, per-command --help, render --watch).

  • Fixture mode deleted across all packages: @modernrelay/notebook-fixture wiring removed from CLI, TUI, and web; NotebookSchema / CellSchema / QuerySchema all gain .strict() and drop the fixture / source keys.
  • Operator-config resolver (packages/client/src/operator.ts): reads ~/.omnigraph/config.yaml + credentials (0600-enforced), resolves named servers, profiles, and a keyed-token chain; Client no longer reads env for tokens.
  • Web canvas layout in App.tsx replaced by a responsive 6-column grid with dnd-kit drag-reorder and resize; proxy.ts now explicitly drops client-supplied Authorization before injecting the server-side token.

Confidence Score: 5/5

Safe to merge — a large but coherent refactor with no active data paths broken and 56 tests green.

The fixture-removal and query-model migration are complete end-to-end: schema, runtime, source, CLI, TUI, and web all move together. The operator-config resolver is new but isolated to Node-only entry points and has its own test coverage. The only non-trivial concern is the as SdkRead type assertion in invoke(), which is a type-safety trade-off rather than a correctness issue at runtime given the expectMutation: false server-side guard.

packages/client/src/http.ts — the invoke() cast warrants a second look once the SDK exposes a narrowed return type.

Important Files Changed

Filename Overview
packages/client/src/operator.ts New Node-only operator-config resolver: reads ~/.omnigraph/config.yaml + credentials with 0600 enforcement, keyed-token chain, named-server registry, and profile support. Logic is clean and handles all precedence levels correctly.
packages/client/src/http.ts Adds invoke() for server-owned catalog queries; removes env-fallback token resolution from Client constructor. The as SdkRead cast in invoke() is the only concern.
packages/core/src/spec/index.ts Strict v1 schema: removes fixture DSL, migrates query model to ref/rawGq XOR, adds Timeline/Card/Quote lens kinds, cell width, and .strict() across all schemas.
packages/core/src/runtime/types.ts SourceCapabilities simplified to namedQueries + rawGq; ReadRequest gets queryRef field; FixtureQuery types removed.
packages/core/src/runtime/compatibility.ts Compatibility validation migrated: ref cells check namedQueries capability; rawGq without capability is now a hard error.
packages/cli/src/source.ts resolveConnection delegates to operator resolver; Connection interface tightened (server/graphId now required). buildSource removes fixture branch cleanly.
packages/cli/src/serve.ts Fixture mode removed; bare-host redirect added; allowRawGq forwarded as ?allowRawGq=1.
packages/cli/src/proxy.ts BFF proxy now explicitly deletes client-supplied Authorization/Proxy-Authorization before injecting the server-side token — security improvement.
packages/cli/src/cli.ts Adds --version/-v, per-command --help, COMMAND_HELP registry, SOURCE_FLAGS constant. Clean DX improvements.
packages/cli/src/commands/render.ts Adds --watch mode with 50ms debounce; splits into renderOnce + safeRender helpers; watch mode holds event loop intentionally.
packages/web/src/App.tsx Major UI rework: 6-column responsive canvas grid with dnd-kit drag-reorder, resize in edit mode, auto-animate, localStorage-persisted overrides.
packages/web/src/config.ts Fixture mode removed; token fallback changed from devtoken to undefined; allowRawGq wired via isTruthyParam.
packages/client/src/source.ts ServerSource.read dispatches on queryRef (invoke) vs querySource (rawGq); all client-side translation removed.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant CLI as CLI / TUI
    participant OpRes as operator.ts resolveConnection
    participant Config as ~/.omnigraph/config.yaml + credentials
    participant Client as Client (http.ts)
    participant Source as ServerSource
    participant Server as omnigraph-server

    CLI->>OpRes: flags + notebook.server/graph
    OpRes->>Config: loadOperatorConfig() + loadCredentials()
    Config-->>OpRes: servers, profiles, defaults, tokens
    OpRes-->>CLI: ResolvedConnection
    CLI->>Client: new Client
    CLI->>Source: new ServerSource

    Note over Source,Server: query.ref path (canonical)
    Source->>Client: invoke(name, params)
    Client->>Server: "POST /graphs/{id}/queries/{name}"
    Server-->>Client: Read response
    Client-->>Source: ReadOutput

    Note over Source,Server: query.rawGq path (escape hatch, gated)
    Source->>Client: "query({ query: rawGq })"
    Client->>Server: "POST /graphs/{id}/query"
    Server-->>Client: Read response
    Client-->>Source: ReadOutput
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant CLI as CLI / TUI
    participant OpRes as operator.ts resolveConnection
    participant Config as ~/.omnigraph/config.yaml + credentials
    participant Client as Client (http.ts)
    participant Source as ServerSource
    participant Server as omnigraph-server

    CLI->>OpRes: flags + notebook.server/graph
    OpRes->>Config: loadOperatorConfig() + loadCredentials()
    Config-->>OpRes: servers, profiles, defaults, tokens
    OpRes-->>CLI: ResolvedConnection
    CLI->>Client: new Client
    CLI->>Source: new ServerSource

    Note over Source,Server: query.ref path (canonical)
    Source->>Client: invoke(name, params)
    Client->>Server: "POST /graphs/{id}/queries/{name}"
    Server-->>Client: Read response
    Client-->>Source: ReadOutput

    Note over Source,Server: query.rawGq path (escape hatch, gated)
    Source->>Client: "query({ query: rawGq })"
    Client->>Server: "POST /graphs/{id}/query"
    Server-->>Client: Read response
    Client-->>Source: ReadOutput
Loading

Comments Outside Diff (1)

  1. packages/web/src/config.ts, line 35-47 (link)

    P2 notebook.server can now be an operator-config name, producing a confusing error in the browser

    NotebookSchema.server was widened from z.url() to z.string().min(1) to allow operator-config server names (e.g. server: prod). In buildConfig, notebook.server is the fallback when no ?server= URL param is present. If a CLI-targeted notebook has server: "prod", this code passes "prod" to new Client({ baseUrl: "prod" }), which fails at the SDK layer with an opaque fetch error rather than a clear schema validation message. The previous z.url() constraint would have caught this at parse time.

    Fix in Claude Code

Reviews (4): Last reviewed commit: "Interactive canvas: drag/resize tiles, r..." | Re-trigger Greptile

aaltshuler and others added 5 commits June 22, 2026 16:11
Removes the @modernrelay/notebook-fixture package and all fixture-mode
wiring — the notebook now runs only against omnigraph-server via the SDK
(no in-memory mock graph). Adds dash-books-canon.md as the project's
source of truth (concept, key modules, current state, target shape) and
refreshes CLAUDE/README/AGENTS to the server-only model.

- delete packages/fixture; drop the notebook-fixture dep from tui/web/cli
- remove top-level notebook.fixture selection + the example fixtures;
  tui/web/cli now build a ServerSource only
- spec keeps the cell-level structured query DSL as the interim server
  query path (slated to become predefined-query `ref` per the canon)
- docs: "server-backed runtime"; package map drops the fixture row

Verified: pnpm -r build/typecheck/test green (62 tests); CLI
validate/schema/catalog smoke-tested.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move reads off the client-side query compiler and onto server-owned
catalog queries invoked by name — the consistency-over-canon model from
dash-books-canon.md §4.1.

- spec: `query.ref` (catalog query, the default) + `query.rawGq` (gated
  raw-.gq escape hatch); delete the nodes/path/ego DSL + `query.fixture`;
  make Notebook/Cell/Query schemas `.strict()` (reject stale keys —
  internal tool, no version negotiation)
- runtime: ReadRequest.queryRef; SourceCapabilities.namedQueries (drops
  structuredQueryKinds); compatibility checks ref→namedQueries, rawGq→rawGq;
  delete resolveFixtureQuery/resolveWhere
- client: Client.invoke wraps og.queries.invoke; ServerSource routes
  ref→invoke / rawGq→query; delete translate.ts read translators + the
  ego decomposition planner (keep translateMutation for interim set_field)
- demo: author examples/server/queries/*.gq (3) + register via
  `queries: ./queries/` in server-demo's cluster; migrate
  company-server.notebook.yaml cells to `query.ref`
- tests rewritten for the ref/rawGq model + strict schema

Read-only (Phase 1); writes stay on the interim set_field path. Verified:
pnpm -r build/typecheck/test green (49 tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a Node-only resolver (`@modernrelay/notebook-client/node`) that reads
omnigraph's RFC-011 operator config — so a notebook connects with zero
flags once you've `omnigraph login`'d (dash-books-canon.md §4.7).

- operator.ts: ~/.omnigraph/config.yaml (servers/defaults/profiles) +
  ~/.omnigraph/credentials (INI, 0600-enforced); `--server <name|URL>`
  resolves a name via the servers registry; token chain
  OMNIGRAPH_TOKEN_<SERVER> → credentials[server] → OMNIGRAPH_BEARER_TOKEN;
  honors OMNIGRAPH_HOME / OMNIGRAPH_PROFILE; drops the invented
  OMNIGRAPH_TOKEN / OMNIGRAPH_GRAPH_ID
- exported via a /node subpath (uses node:fs; out of the browser bundle)
- cli/source.ts + tui/index.tsx delegate to the shared resolver; both gain
  --profile. web/config.ts unchanged (browser uses the view proxy)
- client gains a `yaml` dep + 7 resolver tests

Verified: pnpm -r build/typecheck/test green (56 tests).

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

- `--version` / `-v` (reads packages/cli/package.json at runtime)
- per-command `--help` (`notebook <cmd> --help`) with command-specific usage
  + the shared source-flags block; top-level HELP updated for the
  operator-config connection model (--server NAME|URL, --profile)
- `render --watch` re-runs on notebook-file change (debounced; Ctrl-C to stop)
- `omnigraph-notebook` bin alias (alongside notebook / mr-notebook) so a
  future Rust `omnigraph` plugin-dispatch resolves it
- catalog command: replace the stale `queryKinds: [nodes,path,ego]` with the
  ref/rawGq query surface

Deferred: live-reload `--watch` for view (browser) and tui (Ink) — needs
websocket / reconciler work; tracked for a follow-up.

Verified: pnpm -r build/typecheck/test green (56 tests); CLI
--version/--help/validate/catalog/schema smoke-tested.

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

- CLAUDE.md / AGENTS.md: data-flow + Source sections describe `og.queries.invoke`
  by `ref` (and the `rawGq` escape hatch) instead of the deleted client-side
  DSL/translation; connection paragraph points at the shared
  @modernrelay/notebook-client/node operator-config resolver; drop the invented
  $OMNIGRAPH_GRAPH_ID; note the strict v1 schema
- README.md: source-flags blurb covers --server NAME|URL + --profile and the
  flags → operator-config → notebook resolution order
- dash-books-canon.md: tick off Phase 1 ledger items (query model, rawGq, strict
  schema, connection client, CLI DX, docs); mark the live-catalog validate and
  view/tui live-reload as partial, and the live-cluster e2e as pending

Verified: pnpm -r build/typecheck/test green (56 tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Comment thread packages/cli/src/commands/render.ts
Comment on lines 67 to +105
@@ -172,13 +101,15 @@ const QuerySchema = z
* Shape mirrors a control cell minus `query` (controls don't fetch data;
* they read/write state via $bindState / on.press → setState).
*/
export const CellControlSchema = z.object({
id: z.string().min(1).optional(),
lens: ControlKind,
props: z.record(z.string(), z.unknown()).default({}),
on: z.record(z.string().min(1), ActionBindingSchema).optional(),
visible: z.unknown().optional(),
});
export const CellControlSchema = z
.object({

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 query.name can be set alongside query.ref with no validation error, but is silently ignored

name is documented as "Selects a query within a multi-query rawGq payload" and is only consumed by ServerSource.read on the rawGq code path. When a cell uses query.ref, runtime.ts copies cell.query.name into ReadRequest.queryName, but ServerSource.invoke never reads it. An author who writes query: { ref: "foo", name: "bar" } will get no error and no effect. A refinement guarding name to the rawGq branch would make the constraint explicit.

Fix in Claude Code

aaltshuler and others added 4 commits June 23, 2026 01:10
Phase 2A: cell `display: drawer|modal` + `open_state` lifts detail cells
into a host-shell overlay (web Drawer on base-ui Dialog; pure partitionCells
in layout.ts groups cells sharing an open_state). Runtime/spec-nesting
unchanged — reuses the /selected selection + applyStateChanges; the TUI stays
layout-flat. Adds Timeline + Card lenses, click-to-select + wrap on Table,
and fits tables to their container so the drawer doesn't overflow.

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

- 2D in-flow grid: cell `width: full|half|third|two-thirds` maps to a span in
  the web host's responsive 6-column inline grid (layout.ts `widthToColSpan` →
  literal `md:col-span-*`; App.tsx stack → `grid md:grid-cols-6`). Default full
  = the old single-column stack; halves/thirds sit side-by-side. Host-shell
  only — the TUI stays layout-flat.

- Quote lens: rows as a blockquote feed (`text_column` + a `source · meta…`
  citation) for highlights/annotations/comments — utterance-centric, distinct
  from Timeline. Web + Ink renderers; replaces the cramped highlights table.

Both are host-shell/component-tier only — no runtime/spec-nesting/omnigraph
change. Spec, catalog, tests, and canon updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three security/canon-alignment fixes:

- P1 (BFF): the `view` proxy is now authoritative for auth — it always strips
  client-supplied Authorization/Proxy-Authorization and injects only the
  server-side token; the browser no longer holds a default `devtoken`.

- P2 (rawGq): the raw `.gq` escape hatch is capability-gated and off by default
  (`ServerSource.allowRawGq`). A notebook with a `rawGq` cell now fails
  compatibility unless the explicit dev/CLI hatch is enabled (`--allow-raw-gq`,
  threaded through args/source/view→URL/tui; web `?allowRawGq`).

- P3: `Client` reads no env — dropped the invented `OMNIGRAPH_TOKEN` (and the
  redundant `OMNIGRAPH_BEARER_TOKEN`) fallback; token resolution lives only in
  the shared operator resolver.

Tests + canon updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Evolves the web host into a single canvas of dependent cards:

- Tier-1 arrange: drag-reorder (@dnd-kit) + drag-edge resize (1–6 cols),
  browser-local overrides in localStorage (layout-overrides.ts); @formkit/
  auto-animate eases cards into place; min-w-0 so content reflows without
  blowing out the grid; rAF-coalesced resize.
- Retire the overlay tier: remove cell `display`/`open_state` + the drawer
  primitive; master-detail is now in-flow dependent cards reacting to $state.
- Layout: drop the redundant cell-list sidebar, widen the canvas (max-w-[96rem]),
  sticky header, less-rounded cards.
- view server: bare URL redirects to the served notebook (no more bundled-default
  cross-origin CORS failure).

Spec/runtime/TUI essentially unchanged (TUI ignores layout); web-host only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aaltshuler
aaltshuler merged commit 21eccc7 into main Jun 24, 2026
2 checks passed
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