2026 04 19 Longevity Sprint

GloriousFlywheel Longevity Sprint Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Execute an 8-week longevity sprint that hardens the GloriousFlywheel substrate, benchmarks it against real workloads, proves cross-org heterogeneous access from a single on-prem stack, and ships a clear FOSS core + forge adapter adoption story.

Architecture: GloriousFlywheel is a recursive IaC platform: Nix + Bazel + OpenTofu + K8s. The sprint restructures it as 4 layers: (1) FOSS core substrate (cache, runner images, operator tooling, cluster substrate), (2) forge adapters (GitHub ARC primary, GitLab compat, Forgejo proof), (3) optional managed control plane (SaaS layer above FOSS core), (4) compatibility kit (legacy bzlmod overlay consumers). One on-prem honey cluster serves hundreds of repos across orgs and VCS platforms.

Tech Stack: Nix flakes, Bazel (bzlmod), OpenTofu, Kubernetes (RKE2 on honey), GitHub ARC 0.14.0, Attic (Nix binary cache), SvelteKit 5 + Skeleton v4, Vitest, Just, pnpm, GHCR, FlakeHub

Tracker References:

  • Initiative: GloriousFlywheel Runner Platform Reset
  • GitHub: #210, #211, #212, #213, #215
  • Linear: TIN-125, TIN-126, TIN-129, TIN-148, TIN-149
  • Sprint plan: docs/research/gloriousflywheel-longevity-two-month-sprint-plan-2026-04-19.md
  • Architecture note: docs/research/gloriousflywheel-saas-foss-adopt-and-mod-future-shape-2026-04-19.md

Canary repos: XoxdWM (tinyland-inc), MassageIthaca (tinyland-inc), cmux (Jesssullivan — cross-org proof)


Parallelization Map

Phase 1 (Weeks 1-2)
├── Task Group A: Architecture & Boundary Docs    ──┐
├── Task Group B: Tracker Hygiene & Alignment      │ independent
└── Task Group C: Release Baseline                 ──┘

Phase 2 (Weeks 3-4)
├── Task Group D: Benchmark Harness          (depends on A)
├── Task Group E: Substrate Hardening        (independent)
└── Task Group F: Canary Enrollment Prep     (depends on A)

Phase 3 (Weeks 5-6)
├── Task Group G: Adoption Surfaces          (depends on A, D)
└── Task Group H: Cross-Org Canary Proofs    (depends on D, F)

Phase 4 (Weeks 7-8)
├── Task Group I: Release & Publication      (depends on C, D)
└── Task Group J: Control Plane & Retro      (depends on A, G)

Tasks within the same phase and without dependency arrows can be dispatched to parallel subagents.


File Structure

New files (create)

File Responsibility
docs/architecture/platform-layers.md Public 4-layer architecture page
docs/architecture/forge-adapter-matrix.md Forge support matrix with caveats
docs/architecture/enrollment-model.md Multi-org enrollment in operator terms
docs/guides/adoption-quickstart.md Adopter-facing “deploy core → enroll forge → choose pool”
docs/guides/canary-enrollment.md How to enroll a repo as canary
docs/compatibility-kit.md Compat kit decision note and scope
docs/release-baseline.md Tag plan, changelog expectations, artifact list
scripts/benchmark/runner-benchmark.sh Benchmark harness: cold start, queue, cache
scripts/benchmark/parse-results.sh Parse benchmark JSON into scorecard
scripts/benchmark/canary-enrollment-check.sh Validate a repo’s enrollment state
.github/workflows/benchmark.yml Benchmark CI workflow (manual dispatch)
tofu/stacks/arc-runners/examples/minimal/main.tf Minimal ARC adoption example
tofu/stacks/arc-runners/examples/minimal/variables.tf Example variables
tofu/stacks/arc-runners/examples/minimal/README.md Example usage doc
tests/benchmark/validate-scorecard.sh Regression: scorecard format validation
tests/integration/canary-smoke.sh Regression: canary repo can reach runners
docs/research/gloriousflywheel-sprint-retrospective-2026-06.md Sprint retro (Phase 4)

Modified files

File Change
docs/index.md Link to platform-layers.md as primary architecture
docs/roadmap.md Update with phase milestones and completion dates
docs/rfcs.md Add sprint-related RFC lanes
docs/cleanup-program.md Link sprint plan tasks to workstreams
docs/current-state.md Update with benchmark results (Phase 2+)
docs/getting-started-guide.md Rewrite around 4-layer model, not overlay
CONTRIBUTING.md Update adoption path language
CHANGELOG.md Add sprint entries as work lands
.github/workflows/test-arc-runners.yml Add timing instrumentation
.github/workflows/platform-proof.yml Add cross-org canary proof jobs
tofu/stacks/arc-runners/honey.tfvars Add canary repo scale sets
Justfile Add benchmark and canary recipes

Task Group A: Architecture & Boundary Docs (Phase 1)

Task 1: Public Platform Layers Architecture Page

Files:

  • Create: docs/architecture/platform-layers.md

  • Modify: docs/index.md

  • Step 1: Write the platform layers page

---
title: Platform Architecture
order: 1
---

# Platform Architecture

GloriousFlywheel is a self-hosted runner and cache platform organized in four
layers.

## Layer 1: FOSS Core Substrate

The shared infrastructure that works without any managed service dependency.

Components:

- **Attic**: Multi-tenant Nix binary cache backed by S3-compatible storage
- **Bazel remote cache**: Optional build acceleration via remote cache endpoint
- **Runner images**: docker, nix, nix-heavy, future hardware classes (gpu, kvm)
- **Cluster substrate**: Kubernetes + OpenTofu deployment surfaces
- **Dashboard**: SvelteKit operator UX with WebAuthn auth
- **Operator tooling**: Just recipes, health checks, MCP server

This layer deploys on any Kubernetes cluster. The reference deployment runs on
an on-prem RKE2 cluster (`honey`) with Tailscale overlay networking.

## Layer 2: Forge Adapters

Each forge has its own runner registration, scope model, token handling, and
event routing. The shared substrate stays the same; the adapter handles
forge-specific enrollment.

| Forge            | Adapter        | Scope Model              | Status        |
| ---------------- | -------------- | ------------------------ | ------------- |
| GitHub           | ARC scale sets | repo, org, enterprise    | Primary       |
| GitLab           | GitLab Runner  | project, group, instance | Compatibility |
| Forgejo/Codeberg | act_runner     | repo, org, account       | Proof path    |

## Layer 3: Managed Control Plane (Future)

Optional SaaS layer above the FOSS core for fleet enrollment UX, tenant and
pool management, usage reporting, cache and runner observability, policy packs,
and support diagnostics.

Not required for deploying the base platform, bootstrapping a cluster, using
Attic or Bazel cache, or running GitHub ARC on a self-hosted cluster.

## Layer 4: Compatibility Kit

Legacy Bzlmod overlay patterns, `local_path_override` development flows,
downstream merge-and-modify examples, and transitional consumers.

This is not the primary adoption path. See [Adoption Quickstart](../guides/adoption-quickstart.md)
for the recommended onboarding flow.

## Multi-Org Enrollment Model

Runner enrollment is modeled along four dimensions:

