User Guide · sprint 0.20

How to run everything.

One repo, fourteen pages, four live watchers, zero frameworks. This page is the operator's manual: how to serve the site, use the Twin OS, run the CLIP Interrogator and Refael, drive the chart pipeline and song indexer, run the tests, and understand what the LaunchAgents are doing on this machine.

vanilla HTML/CSS/JS no build step local-first kai-systems house style
01

Quickstart

Everything is static files. Clone, serve the repo root, open a page.

Requirements

Serve the site

git clone https://github.com/kajica2/digital-twin.git
cd digital-twin
node lib/serve.js --port 5180
# open http://127.0.0.1:5180/pages/landing.html

Repo-root serving is the one true convention — it matches GitHub Pages, the launchd dev server, and CI's PR-mode server. If a page 404s, you're serving the wrong root.

Page map

PageWhat it's for
pages/landing.htmlThe public face — three A/B-testable variants (?v=1|2|3).
pages/twin-os/The Twin OS PWA shell — Today / Songs / Twin panels, installable, IndexedDB-backed.
pages/cognitive-twin.htmlThe 4-layer architecture narrative (Scanner → Model → Reasoner → Orchestrator).
pages/how-to.htmlThis page — the operator's manual.
pages/clip-interrogator.htmlCLIP tool page — image→prompt + image analysis (Python backend).
pages/refael-mp4-maker.htmlOffline MP3→MP4 maker with mood-keyword auto-covers.
pages/loopable-video-segmenter.htmlMP4 → N beat-aligned mirror-loopable clips (Python backend).
pages/agent-dashboard.htmlLive view of the persistent agent loop — run rewrites it on pause/exit and every 10 iterations; dashboard --out by hand.
pages/marketplace.htmlMood-frame listing — eight music-video stills, three studio extras, twelve cover studies and ten System Error plates in the house sound-tones, each with its marketplace blurb, plus the Daily Frames section the nightly run fills.
pages/mj-sheets.htmlMidjourney comparison sheet — one brief rendered at twelve camera angles, each tile labelled with the clause that produced it. Built by lib/mj-contact-sheet.js; reference: docs/MJ-CONTACT-SHEET.md.
pages/temple-of-control.htmlThree temperature sliders (heat / breath / glow) driving a meditation visual; MIDI-driven, pluggable on any page via lib/meditate-temperature.js.
pages/floating-toolset.htmlThe floating toolset — a draggable TODAY / NOW PLAYING / CODE / PATTERNS panel; drop-in on any page via lib/floating-toolset.js. Reference: docs/FLOATING-TOOLSET.md.
pages/integrations.htmlConnect external services — enter API keys and cookies for Midjourney, Instagram, YouTube. Styled like Desktop Commander's welcome page.
pages/persona-social-pack.htmlPersona Social Pack — a URL of persona descriptions in, three hashtag tiers + YouTube metadata + four social posts per persona out. Built by lib/persona-social-pack.js; reference: docs/PERSONA-SOCIAL-PACK.md.
02

Twin OS

The actual app. Three panels, local-first, zero backend.

open pages/twin-os/   # or serve the repo root and visit /pages/twin-os/

Installable as a PWA (manifest + service worker: cache-first shell, network-first catalog). Theme toggle persists to localStorage["dt-theme-pref"] with a no-flash bootstrap.

03

CLIP Interrogator

Turn an image into a text prompt, or analyze it into label tables. Python package in lib/clip-interrogator/, uv-managed, device pinned to CPU (deterministic; MPS crashes the label tables).

Setup (once)

npm run clip:setup          # uv sync --project lib/clip-interrogator
npm run clip:check          # import + device + registry, downloads NO weights

Weights download on first real use (~1-2 GB) into your HF cache and the vocab cache at ~/.cache/clip-interrogator.

UI

npm run clip:ui             # gradio app on http://127.0.0.1:7860  (two tabs: Prompt / Analyze)

CLI

npm run clip:interrogate -- path/to/image.jpg --model vit-l --mode best
npm run clip:interrogate -- path/to/image.jpg --model vit-h --mode classic --json
npm run clip:interrogate -- path/to/image.jpg --model vit-l --mode fast --json
Why CPU-only? clip-interrogator 0.6.0's LabelTable only casts vocab embeddings to fp32 on CPU — on MPS every similarity op crashes with a dtype mismatch. CPU keeps it deterministic; it's a calm local utility, not a render farm.
04

Refael MP4 Maker

MP3 → MP4 with mood-keyword auto-covers, at the resolution you choose (HD · 1920×1080 by default, or Regular · 1280×720 from the Resolution dropdown). Single self-contained HTML file, works from file://.

open pages/refael-mp4-maker.html
# or served: http://127.0.0.1:5180/pages/refael-mp4-maker.html
Offline caveat: the footer's "0 network calls" claim is true for all three engines. FFmpeg.wasm now loads the vendored core under /vendor/ffmpeg (installed by bin/setup.sh), so it never reaches a CDN. Documented in docs/REFAEL-MP4-MAKER.md.
05

