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

@aaltshuleraaltshuler 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

FilenameOverview
packages/client/src/operator.tsNew 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.tsAdds 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.tsStrict 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.tsSourceCapabilities simplified to namedQueries + rawGq; ReadRequest gets queryRef field; FixtureQuery types removed.
packages/core/src/runtime/compatibility.tsCompatibility validation migrated: ref cells check namedQueries capability; rawGq without capability is now a hard error.
packages/cli/src/source.tsresolveConnection delegates to operator resolver; Connection interface tightened (server/graphId now required). buildSource removes fixture branch cleanly.
packages/cli/src/serve.tsFixture mode removed; bare-host redirect added; allowRawGq forwarded as ?allowRawGq=1.
packages/cli/src/proxy.tsBFF proxy now explicitly deletes client-supplied Authorization/Proxy-Authorization before injecting the server-side token — security improvement.
packages/cli/src/cli.tsAdds --version/-v, per-command --help, COMMAND_HELP registry, SOURCE_FLAGS constant. Clean DX improvements.
packages/cli/src/commands/render.tsAdds --watch mode with 50ms debounce; splits into renderOnce + safeRender helpers; watch mode holds event loop intentionally.
packages/web/src/App.tsxMajor 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.tsFixture mode removed; token fallback changed from devtoken to undefined; allowRawGq wired via isTruthyParam.
packages/client/src/source.tsServerSource.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)

    P2notebook.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

aaltshulerand 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 threadpackages/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.

P2query.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

aaltshulerand 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 mainJun 24, 2026
2 checks passed
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.

1 participant

@aaltshuler