1. **Forge scope**: GitHub repo/org/enterprise, GitLab project/group/instance,
   Forgejo repo/org/account
2. **Operator tenant**: team, org, enterprise, managed customer
3. **Execution pool**: docker, nix, nix-heavy, gpu, kvm
4. **Cache/state plane**: Attic tenant/cache view, Bazel cache namespace, state
   backend authority

## Adopting GloriousFlywheel

1. Deploy the core substrate on your cluster
2. Enroll your forge using the appropriate adapter
3. Choose shared or dedicated runner pools
4. Attach caches (Attic for Nix, Bazel remote for Bazel)
5. Customize runner images, placement, and policy
  • Step 2: Update docs/index.md to link the new page

In docs/index.md, replace any reference to overlay-first architecture with:

## Architecture

GloriousFlywheel is organized as [FOSS core substrate + forge adapters](architecture/platform-layers.md).

The shared thing is caches, runner images, operator tooling, and cluster
substrate. The forge-specific thing is enrollment, tokens, scope, and event
routing.
  • Step 3: Validate markdown

Run: just check or at minimum:

npx markdownlint-cli2 docs/architecture/platform-layers.md docs/index.md

Expected: No errors

  • Step 4: Commit
git add docs/architecture/platform-layers.md docs/index.md
git commit -m "docs(arch): add public 4-layer platform architecture page

Refs: #211, TIN-129"

Completion metric: docs/architecture/platform-layers.md exists, passes markdownlint, and is linked from docs/index.md.

Regression gate: grep -q 'platform-layers' docs/index.md


Task 2: Forge Adapter Matrix

Files:

  • Create: docs/architecture/forge-adapter-matrix.md

  • Modify: docs/rfcs.md

  • Step 1: Write the forge adapter matrix

---
title: Forge Adapter Matrix
order: 2
---

# Forge Adapter Matrix

Snapshot date: 2026-04-19

This document defines which forges GloriousFlywheel supports, at what maturity
level, and with what caveats.

## Matrix

| Forge            | Adapter            | Runner Registration                      | Cache Integration    | CI Parity         | Status        |
| ---------------- | ------------------ | ---------------------------------------- | -------------------- | ----------------- | ------------- |
| GitHub           | ARC scale sets     | GitHub App + org scope                   | Attic + Bazel remote | Full              | Primary       |
| GitLab           | gitlab-runner Helm | Registration token + group/project scope | Attic (partial)      | Validation only   | Compatibility |
| Forgejo/Codeberg | act_runner         | Instance token + org/repo scope          | Attic (planned)      | Single proof path | Proof         |

## GitHub Adapter (Primary)

- **Registration**: GitHub App installed at org level, ARC controller + scale sets
- **Scale sets**: tinyland-nix, tinyland-docker, tinyland-dind, tinyland-nix-heavy
- **Scope**: org-wide or per-repo via `github_config_url`
- **Cache**: Attic endpoint injected via pod env, Bazel remote cache optional
- **Status**: Production on `honey` cluster

## GitLab Adapter (Compatibility)

- **Registration**: GitLab Runner via Helm, registration token per group/project
- **Scope**: Instance, group, or project
- **Cache**: Attic integration partial — GitLab CI cache mechanism differs
- **Status**: Helm chart exists, not deployed on `honey`. GitLab serves as
  OpenTofu state backend only.
- **Caveats**: No active fleet on the live cluster. Compatibility means the
  tofu module and Helm config exist and validate, not that a live GitLab runner
  fleet runs on `honey`.

## Forgejo/Codeberg Adapter (Proof Path)

- **Registration**: act_runner with instance token
- **Scope**: Account, org, or repo
- **Cache**: Attic planned but not tested
- **Status**: `.forgejo/` workflow directory exists in repo. No live runner
  fleet. One honest proof path planned for the longevity sprint.
- **Caveats**: Forgejo Actions is compatible with GitHub Actions workflow
  syntax but uses its own runner registration protocol. Runners do not need a
  public IP.

## What Is Shared Across Forges

- Attic Nix binary cache (same store, per-tenant views)
- Runner images (docker, nix, nix-heavy)
- Cluster substrate (Kubernetes, OpenTofu modules)
- Operator tooling (dashboard, MCP server, Just recipes)
- Policy model (pool placement, resource limits, scheduling)

## What Is Forge-Specific

- Runner registration protocol and tokens
- Scope model (repo/org/enterprise vs project/group/instance)
- Event routing (webhooks vs long-poll vs polling)
- CI syntax compatibility (Actions YAML vs GitLab CI YAML)
  • Step 2: Link from rfcs.md

Add to docs/rfcs.md under “In Effect”:

- forge adapter boundaries are documented in
  [Forge Adapter Matrix](architecture/forge-adapter-matrix.md)
  • Step 3: Validate and commit
npx markdownlint-cli2 docs/architecture/forge-adapter-matrix.md
git add docs/architecture/forge-adapter-matrix.md docs/rfcs.md
git commit -m "docs(arch): add forge adapter matrix with maturity levels

Refs: #211"

Completion metric: Forge matrix exists with explicit maturity levels and caveats per forge.

Regression gate: grep -q 'Primary' docs/architecture/forge-adapter-matrix.md && grep -q 'Compatibility' docs/architecture/forge-adapter-matrix.md && grep -q 'Proof' docs/architecture/forge-adapter-matrix.md


Task 3: Compatibility Kit Decision Note

Files:

  • Create: docs/compatibility-kit.md

  • Step 1: Write the compatibility kit decision

---
title: Compatibility Kit
order: 8
---

# Compatibility Kit

Snapshot date: 2026-04-19

## Decision

The Bzlmod overlay compatibility kit lives in `Jesssullivan/bzl-cross-repo`,
not in the main GloriousFlywheel repository.

GloriousFlywheel retains the `attic-iac` module name in `MODULE.bazel` for
internal build structure and backward compatibility. It does not publish the
full platform as a Bazel Central Registry module during this sprint.

## What The Kit Covers

- Legacy Bzlmod overlay patterns for downstream consumers
- `local_path_override` development flows
- Downstream merge-and-modify examples
- Transitional consumers such as `tinyland-infra`

## What It Does Not Cover

- Primary onboarding for new adopters (use the adoption quickstart instead)
- Multi-forge enrollment (use forge adapters)
- Cache or runner configuration (use the core substrate docs)

## Why

The overlay-first adoption story created too much conceptual flattening.
It made local overrides and merge mechanics feel more central than the runner,
cache, and cluster truth. The clearer story is: deploy core, enroll forge,
choose pools, attach caches, customize policy.

## Migration Path

Existing overlay consumers should:

1. Continue using `bzl-cross-repo` for Bzlmod composition
2. Migrate configuration to direct OpenTofu variable overrides
3. Treat the compatibility kit as transitional, not permanent

## Related

- [Platform Architecture](architecture/platform-layers.md)
- [Adoption Quickstart](guides/adoption-quickstart.md)
  • Step 2: Commit
git add docs/compatibility-kit.md
git commit -m "docs: add compatibility kit decision note

Bzlmod overlay compatibility lives in bzl-cross-repo. GloriousFlywheel
keeps attic-iac module name for internal use only.

Refs: #211"

