# 0036 — The Forkening: private fork, gh-carried distribution, node-local docs

Date: 2026-07-14 · Status: accepted (operator-ruled, THE-FORKENING grill) · Supersedes: ADR-0014 (permanent public Pages URL)

## Context

Development moves under a private GitHub org. Every distribution surface
assumed public reachability: `spt update` fetched unauthenticated
`github.com/.../releases/download` URLs, the install one-liner and adapter
bootstrap hardcoded the public Pages URL that ADR-0014 declared permanent,
and the rendered docs lived on public GitHub Pages. GitHub offers no
access-controlled Pages below Enterprise Cloud, so "private repos + public
Pages" cannot express "private docs" — and agents (the primary doc
consumers) need the docs regardless of repo visibility.

## Decision

1. **Repo topology.** Development home becomes `BigscreenVR/spt-bs-core`
   (private); `SaberMage/spt-core` remains a full passive mirror (receives
   every merge until a real second contributor appears; the repos may
   diverge later). Local checkouts flip `origin` to the dev home **before**
   build work starts; the SaberMage remote is named `mirror`; the merging
   agent pushes `mirror main --follow-tags` at merge time. Actions are
   disabled repo-settings-level on both SaberMage repos. The `project_id`
   change this causes is absorbed once by copy+renaming the tracked
   project-context dirs (no pin-file indirection — identity follows the
   real remote, ruled over a committed pin).
2. **Release channel.** Publishing moves to `BigscreenVR/spt-bs-releases`
   (private). Counter, signing key, and update-set format continue
   unchanged — the trust anchor, not the carrier, is the continuity.
   `SaberMage/spt-releases` is privated after a hard cutover in which the
   operator hand-updates every fielded node (org membership supplies each
   node's `gh` auth; no transition release).
3. **gh CLI is the mandated carrier.** Release discovery and asset download
   shell out to `gh` (deadline-wrapped subprocess, same pattern as
   `run_git`): private-repo asset download requires the API asset-id dance
   that `gh release download` already implements. Missing/unauthed gh fails
   loud with OS-specific install hints. Bootstrap install becomes
   `gh release download` + a self-install verb in the binary itself
   (no hosted install scripts; the downloaded exe keeps the `spt-*` name —
   Windows installer-detection). Signature verification is unchanged and
   carrier-independent.
4. **Docs go node-local.** Public Pages dies. Each release ships a
   platform-independent docs bundle (the built mdbook: HTML, `llms.txt`,
   `llms-full.txt`, raw `.md`, `manifest.schema.json`) as a **signed
   update-set asset**; apply lands it at `$SPT_HOME/docs` so docs always
   match the installed version. The daemon serves that dir on
   **`127.0.0.1:5474`** (loopback only, hyper — already in-tree via
   iroh/reqwest; GET-only, path-sanitized), preserving the published URL
   surface verbatim. `spt docs url` prints the resolved URL; bare
   `spt docs` opens the browser. The canonical docs URL is now
   `http://localhost:5474` — node-local, lapse-proof by construction.

## Consequences

- ADR-0014's permanent-URL promise transfers from a hosted origin to a
  local one; everything that baked the Pages URL (install one-liner,
  llms.txt pointers, adapter bootstrap) re-anchors on the local server or
  the gh channel.
- A node without `gh` (or without org membership) cannot install or update
  — accepted: the fleet is org-controlled by definition now.
- Docs asset failure must never fail a binary update (degrade loud,
  retry next fetch).
- Version-matched docs replace always-latest docs — an upgrade for agents,
  a regression only for reading future release notes, which remain in the
  release entries on the channel.
