#!/usr/bin/env bash
# atlas — CLI for the ATLAS project-map standard.
#
# Subcommands:
#   atlas init [--style <preset>] [--stack <a,b,c>] [--force] [--analyze] [--loop] [--bugs] [--critics]
#              [--faq] [--tools] [--asop] [--akigi] [--frq] [--brd] [--srd] [--intake]
#                                     (--loop also scaffolds LOOP.md + ROADMAP.md,
#                                     the optional autonomous-loop 5th surface)
#                                     (--bugs scaffolds BUGS.md, the optional
#                                     INTERNAL open-issues register)
#                                     (--critics scaffolds CRITICS.md, the optional
#                                     cross-vendor second-opinion log)
#                                     (--faq scaffolds docs/FAQ.md, the optional
#                                     Q&A knowledge ledger, SPEC §13)
#                                     (--tools writes .atlas/tools.json, the
#                                     machine-readable tool context contract,
#                                     SPEC §14 — regenerated on every run)
#                                     (--asop scaffolds ASOP.md + ASOP-EXECUTOR.md,
#                                     the operating methodology, SPEC §12)
#                                     (--akigi scaffolds AKIGI.md, the purpose
#                                     contract; --frq/--brd/--srd scaffold the
#                                     EXTERNAL intake trio — feature requests /
#                                     bug disclosure / security disclosure —
#                                     each implies --akigi; --intake = all four)
#                                     bootstrap ATLAS.md + SKILL.md + SCARS.md
#                                     + CLAUDE.md + AGENTS.md + EXAMPLES.md (+ seeds).
#                                     if ATLAS.md already exists AND any surface
#                                     flag above was passed, the quartet+seeds
#                                     scaffold is skipped — only the requested
#                                     surface(s) are (re)scaffolded.
#                                     --analyze scans the repo + pre-fills the map.
#                                     + stack-specific docs in cwd.
#                                     --style: default | minimal | strict |
#                                              karpathy | google
#                                     --stack: requires a style with stacks/ subdir
#                                              (comma-separated; pick any subset)
#   atlas check [--changed-files[=REF]] [--json] [--strict] [--deep]  lint the
#                                     quartet + verify anchors unique; --changed-files
#                                     also fails on structural drift (files moved but
#                                     ATLAS.md not updated); --json emits a
#                                     machine-readable report; --strict fails on
#                                     warnings; warns UNMAPPED_TIER_TAG when
#                                     LOOP.md has a 'Model tier mapping' block
#                                     and ROADMAP.md uses a tier: tag not in it;
#                                     --deep adds SCARS anchor-schema validation
#                                     (ToC↔body, labels, Where: paths) +
#                                     EXECUTOR_PACK_MISSING (5+ SCARS anchors,
#                                     ROADMAP.md present, no EXECUTOR PACK block)
#   atlas fix                         auto-resolve conformance warnings: kebab the
#                                     SKILL dir, re-mirror AGENTS.md, regenerate llms.txt
#   atlas measure [--badge]           estimate orientation-token savings (with
#                                     vs without ATLAS); --badge prints a shield
#   atlas measure --tools [--json]    report the tool context contract (SPEC §14):
#                                     canonical project id, indexed commit vs
#                                     HEAD, worktree drift, dead-preload env
#                                     vars, and the .atlas/tools-telemetry.jsonl
#                                     aggregates (bytes indexed/returned,
#                                     retries, stale hits, failures, estimated
#                                     context savings)
#   atlas leaderboard --render        regenerate docs/LEADERBOARD.md's table
#                                     from data/leaderboard.csv (the CSV is
#                                     what contributors edit in a PR)
#   atlas bench [--runtime claude|codex|opencode|openai] [--model M|--matrix M1,M2] [--effort L]
#               [--api-base URL] [--exec CMD] [--task "…"] [--reps N] [--dry-run]
#                                     A/B a task with vs without ATLAS. Agentic
#                                     (claude/codex/opencode/--exec) OR single-shot
#                                     vs any OpenAI-compatible endpoint (--api-base,
#                                     e.g. local vLLM). Writes JSON + Markdown.
#   atlas loop log [--json]           render the autonomous-loop ledger from
#                                     LOOP_HISTORY.md + tracked LOOP_REPORT.md history.
#   atlas map [--ascii|--mermaid|--html] [--out F]
#                                     draw the repo graph: Unicode in a terminal,
#                                     Mermaid when piped (GitHub-native), or an HTML page
#   atlas onboard [--pr]              drop ATLAS into this repo (scaffold + draft the
#                                     map + measure); --pr opens a pull request via gh
#   atlas hooks install [--auto]      git pre-commit hook so ATLAS.md never goes stale
#                                     (--auto: auto-refresh §0.5 + stage it)
#   atlas orient ["task"]             print the map — or just the slice relevant
#                                     to "task" (what the MCP atlas_orient serves)
#   atlas context [--task "<t>"] [--budget N] [--format md|json] [--hash]
#                                     the tool-free context bundle (SPEC §15):
#                                     ATLAS §0-1 + SCARS/SKILL ToCs + CLAUDE.md,
#                                     one command any harness can prepend —
#                                     no hook/MCP/adapter required. --task
#                                     narrows to the atlas-orient slice (needs
#                                     python3; degrades to the full bundle
#                                     otherwise). --budget caps output bytes,
#                                     dropping CLAUDE.md, then SKILL ToC, then
#                                     SCARS ToC (never the ATLAS slice).
#                                     --hash prints a stable sha256 + manifest.
#   atlas mcp [--config] [--http]     run the MCP server — serve the map to any
#                                     MCP client (Claude Code/Cursor/OpenClaw/…)
#   atlas doctor                      diagnose ATLAS_HOME + adapter/hook wiring
#   atlas badge [--for-the-badge]     print a 'Powered by ATLAS' README badge
#   atlas export --to <runtime>       fan the canonical trio out to another
#                                     runtime's context file. <runtime>:
#                                     codex|copilot|gemini|cursor|llms-txt|all
#   atlas anchors                     list every SCARS anchor (machine-readable)
#   atlas anchor add NAME "summary"   append a stub anchor to SCARS.md
#   atlas remember "<note>" --kind scar|skill|bug|faq [--name NAME]
#                                     the write side (SPEC §16): distill a
#                                     durable mid-session learning back into
#                                     the repo instead of losing it at
#                                     compaction. --kind is REQUIRED (no
#                                     auto-classifier). scar/skill also write
#                                     the ToC entry (--deep-clean on write);
#                                     bug/faq die with the init hint when
#                                     their surface is absent.
#   atlas critique <topic> [--range A..B] [--verify "cmds"]
#                          [--with-codex|--with-claude|--no-auto]
#                                     append CRITICS.md row stub + print the
#                                     brutal-honest prompt template, stamped
#                                     with provenance (critic model + effort +
#                                     override-vs-default) and the exact diff
#                                     range/files/verification inputs. Auto-
#                                     detects an installed cross-vendor critic
#                                     (codex CLI) and dispatches it, capturing
#                                     the raw response into CRITICS.md;
#                                     degrades to print-only when none is
#                                     installed. --no-auto forces print-only.
#   atlas gap-to-article <gap-id> [--writer <path>]
#                                     scaffold an article dir for a resolved
#                                     novel implementation gap.
#                                     --writer (default: agent-scientific-ai-writer/)
#   atlas cost                        parse ATLAS §C + §GPU; emit current-state
#                                     summary: idle $, components not at $0,
#                                     stale price-audits.
#   atlas mirror init [--staged|--direct] [--remote NAME]
#                                     scaffold .atlas/mirror.allow + (if
#                                     staged) a GitHub Action that promotes
#                                     public -> main on push. Default: staged.
#   atlas mirror push [--remote NAME] [--dry-run]
#                                     push only allowlisted refs to the public
#                                     remote. Refuses if remote is 'origin'.
#                                     Enforces SKILL §GITLAB-PRIVATE-GITHUB-PUBLIC.
#   atlas mirror status [--remote NAME]
#                                     show config + what would be pushed.
#   atlas adr add "<title>"           create docs/adr/NNNN-<slug>.md from
#                                     the template (auto-numbered). Updates
#                                     ATLAS.md §A3 index if present.
#   atlas adr list                    list every ADR in docs/adr/ with status.
#   atlas research add "<topic>"      create research/NNN-<slug>.md from
#                                     the schema in research/README.md.
#   atlas research list               list every research note.
#   atlas auth login [--method ssh|vendor] [--email <email>]
#                                     log in to GitHub + GitLab. ssh = generate
#                                     per-host keys + ~/.ssh/config. vendor =
#                                     brew-install gh+glab and run their auth
#                                     flows. No --method = interactive picker.
#   atlas auth status                 show what's authenticated (keys, gh
#                                     status, glab status, ssh -T tests).
#   atlas repo create [--github|--gitlab] [--public|--private] [options]
#                                     wrap gh/glab repo create. Creates the
#                                     remote, adds it as `origin` (or
#                                     --remote NAME), pushes main + tags.
#                                     Defaults: --github --public, name=cwd
#                                     basename, description auto-pulled from
#                                     the first README quote-block.
#   atlas install --runtime <name>    wire ATLAS into an agent runtime
#                                     <claude-code|codex|opencode|hermes|generic>
#   atlas uninstall [--purge] [-y]    remove a curl|bash / manual install
#                                     (--purge also removes the claude-code hook)
#   atlas styles                      list available --style presets
#   atlas stacks                      list --stack add-ons per style
#   atlas version                     print version
#   atlas help                        show this message
#
# The CLI looks up its template + adapter assets relative to its install
# location (resolved through symlinks).

set -euo pipefail

ATLAS_VERSION="0.7.0"

# --- color + logo --------------------------------------------------------
# Auto-disabled if NO_COLOR is set, stdout is not a TTY, or ATLAS_NO_COLOR=1.
_c_enabled() {
  [[ -z "${NO_COLOR:-}" ]] && [[ -t 1 ]] && [[ "${ATLAS_NO_COLOR:-0}" != "1" ]]
}
_c() {
  # _c <code> <text>   — wrap text in an ANSI color, no-op when disabled.
  if _c_enabled; then printf '\033[%sm%s\033[0m' "$1" "$2"; else printf '%s' "$2"; fi
}
_c_red()    { _c 31 "$1"; }
_c_green()  { _c 32 "$1"; }
_c_yellow() { _c 33 "$1"; }
_c_blue()   { _c 34 "$1"; }
_c_cyan()   { _c 36 "$1"; }
_c_dim()    { _c 2  "$1"; }
_c_bold()   { _c 1  "$1"; }

# ASCII logo + tagline. Use printf so escapes render even when piped.
_logo() {
  if _c_enabled; then
    local C=$'\033[36m' B=$'\033[1;36m' D=$'\033[2m' Y=$'\033[33m' R=$'\033[0m'
    printf '%s   _   _____ _      _    ____  %s\n' "$B" "$R"
    printf '%s  / \\ |_   _| |    / \\  / ___| %s\n' "$B" "$R"
    printf '%s / _ \\  | | | |   / _ \\ \\___ \\ %s\n' "$B" "$R"
    printf '%s/ ___ \\ | | | |__/ ___ \\ ___) |%s\n' "$B" "$R"
    printf '%s\\_/   \\_\\|_| |____/_/   \\_\\____/%s   %s🗺  atlas v%s%s\n' "$B" "$R" "$Y" "$ATLAS_VERSION" "$R"
    printf '\n'
    printf '  %sthe project-map standard for agentic codebases%s ⭐\n' "$C" "$R"
    printf '  %shttps://github.com/Abbasi-Alain/atlas%s\n' "$D" "$R"
  else
    cat <<EOF
   _   _____ _      _    ____
  / \\ |_   _| |    / \\  / ___|
 / _ \\  | | | |   / _ \\ \\___ \\
/ ___ \\ | | | |__/ ___ \\ ___) |
\\_/   \\_\\|_| |____/_/   \\_\\____/   atlas v${ATLAS_VERSION}

  the project-map standard for agentic codebases
  https://github.com/Abbasi-Alain/atlas
EOF
  fi
}

# Resolve to the real install dir (follow symlinks).
_resolve_self() {
  local src="${BASH_SOURCE[0]}"
  local dir
  while [[ -L "$src" ]]; do
    dir="$(cd -P "$(dirname "$src")" && pwd)"
    src="$(readlink "$src")"
    [[ "$src" != /* ]] && src="$dir/$src"
  done
  cd -P "$(dirname "$src")/.." && pwd
}
ATLAS_HOME="${ATLAS_HOME:-$(_resolve_self)}"
TEMPLATES="$ATLAS_HOME/templates"
ADAPTERS="$ATLAS_HOME/adapters"
# HOOKS is exported for adapters that source us; silence shellcheck SC2034.
# shellcheck disable=SC2034
HOOKS="$ATLAS_HOME/hooks"

# --- helpers --------------------------------------------------------------

_die()  { echo "$(_c_red '❌ atlas:') $*" >&2; exit 1; }
_say()  { echo "$(_c_dim '   atlas:') $*"; }
_warn() { echo "$(_c_yellow '⚠️  atlas:') $*" >&2; }
_ok()   { echo "$(_c_green '   ✅') $*"; }
_fail() { echo "$(_c_red '   ❌') $*"; }
_warnln() { echo "$(_c_yellow '   ⚠️ ') $*"; }
_banner() {
  # _banner <emoji> <title>
  if _c_enabled; then
    printf "\n\033[1;36m%s %s\033[0m\n" "$1" "$2"
  else
    printf "\n%s %s\n" "$1" "$2"
  fi
}
_have() { command -v "$1" >/dev/null 2>&1; }

_project_name() {
  # A project's name is the kebab basis (SPEC §1). Use the git-remote basename
  # only when cwd is the repo ROOT; for a nested project root (a monorepo
  # subproject, or a bundled example inside another repo) fall back to the
  # project's own directory basename — never the enclosing repo's remote.
  # `git rev-parse --show-prefix` is empty only at the repo root, and avoids the
  # macOS /var↔/private/var path-normalization trap of comparing toplevel to PWD.
  local n=""
  if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    if [[ -z "$(git rev-parse --show-prefix 2>/dev/null || true)" ]]; then
      n="$( { git config --get remote.origin.url 2>/dev/null || true; } \
           | sed -E 's#.*/##; s#\.git$##' )"
    fi
  fi
  [[ -z "$n" ]] && n="$(basename "$PWD")"
  echo "$n"
}

# Kebab-case a string per SPEC §1 (lowercase; non-alphanumeric runs collapse to a
# single hyphen; trim leading/trailing hyphens). Portable: tr + BSD/GNU sed -E.
_kebab() {
  printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//'
}

# The spec-mandated SKILL.md directory name: the kebab-cased git-remote (or
# repo-root) basename. Used identically by `init` (scaffold) and `check`
# (validate) so the expectation is derived, never hardcoded per repo.
_project_slug() { _kebab "$(_project_name)"; }

# A REAL Markdown link to a surface file — not merely its name appearing in
# prose. `$2` is the link-target path as an ERE fragment (e.g. `BUGS` or
# `(docs/)?FAQ`); only the link target (inside the parens) is significant, so
# any label text (backticked or not) passes. Used identically by `init`
# (auto-link) and `check` (*_MD_UNLINKED) so a plain-text mention still
# warns while a real link passes (SCARS §BUGS-LINK-NOT-SUBSTRING).
_surface_linked() {
  # Accept the common real Markdown link forms (RM-42, critic-stage finding
  # #8): a bare path, an optional `./` prefix, an optional `#fragment`, an
  # optional quoted title, and angle-bracket targets — not just the two exact
  # forms this originally matched.
  grep -qE '\]\(<?(\./)?'"$2"'\.md>?(#[^) "]*)?([[:space:]]+"[^"]*")?\)' "$1" 2>/dev/null
}
_bugs_md_linked() { _surface_linked "$1" 'BUGS'; }
# FAQ.md may live at root or under docs/ (SPEC §13) — resolve once, same rule
# for init/check/hook/export.
_faq_file() {
  if [[ -f "$PWD/FAQ.md" ]]; then echo "$PWD/FAQ.md"
  elif [[ -f "$PWD/docs/FAQ.md" ]]; then echo "$PWD/docs/FAQ.md"
  fi
}
# The FAQ link must target where the FAQ ACTUALLY lives — the default
# ATLAS.md template ships a docs/FAQ.md §1.2 row ("delete rows for absent
# files"), so accepting any FAQ-shaped link would blind the check to a root
# FAQ.md that is only dead-linked. $2 = the FAQ path relative to repo root.
_faq_linked() {
  local pat='FAQ'
  [[ "$2" == docs/* ]] && pat='docs/FAQ'
  _surface_linked "$1" "$pat"
}

# _replace_marker_block <file> <start-marker> <end-marker> <block-file>
# Replace the marker-bounded block in <file> with <block-file>'s content
# (which must itself carry the markers). The marker PAIR is validated first —
# exactly one of each, start before end — and the file is left untouched
# otherwise: the awk state machine skips from the start marker until it sees
# the end marker, so a missing/duplicated end marker would silently TRUNCATE
# everything after the start (2026-07-10 critic-stage finding #1 — reproduced
# against both docs/LEADERBOARD.md and ~/.ssh/config before this guard).
_replace_marker_block() {
  local file="$1" s="$2" e="$3" block_file="$4"
  local n_s n_e
  n_s=$(grep -cxF "$s" "$file" 2>/dev/null || true)
  n_e=$(grep -cxF "$e" "$file" 2>/dev/null || true)
  if [[ "${n_s:-0}" -ne 1 || "${n_e:-0}" -ne 1 ]]; then
    _die "marker pair broken in $file (found ${n_s:-0} × '$s', ${n_e:-0} × '$e'; need exactly 1 of each) — refusing to rewrite; repair the markers first"
  fi
  local l_s l_e
  l_s=$(grep -nxF "$s" "$file" | head -1 | cut -d: -f1)
  l_e=$(grep -nxF "$e" "$file" | head -1 | cut -d: -f1)
  if [[ "$l_s" -ge "$l_e" ]]; then
    _die "marker pair inverted in $file ('$s' line $l_s is not before '$e' line $l_e) — refusing to rewrite; repair the markers first"
  fi
  awk -v s="$s" -v e="$e" -v bf="$block_file" '
    $0 == s { while ((getline line < bf) > 0) print line; close(bf); skipping = 1; next }
    $0 == e { skipping = 0; next }
    !skipping { print }
  ' "$file" > "$file.tmp" && mv "$file.tmp" "$file"
}

_render() {
  local in="$1" out="$2" force="${3:-0}"
  if [[ -e "$out" && "$force" != "1" ]]; then
    _say "$out exists — skip (--force to overwrite)"
    return 0
  fi
  mkdir -p "$(dirname "$out")"

  local project src_dir build_file test_cmd date_now
  project="$(_project_name)"
  src_dir="src"
  if [[ -d "src" ]]; then
    local subs
    subs="$(find src -mindepth 1 -maxdepth 1 -type d \
            ! -name '__pycache__' ! -name 'node_modules' 2>/dev/null)"
    [[ "$(echo "$subs" | wc -l | tr -d ' ')" == "1" && -n "$subs" ]] && src_dir="$subs"
  elif [[ -d "lib" ]]; then src_dir="lib"
  elif [[ -d "pkg" ]]; then src_dir="pkg"
  fi
  build_file="(none)"
  for f in Cargo.toml pyproject.toml package.json go.mod build.gradle pom.xml CMakeLists.txt Makefile; do
    [[ -f "$f" ]] && { build_file="$f"; break; }
  done
  test_cmd="# (set me — e.g. cargo test / pytest / npm test / go test ./...)"
  if   [[ -f "Cargo.toml"      ]]; then test_cmd="cargo test --all"
  elif [[ -f "pyproject.toml"  ]]; then test_cmd="pytest -m 'not slow'"
  elif [[ -f "package.json"    ]]; then test_cmd="npm test"
  elif [[ -f "go.mod"          ]]; then test_cmd="go test ./..."
  fi
  date_now="$(date +%Y-%m-%d)"

  sed -e "s|{{PROJECT_NAME}}|${project}|g" \
      -e "s|{{SRC_DIR}}|${src_dir}|g" \
      -e "s|{{PRIMARY_BUILD_FILE}}|${build_file}|g" \
      -e "s|{{TEST_CMD}}|${test_cmd}|g" \
      -e "s|{{DATE}}|${date_now}|g" \
      "$in" > "$out"
  _say "wrote $out"
}

# --- subcommands -----------------------------------------------------------

# Heuristic role labels for `atlas init --analyze`.
_role_dir() {
  case "$1" in
    src|lib|pkg|source|app|internal)      echo "source code" ;;
    test|tests|spec|specs|__tests__)      echo "tests" ;;
    doc|docs)                             echo "documentation" ;;
    .github)                              echo "GitHub config + CI workflows" ;;
    bin)                                  echo "executables / CLI" ;;
    cmd)                                  echo "command entrypoints" ;;
    scripts|tools)                        echo "scripts / tooling" ;;
    examples|example)                     echo "examples" ;;
    assets|static|public|media)           echo "static assets" ;;
    packaging|debian|packages)            echo "packaging / distribution" ;;
    adapters|plugins|extensions)          echo "plugins / adapters" ;;
    templates)                            echo "templates" ;;
    hooks)                                echo "hooks" ;;
    migrations|db)                        echo "database / migrations" ;;
    *)                                    echo "_(dir — fill in)_" ;;
  esac
}
_role_file() {
  case "$1" in
    README*|readme*)              echo "public overview" ;;
    LICENSE*|COPYING*)            echo "license" ;;
    CHANGELOG*|HISTORY*)          echo "release history" ;;
    CONTRIBUTING*)                echo "contribution guide" ;;
    Makefile|makefile|GNUmakefile) echo "build tasks" ;;
    Dockerfile*|*.dockerfile|docker-compose*) echo "container build" ;;
    package.json|Cargo.toml|go.mod|pyproject.toml|setup.py|build.gradle|pom.xml|CMakeLists.txt|Gemfile) echo "build / deps config" ;;
    *.md)                         echo "documentation" ;;
    *.sh)                         echo "shell script" ;;
    .gitignore|.gitattributes|.editorconfig) echo "repo config" ;;
    *.yml|*.yaml|*.toml|*.ini|*.cfg) echo "config" ;;
    *)                            echo "_(file — fill in)_" ;;
  esac
}

# Scan the repo + inject a "## 0.5 Auto-detected map" block into ATLAS.md.
_analyze_inject() {
  local atlas="$1"
  [[ -f "$atlas" ]] || return 0
  local langs files build_file test_cmd ci entry inv e typ role block f
  files="$(git ls-files 2>/dev/null)"
  [[ -z "$files" ]] && files="$(find . -type f -not -path './.git/*' -not -path './node_modules/*' 2>/dev/null)"
  langs="$(printf '%s\n' "$files" | grep -oE '\.[A-Za-z0-9]+$' | sort | uniq -c | sort -rn | head -5 | awk '{printf "%s×%s  ", $1, substr($2,2)}' || true)"
  [[ -z "$langs" ]] && langs="(none detected)"
  build_file="(none)"
  for f in Cargo.toml pyproject.toml package.json go.mod build.gradle pom.xml CMakeLists.txt Makefile Gemfile; do
    [[ -f "$f" ]] && { build_file="$f"; break; }
  done
  test_cmd="_(set me)_"
  if   [[ -f Cargo.toml ]];     then test_cmd="cargo test --all"
  elif [[ -f pyproject.toml ]]; then test_cmd="pytest"
  elif [[ -f package.json ]];   then test_cmd="npm test"
  elif [[ -f go.mod ]];         then test_cmd="go test ./..."
  fi
  ci="(none)"
  [[ -f .gitlab-ci.yml ]]    && ci="\`.gitlab-ci.yml\`"
  [[ -d .github/workflows ]] && ci="\`.github/workflows/\`"
  shopt -s nullglob dotglob
  local ents=(bin/* cmd/* main.* src/main.* index.*)
  entry="${ents[*]:0:6}"
  [[ -z "$entry" ]] && entry="_(main / CLI entry — fill in)_"
  # top-level dirs — used to detect a real "talks-to" dependency graph below.
  local dirs=() d talks
  for e in *; do
    case "$e" in .git|.DS_Store|node_modules|target|dist|build|vendor|.agents|ATLAS.md|SCARS.md|CLAUDE.md|AGENTS.md|EXAMPLES.md|llms.txt) continue ;; esac
    [[ -d "$e" ]] && dirs+=("$e")
  done
  inv=""
  for e in *; do
    case "$e" in
      .git|.DS_Store|node_modules|target|dist|build|vendor|.agents|ATLAS.md|SCARS.md|CLAUDE.md|AGENTS.md|EXAMPLES.md|llms.txt) continue ;;
    esac
    if [[ -d "$e" ]]; then
      typ="\`$e/\`"; role="$(_role_dir "$e")"; talks=""
      # which other top-level dirs does this one reference (import/path)?
      for d in "${dirs[@]:0:12}"; do
        [[ "$d" == "$e" ]] && continue
        if grep -rqIF -- "$d/" "$e" 2>/dev/null; then talks+="\`$d/\`, "; fi
      done
      talks="${talks%, }"; [[ -z "$talks" ]] && talks="—"
    else
      typ="\`$e\`"; role="$(_role_file "$e")"; talks="—"
    fi
    inv+="| $typ | $role | $talks |"$'\n'
  done
  shopt -u nullglob dotglob
  local codedir="_(your source dir)_"
  for d in src lib app cmd internal pkg core source; do [[ -d "$d" ]] && { codedir="\`$d/\`"; break; }; done
  block="## 0.5 Auto-detected map  *(from \`atlas init --analyze\` — verify, refine, then delete this note)*

- **Languages:** ${langs}
- **Build:** \`${build_file}\`  ·  **Tests:** \`${test_cmd}\`  ·  **CI:** ${ci}
- **Entry points:** ${entry}

### Where to look
| You want to … | Start here |
|---|---|
| Build / run it | \`${build_file}\` |
| Run the tests | \`${test_cmd}\` |
| CI / release | ${ci} |
| Find the entry point | ${entry} |
| Most of the code | ${codedir} |

### Module graph  *(\`atlas map\` renders this)*
| Node | Role | Talks-to |
|---|---|---|
${inv}"
  # idempotent: strip any prior auto-detected block (## 0.5 … up to the next ##)
  # so re-running (e.g. from the self-maintaining hook) refreshes instead of duplicating.
  local cleaned before after
  cleaned="$(awk '/^## 0\.5 Auto-detected/{skip=1; next} skip&&/^## /{skip=0} !skip' "$atlas")"
  before="$(printf '%s\n' "$cleaned" | awk '/^## 1\./{exit} {print}')"
  after="$(printf '%s\n' "$cleaned" | awk '/^## 1\./{p=1} p')"
  if [[ -n "$after" ]]; then
    printf '%s\n\n%s\n%s\n' "$before" "$block" "$after" > "$atlas"
  else
    printf '%s\n\n%s\n' "$cleaned" "$block" > "$atlas"
  fi
}

# --- tool context contract (SPEC §14) --------------------------------------
# .atlas/tools.json is a GENERATED, LOCAL, machine-readable identity+freshness
# snapshot for external graph/index/context tools: the canonical project id
# (so a tool never has to invent a path-derived one), the HEAD it was written
# at, and the indexed_head an indexer stamps after indexing — which `check`
# and `measure --tools` compare against the live HEAD. One key per line by
# contract, so the zero-dep reader below stays reliable (no jq; §BASH-MONOLITH).

# _tools_json_get <file> <key> — value of a top-level key ('' when absent;
# null stays the literal string "null"; strings come back unquoted).
_tools_json_get() {
  sed -n 's/^[[:space:]]*"'"$2"'":[[:space:]]*//p' "$1" 2>/dev/null \
    | head -1 | sed -E 's/,[[:space:]]*$//; s/^"//; s/"$//'
}

_write_tools_json() {
  local dir="$PWD/.atlas" out head="null" dirty=false gen prev_ih="null" prev_ia="null"
  out="$dir/tools.json"
  mkdir -p "$dir"
  if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    head="\"$(git rev-parse HEAD 2>/dev/null || echo unborn)\""
    [[ "$head" == '"unborn"' ]] && head="null"
    # no `| head` on the producer — §PIPE-HEAD-SIGPIPE
    [[ -n "$(git status --porcelain 2>/dev/null || true)" ]] && dirty=true
  fi
  # Regeneration must not erase an indexer's freshness stamp — carry it over.
  if [[ -f "$out" ]]; then
    prev_ih="$(_tools_json_get "$out" indexed_head)"; prev_ih="${prev_ih:-null}"
    prev_ia="$(_tools_json_get "$out" indexed_at)";   prev_ia="${prev_ia:-null}"
    [[ "$prev_ih" != "null" ]] && prev_ih="\"$prev_ih\""
    [[ "$prev_ia" != "null" ]] && prev_ia="\"$prev_ia\""
  fi
  gen="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
  {
    printf '{\n'
    printf '  "schema": 1,\n'
    printf '  "project": "%s",\n' "$(_json_esc "$(_project_name)")"
    printf '  "project_id": "%s",\n' "$(_json_esc "$(_project_slug)")"
    printf '  "root": ".",\n'
    printf '  "atlas_version": "%s",\n' "$ATLAS_VERSION"
    printf '  "generated": "%s",\n' "$gen"
    printf '  "head": %s,\n' "$head"
    printf '  "dirty": %s,\n' "$dirty"
    printf '  "indexed_head": %s,\n' "$prev_ih"
    printf '  "indexed_at": %s,\n' "$prev_ia"
    printf '  "telemetry": ".atlas/tools-telemetry.jsonl",\n'
    printf '  "recipes": [\n'
    printf '    {"name": "orient", "cmd": "atlas orient \\"<task>\\""},\n'
    printf '    {"name": "anchors", "cmd": "atlas anchors"},\n'
    printf '    {"name": "conformance", "cmd": "atlas check --json"}\n'
    printf '  ],\n'
    printf '  "env_contract": "restore-caller-env; never leak session-temporary preloads into subprocesses (SPEC 14)"\n'
    printf '}\n'
  } > "$out"
  _say "wrote .atlas/tools.json (identity + freshness snapshot — local, regenerate anytime)"
  # Local artifacts stay out of git: HEAD-stamped files would be stale in
  # every commit and machine-specific across clones. Skip the append when a
  # broader pattern (e.g. `.atlas/`) already covers the path.
  local gi="$PWD/.gitignore" entry
  for entry in ".atlas/tools.json" ".atlas/tools-telemetry.jsonl"; do
    if git rev-parse --is-inside-work-tree >/dev/null 2>&1 \
      && git check-ignore -q "$entry" 2>/dev/null; then
      continue
    fi
    if ! grep -qxF "$entry" "$gi" 2>/dev/null; then
      printf '%s\n' "$entry" >> "$gi"
    fi
  done
}

cmd_init() {
  local force=0 style="default" stacks="" analyze=0 loop=0 bugs=0 critics=0 faq=0 tools=0 asop=0 akigi=0 frq=0 brd=0 srd=0
  local surface_requested=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --force|-f)  force=1; shift ;;
      --analyze)   analyze=1; shift ;;
      --loop)      loop=1; surface_requested=1; shift ;;
      --bugs)      bugs=1; surface_requested=1; shift ;;
      --critics)   critics=1; surface_requested=1; shift ;;
      --faq)       faq=1; surface_requested=1; shift ;;
      --tools)     tools=1; surface_requested=1; shift ;;
      --asop)      asop=1; surface_requested=1; shift ;;
      --akigi)     akigi=1; surface_requested=1; shift ;;
      --frq)       frq=1; surface_requested=1; shift ;;
      --brd)       brd=1; surface_requested=1; shift ;;
      --srd)       srd=1; surface_requested=1; shift ;;
      --intake)    akigi=1; frq=1; brd=1; srd=1; surface_requested=1; shift ;;
      --style)     style="${2:-default}"; shift 2 ;;
      --style=*)   style="${1#--style=}"; shift ;;
      --stack)     stacks="${2:-}"; shift 2 ;;
      --stack=*)   stacks="${1#--stack=}"; shift ;;
      *) shift ;;
    esac
  done
  # Surface-only mode: a surface flag (--loop/--bugs/--critics/--faq/--tools/
  # --asop/--akigi/--frq/--brd/--srd/--intake) on a repo that already has an
  # ATLAS.md scaffolds ONLY the requested surface(s) — never the quartet+seeds
  # block. Without this, a repo that deliberately lacks a quartet file (e.g. a
  # removed EXAMPLES.md) gets it silently re-created by an unrelated surface
  # flag (RM-46, and again 2026-07-26). Plain `atlas init` (no surface flags)
  # and a fresh directory (no ATLAS.md yet) are both unaffected.
  local surface_only=0
  [[ $surface_requested -eq 1 && -f "$PWD/ATLAS.md" ]] && surface_only=1
  local project; project="$(_project_name)"
  local slug; slug="$(_project_slug)"   # spec-mandated kebab dir name (SPEC §1)

  # Resolve per-template source by style. A style may override only some
  # files; missing ones fall back to the default template root.
  _src() {
    local base="$1"
    if [[ "$style" != "default" && -f "$TEMPLATES/styles/$style/$base" ]]; then
      echo "$TEMPLATES/styles/$style/$base"
    else
      echo "$TEMPLATES/$base"
    fi
  }

  # Validate the style exists (or is "default").
  if [[ "$style" != "default" && ! -d "$TEMPLATES/styles/$style" ]]; then
    _die "unknown --style '$style' (try: atlas styles)"
  fi

  _banner "🚀" "atlas init  $(_c_dim "(style=$style)")"
  if [[ $surface_only -eq 1 ]]; then
    _say "existing ATLAS repo — scaffolding only the requested surface(s) (plain 'atlas init' scaffolds missing quartet files)"
  else
    _render "$(_src ATLAS.md.tmpl)"    "$PWD/ATLAS.md"                                   "$force"
    _render "$(_src SKILL.md.tmpl)"    "$PWD/.agents/skill/${slug}/SKILL.md"             "$force"
    _render "$(_src SCARS.md.tmpl)"    "$PWD/SCARS.md"                                   "$force"
    _render "$(_src CLAUDE.md.tmpl)"   "$PWD/CLAUDE.md"                                  "$force"
    _render "$(_src EXAMPLES.md.tmpl)" "$PWD/EXAMPLES.md"                                "$force"
    # AGENTS.md is a content mirror of CLAUDE.md so Codex/OpenCode auto-discover.
    if [[ -f "$PWD/CLAUDE.md" ]]; then
      if [[ -e "$PWD/AGENTS.md" && "$force" != "1" ]]; then
        _say "$PWD/AGENTS.md exists — skip (--force to overwrite)"
      else
        cp "$PWD/CLAUDE.md" "$PWD/AGENTS.md"
        _say "wrote $PWD/AGENTS.md (mirror of CLAUDE.md)"
      fi
    fi
  fi
  # Optional 5th surface — the autonomous improvement loop (SPEC §8). Opt-in via
  # --loop; scaffold-missing-only like the quartet (BUG-4).
  if [[ $loop -eq 1 ]]; then
    _render "$(_src LOOP.md.tmpl)"    "$PWD/LOOP.md"    "$force"
    _render "$(_src ROADMAP.md.tmpl)" "$PWD/ROADMAP.md" "$force"
  fi
  # Optional open-issues register (SPEC §9). Opt-in via --bugs; scaffold-missing-
  # only like the quartet. Auto-link it from ATLAS.md so a fresh scaffold stays
  # warning-free (BUGS_MD_UNLINKED only fires on a hand-added, unlinked file).
  if [[ $bugs -eq 1 ]]; then
    _render "$(_src BUGS.md.tmpl)" "$PWD/BUGS.md" "$force"
    if [[ -f "$PWD/ATLAS.md" ]] && ! _bugs_md_linked "$PWD/ATLAS.md"; then
      # shellcheck disable=SC2016  # literal backticks (Markdown), no expansion intended
      printf '\n> Open issues: [`BUGS.md`](BUGS.md) — known-not-yet-understood issues (SPEC §9).\n' >> "$PWD/ATLAS.md"
      _say "linked BUGS.md from ATLAS.md"
    fi
  fi
  # Optional second-opinion log (SPEC §10). Opt-in via --critics; scaffold-
  # missing-only like the quartet. `atlas critique` appends rows to it.
  if [[ $critics -eq 1 ]]; then
    _render "$(_src CRITICS.md.tmpl)" "$PWD/CRITICS.md" "$force"
  fi
  # Optional Q&A knowledge ledger (SPEC §13). Opt-in via --faq; scaffolds
  # docs/FAQ.md (the location the default ATLAS.md §1.2 row already links),
  # keeping an existing root FAQ.md. Auto-link from ATLAS.md when the actual
  # location isn't linked yet, so a fresh scaffold stays warning-free.
  if [[ $faq -eq 1 ]]; then
    if [[ -f "$PWD/FAQ.md" ]]; then
      _say "FAQ.md exists at root — keeping it (SPEC §13 accepts either location)"
    else
      _render "$(_src FAQ.md.tmpl)" "$PWD/docs/FAQ.md" "$force"
    fi
    local faq_rel; faq_rel="$(_faq_file)"; faq_rel="${faq_rel#"$PWD"/}"
    if [[ -f "$PWD/ATLAS.md" ]] && ! _faq_linked "$PWD/ATLAS.md" "$faq_rel"; then
      # shellcheck disable=SC2016  # literal backticks (Markdown), no expansion intended
      printf '\n> Project Q&A: [`%s`](%s) — questions answered once, with pointers (SPEC §13).\n' "$faq_rel" "$faq_rel" >> "$PWD/ATLAS.md"
      _say "linked $faq_rel from ATLAS.md"
    fi
  fi
  # Optional machine-readable tool context contract (SPEC §14). The file is a
  # generated LOCAL snapshot (records HEAD at generation time; graph/index
  # tools update indexed_head as they index), so --tools always (re)writes it
  # and keeps it out of git.
  if [[ $tools -eq 1 ]]; then
    _write_tools_json
  fi
  # Optional operating-methodology surface (SPEC §12): ASOP.md (13 reasoning
  # moves + a 5-question self-test) + ASOP-EXECUTOR.md (the role-based executor
  # card). Both CANONICAL — scaffolded verbatim; --force refreshes to the
  # current shipped version.
  if [[ $asop -eq 1 ]]; then
    _render "$(_src ASOP.md.tmpl)"          "$PWD/ASOP.md"          "$force"
    _render "$(_src ASOP-EXECUTOR.md.tmpl)" "$PWD/ASOP-EXECUTOR.md" "$force"
  fi
  # Optional cross-repo collaboration surfaces (SPEC §11): AKIGI.md (the
  # purpose contract) + the external intake trio FRQ.md (feature requests) /
  # BRD.md (bug disclosure) / SRD.md (security disclosure). Any intake
  # surface implies the AKIGI too — intake is triaged AGAINST the AKIGI's
  # acceptance principles, so intake without one is rudderless.
  if [[ $frq -eq 1 || $brd -eq 1 || $srd -eq 1 ]]; then akigi=1; fi
  if [[ $akigi -eq 1 ]]; then
    _render "$(_src AKIGI.md.tmpl)" "$PWD/AKIGI.md" "$force"
  fi
  if [[ $frq -eq 1 ]]; then
    _render "$(_src FRQ.md.tmpl)" "$PWD/FRQ.md" "$force"
  fi
  if [[ $brd -eq 1 ]]; then
    _render "$(_src BRD.md.tmpl)" "$PWD/BRD.md" "$force"
  fi
  if [[ $srd -eq 1 ]]; then
    _render "$(_src SRD.md.tmpl)" "$PWD/SRD.md" "$force"
  fi
  # Optional per-style seed files (extra documents shipped beyond the trio).
  # Some styles ship ARCHITECTURE.md / GAPS.md / CRITICS.md / STATUS.md /
  # TODO.md / docs/adr/ / docs/gaps/ / research/ / benchmarks/ — each is a
  # separate seed that's rendered iff the style provides it AND the file
  # doesn't already exist (or --force was passed).
  local seeds_dir="$TEMPLATES/styles/$style/seeds"
  if [[ -d "$seeds_dir" && $surface_only -eq 0 ]]; then
    while IFS= read -r seed; do
      local rel="${seed#"$seeds_dir"/}"
      _render "$seed" "$PWD/$rel" "$force"
    done < <(find "$seeds_dir" -type f)
  fi
  # Optional per-stack docs (<style> --stack <names>). Each stack
  # directory ships one or more STACK-*.md files that drop into docs/stacks/.
  if [[ -n "$stacks" ]]; then
    local stacks_root="$TEMPLATES/styles/$style/stacks"
    if [[ ! -d "$stacks_root" ]]; then
      _say "WARNING: style '$style' has no --stack support — ignoring --stack=$stacks"
    else
      IFS=',' read -ra stack_list <<< "$stacks"
      for s in "${stack_list[@]}"; do
        s="$(echo "$s" | tr -d '[:space:]')"
        local sdir="$stacks_root/$s"
        if [[ ! -d "$sdir" ]]; then
          _say "WARNING: unknown --stack '$s' (try: atlas stacks)"
          continue
        fi
        while IFS= read -r stackfile; do
          local rel
          rel="docs/stacks/$(basename "$stackfile")"
          _render "$stackfile" "$PWD/$rel" "$force"
        done < <(find "$sdir" -type f -name '*.md')
      done
    fi
  fi
  if [[ $analyze -eq 1 && -f "$PWD/ATLAS.md" ]]; then
    _analyze_inject "$PWD/ATLAS.md"
    _say "analyzed: injected an auto-detected map into ATLAS.md §0.5 — verify + refine"
  fi
  echo ""
  _say "project: ${project}"
  _say "style:   ${style}"
  [[ -n "$stacks" ]] && _say "stacks:  ${stacks}"
  _say "next:    fill in §-sections of ATLAS.md, then 'atlas check'"
}

