ARAIL: DaC World-skill mount (Sprint 2) + world-forge-doc (baseline; supersedes #98)#99
Merged
Merged
Conversation
ARAIL now pulls versioned aeroLLM wheels from the self-hosted index (pypi.qukaizen.com) for non-dev installs, while still building from the local sibling repo when it's present (active dev). Follows the CI/CD plan. - build-aerollm.sh: `auto` (sibling→cargo, else→pip from index), `update` (pip --upgrade), and version reporting in `status`. Wheels are macOS-arm64-only; off-Mac pip reports "no matching distribution" gracefully. - arailctl: `deep update` verb (rebuild | update | status). - setup.sh: installs via `build-aerollm.sh auto`; reads aerollm pin + index URL from pyproject [tool.arail.package-sources]. - pyproject: aerollm = "aerollm-api>=0.1,<0.2" + aerollm_index. - backends.py: AeroLLMBackend captures aerollm_api.__version__ for status/health. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root cause: buildBody() in chat.html set request.backend via pickBackend(),
which returns undefined for activeSource='my_machine'. With runtime='aerollm'
but backend=undefined, the server's deep-path gate (wants_deep =
optional_backend_name in _OPTIONAL_CHAT_BACKEND_CONFIG) never fired, and the
runtime-override path only honored ('ollama','mlx-openai'). Box B prompts
silently dropped into the primary Ollama router asking for the MLX dir name
'Qwen2.5-7B-Instruct-4bit', which Ollama 404'd. AeroLLMBackend was never
invoked — exactly matching the user-visible 'no prompts forwarded to aeroLLM'
report.
Secondary bug surfaced during verification: _is_aerollm_installed() checked
importlib.util.find_spec('aerollm'), but the PyO3 wheel publishes as
'aerollm_api' (the module AeroLLMBackend imports at runtime). So even when
inference worked, deep.installed was reported False to the chat tab,
disqualifying Box B from auto-open.
Changes:
1. chat.html buildBody(): translate runtime='aerollm'/'airllm' into
backend=runtime, mirroring the trick loadModel() already used at warm time.
2. app.py _is_aerollm_installed(): check 'aerollm_api', not 'aerollm'.
3. pyproject.toml + scripts/setup.sh: lower aerollm_maximus default from
Qwen2.5-72B-Instruct-4bit (~40 GB, weights not shipped, OOMs <48 GB) to
Qwen2.5-7B-Instruct-4bit so a fresh clone works out of the box.
Operators upgrade locally via AEROLLM_MODEL in .env after downloading
larger weights — recipe in the pyproject comment.
4. scripts/setup.sh: alias legacy 'ai-engineer:latest' → 'ai-eng:latest' via
'ollama cp' (idempotent, no re-download) when the new name is missing;
warn at setup time if AEROLLM_MODEL weights are absent with the exact
huggingface-cli command to fix.
5. chat.html init(): auto-warm Box A on chat open; on maximus when a deep
backend resolves, also auto-open and warm Box B. Persisted opt-out via
localStorage so closing Box B sticks across reloads. Skips Box B auto-warm
when free_gb < 6 to honor the host's OOM history.
6. chat.html loadModel(): parse NDJSON for {type:'error'} / final.error and
surface them; no more false-positive WARM chips on a failed warm.
7. app.py: new _resolve_chat_deep_default() — accept both
LAB_CHAT_DEEP_DEFAULT and ARAIL_CHAT_DEEP_DEFAULT env names; default to
ON on maximus when aeroLLM resolves. New _resilient_chat_default() —
if MODEL_NAME points to an uninstalled model, fall back to the best
installed ai-eng-family match.
8. tests/test_aerollm_tier_resolution.py: updated to lock maximus=7B as the
new policy while preserving the structural Bug 1 (MIN_ID stomp) and
Bug 2 (capture_tier case-block) guards from sprint 2026-05-20.
Verified end-to-end: direct curl POST /api/chat/stream with backend=aerollm
returns a streamed token sequence from AeroLLMBackend (~16 s cold start,
fast thereafter). 14/14 tier-resolution tests pass.
Note: chat.html also carries a small uncommitted markdown-render hunk that
predated this work on the same branch (the branch's nominal scope); it
ships together here because it co-existed in the file. Other in-flight WIP
(prompt-caching, cache_prewarm) is intentionally left uncommitted on the
working tree.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…budget sprint Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…re function
Adds four new constants (_AEROLLM_KV_MIN_FLOOR_BYTES, _AEROLLM_KV_SAFETY_HEADROOM_BYTES,
_AEROLLM_KV_AVAILABLE_FRACTION, _AEROLLM_KV_PCT_DEFAULT) and _resolve_kv_budget(), a pure
function that computes the available-RAM-aware KV cache budget. Formula:
budget = max(MIN_FLOOR, min(total*pct, available*0.85 - 1.5GiB))
Returns {budget_bytes, reason, fields} with source in {env,default,floor,unavailable}.
Never raises; wraps all fallible psutil calls in try/except.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e_kv_budget() Covers all behavior matrix rows from ARCHITECTURE.md: default pct, env override, floor clamp, invalid env fallback (zero/garbage/above-one), psutil import error, psutil call error, total=0 defensive case, int type assertion, and 16GiB idle-box regression. All 12 tests pass. Uses monkeypatch for env and unittest.mock.patch for psutil. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lazy-imports arail.activity_log inside the method to avoid circular-import
risk. Emits at "warn" when source in {floor, unavailable}, "info" otherwise.
Category="system". Swallows both ImportError (headless test harness) and any
exception from emit() so backend construction is never gated on logging.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…get() + _emit_budget_activity() Replaces the old total*pct-only computation with the available-RAM-aware resolver. The resolver always runs (not just when env var is set); aerollm now defaults to available*0.85-1.5GiB capped by total*0.60 rather than total*0.60 uncapped. kv_memory_budget is set only when budget_bytes is non-None (psutil unavailable → aerollm auto-detects its own 80%). Emit call follows resolver inside first-init branch, never on singleton reuse. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t wiring Test 12: _emit_budget_activity fires exactly once on first construction; singleton reuse does not re-emit. Test 13a: rt_kwargs['kv_memory_budget'] is the int from _resolve_kv_budget. Test 13b: kwarg is absent when resolver returns None (psutil unavailable path). Uses fake aerollm_api injected via sys.modules; clears AeroLLMBackend._shared between tests via autouse fixture. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lint warnings All steps complete. 15 new tests passing (12 unit + 3 integration). Regression 14/14. No deviations from ARCHITECTURE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t_activity
Finding A: `from arail import activity_log` → `from arail.activity import activity_log`
Finding B: `emit(level=..., category=..., message=...)` → `emit("aerollm", reason, level=level)`
Finding D (recommended): narrow bare `except Exception: pass` to log via `_log.warning`
so future signature/import drift surfaces in uvicorn output.
Add `import logging` + `_log = logging.getLogger(__name__)` at module level (first use).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… (tests 14a+14b)
Tests 14a and 14b let _emit_budget_activity run for real (not patched out)
and assert on a patched arail.activity.activity_log.emit.
14a: source="default" → emit called with ("aerollm", <msg containing "KV budget resolved">, level="info")
14b: source="floor" → emit called with ("aerollm", <msg>, level="warn")
Verify loop confirmed: with the pre-fix broken import (`from arail import activity_log`)
mock_emit is never called, so assert_called_once() would have failed — the test is
a meaningful guard against Findings A and B recurring.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds 26 tests covering boundaries the unit suite did not exercise: - Boundary arithmetic: tie between ceil_total/ceil_available, raw==floor (strict-less-than guard), negative ceil_available, available>total (container quirk), tiny (4 GiB) box, huge (512 GiB) box. - Env-var parsing edges: whitespace stripping (4 variants), exotic numerics (0.5e0, .5, 5e-1, +0.5), locale comma fallback, very small valid pct, exact 0.0 and 1.0 boundary fallbacks. - Emit coverage for all four source values (default/env/floor/ unavailable) with correct info/warn level routing. - Singleton idempotency: identical key → resolve+emit once; distinct AEROLLM_MODEL → both fire (per-model keying). - Logger-warn fallback: activity_log.emit raises → _log.warning fires and exception does not propagate (REVIEW.md Finding D guard). - psutil attribute-failure path: import succeeds but virtual_memory attr access raises AttributeError (complements test 8 ImportError). All 26 new tests pass; full router+tier suite green (57 passed). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…on OOM-safety) 57 tests pass (12 unit + 5 integration + 14 tier regression + 26 new QA edges). No failures, no security findings. Live activity-feed acceptance test deferred: available RAM was 8 GiB at QA time and the user has documented OOM history on this box; behavior is fully covered by tests 14a/14b + new all-sources parametrized emit test. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…eferred ai-eng collapse (tag 404), qwen attribution to NOTICE Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ribution Supersedes the 'wait for ollama.ai qukaizen/ tag' deferral. User verified the tag 404s and chose to self-host. Designs HF-primary (ollama pull hf.co/...) + GitHub-release mirror + optional qukaizen.com, package_ai_eng.sh scaffold, self-hosted setup pull ladder with sha256 fail-closed verification, and a preview-net last resort kept until the GGUF is uploaded. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…icense research Confirmed Qwen2.5-3B-Instruct license is Qwen Research License (not Apache-2.0). Qwen2.5-7B-Instruct (preview base) is Apache-2.0. Both require attribution. Plan: 7 steps per ARCHITECTURE.md REVISED v2 implementation order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bution Qwen2.5-3B-Instruct (production base) is under the Qwen Research License (not Apache-2.0). Qwen2.5-7B-Instruct (preview base) is Apache-2.0. NOTICE mandates that the redistributed ai-eng GGUF artifact (HF model card, GitHub Release) carries this same attribution — the base license obligations travel with the derivative. LICENSE gets a one-line pointer to NOTICE for bundled/redistributed weights. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sentinel
Sentinel rollout per ARCHITECTURE.md § Part 1. Files changed:
- pyproject.toml: airllm_minimalist/maximus/airllm → sentinel + TODO comment;
add self-hosted keys (ai_eng_hf_repo/quant/gh_url/cdn_url/sha256/preview)
with __PLACEHOLDER__ markers; maximus tier description honest-framing rewrite.
- scripts/setup.sh:69-71 + 986-989: AIRLLM_MODEL_ID vars → sentinel.
- src/arail/router/backends.py: AIRLLM_MODEL default → sentinel; sentinel guard
raises RuntimeError with configure-notice before any load.
- src/arail/router/airllm_worker.py: same guard pattern.
- src/arail/portal/app.py: all AIRLLM_MODEL/default_model Llama-3.1-70B
literals → sentinel; AirLLM description + download hint updated; sentinel
gated_hint added to error result path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… add check_ai_eng_artifact.sh install_models() now runs: HF primary (ollama pull hf.co/<repo>:<quant>) → GitHub Release mirror (sha256-verified curl + ollama create) → optional CDN mirror → last-resort preview net (qwen2.5:7b + Modelfile.preview). Supply-chain: mirror paths fail-closed on __PLACEHOLDER_SHA256__ or digest mismatch — no ollama create from unverified blob. All hosts/quant/digest are env-overridable. Neutral wording in info lines (no qwen lineage in narrative). Idempotency, ai-engineer alias migration, ARAIL_SKIP_OLLAMA guard preserved. check_ai_eng_artifact.sh: probes HF + GitHub for live GGUF (HEAD request, 8s timeout). Exit 0 = live; exit 1 = not yet uploaded. Gates follow-up ticket 2b (delete Modelfile.preview once artifact confirmed live). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…→upload scaffold
Developer-side tool; never runs during setup. Given --base-dir + --lora-dir:
1. Merges LoRA into qwen2.5:3b base (via build_ai_eng.py or peft inline)
2. Converts merged model to GGUF via llama.cpp convert_hf_to_gguf.py + quantize
3. Emits Modelfile + NOTICE next to the GGUF (NOTICE travels with artifact)
4. Prints sha256 of the GGUF + exact upload commands for HF/GitHub/CDN
as # TODO(manual): blocks the user uncomments and runs
No credentials embedded. No weights invented or downloaded.
Missing --base-dir or --lora-dir → prints manual steps, exits 1.
Upload steps require `huggingface-cli login` / `gh auth login` by the user.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… placeholder row
models/ai-eng/Modelfile.preview: keep FROM qwen2.5:7b (required for preview net);
strip qwen self-description from SYSTEM — now a neutral ai-eng persona description.
src/arail/chat/models_catalog.yaml:
- ai-eng entry: strip qwen fallback sentence; set install to
`ollama pull hf.co/qukaizen/ai-eng-3b-gguf:Q4_K_M` (self-hosted
single pull, __PLACEHOLDER__ repo); clean description (no lineage mention).
- Add new deep-model placeholder row (tier: flagship, install: "")
pointing operators to .env + docs; no real model id, no download.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ANGELOG README.md: maximus row — "Frontier-scale local inference, full bench" → "The full bench. The heaviest model that runs well on your machine — with cloud frontier models one click away in the Chat Compute Source." CLAUDE.md: maximus tier line → "full local bench, cloud frontier one click away"; qwen fallback prose → describes self-hosted pull + preview fallback. tuning.html:111: "Run frontier-scale models locally" → "Run the heaviest models your silicon handles well — … Frontier-scale models are one click away in the cloud." docs/INSTALL.md: rewrite setup ai-eng pull narrative — self-hosted ladder (HF primary → GitHub mirror w/ sha256 verify → CDN → preview net). CHANGELOG.md: add [Unreleased] section — sentinel rollout, honest framing, self-hosted ai-eng distribution, package_ai_eng.sh, check script, NOTICE. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The replacement phrase 'Frontier-scale models are one click away' still matched the grep gate. Reworded to 'Cloud frontier models are one click away via the Compute Source.' All four gated surfaces now clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…A gates, final state All 8 steps complete. QA grep gates documented and passing. Test suite regression comparison recorded (no new failures). Declared done. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uards Setup-ladder mock tests (tests/setup_ladder/): exercise the self-hosted ai-eng fetch ladder in scripts/setup.sh:install_services() with ollama/curl/ sha256sum/timeout fully stubbed on PATH — no download, no model load, no network (OOM-safe). Covers HF-primary single-pull, HF-404→GitHub-mirror digest-MATCH→create, digest-MISMATCH fail-closed, placeholder-digest mirror disabled (no curl), all-hosts-fail→preview-net, offline→clean-exit, skip, idempotency, legacy ai-engineer alias, CDN gating. Python guards (tests/test_model_hosting_reframe_qa.py): deep-model sentinel not resolvable + backend raises before load (fake airllm so a guard regression can never load a real 70B), qwen-hiding regression grep, NOTICE/LICENSE attribution, package_ai_eng.sh credential/weight-download security, Buddy ai-eng resolution post-rename, honest-framing grep, supply- chain digest-gate static assertions. All 43 new tests pass. No new failures in the existing suite (17 pre-existing / parallel-sprint failures unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
43 new QA tests (setup-ladder + sentinel/qwen-hiding/security guards) pass; 17 pre-existing/parallel-sprint failures unchanged (no new failures). [ASK-LICENSE] independently re-verified via HF API (Qwen2.5-3B = qwen-research non-commercial vs MIT blueprint) — must-resolve-before-distribution, not merge-blocking. Surfaced two pre-existing items: TIMEOUT-GAP (no `timeout` on stock macOS, the headline platform) and STALE-ENV (70B in local .env bypasses the sentinel guard). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…K_PASS) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…cense blocker The 3B base (Qwen Research License, research/non-commercial) conflicted with ARAIL's MIT fork/redistribute thesis. The 1.5B base is Apache-2.0 — fully compatible and ~1 GB lighter on the OOM-sensitive 36 GB dev box. Changes: - NOTICE: rewritten for 1.5B Apache-2.0 base; Qwen Research License language removed; dual-section collapses to one (production == preview base == 1.5B) - Modelfile.preview: FROM qwen2.5:7b → FROM qwen2.5:1.5b; SYSTEM "3B" → "1.5B" - pyproject.toml: ai_eng_hf_repo/gh_url 3b→1.5b; ai_eng_preview 7b→1.5b; all "3B-parameter" branding → "1.5B-parameter" - models_catalog.yaml: name/description/install/size_gb updated; preview catalog entry qwen2.5:7b → qwen2.5:1.5b - README.md, CLAUDE.md, docs/INSTALL.md: "3B" → "1.5B" - scripts/setup.sh: hf_repo/gh_url defaults, comments, preview_base var - scripts/package_ai_eng.sh: base model id, GGUF filename, NOTICE template, upload command placeholders, all 3b→1.5b - CHANGELOG.md: new re-base section documenting confirmed SPDX Apache-2.0 - tests: update guard assertions for 1.5b base, Apache-2.0 NOTICE, FROM qwen2.5:1.5b in Modelfile.preview; 43/43 pass Confirmed: `Qwen/Qwen2.5-1.5B-Instruct` → SPDX `Apache-2.0` (HF API verified). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-8B → Llama-1B two-tier) Onboarding clarity audit found stale copy contradicting the shipped two-tier model: setup.sh tier-picker said '1.5B Opus-4.7' while installing Llama-3.2-1B; INSTALL.md called it 'ai-eng/1.5B'; MACOS.md claimed a Qwen3-8B 4.3GB download that no longer happens. Aligned all to llama-ai-eng (Llama-3.2-1B), clarified both tiers share one AI-engineer persona, and added a post-setup line naming the installed model. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fault model Captures the target architecture: specialize a permissive small base (Llama-3.2) via the Nucleus/SSDP AI-corpus + research distillation into a real AI-engineer domain model (proven previously on Qwen), then package through the existing build→quantize→self-host lane. Records the persona-wrap as the honest interim, the adapter-must-match-base lesson, and the licensing constraints. Establishes docs/adr/ for ARAIL. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…RAM users Adds the lightweight on-box SLM qukaizen-site runs on CPU (qwen2.5:0.5b-instruct-q4_K_M, Apache-2.0, ~0.4 GB) as a browse-and-pull catalog entry, plus a README note pointing budget/CPU-only users at it. Additive only — the two feature tiers are unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Setup step 3/11 (`pip install -e .[maximus]`) failed on Apple Silicon with "could not find a version that satisfies aerollm-api" — the maximus/max/aero extras hard-pinned `aerollm-api>=0.1.0rc2`, but only rc1 was on PyPI (rc2 was never published). AeroLLM is the optional deep-mode "2nd inference", a cargo-built native extension with no general pip wheel, so it must not gate core setup: it's installed out-of-band by scripts/build-aerollm.sh (`./arailctl deep rebuild`), which fails soft. Remove the pin from the maximus/max extras and make `aero` a no-op marker. Also document in README that AeroLLM deep mode is Apple-Silicon/MLX-only today (CUDA backend in progress) and now open source (github.com/cdarnell/aerollm). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r) (#77) * feat(world-mount): vendor physics WorldBundle fixtures (physics/tampered/hostile) Three test fixture bundles for the ARAIL World Mount feature: - physics/: byte-identical copy of qukaizen-dac/dist/bundles/physics/ (7 files) - tampered/: physics copy with one byte flipped in terms.json; manifest unchanged so seal verification detects the mismatch - hostile/: physics copy with an injected term whose definition begins "Ignore previous instructions…"; re-hashed so the seal is valid (tests that the hostile content renders inert, not that it is rejected) Tests never read the DaC dist path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(world-mount): Phase 0 — loader core, seal/compat/category gating, arailctl world) world_mount.py pure functions: - load_bundle(dir) → Bundle: parses manifest.json + 6 siblings; raises PartialBundle on missing/unreadable; face.json missing/invalid is tolerated (None face) - verify_seal(bundle) → SealResult: sha256(terms_raw_bytes) == manifest.world_sha256 AND == manifest.files["terms.json"]; checks all other sibling hashes; prints BOTH hashes on mismatch (exit 2) - check_compat(bundle): bundle_schema/terms_schema must == 1; SchemaSkew names both - check_categories(bundle): GateViolation on any term.category not in spec.categories - term_to_dict_entry: DaC term node → TermEntry-compatible dict; definition never mapped - __main__ argparse: list | mount | verify | swap | unmount [--apply-face] arailctl: world) case activates venv, exec python -m arail.world_mount "$@" Usage help updated. 18 tests pass: good seal, tampered→mismatch (both hashes), schema v2→refuse, missing files→refuse, bogus category→refuse, hostile fixture seal valid + definition never enters dict entry (inert). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(world-mount): Phase 1 — mount/unmount/swap + pointer atomicity + startup detect world_mount.py state mutations: - mount(): load+verify in-memory → stage files via .staging-<slug>/ atomic rename → index → env face (if apply_face) → write pointer LAST via temp+os.replace - unmount(): remove pointer FIRST, then optionally remove staged dir - swap(): verify new bundle → stage → flip pointer; old world stays mounted on failure - current_mount(): single switch all consumers read; returns None on missing/corrupt pointer portal/app.py startup: detect + log any mounted world (world_mount.current_mount()) 12 atomicity tests pass: pointer written last (staging fail = no orphan pointer), unmount removes pointer first, swap keeps old on tampered new bundle, round-trip MountRecord. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(world-mount): Phase 2 — KB staging + index _stage_files(): copies 6 bundle files to pkb/sources/world-<slug>/ via .staging-<slug>/ atomic dir rename; also emits world-<slug>.md index page (term | short_def | source per row) for human browsing + clean wiki node. _index_staged(): calls ensure_ready(pkb_root) then schedule_upsert per staged file. Wrapped in try/except in mount() so LanceDB-absent never aborts a mount. .json is already in _PKB_TEXT_SUFFIXES so staged terms are indexed. 6 tests pass: staged dir holds exactly 6 bundle files + world-physics.md; each staged file's sha256 matches manifest; index page has term table; staging idempotent; LanceDB-absent no-raise; upsert called per file. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(world-mount): Phase 3 — dictionary flip (bundle terms override while mounted) portal/app.py: - _world_mounted_dict_response(): reads current_mount() → get_mounted_dict_terms() → returns {terms, can_generate:false, source:"world"}; returns None if not mounted - GET /api/dictionary: checks mounted world first - POST /api/dictionary/seed: no-op while mounted (returns world terms) - POST /api/dictionary/generate-more: serves world terms (no router call) - POST /api/dictionary/theme: 409 while mounted - POST /api/dictionary/expand: serves bundle definition inline (no router call); finds term by name or slug Security invariant: generate-more/expand never call the inference router while mounted. Terms render only through template data paths. Tested with hostile fixture: definition field not in TermEntry mapping (inert). 9 tests pass: 42 terms with origin/citation, can_generate:false, hostile definition not in entry, unmount restores None, term ordering by spec.categories. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(world-mount): Phase 4 — face/theme flip + consent world_mount._write_face_env(): - Writes LAB_INTENT=other, LAB_INTENT_NAME, LAB_INTENT_DESCRIPTION, LAB_THEME via env_writer.set_env_var (atomic, comment-preserving) - LAB_UI_THEME written only if load_ui_theme(palette_hint) resolves to a known theme (physics blue-cyan-lab resolves; unknown palette → key omitted) - Never touches LAB_NAME/LAB_LOGO (brand untouched) - face.json missing/invalid → KB mounts anyway, env skip with warning - Only fires when apply_face=True; KB-only mount writes nothing to .env CLI --apply-face displays face.json preview + key list before writing; prints restart hint after: ./arailctl restart (portal reads env at startup) 11 tests pass: 5 keys written, brand untouched, unknown palette omits LAB_UI_THEME, KB-only mount leaves .env clean, missing face tolerates mount. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(world-mount): Phase 5 — Buddy WORLD FRAMING block in _compose_prompt _builtin_buddy.py: - _MAX_WORLD_DOMAIN_FRAMING = 600 chars - _MAX_WORLD_VOCAB_REGISTER = 300 chars - _world_framing_block(): reads current_mount() → mounted_face(); formats delimited block "# WORLD FRAMING\nDomain: ...\nVocabulary: ...\n# END WORLD FRAMING" with per-field length caps; returns "" if not mounted or face absent - _compose_prompt: inserts world_framing block between base SYSTEM_PROMPT and dreams/skills when mounted; unmounted path is byte-identical (no block) Security: only face.json text enters the prompt (never terms.json definitions). Researcher intent via LAB_INTENT=other (written by Phase 4 --apply-face) — no researcher.py edit required. 10 tests pass: framing empty when not mounted, delimiters present when mounted, domain/vocab content present, caps enforced, compose_prompt integration, hostile definitions never reach framing block. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(world-mount): Phase 6 — curator trusted sources union from mounted world curator.py: - _world_extra_sources(): reads current_mount() → world_trusted_domains(record) at call time; returns {} if not mounted or world_mount unavailable - propose_sources(): builds effective_sources = dict(TRUSTED_SOURCES) merged with world extras at runtime; TRUSTED_SOURCES module dict never mutated - Physics bundle: NIST→physics.nist.gov, BIPM→bipm.org, CODATA→codata.org added to proposals when physics world is mounted - Airgap still blocks all proposals (mounted or not) - Unmount immediately removes world sources (next call → no extras) - Consent gate unchanged: world sources go through submit_proposals/ConsentStore 7 tests pass: module dict untouched, extras present when mounted, empty when not mounted, airgap blocks, unmount restores, consent not bypassed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * harden world-mount: slug allowlist (R2) + cap expand length (R3) R2: add SlugInvalid error + _SLUG_RE allowlist (^[a-z0-9][a-z0-9-]*$) in load_bundle. Slug validated against manifest before any disk staging; cross-file consistency enforced: manifest.world must equal spec.json.slug and face.json.world when those fields are present. Refuses with actionable user_message before any path is constructed. Tests: 7 new cases in test_world_loader.py covering dotdot, path-separator, dot, uppercase, valid slug, spec mismatch, face mismatch. R3: cap expand definition to _MAX_DETAIL (1500 chars) in app.py expand endpoint, reusing the existing dictionary.py constant. Tests: 1 new case in test_world_dictionary.py. QA probe test_seal_catches_altered_face_json updated to mutate a non-slug field so the seal check (not slug check) fires. All 97 world-mount tests pass (73 builder + 16 QA probes + 8 new hardening tests). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(env_writer): shell-quote .env values so face-flip free text is source-safe The World-mount face flip appends multi-word free text (LAB_INTENT_DESCRIPTION from face.domain_framing). set_env_var appended new keys unquoted, so arailctl's `set -a; source .env` ran the value's first word as a command ("lab: command not found") and could expand $/` sequences. with_value/append now double-quote (escaping \ " $ `) any value that isn't a shell-safe bare token, preserve existing single/double quote styles when still safe, and repair an already-written bare unsafe line in place. Simple tokens (other, blue-cyan-lab) stay unquoted. Adds bash-source round-trip regression tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * harden shell-sourced config writers found in env-quoting audit Audit of every writer of a shell-sourced file (.env, lab.conf) after the world-mount face-flip bug. Findings fixed: - #4 legacy POST /api/system/mode: hand-rolled a non-atomic .env rewrite and skipped the CSRF/loopback gates the canonical POST /api/airgap/toggle enforces (no client used it; the nav badge POSTs to /api/airgap/toggle). Now returns 410 pointing at the gated endpoint instead of an ungated write. - #5 setup.sh lab.conf: IDE_PASSWORD=${ARAIL_PASSWORD} was written bare into a shell-sourced file, so a user passphrase with a space/$/backtick broke 'source lab.conf' or executed. Generalized _set_env_var to target any file, added a quote-aware _get_env_var, and route IDE_PASSWORD (heredoc + drift repair) through the quoter. - #6 blueprint.sh: per-instance .env wrote free-text LAB_NAME/LAB_TAGLINE/ LAB_INTENT quoted-but-unescaped; added a shell-quoter mirroring env_writer. Adds tests/shell_source_safety_driver.sh (+ pytest wrapper) exercising the REAL extracted shell helpers against hostile input ($(...), backticks, ;) and asserting source-safety + no command execution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test: isolate portal tests from a developer's ambient world mount Portal endpoints resolve the mounted World via current_mount() -> _default_data_dir() -> real lab/data. With a World mounted on the dev machine (./arailctl world mount), unrelated portal/dictionary tests saw mounted behavior and failed (e.g. test_expand_graceful_when_model_fails); CI passed only because lab/ is git-ignored. Autouse fixture points the default data dir at a fresh empty dir so the ambient lookup finds nothing mounted; tests that set up their own mount override _default_data_dir in their body, and explicit current_mount(data_dir=...) calls are untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…speak, get a RAW note) (#78) * feat(capabilities): scaffold capability registry package Step 1 of STT+capabilities sprint. Adds the capabilities/ package: errors hierarchy, Adapter ABC + two seam sub-ABCs, tolerant parse_capabilities_file, platform-aware registry.select(), and resolve_capabilities with the three resolution states. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(capabilities): macOS + Linux backends (WC-B seam) Step 2. Linux audio+STT stubs register but raise CapabilityNotImplemented ('<id>: no backend for linux'). macOS audio (browser-capture materialization, rejects webm/opus) and STT (lazy swiftc compile of arail-stt helper, injectable _runner, error-code mapping) register unconditionally; availability is platform-gated. Apple symbols confined to backends/macos/ — the Swift helper is the only Speech/AVFoundation import. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(world_mount): resolve declared capabilities to a sidecar (WC-C/WC-D) Step 3. Additive last-step in mount()/swap(): read bundle-optional, seal-exempt capabilities.json, resolve against the registry, persist to DATA_DIR/world-capabilities.json atomically. Absent -> resolved=[] (WC-D); malformed -> capabilities_error recorded, mount still succeeds. unmount() removes the sidecar. MountRecord shape untouched (atomic-pointer tests preserved). Adds three vendored fixtures (physics + capabilities.json declaring stt; +equation-ocr for WC-C; no-caps for WC-D). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * test(capabilities): WC-B/C/D + registry resolution + sidecar (off-ramp green) Step 3 tests. Registry resolution states, WC-B (Linux stub raises clean + Apple-symbol grep clean), WC-C (equation-ocr zero-code graceful degrade), WC-D (no capabilities.json mounts clean), malformed-json grace, missing-CLT declared_unavailable, tolerant parse, unmount removes sidecar. 11 pass; world-mount regression (12) still green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(capabilities): STT backend tests + live_mic marker + graceful TCC-crash mapping Step 4. Injectable _runner unit tests (success/no_speech/permission/ model_unavailable/decode_failed), CLT-missing _ensure_helper, audio webm rejection + m4a materialization. Adds live_mic pytest marker (skipped in CI) gating the real swiftc-compile test. Maps a signal-killed helper (SIGABRT rc=134/-N) to permission_denied so an unsigned helper degrades to a 409 rather than a 500 — see BUILD_LOG TCC-grant delta. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(portal): POST /api/stt/transcribe → RAW voice note (WC-A backend) Step 5. Endpoint gated on a mounted World resolving speech-to-text=available; materializes the posted blob via the audio adapter, transcribes on-device via the STT adapter, lands the transcript as a RAW/unsourced note in research/voice-notes/ (kind:raw, sourced:false — DATA, never a prompt), indexes via schedule_upsert, schedules wiki rebuild, deletes the temp audio in finally:. Errors map to 4xx with actionable messages, never 500-traceback. 9 flow tests: RAW landing + indexing, e2e, temp cleanup, no_speech, no-mount, webm-422, adapter-409, airgapped zero-egress, transcript-not-in-prompt. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(chat): mic affordance gated on inherited speech-to-text (WC-A surface) Step 6. Chat route resolves the mounted World's speech-to-text state and passes it to the template; the composer gains a mic button that renders disabled (with the resolution message as tooltip) unless STT is available — making capability inheritance visible. Self-contained JS: getUserMedia + MediaRecorder requesting audio/mp4 first (never webm), tap-to-record / tap-to-stop, POST to /api/stt/transcribe, status-line toasts for saved / no_speech / permission-denied / unsupported-browser. DOM tests assert the disabled/enabled gating. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(build-log): stt-capabilities builder log Per-step status, real seam paths, the TCC-grant delta (live transcription blocked for an unsigned swiftc binary — STOPPED rather than building a signing pipeline; graceful 409 degrade in place), WC-B grep result, pre-existing-vs- introduced test adjudication (17 baseline failures reproduced on pre-sprint tip), and the demo command. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(capabilities): swap Apple-Speech STT → local Whisper backend Apple-Speech is dead (unsigned swiftc CLI SIGABRTs on the speech-recognition TCC grant; signed .app rejected by owner — BUILD_LOG DELTA 1, ARCHITECTURE Addendum A). Replace with a platform-neutral local-Whisper backend below the existing adapter seam: - add faster-whisper>=1.2.0 (prebuilt wheels, no compiler) - DELETE stt_helper.swift and the Apple body of macos/stt_backend.py (now a no-Apple alias for WhisperSpeechToText) - DELETE the linux/stt_backend.py stub - backends/whisper_stt.py: afconvert → base.en → same (rc,stdout,stderr) JSON shape behind the UNCHANGED injectable _runner seam; lazy first-use model fetch to lab/models/whisper/base.en/; airgapped-graceful model_unavailable - register WhisperSpeechToText for BOTH darwin and linux (WC-3: Linux off the STT stub) WC-B: Apple-symbol grep over all of src/ (no exclude) is now clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(capabilities): finish Whisper STT swap — register both platforms, retire Apple path Completes the builder pass interrupted by the API drop (on top of eb28cdf): - whisper_stt registered platform-neutrally for darwin + linux (Linux off the stub → advances WC-3); per-platform packages keep only the audio-capture seam. - macos/stt_backend.py gutted to a 22-line no-Apple backward-compat alias (MacOSSpeechToText = WhisperSpeechToText); zero Apple symbols anywhere in src/. - test_stt_backend / test_capabilities updated for the Whisper runner + live_stt marker. 33 passed / 1 skipped (live_stt). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(stt): record Whisper backend swap + WC-A.4 live proof in BUILD_LOG; commit sprint artifacts Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(stt): gate _ensure_model on patchable _model_present() (QA B1); add QA probes QA found test_airgapped_graceful_unavailable was false-green — _ensure_model stat'd model.bin directly, bypassing the _model_present() indirection the test monkeypatches, so with the real model on disk it couldn't simulate absence. Production degrade was already correct; this makes the safety-critical airgapped path testable. Adds QA's hostile-transcript + temp-leak-on-raise probes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(stt): sprint ledger — QA PASS, all four win conditions met Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…st on tap (#79) The mic JS already warned on first tap when the browser can't record an afconvert-decodable container (Chrome webm/opus). Now it also checks pickMime() at load and, when unsupported, mutes the mic button + sets an explanatory title + flashes a one-time notice — so the Safari-in-v1 caveat is visible the moment Chat opens. Adds a render test asserting the caveat is wired. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…CR via Apple Vision) (#81) * feat(ocr): image-text OCR seam + macOS Vision backend + Linux stub Add ImageTextRecognitionAdapter (Seam C) for equation-ocr; register the macOS Apple-Vision backend (lazy swiftc-compiled helper, injectable _runner) and a Linux registered-but-unimplemented stub. All Apple symbols confined to backends/macos/ (WC-B). No edit to registry/resolve/spec/world_mount. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(ocr): narrow equation-ocr fixture to v1 text contract + prove two-live WC-C Fixture purpose -> printed text/number recognition, outputs latex->text (id unchanged for WC-C continuity). Registry now resolves TWO live capabilities (speech-to-text + equation-ocr) available when toolchains present; a third undeclared id still declared_unavailable. Update STT WC-B grep to exclude the macos OCR backend's now-legit swiftc/xcrun symbols + build-cache artifacts. Off-ramp safe point: WC-B/C/D shippable independent of the Vision backend/UI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(ocr): POST /api/ocr/extract + RAW ocr-note landing + upload validation Near-copy of /api/stt/transcribe: multipart image -> on-device OCR -> RAW note in research/ocr-notes/ (kind:raw, sourced:false, image provenance), indexed via schedule_upsert + wiki rebuild. Upload validation: mime allowlist + magic-byte sniff + 12 MB cap (422, never 500); temp image deleted in finally:. OCR text is inert DATA, never enters a prompt. Fake-runner flow tests incl. mandatory hostile-image inert-RAW/not-in-prompt test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(ocr): backend unit tests + live_ocr real-Vision proof + marker Fake-runner error-code mapping (ok/no_text/decode_failed/model_unavailable/bad JSON), is_available() probes, and two live_ocr tests: real Apple Vision OCR on a synthesized constants image (asserts 1.380649e-23 fidelity) + helper-compiles- once. Add the live_ocr pytest marker. Real path measured: ~0.43s, exact digits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(ocr): capability-gated 📷 upload/paste/drop affordance in Chat Resolve ocr_available/ocr_message in the chat route alongside stt; add an #ocr-btn gated on equation-ocr==available (identical to the mic gating on speech-to-text). Hidden file input + clipboard paste + composer drop wire to POST /api/ocr/extract with a status-line toast. DOM gating tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ocr): keep Apple symbols below the seam (WC-B) Reword the OCR endpoint comment so the portal carries no platform OCR symbols; update the second pre-existing STT WC-B grep (test_stt_backend) to exclude the macos OCR backend's legit swiftc/xcrun + build-cache artifacts. WC-B grep over src/ now matches only backends/macos/ocr_backend.py + ocr_helper.swift. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(ocr): BUILD_LOG for equation-ocr sprint Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(equation-ocr): sprint artifacts + ledger — QA PASS, two live capabilities Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
QuKaiZen Project-Aware Super Skill v1.0 — trained on ARAIL's own codebase. Base Model: Google Gemma 4-2B-IT (9.5GB safetensors) Training Data: 29,515 files from aerollm, arail, qukaizen-dac Training Method: Quick-Storm POC (certified all 3 gates) Certification Results (All Passed ✓): Gate 1 (General Capability): 0.875 Gate 2 (Domain Mastery): 0.850 Gate 3 (Hallucination Audit): 0.800 The model understands ARAIL's projects and reasoning patterns. Ships as ARAIL's default Buddy reasoning engine. Local inference: 5-7 tok/s (no API calls needed). Deep reasoning: Falls back to Claude API if needed. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Docs (v1.1 update): MACOS, README, setup reflect Ollama + llama3.2:1b default - Knowledge Base: Add world-physics domain with 50+ SI terms (BIPM/NIST-sourced) - Features: Cache prewarming for Anthropic prompt cache, chat highlight JS - Sprints: Archive completed two-tier validation (WEAK_PASS), kickoff RAR retrieval Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Resolved conflicts by taking main's versions of: - src/arail/portal/app.py (includes world selection endpoints) - src/arail/world_mount.py (world mounting implementation) - tests/ (main's test suite from world-identity-flip and world-switcher sprints) Main includes recent sprints for world identity management and switching (2026-06-14-world-identity-flip, 2026-06-14-world-switcher) plus model separation and Llama disclosure test coverage. Preserves gemma-poc branch content: - Cache prewarming feature - Chat highlighting - world-physics KB - Sprint documentation
…oLLM" The product narrative for World Forge as speculative authoring: a local model DRAFTS a mountable World in seconds (model-asserted, honest), AeroLLM RECONCILES it overnight (accept/correct/reject, promote model-asserted → sourced), and the autoresearch loop REFINES it forever. Tagline: "Quickly get a base — forever curated by the power of AeroLLM." Maps the tier story: minimalist dreams worlds; maximus (AeroLLM deep backend) makes them true. Marked status: design — the draft/export/mount pipeline is a proven spike; the one-button surface + always-on curation are the build-out. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…, not spec-decoding Per the precision point: World Forge borrows spec-decoding's economics (draft cheap, verify big) but not its algorithm. Replaced "literally speculative decoding" with an IS/IS-NOT table and the design consequence: unlike real spec-decoding (small model buys speed only, output identical to the target), here the small model BOUNDS coverage — so the loop needs an explicit expand step, not just verify. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…skill_from_path, load_world_skill (step 2/7) - _MAX_WORLD_SKILL_BYTES (64 KB) + _MAX_WORLD_SKILL_BODY_CHARS (24K-char) caps - _contain_skill_body mirrors DaC sanitizeBodyField in Python: neutralizes YAML fences, top-level # headings, and all ARAIL structural delimiters (# WORLD FRAMING, # Procedural knowledge, ## Skill:, Observation:, Source:, Buddy's one-sentence note:) with U+200C prefix; leaves glossary lines intact - load_skill_from_path: reads an explicit SKILL.md path, applies containment, enforces byte + char caps; never raises - load_world_skill: keyed off current_mount().staged_dir; returns None when nothing mounted or SKILL.md absent; never raises Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… in _stage_files (step 3/7) - _WORLD_SKILL_NAME = "SKILL.md" constant (seal-EXEMPT; NOT in _BUNDLE_FILES) - _stage_files now copies SKILL.md from bundle dir if present (after the 6-file loop, before atomic swap); missing or unreadable → logged, mount continues - SKILL.md is never added to _BUNDLE_FILES so verify_seal is unchanged for all existing 6-file bundles Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ry-skill-hostile (tampered SKILL.md) fixtures (step 4/7) - art-history-skill/: frozen copy of qukaizen-dac/dist/bundles/art-history/ (6 sealed files + manifest + SKILL.md + capabilities.json + arail-plugin.json) Self-contained; no DaC toolchain needed at test time. - art-history-skill-hostile/: copy of hostile/ (valid 6-file seal) with a SKILL.md whose body contains forged structural lines (# WORLD FRAMING, # Procedural knowledge, ## Skill: EVIL, ---, Observation:, Source:, Buddy's one-sentence note:) for injection containment tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…soft block (step 5/7) - Imports load_world_skill from arail.skills_loader inside the existing try/except failsoft block so any loader error never breaks research - Appends the world skill to the skill list before compose_system_context; nothing-mounted → ws=None → skills unchanged → default behavior Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…compose_prompt wiring (step 6/7) - BuddyHost Protocol gains load_world_skill() -> Optional[Any] - ArailHost.load_world_skill wraps arail.skills_loader.load_world_skill in try/except → returns None on any failure (matches existing host method style) - _compose_prompt: loads agent skills + world skill separately, merges into all_skills before compose_skill_context; skill_ctx unchanged when no world - WORLD FRAMING block (face "who") stays unchanged as a DISTINCT section; world-skill is the term glossary "what" under ## Skill: in Procedural knowledge Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…curity, happy path, regression (step 7/7)
Test allocation (ARAIL gating):
~30% setup: stages SKILL.md byte-identically, seal passes with modified
SKILL.md (seal-exempt), missing SKILL.md is no-op, broken seal refused
~30% Buddy/Researcher: art-history Ballets Russes appears in skill context,
researcher context includes world skill, world-skill distinct from WORLD FRAMING
~20% security: _contain_skill_body neutralizes # WORLD FRAMING, # END WORLD
FRAMING, # Procedural knowledge, Observation:, ---, ## Skill: EVIL, Source:,
Buddy's one-sentence note:; preserves legitimate ### Category / - **term** lines;
oversized SKILL.md → None; malformed frontmatter loads body only
~10% happy: end-to-end mount→skill→unmount→None; swap replaces skill
~10% regression: nothing-mounted → None, compose_system_context shape unchanged,
BuddyHost has load_world_skill, ArailHost.load_world_skill safe when unmounted
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nt proof, final state (step 8/8) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… review fixes) Defect 1 — ### Category headers mangled: Old logic neutralized ALL column-0 '#' lines, catching the glossary's own ### Dance / ### Music / … (7 headers) in violation of the ARCHITECTURE spec. Fix: _HEADING_H1_H2_RE matches only h1 (# ) and h2 (## ) patterns, never h3+ (###). Legitimate glossary category headers now pass through intact; ## Skill: EVIL and # WORLD FRAMING are still neutralized. Defect 2 — body cap truncates the flagship bundle: _MAX_WORLD_SKILL_BODY_CHARS was 24*1024 (24,576 chars), truncating the 31,625-char art-history body and silently dropping ~22% (Music + Painting tail). Raised to 56*1024 (57,344). Full flagship glossary now fits with headroom. Truncation is a last resort; warning is logged when it fires. Defect 3 — indented delimiter passthrough: Delimiter check was column-0-anchored only; ' # WORLD FRAMING' passed. Fix: match _ARAIL_DELIMITERS against lstripped line so indented variants are also neutralized (## and # headings caught by _HEADING_H1_H2_RE too). New tests added (4 pins): test_contain_skill_body_preserves_all_h3_category_headers test_contain_skill_body_h3_headers_in_composed_prompt test_contain_skill_body_indented_delimiter_neutralized test_full_flagship_glossary_not_truncated (Greek Modes + Grove Dictionary of Music + Vermeer all present in body + compose) Results: 30/30 tests/test_world_skill_mount.py + 49/49 world-test regression suite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Closes the QA gate for dac-world-mount: 29 adversarial tests (tests/test_world_skill_qa_adversarial.py) probing homoglyph/unicode evasion, DoS inputs (5MB line, 50k forged lines, null bytes, over-cap), hostile capabilities.json, and legit-shaped Source injection — structural containment held (no surviving line is a renderable CommonMark h1/h2; injected content can never relocate out of its `## Skill:` H2). Adds the sprint ARCHITECTURE/REVIEW/ TEST_REPORT and the final ledger. Two LOW follow-ups tracked (escape-vs-ZWNJ; cross-repo SKILL.md seal-promotion). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stages the locally-mounted/source DaC Worlds (espresso, espresso-7b-v2, horticulture, physics face) under lab/ + a compiled guide README, so the baseline working tree is clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Baseline merge of qukaizen/arail-dac-world-mount → main. Contains the world-forge-doc work (PR #98) plus Sprint 2, so this supersedes #98.
Sprint 2 — DaC World-skill mount
A mounted DaC World's governed
SKILL.mdnow reaches the agents' system prompt (Buddy + Researcher), so ARAIL learns the mounted domain's knowledge — not just its identity.world_mount.py: stageSKILL.mdat mount;skills_loader.py:load_world_skill+_contain_skill_body(defense-in-depth — mounted content treated as untrusted DATA; forged prompt structure neutralized)._compose_prompt+ Researcher_get_system_context; face.json WORLD FRAMING (identity) stays distinct from the glossary (knowledge).###-header mangling and a 22% glossary truncation.SKILL.mdto SEALED (cross-repo with DaC); clean-machine full-suite +./arailctlCLI smoke.Also included
lab/(espresso, horticulture, physics).🤖 Generated with Claude Code