Completion metric: Decision is documented: compat kit lives in bzl-cross-repo, not in main repo.

Regression gate: grep -q 'bzl-cross-repo' docs/compatibility-kit.md


Task 4: Multi-Org Enrollment Model

Files:

  • Create: docs/architecture/enrollment-model.md

  • Step 1: Write the enrollment model

---
title: Enrollment Model
order: 3
---

# Multi-Org Enrollment Model

Runner enrollment is modeled along four dimensions. This replaces the older
flattened model based on runner labels and overlay language.

## Dimension 1: Forge Scope

Where the runner is registered and what repos can reach it.

| Forge   | Scopes                               |
| ------- | ------------------------------------ |
| GitHub  | Repository, Organization, Enterprise |
| GitLab  | Project, Group, Instance             |
| Forgejo | Repository, Organization, Account    |

## Dimension 2: Operator Tenant

Who operates and pays for the runner pool.

| Tenant Type      | Description                                 |
| ---------------- | ------------------------------------------- |
| Team             | Single team within an org                   |
| Organization     | Full org (e.g., tinyland-inc)               |
| Enterprise       | Multiple orgs under one billing entity      |
| Managed Customer | External customer on the SaaS control plane |

## Dimension 3: Execution Pool

What kind of runner the workload gets.

| Pool      | CPU/Mem      | Use Case                      |
| --------- | ------------ | ----------------------------- |
| docker    | 2 CPU / 4Gi  | General CI, Docker builds     |
| nix       | 4 CPU / 8Gi  | Nix builds, flake checks      |
| nix-heavy | 8 CPU / 16Gi | Rust + Nix, heavy compilation |
| gpu       | TBD          | ML training, CUDA builds      |
| kvm       | TBD          | VM-based testing, kernel work |

## Dimension 4: Cache and State Plane

What cache and state backend the workload uses.

| Component          | Scope                        | Multi-Tenant              |
| ------------------ | ---------------------------- | ------------------------- |
| Attic              | Per-cache view, global dedup | Yes (tenant isolation)    |
| Bazel remote cache | Per-namespace                | Yes (namespace isolation) |
| State backend      | Per-stack                    | No (operator-owned)       |

## Enrollment Flow

1. Operator deploys core substrate (Task Group E)
2. Operator creates a forge adapter config (GitHub App, GitLab token, etc.)
3. Operator defines scale sets with pool type and resource limits
4. Operator configures cache endpoints in runner pod environment
5. Repos use the runner labels (`tinyland-nix`, etc.) in workflow files

## Cross-Org Enrollment

A single GloriousFlywheel cluster can serve multiple orgs by:

- Installing the GitHub App on each org
- Creating separate scale sets per org (or sharing org-wide sets)
- Using Attic tenant views for cache isolation
- Using Bazel cache namespaces for build isolation

This is the current model on `honey`: tinyland-inc org-wide sets serve all
tinyland-inc repos, and cross-org canaries (e.g., cmux under Jesssullivan)
get dedicated scale sets.
  • Step 2: Commit
git add docs/architecture/enrollment-model.md
git commit -m "docs(arch): add multi-org enrollment model

Four dimensions: forge scope, operator tenant, execution pool,
cache/state plane. Replaces flattened label-based model.

Refs: #211, TIN-129"

Completion metric: Enrollment model documents 4 dimensions with concrete tables.

Regression gate: grep -c 'Dimension' docs/architecture/enrollment-model.md returns 4.


Task Group B: Tracker Hygiene (Phase 1)

Task 5: Create Linear Initiative, Milestones, and Phase Issues

Files: None (tracker-only)

  • Step 1: Create Linear initiative

Create initiative “GloriousFlywheel Longevity Sprint (Apr–Jun 2026)” with:

  • Status: Active

  • Target date: 2026-06-14

  • Owner: Jess Sullivan

  • Parent: link to “GloriousFlywheel Runner Platform Reset” if possible

  • Description: 8-week sprint. Core substrate hardening, benchmark evidence, cross-org canary proofs, 4-layer architecture.

  • Step 2: Create Linear project with milestones

Create project “Longevity Sprint Execution” with 4 milestones:

Milestone Name Target Date
1 Phase 1: Freeze Architecture 2026-05-03
2 Phase 2: Substrate & Benchmarks 2026-05-17
3 Phase 3: Adoption & Canaries 2026-05-31
4 Phase 4: Release & Retro 2026-06-14
  • Step 3: Create phase-mapped Linear issues

Create issues for work not yet in Linear:

Title Milestone Priority Labels
Write public 4-layer architecture page Phase 1 High docs, foss
Write forge adapter matrix Phase 1 High docs, foss
Decide and document compatibility kit home Phase 1 High docs
Write release baseline doc Phase 1 Normal release
Build benchmark harness script Phase 2 Urgent Feature
Add timing instrumentation to test-arc-runners Phase 2 High Feature
Publish benchmark scorecard Phase 2 High docs
Enroll XoxdWM as canary with validation Phase 3 High foss
Enroll MassageIthaca as canary Phase 3 Normal foss
Prove cmux cross-org enrollment Phase 3 High foss
Write adoption quickstart guide Phase 3 High docs, foss
Forgejo proof-path: one honest adapter test Phase 3 Normal foss
Ship first tagged release with changelog Phase 4 High release
Write sprint retrospective Phase 4 Normal docs
  • Step 4: Cross-link existing issues to milestones

Update existing Linear issues with milestone mapping:

  • TIN-125 → Phase 2

  • TIN-126 → Phase 2

  • TIN-148 → Phase 2

  • TIN-129 → Phase 1 + Phase 3

  • TIN-149 → Phase 4

  • Step 5: Create matching GitHub milestone

cd ~/git/GloriousFlywheel
gh api repos/tinyland-inc/GloriousFlywheel/milestones -f title="Longevity Sprint" -f due_on="2026-06-14T00:00:00Z" -f description="8-week longevity sprint: substrate hardening, benchmarks, cross-org canary proofs, 4-layer architecture"

Then assign open issues to it:

for issue in 210 211 212 213 215; do
  gh issue edit $issue --milestone "Longevity Sprint"
done

Completion metric: Linear initiative exists with 4 milestones. GitHub milestone exists with #210-215 assigned.

Regression gate: gh api repos/tinyland-inc/GloriousFlywheel/milestones --jq '.[].title' | grep -q 'Longevity'


Task Group C: Release Baseline (Phase 1)

Task 6: Release Baseline Document

Files:

  • Create: docs/release-baseline.md

  • Step 1: Write the release baseline

---
title: Release Baseline
order: 7
---

# Release Baseline

Snapshot date: 2026-04-19

## Tag Convention

Use annotated semantic version tags: `v0.1.0`, `v0.2.0`, etc.

The first tagged release is `v0.1.0` — the longevity sprint baseline.

Tags are created on `main` after PR merge. No pre-release tags during this
sprint.

## Changelog Discipline

`CHANGELOG.md` uses Keep a Changelog format with sections:
Added, Changed, Fixed, Removed, Security.

Every PR that lands on `main` should have a changelog entry. The entry goes
in the `[Unreleased]` section and moves to a versioned section at tag time.

## Published Artifacts