# Drift gate: fail if files were added/moved/removed without updating ATLAS.md.
_check_drift() {
  local ref="$1" changed atlas_changed structural
  git rev-parse --git-dir >/dev/null 2>&1 || { _say "drift: not a git repo — skip"; return 0; }
  if [[ -n "$ref" ]]; then
    changed="$(git diff --name-status "$ref" 2>/dev/null || true)"
  else
    changed="$(git diff --name-status HEAD 2>/dev/null || true)"
  fi
  [[ -z "$changed" ]] && { _ok "no drift (no changes vs ${ref:-HEAD})"; return 0; }
  atlas_changed=""
  printf '%s\n' "$changed" | grep -qE 'ATLAS\.md$' && atlas_changed=1
  # docs/ (benchmarks, leaderboard, flagship data, SPEC…) is not structural code.
  # `docs/` is unanchored on purpose — the diff line is "A<TAB>docs/…".
  structural="$(printf '%s\n' "$changed" | grep -E '^[ADR]' | grep -vE 'ATLAS\.md|SCARS\.md|SKILL\.md|CLAUDE\.md|AGENTS\.md|EXAMPLES\.md|llms\.txt|\.md$|docs/|^.[[:space:]]+CHANGELOG|README' || true)"
  if [[ -n "$structural" && -z "$atlas_changed" ]]; then
    _fail "structural changes (add/move/remove) without an ATLAS.md update — the map may be stale (SCARS §ATLAS-IS-INDEX):"
    printf '%s\n' "$structural" | head -8 | sed 's/^/      /'
    _say "→ update ATLAS.md in the same change (or 'atlas init --analyze' to refresh §0.5)."
    exit 1
  fi
  _ok "no structural drift — ATLAS.md is current"
}

# --- atlas check finding sink: dual human / machine-readable JSON -------------
# bash-3.2 safe, no jq. The _ck_* sinks use dynamic scope (errs/warns/json/
# errs_json/warns_json are cmd_check locals) — hence the SC2154 directives.
_json_esc() { local s="$1"; s="${s//\\/\\\\}"; s="${s//\"/\\\"}"; printf '%s' "$s"; }
_ck_obj() {  # code message [spec]
  local o
  o="{\"code\":\"$1\",\"message\":\"$(_json_esc "$2")\""
  [[ -n "${3:-}" ]] && o="$o,\"spec\":\"$3\""
  printf '%s}' "$o"
}
_json_join() { local out="" x; for x in "$@"; do [[ -n "$out" ]] && out="$out,"; out="$out$x"; done; printf '%s' "$out"; }
# shellcheck disable=SC2154
_ck_err()  { errs=$((errs+1));   if [[ $json -eq 1 ]]; then errs_json+=("$(_ck_obj "$1" "$2" "${3:-}")");  else _fail "$2"; fi; }
# shellcheck disable=SC2154
_ck_warn() { warns=$((warns+1)); if [[ $json -eq 1 ]]; then warns_json+=("$(_ck_obj "$1" "$2" "${3:-}")"); else _warnln "$2"; fi; }
# shellcheck disable=SC2154
_ck_ok()   { [[ ${json:-0} -eq 0 ]] && _ok "$1"; return 0; }

# Portable "N days ago" as YYYY-MM-DD, for the FRQ_UNANSWERED freshness check.
# BSD date (macOS: `-v-Nd`) and GNU date (`-d "-N days"`) take incompatible
# flags — a §MACOS-SED-class trap — so try both and print nothing (caller
# skips the check) if neither works. This is the ONLY date-math call; every
# per-row comparison against the result is a plain lexicographic YYYY-MM-DD
# string compare, not a second date parse.
_days_ago() {
  local n="$1" d
  d="$(date -v-"${n}"d +%Y-%m-%d 2>/dev/null)" && { printf '%s' "$d"; return 0; }
  d="$(date -d "-${n} days" +%Y-%m-%d 2>/dev/null)" && { printf '%s' "$d"; return 0; }
  return 1
}

# Deep SCARS anchor validation for `atlas check --deep` (SPEC §3.2/§3.3): ToC<->body
# completeness, core-label presence (terse zero-label rules are exempt), and
# Where-path resolution. Warnings only; calls _ck_warn (dynamic scope via cmd_check).
_ck_anchors() {
  local scars="$1" toc body id kind aid val clean
  # Strip code blocks (fenced ``` / ~~~ and 4-space / tab-indented) so that
  # schema EXAMPLES documenting how to write a scar are not mistaken for real
  # anchors. ToC links and body labels live flush-left, so they survive.
  clean="$(awk '
    /^[[:space:]]*(```|~~~)/ { fence = !fence; next }
    fence { next }
    /^( {4,}|\t)/ { next }
    { print }
  ' "$scars")"
  toc="$(printf '%s\n' "$clean" | grep -oE '\]\(#[a-z0-9-]+\)' | sed -E 's/.*\(#([a-z0-9-]+)\)/\1/' | sort -u)"
  body="$(printf '%s\n' "$clean" | grep -oE '<a id="[a-z0-9-]+">' | sed -E 's/.*"([a-z0-9-]+)".*/\1/' | sort -u)"
  while IFS= read -r id; do
    [[ -z "$id" ]] && continue
    printf '%s\n' "$body" | grep -qxF "$id" || _ck_warn ANCHOR_TOC_NO_BODY "ToC links #$id but no matching '<a id=\"$id\">' body (SPEC §3.3)" "§3.3"
  done <<< "$toc"
  while IFS= read -r id; do
    [[ -z "$id" ]] && continue
    printf '%s\n' "$toc" | grep -qxF "$id" || _ck_warn ANCHOR_NOT_IN_TOC "anchor #$id has a body but no ToC entry (SPEC §3.2)" "§3.2"
  done <<< "$body"
  while IFS="$(printf '\t')" read -r kind aid val; do
    [[ -z "$kind" ]] && continue
    case "$kind" in
      LABELS) _ck_warn ANCHOR_NO_REMEDY "§$aid describes a problem (Symptom/Root cause) but gives no '**Do.**'/'**Do NOT.**' remedy (SPEC §3.3)" "§3.3" ;;
      WHERE)  { [[ -e "$PWD/$val" || -e "$val" ]]; } || _ck_warn ANCHOR_WHERE_UNRESOLVED "§$aid 'Where.' path '$val' does not resolve (SPEC §3.3)" "§3.3" ;;
    esac
  done < <(awk '
    function flush(   problem, remedy) {
      if (cur=="") { block=""; return }
      problem=(block ~ /\*\*Symptom\.\*\*/ || block ~ /\*\*Root cause\.\*\*/)
      remedy=(block ~ /\*\*Do\.\*\*/ || block ~ /\*\*Do NOT\.\*\*/)
      if (problem && !remedy) print "LABELS\t" cur "\t"
      if (match(block, /\*\*Where\.\*\*[^\n]*/)) {
        wl=substr(block,RSTART,RLENGTH); n=split(wl,parts,"`")
        for (i=2;i<=n;i+=2){ p=parts[i]; sub(/::.*/,"",p); sub(/#.*/,"",p); if (p ~ /\// && p !~ /[*?]/) print "WHERE\t" cur "\t" p }
      }
      block=""
    }
    /<a id="[a-z0-9-]+">/ { flush(); match($0,/id="[a-z0-9-]+"/); cur=substr($0,RSTART+4,RLENGTH-5) }
    { block=block "\n" $0 }
    END { flush() }
  ' <<< "$clean")
}

cmd_check() {
  local drift=0 ref="" json=0 strict=0 deep=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --changed-files)   drift=1; shift ;;
      --changed-files=*) drift=1; ref="${1#--changed-files=}"; shift ;;
      --json)            json=1; shift ;;
      --strict)          strict=1; shift ;;
      --deep)            deep=1; shift ;;
      *) shift ;;
    esac
  done
  [[ $json -eq 0 ]] && _banner "🔍" "atlas check"
  local atlas="$PWD/ATLAS.md"
  local scars="$PWD/SCARS.md"
  local claude="$PWD/CLAUDE.md"
  local agents="$PWD/AGENTS.md"
  local slug; slug="$(_project_slug)"
  local skill="$PWD/.agents/skill/${slug}/SKILL.md"
  local skill_glob; skill_glob=$(find "$PWD/.agents/skill" -maxdepth 2 -name SKILL.md 2>/dev/null | head -1)
  [[ -n "$skill_glob" ]] && skill="$skill_glob"
  local init_hint="run 'atlas init' (non-destructive: scaffolds only the missing files)"

  # Severity model: errors (spec MUST violations) fail; warnings (SHOULD /
  # conditional MUST) are advisory unless --strict. --json emits a machine-
  # readable report (no human lines) so any CI/agent can consume it.
  local errs=0 warns=0
  local errs_json=() warns_json=()
  local q_atlas=false q_scars=false q_skill=false q_claude=false q_agents="missing"
  local q_loop_md=false q_roadmap=false
  local q_critics=false q_faq=false
  local q_akigi=false q_frq=false q_brd=false q_srd=false

  # --- ATLAS.md (SPEC §1/§2): MUST exist at root with a §0 quick-orientation.
  if [[ ! -f "$atlas" ]]; then
    _ck_err ATLAS_MISSING "missing ATLAS.md — $init_hint"
  else
    q_atlas=true; _ck_ok "ATLAS.md present"
    grep -q "^## 0\." "$atlas" || _ck_err ATLAS_NO_SECTION0 "ATLAS.md missing §0 quick-orientation" "§2"
  fi

  # --- SKILL.md (SPEC §1/§3): MUST exist with an H1 + a '## Table of contents'.
  # The ToC is the orientation surface the SessionStart hook and `atlas measure`
  # read, so it is required even though the recipe bodies are free-form.
  if [[ ! -f "$skill" ]]; then
    _ck_err SKILL_MISSING "missing SKILL.md — $init_hint"
  else
    q_skill=true; _ck_ok "SKILL.md present at $skill"
    if ! grep -q "^## Table of contents" "$skill"; then
      _ck_err SKILL_NO_TOC "SKILL.md missing '## Table of contents' — the orientation hook + 'atlas measure' surface it as the playbook index (SPEC §3); 'atlas init' scaffolds one" "§3"
    else
      # A ToC heading with zero entries beneath it is the same failure mode as
      # no ToC at all (SCARS §SKILL-TOC-LOAD-BEARING): the hook + 'atlas
      # measure' have nothing real to surface as the playbook index.
      local skill_toc_body
      skill_toc_body="$(awk '
        /^## Table of contents/ { intoc=1; next }
        intoc && /^#/ { intoc=0 }
        intoc { print }
      ' "$skill")"
      grep -qE '^[[:space:]]*-[[:space:]]' <<< "$skill_toc_body" \
        || _ck_err SKILL_TOC_EMPTY "SKILL.md's '## Table of contents' has no entries — the orientation hook + 'atlas measure' depend on it actually listing the playbook (SPEC §3)" "§3"
    fi
    # SPEC §1: the directory MUST be the kebab-cased remote/repo basename, so
    # every runtime resolves the same path. Warn (don't fail) on a mismatch —
    # a repo without a remote legitimately uses its root basename.
    local skill_dir; skill_dir="$(basename "$(dirname "$skill")")"
    if [[ "$skill_dir" != "$slug" ]]; then
      _ck_warn SKILL_DIR_NOT_KEBAB "SKILL.md is under '.agents/skill/${skill_dir}/' but the spec expects kebab-case '.agents/skill/${slug}/' — rename it (or run 'atlas fix') so every runtime discovers it" "§1"
    fi
  fi

  # --- SCARS.md (SPEC §1/§3): MUST exist with a ToC + unique anchors.
  if [[ ! -f "$scars" ]]; then
    _ck_err SCARS_MISSING "missing SCARS.md — $init_hint"
  else
    q_scars=true; _ck_ok "SCARS.md present"
    grep -q "^## Table of contents" "$scars" || _ck_err SCARS_NO_TOC "SCARS.md missing Table of contents" "§3"
    local anchors dups
    anchors=$(grep -Eo '<a id="[a-z0-9-]+"></a>' "$scars" | sort)
    dups=$(echo "$anchors" | uniq -d | tr '\n' ' ')
    if [[ -n "${dups// /}" ]]; then
      _ck_err SCARS_DUP_ANCHORS "duplicate anchors: ${dups}" "§3.3"
    else
      local n; n=$(echo "$anchors" | grep -c . || true)
      _ck_ok "$n unique anchors"
    fi
    [[ $deep -eq 1 ]] && _ck_anchors "$scars"
  fi

  # --- CLAUDE.md / AGENTS.md (SPEC §1/§5): the behavioral contract (warnings).
  # CLAUDE.md is a conditional MUST; AGENTS.md SHOULD mirror it byte-for-byte.
  if [[ ! -f "$claude" ]]; then
    _ck_warn CLAUDE_MISSING "no CLAUDE.md — the behavioral contract (SPEC §1/§5; required if you target Claude); 'atlas init' scaffolds it" "§5"
    if [[ -f "$agents" ]]; then
      q_agents="orphan"
      _ck_warn AGENTS_ORPHAN "AGENTS.md present but CLAUDE.md missing — CLAUDE.md is the canonical source they mirror (SPEC §5)" "§5"
    fi
  else
    q_claude=true; _ck_ok "CLAUDE.md present"
    if [[ ! -f "$agents" ]]; then
      _ck_warn AGENTS_MISSING "no AGENTS.md — it SHOULD mirror CLAUDE.md so Codex/OpenCode auto-discover (SPEC §5); 'atlas export --to codex'" "§5"
    elif ! cmp -s "$claude" "$agents"; then
      q_agents="drift"
      _ck_warn AGENTS_DRIFT "AGENTS.md drifted from CLAUDE.md — they SHOULD be byte-identical (SPEC §5); run 'atlas fix' or 'atlas export --to codex'" "§5"
    else
      q_agents="mirror"; _ck_ok "AGENTS.md mirrors CLAUDE.md"
    fi
  fi

  # --- Autonomous loop (SPEC §8): OPTIONAL 5th surface. Validated only when
  # present — a repo without LOOP.md/ROADMAP.md is completely unaffected.
  if [[ -f "$PWD/LOOP.md" ]]; then
    q_loop_md=true; _ck_ok "LOOP.md present (autonomous loop)"
    grep -q "^# " "$PWD/LOOP.md" || _ck_warn LOOP_NO_H1 "LOOP.md has no H1 title (SPEC §8)" "§8"
  fi
  if [[ -f "$PWD/ROADMAP.md" ]]; then
    q_roadmap=true; _ck_ok "ROADMAP.md present"
    grep -qE "^- \[[ xX]\]" "$PWD/ROADMAP.md" || _ck_warn ROADMAP_NO_QUEUE "ROADMAP.md has no '- [ ]' task queue (SPEC §8)" "§8"
    grep -qiE "^#+[[:space:]]+done([[:space:]]|\(|:|$)" "$PWD/ROADMAP.md" || _ck_warn ROADMAP_NO_DONE "ROADMAP.md has no Done log (a heading starting with 'Done') — the loop's shipped-archive (SPEC §8)" "§8"
    if [[ $deep -eq 1 && "${n:-0}" -ge 5 ]]; then
      # Semantic pack check (RM-42 finding #1, tightened by RM-45 finding #2):
      # a heading mention isn't a pack, and neither is a degenerate two-line
      # one. The heading-bounded pack section must carry SPEC §8's FIVE field
      # markers — "tickets point to specs" · "trap-sheet" · "routing" ·
      # "definition of done" · "escalate-up" — AND its trap-sheet must cite
      # at least one REAL SCARS anchor (the template's literal `§ANCHOR`
      # placeholder never matches a real anchor, so an unfilled pack fails).
      local pack_ok=0 pack_missing=""
      if grep -qi "EXECUTOR PACK" "$PWD/ROADMAP.md"; then
        local pack_body
        pack_body="$(awk '
          { low=tolower($0) }
          low ~ /^##[[:space:]]/ && low ~ /executor pack/ { inpack=1; next }
          inpack && low ~ /^##[[:space:]]/ { inpack=0 }
          inpack { print }
        ' "$PWD/ROADMAP.md")" || pack_body=""
        # Match markers against a newline-collapsed copy: a multi-word field
        # name ("definition of done") legitimately wraps across lines in a
        # hand-written pack, and a line-based grep would miss it.
        local pack_flat f
        pack_flat="$(tr '\n' ' ' <<< "$pack_body")"
        for f in "tickets point to specs" "trap-sheet" "routing" "definition of done" "escalate-up"; do
          grep -qi "$f" <<< "$pack_flat" || pack_missing="${pack_missing}${pack_missing:+, }'$f'"
        done
        local known_anchors pack_anchors
        known_anchors="$(grep -oE '^### §[A-Z0-9-]+' "$PWD/SCARS.md" 2>/dev/null | grep -oE '§[A-Z0-9-]+' | sort -u)" || known_anchors=""
        pack_anchors="$(awk '
          { low=tolower($0) }
          low ~ /trap-sheet/ { intrap=1; next }
          intrap && /^\*\*/ { intrap=0 }
          intrap { print }
        ' <<< "$pack_body" | grep -oE '§[A-Z0-9-]+' | sort -u)" || pack_anchors=""
        local anchor_ok=0
        if [[ -n "$known_anchors" && -n "$pack_anchors" ]]; then
          local a
          while IFS= read -r a; do
            [[ -z "$a" ]] && continue
            if grep -qxF "$a" <<< "$known_anchors"; then anchor_ok=1; break; fi
          done <<< "$pack_anchors"
        fi
        [[ $anchor_ok -eq 0 ]] && pack_missing="${pack_missing}${pack_missing:+, }a real SCARS §ANCHOR in the trap-sheet"
        [[ -z "$pack_missing" ]] && pack_ok=1
      else
        pack_missing="the pack section itself"
      fi
      if [[ $pack_ok -eq 0 ]]; then
        _ck_warn EXECUTOR_PACK_MISSING "SCARS.md has ${n:-0} anchors but ROADMAP.md's EXECUTOR PACK is missing: ${pack_missing} — SPEC §8 requires all five fields for a real cross-model handoff" "§8"
      fi
    fi
  fi
  # SPEC §8 frames the loop as the pair LOOP.md + ROADMAP.md — warn if a half is missing.
  if [[ "$q_loop_md" != "$q_roadmap" ]]; then
    if [[ "$q_loop_md" = true ]]; then
      _ck_warn LOOP_NO_ROADMAP "LOOP.md present but no ROADMAP.md — the loop has no task queue (SPEC §8)" "§8"
    else
      _ck_warn ROADMAP_NO_LOOP "ROADMAP.md present but no LOOP.md — the queue has no rulebook (SPEC §8)" "§8"
    fi
  fi

  # --- Capability tiers (SPEC §8, RM-26): an OPTIONAL 'Model tier mapping'
  # block in LOOP.md binds tier: fast|strong|frontier to the operator's real
  # models. Only validated when a mapping block exists — no block means tiers
  # stay descriptive-only and nothing is checked against them.
  if [[ "$q_loop_md" = true && "$q_roadmap" = true ]]; then
    local valid_tiers
    valid_tiers="$(awk '
      /^##[[:space:]]+Model tier mapping/ { inb=1; next }
      inb && /^##[[:space:]]/ { inb=0 }
      inb && /^\|/ {
        line=$0; sub(/^\|[[:space:]]*/,"",line); n=split(line,parts,"|")
        cell=parts[1]; gsub(/^[[:space:]]+|[[:space:]]+$/,"",cell)
        if (cell != "" && tolower(cell) != "tier" && cell !~ /^-+$/) print tolower(cell)
      }
    ' "$PWD/LOOP.md" 2>/dev/null)" || valid_tiers=""
    if [[ -n "$valid_tiers" ]]; then
      local found_tiers unmapped=""
      # Only TICKET RECORDS are routing-relevant (RM-42 finding #4, tightened
      # by RM-45 finding #6): a ticket = a checkbox list line plus its
      # indented continuation lines. Skip the Done log (historical tags a
      # later mapping change shouldn't retroactively flag), fenced code
      # blocks, and — the RM-45 fix — plain prose ("everything not Done and
      # not fenced" is NOT the same predicate as "an active ticket row";
      # a `## Notes` paragraph mentioning a tier must not warn).
      found_tiers="$(awk '
        BEGIN { indone=0; infence=0; inticket=0 }
        /^```/ { infence = !infence; next }
        infence { next }
        tolower($0) ~ /^#+[[:space:]]+done([[:space:]]|\(|:|$)/ { indone=1; next }
        indone && /^#+[[:space:]]/ { indone=0 }
        indone { next }
        /^- \[[ xX]\]/ { inticket=1; print; next }
        /^[[:space:]]/ { if (inticket) print; next }
        { inticket=0 }
      ' "$PWD/ROADMAP.md" 2>/dev/null \
        | grep -oE '(^|[^A-Za-z])tier:[[:space:]]*[A-Za-z0-9_-]+' \
        | sed -E 's/^.*tier:[[:space:]]*//' | tr '[:upper:]' '[:lower:]' | sort -u)" || found_tiers=""
      local t
      while IFS= read -r t; do
        [[ -z "$t" ]] && continue
        grep -qxF "$t" <<< "$valid_tiers" || unmapped="${unmapped}${unmapped:+, }${t}"
      done <<< "$found_tiers"
      if [[ -n "$unmapped" ]]; then
        _ck_warn UNMAPPED_TIER_TAG "ROADMAP.md uses tier tag(s) not in LOOP.md's Model tier mapping: ${unmapped} (SPEC §8)" "§8"
      fi
    fi
  fi

  # --- BUGS.md (SPEC §9): OPTIONAL open-issues register. Validated only when
  # present. A git-ignored BUGS.md (a repo's own private convention, SCARS
  # §PRIVATE-STYLE-OVERLAY) is a valid choice and is skipped — the link
  # requirement is for a BUGS.md meant to be part of the public map.
  if [[ -f "$PWD/BUGS.md" ]]; then
    _ck_ok "BUGS.md present (open-issues register)"
    local bugs_ignored=0
    if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git check-ignore -q "$PWD/BUGS.md" 2>/dev/null; then
      bugs_ignored=1
    fi
    if [[ $bugs_ignored -eq 0 ]]; then
      _bugs_md_linked "$atlas" \
        || _ck_warn BUGS_MD_UNLINKED "BUGS.md present but not linked from ATLAS.md — link it so agents find the open-issues register (SPEC §9); 'atlas init --bugs' scaffolds a linked one" "§9"
    fi
  fi

  # --- FAQ.md (SPEC §13): OPTIONAL Q&A knowledge ledger — root FAQ.md or
  # docs/FAQ.md. Validated only when present. Same public-link rule as
  # BUGS.md: a git-ignored FAQ is a valid private choice and is skipped.
  local faq_file; faq_file="$(_faq_file)"
  if [[ -n "$faq_file" ]]; then
    q_faq=true; _ck_ok "${faq_file#"$PWD"/} present (Q&A knowledge ledger)"
    local faq_ignored=0
    if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git check-ignore -q "$faq_file" 2>/dev/null; then
      faq_ignored=1
    fi
    if [[ $faq_ignored -eq 0 ]]; then
      _faq_linked "$atlas" "${faq_file#"$PWD"/}" \
        || _ck_warn FAQ_MD_UNLINKED "${faq_file#"$PWD"/} present but not linked from ATLAS.md (a link must target its actual location) — link it so agents look answered questions up instead of re-deriving them (SPEC §13); 'atlas init --faq' scaffolds a linked one" "§13"
    fi
  fi

  # --- CRITICS.md (SPEC §10): OPTIONAL cross-vendor second-opinion log.
  # Validated only when present. A git-ignored CRITICS.md (a repo's own
  # private convention, SCARS §PRIVATE-STYLE-OVERLAY) is a valid choice and is
  # exempt from the staleness check below — a private log's cadence is the
  # repo's own business.
  if [[ -f "$PWD/CRITICS.md" ]]; then
    q_critics=true; _ck_ok "CRITICS.md present (second-opinion log)"
    local critics_ignored=0
    if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git check-ignore -q "$PWD/CRITICS.md" 2>/dev/null; then
      critics_ignored=1
    fi
    if [[ $critics_ignored -eq 0 && -f "$PWD/ROADMAP.md" ]]; then
      local done_count critique_count
      done_count=$(awk '
        BEGIN { in_done=0; c=0 }
        tolower($0) ~ /^#+[[:space:]]+done([[:space:]]|\(|:|$)/ { in_done=1; next }
        in_done && /^#+[[:space:]]/ { in_done=0 }
        in_done && /^- \[[xX]\]/ { c++ }
        END { print c }
      ' "$PWD/ROADMAP.md")
      # A real critique row's critique-text column (the 2nd cell) is never a
      # `_(...)_` placeholder — that convention marks "fill this in" text
      # throughout the templates (templates/CRITICS.md.tmpl's own example row
      # uses it), so counting placeholder rows as real critiques silenced
      # CRITICS_STALE on every fresh scaffold (RM-42, critic-stage finding #2).
      # Fenced blocks are skipped entirely: captured raw critic output (incl.
      # a FAILED dispatch's stderr) lives inside ``` fences and can contain
      # table-shaped text that must never count as a logged critique
      # (2026-07-10 critic-stage finding #3 — reproduced before this fix).
      critique_count=$(awk -F'|' '
        /^```/ { infence = !infence; next }
        infence { next }
        /^\|[[:space:]]*[0-9]+[[:space:]]*\|/ {
          cell=$3; gsub(/^[[:space:]]+|[[:space:]]+$/,"",cell)
          if (cell !~ /^_\(/) c++
        }
        END { print c+0 }
      ' "$PWD/CRITICS.md" 2>/dev/null) || critique_count=0
      if [[ "${done_count:-0}" -ge 3 && "${critique_count:-0}" -eq 0 ]]; then
        _ck_warn CRITICS_STALE "ROADMAP.md's Done log has $done_count shipped item(s) but CRITICS.md logs zero critiques — decisions are landing unreviewed (SPEC §10); log one with 'atlas critique \"<topic>\"'" "§10"
      fi
    fi
    # A STREAK of failed dispatches (expired auth token, a critic CLI's flags
    # changing) is a distinct signal from CRITICS_STALE, which only fires on
    # ZERO attempts — a repo actively trying and failing looks identical to
    # one that never tried once failed entries are excluded from that count.
    # --deep only (a second pass over the file); same private-log exemption.
    if [[ $deep -eq 1 && $critics_ignored -eq 0 ]]; then
      local dispatch_flags last3 fails3
      dispatch_flags="$(awk '
        /^```/ { fence = !fence; next }
        fence { next }
        /^### [0-9]{4}-[0-9]{2}-[0-9]{2}/ { n++; failed[n]=0; next }
        n > 0 && /\*\*DISPATCH FAILED\*\*/ { failed[n]=1 }
        END { for (i=1;i<=n;i++) print failed[i] }
      ' "$PWD/CRITICS.md" 2>/dev/null)" || dispatch_flags=""
      last3="$(printf '%s\n' "$dispatch_flags" | tail -3 | grep -c . || true)"
      if [[ "${last3:-0}" -eq 3 ]]; then
        fails3="$(printf '%s\n' "$dispatch_flags" | tail -3 | grep -c '^1$' || true)"
        [[ "${fails3:-0}" -eq 3 ]] \
          && _ck_warn CRITIC_DISPATCH_BROKEN "CRITICS.md's 3 most recent entries are all DISPATCH FAILED — the critic integration is broken (expired auth, a changed CLI contract), not just unreviewed (SPEC §10)" "§10"
      fi
    fi
  fi

  # --- AKIGI.md + FRQ.md (SPEC §11): OPTIONAL cross-repo collaboration
  # surfaces — the purpose contract + the cross-agent feature-request queue.
  # Validated only when present; a repo without them is fully unaffected.
  if [[ -f "$PWD/AKIGI.md" ]]; then
    q_akigi=true; _ck_ok "AKIGI.md present (purpose contract)"
    grep -qiE "^#+[[:space:]].*acceptance" "$PWD/AKIGI.md" \
      || _ck_warn AKIGI_NO_ACCEPTANCE "AKIGI.md has no Acceptance-principles section — incoming requests can't be triaged against it (SPEC §11)" "§11"
  fi
  if [[ -f "$PWD/FRQ.md" ]]; then
    q_frq=true; _ck_ok "FRQ.md present (feature-request queue)"
    grep -qiE "^#+[[:space:]]+protocol" "$PWD/FRQ.md" \
      || _ck_warn FRQ_NO_PROTOCOL "FRQ.md has no Protocol section — requesters can't know how to file (SPEC §11)" "§11"
    grep -qiE "^#+[[:space:]]+index" "$PWD/FRQ.md" \
      || _ck_warn FRQ_NO_INDEX "FRQ.md has no Index section — open requests can't be tracked (SPEC §11)" "§11"
    # The FRQ protocol triages against the AKIGI's acceptance principles —
    # an FRQ without an AKIGI has no criterion to triage by.
    [[ "$q_akigi" = true ]] \
      || _ck_warn FRQ_NO_AKIGI "FRQ.md present but no AKIGI.md — requests have no purpose contract to be triaged against (SPEC §11); 'atlas init --akigi'" "§11"
    # AKIGI's reply-always acceptance principle ("a request that never hears
    # back breaks the cross-repo loop") is machine-checkable: an Index row
    # still marked open with no ✅/⛔ disposition, older than a freshness
    # floor, means demand exists but the owning agent isn't triaging — the
    # cross-repo analogue of CRITICS_STALE. --deep only (needs date math).
    if [[ $deep -eq 1 ]]; then
      local frq_cutoff
      if frq_cutoff="$(_days_ago 14)"; then
        local open_ids stale=""
        open_ids="$(awk -F'|' '
          /^##[[:space:]]+Index/ { intbl=1; next }
          intbl && /^##[[:space:]]/ { intbl=0 }
          intbl && /^\|/ {
            id=$2; status=$5
            gsub(/^[[:space:]]+|[[:space:]]+$/,"",id)
            gsub(/^[[:space:]]+|[[:space:]]+$/,"",status)
            if (id ~ /^FRQ-[0-9]+$/ && status ~ /open/) print id
          }
        ' "$PWD/FRQ.md")"
        local fid fdate
        while IFS= read -r fid; do
          [[ -z "$fid" ]] && continue
          fdate="$(grep -E "^## ${fid}([^0-9]|\$)" "$PWD/FRQ.md" | head -1 \
            | grep -oE '\([0-9]{4}-[0-9]{2}-[0-9]{2}\)' | tr -d '()')"
          if [[ -n "$fdate" && "$fdate" < "$frq_cutoff" ]]; then
            stale="${stale}${stale:+, }${fid} (${fdate})"
          fi
        done <<< "$open_ids"
        [[ -n "$stale" ]] \
          && _ck_warn FRQ_UNANSWERED "FRQ.md has open request(s) with no disposition older than 14 days: $stale — AKIGI's reply-always acceptance principle (SPEC §11)" "§11"
      fi
    fi
  fi
  if [[ -f "$PWD/BRD.md" ]]; then
    q_brd=true; _ck_ok "BRD.md present (bug disclosure intake)"
    grep -qiE "^#+[[:space:]]+protocol" "$PWD/BRD.md" \
      || _ck_warn BRD_NO_PROTOCOL "BRD.md has no Protocol section — disclosers can't know how to file (SPEC §11)" "§11"
    grep -qiE "^#+[[:space:]]+index" "$PWD/BRD.md" \
      || _ck_warn BRD_NO_INDEX "BRD.md has no Index section — open disclosures can't be tracked (SPEC §11)" "§11"
    [[ "$q_akigi" = true ]] \
      || _ck_warn BRD_NO_AKIGI "BRD.md present but no AKIGI.md — disclosures have no purpose contract to be triaged against (SPEC §11); 'atlas init --akigi'" "§11"
  fi
  if [[ -f "$PWD/SRD.md" ]]; then
    q_srd=true; _ck_ok "SRD.md present (security disclosure intake)"
    grep -qiE "^#+[[:space:]]+protocol" "$PWD/SRD.md" \
      || _ck_warn SRD_NO_PROTOCOL "SRD.md has no Protocol section — disclosers can't know how to file (SPEC §11)" "§11"
    grep -qiE "^#+[[:space:]]+index" "$PWD/SRD.md" \
      || _ck_warn SRD_NO_INDEX "SRD.md has no Index section — disclosures can't be tracked (SPEC §11)" "§11"
    # An SRD without a private channel invites exploit detail into a PUBLIC
    # file — the opposite of responsible disclosure.
    grep -qiE "private channel" "$PWD/SRD.md" \
      || _ck_warn SRD_NO_CONTACT "SRD.md has no private channel — full security reports would land in a public file (SPEC §11)" "§11"
    [[ "$q_akigi" = true ]] \
      || _ck_warn SRD_NO_AKIGI "SRD.md present but no AKIGI.md — disclosures have no purpose contract to be triaged against (SPEC §11); 'atlas init --akigi'" "§11"
  fi

  # --- ASOP.md / ASOP-EXECUTOR.md (SPEC §12): CANONICAL — shipped verbatim,
  # so any drift from the shipped template means the repo's copy is stale, not
  # a chosen local variant (unlike AGENTS.md, which may legitimately diverge).
  # --deep only (a template diff isn't spec-required on every run). A missing
  # repo file is not scaffolded here (that's 'atlas init --asop'); a missing
  # template (unusual install) is skipped silently — advisory only, never error.
  if [[ $deep -eq 1 ]]; then
    if [[ -f "$PWD/ASOP.md" && -f "$TEMPLATES/ASOP.md.tmpl" ]]; then
      cmp -s "$PWD/ASOP.md" "$TEMPLATES/ASOP.md.tmpl" \
        || _ck_warn ASOP_STALE "ASOP.md drifts from the current canonical version — refresh with 'atlas init --asop --force' (SPEC §12; ASOP files are shipped verbatim)" "§12"
    fi
    if [[ -f "$PWD/ASOP-EXECUTOR.md" && -f "$TEMPLATES/ASOP-EXECUTOR.md.tmpl" ]]; then
      cmp -s "$PWD/ASOP-EXECUTOR.md" "$TEMPLATES/ASOP-EXECUTOR.md.tmpl" \
        || _ck_warn ASOP_STALE "ASOP-EXECUTOR.md drifts from the current canonical version — refresh with 'atlas init --asop --force' (SPEC §12; ASOP files are shipped verbatim)" "§12"
    fi
  fi

  # --- .atlas/tools.json (SPEC §14): OPTIONAL machine-readable tool context
  # contract. Validated only when present. Freshness (indexed_head vs the live
  # HEAD) is drift-class advisory, so the stale warning is --deep-only — but
  # the state is always reported under "tools" in --json so any agent/CI can
  # act on it without --deep.
  local tools_file="$PWD/.atlas/tools.json" q_tools_json='{"present":false}'
  if [[ -f "$tools_file" ]]; then
    _ck_ok ".atlas/tools.json present (tool context contract)"
    local t_pid t_ih t_head="null" t_dirty=false t_fresh=null t_ih_json=null t_head_json=null
    t_pid="$(_tools_json_get "$tools_file" project_id)"
    t_ih="$(_tools_json_get "$tools_file" indexed_head)"
    [[ -z "$t_pid" ]] \
      && _ck_warn TOOLS_JSON_INVALID ".atlas/tools.json has no project_id — external tools can't resolve the canonical repo identity (SPEC §14); regenerate with 'atlas init --tools'" "§14"
    if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
      t_head="$(git rev-parse HEAD 2>/dev/null || echo null)"
      # no `| head` on the producer — §PIPE-HEAD-SIGPIPE
      [[ -n "$(git status --porcelain 2>/dev/null || true)" ]] && t_dirty=true
    fi
    if [[ -n "$t_ih" && "$t_ih" != "null" && "$t_head" != "null" ]]; then
      if [[ "$t_ih" == "$t_head" ]]; then t_fresh=true; else t_fresh=false; fi
    fi
    if [[ $deep -eq 1 && "$t_fresh" == "false" ]]; then
      _ck_warn TOOLS_INDEX_STALE "graph/index tools last indexed ${t_ih} but HEAD is ${t_head} — tool results may describe old code; re-index, and treat them as advisory until fresh (SPEC §14)" "§14"
    fi
    [[ -n "$t_ih" && "$t_ih" != "null" ]] && t_ih_json="\"$(_json_esc "$t_ih")\""
    [[ "$t_head" != "null" ]] && t_head_json="\"$(_json_esc "$t_head")\""
    q_tools_json="{\"present\":true,\"project_id\":\"$(_json_esc "$t_pid")\",\"indexed_head\":${t_ih_json},\"head\":${t_head_json},\"fresh\":${t_fresh},\"dirty\":${t_dirty}}"
  fi

  # --- emit -------------------------------------------------------------------
  if [[ $json -eq 1 ]]; then
    local ok=true strict_bool=false deep_bool=false
    if [[ $errs -gt 0 ]]; then ok=false; fi
    if [[ $strict -eq 1 ]]; then strict_bool=true; if [[ $warns -gt 0 ]]; then ok=false; fi; fi
    if [[ $deep -eq 1 ]]; then deep_bool=true; fi
    printf '{"ok":%s,"version":"%s","strict":%s,"deep":%s,"counts":{"errors":%d,"warnings":%d},"errors":[%s],"warnings":[%s],"quartet":{"atlas":%s,"scars":%s,"skill":%s,"claude":%s,"agents":"%s"},"loop":{"loop_md":%s,"roadmap_md":%s},"critics":%s,"faq":%s,"akigi":%s,"frq":%s,"brd":%s,"srd":%s,"tools":%s}\n' \
      "$ok" "$ATLAS_VERSION" "$strict_bool" "$deep_bool" "$errs" "$warns" \
      "$(_json_join ${errs_json[@]+"${errs_json[@]}"})" \
      "$(_json_join ${warns_json[@]+"${warns_json[@]}"})" \
      "$q_atlas" "$q_scars" "$q_skill" "$q_claude" "$q_agents" \
      "$q_loop_md" "$q_roadmap" "$q_critics" "$q_faq" "$q_akigi" "$q_frq" "$q_brd" "$q_srd" "$q_tools_json"
    [[ "$ok" == true ]] && exit 0
    exit 1
  fi

  if [[ $errs -gt 0 ]]; then
    local emsg="$errs error(s)"
    if [[ $warns -gt 0 ]]; then emsg="$emsg, $warns warning(s)"; fi
    echo "$(_c_red 'atlas:') $emsg — see ❌ above"
    exit 1
  fi
  if [[ $drift -eq 1 ]]; then _check_drift "$ref"; fi
  if [[ $strict -eq 1 && $warns -gt 0 ]]; then
    echo "$(_c_red 'atlas:') $warns warning(s) — failing under --strict"
    exit 1
  fi
  if [[ $warns -gt 0 ]]; then
    echo "$(_c_green 'atlas:') ok $(_c_yellow "($warns warning(s) — see ⚠️ above)")"
  else
    echo "$(_c_green 'atlas:') ok"
  fi
}

