Version 0.1.0 — UI-agnostic core model
|
Important
|
The Rust |
| Invariant | Description |
|---|---|
Stable IDs |
Node IDs must be deterministic across imports |
Explainability |
Any non-manual tag must carry evidence |
Scenario Isolation |
Scenarios don’t mutate baseline; they reference it |
Reversibility |
Every scenario change can be expressed as a small delta |
UI-Agnostic |
Model doesn’t assume visual layout; layout is optional metadata |
Represents a repository as an addressable unit.
{
"kind": "Repo",
"id": "repo:gh:hyperpolymath/cadre-router",
"forge": "github",
"owner": "hyperpolymath",
"name": "cadre-router",
"default_branch": "main",
"visibility": "public",
"tags": ["router", "rescript"],
"estate": "estate:hyperpolymath",
"metadata": { "manifest_kind": "repo" },
"imports": {
"source": "local-scan",
"path_hint": "../src/cadre-router",
"imported_at": "2025-12-28T00:00:00Z"
}
}-
Forge-hosted:
repo:<forge>:<owner>/<name> -
Local-only:
repo:local:<canonical-path-hash>(with mapping preserved)
An estate is a top-level tenant: a coherent collection of repositories across one
or more forges. Hyperpolymath is "estate #1"; every node carries an estate so
the model generalises to other estates without changing node IDs (multi-tenant
later = filter by estate).
{
"kind": "Estate",
"id": "estate:hyperpolymath",
"name": "Hyperpolymath",
"forges": ["gh", "gl", "bb", "cb", "sr"],
"root_owner": "hyperpolymath"
}-
ID format:
estate:<slug>. -
GraphStorecarriesestates(the set) andestate(the current/primary id).
Reposystem models repositories and forges only. Sibling systems — aerie
(networks / the wider world) and ambientops (machines) — appear as external
seams: clearly-external boundary nodes that repos may point at, storing none of
those systems' internals. Seams are edge sinks: a repo may refers-to a
seam, but a seam never originates a structural edge.
{
"kind": "ExternalSeam",
"id": "seam:ambientops:ambientops",
"domain": "machine",
"system": "ambientops",
"name": "ambientops",
"uri": "git@github.com:hyperpolymath/ambientops.git",
"estate": "estate:hyperpolymath"
}-
ID format:
seam:<system>:<slug>. -
domain∈ {network,machine,service,org,other}. -
The
refers-toedge relation is the only relation permitted to target a seam.
All front-ends (web, GUI, TUI) consume one artifact: the estate-export envelope,
a strict superset of the GraphStore plus estates, seams, aspects,
slots/providers/bindings and plans. Produced by
reposystem export --format estate-json — one serializer, many transports.
{
"schema": "reposystem/estate-export@1",
"estate": { "id": "estate:hyperpolymath", "name": "Hyperpolymath", "forges": ["gh"] },
"estates": [ ... ],
"repos": [ ... ],
"components": [ ... ],
"seams": [ ... ],
"groups": [ ... ],
"edges": [ ... ],
"aspects": [ ... ], "annotations": [ ... ],
"slots": [ ... ], "providers": [ ... ], "bindings": [ ... ],
"plans": [ ... ]
}A component is a logical thing provided by or consumed by a repo.
|
Note
|
Components can be deferred to f2; schema exists now for forward-compatibility. |
{
"kind": "Component",
"id": "comp:repo:gh:hyperpolymath/cadre-router#router.core",
"repo_id": "repo:gh:hyperpolymath/cadre-router",
"name": "router.core",
"type": "router",
"version": "0.1.0",
"interfaces": ["router:v1"]
}Groups are first-class so that "systems" exist above repos.
{
"kind": "Group",
"id": "group:docs-stack",
"name": "Docs Stack",
"description": "SSG + newsroom + deploy",
"members": [
"repo:gh:hyperpolymath/my-ssg",
"repo:gh:hyperpolymath/my-newsroom"
]
}|
Important
|
Groups can overlap — a repo can belong to multiple groups. |
Edges represent relationships between nodes (repos and/or components).
{
"kind": "Edge",
"id": "edge:6a2f...",
"from": "repo:gh:hyperpolymath/my-newsroom",
"to": "repo:gh:hyperpolymath/my-ssg",
"rel": "uses",
"channel": "artifact",
"label": "publishes via",
"evidence": [
{
"type": "file",
"ref": ".github/workflows/deploy.yml",
"excerpt": "uses my-ssg build",
"confidence": 0.6
}
],
"meta": {
"created_by": "manual",
"created_at": "2025-12-28T00:00:00Z"
}
}| Channel | Description |
|---|---|
|
REST/GraphQL/gRPC endpoint consumption |
|
Build output, binary, package |
|
Configuration file or environment variable |
|
Container, process, service dependency |
|
Documentation, manual process handoff |
|
Detected but unclassified |
This is your "no hidden channels" surface.
{
"kind": "Aspect",
"id": "aspect:security",
"name": "Security",
"description": "Authn/authz, vuln exposure, secrets handling, trust boundaries"
}| Aspect | Description |
|---|---|
|
Authentication, authorization, vulnerability exposure |
|
Uptime, fault tolerance, recovery |
|
Code quality, test coverage, documentation |
|
Cross-platform, containerization, dependencies |
|
Speed, resource usage, scalability |
|
Logging, metrics, tracing |
|
User experience, accessibility |
|
Documentation quality and coverage |
|
Dependency provenance, SBOM, signing |
|
CI/CD, deployment, testing automation |
Applies to node OR edge.
{
"kind": "AspectAnnotation",
"id": "aa:9c13...",
"target": "edge:6a2f...",
"aspect_id": "aspect:security",
"weight": 3,
"polarity": "risk",
"reason": "Edge crosses trust boundary without auth",
"evidence": [
{ "type": "manual", "ref": "note", "confidence": 1.0 }
],
"source": {
"mode": "manual",
"who": "user",
"when": "2025-12-28T00:00:00Z",
"rule_id": null
}
}| Field | Values | Description |
|---|---|---|
|
0–3 |
Coarse importance (0=informational, 3=critical) |
|
|
Is this a weakness, strength, or observation? |
|
|
How was this annotation created? |
|
Important
|
If source.mode is inferred, must include rule_id and evidence[].
|
A scenario is a delta over the baseline graph, not a rewrite.
{
"kind": "Scenario",
"id": "scenario:ecosystem-secure",
"name": "Ecosystem Secure",
"base": "scenario:baseline",
"description": "Prefer ecosystem providers for security-critical slots; fallback to local",
"created_at": "2025-12-28T00:00:00Z"
}A scenario holds a list of operations. For v1, keep simple:
-
add_edge -
remove_edge -
add_annotation -
remove_annotation -
set_group_membership(optional)
{
"kind": "ChangeSet",
"scenario_id": "scenario:ecosystem-secure",
"ops": [
{
"op": "add_edge",
"edge": {
"from": "repo:gh:hyperpolymath/proj-a",
"to": "repo:gh:hyperpolymath/cerro-torre",
"rel": "uses",
"channel": "runtime",
"label": "container runtime (ecosystem)",
"evidence": [
{ "type": "manual", "ref": "scenario decision", "confidence": 1.0 }
]
}
},
{
"op": "remove_edge",
"edge_id": "edge:oldhash..."
}
]
}This supports:
-
"local default" scenario = no ops
-
A/B = two scenarios differing by ops
-
fallback = scenario that adds backup edges (priority can come later)
Kept separate so UI can store node positions without polluting the model.
reposystem/
├── graph.json # Repos, Groups, Edges
├── aspects.json # Aspect definitions + annotations
├── scenarios/
│ ├── baseline.json
│ ├── ecosystem-secure.json
│ └── fallback-safe.json
└── layouts/
└── main-canvas.json-
Every object has:
kind,id -
Every edge references existing nodes
-
Every annotation references existing target (node or edge)
-
Every inferred annotation has:
-
source.rule_id -
evidence[]
-
-
Scenario ops must validate against base graph
Without changing v1:
-
Slots/providers become Components
-
Substitution becomes edges:
-
repo → component(provides) -
repo → component(consumes) -
Scenario ops change which provider satisfies a consumer slot
-
Ship f1 without any of this.
For STATE.scm integration, the graph can be represented as:
(define-module (reposystem graph)
#:export (make-repo make-edge make-group))
(define (make-repo id forge owner name)
`((kind . "Repo")
(id . ,id)
(forge . ,forge)
(owner . ,owner)
(name . ,name)))
(define (make-edge from to rel channel)
`((kind . "Edge")
(from . ,from)
(to . ,to)
(rel . ,rel)
(channel . ,channel)))