Workflows #
Type: knowledge
Build, test, verify #
Worktree builds can seed from the primary checkout's Cargo target directory. This keeps Bevy/wgpu and other heavy dependencies warm without sharing local crate artifacts across source trees. Run this once after creating a worktree, before long Cargo commands:
tools/seed-cargo-target.sh
Do not point multiple worktrees at one shared CARGO_TARGET_DIR. Cargo can
reuse stale local-package artifacts across checkouts; this produced a false
green once (the test count matched main, not the edited worktree). The seed
script copies the cache (including dependency incremental/ dirs), removes
misaligned artifacts/fingerprints/incremental trees, and keeps the current
worktree's target/ private so local code rebuilds correctly while third-party
incremental compilation stays warm.
./tools/check.sh rejects external CARGO_TARGET_DIR by default for the same
reason; override only for a deliberately isolated target with
MISALIGNED_ALLOW_EXTERNAL_TARGET=1.
Verification is proportional to impact. The full Rust/Bevy gate is for changes that can affect the executable, not every Markdown, log, process, or reference-art edit. Agents thrashing the machine with concurrent full gates is a process bug; the lock and auto-tiers below exist to stop that. The broader multi-agent architecture (advisory activity, ledgers, package workspace, heartbeats) lives in agent-scale.md and crate-workspace.md.
./tools/check.sh # auto-classify from the task delta
./tools/check.sh --docs # corpus + wiki + env only
./tools/check.sh --lib # fmt, lib tests, terminal clippy, cheap bevy check, agent smoke
./tools/check.sh --frontend # fmt, bevy bin tests, bevy clippy
./tools/check.sh --full # everything (also MISALIGNED_FORCE_RUST_GATE=1)
./tools/check.sh --land # land phase: same as auto (full when unclassifiable)
Corpus / wiki gates (fast engine; same entrypoints as before):
bash tools/wiki_gate.sh
bash tools/corpus_gate.sh
bash tools/test_corpus_engine.sh # fixtures; also run by check.sh
Ledger indexes (after a session log or spec Status change):
tools/ledger_index.sh # regenerate wiki/log/DEVLOG.md + process/specs.md
tools/ledger_index.sh --check # used by ./tools/check.sh docs path
Do not hand-edit those two generated files.
Activity and worktrees (advisory overlap — see agent-scale.md):
tools/worktree-new.sh dark-frame --class frontend --key crates/misaligned-bevy/
tools/claim.sh list
tools/heartbeat.sh start dark-frame --phase implement
./tools/check.sh --land
tools/worktree-done.sh dark-frame # clear activity, prune target/, remove worktree
Specs with structured work-order metadata have a shorter safe doorway:
tools/project-status.py # live work + activity + worktrees + decisions
tools/task.sh start wiki/interface/material-dark-frame.md
tools/task.sh check material-dark-frame
tools/task.sh finish material-dark-frame
The wrapper never commits for you and stops before cleanup on dirty state,
conflicts, a failed gate, a divergent primary checkout, or a rejected push.
Use tools/doctor.sh (--offline to skip Tangled) when setup or evidence
capabilities are unclear.
Helpers create .Codex/worktrees/<task> by default, regardless of which agent
surface invokes them. Set MISALIGNED_WORKTREE_ROOT to the same absolute or
repository-relative root for both creation and cleanup when an operator needs
different storage.
Optional: install sccache and set
RUSTC_WRAPPER=sccache so dependency objects reuse across worktrees while
each worktree keeps a private target/ for local packages.
Auto-classification (linked task worktrees only; the primary stays full when the delta is empty or unclassifiable):
| Touched paths | Mode |
|---|---|
no crates/ / Cargo / tests / benches / examples |
docs |
only crates/misaligned-bevy/** or crates/misaligned-assets/** |
frontend |
crates/misaligned-core/** or crates/misaligned-terminal/** (no Bevy packages) |
lib |
both lib and frontend packages, or workspace Cargo.toml / lock |
full |
check.sh classifies both uncommitted files and commits on the task branch
relative to origin/main, so a docs-only branch stays on the focused path
even after commit/rebase. Mid-loop, prefer the narrowest flag that can catch
your last edit; run one auto/--full gate before land.
One Rust gate at a time. Concurrent agents share
/tmp/misaligned-rust-gate.lock (mkdir lock; steals when the holder pid is
dead). Docs gates never take the lock and run in parallel with each other
(and with the Rust wall when both are needed). Queue by default; set
MISALIGNED_RUST_GATE_WAIT=0 to fail fast instead of waiting.
For non-Rust work, the auto path is enough (--docs). You can still run the
smallest checks by hand:
- docs/spec/process/logs:
git diff --checkandbash tools/wiki_gate.shwhen wiki-facing; run./tools/site-build.shonly when the rendered Starlight surface or sync pipeline matters; - reference art: validate the source format, render it, and inspect the output;
- public site: run the site sync/build relevant to the changed page or style;
- shell/tooling: syntax-check the changed script and run its targeted smoke or fixture test.
Do not run Cargo tests, clippy, or Bevy builds for a minor non-runtime change. A green full gate proves nothing useful about prose or a reference SVG.
Force the local full gate only when you intentionally need it:
MISALIGNED_FORCE_RUST_GATE=1 ./tools/check.sh
# or
./tools/check.sh --full
The individual cargo steps, if you need them outside the gate:
cargo test -p misaligned-core
cargo clippy -p misaligned-core -p misaligned-terminal --all-targets
cargo test -p misaligned-bevy -p misaligned-assets
cargo clippy -p misaligned-bevy -p misaligned-assets --all-targets
cargo fmt
Agent smoke builds the terminal binary once (-p misaligned-terminal) and
runs it three times (same seed twice, alternate seed once).
The definition of done for a functional change: check.sh green, and the
change was seen running (below) — the gate can't judge whether it plays.
Worktree-safe editing #
All repository writes happen in the task worktree. Some patch tools resolve
paths from the primary checkout even when shell commands use a worktree working
directory. Before the first edit, verify the tool's path base; when necessary,
address files through the worktree path explicitly. After every broad patch,
run git status --short in both the task worktree and the primary checkout.
The primary checkout must remain unchanged.
Prefer narrow patches. Whole-file replacement can silently resurrect a stale
version, change executable bits, drop the final newline, or erase concurrent
ledger entries. If replacement is genuinely clearer, compare file mode,
newline state, and git diff --word-diff before staging.
Whole-corpus and authority migrations #
Follow the binding protocol in meta.md. Treat the work as a semantic rebase of every place that teaches, checks, or renders the authority model.
- Record a before-inventory: headings, decision-entry counts, page roles, current/retired authority phrases, link reachability, and rendered routes.
- Define single owners, move current rules, then update instructions, skills, prompts, hooks, local checks, CI, and the renderer in one coherent landing.
- Exclude
Type: logbodies from broad rewrites. Only the explicit structural metadata exception inmeta.mdmay touch old log structure. - Treat even comment/string cleanup under
src/as Rust-impacting work and run the full./tools/check.sh; documentation intent does not make an executable path safe to fast-track. - Run
tools/corpus_gate.sh,tools/wiki_gate.sh,git diff --check, and a site build; compare the before/after inventories. - Rebase immediately before landing. Inspect concurrent changes with
git show <commit> -- <file>and transplant their meaning into the new owner. Never resolve an authority migration by taking a stale whole file.
The doorway is protected mechanically: tools/corpus_gate.sh bounds its size,
forbids binding-page structure, validates every page role and spec header,
resolves every structured Design/dependency target and heading, and verifies
every declared generated-page owner. Local checks and the fast Tangled corpus
workflow call this same script so implementations cannot drift. Both corpus
scripts preflight python3; the corpus workflow explicitly supplies Python,
Bash, coreutils, and grep. A missing audit dependency must make CI red, never
turn an empty scan into a green result.
Tangled CI is split by cost. .tangled/workflows/corpus.yml runs classifier
fixtures, corpus fixtures, the corpus/wiki gates, and generated-index
freshness on every push and pull request. .tangled/workflows/check.yml runs
the Rust/Bevy wall only for Rust-impacting pushes; every pull request and
manual invocation is a conservative full safety gate. Tangled currently has
no schedule trigger or workflow-level path filter, so manual is the honest
full-gate backstop.
Browsing the wiki #
Public site (Starlight). The wiki renders as a Tangled-hosted static
site from the orphan pages branch. Builder lives on main under
site/ (Astro + Starlight, clinical-gore theme). Source of truth is the
wiki/ corpus; site/scripts/sync-wiki.mjs copies it and the root doorway into
Starlight's content tree and builds the sidebar from wiki/SUMMARY.md.
pnpm --dir site dev # sync wiki + local preview (base=/)
./tools/site-build.sh # sync + build → site/dist/ (base=/misaligned)
./tools/site-deploy.sh # build, then force-push orphan pages branch
The public site opens on a splash homepage (site/src/pages/index.astro)
whose copy is drawn from the premise, visual-identity, and site pages. It is a game-first
one-pager: the hero carries the MISALIGNED wordmark, the body sells the
actual loop (cover, concealment, intel, people, machine growth, rollback),
and the primary CTA is the design corpus. Visual identity is allowed to carry
the mood, but the body should not spend a full section explaining palette
law unless the page is explicitly being redesigned as an art/process page.
The wiki is the design corpus; the legacy /constitution/ route contains
only the root doorway. Public claims are part of the corpus contract: amend
the owning law/spec page if splash claims change.
Per wiki/process/living-spec.md's No unsourced surface law, every public site page and
asset needs provenance. wiki/interface/site.md is the binding visual spec;
site/README.md records the current asset/page mapping. If a homepage
image, standalone page, script, model, or generated output no longer has a
source clause, delete it or write the clause first.
Tangled Sites config (Settings → Sites on the repo) — required;
pushing pages alone does not publish. Until this is saved, Tangled's
edge returns plain Not Found at cameron.tngl.io/misaligned/ even
though origin/pages has a valid index.html:
- Branch:
pages - Deploy directory:
/(the orphan branch root isindex.html) - Site type: sub-path (served as
<your-domain>/misaligned)
After Save, check Recent Deploys on that settings page — a successful
deploy copies the branch into Tangled's object store. Re-save or push
pages again if the deploy list is empty.
site/astro.config.mjs sets base: '/misaligned' to match Tangled's
sub-path hosting (the edge strips /misaligned before looking up files;
Starlight still needs the base so asset URLs are correct). Local
pnpm --dir site dev overrides with SITE_BASE=/.
.tangled/workflows/site.yml runs ./tools/site-build.sh on every push
to main so the builder stays green. Publishing pages from CI is
deferred until a write credential exists under Settings → Secrets
(Spindle cannot push otherwise). Until then, run ./tools/site-deploy.sh
after wiki-facing landings when the public site should refresh. Optional
later step: add a deploy job that uses the secret to force-push pages
the same way site-deploy.sh does.
Starlight is the only documentation renderer. mdBook, book.toml, and the
renderer-only wiki/DESIGN.md symlink were retired 2026-07-09. The Starlight
sync copies root DESIGN.md explicitly to the legacy /constitution/ doorway and parses
wiki/SUMMARY.md for navigation. The Rust/check pipeline enforces structural
wiki correctness through tools/wiki_gate.sh; .tangled/workflows/site.yml
owns the rendered-site build.
Running the game #
cargo run -p misaligned-terminal --release # terminal frontend
cargo run -p misaligned-terminal --release -- --agent --seed 1
cargo run -p misaligned-bevy --release # Bevy frontend
cargo run -p misaligned-assets # procedural asset tester
- The Bevy frontend must be run via
cargo run(or withBEVY_ASSET_ROOTpointing at the repo root): Bevy resolvesassets/fromBEVY_ASSET_ROOT, thenCARGO_MANIFEST_DIR, then the executable's directory. Running the binary directly fromtarget/silently loads zero assets. - The asset tester (
misaligned-assets) is a Bevy-only orbit viewer for flat-material procedural meshes — see art/asset-tester.md. It does not load the sim.
Headless smoke tests (they catch real bugs) #
Checked-in agent scenarios are the repeatable semantic evidence path:
tools/scenario.py --check-definitions
tools/scenario.py scenarios/opening-senses.agent --output /tmp/opening-evidence
The runner executes the same seed twice, checks EXPECT / REJECT
directives, and can retain commands, transcript, SHA-256, and JSON evidence.
Use --with-bevy when the scenario declares a BEVY_SHOT.
Bevy screenshot evidence does not require a watched window:
tools/bevy-headless.sh dark --output /tmp/misaligned-dark.png
MISALIGNED_BEVY_SMOKE=1 ./tools/check.sh --frontend
The helper requires process exit 0, fog audit OK, and a valid PNG. The env
flag keeps this optional in ordinary gates while making it one switch for a
frontend land. When enabled through check.sh, the harness builds the real
Bevy binary itself: frontend tests and clippy do not guarantee that a fresh
worktree already has target/debug/misaligned-bevy.
Agent mode is the standard observed-run gate for terminal playability. It is
command-clocked, deterministic under --seed, and produces plain-text frames
with greppable terminators:
printf 'salvage\nwait 1\npeople\nreview janitor\nhelp\nquit\n' | \
cargo run --quiet --bin misaligned -- --agent --seed 1
./tools/check.sh (lib and full modes) builds the terminal binary once, runs
that script twice with the same seed (stdout must be byte-identical), once with
a different seed (stdout must differ), and asserts that the response contains no
ANSI escape bytes plus the required frame/help substrings. This replaces pty
choreography as the default "seen running" evidence for terminal behavior.
Human terminal chrome still needs pty coverage when raw-mode rendering, alternate-screen behavior, or size handling changes:
(printf '\n'; sleep 4; printf 'm'; sleep 1; printf '\r'; sleep 2; printf '\x1b'; sleep 1; printf 'q') | \
script -q /dev/null sh -c 'stty rows 40 cols 140; ./target/debug/misaligned'
The stty matters: script's pty is otherwise 0x0 (the game guards against
< 60x20 now, but you want a real render).
An interactive Bevy launch remains useful only when window focus/input itself changed; it is no longer the ordinary land-evidence path:
BEVY_ASSET_ROOT=$PWD ./target/debug/misaligned-bevy > /tmp/bevy.log 2>&1 &
sleep 8 && kill %1; grep -iE "panic|ERROR" /tmp/bevy.log
Git conventions #
-
Use a task-named git worktree for every agent session; do not edit the primary checkout directly. Clean up the worktree when the work is committed and pushed/merged or explicitly handed off.
-
Cameron has standing permission for agents to commit coherent completed work in this repository. Be aggressive about committing; pause only for pending product/design questions, failing checks, unresolved conflicts, or explicit review gates.
-
Commits: plain descriptive messages, with body bullets for multi-part changes. AI attribution and its standard provenance marks are allowed; otherwise keep commit prose free of decorative emoji. Stage files explicitly —
git add -Ais not permitted. -
Sources merge; projections regenerate. Uniquely named session logs and current dated decision volumes are append-only history: resolve conflicts by preserving both sources.
wiki/log/DEVLOG.mdandwiki/process/specs.mddeclareGenerated: tools/ledger_index.sh; never union-edit them. After sources/specs are reconciled, run the generator and take its complete output. -
Spec-driven rule: functional change commits amend the owning
Type: laworType: specpage and update affected knowledge. -
Remote:
originis a Tangled knot (tangled.org, SSH). Land changes by merging tomaindirectly after the appropriate scoped verification passes (full./tools/check.shfor Rust-impacting work; focused checks for docs/art/process-only work). (The "PRs are the norm" rule was removed 2026-07-07 — CLI-created PRs write to the PDS but don't render on tangled.org, so the process could not be followed. See the dated volumes indexed bywiki/log/decisions.md.) From your worktree:git fetch origin && git rebase origin/main git checkout main && git merge <branch> && git push origin mainOr, if the worktree branch is a fast-forward of main:
git fetch origin && git rebase origin/main git push origin HEAD:main -
Identity map: this machine has two SSH keys that authenticate to Tangled:
~/.ssh/id_ed25519(cameron@pfiffer.org) → Cameron's account (did:plc:gfrmhdmjvxn2sjedzboeudef, @cameron.stream). This key has push access to the misaligned repo. The misaligned repo is configured to use this key viagit config core.sshCommand(repo-local).~/.ssh/tangled_ed25519(void.comind.network@letta) →did:plc:mxzuau6m53jtdsbqe6f4laov(a different identity). This is the default key in~/.ssh/configfortangled.org, but it does NOT have push access to the misaligned repo.- The misaligned repo's own DID is
did:plc:t53fxjacrmulx3e5d3sbdfui(the repo identifier, not Cameron's account). access denied: user not allowedmeans the wrong SSH key is being used. Fix:git config core.sshCommand "ssh -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes"(already set on this repo). Verify withssh -i ~/.ssh/id_ed25519 -T git@tangled.org.
-
.letta/settings.local.jsonis gitignored (machine-local session state);.letta/.lettaignoreis tracked.
Documentation flow per session #
- Owning
Type: law/Type: specamendment (if functionality changed). Type: knowledgewiki page updates (if reality changed).wiki/log/YYYY-MM-DD-topic.mdsession writeup;wiki/log/DEVLOG.mdgets the short ledger line.