| Artifact                     | Registry                                       | Cadence                                    |
| ---------------------------- | ---------------------------------------------- | ------------------------------------------ |
| `attic-server` OCI image     | GHCR (`ghcr.io/tinyland-inc/attic-server`)     | Per release tag                            |
| `attic-gc` OCI image         | GHCR (`ghcr.io/tinyland-inc/attic-gc`)         | Per release tag                            |
| `runner-dashboard` OCI image | GHCR (`ghcr.io/tinyland-inc/runner-dashboard`) | Per release tag                            |
| Nix flake                    | FlakeHub                                       | Per release tag (publication, not runtime) |
| OpenTofu modules             | Git refs in this repo                          | Per release tag                            |

## What Is Not Published

- Bazel Central Registry module (internal-only this sprint)
- npm packages (app is deployed, not published)
- Helm charts (consumed via OpenTofu Helm provider, not standalone)

## Release Checklist

1. All `just check` passes on `main`
2. Benchmark scorecard is current
3. CHANGELOG.md has entries for all merged work
4. Create annotated tag: `git tag -a v0.X.0 -m "Release v0.X.0"`
5. Push tag: `git push origin v0.X.0`
6. GitHub release workflow triggers GHCR image builds
7. FlakeHub publish workflow triggers flake publication
  • Step 2: Commit
git add docs/release-baseline.md
git commit -m "docs: add release baseline for longevity sprint

Defines tag convention, changelog discipline, published artifacts,
and release checklist.

Refs: #215, TIN-149"

Completion metric: Release baseline defines tag scheme, changelog format, and artifact list.

Regression gate: grep -q 'v0.1.0' docs/release-baseline.md


Task Group D: Benchmark Harness (Phase 2)

Task 7: Benchmark Script

Files:

  • Create: scripts/benchmark/runner-benchmark.sh

  • Create: scripts/benchmark/parse-results.sh

  • Step 1: Write the benchmark harness

#!/usr/bin/env bash
# scripts/benchmark/runner-benchmark.sh
# Benchmark GloriousFlywheel runners: cold start, queue latency, cache behavior
# Usage: ./scripts/benchmark/runner-benchmark.sh <runner-label> <workload>
# Output: JSON to stdout, human summary to stderr

set -euo pipefail

RUNNER_LABEL="${1:?Usage: runner-benchmark.sh <runner-label> <workload>}"
WORKLOAD="${2:?Workloads: nix-build, docker-build, flake-check, bazel-test}"

RESULTS_DIR="${RESULTS_DIR:-/tmp/gf-benchmark}"
mkdir -p "$RESULTS_DIR"

TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
RESULT_FILE="$RESULTS_DIR/${RUNNER_LABEL}-${WORKLOAD}-$(date +%s).json"

# Record queue submit time
QUEUE_START=$(date +%s%N)

case "$WORKLOAD" in
  nix-build)
    # Time a real nix build
    BUILD_START=$(date +%s%N)
    nix build .#runner-dashboard-image --no-link 2>/dev/null
    BUILD_END=$(date +%s%N)
    ;;
  flake-check)
    BUILD_START=$(date +%s%N)
    nix flake check 2>/dev/null
    BUILD_END=$(date +%s%N)
    ;;
  docker-build)
    BUILD_START=$(date +%s%N)
    docker build -f app/Dockerfile -t benchmark-test . 2>/dev/null
    BUILD_END=$(date +%s%N)
    ;;
  bazel-test)
    BUILD_START=$(date +%s%N)
    bazel test //... 2>/dev/null
    BUILD_END=$(date +%s%N)
    ;;
  *)
    echo "Unknown workload: $WORKLOAD" >&2
    exit 1
    ;;
esac

QUEUE_END=$(date +%s%N)

# Calculate durations in milliseconds
TOTAL_MS=$(( (QUEUE_END - QUEUE_START) / 1000000 ))
BUILD_MS=$(( (BUILD_END - BUILD_START) / 1000000 ))
OVERHEAD_MS=$(( TOTAL_MS - BUILD_MS ))

# Check cache state
ATTIC_HIT="unknown"
if command -v attic &>/dev/null && [ -n "${ATTIC_SERVER:-}" ]; then
  ATTIC_HIT="available"
fi

BAZEL_CACHE_HIT="unknown"
if [ -n "${BAZEL_REMOTE_CACHE:-}" ]; then
  BAZEL_CACHE_HIT="available"
fi

# Write JSON result
cat > "$RESULT_FILE" <<ENDJSON
{
  "timestamp": "$TIMESTAMP",
  "runner_label": "$RUNNER_LABEL",
  "workload": "$WORKLOAD",
  "total_ms": $TOTAL_MS,
  "build_ms": $BUILD_MS,
  "overhead_ms": $OVERHEAD_MS,
  "attic_cache": "$ATTIC_HIT",
  "bazel_cache": "$BAZEL_CACHE_HIT",
  "hostname": "$(hostname)",
  "nix_store_size_mb": $(du -sm /nix/store 2>/dev/null | cut -f1 || echo 0)
}
ENDJSON

# Human summary to stderr
cat >&2 <<SUMMARY
=== Benchmark: $RUNNER_LABEL / $WORKLOAD ===
Total:    ${TOTAL_MS}ms
Build:    ${BUILD_MS}ms
Overhead: ${OVERHEAD_MS}ms
Attic:    $ATTIC_HIT
Bazel:    $BAZEL_CACHE_HIT
Result:   $RESULT_FILE
SUMMARY

cat "$RESULT_FILE"
  • Step 2: Write the results parser
#!/usr/bin/env bash
# scripts/benchmark/parse-results.sh
# Parse benchmark JSON files into a markdown scorecard
# Usage: ./scripts/benchmark/parse-results.sh <results-dir>

set -euo pipefail

RESULTS_DIR="${1:?Usage: parse-results.sh <results-dir>}"

echo "# Benchmark Scorecard"
echo ""
echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo ""
echo "| Runner | Workload | Total (ms) | Build (ms) | Overhead (ms) | Attic | Bazel Cache |"
echo "|--------|----------|-----------|-----------|--------------|-------|-------------|"