# fix — auto-resolve the conformance *warnings* `atlas check` surfaces:
#   1. rename a non-kebab SKILL.md dir to the spec kebab name (+ update path refs)
#   2. re-mirror AGENTS.md from CLAUDE.md when it's missing/drifted
#   3. regenerate a stale llms.txt so its read-first set includes SCARS.md
# Idempotent (re-running is a no-op). Missing quartet *files* are not authored
# here — run 'atlas init' (it scaffolds only what's missing).
cmd_fix() {
  _banner "🔧" "atlas fix"
  local fixes=0

  # 1. kebab the SKILL.md directory (SPEC §1) --------------------------------
  local slug; slug="$(_project_slug)"
  local skill_glob; skill_glob=$(find "$PWD/.agents/skill" -maxdepth 2 -name SKILL.md 2>/dev/null | head -1)
  if [[ -n "$skill_glob" ]]; then
    local cur_dir cur_name new_dir
    cur_dir="$(dirname "$skill_glob")"
    cur_name="$(basename "$cur_dir")"
    new_dir="$PWD/.agents/skill/$slug"
    if [[ "$cur_name" != "$slug" ]]; then
      local lc tracked=0 tmp
      lc="$(printf '%s' "$cur_name" | tr '[:upper:]' '[:lower:]')"
      if git rev-parse --is-inside-work-tree >/dev/null 2>&1 && git ls-files --error-unmatch "$skill_glob" >/dev/null 2>&1; then tracked=1; fi
      if [[ "$lc" != "$slug" && -e "$new_dir" ]]; then
        # a genuine (not case-only) rename into a dir that already exists — don't clobber
        _warnln "can't rename '$cur_name' → '$slug': .agents/skill/$slug already exists — resolve by hand"
      else
        # Two-step via a temp name so a *case-only* rename also works on a
        # case-insensitive filesystem (macOS), where 'mv Foo foo' is a no-op
        # and '-e foo' matches 'Foo'. SCARS §MACOS-SED.
        tmp="$PWD/.agents/skill/.atlas-rename.$$"
        if [[ $tracked -eq 1 ]]; then
          git mv "$cur_dir" "$tmp" && git mv "$tmp" "$new_dir"
        else
          mv "$cur_dir" "$tmp" && mv "$tmp" "$new_dir"
        fi
        _ok "renamed .agents/skill/$cur_name → .agents/skill/$slug"
        fixes=$((fixes+1))
        # update skill-path references in the quartet docs (portable; SCARS §MACOS-SED)
        local f
        for f in ATLAS.md CLAUDE.md AGENTS.md SCARS.md llms.txt EXAMPLES.md; do
          if [[ -f "$PWD/$f" ]] && grep -q "\.agents/skill/$cur_name/" "$PWD/$f"; then
            sed -i.bak "s#\.agents/skill/$cur_name/#.agents/skill/$slug/#g" "$PWD/$f" && rm -f "$PWD/$f.bak"
            _say "updated skill-path references in $f"
          fi
        done
      fi
    fi
  fi

  # 2. re-mirror AGENTS.md ← CLAUDE.md (SPEC §5) -----------------------------
  if [[ -f "$PWD/CLAUDE.md" ]]; then
    if [[ ! -f "$PWD/AGENTS.md" ]] || ! cmp -s "$PWD/CLAUDE.md" "$PWD/AGENTS.md"; then
      cp "$PWD/CLAUDE.md" "$PWD/AGENTS.md"
      _ok "mirrored AGENTS.md ← CLAUDE.md (byte-identical)"
      fixes=$((fixes+1))
    fi
  fi

  # 3. regenerate a stale llms.txt — read-first set should list SCARS.md and,
  #    when the repo runs a loop, LOOP.md (parallels BUG-5/BUG-7).
  if [[ -f "$PWD/llms.txt" ]]; then
    local llms_stale=0
    if [[ -f "$PWD/SCARS.md" ]] && ! grep -q "SCARS.md" "$PWD/llms.txt"; then llms_stale=1; fi
    if [[ -f "$PWD/LOOP.md"  ]] && ! grep -q "LOOP.md"  "$PWD/llms.txt"; then llms_stale=1; fi
    if [[ -n "$(_faq_file)" ]] && ! grep -q "FAQ.md" "$PWD/llms.txt"; then llms_stale=1; fi
    if [[ $llms_stale -eq 1 ]]; then
      cmd_export --to llms-txt >/dev/null 2>&1
      _ok "regenerated llms.txt (read-first set now lists SCARS.md / LOOP.md / FAQ.md)"
      fixes=$((fixes+1))
    fi
  fi

  echo ""
  if [[ $fixes -eq 0 ]]; then
    _say "nothing to fix — already conformant (missing files? run 'atlas init')."
  else
    _say "$fixes fix(es) applied — run 'atlas check' to confirm."
  fi
}

cmd_anchors() {
  local scars="$PWD/SCARS.md"
  [[ -f "$scars" ]] || _die "no SCARS.md found (run 'atlas init')"
  # Extract: anchor id + section title.
  awk '
    /<a id="[a-z0-9-]+"><\/a>/ {
      match($0, /id="[a-z0-9-]+"/); id = substr($0, RSTART+4, RLENGTH-5);
    }
    id && /^### §/ {
      sub(/^### /, "");
      print id "\t" $0;
      id = "";
    }
  ' "$scars"
}

cmd_anchor() {
  local op="${1:-}"; shift || true
  case "$op" in
    add)
      local name="${1:-}"; shift || true
      local summary="${*:-}"
      [[ -z "$name" || -z "$summary" ]] && _die "usage: atlas anchor add NAME \"summary\""
      local scars="$PWD/SCARS.md"
      [[ -f "$scars" ]] || _die "no SCARS.md (run 'atlas init')"
      local upper lower
      upper="$(echo "$name" | tr '[:lower:]' '[:upper:]' | tr ' _' '--')"
      lower="$(echo "$upper" | tr '[:upper:]' '[:lower:]')"
      cat >> "$scars" <<EOF

---

<a id="${lower}"></a>
### §${upper} — ${summary}

**Symptom.** _(what the user/agent observes)_

**Root cause.** _(why it happens)_

**Do NOT.** _(anti-patterns)_

**Do.** _(correct pattern with code/steps)_

