ADR 0019 — private members render in the public projections without a dead repo link
- Status: accepted
- Date: 2026-06-13
- Relates: 0002 (the registry + manifest the flag lands in), 0005 (the generated views the flag is honored by), 0001 (the layer model that puts consumers in the topology)
A member can be in the family without its repo being public.
private: trueintopology/registry.jsonmarks a member whose repo 404s for everyone outside the org. The public projections honor it:topology/family.mdrenders the name unlinked, and the site (/systems,/systems/[name],/graph) shows aprivateaffordance instead of a repo link. The node still appears — consumers should be visible in the topology — andatlas topology syncstill fetches the member'satlas.jsonthrough the org CI token.
Context
#533 (visual-atlas, the first application
consumer) forces a small model decision. visual-atlas is a private repo, but
topology/registry.json feeds the generated constellation figure and the public site
(constellation.bitspark.xyz). We want to register it as a
downstream member — consumers belong in the topology, the same way draft members are named
before they are placed (ADR 0001). But a naive registration puts a node in the public
projections whose repo link 404s for everyone outside the org.
The dead-link vectors are the two public views generated from the registry:
topology/family.md— a committed Markdown view that renders each member as aname→repoMarkdown link. A private repo URL becomes a 404 link on GitHub for any outside reader.- the site —
/systems,/systems/[name], and/grapheach render the member'srepoas an external link; a private one is a dead link, and adocsdeep-link into a private target repo 404s the same way.
The figure (diagrams/constellation-*.svg) is not a link vector — it is a visual graph
with no hyperlinks, and it only draws members that have a hand-tuned layout position plus a
@bitspark/design wordmark. A private downstream consumer has neither, so it is already absent
from the figure; the only requirement there is that figure-coverage auditing must not demand
a position for it.
Three options were weighed:
- a
private: trueflag the figure/site honor — the node stays visible, but with no public repo link (or a "private" affordance); - exclude private members from the public site entirely, but keep them in the committed figure;
- accept the dead link (cheapest, ugliest).
Option 2 contradicts the reason we register consumers at all — they should be visible. Option 3 ships a known-broken link from the canonical public surface. Option 1 keeps the member visible and the surface honest.
Decision
Add an optional private: true flag to a registry member (topology/registry.json),
schema-validated by schema/atlas-registry.schema.json alongside draft. atlas topology sync
passes it through to topology/constellation.json, from which every generated view derives, so
the rule is a renderer rule, not a hand edit (the constellation is generated; ADR 0002/0005).
The flag is honored as:
family.md— the member's name renders unlinked (bold, with no link markup). The absence of a link is the affordance in this terse generated view.- the site —
/systems,/systems/[name], and/graphrender a muted "private repository" line and aprivatebadge in place of the repo link; adocsdeep-link whose target is private is suppressed. - the figure — unchanged: figure-coverage auditing exempts private members (as it does drafts), since a private member has no wordmark to lift and so could not be drawn even if placed.
atlas doctor— surfaces private members as an informational note, mirroring drafts.
The flag lives in the registry, not the member's atlas.json: whether a member is publicly
linkable is a projection-policy fact owned by the public hub that does the rendering, and it
must be readable without first fetching the private manifest. The repo URL stays in the
registry (it is just a string, and topology sync needs it to fetch the manifest via the org
CI token) — private: true governs only whether that URL is rendered as a public link.
Consequences
- visual-atlas can be registered (#533) as
draft: true,private: true,layer: downstream, with itsatlas.jsondeclaring build edges to ontos/horos — landing a visible node with no dead link. That registration is a follow-up; this ADR lands only the rule. - No committed artifact changes when the rule lands: with no private member in the registry
yet,
constellation.json,family.md, and the figure SVGs are byte-identical, soatlas doctorstays green. The mechanism is proved by unit tests (renderFamily, auditDiagram, the registry schema, the drift classifier), not by a placeholder member. - The repo URL is still committed in the public registry.
privatehides the link, not the fact that the member exists or the string of its URL; this is deliberate — the topology is a public map of who builds on whom, and a private repo's existence is not itself a secret. - A future member that needs its very name withheld is out of scope; that is a stronger policy (omission, not de-linking) and would be its own decision.