Yusef Mosiah Nathanson

Founder of Choir

What go-choir Still Lacks Before It Is Fully Object-Graph Native

Mosiah.org · article artifact

Status: internal implementation note\ Reviewed revision: go-choir main at 0b6655860c37\ Core claim: Choir now has the right object-graph substrate in code, but the migration is not complete until all authoritative state, read paths, publication paths, tests, and operator surfaces treat og_objects and og_edges as the primary model rather than a parallel projection.

The July object-graph cutover was real. It was not just a design memo. At 0b6655860c37, go-choir has a shared object-graph schema, VM-local Dolt storage, corpusd platform storage, graph-native publication writes, and large slices of the runtime store wired into graph objects and edges.

That is the right direction. But the important caveat remains: Choir is not yet fully object-graph native.

The current code is in a migration state. The object graph is increasingly the substrate, but several areas still treat the graph as a projection, compatibility layer, or partial replacement. This note names what remains.

What is already real

The common schema exists.

internal/objectgraph/dolt_store.go defines the shared tables:

og_objects(
  canonical_id,
  object_kind,
  owner_id,
  computer_id,
  version_id,
  content_hash,
  body,
  metadata,
  created_at,
  updated_at,
  tombstone,
  superseded_by
)

og_edges(
  edge_id,
  from_id,
  to_id,
  kind,
  metadata,
  created_at,
  tombstone
)

The VM store initializes that object graph in the same embedded Dolt workspace as runtime state. internal/store/store.go explicitly says the object graph tables coexist with relational tables during Phase 3 migration: converted methods use object graph while unconverted methods continue using SQL.

The registry now names a broad ontology:

  • agents;
  • runs;
  • events;
  • trajectories;
  • work items;
  • channel messages;
  • worker updates;
  • run memory;
  • acceptances;
  • continuations;
  • Texture documents;
  • Texture revisions;
  • Texture decisions;
  • evidence;
  • content items;
  • browser sessions;
  • app packages;
  • app adoptions;
  • desktop sessions;
  • desktop app instances;
  • source entities;
  • source refs;
  • web captures;
  • publications;
  • publication versions;
  • public routes;
  • artifact manifests and blobs;
  • provenance records;
  • consent and review records;
  • retrieval sources and spans;
  • verifier attestations;
  • publication policies;
  • publication source entities;
  • publication transclusions.

Publication is no longer merely a relational side table. internal/platform/publication_graph.go writes publication data as graph objects and edges: publication proposal, publication, version, route, manifest, blob, consent, review, retrieval source, retrieval span, retrieval manifest, provenance entity, provenance activity, provenance agent, verifier attestation, policy, source entities, transclusions, citations.

The architecture is now visible in code.

The main gap

A system is fully object-graph native only when the graph is the authority, not an annotation.

Right now the object graph is authoritative in some paths, dual-written in others, and absent or compatibility-bound in still others. That is normal for a migration. But it means documentation and public claims should avoid saying “everything is the object graph” unless the specific path has been checked.

A safe formulation is:

Choir is converging on a unified object graph as the audited-computer substrate. The substrate exists, graph-native publication exists, and major runtime record families are being wired in. The migration is incomplete until all writes, reads, validation gates, and operator surfaces use the graph as primary authority.

Gap 1: dual-write and converted-method boundaries

The store layer still has a mixed world:

  • converted methods write/read graph objects;
  • unconverted methods still use SQL tables;
  • some code likely still depends on relational table shape;
  • some graph writes may be backfilled rather than original source of truth;
  • correctness depends on parity between legacy tables and graph objects during the transition.

The risk is split authority. If a bug writes one substrate but not the other, the system may appear correct through one read path and incorrect through another.

To be fully object-graph native, each state family needs an explicit answer:

  1. What is the canonical graph object kind?
  2. What are its required metadata fields?
  3. What are its required edges?
  4. Which write path creates it?
  5. Which read path is authoritative?
  6. What legacy table, if any, remains only as compatibility/cache?
  7. What test proves legacy and graph representations cannot diverge silently?

Gap 2: graph-first reads

Writing objects is not enough. A graph-native system reads by traversal.

The important question for each product path is not “does an object exist?” It is:

  • Can the UI/API answer the product question from the graph?
  • Can a verifier reconstruct the evidence path from graph edges?
  • Can an operator inspect a failed run as a connected object neighborhood?
  • Can publication derive route, artifact, source, consent, policy, and attestation from graph state without reaching around it?

Some paths already move that way. Publication graph read helpers and metadata lookups exist. But full graph-native operation requires broad read-path replacement, not only write-path projection.

Gap 3: source identity is still hybrid

The current source/publication contract correctly says rendered prose is not source authority. Source identity lives in metadata and structured source records, not in inline links or DOM output.