**Where.** \`path/to/file.ext::function_name\`

**Shipped in.** \`<commit sha>\`.
EOF
      _say "appended §${upper} stub to $scars"
      _say "add a ToC line manually: - [§${upper} — ${summary}](#${lower})"
      ;;
    *)
      _die "usage: atlas anchor add NAME \"summary\""
      ;;
  esac
}

# The next NNN in a `PREFIXNNN` id scheme (e.g. `BUG-` or `FAQ-`), scanning
# whatever's already in the file — 1 when none exist. $3 = zero-pad width
# (0 = unpadded, matching BUGS.md's `BUG-N`; 3 matches FAQ.md's `FAQ-NNN`).
_remember_next_id() {
  # $4 = ERE a LINE must match to count as a real ENTRY — ids mentioned in
  # prose (the FAQ template's own "cross-linked (`FAQ-012`)" example, a Done
  # log narrating "BUG-7") must never inflate the numbering: a fresh scaffold
  # starts at 001, not at template-example + 1.
  local file="$1" prefix="$2" pad="$3" line_re="$4" max=0 n
  while IFS= read -r n; do
    [[ -z "$n" ]] && continue
    n=$((10#$n))  # force base-10 — a "012" match is decimal 12, NOT octal 10
    [[ "$n" -gt "$max" ]] && max="$n"
  done < <(grep -E "$line_re" "$file" 2>/dev/null | grep -oE "${prefix}[0-9]+" | grep -oE '[0-9]+')
  local next=$((max + 1))
  if [[ "$pad" -gt 0 ]]; then printf "%0${pad}d" "$next"; else printf '%d' "$next"; fi
}

# scar kind: a stub §ANCHOR in SCARS.md, same shape as `atlas anchor add`,
# PLUS its ToC entry — `atlas anchor add` alone leaves the ToC line for a
# human to add by hand, which would fail `atlas check --deep --strict`
# (ANCHOR_NOT_IN_TOC) the moment this note is written. The heading stays the
# short title; the full note goes under Symptom.
_remember_scar() {
  local note="$1" title="$2" slug="$3"
  local scars="$PWD/SCARS.md"
  [[ -f "$scars" ]] || _die "no SCARS.md — run 'atlas init' first"
  local upper; upper="$(printf '%s' "$slug" | tr '[:lower:]' '[:upper:]')"
  cat >> "$scars" <<EOF

---

<a id="${slug}"></a>
### §${upper} — ${title}

**Symptom.** ${note}

**Root cause.** _(not yet diagnosed — recorded via 'atlas remember')_

**Do NOT.** _(fill in the anti-pattern)_

**Do.** _(fill in the correct pattern)_
EOF
  local toc_line="- [§${upper} — ${title}](#${slug})"
  awk -v nl="$toc_line" '
    /^## Table of contents/ && !done { print; print nl; done=1; next }
    { print }
  ' "$scars" > "$scars.tmp" && mv "$scars.tmp" "$scars"
  _say "appended §${upper} to $scars (+ ToC entry)"
}

# skill kind: a new recipe section in the resolved SKILL.md PLUS its ToC
# entry — the ToC is load-bearing (SCARS §SKILL-TOC-LOAD-BEARING), never
# optional.
_remember_skill() {
  local note="$1" title="$2" slug="$3"
  local skill; skill=$(find "$PWD/.agents/skill" -maxdepth 2 -name SKILL.md 2>/dev/null | head -1)
  [[ -n "$skill" && -f "$skill" ]] || _die "no SKILL.md — run 'atlas init' first"
  cat >> "$skill" <<EOF

---

<a id="${slug}"></a>
### ${title}

${note}
EOF
  local toc_line="- [${title}](#${slug})"
  awk -v nl="$toc_line" '
    /^## Table of contents/ && !done { print; print nl; done=1; next }
    { print }
  ' "$skill" > "$skill.tmp" && mv "$skill.tmp" "$skill"
  _say "appended recipe '${title}' to $skill (+ ToC entry)"
}

# bug kind: an entry under BUGS.md's '## Open' (SPEC §9), numbered the next
# unpadded BUG-N. Dies with the init hint when BUGS.md is absent (it's an
# opt-in surface — 'atlas remember' doesn't scaffold it).
_remember_bug() {
  local note="$1"
  local bugs="$PWD/BUGS.md"
  [[ -f "$bugs" ]] || _die "no BUGS.md — run 'atlas init --bugs' first"
  local n; n="$(_remember_next_id "$bugs" 'BUG-' 0 '^[[:space:]]*-[[:space:]]+(~~)?\*\*BUG-[0-9]+')"
  local date; date="$(date +%Y-%m-%d 2>/dev/null || echo unknown)"
  local entry="- **BUG-${n}** — *(${date})* — ${note}"
  awk -v nl="$entry" '
    /^## Open/ { print; inopen=1; next }
    inopen && /^_\(nothing open yet/ { next }
    inopen && /^## / { print nl; print ""; inopen=0; print; next }
    { print }
    END { if (inopen) print nl }
  ' "$bugs" > "$bugs.tmp" && mv "$bugs.tmp" "$bugs"
  _say "appended BUG-${n} to $bugs under '## Open'"
}

# faq kind: a new FAQ-NNN entry (SPEC §13) in the FAQ file (root FAQ.md or
# docs/FAQ.md, first found), appended at the end of its first topic section.
# The entry body is multi-line, so it's streamed in via a temp file + getline
# (SCARS §AWK-MULTILINE-V — `awk -v` with a multi-line value warns on macOS's
# default awk), not passed through -v.
_remember_faq() {
  local note="$1"
  local faq; faq="$(_faq_file)"
  [[ -n "$faq" ]] || _die "no FAQ.md/docs/FAQ.md — run 'atlas init --faq' first"
  local n; n="$(_remember_next_id "$faq" 'FAQ-' 3 '^#+[[:space:]]+FAQ-[0-9]+')"
  local date; date="$(date +%Y-%m-%d 2>/dev/null || echo unknown)"
  local block; block="$(mktemp)"
  cat > "$block" <<EOF
### FAQ-${n} · Q: ${note} (${date})
- asked-by: agent

_(recorded via 'atlas remember' — no answer captured yet)_
EOF
  awk -v bf="$block" '
    /^## / && !seen { print; intopic=1; seen=1; next }
    intopic && /^_\(no entries yet/ { next }
    intopic && /^## / {
      print ""; while ((getline line < bf) > 0) print line; close(bf); print ""
      intopic=0; print; next
    }
    { print }
    END { if (intopic) { print ""; while ((getline line < bf) > 0) print line; close(bf) } }
  ' "$faq" > "$faq.tmp" && mv "$faq.tmp" "$faq"
  rm -f "$block"
  _say "appended FAQ-${n} to $faq"
}

# remember — the write side: distill a durable mid-session learning back into
# the repo's surfaces instead of losing it at compaction/session end (SPEC
# §16, the session write-back protocol). `--kind` is REQUIRED — no keyword
# auto-classifier (a heuristic guess would be confidently wrong often enough
# to be worse than asking).
#   atlas remember "<note>" --kind scar|skill|bug|faq [--name NAME]
cmd_remember() {
  local note="${1:-}"; shift || true
  [[ -z "$note" || "$note" == --* ]] && _die 'usage: atlas remember "<note>" --kind scar|skill|bug|faq [--name NAME]'
  note="$(printf '%s' "$note" | tr '\n' ' ')"  # single-line everywhere it lands (ToC/heading/BUG line)
  local kind="" name=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --kind)   kind="${2:-}"; shift 2 ;;
      --kind=*) kind="${1#--kind=}"; shift ;;
      --name)   name="${2:-}"; shift 2 ;;
      --name=*) name="${1#--name=}"; shift ;;
      *) shift ;;
    esac
  done
  case "$kind" in
    scar|skill|bug|faq) ;;
    *) _die "atlas remember needs --kind scar|skill|bug|faq (explicit — no auto-classifier)" ;;
  esac

  local derived title slug
  derived="$(printf '%s' "$note" | tr -s '[:space:]' ' ' | cut -d' ' -f1-6)"
  title="${name:-$derived}"
  slug="$(_kebab "$title")"
  [[ -z "$slug" ]] && _die "atlas remember: could not derive a name/slug from the note — pass --name explicitly"

  case "$kind" in
    scar)  _remember_scar  "$note" "$title" "$slug" ;;
    skill) _remember_skill "$note" "$title" "$slug" ;;
    bug)   _remember_bug   "$note" ;;
    faq)   _remember_faq   "$note" ;;
  esac
}

cmd_stacks() {
  # List --stack add-ons available for the the style (and any future
  # style that ships stacks/).
  for style_dir in "$TEMPLATES/styles"/*/; do
    local s
    s="$(basename "$style_dir")"
    [[ -d "$style_dir/stacks" ]] || continue
    echo "style $s ships these --stack add-ons:"
    for sd in "$style_dir/stacks"/*/; do
      [[ -d "$sd" ]] && echo "  $(basename "$sd")"
    done | sort -u
  done
}

# ---------------------------------------------------------------------------
# critique <topic> [--with-codex | --with-claude]
#
# Appends a CRITICS.md row stub and prints the brutal-honest prompt template
# the user can paste into ChatGPT 5.5 Extended Thinking (or pipe through
# 'codex' / 'claude' CLIs). Default behavior is print-only.
# ---------------------------------------------------------------------------
cmd_critique() {
  local topic="" with="" range="" verify="" no_auto=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --with-codex)  with="codex";  shift ;;
      --with-claude) with="claude"; shift ;;
      --range)       range="${2:-}"; shift 2 ;;
      --verify)      verify="${2:-}"; shift 2 ;;
      --no-auto)     no_auto=1; shift ;;
      --*)           shift ;;
      *)             [[ -z "$topic" ]] && topic="$1"; shift ;;
    esac
  done
  [[ -z "$topic" ]] && _die "usage: atlas critique <topic> [--range <A>..<B>] [--verify \"<cmds>\"] [--with-codex | --with-claude | --no-auto]"

  # RM-2b: auto-detect a cross-vendor critic when the caller didn't force
  # one. Preference order matches LOOP.md's documented critic-selection —
  # a direct CLI (codex exec) is the reliable, synchronous dispatch; degrade
  # to print-only when nothing is installed (zero-dep). --no-auto forces
  # print-only even when a critic CLI is on PATH.
  local auto=0
  if [[ -z "$with" && "$no_auto" -eq 0 ]] && _have codex; then
    with="codex"; auto=1
  fi

  local critics="$PWD/CRITICS.md"
  local date_now; date_now="$(date +%Y-%m-%d)"
  local have_critics=1
  if [[ ! -f "$critics" ]]; then
    have_critics=0
    _say "no CRITICS.md in $PWD (run 'atlas init' first (style must scaffold this file))"
  fi

  # "Inputs they had" (RM-2b provenance): the exact diff range, files
  # touched, and verification commands already run — never a static
  # placeholder line.
  local range_line="not specified" files_line="n/a" verify_line="none recorded"
  [[ -n "$verify" ]] && verify_line="$verify"
  if [[ -n "$range" ]]; then
    range_line="$range"
    if _have git && git -C "$PWD" rev-parse --git-dir >/dev/null 2>&1; then
      local nfiles
      nfiles="$(git -C "$PWD" diff --name-only "$range" 2>/dev/null | wc -l | tr -d '[:space:]')" || nfiles=0
      files_line="${nfiles:-0} file(s) (git diff --name-only $range)"
    fi
  fi

  # Provenance: critic model id + reasoning effort, and whether each came
  # from an explicit override or the tool's own config default (RM-2b).
  local critic_label="_(ChatGPT 5.5 Extended Thinking — manual paste)_"
  local codex_model=""
  if [[ "$with" == "codex" ]]; then
    local model_src="tool default" effort_src="tool default" codex_effort=""
    if [[ -n "${CODEX_MODEL:-}" ]]; then
      codex_model="$CODEX_MODEL"; model_src="override (\$CODEX_MODEL)"
    elif [[ -f "$HOME/.codex/config.toml" ]]; then
      codex_model="$(grep -m1 -E '^model[[:space:]]*=' "$HOME/.codex/config.toml" 2>/dev/null \
        | sed -E 's/^model[[:space:]]*=[[:space:]]*"?([^"#[:space:]]+).*/\1/' || true)"
      [[ -n "$codex_model" ]] && model_src="config default (~/.codex/config.toml)"
    fi
    codex_model="${codex_model:-gpt-5}"
    if [[ -n "${CODEX_EFFORT:-}" ]]; then
      codex_effort="$CODEX_EFFORT"; effort_src="override (\$CODEX_EFFORT)"
    elif [[ -f "$HOME/.codex/config.toml" ]]; then
      codex_effort="$(grep -m1 -E '^model_reasoning_effort[[:space:]]*=' "$HOME/.codex/config.toml" 2>/dev/null \
        | sed -E 's/^model_reasoning_effort[[:space:]]*=[[:space:]]*"?([^"#[:space:]]+).*/\1/' || true)"
      [[ -n "$codex_effort" ]] && effort_src="config default (~/.codex/config.toml)"
    fi
    codex_effort="${codex_effort:-unset}"
    critic_label="codex (model: ${codex_model}, source: ${model_src}; effort: ${codex_effort}, source: ${effort_src})$( if [[ $auto -eq 1 ]]; then echo "; auto-detected"; fi )"
  elif [[ "$with" == "claude" ]]; then
    local claude_model="${ANTHROPIC_MODEL:-opus}" claude_src="CLI default"
    [[ -n "${ANTHROPIC_MODEL:-}" ]] && claude_src="override (\$ANTHROPIC_MODEL)"
    critic_label="claude (model: ${claude_model}, source: ${claude_src})"
  fi

  # Build the prompt. The "Context to read" list is built from files that
  # ACTUALLY exist in $PWD (RM-43, critic-stage finding #7) — a prior version
  # unconditionally listed ARCHITECTURE.md/docs/adr/research/, which assumed
  # one project's private layout even though ATLAS conformance never requires
  # them; a plain conformant repo got a prompt that pointed a critic at
  # nonexistent files.
  local ctx_items="" ctx_n=0
  if [[ -f "$PWD/ATLAS.md" ]]; then
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. ATLAS.md is at $PWD/ATLAS.md — read §0 (quick orientation) and any other sections present."$'\n'
  else
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. (no ATLAS.md found in $PWD)"$'\n'
  fi
  if [[ -f "$PWD/SCARS.md" ]]; then
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. SCARS.md — hard-won failure memory; anchors are stable, load-bearing constraints"$'\n'
  fi
  local skill_glob; skill_glob=$(find "$PWD/.agents/skill" -maxdepth 2 -name SKILL.md 2>/dev/null | head -1)
  if [[ -n "$skill_glob" ]]; then
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. .agents/skill/*/SKILL.md — anchors are stable; treat them as load-bearing constraints"$'\n'
  fi
  if [[ -f "$PWD/docs/SPEC.md" ]]; then
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. docs/SPEC.md — the design spec"$'\n'
  fi
  if [[ -f "$PWD/CLAUDE.md" ]]; then
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. CLAUDE.md — the behavioral contract"$'\n'
  fi
  # A private-style layout (ARCHITECTURE.md/docs/adr//research/) is only
  # listed when it actually exists — never assumed.
  if [[ -f "$PWD/ARCHITECTURE.md" ]]; then
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. ARCHITECTURE.md — the proposed shape"$'\n'
  fi
  if [[ -d "$PWD/docs/adr" ]]; then
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. docs/adr/ — past decisions"$'\n'
  fi
  if [[ -d "$PWD/research" ]]; then
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. research/ — what the field already does"$'\n'
  fi
  if [[ -f "$critics" ]]; then
    ctx_n=$((ctx_n+1)); ctx_items="${ctx_items}${ctx_n}. CRITICS.md — prior objections (do NOT repeat them)"$'\n'
  fi
  local prompt
  prompt="You are a hostile reviewer for the project at $PWD.

Topic under review: ${topic}
Diff range under review: ${range_line} (files touched: ${files_line})
Verification commands already run: ${verify_line}

Context to read (in this order):
${ctx_items}
Your job: bring me the strongest objections, not the most polite ones.

For EACH critique you produce:
  - One-line summary (the objection itself).
  - Severity: high | medium | low.
  - Rationale (2-3 sentences with concrete failure mode).
  - WHEN this would break (load, scale, edge case, time horizon).
  - WHY it would break (root cause, not symptom).
  - HOW to fix or mitigate (concrete pattern, library, or pattern name).
  - Suggested disposition: fixed | accept | reject (with reason) | defer (with GAPS link) | verified-no-issue.

Also call out:
  - Assumptions challenged: anything this change takes for granted that may not hold.
  - Proposals: any concrete alternative you'd make instead, each with an evidence bar
    (what would have to be true, or what test would confirm it).

Rules:
  - NO polite endorsements. If you can't find an objection, the prompt is wrong — re-ask.
  - Hostile-reviewer voice: assume reviewer is at a conference Q&A.
  - At 10x scale, what breaks? At 100x cost, what fires? At adversarial input?
  - Cite SKILL §ANCHORS where applicable.
  - Output as Markdown rows compatible with the CRITICS.md table.

End with 'Top 3 things to do BEFORE locking the ADR' as a bullet list."

  echo ""
  echo "================================================================"
  echo "PROMPT (paste into ChatGPT 5.5 Extended Thinking, or use --with-codex):"
  echo "================================================================"
  echo ""
  echo "$prompt"
  echo ""

  # Dispatch + capture the critic's raw response synchronously — the
  # CRITICS.md entry carries the real reply, not just a terminal echo the
  # human has to copy back by hand (RM-2b "receive rows back").
  local raw_output="" dispatched=0 dispatch_rc=0
  if [[ "$with" == "codex" ]]; then
    if _have codex; then
      dispatched=1
      echo "================================================================"
      echo "running: codex exec (model: $codex_model)$( if [[ $auto -eq 1 ]]; then echo " [auto-detected]"; fi )"
      echo "================================================================"
      raw_output="$(CODEX_MODEL="$codex_model" codex exec --model "$codex_model" "$prompt" 2>&1)" || dispatch_rc=$?
      echo "$raw_output"
    elif [[ $auto -eq 0 ]]; then
      _die "'codex' CLI not found on PATH (https://github.com/openai/codex)"
    fi
  elif [[ "$with" == "claude" ]]; then
    if _have claude; then
      dispatched=1
      echo "================================================================"
      echo "running: claude --print  (model: \${ANTHROPIC_MODEL:-opus})"
      echo "================================================================"
      raw_output="$(claude --print "$prompt" 2>&1)" || dispatch_rc=$?
      echo "$raw_output"
    else
      _die "'claude' CLI not found on PATH"
    fi
  fi
  [[ $dispatched -eq 0 ]] && _say "(print-only; pass --with-codex/--with-claude, or install 'codex' on PATH to auto-dispatch)"

  # Cap the captured output at a byte budget (RM-44): a verbose critic CLI's
  # full transcript must never silently balloon CRITICS.md — a future
  # dispatch that reads "CRITICS.md — prior objections" would pay the full
  # cost of every past one. Keep the TAIL (the final answer usually lands at
  # the end of a transcript), regardless of format (JSON events or plain text).
  # The predicate must measure BYTES, not characters: `${#var}` counts
  # characters under a UTF-8 locale, so a 32KB emoji transcript measured
  # 8000 "characters" and skipped the cap entirely (2026-07-10 critic-stage
  # finding #4 — reproduced before this fix). `tail -c` may split one
  # multibyte character at the cut point; acceptable for a raw transcript.
  local raw_cap_bytes=20000 raw_total_bytes=0
  if [[ $dispatched -eq 1 ]]; then
    raw_total_bytes=$(printf '%s' "$raw_output" | wc -c | tr -d ' ')
  fi
  if [[ $dispatched -eq 1 && "${raw_total_bytes:-0}" -gt $raw_cap_bytes ]]; then
    local raw_tail
    raw_tail="$(printf '%s' "$raw_output" | tail -c "$raw_cap_bytes")"
    raw_output="[...truncated: showing the final ${raw_cap_bytes} of ${raw_total_bytes} bytes captured...]
${raw_tail}"
  fi

  [[ $have_critics -eq 0 ]] && return 0

  # A failed dispatch must never masquerade as a real review (RM-43, critic-
  # stage finding #3): record it plainly and skip the critique-row stub
  # entirely, so it can never satisfy CRITICS_STALE's row count.
  if [[ $dispatched -eq 1 && $dispatch_rc -ne 0 ]]; then
    {
      printf '\n### %s — %s\n' "$date_now" "$topic"
      printf '**Critic:** %s\n' "$critic_label"
      printf '**DISPATCH FAILED** (exit %s) — this is NOT a critique; nothing was reviewed.\n\n' "$dispatch_rc"
      # shellcheck disable=SC2016  # literal backticks (Markdown fence), no expansion intended
      printf '<details><summary>Failure output</summary>\n\n```\n%s\n```\n\n</details>\n' "$raw_output"
    } >> "$critics"
    _warn "critic dispatch failed (exit $dispatch_rc) — recorded as a failed dispatch, not a critique"
    return 0
  fi

  # Append a stub row to CRITICS.md, stamped with real provenance + inputs
  # (RM-2b) and the critic's raw response when one was actually dispatched.
  {
    printf '\n### %s — %s\n' "$date_now" "$topic"
    printf '**Critic:** %s\n' "$critic_label"
    printf '**Topic:** %s\n' "$topic"
    printf '**Inputs they had:** diff range: %s; files: %s; verification commands run: %s\n\n' \
      "$range_line" "$files_line" "$verify_line"
    printf '| # | Critique | Severity | Disposition | ADR/GAPS link |\n'
    printf '|---|---|---|---|---|\n'
    printf '| 1 | _(paste verbatim)_ | high/med/low | fixed / accepted / rejected (rationale) / deferred / verified-no-issue | _(ADR-NNNN / GAPS G00N)_ |\n\n'
    printf '**Assumptions challenged:**\n- _(fill in from critic output)_\n\n'
    printf '**Proposals (with evidence bar):**\n- _(fill in from critic output)_\n'
    if [[ $dispatched -eq 1 ]]; then
      # shellcheck disable=SC2016  # literal backticks (Markdown fence), no expansion intended
      printf '\n<details><summary>Raw critic output (auto-captured)</summary>\n\n```\n%s\n```\n\n</details>\n' "$raw_output"
    fi
  } >> "$critics"
  _say "appended CRITICS.md row for: ${topic}"
}

# ---------------------------------------------------------------------------
# gap-to-article <gap-id> [--writer <path>]
#
# Scaffolds an article directory for a resolved + novel implementation gap.
# Refuses to scaffold if the gap is not yet marked resolved+novel in
# docs/gaps/GAPS-Implementation.md (best-effort grep; user can --force).
# ---------------------------------------------------------------------------
cmd_gap_to_article() {
  local gap_id="" writer="agent-scientific-ai-writer" force=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --writer)    writer="$2"; shift 2 ;;
      --writer=*)  writer="${1#--writer=}"; shift ;;
      --force|-f)  force=1; shift ;;
      *)           [[ -z "$gap_id" ]] && gap_id="$1"; shift ;;
    esac
  done
  [[ -z "$gap_id" ]] && _die "usage: atlas gap-to-article <gap-id> [--writer <path>] [--force]"

  local gaps_impl="$PWD/docs/gaps/GAPS-Implementation.md"
  if [[ ! -f "$gaps_impl" ]]; then
    _die "no docs/gaps/GAPS-Implementation.md in $PWD (run 'atlas init' first (style must scaffold this file))"
  fi

  # Best-effort check: gap line should be in the Shipped table with Novel=yes.
  if ! grep -qE "\\b${gap_id}\\b" "$gaps_impl"; then
    _die "gap '${gap_id}' not found in $gaps_impl (use --force to scaffold anyway)"
  fi
  if [[ $force -ne 1 ]]; then
    local line; line=$(grep -E "\\b${gap_id}\\b" "$gaps_impl" | head -1)
    if ! echo "$line" | grep -qiE "(yes|novel)"; then
      _say "WARNING: '${gap_id}' is not marked novel in GAPS-Implementation.md"
      _say "         Articles are only for novel resolved gaps."
      _say "         Use --force to scaffold anyway."
      exit 1
    fi
  fi

  local date_now project slug count next_n art_dir
  date_now="$(date +%Y_%m_%d)"
  project="$(_project_name)"
  # Slug from the gap line — take whatever's after the gap id, slugify.
  slug=$(grep -E "\\b${gap_id}\\b" "$gaps_impl" | head -1 \
         | sed -E "s/.*${gap_id}[^a-zA-Z0-9]+//; s/[|].*//; s/[^a-zA-Z0-9]+/-/g; s/^-+//; s/-+$//; s/(.{40}).*/\\1/")
  [[ -z "$slug" ]] && slug="novel-result"
  # Next article number: count existing Article_NNNN_ dirs and add one.
  count=0
  if [[ -d "$writer" ]]; then
    count=$(find "$writer" -maxdepth 1 -type d -name '*_Article_*' 2>/dev/null | wc -l | tr -d ' ')
  fi
  next_n=$(printf "%04d" $((count + 1)))
  art_dir="${writer}/${date_now}_Article_${next_n}_${slug}"

  mkdir -p "$art_dir"/{figures,code}

  cat > "$art_dir/STATUS.md" <<EOF
# STATUS — Article ${next_n} (${slug})

