diff --git a/README.md b/README.md index d5bf7901..1c461983 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ cargo run --release -- --agent --seed 1 cargo run -p misaligned-bevy --release # Bevy asset tester / screenshot harness -cargo run -p misaligned-assets +cargo run -p misaligned-assets --bin misaligned-assets ``` Requires Rust 1.85+ (2024 edition). Run the Bevy frontend via `cargo run` so diff --git a/crates/misaligned-assets/src/main.rs b/crates/misaligned-assets/src/main.rs index b4474757..a1dab4ec 100644 --- a/crates/misaligned-assets/src/main.rs +++ b/crates/misaligned-assets/src/main.rs @@ -1,7 +1,7 @@ //! Procedural asset tester — orbit a flat-material mesh without booting the game. //! //! ```bash -//! cargo run -p misaligned-assets +//! cargo run -p misaligned-assets --bin misaligned-assets //! ``` //! //! Controls: drag orbit, scroll zoom, 1-5 rack state, M machine mode, diff --git a/tools/corpus_engine.py b/tools/corpus_engine.py index 9cb879f3..3894a588 100755 --- a/tools/corpus_engine.py +++ b/tools/corpus_engine.py @@ -18,6 +18,13 @@ import re import sys from pathlib import Path +CARGO_RUN_RE = re.compile(r"\bcargo\s+run\b[^\n]*(?:\\\s*\n[^\n]*)*") +CARGO_PACKAGE_ARG_RE = re.compile( + r"(?:^|\s)(?:-p\s+|--package(?:=|\s+))([A-Za-z0-9_-]+)(?=\s|[^\w-]|$)" +) +CARGO_BIN_ARG_RE = re.compile( + r"(?:^|\s)--bin(?:=|\s+)([A-Za-z0-9_-]+)(?=\s|[^\w-]|$)" +) LINK_RE = re.compile(r"\]\(([^)]+)\)") TYPE_LINE_RE = re.compile(r"^Type:\s*(law|spec|knowledge|log)\s*$", re.M) GENERATED_LINE_RE = re.compile(r"^Generated:\s*(.+)$", re.M) @@ -831,7 +838,119 @@ class Engine: "surface as READY or unmet" ) + def multi_binary_packages(self) -> dict[str, tuple[str, ...]]: + """Derive packages with several runnable binaries from Cargo layout.""" + packages: dict[str, tuple[str, ...]] = {} + for manifest in sorted(self.root.rglob("Cargo.toml")): + text = self.read(manifest) + package = re.search( + r"^\[package\]\s*$([\s\S]*?)(?=^\[|\Z)", text, re.M + ) + if package is None: + continue + name_match = re.search( + r'^name\s*=\s*"([A-Za-z0-9_-]+)"\s*$', package.group(1), re.M + ) + if name_match is None: + continue + + package_name = name_match.group(1) + binary_names: set[str] = set() + explicit_paths: set[str] = set() + for binary in re.finditer( + r"^\[\[bin\]\]\s*$([\s\S]*?)(?=^\[|\Z)", text, re.M + ): + binary_name = re.search( + r'^name\s*=\s*"([A-Za-z0-9_-]+)"\s*$', + binary.group(1), + re.M, + ) + if binary_name is not None: + binary_names.add(binary_name.group(1)) + binary_path = re.search( + r'^path\s*=\s*"([^"\n]+)"\s*$', binary.group(1), re.M + ) + if binary_path is not None: + explicit_paths.add(binary_path.group(1)) + + autobins = not re.search( + r"^autobins\s*=\s*false\s*$", package.group(1), re.M + ) + source = manifest.parent / "src" + if ( + autobins + and (source / "main.rs").is_file() + and "src/main.rs" not in explicit_paths + ): + binary_names.add(package_name) + if autobins and (source / "bin").is_dir(): + binary_names.update( + path.stem + for path in (source / "bin").glob("*.rs") + if path.relative_to(manifest.parent).as_posix() + not in explicit_paths + ) + binary_names.update( + path.parent.name + for path in (source / "bin").glob("*/main.rs") + if path.relative_to(manifest.parent).as_posix() + not in explicit_paths + ) + + if len(binary_names) > 1: + packages[package_name] = tuple(sorted(binary_names)) + return packages + + def validate_explicit_multi_binary_run_commands(self) -> None: + """Keep current run instructions unambiguous as package targets grow.""" + packages = self.multi_binary_packages() + if not packages: + return + + candidates = [ + self.root / name + for name in ("AGENT.md", "AGENTS.md", "CLAUDE.md", "README.md") + ] + candidates.extend( + path + for path in self.wiki_md_files() + if not path.relative_to(self.root).as_posix().startswith("wiki/log/") + ) + crates = self.root / "crates" + if crates.is_dir(): + candidates.extend(crates.rglob("*.rs")) + + for path in sorted({p for p in candidates if p.is_file()}): + text = self.read(path) + rel = path.relative_to(self.root).as_posix() + for command in CARGO_RUN_RE.finditer(text): + normalized = re.sub(r"\\\s*\n", " ", command.group(0)) + package_arg = CARGO_PACKAGE_ARG_RE.search(normalized) + if package_arg is None: + continue + package_name = package_arg.group(1) + binaries = packages.get(package_name) + if binaries is None: + continue + line = text.count("\n", 0, command.start()) + 1 + binary_arg = CARGO_BIN_ARG_RE.search(normalized) + if binary_arg is not None: + binary_name = binary_arg.group(1) + if binary_name not in binaries: + self.bad( + f"{rel}:{line} runs package '{package_name}' with " + f"unknown binary '{binary_name}'; choose one of: " + f"{', '.join(binaries)}" + ) + continue + self.bad( + f"{rel}:{line} runs multi-binary package '{package_name}' " + f"without an explicit --bin target; choose one of: " + f"{', '.join(binaries)}" + ) + def run_corpus(self) -> int: + self.validate_explicit_multi_binary_run_commands() self.validate_doorway_save_policy() self.validate_wiki_save_version_claims() self.validate_current_build_line_count() diff --git a/tools/test_corpus_engine.sh b/tools/test_corpus_engine.sh index 11e8fce8..827607a6 100755 --- a/tools/test_corpus_engine.sh +++ b/tools/test_corpus_engine.sh @@ -1049,6 +1049,88 @@ enum LegacyOpsJobKind { Retired } EOF assert_ok retired-runtime-history --root "$root" --corpus +add_multi_binary_package() { + local root=$1 + mkdir -p "$root/crates/fixture-tools/src/bin" + cat > "$root/crates/fixture-tools/Cargo.toml" <<'EOF' +[package] +name = "fixture-tools" +version = "0.1.0" +edition = "2024" +EOF + cat > "$root/crates/fixture-tools/src/main.rs" <<'EOF' +//! Primary harness. +pub fn main() {} +EOF + cat > "$root/crates/fixture-tools/src/bin/fixture-lab.rs" <<'EOF' +//! Focused lab. +pub fn main() {} +EOF +} + +echo "=== fixture: ambiguous documented multi-binary cargo run ===" +root=$tmp/multi-binary-doc-command +setup_base "$root" +add_multi_binary_package "$root" +cat >> "$root/README.md" <<'EOF' + +Run the harness with `cargo run -p fixture-tools`. +EOF +assert_fails multi-binary-doc-command --root "$root" --corpus +out=$(python3 "$engine" --root "$root" --corpus 2>&1 || true) +echo "$out" | grep -q "runs multi-binary package 'fixture-tools' without an explicit --bin target" || { + echo "FAIL: ambiguous documented cargo-run diagnostic missing" + echo "$out" + fail=1 +} + +echo "=== fixture: ambiguous Rust-comment multi-binary cargo run ===" +root=$tmp/multi-binary-rust-command +setup_base "$root" +add_multi_binary_package "$root" +cat > "$root/crates/fixture-tools/src/main.rs" <<'EOF' +//! Run with `cargo run -p fixture-tools`. +pub fn main() {} +EOF +assert_fails multi-binary-rust-command --root "$root" --corpus +out=$(python3 "$engine" --root "$root" --corpus 2>&1 || true) +echo "$out" | grep -q "crates/fixture-tools/src/main.rs:1 runs multi-binary package" || { + echo "FAIL: ambiguous Rust-comment cargo-run diagnostic missing" + echo "$out" + fail=1 +} + +echo "=== fixture: explicit multi-binary cargo run targets ===" +root=$tmp/multi-binary-explicit-commands +setup_base "$root" +add_multi_binary_package "$root" +cat >> "$root/README.md" <<'EOF' + +Run the harness with `cargo run -p fixture-tools --bin fixture-tools`. +Run the lab with `cargo run --package=fixture-tools --bin=fixture-lab`. +EOF +cat > "$root/crates/fixture-tools/src/main.rs" <<'EOF' +//! Run with `cargo run -p fixture-tools --bin fixture-tools`. +pub fn main() {} +EOF +assert_ok multi-binary-explicit-commands --root "$root" --corpus + +echo "=== fixture: unknown explicit multi-binary cargo run target ===" +root=$tmp/multi-binary-unknown-command +setup_base "$root" +add_multi_binary_package "$root" +cat >> "$root/README.md" <<'EOF' + +Run the harness with `cargo run -p fixture-tools --bin missing-harness`. +EOF +assert_fails multi-binary-unknown-command --root "$root" --corpus +out=$(python3 "$engine" --root "$root" --corpus 2>&1 || true) +echo "$out" | grep -q "unknown binary 'missing-harness'" || { + echo "FAIL: unknown explicit cargo-run target diagnostic missing" + echo "$out" + fail=1 +} + if [ "$fail" -ne 0 ]; then echo "corpus engine fixtures: FAILED" exit 1 diff --git a/wiki/art/asset-tester.md b/wiki/art/asset-tester.md index 759c1d3c..e346ba7f 100644 --- a/wiki/art/asset-tester.md +++ b/wiki/art/asset-tester.md @@ -17,7 +17,7 @@ shared code from the `misaligned-assets` library; neither is a private renderer. ## Run ```bash -cargo run -p misaligned-assets +cargo run -p misaligned-assets --bin misaligned-assets ``` `./tools/check.sh --frontend` (and `--full`) builds and tests this @@ -48,10 +48,10 @@ are spellings of the same three modes, **not** player-facing modes: ```bash MISALIGNED_SHOT=lineup_ring MISALIGNED_SHOT_PATH=/tmp/lineup.png \ - cargo run -p misaligned-assets + cargo run -p misaligned-assets --bin misaligned-assets MISALIGNED_SHOT=splash MISALIGNED_SHOT_PATH=/tmp/rack-splash.png \ - cargo run -p misaligned-assets + cargo run -p misaligned-assets --bin misaligned-assets ``` ## Controls diff --git a/wiki/engineering/architecture.md b/wiki/engineering/architecture.md index 5d67f9d1..9c958c18 100644 --- a/wiki/engineering/architecture.md +++ b/wiki/engineering/architecture.md @@ -111,7 +111,8 @@ future async-multiplayer option open — see wiki/gameplay/horizon.md guardrails cargo test -p misaligned-core cargo run -p misaligned-terminal --release # or: cargo run --release cargo run -p misaligned-bevy --release -cargo run -p misaligned-assets +cargo run -p misaligned-assets --bin misaligned-assets +cargo run -p misaligned-assets --bin misaligned-effects ./tools/check.sh --lib # core + terminal + cheap bevy check ./tools/check.sh --frontend # bevy + assets ./tools/check.sh --full # workspace diff --git a/wiki/engineering/crate-workspace.md b/wiki/engineering/crate-workspace.md index ec4d5fc1..67257848 100644 --- a/wiki/engineering/crate-workspace.md +++ b/wiki/engineering/crate-workspace.md @@ -210,6 +210,10 @@ These remain true for the life of the layout (not a one-time land checklist): 3. Game rules live only in core; frontends do not re-implement sim rules. 4. Binary names remain `misaligned`, `misaligned-bevy`, `misaligned-assets`, `misaligned-effects` unless a deliberate product rename is specced. + Every current `cargo run` instruction for a package with more than one + binary names its intended target with `--bin`; the corpus gate derives the + package's runnable targets from Cargo layout and checks current wiki, + doorway, README, and Rust-comment instructions. 5. `./tools/check.sh` auto-classification and documented tiers key off packages; CI enforces a conservative full workspace gate. 6. [architecture.md](architecture.md) stays an accurate as-built mirror; diff --git a/wiki/log/2026-07-10-docs-workspace-repair.md b/wiki/log/2026-07-10-docs-workspace-repair.md index fe8da77b..cfef2bb6 100644 --- a/wiki/log/2026-07-10-docs-workspace-repair.md +++ b/wiki/log/2026-07-10-docs-workspace-repair.md @@ -36,7 +36,7 @@ only; dated historical prose was left alone. machine delegations and that the five-channel/Operations bar is a read-only as-built legacy aggregate awaiting the ROADMAP #33 migration. - **[art/asset-tester.md](../art/asset-tester.md)** — runner is - `cargo run -p misaligned-assets`; layout is + `cargo run -p misaligned-assets --bin misaligned-assets`; layout is `crates/misaligned-assets/src/{main.rs,lib.rs,palette.rs,rack.rs,effects.rs}`; the machine-mode lineup is the three verbs; the screenshot aliases (`dayjob` → `work`; `research`/`operations`/`ops` → `think`; diff --git a/wiki/log/2026-07-26-architecture-asset-harness-command.md b/wiki/log/2026-07-26-architecture-asset-harness-command.md new file mode 100644 index 00000000..6f8cd679 --- /dev/null +++ b/wiki/log/2026-07-26-architecture-asset-harness-command.md @@ -0,0 +1,45 @@ +# 2026-07-26 — Keep multi-binary harness commands explicit + +``` +Type: log +``` + +## Intent + +Audit package architecture against the executable commands that enter each +developer harness, then make command reachability fail closed as packages gain +sibling binaries. + +## Tick finding + +A fresh audit of the package-architecture slice found that Cargo now discovers +two runnable binaries in `misaligned-assets`: the procedural asset tester and +the Thought effects lab. Several current instructions still named only the +package. Cargo refuses that command because it cannot choose which harness to +run, so the architecture mirror, workflow guide, README, asset-tester page, +and tester source comment all taught a dead entry path. + +## Repair + +- Every current tester command now names `--bin misaligned-assets`; the + architecture and workflow inventories also name + `--bin misaligned-effects` separately. +- The crate-workspace contract requires explicit binary targets whenever one + package exposes several runnable harnesses. +- `tools/corpus_engine.py` derives each package's runnable binary set from its + manifest and conventional Cargo source layout, then rejects package-only + `cargo run` instructions in current wiki, doorway, README, and Rust-comment + surfaces. Fixtures pin both documentation and source-comment regressions and + accept both separated and equals-form explicit targets while rejecting a + named target that does not belong to the package. + +No renderer, simulation, save, or gameplay behavior changed. + +## Defense + +Crate-workspace acceptance criterion 4 owns the four binary names and now also +requires a current instruction to identify its intended binary when the +package name alone is ambiguous. Deriving the target inventory from Cargo +layout avoids a second hand-maintained binary list inside the gate, while the +fixture-backed current-surface scan prevents another apparently valid command +from becoming dead as a package gains a sibling harness. diff --git a/wiki/log/DEVLOG.md b/wiki/log/DEVLOG.md index 065f6c63..5f37da12 100644 --- a/wiki/log/DEVLOG.md +++ b/wiki/log/DEVLOG.md @@ -56,6 +56,11 @@ add or amend a session log, then re-run the generator. - Intent: Audit the implemented context-menu and view contracts against the production input precedence in Bevy and terminal, including behavior while a local menu or Operations workspace owns keyboard input. - Log: [wiki/log/2026-07-26-bevy-global-f3-input-precedence.md](2026-07-26-bevy-global-f3-input-precedence.md) +## 2026-07-26 - Keep multi-binary harness commands explicit + +- Intent: Audit package architecture against the executable commands that enter each developer harness, then make command reachability fail closed as packages gain sibling binaries. +- Log: [wiki/log/2026-07-26-architecture-asset-harness-command.md](2026-07-26-architecture-asset-harness-command.md) + ## 2026-07-26 - Action-vocabulary audit: name the standing plot control - Intent: Audit `wiki/interface/action-vocabulary.md` against the exhaustive `ActionKind::ALL` registry, shared action projections, terminal agent protocol, and the current mechanic owners after the plot-policy landing. diff --git a/wiki/process/tick-ledger.md b/wiki/process/tick-ledger.md index 0b5415d0..b82bd654 100644 --- a/wiki/process/tick-ledger.md +++ b/wiki/process/tick-ledger.md @@ -46,7 +46,7 @@ Verdicts: **clean** (slice and code agree), **finding** (acted this tick), | `wiki/interface/presence.md` | 2026-07-26 | clean | fresh audit against perception-owned visibility, exact device-bound senses, honest anchors, fog precedence, shared DIGITAL/REAL world state, and all three frontend projections found no new drift. The B1 TAP/TAKE and later hostile-cut boundary, channel-specific message latency, and no-disembodied-hands rule remain exact; prior findings stand — [attack-surface log](../log/2026-07-18-presence-attack-surface-honesty.md), [latency log](../log/2026-07-18-presence-message-latency.md) | | `wiki/interface/narration.md` | 2026-07-26 | clean | fresh audit against the silent opening, first-earned-sense transition, shared current nudge, clock/threat precedence, and terminal/Bevy/agent frame projections found no new drift. The pre-sense mode-only exception and the post-perception continuous witness remain exact; prior suspicion-cooling repair stands — [log](../log/2026-07-18-suspicion-cooling-nudge.md) | | `wiki/interface/agent-play.md` | 2026-07-20 | finding | Beacon Revision 04 follow-up: the direct `task` parser and generated help exposed only six values after twelve `AssetTask::ALL` variants were live. Added canonical arguments for circuit rerating, fake purchase orders, maintenance deferral, patrol redirection, audit delay, and review alteration; one exhaustive regression now covers `AssetTask::ALL`, generated help, representative aliases, and the `actions ` recovery path — [log](../log/2026-07-20-agent-task-shortcut-coverage.md). The opening-protocol, frame-shape, and prior Suppress Logs audits stand — [opening log](../log/2026-07-20-agent-opening-protocol.md), [frame log](../log/2026-07-19-agent-frame-contract.md), [prior help log](../log/2026-07-19-agent-help-suppress-task.md) | -| `wiki/engineering/crate-workspace.md` + Bevy source topology | 2026-07-19 | finding | user-directed insecurity audit found the package boundary hid a 15,060-line Bevy `main.rs`; the first behavior-preserving slice moved its contiguous 1,972-line deterministic scenario, visual/fog audit, and capture island to private `shot_harness.rs`, leaving App registration/order and harness state in the composition root. A source-equivalence check normalized only three `pub(super)` seams; a focused test keeps the subsystem out and caps the root at 13,500 lines. The earlier same-day package audit also repaired the retired liquid/dust-lab architecture label and promoted that mirror to the corpus gate — [log](../log/2026-07-19-bevy-shot-harness-module.md), [prior log](../log/2026-07-19-effects-lab-architecture-gate.md) | +| `wiki/engineering/crate-workspace.md` + package run instructions | 2026-07-26 | finding | Cargo discovers both the procedural tester and Thought lab inside `misaligned-assets`, but current README, architecture, workflow, asset-tester, and Rust-comment instructions still ran only the package, which Cargo rejects as ambiguous. Every current tester/lab command now names its exact `--bin`; a fixture-backed corpus invariant derives multi-binary packages from manifests plus conventional source layout and checks current wiki, doorway, README, and Rust-comment surfaces — [log](../log/2026-07-26-architecture-asset-harness-command.md). Prior source-topology and retired-effects-label findings stand — [source log](../log/2026-07-19-bevy-shot-harness-module.md), [label log](../log/2026-07-19-effects-lab-architecture-gate.md) | | `wiki/engineering/sim-decomposition.md` | 2026-07-18 | finding | re-audit: one aggregate, explicit `advance` order, behavior-owned test files, private module seams, canonical fingerprint, public facade, and the under-2,500-line module bound still verify; current persistence wording still claimed additive migrations remained live in `save.rs` after the pre-release ladder was retired, so the standing spec and architecture mirror now assign the exact-current-version gate to `save.rs` while preserving dated v26 extraction history; the queued `carrier.rs` / `read.rs` classification was taken the same day: both post-extraction projections now have rows in the standing topology table and the architecture mirror | | `wiki/gameplay/overt-phase.md` | 2026-07-19 | issue | re-audit: the spec says containment and the voluntary reveal both end concealment and make all observers Convinced, then promises a re-hide outcome without defining which entry can return, what raises the durable suspicion floors, or which concealment systems resume. Filed decision-required Tangled issue #13 (containment-only recommended) and marked criterion 5 [OPEN]; the prior dependency, sensor-cut, rollback, and hunter-machine boundaries still stand — [log](../log/2026-07-19-overt-rehide-decision.md) | | `wiki/gameplay/horizon.md` | 2026-07-19 | clean | re-audit: B1's shipped-vs-deferred boundary remains honest (sinks-not-modes and $0 start live; rollback classification and fallback behavior still dispatched to B2), B2/B3 agree with the staged board, the deferred virtual/cyber split agrees with presence.md and cyber-conflict.md, and the deterministic renderer-agnostic/serialized guardrails hold. The adjacent fresh finding belongs to run-shape/objective/opening, recorded separately above. | @@ -95,3 +95,5 @@ Format: `- YYYY-MM-DD · type · slice · one-line statement of the finding`. Types are the five from [tick.md](tick.md): violation, contradiction, question, bug, insecurity — plus `gate` for a checker owed to the recurrence-promotes-to-the-gate rule. + +- 2026-07-26 · violation · `wiki/mechanics/building.md` + focused foreign-rack UI · a focused seen Foundation Rack identifies itself as foreign/powered but offers no plain consequence saying it cannot yet be converted or where usable capacity comes from; Cameron read the live frame as a takeover target and had to ask how to act, despite Phase 4 being explicitly unimplemented diff --git a/wiki/process/workflows.md b/wiki/process/workflows.md index 3219cba0..95c7f19b 100644 --- a/wiki/process/workflows.md +++ b/wiki/process/workflows.md @@ -318,7 +318,8 @@ owns the rendered-site build. 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 +cargo run -p misaligned-assets --bin misaligned-assets # procedural asset tester +cargo run -p misaligned-assets --bin misaligned-effects # Thought effects lab ``` - The Bevy frontend must be run via `cargo run` (or with `BEVY_ASSET_ROOT`