| name | aps-build-and-env |
| description | Use when setting up this repo on a new or freshly wiped macOS machine, rebuilding the Python 한국어 트리거: 새 맥북 세팅, 환경 재구축, venv 재생성, 서브모듈 초기화, 심링크 복구, 헤드리스 인증, 부트스트랩. .venv, fixing broken external skill symlinks, re-running scripts/env_bootstrap.sh, restoring headless claude auth (setup-token / headless.env), initializing git submodules, recreating the isolated TTS/STT venvs, or registering a new machine in machine_registry.json. Korean triggers: 새 맥북 세팅, 환경 구축, 환경 재구축, venv 재생성, 서브모듈 초기화, 심링크 복구, 헤드리스 인증, 환경 패리티, 부트스트랩. English triggers: new machine setup, environment rebuild, bootstrap, env parity, venv rebuild, submodule init, headless auth, CLI install. Do NOT use for day-2 fleet operation, launchd lifecycle, or runner anatomy (use aps-run-and-operate); do NOT use for config axes like model policy, hooks wiring, or plists (use aps-config-and-flags); do NOT use for diagnosing why a job fails (use aps-debugging-playbook). |
aps-build-and-env — Recreate the Working Environment from Scratch
Scope: everything needed to go from a blank macOS machine to a machine where this repo's
agent harness (interactive Claude Code sessions + unmanned launchd fleet) runs. Day-2
operation (starting/stopping jobs, signal bus, outputs) is owned by aps-run-and-operate.
Definitions used throughout:
- Harness = the Claude Code operating layer of this repo:
.claude/ (skills, rules,
hooks) + scripts/ + the scheduled launchd fleet.
- Parity = the invariant that the home and office machines behave identically. git syncs
tracked files;
scripts/env_bootstrap.sh + env-parity.json sync the machine-external
state (symlinks, venv, CLIs) that git cannot.
- launchd = macOS's scheduler daemon; all cron-style jobs here are launchd agents
(plists in
scripts/launchd/).
All commands assume cwd = repo root (~/thaki/ai-platform-strategy on existing machines).
0. Prerequisites (before cloning)
| Item | How | Why |
|---|
| Xcode CLT | xcode-select --install | git, compilers |
| Homebrew | https://brew.sh installer | nearly every CLI below |
SSH key registered with GitHub, with access to the ThakiCloud org | ssh -T git@github.com must greet you | parent repo + most submodules are private SSH remotes |
| pyenv | brew install pyenv | provides the base interpreters (3.12.8 for the main venv, 3.11.x for VoxCPM2) |
| uv | brew install uv | the venv/package manager of record (.claude/rules/python-runtime.md) |
The parent repo remote (as of 2026-07-06) is git@github.com:sylvanus4/ai-strategy.git
(private, personal remote — not under the ThakiCloud org).
1. Clone + submodule init
git clone git@github.com:sylvanus4/ai-strategy.git ~/thaki/ai-platform-strategy
cd ~/thaki/ai-platform-strategy
Work on main only — no feature branches; sync at session start, push at end
(see .claude/rules/git-workflow-main-only.md).
Submodule pins from .gitmodules (verified 2026-07-06). Mixed HTTPS/SSH remotes; all
ThakiCloud repos are private (HTTPS ones need gh auth login or a credential helper;
SSH ones need your org-authorized key). external/hermes-agent is public.
| Submodule | Remote type | Branch pin | Note |
|---|
ai-suite | https | dev | frontend monorepo |
thaki-ui | https | develop | (not dev) |
ai-platform-webui | https | dev | Go backend despite the name |
thaki-cloud-assistant | https | main | |
praxis | ssh | main | |
thakicloud.github.io | ssh | (no pin — tracks remote HEAD) | blog |
tkai-deploy | ssh | dev | GitOps |
external/hermes-agent | https | main | public (NousResearch) |
ml-platform | ssh | dev | ⚠ code lives on dev; main is stale design docs |
kubeflow-llm-training | ssh | main | |
tkai-multi-cluster | ssh | main | |
You do NOT need all of them. As of 2026-07-06 the reference machine leaves
kubeflow-llm-training, tkai-deploy, and tkai-multi-cluster uninitialized
(git submodule status shows a - prefix). Initialize what you need:
git submodule update --init ai-suite ai-platform-webui thakicloud.github.io praxis external/hermes-agent
git submodule status
Routing depth (which submodule for which question) is owned by
.claude/rules/thaki-platform-repos.md — a router table; read it before diving into any
submodule. Never edit submodule files from the parent repo.
2. Python: the single 3.12.8 venv
Canonical spec: .claude/rules/python-runtime.md (single unified interpreter; all repo
scripts run via .venv/bin/python). Exact rebuild (same commands the rule and
env_bootstrap.sh heal_venv() use):
pyenv install 3.12.8
cd ~/thaki/ai-platform-strategy
uv venv --python "$HOME/.pyenv/versions/3.12.8/bin/python3.12" --prompt ai-platform .venv
echo "3.12.8" > .python-version
VIRTUAL_ENV="$PWD/.venv" uv pip install "headroom-ai[code,relevance]"
.venv/bin/python --version
Notes:
- There is no
requirements.txt; pyproject.toml + uv.lock exist but the core dependency
documented for the venv is headroom-ai[code,relevance] — everything else is installed
on demand per skill (this is by design).
- Install into this venv only via
VIRTUAL_ENV="$PWD/.venv" uv pip install <pkg>. Do not
create ad-hoc venvs inside the repo.
- Never
rm -rf .venv — uv venv overwrites in place, so deletion is unnecessary
(prohibited by the python-runtime rule; see traps table).
3. env_bootstrap: verify → heal
scripts/env_bootstrap.sh reconciles the machine-external state recorded in
env-parity.json (committed at repo root):
bash scripts/env_bootstrap.sh --verify
bash scripts/env_bootstrap.sh --heal
bash scripts/env_bootstrap.sh --export
What each check covers (read from the script, 2026-07-06):
| Check | verify | heal |
|---|
External skill symlinks (.claude/skills/*, .claude/agents/* → repos outside this one, e.g. ~/academic-research-skills) | broken/missing detection | clones the source_repo at the recorded branch, recreates the symlink |
.venv | exists + Python version matches manifest (3.12.8) | rebuilds via uv (same commands as §2) + installs headroom-ai |
| Required CLIs | reports missing (never auto-installs) | reports only — install manually per §4 |
gws under launchd PATH | resolvable with launchd's minimal PATH? | symlinks the real gws into ~/.local/bin/gws |
env-parity.json fields: python.version, external_symlinks[]
(link/target/source_repo/clone_path/branch), required_clis[]. --export regenerates it
from the current machine and preserves manually filled source_repo hints; it skips the
write when nothing changed (avoids daily no-op commits). Known open item: the webwright
symlink entry carries a _note that its source_repo needs confirming — --heal will
warn+skip it (status: open).
4. CLI inventory
Authoritative list = required_clis in env-parity.json:
gh gws hf rtk uv rsync jq node python3 docker. Install hints:
| CLI | Install | Post-install |
|---|
gh | brew install gh | gh auth login |
gws | npm i -g @googleworkspace/cli (hint from env_bootstrap.sh heal path) | authorize once interactively; then --heal for the launchd symlink |
hf | pip install -U "huggingface_hub[cli]" [unverified exact install source — verify with: head -1 $(which hf)] | hf auth login if pushing |
rtk | present at /opt/homebrew/bin/rtk (v0.34.1) [unverified install channel — verify with: brew list rtk] | optional; commands fall back to native if absent (.claude/rules/rtk-token-optimization.md) |
uv | brew install uv | — |
rsync, jq, node | rsync ships with macOS; brew install jq node | node also arrives via nvm on some machines |
docker | Docker Desktop | only needed for container work |
claude (not in the manifest but load-bearing) | official Claude Code installer → ~/.local/bin/claude | see §5 for headless auth |
Re-check any time with bash scripts/env_bootstrap.sh --verify (CLI section reports
missing ones).
5. Headless auth (unmanned claude -p runs)
Canonical spec: .claude/rules/scheduled-skill-model-escalation.md §"헤드리스 인증 전제"
— headless runs must use the subscription OAuth token, never a metered API key.
- In a real interactive terminal (NOT inside a Claude session — stdin must be a TTY):
claude setup-token
- Put the token in
~/.config/claude-code/headless.env (machine-local, OUTSIDE the repo),
containing export CLAUDE_CODE_OAUTH_TOKEN=..., and lock it down:
chmod 600 ~/.config/claude-code/headless.env
(Verified on the reference machine: file exists, mode -rw-------.)
- Verify:
env -u ANTHROPIC_API_KEY claude -p "AUTH_OK" --model sonnet </dev/null
Every scheduled runner sources this file. The full hardening pattern additionally
runs a defensive unset ANTHROPIC_API_KEY before invoking claude -p — but as of
2026-07-06 only 13 of 22 scripts/skills/run_*.sh runners carry the unset (verified:
run_twitter_timeline.sh, run_blog_evolve.sh, others); 9 runners source
headless.env WITHOUT it (run_bespin_news.sh, run_daily_pm.sh,
run_gmail_spam_sweep.sh, run_hf_trending.sh, run_morning_surface.sh,
run_sales_crm_morning.sh, run_skill_evolution.sh, run_sprint_archive.sh,
run_selfharness_evolve.sh — enumerate with
grep -L 'unset ANTHROPIC_API_KEY' scripts/skills/run_*.sh). This is an open
hardening gap: those 9 are exposed to API-key poisoning if a stray
ANTHROPIC_API_KEY ever enters their environment. When debugging auth on one of
them, do NOT rule out key poisoning. NEVER add ANTHROPIC_API_KEY to
.claude/settings.local.json env — it forces metered billing or 401s on all claude -p
calls. This file works even when the login keychain is locked (launchd sessions).
6. Isolated media venvs (OPTIONAL — only if TTS/STT pipelines are needed)
These are deliberately separate from .venv (conflicting transformers pins). Never merge
them. Canonical rules: .claude/rules/tts-generation.md, .claude/rules/stt-generation.md.
All models auto-download to ~/.cache/huggingface on first run; brew install ffmpeg first.
| Purpose | Interpreter / venv | Install (exact, from the driver scripts) |
|---|
| VoxCPM2 TTS (video narration, voice clone) | any pyenv 3.11.x (auto-detected by scripts/tts/voxcpm_tts.py; override VOXCPM_PYTHON) | ~/.pyenv/versions/3.11.9/bin/python -m pip install voxcpm==2.0.3 soundfile (3.11.9 is the version present on the reference machine; the script also probes 3.11.12 and any 3.11.*) |
| Qwen3-ASR STT | ~/.venvs/qwen-asr | python3 -m venv --system-site-packages ~/.venvs/qwen-asr && ~/.venvs/qwen-asr/bin/python -m pip install 'qwen-asr==0.0.6' |
| pyannote diarization | ~/.venvs/pyannote | python3 -m venv --system-site-packages ~/.venvs/pyannote && ~/.venvs/pyannote/bin/python -m pip install 'pyannote.audio>=4.0' — gated HF models: accept licenses at hf.co/pyannote/speaker-diarization-community-1 and /speaker-diarization-3.1 with your HF_TOKEN account, or the pipeline 403s |
| Qwen3-TTS (audiobooks, multivoice) | ~/.venvs/qwen3-tts | python3 -m venv --system-site-packages ~/.venvs/qwen3-tts && ~/.venvs/qwen3-tts/bin/python -m pip install qwen-tts==0.1.1 |
--system-site-packages is the trick that reuses the base interpreter's MPS-built torch
instead of reinstalling multi-GB torch per venv. As of 2026-07-06 the reference machine has
~/.venvs/{qwen-asr,qwen3-tts} (plus unrelated mlx-vlm, paddleocr-vl) but NOT
~/.venvs/pyannote — install it only when diarization is actually needed.
Smoke tests:
python3 scripts/tts/voxcpm_tts.py --text "테스트입니다" --out /tmp/t.wav
bash scripts/stt/verify_stt.sh
7. Machine registration + the daily parity loop
- Register the new machine's Slack label in
scripts/machine_registry.json
(hostname with trailing .local stripped → label). Consumer:
scripts/machine_label.py, used by scripts/slack_post_message.py to tag every post so
home/office alerts are distinguishable. Current entries: Hanui-MacBookPro-4 (회사맥),
Hanui-MacBookPro (home). Unknown hosts fall back to 🖥️ <hostname> — so this step is
cosmetic-but-expected, not blocking.
hostname
- The parity loop that keeps two machines converged (verified in the skill files):
- sod-ship (start of day) runs
bash scripts/env_bootstrap.sh --heal — pulls the
other machine's manifest and heals local drift.
- eod-ship Phase 1.8 (end of day) runs
scripts/env_bootstrap.sh --export and
commits the refreshed env-parity.json so the OTHER machine can heal tomorrow.
Launchd installation of these jobs is day-2 territory → aps-run-and-operate.
8. Go backend — REFERENCE ONLY
- Real path:
ai-platform-webui/ai-platform/backend/go/ (inside the submodule). The path
ai-platform/backend/go/ cited in the root CLAUDE.md does not exist at repo root
on this machine (verified 2026-07-06) — treat that CLAUDE.md path as stale.
go.work is gitignored in both the parent repo (.gitignore:116) and the submodule
(ai-platform-webui/.gitignore:102) — it is a per-developer artifact; absence after a
fresh clone is normal.
- Do not develop platform code here. Product development happens in the submodule repos on
their own branches with their own CLAUDE.md conventions
(
.claude/rules/thaki-platform-repos.md).
Known traps (each cost real time at least once)
| Trap | Symptom | Fix / rule |
|---|
| launchd can't see npm globals | headless jobs fail to find gws (2026-06-19 sales-crm calendar miss) | env_bootstrap.sh --heal symlinks the real gws into ~/.local/bin (part of launchd's minimal PATH) |
| Keychain-locked launchd sessions | interactive-auth CLIs fail in scheduled jobs | headless.env token file (§5) works without keychain; prefer file-based tokens for anything scheduled |
Deleting .venv | breaks the unified-interpreter invariant | never delete; uv venv overwrites in place (.claude/rules/python-runtime.md). The rule says the safety gate blocks rm -rf .venv; as of 2026-07-06 pre-tool-safety-gate.sh has no .venv-specific pattern — do not rely on the gate, just don't delete |
gws stdout header pollution | Using keyring backend: keyring line breaks JSON parsing | redirect: gws ... --format json 2>/dev/null > /tmp/out.json then parse the file (.claude/rules/mcp-graceful-degradation.md) |
| Submodule main-vs-dev confusion | "ml-platform has no code" | ml-platform code is on dev; main is stale design docs. Also thaki-ui pins develop, not dev. Check .gitmodules branch=, not assumptions |
ANTHROPIC_API_KEY anywhere in env for claude -p | 401s or silent metered billing on the whole fleet | keep it out of .claude/settings.local.json; runners unset it defensively (§5) |
Stale CLAUDE.md Go path | cd ai-platform/backend/go fails | real path is ai-platform-webui/ai-platform/backend/go/ (§8) |
env-parity.json missing on an exotic checkout | --verify exits 1 immediately | it is committed at repo root; if truly absent run --export on a healthy machine first |
pyproject.toml uv-workspace member ai-platform/backend/python doesn't exist at root | possible uv workspace warnings | candidate cleanup item (status: open); uv venv / uv pip install per §2 work regardless on the reference machine |
Post-setup acceptance checklist
When NOT to use this skill
| You want to… | Use instead |
|---|
| Install/start/stop launchd jobs, understand runner anatomy, signal bus, outputs layout | aps-run-and-operate |
| Change model policy, hooks wiring, plists, contracts.json, add a flag | aps-config-and-flags |
| Figure out why a scheduled job is failing | aps-debugging-playbook |
| Know whether a change is safe to make at all | aps-change-control |
| History of past incidents and dead ends | aps-failure-archaeology |
| Measure fleet health | aps-diagnostics-and-tooling |
Provenance and maintenance
Verified 2026-07-06 against the live repo (read-only):
- Submodule pins/remotes: read
.gitmodules; init state via git submodule status
(3 uninitialized on reference machine). Re-verify: cat .gitmodules && git submodule status.
- Parent remote:
git remote get-url origin.
- venv rebuild commands:
.claude/rules/python-runtime.md + heal_venv() in
scripts/env_bootstrap.sh (they match). Re-verify: grep -n "uv venv" scripts/env_bootstrap.sh .claude/rules/python-runtime.md.
- env_bootstrap modes/behavior + gws launchd symlink + npm install hint: read the full
script. Re-verify:
bash scripts/env_bootstrap.sh --verify.
required_clis + symlink entries: read env-parity.json. Re-verify: jq .required_clis env-parity.json.
- Headless auth: rule text +
grep -rn headless.env scripts/skills/*.sh; file mode checked
with ls -l ~/.config/claude-code/headless.env (0600). Re-verify with the AUTH_OK one-liner.
- Runner
unset ANTHROPIC_API_KEY coverage (13 of 22; 9 lacking, listed in §5): measured
2026-07-06. Re-verify: grep -L 'unset ANTHROPIC_API_KEY' scripts/skills/run_*.sh.
- Media venv install commands: read from
scripts/tts/voxcpm_tts.py (3.11.x auto-detect),
scripts/stt/qwen_asr_stt.py, scripts/stt/diarize.py, scripts/tts/qwen3_tts.py
(each embeds its own install hint). ~/.venvs contents listed (no pyannote yet).
Re-verify: ls ~/.venvs && grep -n "pip install" scripts/tts/qwen3_tts.py scripts/stt/qwen_asr_stt.py scripts/stt/diarize.py.
- Machine registry + consumer:
scripts/machine_registry.json, scripts/machine_label.py.
- sod/eod parity wiring:
grep -n env_bootstrap .claude/skills/sod-ship/SKILL.md .claude/skills/eod-ship/SKILL.md.
- Go path + go.work ignores:
ls -d ai-platform-webui/ai-platform/backend/go and
grep -n go.work .gitignore ai-platform-webui/.gitignore; root ai-platform/ confirmed absent.
- Safety-gate
.venv claim: full read of .claude/hooks/pre-tool-safety-gate.sh (80 lines,
no venv pattern) — documented honestly in the traps table.
Drift-prone items: submodule pins (.gitmodules), required_clis, CLI install channels
for hf/rtk (marked unverified), the webwright _note, pyannote venv presence, and the
pyproject workspace-member path. Re-run the listed one-liners before trusting them after
2026-07.