- **Source gap:** ${gap_id} in [\`docs/gaps/GAPS-Implementation.md\`]($PWD/docs/gaps/GAPS-Implementation.md)
- **Project:** ${project}
- **Created:** ${date_now}

## Pipeline
- [ ] REPORT.md drafted from the gap row
- [ ] Figures extracted from benchmarks/results/
- [ ] Code excerpted into code/
- [ ] Critic pass (CRITICS.md — \`atlas critique ${gap_id}\`)
- [ ] Cross-reference SKILL anchors + ADR numbers
- [ ] Submit / publish
EOF

  cat > "$art_dir/REPORT.md" <<EOF
# Article ${next_n} — ${slug}

> Draft. Source: ${gap_id} (see [\`docs/gaps/GAPS-Implementation.md\`]($PWD/docs/gaps/GAPS-Implementation.md)).

## TL;DR
_(3-5 lines)_

## Background
_(field context — pull from \`research/\` notes)_

## Method
_(what we did — pull from ADR + SKILL anchors)_

## Results
_(figures from \`figures/\`; reproducible from \`code/\`; benchmark JSONs from \`$PWD/benchmarks/results/\`)_

## Comparison
_(rows referencing \`docs/gaps/GAPS-Landscape-Github.md\` and \`-Papers.md\`)_

## Limitations & future work
_(open items → new GAPS rows)_

## References
- SKILL anchors: §...
- ADRs: docs/adr/NNNN-...
- Code: \`code/\` in this dir; canonical at \`$PWD/...\`
EOF

  cat > "$art_dir/CODE.md" <<EOF
# CODE — Article ${next_n}

- **Canonical implementation:** \`$PWD/...\`  *(fill in path + SHA)*
- **Extracted (reproducible):** \`code/\` in this dir

## Reproducibility
\`\`\`bash
cd code/
# (commands to reproduce the headline result)
\`\`\`
EOF

  _say "scaffolded article dir: $art_dir"
  _say "next:"
  _say "  1. flesh out REPORT.md"
  _say "  2. pull figures from $PWD/benchmarks/results/"
  _say "  3. run 'atlas critique ${gap_id}' for a brutal pass"
}

# ---------------------------------------------------------------------------
# cost
#
# Parse ATLAS.md §C and §GPU tables; emit current-state summary.
# Flag: components NOT at $0 idle, missing verification dates, stale GPU
# price audits (> 14 days).
# ---------------------------------------------------------------------------
cmd_cost() {
  local atlas="$PWD/ATLAS.md"
  [[ -f "$atlas" ]] || _die "no ATLAS.md in $PWD"

  echo "atlas cost — $(date +%Y-%m-%d)"
  echo "==================================="
  echo ""

  # Extract §C (Cost & scaling) and §GPU sections via awk between headings.
  local c_block gpu_block
  c_block=$(awk '/^## C\. /{flag=1; next} /^## [A-Z]/{flag=0} flag' "$atlas" || true)
  gpu_block=$(awk '/^## GPU\. /{flag=1; next} /^## [A-Z]/{flag=0} flag' "$atlas" || true)

  if [[ -z "$c_block" ]]; then
    echo "  (no §C section found in ATLAS.md — Cost & scaling section not in this style)"
  else
    echo "### §C — Cost & scaling"
    echo ""
    echo "$c_block" | grep -E '^\|' || true
    echo ""
    echo "Flags:"
    local flagged=0
    # Only inspect REAL data rows: start with '|', have ≥4 cells, not separator,
    # not header, not placeholder (cells starting with `_(`).
    while IFS= read -r row; do
      [[ "$row" =~ ^\|[[:space:]]*---  ]] && continue
      [[ "$row" =~ ^\|[[:space:]]*Component[[:space:]]*\| ]] && continue
      [[ "$row" =~ ^\|[[:space:]]*\_\( ]] && continue
      local n_pipes; n_pipes=$(echo "$row" | tr -cd '|' | wc -c | tr -d ' ')
      [[ "$n_pipes" -lt 4 ]] && continue
      # col 3 = at-rest cost ; col 5 = verified date
      local cost ver
      cost=$(echo "$row" | awk -F'|' '{ gsub(/^ +| +$/, "", $3); print $3 }')
      ver=$(echo  "$row" | awk -F'|' '{ gsub(/^ +| +$/, "", $5); print $5 }')
      # Idle cost not $0?
      if [[ -n "$cost" && ! "$cost" =~ ^\$0 && ! "$cost" =~ ^_\( ]]; then
        echo "  ! non-zero idle cost row: $row"
        flagged=$((flagged+1))
      fi
      # Missing verified date (or still placeholder)?
      if [[ -z "$ver" || "$ver" =~ ^_\( ]]; then
        echo "  ! missing verified date: $row"
        flagged=$((flagged+1))
      fi
    done <<< "$c_block"
    [[ $flagged -eq 0 ]] && echo "  ok — no flags."
  fi

  echo ""
  if [[ -z "$gpu_block" ]]; then
    echo "  (no §GPU section in ATLAS.md — skip if this project doesn't use GPU)"
  else
    echo "### §GPU — GPU sourcing"
    echo ""
    echo "$gpu_block" | grep -E '^\|' || true
    echo ""
    echo "Reminders:"
    echo "  - Run 'sky show-gpus --cloud all' weekly to refresh."
    echo "  - If any provider beats the primary by >20%, update §GPU rows."
    echo "  - SKILL §GPU-PRICE-AUDIT enforces this."
  fi

  echo ""
}

# ---------------------------------------------------------------------------
# mirror — enforce SKILL §GITLAB-PRIVATE-GITHUB-PUBLIC.
#
# Subcommands:
#   atlas mirror init    scaffold .atlas/mirror.allow (+ optional GH Action)
#   atlas mirror push    push allowlisted refs to the public remote
#   atlas mirror status  show config + what would be pushed
#
# Defaults:
#   remote name = "public"  (never "origin" — origin is canonical/private)
#   pattern     = staged     (push to a 'public' branch on the remote, a
#                              GitHub Action promotes to main + deletes the
#                              staging branch)
#
# Config file: .atlas/mirror.allow — one git refspec per line.
#   refs/heads/main:refs/heads/public          # default staged
#   refs/tags/v*:refs/tags/v*                  # tags pass through
#   refs/heads/release/*:refs/heads/release/*  # release branches
# ---------------------------------------------------------------------------

_mirror_allow_path() { echo "$PWD/.atlas/mirror.allow"; }

_mirror_assert_safe_remote() {
  local remote="$1"
  if [[ "$remote" == "origin" ]]; then
    _die "refusing to mirror to 'origin' — origin is reserved for the canonical (private) remote (SKILL §GITLAB-PRIVATE-GITHUB-PUBLIC)"
  fi
  if ! git remote get-url "$remote" >/dev/null 2>&1; then
    _die "remote '$remote' not configured. Add it first:
    git remote add $remote git@github.com:you/project.git"
  fi
}

cmd_mirror() {
  local sub="${1:-help}"; shift || true
  case "$sub" in
    init)    cmd_mirror_init    "$@" ;;
    push)    cmd_mirror_push    "$@" ;;
    status)  cmd_mirror_status  "$@" ;;
    help|"") cat <<'HELP'
atlas mirror — enforce SKILL §GITLAB-PRIVATE-GITHUB-PUBLIC

  atlas mirror init [--staged | --direct | --dual-repo] [--remote NAME] [--public-repo URL] [--force]
      Scaffold .atlas/mirror.allow.

      --staged    (default) push to a 'public' branch on ONE GitHub repo;
                  Action promotes 'public' → 'main' + auto-deletes the
                  staging branch. Renders .github/workflows/atlas-promote.yml.
                  Public repo at rest shows only 'main' + tags.

      --direct    push straight to 'main' on ONE GitHub repo. No staging,
                  no Action. Trust your allowlist.

      --dual-repo MAX PARANOIA: two GitHub repos. Push lands on a PRIVATE
                  staging repo ('--remote staging' by default). An Action
                  there pushes only 'main' + tags to the PUBLIC release
                  repo at '--public-repo URL' (required).
                  Renders .github/workflows/atlas-promote-to-public.yml
                  for the cross-repo push. Needs a deploy key.

      --remote NAME      default: 'public' (or 'staging' under --dual-repo).
                          MUST NOT be 'origin'.
      --public-repo URL  required with --dual-repo. Git URL of the PUBLIC repo.

  atlas mirror push [--remote NAME] [--dry-run]
      Push every allowlisted refspec to the public remote.

  atlas mirror status [--remote NAME]
      Show config + what would be pushed.

Auth setup: see docs at https://github.com/Abbasi-Alain/atlas#auth
HELP
      ;;
    *) _die "unknown 'mirror' subcommand: $sub (try 'atlas mirror help')" ;;
  esac
}

cmd_mirror_init() {
  local pattern="staged" remote="public" force=0
  local public_repo=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --staged)      pattern="staged";   shift ;;
      --direct)      pattern="direct";   shift ;;
      --dual-repo)   pattern="dual";     shift ;;
      --remote)      remote="$2"; shift 2 ;;
      --remote=*)    remote="${1#--remote=}"; shift ;;
      --public-repo) public_repo="$2"; shift 2 ;;
      --public-repo=*) public_repo="${1#--public-repo=}"; shift ;;
      --force|-f)    force=1; shift ;;
      *) shift ;;
    esac
  done
  [[ "$remote" == "origin" ]] && _die "remote name 'origin' is reserved for the canonical/private remote"
  if [[ "$pattern" == "dual" ]]; then
    # dual-repo defaults: remote is named 'staging' (the private GH repo);
    # the CI later pushes to a 'public' repo (URL = $public_repo, required).
    [[ "$remote" == "public" ]] && remote="staging"
    [[ -z "$public_repo" ]] && _die "usage: --dual-repo requires --public-repo <git-url>"
  fi

  local allow; allow="$(_mirror_allow_path)"
  mkdir -p "$(dirname "$allow")"

  if [[ -f "$allow" && $force -ne 1 ]]; then
    _say "$allow exists — skip (--force to overwrite)"
  else
    case "$pattern" in
      staged)
        cat > "$allow" <<EOF
# .atlas/mirror.allow — mirror push allowlist
#
# One git refspec per line: <local-ref>:<remote-ref>
# Lines starting with # are comments.
# 'atlas mirror push' refuses any push that isn't in this file.
#
# Default: STAGED pattern.
#   local 'main' goes to a 'public' staging branch on the remote.
#   A GitHub Action (.github/workflows/atlas-promote.yml) runs release
#   gates, fast-forwards 'main' from 'public', then deletes the
#   staging branch. At rest, your public repo only shows 'main' and tags.

refs/heads/main:refs/heads/public
refs/tags/v*:refs/tags/v*
EOF
        _say "wrote $allow (staged pattern)"
        ;;
      direct)
        cat > "$allow" <<EOF
# .atlas/mirror.allow — mirror push allowlist
#
# One git refspec per line: <local-ref>:<remote-ref>
# 'atlas mirror push' refuses any push that isn't in this file.
#
# DIRECT pattern: local 'main' goes straight to remote 'main'.
# No staging branch, no GitHub Action. Trust your allowlist.

refs/heads/main:refs/heads/main
refs/tags/v*:refs/tags/v*
EOF
        _say "wrote $allow (direct pattern)"
        ;;
      dual)
        cat > "$allow" <<EOF
# .atlas/mirror.allow — mirror push allowlist (DUAL-REPO pattern)
#
# One git refspec per line: <local-ref>:<remote-ref>
# 'atlas mirror push' refuses any push that isn't in this file.
#
# DUAL-REPO pattern (maximum paranoia):
#   - 'atlas mirror push' lands on the private '${remote}' GitHub repo.
#   - A GitHub Action there pushes only main + tags to the PUBLIC repo:
#       ${public_repo}
#   - The public repo never sees branches, PRs, or any other refs.
#
# This is two repos. The staging one is private; the public one only
# ever receives 'main' and tags.

refs/heads/main:refs/heads/main
refs/tags/v*:refs/tags/v*
refs/heads/release/*:refs/heads/release/*
EOF
        _say "wrote $allow (dual-repo pattern)"
        ;;
    esac
  fi

  # Drop the dual-repo cross-repo promote action (separate workflow file).
  if [[ "$pattern" == "dual" ]]; then
    local wf="$PWD/.github/workflows/atlas-promote-to-public.yml"
    mkdir -p "$(dirname "$wf")"
    if [[ -f "$wf" && $force -ne 1 ]]; then
      _say "$wf exists — skip (--force to overwrite)"
    else
      cat > "$wf" <<YAML
# atlas-promote-to-public.yml — generated by 'atlas mirror init --dual-repo'.
#
# This file lives in the PRIVATE STAGING GitHub repo. It fires on push to
# main / release/* / v*. It runs release gates, then pushes ONLY main + tags
# to the PUBLIC repo:
#
#     ${public_repo}
#
# The PUBLIC repo never sees feature branches, PRs, or any other refs.
#
# Required secrets in the staging GH repo:
#   PUBLIC_REPO_DEPLOY_KEY    SSH private key with write access to ${public_repo}
#                              (generate a deploy key; ed25519; upload public
#                               half to public repo with 'write' permission)

name: atlas-promote-to-public

on:
  push:
    branches: [main, "release/**"]
    tags:     ["v*"]

permissions:
  contents: read

jobs:
  promote:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout staging
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      # ─── Release gates ────────────────────────────────────────────
      - name: atlas check
        run: |
          curl -fsSL https://raw.githubusercontent.com/Abbasi-Alain/atlas/main/install.sh | bash
          ~/.local/bin/atlas check

      # ─── Configure SSH for the public repo push ───────────────────
      - name: Setup deploy key
        uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: \${{ secrets.PUBLIC_REPO_DEPLOY_KEY }}

      # ─── Push allowed refs to the public repo ─────────────────────
      - name: Push main + tags to public repo
        run: |
          git remote add public ${public_repo}
          # main goes straight to main on the public repo
          if [ "\${{ github.ref }}" = "refs/heads/main" ]; then
            git push public HEAD:refs/heads/main
          fi
          # tags pass through unchanged
          if [[ "\${{ github.ref }}" == refs/tags/v* ]]; then
            git push public "\${{ github.ref }}"
          fi
          # release/* branches mirror through
          if [[ "\${{ github.ref }}" == refs/heads/release/* ]]; then
            git push public "\${{ github.ref }}":"\${{ github.ref }}"
          fi
YAML
      _say "wrote $wf"
    fi
  fi

  # Drop the GitHub Action only for the staged pattern.
  if [[ "$pattern" == "staged" ]]; then
    local wf="$PWD/.github/workflows/atlas-promote.yml"
    mkdir -p "$(dirname "$wf")"
    if [[ -f "$wf" && $force -ne 1 ]]; then
      _say "$wf exists — skip (--force to overwrite)"
    else
      cat > "$wf" <<'YAML'
# atlas-promote.yml — generated by 'atlas mirror init --staged'.
#
# Fires when 'public' (the mirror staging branch) is pushed. Runs release
# gates, fast-forwards 'main' to 'public', then deletes 'public' so the
# repo only shows 'main' + tags at rest.

name: atlas-promote

on:
  push:
    branches: [public]

permissions:
  contents: write

jobs:
  promote:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout public
        uses: actions/checkout@v4
        with:
          ref: public
          fetch-depth: 0

      # ─── Release gates ────────────────────────────────────────────
      # Replace these with your project's real gates (atlas check is the
      # baseline; add tests/lint/secret-scan as needed).
      - name: atlas check
        run: |
          curl -fsSL https://raw.githubusercontent.com/Abbasi-Alain/atlas/main/install.sh | bash
          ~/.local/bin/atlas check

      # ─── Promote ──────────────────────────────────────────────────
      - name: Fast-forward main from public
        run: |
          git config user.name  "atlas-bot"
          git config user.email "atlas-bot@users.noreply.github.com"
          git fetch origin main:main || git checkout -b main
          git checkout main
          git merge --ff-only public
          git push origin main

      # ─── Auto-delete the staging branch ──────────────────────────
      - name: Delete the public staging branch
        run: |
          git push origin --delete public
YAML
      _say "wrote $wf"
    fi
  fi

  _say ""
  _say "next:"
  if [[ "$pattern" == "dual" ]]; then
    _say "  1. create the PRIVATE staging GitHub repo + add as '${remote}' remote:"
    _say "       git remote add ${remote} git@github.com:you/${PWD##*/}-staging.git"
    _say "  2. create the PUBLIC release GitHub repo at: ${public_repo}"
    _say "  3. generate a deploy key with WRITE access to the public repo:"
    _say "       ssh-keygen -t ed25519 -f /tmp/deploy_key -C 'atlas-promote' -N ''"
    _say "       cat /tmp/deploy_key.pub      # add to public repo deploy keys (write enabled)"
    _say "       cat /tmp/deploy_key          # add as PUBLIC_REPO_DEPLOY_KEY secret in staging repo"
    _say "  4. atlas mirror status            # see what would be pushed"
    _say "  5. atlas mirror push              # push to the staging repo"
    _say "  6. (GitHub Action takes over and pushes main/tags to the public repo)"
  else
    _say "  1. git remote add ${remote} git@github.com:you/project.git"
    _say "  2. atlas mirror status            # see what would be pushed"
    _say "  3. atlas mirror push              # actually push"
  fi
  _say ""
  _say "auth: 'atlas auth login' wires up SSH or vendor CLIs for both GitHub + GitLab"
}

cmd_mirror_push() {
  local remote="public" dry=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --remote)    remote="$2"; shift 2 ;;
      --remote=*)  remote="${1#--remote=}"; shift ;;
      --dry-run|-n) dry=1; shift ;;
      *) shift ;;
    esac
  done

  local allow; allow="$(_mirror_allow_path)"
  [[ -f "$allow" ]] || _die "no .atlas/mirror.allow — run 'atlas mirror init' first"
  _mirror_assert_safe_remote "$remote"

  local n_pushed=0 n_failed=0
  while IFS= read -r line; do
    # strip whitespace + comments + blank lines
    line="$(echo "$line" | sed -E 's/[[:space:]]+#.*$//; s/^[[:space:]]+|[[:space:]]+$//g')"
    [[ -z "$line" || "${line:0:1}" == "#" ]] && continue
    # MUST be a refspec (contain ':' between two refs)
    if [[ "$line" != *":"* ]]; then
      _say "WARNING: bad line in $allow (no ':'): $line"
      continue
    fi
    if [[ "$dry" == "1" ]]; then
      echo "  would: git push $remote '$line'"
    else
      echo "  push:  git push $remote '$line'"
      if git push "$remote" "$line"; then
        n_pushed=$((n_pushed+1))
      else
        n_failed=$((n_failed+1))
        _say "WARNING: refspec '$line' failed (may be no matching local refs)"
      fi
    fi
  done < "$allow"

  if [[ "$dry" == "1" ]]; then
    _say "dry-run — no refs pushed"
  else
    _say "done — pushed $n_pushed refspec(s), $n_failed failed"
    [[ $n_failed -gt 0 ]] && exit 1
  fi
}

cmd_mirror_status() {
  local remote="public"
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --remote)   remote="$2"; shift 2 ;;
      --remote=*) remote="${1#--remote=}"; shift ;;
      *) shift ;;
    esac
  done

  local allow; allow="$(_mirror_allow_path)"
  echo "atlas mirror status"
  echo "==================="
  echo ""
  echo "config:     $allow"
  if [[ ! -f "$allow" ]]; then
    echo "  (none — run 'atlas mirror init')"
    return 0
  fi
  echo "remote:     $remote"
  if git remote get-url "$remote" >/dev/null 2>&1; then
    echo "  url:      $(git remote get-url "$remote")"
  else
    echo "  url:      (not configured — git remote add $remote ...)"
  fi
  if git remote get-url origin >/dev/null 2>&1; then
    echo "origin:     $(git remote get-url origin)  (canonical / never mirrored)"
  fi
  echo ""
  echo "allowed refspecs:"
  grep -vE '^[[:space:]]*(#|$)' "$allow" | sed 's/^/  /' || true
  echo ""
  echo "GH action:"
  if [[ -f "$PWD/.github/workflows/atlas-promote.yml" ]]; then
    echo "  .github/workflows/atlas-promote.yml present (staged pattern)"
  else
    echo "  (none — direct pattern or not configured)"
  fi
}

# ---------------------------------------------------------------------------
# adr — manage Architecture Decision Records.
#
# atlas adr add "<title>"   scaffold docs/adr/NNNN-<slug>.md from
#                            docs/adr/0000-template.md (auto-numbered).
#                            Append a row to ATLAS.md §A3 ADR index if
#                            present (best-effort; falls back to printing
#                            the row to paste manually).
# atlas adr list             list every NNNN-*.md in docs/adr/ with status.
# ---------------------------------------------------------------------------

_slugify() {
  echo "$1" | tr '[:upper:]' '[:lower:]' \
    | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//' \
    | cut -c1-50
}

cmd_adr() {
  local sub="${1:-help}"; shift || true
  case "$sub" in
    add)  cmd_adr_add "$@" ;;
    list) cmd_adr_list "$@" ;;
    help|"") cat <<'HELP'
atlas adr — manage Architecture Decision Records.

  atlas adr add "<title>"
      Auto-numbers (reads docs/adr/ for last NNNN).
      Renders docs/adr/0000-template.md → docs/adr/NNNN-<slug>.md.
      Pre-fills title, date, status=Proposed.
      Appends a row to ATLAS.md §A3 ADR index (best-effort).

  atlas adr list
      List every NNNN-*.md with status.
HELP
      ;;
    *) _die "unknown 'adr' subcommand: $sub" ;;
  esac
}

cmd_adr_add() {
  local title="${1:-}"
  [[ -z "$title" ]] && _die 'usage: atlas adr add "<title>"'

  local adr_dir="$PWD/docs/adr"
  local template="$adr_dir/0000-template.md"
  [[ -d "$adr_dir" ]] || _die "no $adr_dir — run 'atlas init' first (style must scaffold this file)"
  [[ -f "$template" ]] || _die "no $template (the ADR template from the current style)"

  # Next number: max existing NNNN (excluding 0000-template) + 1.
  # Use a glob + bash string comparison — shellcheck SC2010-clean.
  local max_n=""
  local f base n
  shopt -s nullglob
  for f in "$adr_dir"/[0-9][0-9][0-9][0-9]-*.md; do
    [[ -f "$f" ]] || continue
    base="$(basename "$f")"
    n="${base:0:4}"
    [[ "$n" == "0000" ]] && continue
    if [[ -z "$max_n" || "$n" > "$max_n" ]]; then max_n="$n"; fi
  done
  shopt -u nullglob
  local next_n
  if [[ -z "$max_n" ]]; then
    next_n="0001"
  else
    next_n=$(printf "%04d" $((10#$max_n + 1)))
  fi

  local slug; slug="$(_slugify "$title")"
  local out="$adr_dir/${next_n}-${slug}.md"
  local date_now; date_now="$(date +%Y-%m-%d)"

  # Render from template: replace ADR-0000 + Template + date placeholders.
  sed \
    -e "s|^# ADR-0000 — Template|# ADR-${next_n} — ${title}|" \
    -e "s|^- \*\*Status:\*\* Proposed \\| Accepted \\| Deprecated \\| Superseded.*|- **Status:** Proposed|" \
    -e "s|^- \*\*Date:\*\* YYYY-MM-DD|- **Date:** ${date_now}|" \
    "$template" > "$out"

  _say "wrote $out"

  # Best-effort: append a row to ATLAS.md §A3 ADR index. Find the line
  # '| _(fill in)_ | | | |' inside §A3 and insert before it.
  local atlas="$PWD/ATLAS.md"
  if [[ -f "$atlas" ]] && grep -q '^### A3\. ADR index' "$atlas"; then
    local row="| ${next_n} | ${title} | Proposed | ${date_now} |"
    # Try to insert before the '_(fill in)_' placeholder row in §A3.
    if grep -q '^| _(fill in)_ | | | |' "$atlas"; then
      # macOS-safe in-place sed
      sed -i.bak "/^| _(fill in)_ | | | |/i\\
$row
" "$atlas" && rm "$atlas.bak"
      _say "updated ATLAS.md §A3 with new row"
    else
      _say "ATLAS.md §A3 present but no '(fill in)' placeholder — add this row manually:"
      echo "  $row"
    fi
  else
    _say "(no ATLAS.md §A3 ADR index found — skip)"
  fi

  _say ""
  _say "next:"
  _say "  1. fill in Context / Options / Decision / Consequences in $out"
  _say "  2. run 'atlas critique \"$title\"' for a hostile review BEFORE locking"
  _say "  3. flip Status: Proposed → Accepted in the same commit it ships"
}

cmd_adr_list() {
  local adr_dir="$PWD/docs/adr"
  [[ -d "$adr_dir" ]] || _die "no $adr_dir"
  printf "%-6s %-10s %s\n" "#" "Status" "Title"
  printf "%-6s %-10s %s\n" "----" "------" "-----"
  for f in "$adr_dir"/[0-9][0-9][0-9][0-9]-*.md; do
    [[ -f "$f" ]] || continue
    local base; base="$(basename "$f" .md)"
    local n="${base:0:4}"
    [[ "$n" == "0000" ]] && continue
    local title; title=$(grep -m1 '^# ADR-' "$f" 2>/dev/null \
      | sed -E 's/^# ADR-[0-9]{4} — //')
    local status; status=$(grep -m1 '^- \*\*Status:\*\*' "$f" 2>/dev/null \
      | sed -E 's/.*Status:\*\* //; s/ .*//')
    printf "%-6s %-10s %s\n" "$n" "$status" "$title"
  done
}

# ---------------------------------------------------------------------------
# research — manage deep-research notes.
#
# atlas research add "<topic>"  scaffold research/NNN-<slug>.md following the
#                                schema documented in research/README.md.
# atlas research list            list every NNN-*.md in research/.
# ---------------------------------------------------------------------------

cmd_research() {
  local sub="${1:-help}"; shift || true
  case "$sub" in
    add)  cmd_research_add "$@" ;;
    list) cmd_research_list "$@" ;;
    help|"") cat <<'HELP'
atlas research — manage deep-research notes.

  atlas research add "<topic>"
      Auto-numbers (reads research/ for last NNN).
      Renders a fresh research/NNN-<slug>.md with TL;DR / SOTA /
      Approaches / Open questions / References sections.

  atlas research list
      List every NNN-*.md in research/.
HELP
      ;;
    *) _die "unknown 'research' subcommand: $sub" ;;
  esac
}

cmd_research_add() {
  local topic="${1:-}"
  [[ -z "$topic" ]] && _die 'usage: atlas research add "<topic>"'

  local research_dir="$PWD/research"
  [[ -d "$research_dir" ]] || _die "no $research_dir — run 'atlas init' first (style must scaffold this file)"

  # Next number: max NNN in research/ + 1. Use a glob — SC2010-clean.
  local max_n=""
  local f base n
  shopt -s nullglob
  for f in "$research_dir"/[0-9][0-9][0-9]-*.md; do
    [[ -f "$f" ]] || continue
    base="$(basename "$f")"
    n="${base:0:3}"
    if [[ -z "$max_n" || "$n" > "$max_n" ]]; then max_n="$n"; fi
  done
  shopt -u nullglob
  local next_n
  if [[ -z "$max_n" ]]; then
    next_n="001"
  else
    next_n=$(printf "%03d" $((10#$max_n + 1)))
  fi

  local slug; slug="$(_slugify "$topic")"
  local out="$research_dir/${next_n}-${slug}.md"
  local date_now; date_now="$(date +%Y-%m-%d)"

  cat > "$out" <<EOF
# Research ${next_n} — ${topic}

- **Date:** ${date_now}
- **Source:** _(ChatGPT 5.5 Extended Thinking | Claude Opus 4.x | manual | other)_
- **Prompts:** _(one-line summary or link to the saved prompt)_

## TL;DR
_(3-5 lines — the headline result)_

## State of the art
_(what exists, who built it, what's the gap)_

## Approaches

| # | Approach | Pros | Cons | Sources |
|---|---|---|---|---|
| 1 | _(approach)_ | | | |
| 2 | _(approach)_ | | | |

## Open questions
_(unresolved — these become CRITICS.md rows or docs/gaps/ rows)_

## Decision pointer
- If this informs a decision → write \`docs/adr/NNNN-<slug>.md\`.
- If this surfaces a gap → add a row to \`docs/gaps/GAPS-Landscape-{Github,Papers}.md\`.
- If this enables an implementation → add a row to \`docs/gaps/GAPS-Implementation.md\`.

## References
- _(arXiv:NNNN.NNNNN — Title)_
- _(github.com/org/repo — what it does)_
- _(blog/talk/video — link)_
EOF

  _say "wrote $out"
  _say ""
  _say "next:"
  _say "  1. fill in TL;DR + State of the art (paste from ChatGPT 5.5)"
  _say "  2. run 'atlas critique \"$topic\"' for a hostile review of the approaches"
  _say "  3. follow the 'Decision pointer' section to GAPS / ADR / impl"
}

cmd_research_list() {
  local research_dir="$PWD/research"
  [[ -d "$research_dir" ]] || _die "no $research_dir"
  printf "%-5s %-12s %s\n" "#" "Date" "Topic"
  printf "%-5s %-12s %s\n" "---" "------" "-----"
  for f in "$research_dir"/[0-9][0-9][0-9]-*.md; do
    [[ -f "$f" ]] || continue
    local base; base="$(basename "$f" .md)"
    local n="${base:0:3}"
    local title; title=$(grep -m1 '^# Research ' "$f" 2>/dev/null \
      | sed -E 's/^# Research [0-9]{3} — //')
    local date_; date_=$(grep -m1 '^- \*\*Date:\*\*' "$f" 2>/dev/null \
      | sed -E 's/.*Date:\*\* //')
    printf "%-5s %-12s %s\n" "$n" "$date_" "$title"
  done
}

# ---------------------------------------------------------------------------
# auth — log in to GitHub + GitLab side-by-side.
#
# Two methods:
#   ssh     — generate ~/.ssh/id_ed25519_{github,gitlab} + ~/.ssh/config.
#             Best for CI / multi-machine / headless.
#   vendor  — brew install gh + glab + run their auth flows (browser-based).
#             Best for one-machine convenience.
#
# Both are idempotent: re-running detects what's done and skips.
# ---------------------------------------------------------------------------

cmd_auth() {
  local sub="${1:-help}"; shift || true
  case "$sub" in
    login)  cmd_auth_login  "$@" ;;
    status) cmd_auth_status "$@" ;;
    help|"") cat <<'HELP'
atlas auth — log in to GitHub + GitLab.

  atlas auth login [--method ssh|vendor] [--email <email>]
      Without --method, asks interactively.
      --method ssh     generate ed25519 keys, configure ~/.ssh/config
                       (idempotent; preserves existing config).
      --method vendor  brew install gh + glab, then `gh auth login`
                       and `glab auth login` (browser flow).
      --email          email for SSH key comments (ssh method only).

  atlas auth status
      Show what's authenticated:
        - gh / glab installed + logged in?
        - SSH keys present + ~/.ssh/config configured?
        - ssh -T github.com / gitlab.com results.
HELP
      ;;
    *) _die "unknown 'auth' subcommand: $sub" ;;
  esac
}

_auth_pick_method() {
  echo "atlas auth — pick a method:"
  echo ""
  echo "  1) SSH        (per-host ed25519 keys + ~/.ssh/config)"
  echo "     pros: works in CI, one-time setup, more secure for shared machines"
  echo "     cons: more steps to generate + upload keys to each host"
  echo ""
  echo "  2) Vendor CLI (brew install gh + glab; browser-based login)"
  echo "     pros: 60-second setup, tokens stored in OS keychain"
  echo "     cons: requires gh + glab installed; one-machine convenience"
  echo ""
  printf "Choose [1 = ssh, 2 = vendor]: "
  read -r choice
  case "$choice" in
    1|ssh)    echo "ssh" ;;
    2|vendor) echo "vendor" ;;
    *) _die "invalid choice: $choice" ;;
  esac
}

cmd_auth_login() {
  local method="" email=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --method)   method="$2"; shift 2 ;;
      --method=*) method="${1#--method=}"; shift ;;
      --email)    email="$2"; shift 2 ;;
      --email=*)  email="${1#--email=}"; shift ;;
      *) shift ;;
    esac
  done
  [[ -z "$method" ]] && method="$(_auth_pick_method)"
  case "$method" in
    ssh)    _auth_login_ssh    "$email" ;;
    vendor) _auth_login_vendor ;;
    *) _die "unknown --method '$method' (try: ssh | vendor)" ;;
  esac
}

# ---------- vendor (brew + gh + glab) ----------

_auth_brew_install() {
  if _have brew; then
    _say "brew is installed"
  else
    cat <<'EOF'

atlas auth: 'brew' is not on PATH.

On macOS or Linuxbrew:
  /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

On Debian/Ubuntu without brew:
  sudo apt install gh                            # GitHub CLI
  # glab via official deb:
  curl -sLO https://gitlab.com/gitlab-org/cli/-/releases/permalink/latest/downloads/glab_amd64.deb
  sudo apt install ./glab_amd64.deb

On Windows:
  winget install --id GitHub.cli
  winget install --id GitLab.glab

After installing brew (or your platform's equivalent), re-run 'atlas auth login --method vendor'.
EOF
    exit 1
  fi
  if ! _have gh;   then _say "installing gh   (brew install gh)";   brew install gh   || _die "brew install gh failed"; fi
  if ! _have glab; then _say "installing glab (brew install glab)"; brew install glab || _die "brew install glab failed"; fi
}

_auth_login_vendor() {
  _auth_brew_install
  echo ""
  if gh auth status >/dev/null 2>&1; then
    _say "gh: already authenticated ($(gh auth status 2>&1 | grep -m1 'account' || echo 'logged in'))"
  else
    _say "gh: launching 'gh auth login' (browser flow)"
    gh auth login
  fi
  echo ""
  if glab auth status >/dev/null 2>&1; then
    _say "glab: already authenticated"
  else
    _say "glab: launching 'glab auth login' (browser flow)"
    glab auth login
  fi
  echo ""
  _say "done. Now add remotes with HTTPS URLs:"
  _say "  git remote add origin https://gitlab.com/you/project.git"
  _say "  git remote add public https://github.com/you/project.git"
  _say ""
  _say "verify: atlas auth status"
}

# ---------- ssh (per-host keys + ~/.ssh/config) ----------

_auth_login_ssh() {
  local email="$1"
  if [[ -z "$email" ]]; then
    printf "atlas auth (ssh): email for key comments (e.g. you@example.com): "
    read -r email
  fi
  [[ -z "$email" ]] && _die "email is required (--email <email>)"

  mkdir -p "$HOME/.ssh"
  chmod 700 "$HOME/.ssh"

  local gh_key="$HOME/.ssh/id_ed25519_github"
  local gl_key="$HOME/.ssh/id_ed25519_gitlab"

  # Generate keys idempotently.
  if [[ ! -f "$gh_key" ]]; then
    _say "generating GitHub key: $gh_key"
    ssh-keygen -t ed25519 -f "$gh_key" -C "$email" -N "" -q
  else
    _say "GitHub key exists: $gh_key (skip)"
  fi
  if [[ ! -f "$gl_key" ]]; then
    _say "generating GitLab key: $gl_key"
    ssh-keygen -t ed25519 -f "$gl_key" -C "$email" -N "" -q
  else
    _say "GitLab key exists: $gl_key (skip)"
  fi

  # Update ~/.ssh/config idempotently with a marker block.
  local cfg="$HOME/.ssh/config"
  touch "$cfg" && chmod 600 "$cfg"
  local marker_start="# atlas-auth:start"
  local marker_end="# atlas-auth:end"
  local block
  block=$(cat <<EOF
$marker_start
Host github.com
  HostName github.com
  User git
  IdentityFile $gh_key
  IdentitiesOnly yes

Host gitlab.com
  HostName gitlab.com
  User git
  IdentityFile $gl_key
  IdentitiesOnly yes
$marker_end
EOF
)
  if grep -q "$marker_start" "$cfg"; then
    # Replace the existing block via the pair-validating helper — a broken
    # marker pair must hard-error rather than truncate the user's SSH config
    # (§AWK-MULTILINE-V for the temp-file/getline part; 2026-07-10 critic
    # finding #1 for the pair validation).
    local block_file; block_file="$(mktemp)"
    printf '%s\n' "$block" > "$block_file"
    _replace_marker_block "$cfg" "$marker_start" "$marker_end" "$block_file"
    rm -f "$block_file"
    _say "updated $cfg (atlas-auth block replaced)"
  else
    printf "\n%s\n" "$block" >> "$cfg"
    _say "appended atlas-auth block to $cfg"
  fi

  # Optional: add to ssh-agent if present (macOS Keychain or running agent).
  if _have ssh-add && [[ -n "${SSH_AUTH_SOCK:-}" ]]; then
    ssh-add "$gh_key" 2>/dev/null || true
    ssh-add "$gl_key" 2>/dev/null || true
    _say "keys added to ssh-agent"
  fi

  echo ""
  echo "================================================================"
  echo "Upload these public keys (copy/paste):"
  echo "================================================================"
  echo ""
  echo "GitHub  →  https://github.com/settings/ssh/new"
  echo ""
  cat "$gh_key.pub"
  echo ""
  echo "GitLab  →  https://gitlab.com/-/user_settings/ssh_keys"
  echo ""
  cat "$gl_key.pub"
  echo ""
  echo "After uploading, verify with: atlas auth status"
  echo ""
  _say "remotes to add once keys are uploaded:"
  _say "  git remote add origin git@gitlab.com:you/project.git"
  _say "  git remote add public git@github.com:you/project.git"
}

# ---------- status ----------

cmd_auth_status() {
  echo "atlas auth status"
  echo "================="
  echo ""

  echo "vendor CLIs:"
  if _have gh; then
    if gh auth status >/dev/null 2>&1; then
      echo "  gh   ✓ installed + authenticated"
    else
      echo "  gh   ⚠ installed but not authenticated (gh auth login)"
    fi
  else
    echo "  gh   ✗ not installed (atlas auth login --method vendor)"
  fi
  if _have glab; then
    if glab auth status >/dev/null 2>&1; then
      echo "  glab ✓ installed + authenticated"
    else
      echo "  glab ⚠ installed but not authenticated (glab auth login)"
    fi
  else
    echo "  glab ✗ not installed (atlas auth login --method vendor)"
  fi

  echo ""
  echo "SSH keys:"
  for k in github gitlab; do
    if [[ -f "$HOME/.ssh/id_ed25519_$k" ]]; then
      echo "  $HOME/.ssh/id_ed25519_$k  ✓ present"
    else
      echo "  $HOME/.ssh/id_ed25519_$k  ✗ missing (atlas auth login --method ssh)"
    fi
  done

  echo ""
  echo "$HOME/.ssh/config:"
  if [[ -f "$HOME/.ssh/config" ]] && grep -q "atlas-auth:start" "$HOME/.ssh/config" 2>/dev/null; then
    echo "  ✓ atlas-auth block present"
  else
    echo "  ✗ atlas-auth block absent"
  fi

  echo ""
  echo "ssh -T live tests:"
  for host in github.com gitlab.com; do
    if timeout 5 ssh -T -o StrictHostKeyChecking=accept-new -o BatchMode=yes "git@$host" 2>&1 \
       | grep -qE "(successfully authenticated|Welcome to GitLab)"; then
      echo "  git@$host  ✓ authenticated"
    else
      echo "  git@$host  ✗ not authenticated (or key not yet uploaded)"
    fi
  done
}

# ---------------------------------------------------------------------------
# repo — wrap gh / glab to create + push a new repo in one shot.
#
# atlas repo create
#   --github | --gitlab     which host (default: github)
#   --public | --private    visibility (default: public)
#   --name NAME             repo name (default: cwd basename)
#   --description "..."     description (default: pulled from README's
#                            first quote-block paragraph)
#   --remote NAME           local git remote name (default: origin)
#   --no-push               skip the initial push
# ---------------------------------------------------------------------------

cmd_repo() {
  local sub="${1:-help}"; shift || true
  case "$sub" in
    create) cmd_repo_create "$@" ;;
    help|"") cat <<'HELP'
atlas repo — create + push a new repo via gh / glab.

  atlas repo create [--github|--gitlab] [--public|--private] \
                    [--name NAME] [--description "..."] \
                    [--remote NAME] [--no-push]

  Defaults: --github --public, name = cwd basename, remote = origin,
            description = first quote-block paragraph of README.md.

  Requires: gh or glab installed + authenticated.
  Run 'atlas auth login --method vendor' first if not.
HELP
      ;;
    *) _die "unknown 'repo' subcommand: $sub" ;;
  esac
}

_repo_default_description() {
  # Pull the first '>' quote-block paragraph from README.md as the
  # repo description, strip Markdown.
  local readme="$PWD/README.md"
  [[ -f "$readme" ]] || { echo ""; return; }
  awk '
    /^> / { sub(/^> +/, ""); print; in_block = 1; next }
    /^>$/ && in_block { exit }
    in_block && !/^> / { exit }
  ' "$readme" \
    | head -3 \
    | tr '\n' ' ' \
    | sed -E 's/\*\*//g; s/\[([^]]+)\]\([^)]+\)/\1/g; s/  +/ /g; s/^ +| +$//g' \
    | head -c 200
}

cmd_repo_create() {
  local host="github" vis="public" name="" desc="" remote="origin" push=1
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --github)         host="github";   shift ;;
      --gitlab)         host="gitlab";   shift ;;
      --public)         vis="public";    shift ;;
      --private)        vis="private";   shift ;;
      --name)           name="$2"; shift 2 ;;
      --name=*)         name="${1#--name=}"; shift ;;
      --description)    desc="$2"; shift 2 ;;
      --description=*)  desc="${1#--description=}"; shift ;;
      --remote)         remote="$2"; shift 2 ;;
      --remote=*)       remote="${1#--remote=}"; shift ;;
      --no-push)        push=0; shift ;;
      *) shift ;;
    esac
  done

  [[ -z "$name" ]] && name="$(basename "$PWD")"
  [[ -z "$desc" ]] && desc="$(_repo_default_description)"
  [[ -z "$desc" ]] && desc="Project at $name"

  _banner "📤" "atlas repo create  $(_c_dim "($host/$vis)")"

  # Need a git repo locally.
  git rev-parse --git-dir >/dev/null 2>&1 || _die "$PWD is not a git repository (run 'git init' first)"

  # Need at least one commit.
  git rev-parse HEAD >/dev/null 2>&1 || _die "no commits in this repo yet (commit something first)"

  case "$host" in
    github)
      _have gh || _die "'gh' CLI not installed. Run: atlas auth login --method vendor"
      if ! gh auth status >/dev/null 2>&1; then
        _die "gh not authenticated. Run: atlas auth login --method vendor"
      fi
      # Detect the authenticated user/login to display.
      local who; who="$(gh api user --jq .login 2>/dev/null || echo '?')"
      _say "github user: $(_c_cyan "$who")"
      _say "name:        $(_c_cyan "$name")"
      _say "visibility:  $(_c_cyan "$vis")"
      _say "description: $(_c_dim "$desc")"
      _say "remote:      $(_c_cyan "$remote")"
      _say "push:        $([[ $push -eq 1 ]] && echo yes || echo no)"
      echo ""
      # If the remote already exists on this repo, refuse — user can use git push directly.
      if git remote get-url "$remote" >/dev/null 2>&1; then
        _die "remote '$remote' already configured (url: $(git remote get-url "$remote"))"
      fi
      local args=(repo create "$name" "--$vis" --source=. --remote="$remote")
      [[ -n "$desc" ]] && args+=(--description "$desc")
      [[ $push -eq 1 ]] && args+=(--push)
      _say "running: $(_c_dim "gh ${args[*]}")"
      echo ""
      gh "${args[@]}"
      ;;
    gitlab)
      _have glab || _die "'glab' CLI not installed. Run: atlas auth login --method vendor"
      if ! glab auth status >/dev/null 2>&1; then
        _die "glab not authenticated. Run: atlas auth login --method vendor"
      fi
      _say "name:        $(_c_cyan "$name")"
      _say "visibility:  $(_c_cyan "$vis")"
      _say "description: $(_c_dim "$desc")"
      _say "remote:      $(_c_cyan "$remote")"
      echo ""
      if git remote get-url "$remote" >/dev/null 2>&1; then
        _die "remote '$remote' already configured (url: $(git remote get-url "$remote"))"
      fi
      local args=(repo create "$name" "--$vis")
      [[ -n "$desc" ]] && args+=(--description "$desc")
      _say "running: $(_c_dim "glab ${args[*]}")"
      echo ""
      glab "${args[@]}"
      # glab doesn't have --source/--push, so we do them manually.
      local who
      who="$(glab api user --jq .username 2>/dev/null || git config user.name | tr -d ' ')"
      git remote add "$remote" "git@gitlab.com:$who/$name.git"
      _say "added remote: $remote → git@gitlab.com:$who/$name.git"
      if [[ $push -eq 1 ]]; then
        git push -u "$remote" main
        _say "pushed main to $remote"
      fi
      ;;
    *) _die "unknown --host: $host" ;;
  esac

  echo ""
  _banner "✅" "done"
  if [[ "$host" == "github" ]] && _have gh; then
    local url; url="$(gh repo view "$name" --json url --jq .url 2>/dev/null || true)"
    [[ -n "$url" ]] && _say "url: $(_c_cyan "$url")"
    _say "star history will start ticking on first visit:"
    _say "  $(_c_dim "https://star-history.com/#${who:-you}/${name}&Date")"
  fi
  _say ""
  _say "next:"
  _say "  - share the repo URL on X / HN / your README"
  _say "  - $(_c_dim 'atlas mirror init') if you also have a GitLab canonical"
}

cmd_install() {
  local runtime=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --runtime) runtime="$2"; shift 2 ;;
      *) shift ;;
    esac
  done
  [[ -z "$runtime" ]] && _die "usage: atlas install --runtime <claude-code|codex|opencode|hermes|generic>"
  local adapter="$ADAPTERS/$runtime/install.sh"
  if [[ ! -x "$adapter" ]]; then
    if [[ -f "$adapter" ]]; then chmod +x "$adapter"; else _die "no adapter for runtime '$runtime' (looked for $adapter)"; fi
  fi
  ATLAS_HOME="$ATLAS_HOME" "$adapter"
}

cmd_styles() {
  echo "available --style presets:"
  echo "  default   - universal (everything: §0-§9 + §A/G/D/X/R/O/Sec/B)"
  for d in "$TEMPLATES/styles"/*/; do
    [[ -d "$d" ]] && echo "  $(basename "$d")"
  done | sort -u | grep -v "^  default$" || true
}

cmd_version() {
  _logo
  echo ""
}

cmd_help() {
  _logo
  echo ""
  printf '  %s\n\n' "${ATLAS_HELP_HEADER:-Usage:}"
  # Pull the documented subcommand block from the file header.
  awk 'NR >= 2 { if ($0 !~ /^#/) exit; print }' "$0" | sed -E 's/^# \{0,1\}//'
}

_loop_extract_entries() {
  local source="$1"
  awk -v source="$source" '
    function trim(s) {
      gsub(/^[[:space:]]+|[[:space:]]+$/, "", s)
      return s
    }
    function clean(s) {
      gsub(/\r/, "", s)
      gsub(/\t/, " ", s)
      gsub(/\*\*/, "", s)
      gsub(/`/, "", s)
      gsub(/[[:space:]]+/, " ", s)
      return trim(s)
    }
    function val(s) {
      s = clean(s)
      if (s == "") s = "-"
      return s
    }
    function reset_entry() {
      iter = ""; ts = ""; model = ""; effort = ""; duration = ""
      subagents = ""; ticket = ""; impact = ""; in_ticket = 0; in_impact = 0
    }
    function flush() {
      if (ts != "") {
        print val(ts) "\t" val(source) "\t" val(iter) "\t" val(model) "\t" \
          val(effort) "\t" val(duration) "\t" val(subagents) "\t" \
          val(ticket) "\t" val(impact)
      }
    }
    function parse_header(line, parts, n, i, key, val) {
      flush()
      reset_entry()
      n = split(line, parts, /[[:space:]]*·[[:space:]]*/)
      iter = parts[1]
      sub(/^iteration[[:space:]]+/, "", iter)
      ts = parts[2]
      for (i = 3; i <= n; i++) {
        key = parts[i]
        val = parts[i]
        sub(/=.*/, "", key)
        sub(/^[^=]*=/, "", val)
        if (key == "model") model = val
        else if (key == "effort") effort = val
        else if (key == "duration") duration = val
        else if (key == "sub-agents") subagents = val
      }
    }
    BEGIN { reset_entry() }
    /^iteration[[:space:]][0-9]+[[:space:]]*·/ { parse_header($0); next }
    /^## Ticket selected/ { in_ticket = 1; in_impact = 0; next }
    /^## Impact check/ { in_ticket = 0; in_impact = 1; next }
    /^## / { in_ticket = 0; in_impact = 0; next }
    /^ticket:/ && ticket == "" {
      ticket = $0
      sub(/^ticket:[[:space:]]*/, "", ticket)
      next
    }
    /^CRITIC ITERATION/ && ticket == "" {
      ticket = $0
      sub(/^CRITIC ITERATION[[:space:]:]*/, "CRITIC ITERATION: ", ticket)
      next
    }
    /^impact:/ && impact == "" {
      impact = $0
      sub(/^impact:[[:space:]]*/, "", impact)
      next
    }
    in_ticket && ticket == "" && trim($0) != "" { ticket = $0; next }
    in_impact && impact == "" && trim($0) != "" { impact = $0; next }
    END { flush() }
  '
}