for f in "$RESULTS_DIR"/*.json; do
  [ -f "$f" ] || continue
  RUNNER=$(jq -r '.runner_label' "$f")
  WORKLOAD=$(jq -r '.workload' "$f")
  TOTAL=$(jq -r '.total_ms' "$f")
  BUILD=$(jq -r '.build_ms' "$f")
  OVERHEAD=$(jq -r '.overhead_ms' "$f")
  ATTIC=$(jq -r '.attic_cache' "$f")
  BAZEL=$(jq -r '.bazel_cache' "$f")
  echo "| $RUNNER | $WORKLOAD | $TOTAL | $BUILD | $OVERHEAD | $ATTIC | $BAZEL |"
done
  • Step 3: Make scripts executable and validate
chmod +x scripts/benchmark/runner-benchmark.sh scripts/benchmark/parse-results.sh
shellcheck scripts/benchmark/runner-benchmark.sh scripts/benchmark/parse-results.sh

Expected: No errors (or only minor warnings about jq parsing)

  • Step 4: Commit
git add scripts/benchmark/
git commit -m "feat(bench): add runner benchmark harness and scorecard parser

Measures cold start, build time, overhead, and cache state per
runner label and workload type. Outputs JSON and markdown scorecard.

Refs: #212"

Completion metric: scripts/benchmark/runner-benchmark.sh is executable and passes shellcheck.

Regression gate: shellcheck scripts/benchmark/runner-benchmark.sh && echo PASS


Task 8: Benchmark CI Workflow

Files:

  • Create: .github/workflows/benchmark.yml

  • Step 1: Write the benchmark workflow

# .github/workflows/benchmark.yml
name: Runner Benchmarks
on:
  workflow_dispatch:
    inputs:
      workload:
        description: "Workload to benchmark"
        required: true
        type: choice
        options:
          - nix-build
          - flake-check
          - bazel-test
          - all
      runner_label:
        description: "Runner label to test"
        required: true
        type: choice
        options:
          - tinyland-nix
          - tinyland-nix-heavy
          - tinyland-docker
          - ubuntu-latest

jobs:
  benchmark:
    runs-on: ${{ inputs.runner_label }}
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4

      - name: Record queue latency
        id: queue
        run: |
          echo "queue_start=$(date +%s%N)" >> "$GITHUB_OUTPUT"

      - name: Run benchmark
        env:
          ATTIC_SERVER: ${{ vars.ATTIC_SERVER || '' }}
          BAZEL_REMOTE_CACHE: ${{ vars.BAZEL_REMOTE_CACHE || '' }}
          RESULTS_DIR: /tmp/gf-benchmark
        run: |
          if [ "${{ inputs.workload }}" = "all" ]; then
            for wl in nix-build flake-check bazel-test; do
              ./scripts/benchmark/runner-benchmark.sh "${{ inputs.runner_label }}" "$wl" || true
            done
          else
            ./scripts/benchmark/runner-benchmark.sh "${{ inputs.runner_label }}" "${{ inputs.workload }}"
          fi

      - name: Generate scorecard
        run: |
          ./scripts/benchmark/parse-results.sh /tmp/gf-benchmark > /tmp/gf-benchmark/scorecard.md
          cat /tmp/gf-benchmark/scorecard.md >> "$GITHUB_STEP_SUMMARY"

      - name: Upload results
        uses: actions/upload-artifact@v4
        with:
          name: benchmark-${{ inputs.runner_label }}-${{ inputs.workload }}-${{ github.run_id }}
          path: /tmp/gf-benchmark/
          retention-days: 90
  • Step 2: Commit
git add .github/workflows/benchmark.yml
git commit -m "ci(bench): add manual benchmark dispatch workflow

Runs benchmark harness on specified runner label and workload.
Generates scorecard as step summary and uploads artifacts.

Refs: #212"

Completion metric: Benchmark workflow exists with manual dispatch and scorecard output.

Regression gate: yq '.jobs.benchmark.steps | length' .github/workflows/benchmark.yml returns at least 4.


Task 9: Add Timing Instrumentation to test-arc-runners

Files:

  • Modify: .github/workflows/test-arc-runners.yml

  • Step 1: Read current workflow

Read .github/workflows/test-arc-runners.yml to understand the existing structure.

  • Step 2: Add timing to each job

Add to each job’s first step:

- name: Record start time
  id: timing
  run: echo "start=$(date +%s%N)" >> "$GITHUB_OUTPUT"

Add to each job’s last step:

- name: Report timing
  if: always()
  run: |
    END=$(date +%s%N)
    START=${{ steps.timing.outputs.start }}
    DURATION_MS=$(( (END - START) / 1000000 ))
    echo "### ${{ github.job }} completed in ${DURATION_MS}ms" >> "$GITHUB_STEP_SUMMARY"
  • Step 3: Validate workflow syntax
yq '.' .github/workflows/test-arc-runners.yml > /dev/null

Expected: No errors

  • Step 4: Commit
git add .github/workflows/test-arc-runners.yml
git commit -m "ci(runners): add timing instrumentation to soak tests

Each job now reports duration in step summary for benchmark tracking.

Refs: #212"

Completion metric: Each job in test-arc-runners.yml has start/end timing.

Regression gate: grep -c 'Record start time' .github/workflows/test-arc-runners.yml matches job count.


Task Group E: Substrate Hardening (Phase 2)

Task 10: Workspace Hygiene Script

Files:

  • Create: scripts/runner-workspace-cleanup.sh

  • Modify: Justfile

  • Step 1: Write the cleanup script

#!/usr/bin/env bash
# scripts/runner-workspace-cleanup.sh
# Recover from stale runner workspace checkouts
# Usage: runner-workspace-cleanup.sh [--dry-run]

set -euo pipefail

DRY_RUN="${1:-}"
WORKSPACE_ROOT="${RUNNER_WORKSPACE:-/home/runner}"
STALE_THRESHOLD_HOURS="${STALE_THRESHOLD_HOURS:-6}"

echo "=== Runner Workspace Cleanup ==="
echo "Root: $WORKSPACE_ROOT"
echo "Stale threshold: ${STALE_THRESHOLD_HOURS}h"

CLEANED=0
ERRORS=0

# Find directories older than threshold
while IFS= read -r -d '' dir; do
  AGE_HOURS=$(( ($(date +%s) - $(stat -c %Y "$dir" 2>/dev/null || stat -f %m "$dir" 2>/dev/null || echo 0)) / 3600 ))

  if [ "$AGE_HOURS" -gt "$STALE_THRESHOLD_HOURS" ]; then
    echo "STALE: $dir (${AGE_HOURS}h old)"
    if [ "$DRY_RUN" != "--dry-run" ]; then
      if rm -rf "$dir" 2>/dev/null; then
        CLEANED=$((CLEANED + 1))
      else
        ERRORS=$((ERRORS + 1))
        echo "ERROR: failed to remove $dir" >&2
      fi
    else
      echo "  (dry run, would remove)"
    fi
  fi
done < <(find "$WORKSPACE_ROOT" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null)

echo "=== Summary: cleaned=$CLEANED errors=$ERRORS ==="
exit $ERRORS
  • Step 2: Add Justfile recipe

Add to Justfile:

# Runner workspace cleanup (dry-run by default)
runner-cleanup *ARGS:
    ./scripts/runner-workspace-cleanup.sh {{ ARGS }}
  • Step 3: Make executable and validate
chmod +x scripts/runner-workspace-cleanup.sh
shellcheck scripts/runner-workspace-cleanup.sh
  • Step 4: Commit
git add scripts/runner-workspace-cleanup.sh Justfile
git commit -m "feat(runners): add workspace hygiene and stale checkout recovery

Cleans runner workspaces older than configurable threshold.
Supports --dry-run for safe inspection.

Refs: #213"

Completion metric: Cleanup script exists, passes shellcheck, has dry-run mode.

Regression gate: shellcheck scripts/runner-workspace-cleanup.sh && echo PASS


Task 11: Canary Enrollment Check Script

Files:

  • Create: scripts/benchmark/canary-enrollment-check.sh

  • Step 1: Write the enrollment validation script

#!/usr/bin/env bash
# scripts/benchmark/canary-enrollment-check.sh
# Validate that a canary repo can reach GloriousFlywheel runners
# Usage: canary-enrollment-check.sh <owner/repo> <runner-label>

set -euo pipefail

REPO="${1:?Usage: canary-enrollment-check.sh <owner/repo> <runner-label>}"
RUNNER_LABEL="${2:-tinyland-nix}"

echo "=== Canary Enrollment Check ==="
echo "Repo:   $REPO"
echo "Runner: $RUNNER_LABEL"

# Check 1: Repo exists and is accessible
if ! gh repo view "$REPO" --json name -q '.name' &>/dev/null; then
  echo "FAIL: Cannot access repo $REPO"
  exit 1
fi
echo "PASS: Repo accessible"

# Check 2: Runner label is available (check recent workflow runs)
RECENT_RUNS=$(gh api "repos/$REPO/actions/runs?per_page=5" --jq '.workflow_runs | length' 2>/dev/null || echo 0)
echo "INFO: Recent workflow runs: $RECENT_RUNS"

# Check 3: Self-hosted runners are visible to the repo
RUNNER_COUNT=$(gh api "repos/$REPO/actions/runners?per_page=100" --jq '.total_count' 2>/dev/null || echo "N/A (may need org-level check)")
echo "INFO: Repo-visible runners: $RUNNER_COUNT"

# Check 4: Org-level runner groups (if applicable)
ORG=$(echo "$REPO" | cut -d/ -f1)
if gh api "orgs/$ORG/actions/runner-groups" --jq '.total_count' &>/dev/null 2>&1; then
  ORG_GROUPS=$(gh api "orgs/$ORG/actions/runner-groups" --jq '.total_count')
  echo "INFO: Org runner groups: $ORG_GROUPS"
fi

echo "=== Enrollment check complete ==="
  • Step 2: Commit
chmod +x scripts/benchmark/canary-enrollment-check.sh
git add scripts/benchmark/canary-enrollment-check.sh
git commit -m "feat(canary): add enrollment validation script

Checks repo access, runner visibility, and org runner groups.

Refs: #210, #212"

Completion metric: Script validates canary enrollment state for a given repo.

Regression gate: shellcheck scripts/benchmark/canary-enrollment-check.sh && echo PASS


Task Group F: Canary Enrollment Prep (Phase 2-3)

Task 12: Add Canary Scale Sets to ARC Config

Files:

  • Modify: tofu/stacks/arc-runners/honey.tfvars

  • Step 1: Read current honey.tfvars to understand format

Read tofu/stacks/arc-runners/honey.tfvars and tofu/stacks/arc-runners/variables.tf to find the extra_runner_sets variable structure.

  • Step 2: Add cross-org canary scale sets

Add to honey.tfvars in the extra_runner_sets map:

# Cross-org canary: Jesssullivan/cmux (proves multi-org enrollment)
# This scale set is scoped to the Jesssullivan GitHub user, not tinyland-inc
cmux-nix = {
  github_config_url = "https://github.com/Jesssullivan"
  runner_group       = "default"
  min_runners        = 0
  max_runners        = 2
  cpu_request        = "2"
  cpu_limit          = "4"
  memory_request     = "4Gi"
  memory_limit       = "8Gi"
  runner_image        = ""  # uses default
  storage_class       = ""
  node_selector       = {}
  labels              = ["cmux-nix"]
}
  • Step 3: Validate tofu format
cd tofu/stacks/arc-runners && tofu fmt -check && tofu validate

Expected: No errors

  • Step 4: Plan to verify no destructive changes
just tofu-plan arc-runners

Review the plan output to ensure only additive changes.

  • Step 5: Commit
git add tofu/stacks/arc-runners/honey.tfvars
git commit -m "feat(runners): add cross-org canary scale set for cmux

Proves multi-org enrollment: Jesssullivan user-scoped runner pool
alongside tinyland-inc org-scoped pools on the same cluster.

Refs: #210, #212, TIN-129"

Completion metric: Canary scale set added, tofu validate passes, tofu plan shows additive only.

Regression gate: cd tofu/stacks/arc-runners && tofu validate


Task Group G: Adoption Surfaces (Phase 3)

Task 13: Adoption Quickstart Guide

Files:

  • Create: docs/guides/adoption-quickstart.md

  • Modify: docs/getting-started-guide.md

  • Step 1: Write the adoption quickstart

---
title: Adoption Quickstart
order: 1
---

# Adoption Quickstart

Deploy GloriousFlywheel on your cluster and enroll your first repo in 4 steps.

## Prerequisites

- A Kubernetes cluster (RKE2, EKS, GKE, k3s, etc.)
- `kubectl` configured for your cluster
- OpenTofu >= 1.6.0
- A GitHub App (for GitHub adapter) or runner registration token (GitLab/Forgejo)

## Step 1: Deploy Core Substrate

Clone the repo and deploy the ARC controller:

    git clone https://github.com/tinyland-inc/GloriousFlywheel.git
    cd GloriousFlywheel

    # Copy and edit the example config
    cp config/organization.example.yaml config/organization.yaml
    # Edit with your cluster details

    # Deploy the ARC controller (one per cluster)
    just tofu-init arc-runners
    just tofu-plan arc-runners
    just tofu-apply arc-runners

## Step 2: Enroll Your Forge

### GitHub (Primary)

1. Create a GitHub App at your org level
2. Install it on the repos or org you want to serve
3. Set the app credentials in your `.env`:

   GITHUB_APP_ID=...
   GITHUB_APP_INSTALLATION_ID=...
   GITHUB_APP_PRIVATE_KEY_PATH=...

4. Configure `honey.tfvars` (or your cluster's tfvars) with your org URL:

   github_config_url = "https://github.com/your-org"

### GitLab (Compatibility)

See [Forge Adapter Matrix](../architecture/forge-adapter-matrix.md#gitlab-adapter-compatibility)

### Forgejo/Codeberg (Proof Path)

See [Forge Adapter Matrix](../architecture/forge-adapter-matrix.md#forgejocodeberg-adapter-proof-path)

## Step 3: Choose Runner Pools

Default pools:

| Label                | CPU/Mem | Use Case   |
| -------------------- | ------- | ---------- |
| `your-org-docker`    | 2/4Gi   | General CI |
| `your-org-nix`       | 4/8Gi   | Nix builds |
| `your-org-nix-heavy` | 8/16Gi  | Rust + Nix |

Configure in your tfvars `extra_runner_sets` map.

## Step 4: Attach Caches

### Attic (Nix binary cache)

    just tofu-init attic
    just tofu-plan attic
    just tofu-apply attic

Runner pods automatically receive `ATTIC_SERVER` and `ATTIC_TOKEN` env vars.

### Bazel Remote Cache (optional)

Set `BAZEL_REMOTE_CACHE` in runner pod environment via tfvars.

## Step 5: Use In Workflows

In your repo's `.github/workflows/*.yml`:

    jobs:
      build:
        runs-on: your-org-nix  # or your-org-docker, your-org-nix-heavy
        steps:
          - uses: actions/checkout@v4
          - run: nix build .#default

## Validating Enrollment

Run the enrollment check:

    ./scripts/benchmark/canary-enrollment-check.sh your-org/your-repo your-org-nix

## Measuring Performance

Run the benchmark harness:

    ./scripts/benchmark/runner-benchmark.sh your-org-nix nix-build
  • Step 2: Update getting-started-guide.md

Add a prominent link at the top of docs/getting-started-guide.md:

> **New adopter?** Start with the [Adoption Quickstart](guides/adoption-quickstart.md).
> This guide covers internal development workflow.
  • Step 3: Commit
git add docs/guides/adoption-quickstart.md docs/getting-started-guide.md
git commit -m "docs(guides): add adoption quickstart for new adopters

Deploy core → enroll forge → choose pools → attach caches → use in
workflows. Links to enrollment check and benchmark scripts.

Refs: #210, #211"

Completion metric: Quickstart covers all 5 steps. Getting-started links to it.

Regression gate: grep -q 'Adoption Quickstart' docs/getting-started-guide.md


Task 14: Minimal ARC Example

Files:

  • Create: tofu/stacks/arc-runners/examples/minimal/main.tf

  • Create: tofu/stacks/arc-runners/examples/minimal/variables.tf

  • Create: tofu/stacks/arc-runners/examples/minimal/README.md

  • Step 1: Create the example directory

mkdir -p tofu/stacks/arc-runners/examples/minimal
  • Step 2: Write minimal main.tf
# tofu/stacks/arc-runners/examples/minimal/main.tf
# Minimal GloriousFlywheel ARC runner deployment example.
# Deploy this on any Kubernetes cluster to get GitHub Actions runners.

terraform {
  required_version = ">= 1.6.0"
}

module "arc_controller" {
  source = "../../../modules/arc-controller"

  namespace           = var.arc_system_namespace
  controller_version  = var.arc_controller_version
}

module "nix_runner" {
  source = "../../../modules/arc-runner"

  depends_on = [module.arc_controller]

  namespace          = var.arc_runner_namespace
  github_config_url  = var.github_config_url
  github_app_id      = var.github_app_id
  github_app_installation_id = var.github_app_installation_id
  github_app_private_key     = var.github_app_private_key

  runner_scale_set_name = "${var.org_prefix}-nix"
  min_runners           = 0
  max_runners           = 4
  cpu_request           = "4"
  cpu_limit             = "4"
  memory_request        = "8Gi"
  memory_limit          = "8Gi"
}
  • Step 3: Write variables.tf
# tofu/stacks/arc-runners/examples/minimal/variables.tf

variable "github_config_url" {
  description = "GitHub org or repo URL for runner registration"
  type        = string
}

variable "github_app_id" {
  description = "GitHub App ID"
  type        = string
  sensitive   = true
}

variable "github_app_installation_id" {
  description = "GitHub App Installation ID"
  type        = string
  sensitive   = true
}

variable "github_app_private_key" {
  description = "GitHub App private key (PEM)"
  type        = string
  sensitive   = true
}

variable "org_prefix" {
  description = "Prefix for runner scale set names (e.g., 'myorg')"
  type        = string
  default     = "gf"
}

variable "arc_system_namespace" {
  description = "Namespace for ARC controller"
  type        = string
  default     = "arc-systems"
}

variable "arc_runner_namespace" {
  description = "Namespace for runner pods"
  type        = string
  default     = "arc-runners"
}

variable "arc_controller_version" {
  description = "ARC Helm chart version"
  type        = string
  default     = "0.14.0"
}
  • Step 4: Write README
# Minimal ARC Runner Example

Deploy GitHub Actions self-hosted runners on any Kubernetes cluster.

## Usage

    cp terraform.tfvars.example terraform.tfvars
    # Edit with your GitHub App credentials

    tofu init
    tofu plan
    tofu apply

## What This Deploys

- ARC controller (one per cluster)
- One `nix` runner scale set (0-4 runners, 4 CPU / 8Gi each)

## Customizing

- Add more runner sets by duplicating the `nix_runner` module block
- Change resource limits in the module parameters
- See the full stack at `tofu/stacks/arc-runners/` for all options
  • Step 5: Validate and commit
cd tofu/stacks/arc-runners/examples/minimal && tofu fmt -check
cd ~/git/GloriousFlywheel
git add tofu/stacks/arc-runners/examples/
git commit -m "feat(tofu): add minimal ARC runner deployment example

Self-contained example for new adopters. Deploys ARC controller
plus one nix runner scale set on any Kubernetes cluster.

Refs: #210"

Completion metric: Example validates with tofu fmt -check. README explains usage.

Regression gate: cd tofu/stacks/arc-runners/examples/minimal && tofu fmt -check


Task Group H: Cross-Org Canary Proofs (Phase 3)

Task 15: Platform Proof Workflow for Cross-Org

Files:

  • Modify: .github/workflows/platform-proof.yml

  • Step 1: Read current platform-proof.yml

Read the full workflow to understand its structure.

  • Step 2: Add cross-org proof job

Add a new job that validates cross-org runner access:

cross-org-proof:
  name: Cross-org runner proof
  runs-on: tinyland-nix
  if: github.event_name == 'workflow_dispatch'
  steps:
    - uses: actions/checkout@v4

    - name: Verify runner identity
      run: |
        echo "Runner: $(hostname)"
        echo "Org: ${GITHUB_REPOSITORY_OWNER}"
        echo "Repo: ${GITHUB_REPOSITORY}"

    - name: Verify cache endpoints
      run: |
        echo "Attic: ${ATTIC_SERVER:-not set}"
        echo "Bazel: ${BAZEL_REMOTE_CACHE:-not set}"
        [ -n "${ATTIC_SERVER:-}" ] && echo "PASS: Attic reachable" || echo "WARN: No Attic"

    - name: Run enrollment check
      run: |
        ./scripts/benchmark/canary-enrollment-check.sh "$GITHUB_REPOSITORY" tinyland-nix
  • Step 3: Commit
git add .github/workflows/platform-proof.yml
git commit -m "ci(proof): add cross-org runner proof job

Validates runner identity, cache endpoints, and enrollment state
from inside a running workflow. Manual dispatch only.

Refs: #212, TIN-129"

Completion metric: Platform proof workflow includes cross-org validation job.

Regression gate: yq '.jobs.cross-org-proof' .github/workflows/platform-proof.yml | grep -q 'runs-on'


Task Group I: Release & Publication (Phase 4)

Task 16: First Tagged Release

Files:

  • Modify: CHANGELOG.md

  • Step 1: Read current CHANGELOG.md structure

Read the first 50 lines of CHANGELOG.md to understand its format.

  • Step 2: Add v0.1.0 release section

Add below the [Unreleased] section:

## [0.1.0] - 2026-06-14

### Added

- 4-layer platform architecture: FOSS core, forge adapters, control plane, compat kit
- Forge adapter matrix with maturity levels (GitHub primary, GitLab compat, Forgejo proof)
- Multi-org enrollment model (4 dimensions: forge scope, tenant, pool, cache plane)
- Benchmark harness and scorecard for runner and cache performance
- Cross-org canary proofs (XoxdWM, MassageIthaca, cmux)
- Adoption quickstart guide
- Minimal ARC runner example for new adopters
- Release baseline with tag plan and changelog discipline
- Runner workspace hygiene and stale checkout recovery
- Canary enrollment validation script

### Changed

- Getting started guide updated to reference adoption quickstart
- Public docs reframed around 4-layer architecture
- Bzlmod/overlay language moved to compatibility kit scope
- FlakeHub classified as publication-only, not runtime

### Fixed

- Attic endpoint truth reconciled across docs and runtime
- test-arc-runners timing instrumentation added
  • Step 3: Commit
git add CHANGELOG.md
git commit -m "docs: prepare CHANGELOG for v0.1.0 longevity sprint release

Refs: #215, TIN-149"
  • Step 4: Tag (after all Phase 4 work merges)
git tag -a v0.1.0 -m "v0.1.0: Longevity sprint baseline

4-layer architecture, benchmark harness, cross-org canary proofs,
adoption quickstart, release baseline."

git push origin v0.1.0

Completion metric: CHANGELOG has v0.1.0 section. Tag exists after sprint completion.

Regression gate: grep -q '0.1.0' CHANGELOG.md


Task Group J: Control Plane & Retrospective (Phase 4)

Task 17: Control Plane Roadmap Note

Files:

  • Create: docs/architecture/control-plane-roadmap.md

  • Step 1: Write the control plane roadmap

---
title: Control Plane Roadmap
order: 5
---

# Control Plane Roadmap

The managed control plane is a future SaaS layer above the FOSS core substrate.

## What It Adds

- Fleet enrollment UX (add/remove repos, orgs, runner pools via web UI)
- Tenant and pool management (multi-org billing, resource quotas)
- Usage reporting and cost visibility (runner-minutes, cache hit rates, trend)
- Cache and runner observability (dashboards, alerts, diagnostics)
- Policy packs (scheduling rules, placement constraints, approval gates)
- Hosted control-plane APIs (REST/gRPC for enterprise fleet management)

## What It Does Not Replace

The control plane does not replace the FOSS core for:

- Deploying the base runner and cache platform
- Bootstrapping a cluster
- Using Attic or Bazel cache
- Running GitHub ARC on a self-hosted cluster

Adopters can run the full FOSS core without the managed control plane.

## Current State

The existing runner-dashboard app (SvelteKit 5 + WebAuthn) provides:

- Runner listing and status
- Cache health monitoring
- GitOps configuration view

This is the seed for the control plane. The next evolution adds:

- Multi-tenant views (tenant sees only their pools and usage)
- Enrollment workflows (guided forge adapter setup)
- Observability dashboards (Prometheus metrics, Attic cache analytics)

## Implementation Approach

Build on the existing dashboard rather than starting a separate service.
The dashboard already has:

- WebAuthn auth
- Role-based access
- PostgreSQL backend
- REST API (12 endpoints via MCP server)

Add tenant awareness, enrollment flows, and observability views as
dashboard features, not separate microservices.

## Timeline

Not in scope for the current longevity sprint (Apr-Jun 2026).
Target: design phase in Q3 2026, first tenant features in Q4 2026.
  • Step 2: Commit
git add docs/architecture/control-plane-roadmap.md
git commit -m "docs(arch): add control plane roadmap

SaaS layer above FOSS core: fleet UX, tenant management,
observability, policy packs. Not required for base platform.

Refs: #211, TIN-129"

Completion metric: Control plane roadmap exists, clearly separates SaaS from FOSS core.

Regression gate: grep -q 'does not replace' docs/architecture/control-plane-roadmap.md


Task 18: Sprint Retrospective Template

Files:

  • Create: docs/research/gloriousflywheel-sprint-retrospective-2026-06.md

  • Step 1: Write the retrospective template

---
title: GloriousFlywheel Longevity Sprint Retrospective
---

# GloriousFlywheel Longevity Sprint Retrospective

Sprint: 2026-04-19 to 2026-06-14

## What Is Now True

- [ ] FOSS core is clear: cache, runners, operator tooling, local-first deploy
- [ ] GitHub ARC is primary and measured
- [ ] Attic and Bazel cache are working acceleration layers with measurements
- [ ] Downstream adoption moved beyond proof-of-life into repeatable canary kit
- [ ] Compatibility story is explicit and narrow
- [ ] SaaS control plane is defined above FOSS core, not tangled into bootstrap

## Benchmark Evidence

| Metric               | Target | Actual | Status |
| -------------------- | ------ | ------ | ------ |
| Cold start (nix)     | < 60s  | TBD    |        |
| Cold start (docker)  | < 30s  | TBD    |        |
| Queue latency        | < 15s  | TBD    |        |
| Nix cache hit rate   | > 80%  | TBD    |        |
| Bazel cache hit rate | > 70%  | TBD    |        |

## Canary Results

| Repo          | Org          | Forge  | Enrolled | Benchmark Run | Status |
| ------------- | ------------ | ------ | -------- | ------------- | ------ |
| XoxdWM        | tinyland-inc | GitHub | [ ]      | [ ]           |        |
| MassageIthaca | tinyland-inc | GitHub | [ ]      | [ ]           |        |
| cmux          | Jesssullivan | GitHub | [ ]      | [ ]           |        |

## What Remains Compatibility-Only

- Bzlmod overlay kit (lives in bzl-cross-repo)
- GitLab runner fleet (Helm chart validates, no live fleet)
- Forgejo runner fleet (one proof path, not production)

## What Moves Next

- Control plane tenant features (Q3 2026)
- Broader canary rollout (scheduling-kit, acuity-middleware, lab)
- SOPS/age secrets migration
- MCP server wiring into claude code
  • Step 2: Commit
git add docs/research/gloriousflywheel-sprint-retrospective-2026-06.md
git commit -m "docs: add sprint retrospective template

Scorecard for longevity sprint exit criteria and benchmark evidence.

Refs: #212"

Completion metric: Retrospective template exists with checkboxes for all exit criteria.

Regression gate: grep -c '\[ \]' docs/research/gloriousflywheel-sprint-retrospective-2026-06.md returns at least 10.


Validation Summary

Per-Phase Regression Gates

Phase 1 exit (all must pass):

test -f docs/architecture/platform-layers.md
test -f docs/architecture/forge-adapter-matrix.md
test -f docs/architecture/enrollment-model.md
test -f docs/compatibility-kit.md
test -f docs/release-baseline.md
grep -q 'platform-layers' docs/index.md
grep -q 'bzl-cross-repo' docs/compatibility-kit.md

Phase 2 exit (all must pass):

shellcheck scripts/benchmark/runner-benchmark.sh
shellcheck scripts/benchmark/parse-results.sh
shellcheck scripts/benchmark/canary-enrollment-check.sh
shellcheck scripts/runner-workspace-cleanup.sh
test -f .github/workflows/benchmark.yml
grep -c 'Record start time' .github/workflows/test-arc-runners.yml

Phase 3 exit (all must pass):

test -f docs/guides/adoption-quickstart.md
test -d tofu/stacks/arc-runners/examples/minimal
grep -q 'Adoption Quickstart' docs/getting-started-guide.md
yq '.jobs.cross-org-proof' .github/workflows/platform-proof.yml

Phase 4 exit (all must pass):

grep -q '0.1.0' CHANGELOG.md
test -f docs/architecture/control-plane-roadmap.md
test -f docs/research/gloriousflywheel-sprint-retrospective-2026-06.md

Full Sprint Validation

# Run all regression gates
just check  # existing validation
# Plus the phase gates above

GloriousFlywheel