Graph Metadata Editor
Status: design contract.
The editor works on graph claims, not raw file formats. A human or agent edits the same conceptual object everywhere:
kind
nodes
summary
links
backing source
validation issues
The backing writer decides whether the change eventually lands in Markdown
frontmatter, .graph.ts, an inline @graph block, QA suite YAML, test catalog
metadata, or an Ops DB row.
Non-Negotiables
- API first, MCP second, UI third.
- No raw YAML/TypeScript editing in the normal UI.
- No required "preview patch" or "commit metadata" step in the editor.
- The editor saves through the owning API.
- A later Git flush path materializes file-backed overlays into source files.
- Domain validators stay domain-specific. Only the actor policy gate is shared.
Claim Shape
interface GraphClaim {
id: string;
kind: 'idea' | 'behaviour' | 'implementation' | 'suite' | 'test' | 'journal' | 'infra' | 'anomaly' | 'event';
nodes: string[];
summary?: string;
links: Partial<Record<GraphClaim['kind'], string[]>>;
backing: {
type: 'markdown-frontmatter' | 'graph-ts' | 'inline-graph-block' | 'suite-yaml' | 'test-catalog' | 'ops-db';
path?: string;
table?: string;
recordId?: string;
sourceHash?: string;
};
issues: GraphIssue[];
}
interface GraphIssue {
level: 'hard' | 'soft' | 'info';
code: string;
message: string;
path?: string;
}
Actor Policy Gate
Validators emit issues. The shared gate decides whether the actor may save.
| Actor | Soft issues | Hard issues |
|---|---|---|
human |
Save allowed; UI shows warning. | Reject. |
agent |
Reject unless force: true; forced save is audited. |
Reject. |
system / pipeline |
Reject. | Reject. |
This is the same policy shape as CVL selection cardinality: humans can leave honest invalid state, agents can deliberately force soft-rule exceptions, and less intelligent machinery must keep invariants clean. Hard gates protect parseability, source safety, and known hub identity for everyone.
Validation Levels
Hard:
- unknown hub kind;
- unparseable backing source;
- malformed node id that cannot roundtrip;
- writer cannot serialize without corrupting the source;
- path/table/record target is outside the allowed backing set.
Soft:
- missing required graph link;
- dangling target that could be created later;
- source-backed grounding gap;
- duplicate link or duplicate node after normalization.
Info:
- suggested link target;
- stale summary;
- generated claim that should be promoted to an owning source.
API Contract
The API owns the write path. The UI does not know whether it is editing Markdown, TypeScript, YAML, or a DB record.
GET /v1/graph/claims
GET /v1/graph/claims/:claimId
PATCH /v1/graph/claims/:claimId
POST /v1/graph/claims/:claimId/validate
GET /v1/graph/metadata/flush/status
POST /v1/graph/metadata/flush
PATCH /v1/graph/claims/:claimId receives:
interface UpdateGraphClaimRequest {
actorClass: 'human' | 'agent' | 'system' | 'pipeline';
actorId?: string;
force?: boolean;
baseSourceHash?: string;
patch: {
kind?: GraphClaim['kind'];
nodes?: string[];
summary?: string | null;
links?: Partial<Record<GraphClaim['kind'], string[]>>;
};
}
The response always includes the policy decision:
interface UpdateGraphClaimResponse {
claim: GraphClaim;
accepted: boolean;
warnings: GraphIssue[];
errors: GraphIssue[];
persisted: 'overlay' | 'db-record' | 'source-file';
flushState?: 'not-needed' | 'pending' | 'flushed' | 'conflict';
}
For file-backed objects, the online API should write an overlay row first. The
live graph projection reads source claim + overlay. A flush worker
materializes overlays into Git-backed files and marks the overlay flushed only
after the source write succeeds.
MCP Contract
MCP mirrors the API. Tool names should be boring and direct:
list_graph_claims
get_graph_claim
update_graph_claim
validate_graph_claim
flush_graph_metadata
update_graph_claim takes the same actor envelope and force flag as the API.
Agent calls without force must obey soft graph rules. Agent calls with
force: true may save soft violations and must create audit/event evidence.
MCP must not expose a separate shape or bypass the API's policy gate.
flush_graph_metadata is an operator/tooling action, not an editor button. It
is useful for agents and scheduled jobs that materialize pending overlays into
Git-backed files.
Writer Registry
Each writer implements the same small interface:
interface GraphClaimWriter {
read(ref: BackingRef): Promise<GraphClaim>;
validate(claim: GraphClaim): Promise<GraphIssue[]>;
saveOverlay(claim: GraphClaim, actor: ActorEnvelope): Promise<GraphClaim>;
flush?(claim: GraphClaim): Promise<FlushResult>;
}
Initial writers:
| Writer | Owns |
|---|---|
markdown-frontmatter |
Wiki idea, journal, infra, and Markdown-backed behaviour notes. |
graph-ts |
Codebook implementation claims. |
inline-graph-block |
Small code-side @graph blocks. |
suite-yaml |
QA suite metadata and links. |
test-catalog |
Test metadata and suite/checks links. |
ops-db |
Ops ops_events and ops_anomalies; later mirrored by JobCache-owned Ops tables. |
UI Placement
- Wiki page detail: Markdown-backed
idea,journal,infra. - Codebook detail: code-backed
implementationclaims. - QA behaviour/suite/test detail:
behaviour,suite,test. - Ops anomaly/event pages: DB-backed
anomaly,event.
All surfaces use the same Graph Metadata Panel and the same API/MCP contract. Only the backing writer changes.