Chart pipeline

MusicXML in → full chart package out: per-part PDFs, whole-score PDF, Demo MP3, muted backing tracks, multi-track MIDI.

One-shot export

npm run export-all -- /path/to/song.musicxml
# produces: Full_Score.pdf + per-part PDFs (canonical band order) +
# Demo.mp3 + No_Trumpet.mp3 + No_Sax.mp3 + song.mid

Watched inbox (the zero-touch path)

cp new-chart.musicxml chart-inbox/
# the chart-watcher LaunchAgent (live) picks it up within ~2s,
# exports the full package, moves the input to chart-inbox/processed/,
# and logs to logs/chart-watcher.log. Retries once on failure.

Manual run: node lib/chart-watcher.js --once. Poll loop, not fs.watch — 2s is calm and predictable.

06

Song indexer

Walks your audio folders, reads MP3 ID3v2 / WAV / AIFF metadata, writes a searchable catalog for the Songs panel.

Manual rebuild

# point lib/sources.config.json at where audio actually lives, then:
npm run index-songs          # writes data/songs/catalog.json
# absolute paths are stripped from shipped output by default

Watched inbox

cp new-track.mp3 audio-inbox/
# the songs-watcher LaunchAgent (live) runs the indexer automatically,
# logs to logs/songs-watcher.log
Catalog still empty? The default config points at the AGENTS.md-named dirs, which contain no audio. Edit lib/sources.config.json, re-run npm run index-songs. The Songs panel shows the configured roots in its empty state so you know what to fix.
07

Testing

Puppeteer specs for pages, unit tests for the watchers and the agent loop. Every sprint ships green.

cd e2e && npm install
node lib/serve.js --port 5180 &    # repo-root server, one true convention
npm run verify               # landing + twin-os + twin-os-songs
npm run e2e:ct               # 8 cognitive-twin specs (needs Chrome)
npm run e2e:clip             # CLIP page contract (no Python deps)
npm run e2e:refael           # Refael page contract
npm run test:all             # 19 unit suites: watchers, MJ, jazz-solos, agent-loop, toolset
08

LaunchAgents

The twin runs itself via launchd. State as of this writing:

AgentJobState
digital-twin.serverKeeps the repo served on :5173 (node lib/serve.js)live
digital-twin.chart-watcherAuto-exports anything dropped in chart-inbox/live
digital-twin.songs-watcherAuto-rebuilds the catalog from audio-inbox/live
digital-twin.mj-watcherSubmits Midjourney prompts dropped in the MJ inboxlive
digital-twin.bootAt login: auto-update → ensure server → open Twin OS → Apple Notes statusat login
digital-twin.log-rotateDaily 03:00 — rotates logs/*.log, keeps 7 gzipped days03:00 daily
# verify a watcher is actually alive (PID, not just "loaded")
pgrep -fl "chart-watcher|songs-watcher|mj-watcher"

# taps
tail -f logs/chart-watcher.log logs/songs-watcher.log

# teardown / reinstall per-service docs
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.kaidjuric.digital-twin.<service>.plist

Per-service install + verify steps: docs/CHART-WATCHER.md, docs/SONGS-WATCHER.md, docs/MJ-WATCHER.md.

Two checkouts, one architecture. The LaunchAgents run the operational clone at ~/digital-twin — the dev checkout in Documents/GitHub/digital-twin is where code lands and gets pushed. The boot agent runs git pull --ff-only at every login, so the machine self-updates. To push a change live without waiting for login:
cd ~/digital-twin && git pull --ff-only \
  && launchctl kickstart -k gui/$(id -u)/com.kaidjuric.digital-twin.server

If a page 404s on :5173, the operational clone is stale — pull it, don't edit the plists. The watchers all live there too (chart-inbox/, audio-inbox/, prompts-inbox/).

09

Troubleshooting

ProblemFix
Port already in uselsof -ti :5180 | xargs kill -9 (or :5173), then restart the server.
Page 404s locallyYou're serving pages/ instead of the repo root. Serve --directory . — every page lives under /pages/.
Songs panel says "empty"Point lib/sources.config.json at real audio, re-run npm run index-songs.
MuseScore exits non-zero but PDFs/MP3s existKnown macOS Qt quirk — the pipeline validates by file existence + ffprobe, not exit code. Check the artifacts.
CLIP crashes on MPSBy design it pins CPU (fp32 determinism). Don't "fix" it to MPS — see the callout in §03.
fails: No module named 'clip_interrogator_app'Stale uv cache. uv sync --project lib/clip-interrogator --reinstall --no-cache.
e2e says 0 console errors brokenA missing favicon makes Chrome auto-request /favicon.ico (404). Every page declares a data-URI favicon — keep it that way.
Committing for this repoUse the repo author: -c user.name="Kajica Djuric" -c user.email="kai.djuric@gmail.com".

Memory

Project intelligence lives in .kai/ (gitignored, local-only): conventions, ADRs, tech-debt register, prevention rules. Read .kai/memory.yaml first on any hard problem. Never store secrets there.