_loop_md_cell() {
  local s="${1:-}"
  [[ -n "$s" ]] || s="-"
  s="${s//|/\\|}"
  printf '%s' "$s"
}

cmd_loop_log() {
  local json=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --json) json=1; shift ;;
      -h|--help)
        echo "usage: atlas loop log [--json]"
        return 0
        ;;
      *) _die "usage: atlas loop log [--json]" ;;
    esac
  done

  local raw sorted
  raw="$(mktemp "${TMPDIR:-/tmp}/atlas-loop-log.XXXXXX")"
  sorted="$(mktemp "${TMPDIR:-/tmp}/atlas-loop-log.XXXXXX")"

  if [[ -f "$PWD/LOOP_HISTORY.md" ]]; then
    _loop_extract_entries "LOOP_HISTORY.md" < "$PWD/LOOP_HISTORY.md" >> "$raw"
  fi

  if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    if git ls-files --error-unmatch LOOP_REPORT.md >/dev/null 2>&1; then
      local sha
      while IFS= read -r sha; do
        [[ -n "$sha" ]] || continue
        if git show "${sha}:LOOP_REPORT.md" 2>/dev/null | _loop_extract_entries "git:${sha}" >> "$raw"; then
          :
        fi
      done < <(git log --format='%H' -- LOOP_REPORT.md 2>/dev/null || true)
    fi
  fi

  if [[ -s "$raw" ]]; then
    if ! LC_ALL=C sort -r "$raw" > "$sorted"; then
      rm -f "$raw" "$sorted"
      _die "could not sort loop log"
    fi
    mv "$sorted" "$raw"
  fi

  if [[ $json -eq 1 ]]; then
    local first=1 ts source iter model effort duration subagents ticket impact
    printf '{"iterations":['
    while IFS=$'\t' read -r ts source iter model effort duration subagents ticket impact; do
      [[ -n "$ts" ]] || continue
      if [[ $first -eq 0 ]]; then printf ','; fi
      first=0
      printf '{"timestamp":"%s","source":"%s","iteration":"%s","model":"%s","effort":"%s","duration":"%s","sub_agents":"%s","ticket":"%s","impact":"%s"}' \
        "$(_json_esc "$ts")" "$(_json_esc "$source")" "$(_json_esc "$iter")" \
        "$(_json_esc "$model")" "$(_json_esc "$effort")" "$(_json_esc "$duration")" \
        "$(_json_esc "$subagents")" "$(_json_esc "$ticket")" "$(_json_esc "$impact")"
    done < "$raw"
    printf ']}\n'
  else
    if [[ ! -s "$raw" ]]; then
      _say "no loop ledger entries found (looked for LOOP_HISTORY.md and tracked LOOP_REPORT.md history)"
    else
      printf '| Iteration | Timestamp | Model | Effort | Duration | Sub-agents | Source | Ticket | Impact |\n'
      printf '|---|---|---|---|---|---|---|---|---|\n'
      local ts source iter model effort duration subagents ticket impact
      while IFS=$'\t' read -r ts source iter model effort duration subagents ticket impact; do
        [[ -n "$ts" ]] || continue
        printf '| %s | %s | %s | %s | %s | %s | %s | %s | %s |\n' \
          "$(_loop_md_cell "$iter")" "$(_loop_md_cell "$ts")" "$(_loop_md_cell "$model")" \
          "$(_loop_md_cell "$effort")" "$(_loop_md_cell "$duration")" \
          "$(_loop_md_cell "$subagents")" "$(_loop_md_cell "$source")" \
          "$(_loop_md_cell "$ticket")" "$(_loop_md_cell "$impact")"
      done < "$raw"
    fi
  fi

  rm -f "$raw" "$sorted"
}

cmd_loop() {
  local sub="${1:-help}"
  if [[ $# -gt 0 ]]; then shift; fi
  case "$sub" in
    log) cmd_loop_log "$@" ;;
    help|-h|--help)
      echo "usage: atlas loop log [--json]"
      ;;
    *) _die "usage: atlas loop log [--json]" ;;
  esac
}

# ---------------------------------------------------------------------------
# measure — estimate the orientation-token savings ATLAS buys this repo.
#
# Compares two orientation surfaces (rough estimate, bytes / 4):
#   WITHOUT ATLAS  what an agent skims to self-orient: README + the file
#                  tree + the head of the top source files.
#   WITH ATLAS     the front-loaded trio: ATLAS.md + SKILL.md + CLAUDE.md.
#
# --badge prints a shields.io badge with the savings %, for the README.
# ---------------------------------------------------------------------------
# measure --tools — report the tool context contract's state (SPEC §14): the
# canonical identity externals should use, index freshness vs the live HEAD,
# worktree drift, dead session-temporary preloads in the caller env, and the
# .atlas/tools-telemetry.jsonl aggregates (one JSON line per tool operation:
# ts · tool · op · bytes_in · bytes_out · retries · stale · ok — appended by
# the tools themselves; ATLAS only aggregates).
_measure_tools() {
  local json="$1"
  local tf="$PWD/.atlas/tools.json"
  [[ -f "$tf" ]] || _die "no .atlas/tools.json — run 'atlas init --tools' first (SPEC §14)"
  local pid ih gen head="null" dirty=false fresh=null
  pid="$(_tools_json_get "$tf" project_id)"
  ih="$(_tools_json_get "$tf" indexed_head)"; [[ -z "$ih" ]] && ih=null
  gen="$(_tools_json_get "$tf" generated)"
  if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
    head="$(git rev-parse HEAD 2>/dev/null || echo null)"
    # no `| head` on the producer — §PIPE-HEAD-SIGPIPE
    [[ -n "$(git status --porcelain 2>/dev/null || true)" ]] && dirty=true
  fi
  if [[ "$ih" != "null" && "$head" != "null" ]]; then
    if [[ "$ih" == "$head" ]]; then fresh=true; else fresh=false; fi
  fi
  # Dead session-temporary preloads — the env half of the §14 subprocess
  # contract: a NODE_OPTIONS --require/-r pointing at a file that no longer
  # exists hard-fails every child node process (observed in the wild with a
  # context tool's vanished cm-fs-preload-*.js).
  local dead="" tok expect=0
  for tok in ${NODE_OPTIONS:-}; do
    if [[ $expect -eq 1 ]]; then
      [[ -e "$tok" ]] || dead="${dead}${dead:+ }${tok}"
      expect=0; continue
    fi
    case "$tok" in
      --require|-r) expect=1 ;;
      --require=*)  [[ -e "${tok#--require=}" ]] || dead="${dead}${dead:+ }${tok#--require=}" ;;
    esac
  done
  # Telemetry ledger aggregation (zero-dep awk; unknown lines are skipped).
  local ledger="$PWD/.atlas/tools-telemetry.jsonl" agg="0 0 0 0 0 0"
  if [[ -f "$ledger" ]]; then
    agg="$(awk '
      function num(k,   re, s) {
        re = "\"" k "\":[ ]*[0-9]+"
        if (match($0, re)) { s = substr($0, RSTART, RLENGTH); sub(/.*:[ ]*/, "", s); return s + 0 }
        return 0
      }
      /"tool"/ {
        n++; bi += num("bytes_in"); bo += num("bytes_out"); r += num("retries")
        if ($0 ~ /"stale":[ ]*true/) st++
        if ($0 ~ /"ok":[ ]*false/) f++
      }
      END { printf "%d %d %d %d %d %d", n+0, bi+0, bo+0, r+0, st+0, f+0 }
    ' "$ledger" 2>/dev/null)" || agg="0 0 0 0 0 0"
  fi
  local ev bi bo rt st fl saved
  read -r ev bi bo rt st fl <<< "$agg"
  saved=$(( (bi - bo) / 4 )); [[ $saved -lt 0 ]] && saved=0

  if [[ "$json" -eq 1 ]]; then
    local ih_json=null head_json=null dead_json=""
    [[ "$ih" != "null" ]] && ih_json="\"$(_json_esc "$ih")\""
    [[ "$head" != "null" ]] && head_json="\"$(_json_esc "$head")\""
    for tok in $dead; do dead_json="${dead_json}${dead_json:+,}\"$(_json_esc "$tok")\""; done
    printf '{"project_id":"%s","generated":"%s","head":%s,"indexed_head":%s,"fresh":%s,"dirty":%s,"dead_preloads":[%s],"telemetry":{"events":%d,"bytes_indexed":%d,"bytes_returned":%d,"retries":%d,"stale_hits":%d,"failures":%d,"est_context_tokens_saved":%d}}\n' \
      "$(_json_esc "$pid")" "$(_json_esc "$gen")" "$head_json" "$ih_json" "$fresh" "$dirty" \
      "$dead_json" "$ev" "$bi" "$bo" "$rt" "$st" "$fl" "$saved"
    return 0
  fi

  _banner "🔌" "atlas measure --tools  $(_c_dim '(SPEC §14 tool context contract)')"
  printf "  %-24s %s\n" "canonical project id:" "$(_c_bold "$pid")"
  printf "  %-24s %s\n" "HEAD:" "$head"
  printf "  %-24s %s\n" "indexed at:" "$ih"
  if [[ "$fresh" == "false" ]]; then
    printf "  %-24s %s\n" "index freshness:" "$(_c_yellow 'STALE — re-index; treat graph/search results as advisory')"
  elif [[ "$fresh" == "true" ]]; then
    printf "  %-24s %s\n" "index freshness:" "$(_c_green 'fresh (indexed_head == HEAD)')"
  else
    printf "  %-24s %s\n" "index freshness:" "$(_c_dim 'unknown (no indexer has stamped indexed_head yet)')"
  fi
  local wt_state; wt_state="$(_c_green 'clean')"
  [[ "$dirty" == true ]] && wt_state="$(_c_yellow 'dirty (uncommitted changes not in any index)')"
  printf "  %-24s %s\n" "worktree:" "$wt_state"
  if [[ -n "$dead" ]]; then
    printf "  %-24s %s\n" "env contract:" "$(_c_red "VIOLATED — dead preload(s) in NODE_OPTIONS: $dead")"
    _say "strip them before running child processes, or tests will fail on a ghost"
  else
    printf "  %-24s %s\n" "env contract:" "$(_c_green 'clean (no dead preloads detected)')"
  fi
  echo ""
  if [[ "$ev" -gt 0 ]]; then
    printf "  %-24s %s\n" "telemetry events:" "$ev"
    printf "  %-24s %s\n" "bytes indexed → returned:" "$bi → $bo  $(_c_dim "(≈${saved} tok kept out of context)")"
    printf "  %-24s %s\n" "retries / stale / failed:" "$rt / $st / $fl"
  else
    _say "no telemetry yet — tools append JSON lines to .atlas/tools-telemetry.jsonl (SPEC §14)"
  fi
}

cmd_measure() {
  local want_badge=0 want_share=0 want_log=0 want_tools=0 want_json=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --badge) want_badge=1; shift ;;
      --share) want_share=1; shift ;;
      --log)   want_log=1; shift ;;
      --tools) want_tools=1; shift ;;
      --json)  want_json=1; shift ;;
      *)       shift ;;
    esac
  done

  # --tools is its own report (SPEC §14) and doesn't need an ATLAS.md.
  if [[ $want_tools -eq 1 ]]; then
    _measure_tools "$want_json"
    return $?
  fi

  local atlas="$PWD/ATLAS.md"
  [[ -f "$atlas" ]] || _die "no ATLAS.md in $PWD — run 'atlas init' first"
  local skill; skill=$(find "$PWD/.agents/skill" -maxdepth 2 -name SKILL.md 2>/dev/null | head -1)
  local scars="$PWD/SCARS.md"

  _bytes() { if [[ -f "$1" ]]; then wc -c < "$1" | tr -d ' '; else echo 0; fi; }

  # WITH ATLAS — the front-loaded orientation *spine* (what the hook injects first:
  # ATLAS §0-1 + the SKILL/SCARS tables of contents), NOT the whole files. This is
  # the same surface the benchmark measures, so `measure` and `bench` agree.
  local with_b=0 _sp
  _sp=$(awk '/^## 2\./{exit} 1' "$atlas" 2>/dev/null | wc -c | tr -d ' '); with_b=$(( with_b + _sp ))
  if [[ -f "$scars" ]]; then
    _sp=$(awk '/^## Table of contents/{x=1} x&&/^## /&&!/Table of contents/{exit} x' "$scars" 2>/dev/null | wc -c | tr -d ' ')
    with_b=$(( with_b + _sp ))
  fi
  if [[ -n "$skill" ]]; then
    _sp=$(awk '/^## Table of contents/{x=1} x&&/^## /&&!/Table of contents/{exit} x' "$skill" 2>/dev/null | wc -c | tr -d ' ')
    with_b=$(( with_b + _sp ))
  fi

  # WITHOUT ATLAS — README + file tree + head of the top source files.
  local without_b=0
  without_b=$(( without_b + $(_bytes "$PWD/README.md") ))
  local tree_b
  # `| head` closes the pipe early on big repos → git ls-files gets SIGPIPE → under
  # pipefail the whole substitution fails and aborts measure; `|| true` keeps the
  # count (it's already on stdout before the failure). SCARS §PIPE-HEAD-SIGPIPE.
  tree_b=$( { { git -C "$PWD" ls-files 2>/dev/null || find "$PWD" -maxdepth 3 -type f; } \
            | head -400 | wc -c | tr -d ' '; } || true )
  without_b=$(( without_b + tree_b ))
  local f cnt=0 head_b=0 fb
  while IFS= read -r f; do
    [[ $cnt -ge 15 ]] && break
    [[ -f "$PWD/$f" ]] || continue
    fb=$(head -c 1600 "$PWD/$f" 2>/dev/null | wc -c | tr -d ' ')
    head_b=$(( head_b + fb ))
    cnt=$(( cnt + 1 ))
  done < <( { git -C "$PWD" ls-files 2>/dev/null || true; } \
            | grep -Ei '\.(py|js|ts|tsx|jsx|go|rs|rb|java|c|cc|cpp|h|hpp|sh|php|swift|kt|scala|cs|m|mm)$' || true )
  without_b=$(( without_b + head_b ))

  # WITHOUT ATLAS — upper bound: the whole repo (a RAG dump / "load everything"
  # agent), skipping binaries. The reduction is a *range*: smart-skim → whole-repo.
  local full_b=0 ff
  while IFS= read -r ff; do
    case "$ff" in *.png|*.gif|*.jpg|*.jpeg|*.pdf|*.zip|*.deb|*.ico|*.woff|*.woff2|*.ttf|*.mp4|*.mov|*.lock) continue ;; esac
    [[ -f "$PWD/$ff" ]] || continue
    full_b=$(( full_b + $(_bytes "$PWD/$ff") ))
  done < <( { git -C "$PWD" ls-files 2>/dev/null | head -3000; } || true )

  [[ $with_b    -lt 1 ]] && with_b=1
  [[ $without_b -lt 1 ]] && without_b=1
  [[ $full_b -lt $without_b ]] && full_b=$without_b
  local with_t=$(( with_b / 4 )) without_t=$(( without_b / 4 )) full_t=$(( full_b / 4 ))
  local ratio pct ratio_hi pct_hi tiny=0
  ratio=$(awk    -v a="$without_t" -v b="$with_t" 'BEGIN{ if(b<1)b=1; printf "%.1f", a/b }')
  pct=$(awk      -v a="$without_t" -v b="$with_t" 'BEGIN{ if(a<1)a=1; r=100*(a-b)/a; if(r<0)r=0; printf "%.0f", r }')
  ratio_hi=$(awk -v a="$full_t"    -v b="$with_t" 'BEGIN{ if(b<1)b=1; printf "%.1f", a/b }')
  pct_hi=$(awk   -v a="$full_t"    -v b="$with_t" 'BEGIN{ if(a<1)a=1; r=100*(a-b)/a; if(r<0)r=0; printf "%.0f", r }')
  [[ $full_t -le $with_t ]] && tiny=1   # whole repo smaller than the spine → ATLAS is light overhead *now*

  if [[ $want_badge -eq 1 ]]; then
    echo "[![ATLAS](https://img.shields.io/badge/ATLAS-%E2%88%92${pct}%25%E2%80%93${pct_hi}%25%20orientation%20tokens-06b6d4)](https://github.com/Abbasi-Alain/atlas)"
    return 0
  fi

  _banner "📊" "atlas measure  $(_c_dim "(estimate · bytes ÷ 4)")"
  printf "  %-30s %s\n" "with ATLAS (quartet spine):" "$(_c_green  "~${with_t} tok")"
  if [[ $tiny -eq 1 ]]; then
    printf "  %-30s %s\n" "this whole repo:" "$(_c_yellow "~${full_t} tok")"
    echo ""
    printf "  %s\n" "$(_c_bold "Repo's smaller than the ATLAS spine — ~$(( with_t - full_t )) tok of light overhead for now.")"
    _say "the win grows fast with the codebase: ~5–15× on a medium repo, 50–100× on a monorepo."
  else
    printf "  %-30s %s %s %s %s\n" "without — smart skim:"      "$(_c_yellow "~${without_t} tok")" "$(_c_dim '→')" "$(_c_bold "−${pct}%")"    "$(_c_dim "(${ratio}×)")"
    printf "  %-30s %s %s %s %s\n" "without — whole-repo dump:" "$(_c_yellow "~${full_t} tok")"    "$(_c_dim '→')" "$(_c_bold "−${pct_hi}%")" "$(_c_dim "(${ratio_hi}×)")"
    echo ""
    printf "  %s %s\n" "$(_c_bold "ATLAS cuts orientation −${pct}% to −${pct_hi}%")" "$(_c_dim '(smart-skim → whole-repo baseline)')"
  fi
  echo ""
  _say "method: 'with' = ATLAS+SKILL+SCARS+CLAUDE spine; 'without' ranges from a"
  _say "        smart skim (README+tree+heads) to the whole repo. Estimate (bytes÷4)."
  _say "shareable README badge:  $(_c_dim 'atlas measure --badge')"

  if [[ $want_share -eq 1 ]]; then
    local repo date_s row title body url
    repo="$(basename "$(git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || echo "$PWD")")"
    date_s="$(date +%Y-%m-%d)"
    row="| ${repo} | ~${without_t} → ${with_t} tok | −${pct}% to −${pct_hi}% | ${date_s} |"
    echo ""
    _banner "🏆" "share your savings $(_c_dim '(grow the leaderboard)')"
    echo "  Leaderboard row (paste into docs/LEADERBOARD.md or the issue):"
    echo "    ${row}"
    if _have python3; then
      title="ATLAS savings: ${repo} (−${pct}% to −${pct_hi}%)"
      body="Add my repo to the leaderboard:  ${row}  — reproduced with 'atlas measure'."
      url="$(python3 -c 'import sys,urllib.parse as u; print("https://github.com/Abbasi-Alain/atlas/issues/new?labels=leaderboard&title=%s&body=%s"%(u.quote(sys.argv[1]),u.quote(sys.argv[2])))' "$title" "$body")"
      echo ""
      echo "  One-click submit:"
      echo "    $(_c_dim "$url")"
    fi
    _say "the board lives at docs/LEADERBOARD.md"
  fi

  if [[ $want_log -eq 1 ]]; then
    # append a dated row to a central history ledger so flagship/longitudinal
    # measurements are kept and comparable later.
    local hist repo3 d3 nfiles
    hist="$ATLAS_HOME/docs/benchmarks/flagship/measure-history.jsonl"
    mkdir -p "$(dirname "$hist")" 2>/dev/null || true
    repo3="$(basename "$(git -C "$PWD" rev-parse --show-toplevel 2>/dev/null || echo "$PWD")")"
    d3="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
    nfiles="$(git -C "$PWD" ls-files 2>/dev/null | wc -l | tr -d ' ')"
    if printf '{"date":"%s","atlas_version":"%s","repo":"%s","files":%s,"skim_tok":%s,"spine_tok":%s,"whole_tok":%s,"pct_vs_skim":%s,"pct_vs_whole":%s}\n' \
        "$d3" "$ATLAS_VERSION" "$repo3" "${nfiles:-0}" "$without_t" "$with_t" "$full_t" "$pct" "$pct_hi" >> "$hist" 2>/dev/null; then
      _ok "logged to history → $(_c_dim "$hist")"
    fi
  fi
}

# ---------------------------------------------------------------------------
# leaderboard --render — regenerate a Markdown table (RM-9) from a CSV source
# of truth: data/leaderboard.csv (repo · commit · files · skim_tok · spine_tok
# · reduction_pct_low · reduction_pct_high · atlas_version · date) renders
# into docs/LEADERBOARD.md between marker comments. The CSV is what
# contributors edit in a PR; the Markdown table is a derived, deterministic
# render — never hand-edited directly once markers are present.
# ---------------------------------------------------------------------------
cmd_leaderboard() {
  local render=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --render) render=1; shift ;;
      *) shift ;;
    esac
  done
  [[ $render -eq 1 ]] || _die "usage: atlas leaderboard --render"

  local csv="$PWD/data/leaderboard.csv"
  local md="$PWD/docs/LEADERBOARD.md"
  [[ -f "$csv" ]] || _die "no data/leaderboard.csv in $PWD"
  [[ -f "$md" ]]  || _die "no docs/LEADERBOARD.md in $PWD"

  local expected_header="repo,commit,files,skim_tok,spine_tok,reduction_pct_low,reduction_pct_high,atlas_version,date"
  local header; header="$(head -n1 "$csv" | tr -d '\r')"
  [[ "$header" == "$expected_header" ]] \
    || _die "data/leaderboard.csv header mismatch — expected: $expected_header"

  # Per-field schema validation (RM-45, critic-stage finding #5): field-count
  # alone accepted non-numeric counts, >100% reductions, and Markdown-pipe
  # injection into the rendered table. Every field is now typed; since no
  # pattern admits '|', pipe injection is dead by construction.
  local expected_fields=9 line_no=1 line n
  local f_repo f_commit f_files f_skim f_spine f_lo f_hi f_ver f_date
  while IFS= read -r line; do
    line_no=$((line_no+1))
    line="${line%$'\r'}"
    [[ -z "$line" ]] && continue
    n=$(( $(grep -o ',' <<< "$line" | wc -l) + 1 ))
    [[ "$n" -eq "$expected_fields" ]] \
      || _die "data/leaderboard.csv line $line_no has $n field(s), expected $expected_fields: $line"
    IFS=',' read -r f_repo f_commit f_files f_skim f_spine f_lo f_hi f_ver f_date <<< "$line"
    [[ "$f_repo" =~ ^[A-Za-z0-9._/-]+$ ]] \
      || _die "data/leaderboard.csv line $line_no: repo '$f_repo' must be a plain slug ([A-Za-z0-9._/-])"
    [[ "$f_commit" =~ ^[0-9a-fA-F]{7,40}$ ]] \
      || _die "data/leaderboard.csv line $line_no: commit '$f_commit' must be a 7-40 char hex SHA"
    [[ "$f_files" =~ ^[0-9]+$ && "$f_skim" =~ ^[0-9]+$ && "$f_spine" =~ ^[0-9]+$ ]] \
      || _die "data/leaderboard.csv line $line_no: files/skim_tok/spine_tok must be integers (got '$f_files'/'$f_skim'/'$f_spine')"
    if ! [[ "$f_lo" =~ ^[0-9]+$ && "$f_hi" =~ ^[0-9]+$ ]] \
       || [[ "$f_lo" -gt 100 || "$f_hi" -gt 100 || "$f_lo" -gt "$f_hi" ]]; then
      _die "data/leaderboard.csv line $line_no: reduction percents must be integers 0-100 with low<=high (got '$f_lo'/'$f_hi')"
    fi
    [[ "$f_ver" =~ ^[0-9]+\.[0-9]+\.[0-9]+([.-][A-Za-z0-9.-]+)?$ ]] \
      || _die "data/leaderboard.csv line $line_no: atlas_version '$f_ver' must be semver-like X.Y.Z"
    [[ "$f_date" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]] \
      || _die "data/leaderboard.csv line $line_no: date '$f_date' must be YYYY-MM-DD"
  done < <(tail -n +2 "$csv")

  local table
  table="$(awk -F',' 'NF==9 {
    printf "| %s | ~%s → ~%s tok | −%s%% to −%s%% | %s |\n", $1, $4, $5, $6, $7, $9
  }' <(tail -n +2 "$csv"))"

  local marker_start="<!-- leaderboard:start -->"
  local marker_end="<!-- leaderboard:end -->"

  grep -q "$marker_start" "$md" \
    || _die "docs/LEADERBOARD.md has no $marker_start marker — add it around the table first"

  # Replace via the pair-validating helper (temp file + getline for
  # §AWK-MULTILINE-V; pair validation so a missing end marker hard-errors
  # instead of truncating everything after the start marker).
  local block_file; block_file="$(mktemp)"
  {
    printf '%s\n' "$marker_start"
    printf '| repo | orientation tokens (without → with) | reduction | date |\n'
    printf '|---|---|---|---|\n'
    printf '%s\n' "$table"
    printf '%s\n' "$marker_end"
  } > "$block_file"
  _replace_marker_block "$md" "$marker_start" "$marker_end" "$block_file"
  rm -f "$block_file"
  local n_rows; n_rows=$(( $(wc -l < "$csv" | tr -d ' ') - 1 ))
  _ok "regenerated docs/LEADERBOARD.md table from data/leaderboard.csv ($n_rows row(s))"
}

# ---------------------------------------------------------------------------
# doctor — diagnose the ATLAS install + this project's harness + runtime wiring.
# ---------------------------------------------------------------------------
cmd_doctor() {
  local warns=0
  _banner "🩺" "atlas doctor"
  if _have atlas; then _ok "atlas on PATH ($(command -v atlas))"
  else _fail "atlas not on PATH — add your bin dir to PATH (see install.sh)"; warns=$(( warns + 1 )); fi
  echo "   $(_c_dim "ATLAS_HOME=$ATLAS_HOME")"
  for d in templates adapters hooks; do
    if [[ -d "$ATLAS_HOME/$d" ]]; then _ok "$d/ present"
    else _fail "$ATLAS_HOME/$d missing — reinstall ATLAS"; warns=$(( warns + 1 )); fi
  done

  _banner "📂" "project: $(_project_name)"
  local skill; skill=$(find "$PWD/.agents/skill" -maxdepth 2 -name SKILL.md 2>/dev/null | head -1)
  local label path
  for pair in "ATLAS.md:$PWD/ATLAS.md" "SCARS.md:$PWD/SCARS.md" "CLAUDE.md:$PWD/CLAUDE.md" "AGENTS.md:$PWD/AGENTS.md"; do
    label="${pair%%:*}"; path="${pair#*:}"
    if [[ -f "$path" ]]; then _ok "$label present"
    else _fail "$label missing — run 'atlas init'"; warns=$(( warns + 1 )); fi
  done
  if [[ -n "$skill" ]]; then _ok "SKILL.md present ($skill)"
  else _fail "SKILL.md missing — run 'atlas init'"; warns=$(( warns + 1 )); fi

  # CLAUDE.md is loaded every turn — Claude's own docs say keep it concise.
  local claude_f="$PWD/CLAUDE.md" lines
  if [[ -f "$claude_f" ]]; then
    lines=$(wc -l < "$claude_f" | tr -d ' ')
    if [[ "$lines" -gt 200 ]]; then _say "CLAUDE.md is $lines lines — it loads every turn; consider trimming < 200"
    else _ok "CLAUDE.md concise ($lines lines)"; fi
    if [[ -f "$PWD/AGENTS.md" ]] && ! diff -q "$claude_f" "$PWD/AGENTS.md" >/dev/null 2>&1; then
      _fail "AGENTS.md drifted from CLAUDE.md — run 'atlas export --to codex'"; warns=$(( warns + 1 ))
    fi
  fi

  _banner "🧩" "runtime exports"
  local rt rp
  for pair in "Copilot:.github/copilot-instructions.md" "Gemini:GEMINI.md" "Cursor:.cursor/rules/atlas.mdc" "llms.txt:llms.txt"; do
    rt="${pair%%:*}"; rp="${pair#*:}"
    if [[ -e "$PWD/$rp" ]]; then _ok "$rt export present ($rp)"
    else _say "$rt not exported — $(_c_dim "atlas export --to all")"; fi
  done

  _banner "🔌" "runtime: claude-code"
  if [[ -f "$HOME/.claude/hooks/atlas-skill-loader.sh" ]]; then _ok "skill-loader hook installed"
  else _say "hook not installed — $(_c_dim 'atlas install --runtime claude-code')"; fi
  if [[ -f "$HOME/.claude/settings.json" ]] && grep -q "atlas-skill-loader" "$HOME/.claude/settings.json" 2>/dev/null; then
    _ok "hook wired into ~/.claude/settings.json"
  else
    _say "hook not wired in settings.json — $(_c_dim 'atlas install --runtime claude-code')"
  fi

  echo ""
  if [[ $warns -eq 0 ]]; then echo "$(_c_green 'atlas doctor:') healthy ✅"
  else echo "$(_c_yellow 'atlas doctor:') $warns issue(s) — see fixes above"; fi
}

