docs/adr/0004-public-real-history-private-ios.md

ADR 0004: Public Real History With Private iOS Repo

  • Status: Accepted
  • Date: 2026-07-05
  • Supersedes: the sanitized-snapshot public mirror (a GitHub Action that republished each master tree as a single github-actions[bot] commit).

Context

The public repository should carry Clankie's real commit history — real authors, dates, and messages — not a bot-authored snapshot. GitHub repository visibility is repository-wide: every reachable Git object in a public repo is public, so the private iOS app cannot live as a directory in the public repository, and its source cannot survive anywhere in the public history.

The iOS app has been renamed and rewritten across the project's life (AgentRoomiOSSwarmiOSClankieIOS native Swift → apps/mobile React Native → clankie-ios/), so iOS source is scattered across many historical paths, not just the current directory. The app consumes @volpestyle/clankie-contract as raw TypeScript source (ADR 0002), so wherever the app builds it needs that source reachable from the checkout.

Decision

Two repositories, no mirror:

  • Volpestyle/clankie (public): the monorepo itself, published with its real filtered history. Every historical iOS/mobile path family is removed from every commit with git filter-repo; commits that were purely iOS collapse to empty and are pruned, mixed commits keep their message and their non-iOS changes. Authorship, dates, and messages are preserved. Work is pushed here directly.
  • Volpestyle/clankies (private): the iOS app (official title Clankies). It carries the public monorepo as a git submodule and globs clankie-contract from inside it, so it consumes contract as raw source (ADR 0002 preserved) with no registry publish. Because the submodule is public, EAS needs no extra credentials. Its TestFlight/EAS release lane lives here.
  • Volpestyle/clanky-internal (private archive): the pre-split full-history checkout is retained, untouched, as the archive of record. Every dropped legacy commit — including the native-Swift iOS eras — remains recoverable there. It is no longer a working repo and no longer mirrors anything.

Before publishing, the filtered public history is secret-scanned in full (gitleaks) and audited to confirm no iOS/mobile path survives in any commit. The initial publish is a one-time force-push over the old snapshot history.

Publishing is direct: there is no export/scrub step on the normal path. The old Public Export workflow, scripts/prepare-public-export.sh, and docs/public-export.md are removed.

flowchart LR
  internal["clanky-internal<br/>private full-history archive"]
  public["Volpestyle/clankie<br/>public · real filtered history · no iOS"]
  clankies["Volpestyle/clankies<br/>private iOS app"]
  testflight["EAS production build<br/>TestFlight submit"]

  internal -. "one-time filter + audit + publish" .-> public
  internal -. "one-time extract" .-> clankies
  public -->|"git submodule → clankie-contract source"| clankies
  clankies --> testflight

Consequences

  • The public repo shows real authorship and history; no bot snapshot commits.
  • Private mobile source is absent from the public tree and every public commit.
  • The iOS ↔ clankie-contract dependency is preserved through a submodule, with no package publish and no change to contract's raw-source model.
  • Public history is lossy wherever iOS was involved (pure-iOS commits vanish, mixed commits show only their non-iOS half); the full record stays in clanky-internal.
  • Any future history rewrite (adding a path to strip, scrubbing a secret) must be re-filtered, re-scanned, and force-pushed, invalidating downstream clones.
  • Contributors and in-flight branches that straddle the split rebase their backend parts onto clankie's history and their iOS parts onto Clankies.