But source identity is still in a hybrid stage:

  • Texture revisions carry source_entities metadata;
  • body documents carry source-ref nodes;
  • source/ref graph records exist;
  • publication exports selected source entities into publication graph objects;
  • legacy metadata compatibility still matters.

The target is clear: every source entity and source ref should be a durable object with stable identity, version pinning, selector metadata, evidence state, and explicit edges to the Texture revision or publication version that uses it.

Until graph-first source reads land everywhere, it is safer to say:

Source entities are being promoted from fragile revision metadata and render-time citations into durable graph records. The current system preserves compatibility with revision metadata while moving toward graph authority.

Gap 4: publication gates need first-class product vocabulary

The code already has gate-like behavior:

  • autonomous Wire publishing checks platform ownership;
  • revisions must carry Wire lineage;
  • seed briefs are rejected;
  • canonical article metadata matters;
  • publication writes consent records, review records, policies, verifier attestations, artifact manifests, route objects, and provenance edges.

But the product vocabulary is not yet as crisp as the implementation wants. “Publish” should mean:

Create a selected, immutable, policy-bound public projection from private graph state, with provenance, consent, review, verification, artifact manifests, routes, source records, citations, transclusions, and rollback/supersession semantics.

That gate should become inspectable as a graph neighborhood. A publication should not be understood as a page upload. It is a transaction that converts private state into public artifact state under policy.

Gap 5: platform graph and VM graph are not yet federation protocol

The code now has the crucial invariant: VM graph and corpusd graph use the same og_objects / og_edges schema.

That makes federation plausible. But it is not yet a complete federation protocol.

A fully graph-native platform needs explicit rules for:

  • which object kinds may cross boundaries;
  • which edges are public, private, enterprise-visible, team-visible, or child-visible;
  • how private canonical IDs are projected or redacted;
  • how publication proposals attach to the receiving graph;
  • how enterprise-level graph objects accept employee/team publications;
  • how global-level graph objects accept enterprise publications;
  • how revocation, supersession, rollback, and policy changes propagate;
  • how signatures/verifier attestations travel with the object neighborhood;
  • how receiving graphs distinguish source evidence from executable instructions.

Right now the same schema gives Choir a path. The remaining work is a capability and policy layer over graph exchange.

Gap 6: operator surfaces need graph neighborhoods

A fully object-graph-native system should let an operator click or query an object and see its neighborhood:

  • this run;
  • the agent that ran it;
  • the trajectory it belongs to;
  • the messages it consumed;
  • the Texture revision it changed;
  • the evidence it produced;
  • the acceptance gate it passed or failed;
  • the publication it created;
  • the route it updated;
  • the verifier attestation that blessed it;
  • the rollback target if it regresses.

If operators still debug by reading disconnected logs, tables, and rendered pages, the graph exists but is not yet the operational surface.

The object graph becomes native when debugging, review, publication, rollback, and governance all happen through object neighborhoods.

Gap 7: tests must assert graph semantics, not only API parity

Parity tests are necessary but insufficient.

A graph-native test should assert structural meaning:

  • a run has an edge to its agent;
  • a Texture document has revision edges;
  • a revision has a parent edge;
  • a publication version has a manifest edge;
  • a manifest contains a blob;
  • a public route routes to a publication;
  • a publication version references source entities;
  • a transclusion is represented as an edge with selector metadata;
  • an attestation points at the version it attests;
  • a private source revision is represented hash-only when projected public;
  • tombstone and supersession semantics work across read paths.

The test suite should make ontology regressions hard. A feature should fail not only when an HTTP response changes, but when it fails to leave the right graph trace.

Gap 8: migration/backfill has to become boring

A graph-native cutover needs operational confidence:

  • idempotent backfills;
  • clear migration checkpoints;
  • divergence detection;
  • graph/table parity reports during transition;
  • rollback plans;
  • missing-edge detectors;
  • stale-kind detectors;
  • tombstone/supersession audits;
  • public/private leak scans over graph exports;
  • performance checks for graph-neighborhood reads.

Without those, the graph can become another layer of complexity instead of the simplification.

Gap 9: docs must stop describing yesterday’s architecture as current state

Several older docs are useful historical signal, but current architecture moved quickly.

Safe documentation rules:

  • use Texture, not VText, unless discussing history;
  • describe StoryGraph-like or archived object-graph docs as historical signal unless current code confirms them;
  • distinguish mission target from implemented path;
  • say “publication is a selected projection/gate,” not “the public graph sees the private computer”;
  • say “object graph is becoming the authoritative substrate,” not “all state is already graph-native,” unless the path is verified.

The completion criterion

Choir is fully object-graph native when this statement is true:

For any meaningful product state, there is a canonical object or edge; writes create graph state first; reads answer through graph state; publication is a graph transaction; verification inspects graph neighborhoods; rollback and supersession are graph operations; and legacy tables are either gone or explicitly marked as caches/compatibility views.

That is the line.

The July cutover built the road. The remaining work is to make every important path drive on it.