# ---------------------------------------------------------------------------
# badge — print a 'Powered by ATLAS' README badge (markdown + HTML).
# ---------------------------------------------------------------------------
cmd_badge() {
  local style="flat"
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --for-the-badge) style="for-the-badge"; shift ;;
      --flat)          style="flat";          shift ;;
      *)               shift ;;
    esac
  done
  local img="https://img.shields.io/badge/harness-ATLAS-06b6d4?style=${style}&logo=markdown&logoColor=white"
  local link="https://github.com/Abbasi-Alain/atlas"
  _banner "🏷️ " "atlas badge"
  echo "Markdown — paste into your README:"
  echo ""
  echo "  [![ATLAS](${img})](${link})"
  echo ""
  echo "HTML:"
  echo ""
  echo "  <a href=\"${link}\"><img src=\"${img}\" alt=\"ATLAS harness\"></a>"
  echo ""
  _say "every repo that adds this links back to ATLAS — thank you 💙"
}

# ---------------------------------------------------------------------------
# export — fan the canonical trio out to other runtimes' context files.
#
# CLAUDE.md (behavioral) + ATLAS.md (structural) are the source of truth.
# This regenerates the per-runtime files so you maintain ONE set of docs:
#   codex/agents  -> AGENTS.md
#   copilot       -> .github/copilot-instructions.md
#   gemini        -> GEMINI.md
#   cursor        -> .cursor/rules/atlas.mdc
#   llms-txt      -> llms.txt
#   all           -> every target above
# ---------------------------------------------------------------------------
cmd_export() {
  local target=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --to)   target="${2:-}"; shift 2 ;;
      --to=*) target="${1#--to=}"; shift ;;
      *)      [[ -z "$target" ]] && target="$1"; shift ;;
    esac
  done
  [[ -z "$target" ]] && _die "usage: atlas export --to <codex|copilot|gemini|cursor|llms-txt|all>"

  local claude="$PWD/CLAUDE.md" atlas="$PWD/ATLAS.md"
  [[ -f "$claude" ]] || _die "no CLAUDE.md in $PWD — run 'atlas init' first"

  local gen="Generated by 'atlas export' from CLAUDE.md — edit CLAUDE.md, not this file."

  _export_one() {
    case "$1" in
      codex|agents)
        cp "$claude" "$PWD/AGENTS.md"
        _ok "AGENTS.md (Codex / OpenCode — mirror of CLAUDE.md)" ;;
      copilot)
        mkdir -p "$PWD/.github"
        { printf '<!-- %s -->\n\n' "$gen"; cat "$claude"; } > "$PWD/.github/copilot-instructions.md"
        _ok ".github/copilot-instructions.md (GitHub Copilot)" ;;
      gemini)
        { printf '<!-- %s -->\n\n' "$gen"; cat "$claude"; } > "$PWD/GEMINI.md"
        _ok "GEMINI.md (Gemini CLI)" ;;
      cursor)
        mkdir -p "$PWD/.cursor/rules"
        { printf -- '---\ndescription: ATLAS project rules (from CLAUDE.md)\nalwaysApply: true\n---\n\n'; cat "$claude"; } > "$PWD/.cursor/rules/atlas.mdc"
        _ok ".cursor/rules/atlas.mdc (Cursor)" ;;
      llms-txt|llmstxt|llms)
        local project; project="$(_project_name)"
        local skill; skill=$(find "$PWD/.agents/skill" -maxdepth 2 -name SKILL.md 2>/dev/null | head -1)
        {
          printf '# %s\n\n' "$project"
          if [[ -f "$atlas" ]]; then
            awk '/^> / { sub(/^> +/,""); print; n++ } n>=2 { exit }' "$atlas"
            echo ""
          fi
          echo "## For AI coding agents — read these first"
          echo "- [ATLAS.md](ATLAS.md): repository map — where everything lives, commands, architecture, constraints."
          [[ -f "$PWD/SCARS.md" ]] && echo "- [SCARS.md](SCARS.md): failure memory — read before fixing a bug; stable §anchors (symptom → root cause → do NOT → do)."
          [[ -n "$skill" ]] && echo "- ${skill#"$PWD"/}: procedural playbook — how to run, test, and do common tasks here."
          echo "- [CLAUDE.md](CLAUDE.md) / [AGENTS.md](AGENTS.md): behavioral contract for coding agents."
          local faqf; faqf="$(_faq_file)"
          [[ -n "$faqf" ]] && echo "- [${faqf#"$PWD"/}](${faqf#"$PWD"/}): project Q&A knowledge — questions answered once, with pointers; check it before asking or re-deriving."
          [[ -f "$PWD/LOOP.md" ]] && echo "- [LOOP.md](LOOP.md): autonomous improvement loop — one iteration = pick the top [ROADMAP.md](ROADMAP.md) item by EV → implement → 'atlas check' → commit."
          [[ -f "$PWD/AKIGI.md" ]] && echo "- [AKIGI.md](AKIGI.md): purpose contract — why this repo exists, scope/non-goals, how incoming requests are triaged."
          [[ -f "$PWD/FRQ.md" ]] && echo "- [FRQ.md](FRQ.md): cross-agent feature-request queue — outside agents: read AKIGI.md first, then file your ask here."
          [[ -f "$PWD/BRD.md" ]] && echo "- [BRD.md](BRD.md): bug disclosure intake for outside agents — broken behavior goes here (with evidence + repro)."
          [[ -f "$PWD/SRD.md" ]] && echo "- [SRD.md](SRD.md): security disclosure — minimal public marker only; full detail via its private channel."
        } > "$PWD/llms.txt"
        _ok "llms.txt" ;;
      claude)
        _say "CLAUDE.md is the canonical source — nothing to export" ;;
      *)
        _warn "unknown export target '$1' (skip)" ;;
    esac
  }

  _banner "📤" "atlas export  $(_c_dim "(→ $target)")"
  if [[ "$target" == "all" ]]; then
    local t
    for t in codex copilot gemini cursor llms-txt; do _export_one "$t"; done
  else
    _export_one "$target"
  fi
  echo ""
  _say "source of truth: CLAUDE.md + ATLAS.md — re-run after edits to resync."
  _say "$(_c_dim "tip: 'atlas doctor' flags exports that have drifted.")"
}

# ---------------------------------------------------------------------------
# uninstall — remove a curl|bash / manual ATLAS install (launcher + home).
# Package-manager installs (brew/apt/AUR/npm) are deferred to that manager.
# ---------------------------------------------------------------------------
cmd_uninstall() {
  local purge=0 assume_yes=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --purge)  purge=1; shift ;;
      --yes|-y) assume_yes=1; shift ;;
      *)        shift ;;
    esac
  done
  _banner "🗑️ " "atlas uninstall"

  case "$ATLAS_HOME" in
    */Cellar/*|/usr/*|/opt/*)
      _warn "ATLAS lives under a package-managed path: $ATLAS_HOME"
      _say "remove it with the manager you installed with:"
      _say "  brew uninstall atlas"
      _say "  sudo apt remove atlas        # or: sudo dpkg -r atlas"
      _say "  yay -R atlas                 # Arch / AUR"
      _say "  npm rm -g @alainabbasi/atlas"
      return 0 ;;
  esac

  local launcher; launcher="$(command -v atlas 2>/dev/null || true)"
  echo "  will remove:"
  [[ -n "$launcher" ]] && echo "    launcher:    $launcher"
  echo "    ATLAS_HOME:  $ATLAS_HOME"
  [[ $purge -eq 1 ]] && echo "    hook:        $HOME/.claude/hooks/atlas-skill-loader.sh"
  echo ""

  if [[ $assume_yes -ne 1 ]]; then
    printf "  proceed? [y/N]: "
    local ans; read -r ans
    case "$ans" in y|Y|yes) : ;; *) _say "aborted — nothing removed"; return 0 ;; esac
  fi

  [[ -n "$launcher" && -f "$launcher" ]] && rm -f "$launcher" && _ok "removed $launcher"
  if [[ "$ATLAS_HOME" == "$HOME/.atlas" ]]; then
    rm -rf "$ATLAS_HOME" && _ok "removed $ATLAS_HOME"
  else
    _warn "ATLAS_HOME is not the default ~/.atlas — remove it yourself if intended:"
    _say  "  rm -rf \"$ATLAS_HOME\""
  fi
  if [[ $purge -eq 1 && -f "$HOME/.claude/hooks/atlas-skill-loader.sh" ]]; then
    rm -f "$HOME/.claude/hooks/atlas-skill-loader.sh" && _ok "removed claude-code hook"
    _say "(left ~/.claude/settings.json wiring intact — edit it if you want the entry gone)"
  fi
  _say "done. re-install: curl -fsSL https://raw.githubusercontent.com/Abbasi-Alain/atlas/main/install.sh | bash"
}

# ---------------------------------------------------------------------------
# bench — A/B the orientation cost of a task WITH vs WITHOUT ATLAS.
#
# Runs the SAME task twice on a copy of a repo (quartet present vs hidden) via a
# headless agent, and writes a JSON + a Markdown report with the token/turn/
# cost/time delta and full reproducibility metadata (runtime version, model,
# effort, date, repo sha). Provider-agnostic:
#   --runtime claude            claude -p … --output-format json  (rich tokens)
#   --runtime codex             codex exec …                       (wall + output)
#   --exec '<cmd>'              ANY provider; reference the prompt as
#                               "$ATLAS_BENCH_TASK" in the command
#   --model <m> --effort <lvl>  passed to the runtime + recorded
# --dry-run previews without invoking anything. Needs python3 + the runtime CLI.
# ---------------------------------------------------------------------------
cmd_bench() {
  local runtime="claude" model="" matrix="" effort="" exec_tpl="" task="" task_file="" repo="$PWD" reps=1 out="" dry=0 api_base="" api_key=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --runtime)    runtime="${2:-}"; shift 2 ;;
      --runtime=*)  runtime="${1#--runtime=}"; shift ;;
      --model)      model="${2:-}"; shift 2 ;;
      --model=*)    model="${1#--model=}"; shift ;;
      --matrix)     matrix="${2:-}"; shift 2 ;;
      --matrix=*)   matrix="${1#--matrix=}"; shift ;;
      --effort)     effort="${2:-}"; shift 2 ;;
      --effort=*)   effort="${1#--effort=}"; shift ;;
      --api-base)   api_base="${2:-}"; runtime="openai"; shift 2 ;;
      --api-base=*) api_base="${1#--api-base=}"; runtime="openai"; shift ;;
      --api-key)    api_key="${2:-}"; shift 2 ;;
      --api-key=*)  api_key="${1#--api-key=}"; shift ;;
      --exec)       exec_tpl="${2:-}"; runtime="generic"; shift 2 ;;
      --exec=*)     exec_tpl="${1#--exec=}"; runtime="generic"; shift ;;
      --task)       task="${2:-}"; shift 2 ;;
      --task=*)     task="${1#--task=}"; shift ;;
      --task-file)  task_file="${2:-}"; shift 2 ;;
      --repo)       repo="${2:-}"; shift 2 ;;
      --reps)       reps="${2:-1}"; shift 2 ;;
      --out)        out="${2:-}"; shift 2 ;;
      --dry-run|-n) dry=1; shift ;;
      *) shift ;;
    esac
  done
  [[ -z "$out" ]] && out="$PWD/docs/benchmarks/results"
  [[ -n "$task_file" && -f "$task_file" ]] && task="$(cat "$task_file")"
  [[ -z "$task" ]] && task="In as few steps as possible, answer: where is the release pipeline configured, and what are the exact steps to add a new package channel? List every file you opened to find out."

  local -a bench_models raw_models
  local raw_model clean_model
  bench_models=()
  if [[ -n "$matrix" ]]; then
    if [[ -n "$model" ]]; then
      _die "--matrix cannot be combined with --model"
    fi
    IFS=',' read -r -a raw_models <<< "$matrix"
    for raw_model in "${raw_models[@]}"; do
      clean_model="$(printf '%s' "$raw_model" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
      if [[ -n "$clean_model" ]]; then
        bench_models+=("$clean_model")
      fi
    done
    if [[ ${#bench_models[@]} -eq 0 ]]; then
      _die "--matrix needs at least one model"
    fi
  else
    bench_models=("$model")
  fi

  local parser rt_check
  case "$runtime" in
    claude)   parser="claude";  rt_check="claude" ;;
    codex)    parser="codex";    rt_check="codex" ;;
    opencode) parser="opencode"; rt_check="opencode" ;;
    openai)   parser="openai";  rt_check="" ;;
    generic)  parser="generic"; rt_check=""; [[ -z "$exec_tpl" ]] && _die "--runtime generic needs --exec '<cmd referencing \$ATLAS_BENCH_TASK>'" ;;
    *) _die "unknown --runtime '$runtime' (claude | codex | opencode | openai | generic via --exec)" ;;
  esac
  if [[ "$runtime" == "openai" ]]; then
    if [[ -z "$api_base" ]]; then api_base="https://api.openai.com/v1"; fi
    if [[ -z "$api_key"  ]]; then api_key="${OPENAI_API_KEY:-none}"; fi
    if [[ -z "$model" && -z "$matrix" ]]; then
      _die "--runtime openai needs --model or --matrix (e.g. --model qwen3.6-35b)"
    fi
  fi
  if [[ "$runtime" == "opencode" && -z "$model" && -z "$matrix" && $dry -ne 1 ]]; then
    _die "--runtime opencode needs --model or --matrix provider/model (e.g. --model dgx-spark-prisma/qwen3.6-35b) — without it opencode hangs on selection."
  fi
  if [[ $dry -ne 1 ]]; then
    _have python3 || _die "atlas bench needs python3 for parsing + the report."
    if [[ "$runtime" == "openai" ]]; then _have curl || _die "openai mode needs curl."; fi
    if [[ -n "$rt_check" ]]; then _have "$rt_check" || _die "'$rt_check' not on PATH — install it, or pass --dry-run."; fi
  fi

  # Reproducibility metadata.
  local rt_ver date_utc base cleanup_base="" repo_sha="n/a" repo_label
  date_utc="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
  case "$runtime" in
    claude)   rt_ver="$(claude --version 2>/dev/null | head -1 || true)" ;;
    codex)    rt_ver="$(codex --version 2>/dev/null | head -1 || true)" ;;
    opencode) rt_ver="$(opencode --version 2>/dev/null | head -1 || true)" ;;
    openai)   rt_ver="openai-compat: $api_base (${model:-matrix:$matrix})" ;;
    generic)  rt_ver="exec: $exec_tpl" ;;
  esac
  # codex's `exec --json` stdout never carries the model name — fall back to its
  # configured default so the ledger records the real model, not "default".
  if [[ "$runtime" == "codex" && -z "$model" && -f "$HOME/.codex/config.toml" ]]; then
    model="$(grep -m1 -E '^model[[:space:]]*=' "$HOME/.codex/config.toml" 2>/dev/null | sed -E 's/^model[[:space:]]*=[[:space:]]*"?([^"#[:space:]]+).*/\1/' || true)"
  fi

  _banner "📊" "atlas bench  $(_c_dim "(runtime=$runtime · reps=$reps${model:+ · $model}${matrix:+ · matrix=$matrix}${effort:+ · effort=$effort})")"
  _say "task: ${task:0:150}…"

  if [[ "$repo" == http*://* || "$repo" == git@* || "$repo" == *.git ]]; then
    base="$(mktemp -d)"; cleanup_base="$base"
    if [[ $dry -eq 1 ]]; then _say "(dry-run) would clone $repo"
    else _say "cloning $repo"; git clone --depth 1 "$repo" "$base" >/dev/null 2>&1 || _die "clone failed"; fi
  else
    base="$(cd "$repo" && pwd)"
  fi
  git -C "$base" rev-parse --short HEAD >/dev/null 2>&1 && repo_sha="$(git -C "$base" rev-parse --short HEAD)"
  repo_label="$(basename "$base")@${repo_sha}"

  local quartet=(ATLAS.md SCARS.md CLAUDE.md AGENTS.md EXAMPLES.md llms.txt .agents)
  local runs; runs="$(mktemp -d)"
  printf '%s' "$task" > "$runs/task.txt"

  local cond rep work f t0 t1 active_model model_slug out_fn run_label
  if [[ "$runtime" == "openai" ]]; then
    # Single-shot: feed the quartet vs the raw repo as context; measure prompt_tokens.
    local ctx_w="$runs/ctx-with.txt" ctx_o="$runs/ctx-without.txt" ctxf sk cnt ff
    {
      echo "# ATLAS.md (orientation spine §0-1 — what the hook injects first)"
      awk '/^## 2\./{exit} 1' "$base/ATLAS.md" 2>/dev/null || true
      echo; echo "# SCARS.md (table of contents)"
      awk '/^## Table of contents/{x=1} x&&/^## /&&!/Table of contents/{exit} x' "$base/SCARS.md" 2>/dev/null || true
      sk=$(find "$base/.agents/skill" -name SKILL.md 2>/dev/null | head -1)
      echo; echo "# SKILL.md (table of contents)"
      { [[ -n "$sk" ]] && awk '/^## Table of contents/{x=1} x&&/^## /&&!/Table of contents/{exit} x' "$sk"; } || true
    } > "$ctx_w"
    {
      echo "# README.md"; cat "$base/README.md" 2>/dev/null || true
      echo; echo "# Files"; { { git -C "$base" ls-files 2>/dev/null || ( cd "$base" && find . -type f ); } | head -400; } || true   # SCARS §PIPE-HEAD-SIGPIPE
      echo; echo "# Source file heads"; cnt=0
      while IFS= read -r ff; do
        [[ $cnt -ge 15 ]] && break
        [[ -f "$base/$ff" ]] || continue
        echo "--- $ff ---"; head -c 1800 "$base/$ff" 2>/dev/null; echo; cnt=$(( cnt + 1 ))
      done < <( git -C "$base" ls-files 2>/dev/null | grep -Ei '\.(py|js|ts|go|rs|rb|sh|java|c|cc|cpp|h)$' || true )
    } > "$ctx_o"
    for active_model in "${bench_models[@]}"; do
      model_slug="$(printf '%s' "${active_model:-default}" | sed 's/[^A-Za-z0-9_.-]/_/g')"
      for cond in without with; do
        if [[ "$cond" == "with" ]]; then ctxf="$ctx_w"; else ctxf="$ctx_o"; fi
        for (( rep=1; rep<=reps; rep++ )); do
          out_fn="${cond}-${rep}.out"
          if [[ -n "$matrix" ]]; then out_fn="${model_slug}-${cond}-${rep}.out"; fi
          run_label="$cond"
          if [[ -n "$matrix" ]]; then run_label="${active_model:-default} $cond"; fi
          if [[ $dry -eq 1 ]]; then
            printf "  %-22s #%d  →  POST %s/chat/completions  model=%s  ctx=%s bytes\n" "$run_label" "$rep" "$api_base" "${active_model:-default}" "$(wc -c < "$ctxf" | tr -d ' ')"
            continue
          fi
          _say "querying $run_label #$rep  ($(wc -c < "$ctxf" | tr -d ' ') ctx bytes)…"
          python3 -c 'import json,sys; ctx=open(sys.argv[1]).read(); print(json.dumps({"model":sys.argv[3],"messages":[{"role":"user","content":ctx+"\n\n# Question\n"+sys.argv[2]+"\n\nAnswer concisely; name the files that hold the answer."}],"max_tokens":400,"temperature":0}))' "$ctxf" "$task" "$active_model" > "$runs/payload.json"
          t0=$(date +%s)
          curl -sS --max-time 180 "$api_base/chat/completions" -H "Content-Type: application/json" -H "Authorization: Bearer $api_key" -d @"$runs/payload.json" > "$runs/$out_fn" 2>/dev/null || true
          t1=$(date +%s)
          printf '%s\t%d\t%d\t%s\t%s\n' "$cond" "$rep" "$(( t1 - t0 ))" "$out_fn" "${active_model:-default}" >> "$runs/index.tsv"
        done
      done
    done
  else
  for active_model in "${bench_models[@]}"; do
    model_slug="$(printf '%s' "${active_model:-default}" | sed 's/[^A-Za-z0-9_.-]/_/g')"
    for cond in without with; do
      for (( rep=1; rep<=reps; rep++ )); do
        out_fn="${cond}-${rep}.out"
        if [[ -n "$matrix" ]]; then out_fn="${model_slug}-${cond}-${rep}.out"; fi
        run_label="$cond"
        if [[ -n "$matrix" ]]; then run_label="${active_model:-default} $cond"; fi
      local agent=()
      case "$runtime" in
        claude)
          agent=(claude -p "$task" --output-format json)
          if [[ -n "$active_model" ]]; then agent+=(--model "$active_model"); fi
          if [[ -n "$effort" ]]; then agent+=(--effort "$effort"); fi ;;
        codex)
          # --skip-git-repo-check: the work dir is a `git archive` export with no
          # .git, and `codex exec` otherwise refuses to run there → 0s / no output
          # (SCARS §BENCH-NEEDS-GIT).
          agent=(codex exec --json --skip-git-repo-check "$task")
          if [[ -n "$active_model" ]]; then agent+=(--model "$active_model"); fi
          if [[ -n "$effort" ]]; then agent+=(-c "model_reasoning_effort=$effort"); fi ;;
        opencode)
          # --pure: skip user plugins that otherwise flood stdout with log lines
          # and corrupt the JSON (SCARS §OPENCODE-PURE-JSON). A model is required
          # or opencode hangs waiting on selection.
          agent=(opencode run --pure --format json "$task")
          if [[ -n "$active_model" ]]; then agent+=(-m "$active_model"); fi
          if [[ -n "$effort" ]]; then agent+=(--variant "$effort"); fi ;;
      esac

      # Dry-run is print-only — NO mktemp / git-archive. (`git archive | tar`
      # aborts under bin/atlas's set -e+pipefail on a shallow CI clone, which is
      # what broke the `bench dry-run` CI check.)
      if [[ $dry -eq 1 ]]; then
        if [[ "$runtime" == "generic" ]]; then
          printf "  %-22s #%d  →  (cd <tmp> && ATLAS_BENCH_TASK=… bash -c %q)\n" "$run_label" "$rep" "$exec_tpl"
        else
          printf "  %-22s #%d  →  (cd <tmp> && %s)\n" "$run_label" "$rep" "${agent[*]}"
        fi
        continue
      fi

      work="$(mktemp -d)"
      if git -C "$base" rev-parse HEAD >/dev/null 2>&1; then
        git -C "$base" archive HEAD 2>/dev/null | tar -x -C "$work" 2>/dev/null || cp -R "$base/." "$work/" 2>/dev/null || true
      else
        cp -R "$base/." "$work/" 2>/dev/null || true
      fi
      if [[ "$cond" == "without" ]]; then
        for f in "${quartet[@]}"; do rm -rf "${work:?}/$f"; done
      fi

      _say "running $run_label #$rep …"
      t0=$(date +%s)
      # < /dev/null: headless agents (codex --json) otherwise block "Reading
      # additional input from stdin…" and hang. keep stderr (a silent 2>/dev/null
      # once hid a runtime that bailed at 0s — SCARS §BENCH-NEEDS-GIT).
      if [[ "$runtime" == "generic" ]]; then
        ( cd "$work" && ATLAS_BENCH_TASK="$task" bash -c "$exec_tpl" < /dev/null ) > "$runs/$out_fn" 2> "$runs/${out_fn%.out}.err" || true
      else
        ( cd "$work" && "${agent[@]}" < /dev/null ) > "$runs/$out_fn" 2> "$runs/${out_fn%.out}.err" || true
      fi
      t1=$(date +%s)
      printf '%s\t%d\t%d\t%s\t%s\n' "$cond" "$rep" "$(( t1 - t0 ))" "$out_fn" "${active_model:-default}" >> "$runs/index.tsv"
      rm -rf "$work"
      done
    done
  done
  fi

  if [[ $dry -eq 1 ]]; then
    _say "dry-run — no agents invoked."
    rm -rf "$runs"; [[ -n "$cleanup_base" ]] && rm -rf "$cleanup_base"
    return 0
  fi

  # Metadata file (python writes it so json escaping is safe).
  python3 -c 'import json,sys; json.dump(dict(zip(["runtime","runtime_version","model","effort","atlas_version","date_utc","repo","reps","parser","matrix"],sys.argv[2:])),open(sys.argv[1],"w"))' \
    "$runs/meta.json" "$runtime" "$rt_ver" "${model:-default}" "${effort:-default}" "$ATLAS_VERSION" "$date_utc" "$repo_label" "$reps" "$parser" "$matrix"

  mkdir -p "$out"
  local stamp base_path; stamp="$(date +%Y%m%d-%H%M%S)"; base_path="$out/bench-${runtime}-${stamp}"
  python3 - "$runs" "$base_path" <<'PY'
import json, sys, os, statistics
runs_dir, base_path = sys.argv[1:3]
def count_tokens(text):
    try:
        import tiktoken; return len(tiktoken.get_encoding("cl100k_base").encode(text))
    except Exception:
        return max(1, len(text)//4)
meta=json.load(open(os.path.join(runs_dir,"meta.json")))
task=open(os.path.join(runs_dir,"task.txt")).read()
parser=meta["parser"]
rows=[]; detected_models=set(); idx=os.path.join(runs_dir,"index.tsv")
if os.path.exists(idx):
    for line in open(idx):
        parts=line.rstrip("\n").split("\t")
        if len(parts)<4:
            continue
        cond,rep,wall,fn=parts[:4]
        row_model=parts[4] if len(parts)>4 else meta.get("model","default")
        raw=open(os.path.join(runs_dir,fn)).read()
        rec={"condition":cond,"rep":int(rep),"model":row_model,"wall_s":int(wall),"output_chars":len(raw)}
        if not raw.strip():
            ep=os.path.join(runs_dir, fn.rsplit(".",1)[0]+".err")
            errtxt=open(ep).read().strip() if os.path.exists(ep) else ""
            rec["parse_error"]="empty output"+((": "+errtxt[:200]) if errtxt else " (runtime produced nothing — check install/auth)")
        if parser=="claude":
            try:
                d=json.loads(raw); events=d if isinstance(d,list) else [d]
                result=None; inp=0; outp=0
                for e in events:
                    if not isinstance(e,dict): continue
                    u=e.get("usage") or (e.get("message") or {}).get("usage") or {}
                    mdl=(e.get("message") or {}).get("model")
                    if mdl: detected_models.add(mdl)
                    if e.get("type")=="assistant" and u:
                        inp+=(u.get("input_tokens") or 0)+(u.get("cache_creation_input_tokens") or 0)
                        outp+=(u.get("output_tokens") or 0)
                    if e.get("type")=="result" or "total_cost_usd" in e: result=e
                    for mk in (e.get("modelUsage") or {}): detected_models.add(mk)
                if result is not None:
                    rec["num_turns"]=result.get("num_turns")
                    rec["cost_usd"]=result.get("total_cost_usd")
                    ru=result.get("usage") or {}
                    rec["input_tokens"]=inp or (ru.get("input_tokens") or 0)
                    rec["output_tokens"]=outp or (ru.get("output_tokens") or 0)
                    rec["is_error"]=result.get("is_error")
                else:
                    rec["parse_error"]="no result event in output"
            except Exception as ex:
                rec["parse_error"]=str(ex)
        elif parser=="openai":
            # Deterministic + endpoint-independent: tokenize the fixed context
            # (reproducible for the ledger). The endpoint's reported usage is
            # recorded separately as a cross-check — it can be unreliable under
            # server-side prefix caching (observed on a local vLLM).
            ctxf=os.path.join(runs_dir,"ctx-%s.txt"%cond)
            rec["input_tokens"]=count_tokens(open(ctxf).read()) if os.path.exists(ctxf) else 0
            rec["num_turns"]=1
            try:
                d=json.loads(raw)
                if "error" in d: rec["parse_error"]=str(d["error"])[:120]
                u=d.get("usage") or {}
                rec["endpoint_prompt_tokens"]=u.get("prompt_tokens")
                rec["output_tokens"]=u.get("completion_tokens") or 0
            except Exception as ex:
                rec["parse_error"]=str(ex)
        elif parser=="opencode":
            # opencode `run --pure --format json` may emit a single JSON doc OR a
            # JSONL event stream. Collect every parseable object either way; each
            # assistant message carries tokens:{input,output,…}+cost+modelID.
            objs=[]
            try:
                objs=[json.loads(raw)]
            except Exception:
                for ln in raw.splitlines():
                    ln=ln.strip()
                    if not ln or ln[0] not in "[{": continue   # skip any stray log lines
                    try: objs.append(json.loads(ln))
                    except Exception: pass
                if not objs:
                    for i in (raw.find("["), raw.find("{")):
                        if i>=0:
                            try: objs.append(json.loads(raw[i:])); break
                            except Exception: pass
            if not objs:
                if not rec.get("parse_error"): rec["parse_error"]="opencode: no JSON in output"
            else:
                acc={"inp":0,"outp":0,"cost":0.0,"turns":0}
                def _visit(o):
                    if isinstance(o,dict):
                        tk=o.get("tokens")
                        if isinstance(tk,dict) and (("input" in tk) or ("output" in tk)):
                            acc["inp"]+=tk.get("input") or 0; acc["outp"]+=tk.get("output") or 0; acc["turns"]+=1
                        if isinstance(o.get("cost"),(int,float)): acc["cost"]+=o["cost"]
                        if o.get("modelID"): detected_models.add(o["modelID"])
                        for v in o.values(): _visit(v)
                    elif isinstance(o,list):
                        for v in o: _visit(v)
                for o in objs: _visit(o)
                if acc["inp"] or acc["outp"]: rec["input_tokens"]=acc["inp"]; rec["output_tokens"]=acc["outp"]
                if acc["cost"]: rec["cost_usd"]=round(acc["cost"],6)
                if acc["turns"]>0: rec["num_turns"]=acc["turns"]   # else fall back to wall_s
                if acc["turns"]==0 and acc["inp"]==0 and not rec.get("parse_error"):
                    rec["parse_error"]="opencode: parsed %d obj(s) but no token-bearing messages — using wall_s"%len(objs)
        elif parser=="codex":
            # codex `exec --json` emits JSONL events. Token usage rides on
            # payload.type=="token_count" (info.total_token_usage); count
            # agent_message events as turns. ($ cost is plan-based, not reported.)
            turns=0; last=None; seen=False
            for ln in raw.splitlines():
                ln=ln.strip()
                if not ln: continue
                try: e=json.loads(ln)
                except Exception: continue
                seen=True
                pl=e.get("payload") if isinstance(e.get("payload"),dict) else e
                pt=pl.get("type")
                # turn markers vary by codex version: agent_message (rollout) or an
                # item.completed carrying an assistant message (exec --json stream).
                if pt in ("agent_message","agent_reasoning"): turns+=1
                elif pt in ("item.completed","item_completed"):
                    it=pl.get("item") or e.get("item") or {}
                    if str(it.get("type") or "").endswith("message") and (it.get("role")=="assistant" or "text" in it): turns+=1
                if pt=="token_count":
                    info=pl.get("info") or {}
                    tu=info.get("total_token_usage") or info.get("last_token_usage") or {}
                    if tu: last=tu
                u2=pl.get("usage") or (pl.get("response") or {}).get("usage")
                if isinstance(u2,dict) and (u2.get("input_tokens") or u2.get("prompt_tokens")): last=u2
                if pl.get("model"): detected_models.add(pl["model"])
            if last:
                rec["input_tokens"]=last.get("input_tokens") or last.get("prompt_tokens") or 0
                rec["output_tokens"]=last.get("output_tokens") or last.get("completion_tokens") or 0
            if turns>0: rec["num_turns"]=turns   # else: fall back to wall_s (never fake a turn count)
            if not seen and not rec.get("parse_error"):
                rec["parse_error"]="codex: no JSONL events parsed (is --json supported?)"
            elif seen and turns==0 and last is None and not rec.get("parse_error"):
                rec["parse_error"]="codex: events parsed but no turn/usage markers — headlining wall_s"
        # On any parse miss, keep the raw output next to the report so the schema
        # can be inspected without re-running (the temp run dir is cleaned).
        if rec.get("parse_error") and raw.strip():
            try: open("%s-%s-%d.rawout"%(base_path,cond,rec["rep"]),"w").write(raw[:30000])
            except Exception: pass
        rows.append(rec)

# Record the model the runtime *actually* resolved (e.g. "default" → claude-opus-4-8),
# so the ledger never just says "default" (it can't be reconstructed later).
if detected_models:
    meta["model"]="+".join(sorted(detected_models))

if parser in ("claude","opencode"): metrics=["num_turns","cost_usd","wall_s","input_tokens","output_tokens"]
elif parser=="codex": metrics=["num_turns","wall_s","input_tokens","output_tokens"]
elif parser=="openai": metrics=["input_tokens","output_tokens","wall_s"]
else: metrics=["wall_s","output_chars"]
def agg(cond,key):
    vals=[r[key] for r in rows if r["condition"]==cond and isinstance(r.get(key),(int,float))]
    return round(statistics.mean(vals),4) if vals else None
summary={c:{m:agg(c,m) for m in metrics} for c in ("without","with")}
def agg_model(model,cond,key):
    vals=[r[key] for r in rows if r.get("model")==model and r["condition"]==cond and isinstance(r.get(key),(int,float))]
    return round(statistics.mean(vals),4) if vals else None
matrix_summary={}
if meta.get("matrix"):
    for mdl in sorted({r.get("model","default") for r in rows}):
        matrix_summary[mdl]={c:{m:agg_model(mdl,c,m) for m in metrics} for c in ("without","with")}
json.dump({"meta":meta,"task":task,"runs":rows,"summary":summary,"matrix_summary":matrix_summary},open(base_path+".json","w"),indent=2)

# Headline metric. All are lower-is-better. For agentic runtimes we use *turns*
# (orientation efficiency) — NOT summed input_tokens, which double-counts cached
# context per turn and can climb even as cost falls (SCARS §BENCH-TOKEN-SUM-CACHE).
# Falls back to wall_s when turns weren't parsed (e.g. codex/opencode schema drift).
def _has(m): return isinstance(summary["without"].get(m),(int,float)) and isinstance(summary["with"].get(m),(int,float))
def _signal(m): return _has(m) and summary["without"][m]!=summary["with"][m]  # a metric equal on both sides carries no signal
_pref={"claude":["num_turns","wall_s"],"opencode":["num_turns","wall_s"],"codex":["num_turns","wall_s"],
       "openai":["input_tokens"]}.get(parser,["wall_s","output_chars"])
# prefer the first preferred metric with a real (non-degenerate) delta; else the
# first with data. Avoids headlining e.g. opencode's 1-vs-1 message count as "0%".
prim=next((m for m in _pref if _signal(m)), None) or next((m for m in _pref if _has(m)), _pref[-1])
plabel={"num_turns":"fewer turns","input_tokens":"fewer orientation tokens",
        "wall_s":"less wall-time","output_chars":"less output"}.get(prim,"less "+prim)
wo,wi=summary["without"].get(prim),summary["with"].get(prim)
ratio=pct=None
if wo and wi and wi>0: ratio=wo/wi; pct=(1-wi/wo)*100
n=len([r for r in rows if r["condition"]=="with"])
def fn(v):
    if v is None: return "—"
    if isinstance(v,float): return (f"{v:.4f}".rstrip("0").rstrip(".")) if v<10 else f"{v:.0f}"
    return str(v)

md=[f"# ATLAS benchmark — {meta['runtime']} ({meta['date_utc']})",""]
md+=["| field | value |","|---|---|"]
for k in ["date_utc","runtime","runtime_version","model","matrix","effort","atlas_version","repo","reps"]:
    if k=="matrix" and not meta.get(k): continue
    md.append(f"| {k} | {meta[k]} |")
md.append(f"| task | {task.strip()[:300]} |")
md+=["","## Results (mean per condition)","",
     "| condition | "+" | ".join(metrics)+" |",
     "|---|"+"|".join(["---"]*len(metrics))+"|"]
for c in ("without","with"):
    md.append("| "+("without ATLAS" if c=="without" else "with ATLAS")+" | "+" | ".join(fn(summary[c][m]) for m in metrics)+" |")
md.append("")
if matrix_summary:
    md+=["## Matrix results","",
         "| model | condition | "+" | ".join(metrics)+" |",
         "|---|---|"+"|".join(["---"]*len(metrics))+"|"]
    for mdl in sorted(matrix_summary):
        for c in ("without","with"):
            md.append("| "+mdl+" | "+("without ATLAS" if c=="without" else "with ATLAS")+" | "+" | ".join(fn(matrix_summary[mdl][c][m]) for m in metrics)+" |")
    md.append("")
if ratio:
    md.append(f"**→ with ATLAS: {ratio:.1f}× {plabel} ({pct:.0f}% lower).**  _[N={n} per condition — directional; {meta['runtime_version']}, model={meta['model']}, effort={meta['effort']}]_")
else:
    md.append(f"_(no delta computed — check runs for errors. N={n}.)_")
if parser in ("claude","opencode"):
    md.append("")
    md.append("> _Agentic note: summed `input_tokens` over-counts cached context across turns — it moves independently of real cost (it can rise even when turns, output, or **cost** fall), so the headline here is **turns**, with cost + wall-time shown alongside. On a small repo / short task the quartet's fixed injection may not amortize, so cost can go either way. The clean, reproducible token-reduction figure is the deterministic `openai` / `atlas measure` mode (see [RESULTS.md](../RESULTS.md))._")
md+=["","<details><summary>raw per-run</summary>","","```json",json.dumps(rows,indent=2),"```","</details>"]
open(base_path+".md","w").write("\n".join(md)+"\n")

# Longitudinal ledger: append this run + regenerate the master comparison table,
# so results accumulate and ATLAS's reduction is trackable across versions/dates.
ledger_path=os.path.join(os.path.dirname(base_path),"ledger.jsonl")
ledger_records=[]
if matrix_summary:
    for mdl in sorted(matrix_summary):
        msum=matrix_summary[mdl]
        mwo,mwi=msum["without"].get(prim),msum["with"].get(prim)
        mratio=mpct=None
        if mwo and mwi and mwi>0:
            mratio=mwo/mwi; mpct=(1-mwi/mwo)*100
        ledger_records.append({"date":meta["date_utc"],"atlas_version":meta["atlas_version"],"runtime":meta["runtime"],
          "runtime_version":meta["runtime_version"],"model":mdl,"effort":meta["effort"],
          "repo":meta["repo"],"reps":meta["reps"],"metric":prim,"without":mwo,"with":mwi,
          "reduction_pct":(round(mpct,1) if mpct is not None else None),
          "ratio":(round(mratio,2) if mratio is not None else None),
          "report":os.path.basename(base_path)+".md"})
else:
    ledger_records.append({"date":meta["date_utc"],"atlas_version":meta["atlas_version"],"runtime":meta["runtime"],
      "runtime_version":meta["runtime_version"],"model":meta["model"],"effort":meta["effort"],
      "repo":meta["repo"],"reps":meta["reps"],"metric":prim,"without":wo,"with":wi,
      "reduction_pct":(round(pct,1) if pct is not None else None),
      "ratio":(round(ratio,2) if ratio is not None else None),
      "report":os.path.basename(base_path)+".md"})
with open(ledger_path,"a") as fh:
    for lrec in ledger_records:
        fh.write(json.dumps(lrec)+"\n")
matrix_ledger_path=os.path.join(os.path.dirname(base_path),"matrix-ledger.jsonl")
if matrix_summary:
    with open(matrix_ledger_path,"a") as fh:
        for r in rows:
            mrec={"date":meta["date_utc"],"atlas_version":meta["atlas_version"],"runtime":meta["runtime"],
                  "runtime_version":meta["runtime_version"],"model":r.get("model","default"),
                  "effort":meta["effort"],"repo":meta["repo"],"condition":r["condition"],
                  "rep":r["rep"],"report":os.path.basename(base_path)+".md"}
            for m in metrics:
                if r.get(m) is not None:
                    mrec[m]=r.get(m)
            if r.get("parse_error"):
                mrec["parse_error"]=r.get("parse_error")
            fh.write(json.dumps(mrec)+"\n")
entries=[json.loads(l) for l in open(ledger_path) if l.strip()]
entries.sort(key=lambda r: r.get("date",""))
res_md=os.path.join(os.path.dirname(os.path.dirname(base_path)),"RESULTS.md")
cols=["date","atlas_version","runtime","model","effort","repo","metric","without","with","reduction_pct","ratio"]
LL=["# ATLAS benchmark ledger","",
    "Every `atlas bench` run is appended here (newest last) for longitudinal comparison —",
    "track ATLAS's orientation-token reduction across versions, repos, models, and dates.",
    "Source of truth: `results/ledger.jsonl`. Per-run detail: the linked report.","",
    "| "+" | ".join(c.replace("_"," ") for c in cols)+" |",
    "|"+"|".join(["---"]*len(cols))+"|"]
for e in entries:
    LL.append("| "+" | ".join((str(e.get(c)) if e.get(c) is not None else "—") for c in cols)+" |")
LL+=["",
     "> **Reading this table.** `openai` rows are the **deterministic single-shot** headline — tokenize a *fixed* context once (reproducible, model-independent) — the source of **−92% / 12.8×**. `claude` (agentic) rows are a live task loop: metric = **turns** (lower-is-better), N per `reps`, **directional**. Their cost/wall vary with model, task, and repo size — on a tiny repo the quartet's fixed injection may not amortize, so agentic *cost* can rise even when turns fall. Don't headline agentic token counts (SCARS §BENCH-TOKEN-SUM-CACHE). See [methodology](methodology.md)."]
open(res_md,"w").write("\n".join(LL)+"\n")

W=16
print(); print("  "+"condition".ljust(W)+"".join(m.ljust(W) for m in metrics))
for c in ("without","with"): print("  "+c.ljust(W)+"".join(fn(summary[c][m]).ljust(W) for m in metrics))
print()
if ratio: print(f"  → with ATLAS: {ratio:.1f}× {plabel} ({pct:.0f}% lower)   [N={n} — directional]")
else: print(f"  → no delta computed (check for errors). N={n}")
errs=[r for r in rows if r.get("parse_error") or r.get("is_error")]
if errs: print(f"  ⚠ {len(errs)} run(s) flagged errors — see the JSON")
print(f"  report: {base_path}.md")
print(f"  ledger: {res_md}  (all runs, for comparison over time)")
if matrix_summary: print(f"  matrix ledger: {matrix_ledger_path}  (one row per model × condition × rep)")
PY

  rm -rf "$runs"; [[ -n "$cleanup_base" ]] && rm -rf "$cleanup_base"
  _say "comparable across runs (version+model+effort+date+sha recorded). Methodology: docs/benchmarks/methodology.md"
}

# onboard — drop ATLAS into THIS repo in one shot: scaffold the quartet, auto-draft
# the map, measure the savings, and (with --pr) open a pull request. The spread engine.
#   atlas onboard            scaffold + analyze + measure (local)
#   atlas onboard --pr       also: branch, commit the quartet, open a PR via gh
cmd_onboard() {
  local do_pr=0 branch="atlas-onboard"
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --pr)       do_pr=1; shift ;;
      --branch)   branch="${2:-atlas-onboard}"; shift 2 ;;
      --branch=*) branch="${1#--branch=}"; shift ;;
      *) shift ;;
    esac
  done
  git rev-parse --git-dir >/dev/null 2>&1 || _die "not a git repository — run inside the repo you want to onboard."
  _banner "🚀" "onboarding $(basename "$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")") to ATLAS"
  cmd_init --analyze
  echo ""
  local measure; measure="$(cmd_measure 2>/dev/null | grep -v 'atlas:' || true)"
  printf '%s\n' "$measure"
  if [[ $do_pr -eq 0 ]]; then
    echo ""; _say "review the quartet, then commit — or re-run with --pr to open a PR."
    return 0
  fi
  _have gh || _die "--pr needs the gh CLI — install it, or commit the quartet yourself."
  git remote get-url origin >/dev/null 2>&1 || _die "--pr needs an 'origin' remote."
  git checkout -b "$branch" 2>/dev/null || git checkout "$branch" 2>/dev/null || _die "couldn't switch to branch '$branch'."
  git add ATLAS.md SCARS.md CLAUDE.md AGENTS.md EXAMPLES.md llms.txt .agents >/dev/null 2>&1 || true
  if git diff --cached --quiet; then _die "nothing to commit (already onboarded?)."; fi
  git commit -q -m "chore: add ATLAS — agent orientation map, behavioral contract, failure memory"
  git push -u origin "$branch" >/dev/null 2>&1 || _die "push failed (check your remote + auth)."
  local body
  body="## Add ATLAS — the map your agents read first

Adds the ATLAS quartet (ATLAS.md + SKILL.md + SCARS.md + CLAUDE.md/AGENTS.md) so any AI agent (Claude Code, Cursor, Codex, OpenClaw, …) orients from a curated map instead of grepping.

\`\`\`
${measure}
\`\`\`

\`atlas check\` validates it · \`atlas map\` draws it · \`atlas mcp\` serves it over MCP. The §0.5 map was auto-drafted by \`atlas init --analyze\` — refine it, then delete the note.

Powered by [ATLAS](https://github.com/Abbasi-Alain/atlas)."
  gh pr create --title "Add ATLAS — agent orientation map" --body "$body" 2>&1 | tail -1
  _ok "opened the onboarding PR."
}

# hooks — install a git pre-commit hook that keeps ATLAS.md from going stale.
#   atlas hooks install [--auto]   warn on map drift (--auto: refresh §0.5 + stage it)
#   atlas hooks uninstall          remove the atlas hook block
#   atlas hooks status             is it installed?
cmd_hooks() {
  local sub="${1:-status}"; [[ $# -gt 0 ]] && shift
  local auto=0 a; for a in "$@"; do [[ "$a" == "--auto" ]] && auto=1; done
  local gd; gd="$(git rev-parse --git-dir 2>/dev/null)" || _die "not a git repository."
  local hook="$gd/hooks/pre-commit"
  local START="# >>> atlas self-maintaining map >>>" END="# <<< atlas self-maintaining map <<<"
  case "$sub" in
    install)
      mkdir -p "$gd/hooks"
      local body="#!/usr/bin/env bash"
      if [[ -f "$hook" ]]; then
        body="$(awk -v s="$START" -v e="$END" '$0==s{skip=1} !skip{print} $0==e{skip=0}' "$hook")"
        [[ "$body" == *'#!'* ]] || body="#!/usr/bin/env bash"$'\n'"$body"
      fi
      local action
      if [[ $auto -eq 1 ]]; then
        action='atlas init --analyze >/dev/null 2>&1 && git add ATLAS.md && echo "atlas: refreshed ATLAS.md §0.5 — map kept current"'
      else
        action='echo "atlas: structural change without an ATLAS.md update — run: atlas init --analyze  (or: atlas hooks install --auto)" >&2'
      fi
      {
        printf '%s\n' "$body" "$START" \
          'command -v atlas >/dev/null 2>&1 || exit 0' \
          'atlas check --changed-files >/dev/null 2>&1 && exit 0' \
          "$action" "$END"
      } > "$hook"
      chmod +x "$hook"
      _ok "installed pre-commit hook$([[ $auto -eq 1 ]] && echo ' (--auto: auto-refreshes §0.5)') → $(_c_dim "$hook")"
      ;;
    uninstall)
      [[ -f "$hook" ]] || { _say "no pre-commit hook."; return 0; }
      awk -v s="$START" -v e="$END" '$0==s{skip=1} !skip{print} $0==e{skip=0}' "$hook" > "$hook.tmp" && mv "$hook.tmp" "$hook"
      _ok "removed the atlas hook block."
      ;;
    status)
      if [[ -f "$hook" ]] && grep -qF "$START" "$hook"; then _ok "self-maintaining hook is installed."; else _say "not installed — run 'atlas hooks install'."; fi
      ;;
    *) _die "usage: atlas hooks <install [--auto] | uninstall | status>" ;;
  esac
}

# map — render the repo's structure (ATLAS.md §1) as a diagram.
#   atlas map                    a Unicode graph in a terminal; Mermaid when piped/redirected
#   atlas map --ascii|--mermaid  force the terminal graph or the Markdown code block
#   atlas map --html [--out f]   a standalone HTML page (mermaid.js) — screenshot it
cmd_map() {
  local fmt="" out=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --html)    fmt="--html"; shift ;;
      --ascii)   fmt="--ascii"; shift ;;
      --mermaid) fmt="--mermaid"; shift ;;
      --out)     out="${2:-}"; shift 2 ;;
      --out=*)   out="${1#--out=}"; shift ;;
      *) shift ;;
    esac
  done
  _have python3 || _die "atlas map needs python3."
  local server="$ATLAS_HOME/bin/atlas-mcp"
  [[ -f "$server" ]] || _die "atlas-mcp not found at $server"
  export ATLAS_PROJECT="${ATLAS_PROJECT:-$PWD}" ATLAS_BIN="$ATLAS_HOME/bin/atlas" ATLAS_VERSION
  # No explicit format: a terminal gets the visual ASCII map; a pipe/file gets
  # Mermaid (so `atlas map > X.md` still renders on GitHub). SCARS — keep this.
  if [[ -z "$fmt" ]]; then
    if [[ -z "$out" && -t 1 ]]; then fmt="--ascii"; else fmt="--mermaid"; fi
  fi
  if [[ -n "$out" ]]; then
    python3 "$server" --map $fmt > "$out" && _ok "wrote $out  $(_c_dim '(open it / screenshot for the README)')"
  else
    python3 "$server" --map $fmt
  fi
}

# context — the tool-free, zero-dependency context bundle (SPEC §15): the
# canonical consumption form any harness (a bare API call, a non-function-
# calling local model, a RAG pipeline, a human pasting into a chat) can
# prepend verbatim — no hook/MCP/adapter required. In priority order: ATLAS.md
# through end of §1, SCARS.md ToC, SKILL.md ToC, CLAUDE.md (full). --task
# delegates to `atlas orient`'s relevance slice when python3 is available;
# without it, degrades to the full bundle (never fails). --budget caps the
# emitted bytes, dropping lowest-priority sections first (CLAUDE.md, then
# SKILL ToC, then SCARS ToC — the ATLAS slice is never dropped), with a final
# hard truncate as a last resort so output NEVER exceeds N. --hash prints a
# stable sha256 of exactly the emitted bundle + a manifest (files, commit,
# bytes) — context provenance, citable as "ran against spine sha256:…".
cmd_context() {
  local task="" budget=0 format="md" want_hash=0
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --task)     task="${2:-}"; shift 2 ;;
      --task=*)   task="${1#--task=}"; shift ;;
      --budget)   budget="${2:-0}"; shift 2 ;;
      --budget=*) budget="${1#--budget=}"; shift ;;
      --format)   format="${2:-md}"; shift 2 ;;
      --format=*) format="${1#--format=}"; shift ;;
      --hash)     want_hash=1; shift ;;
      *) shift ;;
    esac
  done
  [[ "$format" == "md" || "$format" == "json" ]] || _die "--format must be 'md' or 'json'"
  [[ "$budget" =~ ^[0-9]+$ ]] || _die "--budget must be a non-negative integer (bytes)"

  local atlas="$PWD/ATLAS.md"
  [[ -f "$atlas" ]] || _die "no ATLAS.md in $PWD — run 'atlas init' first"
  local scars="$PWD/SCARS.md"
  local claude="$PWD/CLAUDE.md"
  local skill; skill=$(find "$PWD/.agents/skill" -maxdepth 2 -name SKILL.md 2>/dev/null | head -1)

  local bundle="" delegated=0

  # --task: delegate to the atlas-mcp orient slice when python3 is available.
  # Never fail on its absence — degrade to the full spine + a stderr note.
  if [[ -n "$task" ]]; then
    if _have python3; then
      local server="$ATLAS_HOME/bin/atlas-mcp"
      if [[ -f "$server" ]]; then
        export ATLAS_PROJECT="${ATLAS_PROJECT:-$PWD}" ATLAS_BIN="$ATLAS_HOME/bin/atlas" ATLAS_VERSION
        bundle="$(python3 "$server" --orient "$task" 2>/dev/null)"
      fi
    fi
    if [[ -n "$bundle" ]]; then
      delegated=1
    else
      echo "atlas context: no python3 (or atlas-mcp) available for --task — degrading to the full spine" >&2
    fi
  fi

  # The full spine: ATLAS §0-1 (never dropped) + SCARS ToC + SKILL ToC +
  # CLAUDE.md (full), each toggled off in that order as --budget requires.
  local use_claude=1 use_skill=1 use_scars=1
  local atlas_slice scars_toc skill_toc claude_full
  if [[ $delegated -eq 0 ]]; then
    atlas_slice="$(awk '/^## 2\./{exit} 1' "$atlas" 2>/dev/null)"
    scars_toc=""
    [[ -f "$scars" ]] && scars_toc="$(awk '/^## Table of contents/{x=1} x&&/^## /&&!/Table of contents/{exit} x' "$scars" 2>/dev/null)"
    skill_toc=""
    [[ -n "$skill" && -f "$skill" ]] && skill_toc="$(awk '/^## Table of contents/{x=1} x&&/^## /&&!/Table of contents/{exit} x' "$skill" 2>/dev/null)"
    claude_full=""
    [[ -f "$claude" ]] && claude_full="$(cat "$claude")"

    _ctx_build() {
      local out
      out="# ATLAS CONTEXT — $(_project_name)"$'\n\n'"## ATLAS.md (map §0-1)"$'\n'"$atlas_slice"
      [[ "$use_scars"  == 1 && -n "$scars_toc"   ]] && out="$out"$'\n\n'"## SCARS.md — Table of contents"$'\n'"$scars_toc"
      [[ "$use_skill"  == 1 && -n "$skill_toc"   ]] && out="$out"$'\n\n'"## SKILL.md — Table of contents"$'\n'"$skill_toc"
      [[ "$use_claude" == 1 && -n "$claude_full" ]] && out="$out"$'\n\n'"## CLAUDE.md (behavioral contract)"$'\n'"$claude_full"
      printf '%s' "$out"
    }

    bundle="$(_ctx_build)"
    if [[ "$budget" -gt 0 ]]; then
      local n; n=$(printf '%s' "$bundle" | wc -c | tr -d ' ')
      if [[ "$n" -gt "$budget" ]]; then use_claude=0; bundle="$(_ctx_build)"; n=$(printf '%s' "$bundle" | wc -c | tr -d ' '); fi
      if [[ "$n" -gt "$budget" ]]; then use_skill=0;  bundle="$(_ctx_build)"; n=$(printf '%s' "$bundle" | wc -c | tr -d ' '); fi
      if [[ "$n" -gt "$budget" ]]; then use_scars=0;  bundle="$(_ctx_build)"; fi
    fi
  fi

  # Last-resort invariant: output must NEVER exceed --budget bytes, even for
  # a --task slice (no named sub-sections to drop from) or an ATLAS-only
  # spine that alone exceeds a very small budget.
  if [[ "$budget" -gt 0 ]]; then
    local final_n; final_n=$(printf '%s' "$bundle" | wc -c | tr -d ' ')
    [[ "$final_n" -gt "$budget" ]] && bundle="$(printf '%s' "$bundle" | head -c "$budget")"
  fi

  local files_included=("ATLAS.md")
  if [[ $delegated -eq 1 ]]; then
    files_included=("ATLAS.md" "SCARS.md" "SKILL.md")
  else
    [[ "$use_scars"  == 1 && -n "$scars_toc"   ]] && files_included+=("SCARS.md")
    [[ "$use_skill"  == 1 && -n "$skill_toc"   ]] && files_included+=("SKILL.md")
    [[ "$use_claude" == 1 && -n "$claude_full" ]] && files_included+=("CLAUDE.md")
  fi

  local bytes commit hash=""
  bytes=$(printf '%s' "$bundle" | wc -c | tr -d ' ')
  # --verify -q prints NOTHING on failure — a plain `rev-parse HEAD || echo
  # unborn` emits the literal two-line "HEAD\nunborn" in an unborn repo (the
  # error goes to stderr but "HEAD" still lands on stdout), and that raw
  # newline reaches the JSON manifest as an invalid control character.
  commit="$(git rev-parse --verify -q HEAD 2>/dev/null)" || commit=""
  [[ -z "$commit" ]] && commit="unborn"
  if [[ $want_hash -eq 1 || "$format" == "json" ]]; then
    if _have shasum; then
      hash="$(printf '%s' "$bundle" | shasum -a 256 | awk '{print $1}')"
    elif _have sha256sum; then
      hash="$(printf '%s' "$bundle" | sha256sum | awk '{print $1}')"
    fi
  fi

  if [[ "$format" == "json" ]]; then
    # JSON needs control characters escaped too (a multi-line bundle isn't
    # covered by the single-line-message _json_esc used by `check --json`).
    _ctx_json_esc() {
      local s="$1"
      s="${s//\\/\\\\}"
      s="${s//\"/\\\"}"
      s="${s//$'\r'/}"
      s="${s//$'\n'/\\n}"
      s="${s//$'\t'/\\t}"
      printf '%s' "$s"
    }
    local files_arr="" f
    for f in "${files_included[@]}"; do
      [[ -n "$files_arr" ]] && files_arr="$files_arr,"
      files_arr="$files_arr\"$(_json_esc "$f")\""
    done
    local task_json="null"
    [[ $delegated -eq 1 ]] && task_json="\"$(_json_esc "$task")\""
    printf '{"bundle":"%s","hash":"%s","manifest":{"files":[%s],"commit":"%s","bytes":%s,"task":%s}}\n' \
      "$(_ctx_json_esc "$bundle")" "$hash" "$files_arr" "$commit" "$bytes" "$task_json"
  else
    # No added trailing newline: stdout bytes must equal exactly $bytes (the
    # --budget invariant and the hashed/manifest byte count both count $bundle
    # as-is — an appended newline here would silently exceed --budget by 1).
    printf '%s' "$bundle"
    if [[ $want_hash -eq 1 ]]; then
      echo ""
      echo "---"
      printf 'sha256: %s\nfiles:  %s\ncommit: %s\nbytes:  %s\n' "$hash" "${files_included[*]}" "$commit" "$bytes"
    fi
  fi
}

# orient — print the project map, or (with a task) just the relevant slice.
#   atlas orient                       full §0 map + SKILL/SCARS ToCs
#   atlas orient "add a CLI command"   only the relevant map rows + playbook + SCARS
# Same logic the MCP `atlas_orient` tool serves — handy for humans + scripts.
cmd_orient() {
  _have python3 || _die "atlas orient needs python3."
  local server="$ATLAS_HOME/bin/atlas-mcp"
  [[ -f "$server" ]] || _die "atlas-mcp not found at $server"
  export ATLAS_PROJECT="${ATLAS_PROJECT:-$PWD}" ATLAS_BIN="$ATLAS_HOME/bin/atlas" ATLAS_VERSION
  python3 "$server" --orient "$*"
}

# mcp — launch the ATLAS Model Context Protocol server (stdio, zero-infra).
#
# Serves this project's map to any MCP client (Claude Code/Cursor/OpenClaw/Codex):
# atlas_orient · atlas_find · atlas_scars · atlas_measure. Deep tools
# (atlas_graph/deepsearch/recall) appear only when ATLAS_MCP_BACKEND_URL is set —
# a bring-your-own graph+vector backend (e.g. FuseGraph/FuseRAG), never required.
# `--config` prints the registration snippet. `--http [--host H] [--port N]
# [--token T]` serves over HTTP for team/remote use (token auth is opt-in; a
# token is required to be safe on any non-localhost host).
cmd_mcp() {
  local want_config=0 http=0 token=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --config|--print-config) want_config=1; shift ;;
      --http)      http=1; shift ;;
      --port)      export ATLAS_MCP_PORT="${2:-}"; shift 2 ;;
      --port=*)    export ATLAS_MCP_PORT="${1#--port=}"; shift ;;
      --host)      export ATLAS_MCP_HOST="${2:-}"; shift 2 ;;
      --host=*)    export ATLAS_MCP_HOST="${1#--host=}"; shift ;;
      --token)     token="${2:-}"; shift 2 ;;
      --token=*)   token="${1#--token=}"; shift ;;
      *) shift ;;
    esac
  done
  if [[ $want_config -eq 1 ]]; then
    cat <<'JSON'
{
  "mcpServers": {
    "atlas": { "command": "atlas", "args": ["mcp"] }
  }
}
JSON
    echo "# Claude Code:  claude mcp add atlas -- atlas mcp" >&2
    return 0
  fi
  _have python3 || _die "atlas mcp needs python3."
  local server="$ATLAS_HOME/bin/atlas-mcp"
  [[ -f "$server" ]] || _die "atlas-mcp server not found at $server"
  export ATLAS_PROJECT="${ATLAS_PROJECT:-$PWD}" ATLAS_BIN="$ATLAS_HOME/bin/atlas" ATLAS_VERSION
  [[ -n "$token" ]] && export ATLAS_MCP_TOKEN="$token"
  if [[ $http -eq 1 ]]; then exec python3 "$server" --http; else exec python3 "$server"; fi
}

# --- dispatch -------------------------------------------------------------

main() {
  local sub="${1:-help}"; shift || true
  case "$sub" in
    init)     cmd_init "$@" ;;
    check)    cmd_check "$@" ;;
    fix)      cmd_fix "$@" ;;
    measure)  cmd_measure "$@" ;;
    bench)    cmd_bench "$@" ;;
    loop)     cmd_loop "$@" ;;
    map)      cmd_map "$@" ;;
    onboard)  cmd_onboard "$@" ;;
    hooks)    cmd_hooks "$@" ;;
    orient)   cmd_orient "$@" ;;
    context)  cmd_context "$@" ;;
    mcp)      cmd_mcp "$@" ;;
    doctor)   cmd_doctor "$@" ;;
    badge)    cmd_badge "$@" ;;
    leaderboard) cmd_leaderboard "$@" ;;
    export)   cmd_export "$@" ;;
    anchors)  cmd_anchors "$@" ;;
    anchor)   cmd_anchor "$@" ;;
    remember) cmd_remember "$@" ;;
    install)  cmd_install "$@" ;;
    uninstall) cmd_uninstall "$@" ;;
    styles)   cmd_styles "$@" ;;
    stacks)   cmd_stacks "$@" ;;
    critique) cmd_critique "$@" ;;
    gap-to-article) cmd_gap_to_article "$@" ;;
    cost)     cmd_cost "$@" ;;
    mirror)   cmd_mirror "$@" ;;
    adr)      cmd_adr "$@" ;;
    research) cmd_research "$@" ;;
    auth)     cmd_auth "$@" ;;
    repo)     cmd_repo "$@" ;;
    version|--version|-v) cmd_version ;;
    help|--help|-h|"") cmd_help ;;
    *) _die "unknown subcommand: $sub (try 'atlas help')" ;;
  esac
}

main "$@"
