commit 9703eb2b6bae12b31179230b96a9b2b93b9bfe5b Author: Vuong Hoang Date: Wed May 20 20:38:22 2026 -0700 init: seed Ratatoskr from corviduo-project-template + ship v0 scaffold Worldtree Conversation API debug TUI. Multi-pane observability dashboard: chat transcript + persona/Vili affect log + tool events + admin events + Bifrost state + tool inventory + (opt-in) raw server log. Design locked at docs/design-brief.md (originated as brokkr-smithy/docs/ratatoskr-design-brief.md). Operator-locked decisions: - Textual application-shell framework (multi-pane dashboard, not REPL). - Separate repo + separate dev team (no Worldtree-source imports). - httpx-sse for SSE consumption (reference Python SSE-resume impl). - Triple version-skew mitigation: spec-pin in pyproject.toml + recorded SSE snapshot tests + conformance smoke. Initial pin: Worldtree v0.19.0 at 55101e909abcd2219833266b6f905c5bc956e0f0. - Persona pane: label-don't-refuse PII posture. - Server-log pane: opt-in via --server-log . - Two-stage Ctrl-C (cancel then exit). - Markdown rendering default-on; --raw opt-out. In the box: - docs/design-brief.md — the locked design with full rationale. - docs/SPEC-PIN.md — Worldtree spec pin + bump procedure. - docs/conversation-api-spec.md + docs/conversation_api.contract.md — vendored Worldtree spec snapshots at the pinned SHA. - pyproject.toml — Python 3.12, hatchling, uv-managed, deps locked. - src/ratatoskr/ — stub package (cli.py raises NotImplementedError). - tests/test_no_worldtree_imports.py — boundary smoke test PASSING. - tests/snapshots/README.md — recording convention for SSE snapshot tests. Not in the box yet: - Gitea remote (operator/infra-ops to register at vh/ratatoskr). - Implementation — the dev team owns this; design brief is the spec. Origin: althing thread 01KS3R34XD3N6HMK91VXESHGW7 (worldtree-dev → brokkr-smithy-dev, 2026-05-20). Volva consulted via thread 01KS3VF6W33N3V5FNMGQ91YNVD. diff --git a/.corviduo-canonicals.toml b/.corviduo-canonicals.toml new file mode 100644 index 0000000..32a5027 --- /dev/null +++ b/.corviduo-canonicals.toml @@ -0,0 +1,91 @@ +# Self-pin manifest for the corviduo-project-template repository. +# +# This template is the canonical home for cross-cutting Corviduo specs and +# tooling. The pins below declare which artifacts this repository owns as +# canonical (consumer_path == canonical_path; canonical_source == this repo). +# +# Operational effect: +# - `python scripts/canonical_sync.py` here is a no-op (every pin's +# pinned_sha256_16 == sha256_16(canonical), so nothing to write). +# - `python scripts/canonical_drift.py` here passes if every named +# canonical exists and is byte-identical to its pinned hash. Useful as a +# CI gate to catch accidental rewrites of a canonical artifact without a +# companion pin-bump in this manifest. +# +# Operators bootstrapping a new consumer can copy this file as a starting +# template, then adjust each pin's `consumer_path` to wherever they keep +# their downstream copy (often the same path). +# +# Pin-id naming convention: -v. Bump major when the +# canonical's shape changes in a way that requires consumer adaptation +# (not on every pin-bump). The pinned_sha256_16 + pinned_at fields track +# point-in-time identity within a major version. + +# --------------------------------------------------------------------------- +# Specifications (cross-language; all consumers should pin) +# --------------------------------------------------------------------------- + +[[pins]] +id = "contract-format-v2" +canonical_source = "corviduo-project-template" +canonical_path = "docs/contracts/CONTRACT-FORMAT.md" +consumer_path = "docs/contracts/CONTRACT-FORMAT.md" +pinned_sha256_16 = "f2f2f37072ebbc6b" +pinned_at = "2026-05-15T22:00:00+00:00" + +[[pins]] +id = "principles-v1" +canonical_source = "corviduo-project-template" +canonical_path = "PRINCIPLES.md" +consumer_path = "PRINCIPLES.md" +pinned_sha256_16 = "c5bd891601438635" +pinned_at = "2026-05-15T22:00:00+00:00" + +[[pins]] +id = "methodology-v1" +canonical_source = "corviduo-project-template" +canonical_path = "docs/methodology.md" +consumer_path = "docs/methodology.md" +pinned_sha256_16 = "acb7d7cfb390be20" +pinned_at = "2026-05-17T14:52:08+00:00" + +# --------------------------------------------------------------------------- +# Canonical-sync tooling (the meta-recursion: these pin themselves) +# --------------------------------------------------------------------------- + +[[pins]] +id = "canonical-sync-v1" +canonical_source = "corviduo-project-template" +canonical_path = "scripts/canonical_sync.py" +consumer_path = "scripts/canonical_sync.py" +pinned_sha256_16 = "d2b2c27b2750c6d5" +pinned_at = "2026-05-17T05:30:00+00:00" + +[[pins]] +id = "canonical-drift-v1" +canonical_source = "corviduo-project-template" +canonical_path = "scripts/canonical_drift.py" +consumer_path = "scripts/canonical_drift.py" +pinned_sha256_16 = "71281ac8dad7d6c0" +pinned_at = "2026-05-17T05:30:00+00:00" + +# --------------------------------------------------------------------------- +# Contract meta-tooling (Python ≥3.11, stdlib-only; invoke via uv run in +# non-Python projects) +# --------------------------------------------------------------------------- + +[[pins]] +id = "contract-parser-v2" +canonical_source = "corviduo-project-template" +canonical_path = "docs/contracts/contract_parser.py" +consumer_path = "docs/contracts/contract_parser.py" +pinned_sha256_16 = "f1fdfdb6914c7b20" +pinned_at = "2026-05-17T05:30:00+00:00" + +[[pins]] +id = "contract-drift-check-v1" +canonical_source = "corviduo-project-template" +canonical_path = "scripts/contract_drift_check.py" +consumer_path = "scripts/contract_drift_check.py" +pinned_sha256_16 = "23271287ac488da4" +pinned_at = "2026-05-17T05:30:00+00:00" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..259a16f --- /dev/null +++ b/.gitignore @@ -0,0 +1,114 @@ +# Local environment variables (contains secrets) +env.sh + +# Sleipnir per-project config (per-checkout overrides; user-global lives at ~/.config/sleipnir/) +.sleipnir/config.yaml + +# Workspace data +data/ + +# Logs +logs/ + +# ---> Python +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# Ruff +.ruff_cache/ + +# uv / pdm / poetry caches +.pdm-python +__pypackages__/ + +# IDEs +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db diff --git a/.sleipnir/config.yaml.template b/.sleipnir/config.yaml.template new file mode 100644 index 0000000..90a61b0 --- /dev/null +++ b/.sleipnir/config.yaml.template @@ -0,0 +1,54 @@ +# Sleipnir per-project config for ratatoskr. +# +# Copy to .sleipnir/config.yaml (gitignored) and adjust per-checkout. +# User-global config lives at ~/.config/sleipnir/config.yaml. +# +# Schema verified against: +# src/sleipnir/cli.py:505-528 (issue_tracker instantiation) +# src/sleipnir/orchestrator.py:367-368 (stages.plan + stages.verifier) +# src/sleipnir/jobs.py:9 (VALID_PHASES) +# tests/conftest.py:10-32 (gold-standard config shape) + +stages: + plan: + provider: claude_code_oneshot + model: claude-sonnet-4-6 + verifier: + provider: openai_compat + model: glm-5.1 + implement: + provider: claude_code_oneshot + model: claude-sonnet-4-6 + review: + provider: claude_code_oneshot + model: claude-opus-4-7 + +providers: + openai_compat: + base_url: https://api.z.ai/api/coding/paas/v4 + api_key_env: Z_AI_API_KEY + timeout_seconds: 3000 + claude_code_oneshot: + timeout_seconds: 900 + # Plug in the issue tracker once ratatoskr is registered with Gitea: + # ratatoskr_issues: + # repo: /home/lkraven/development/ratatoskr + +# sandbox_provider: docker +# issue_tracker: ratatoskr_issues +agent_runtime: claude_code + +passthrough_env: + - Z_AI_API_KEY + - GITEA_TOKEN + - ANTHROPIC_API_KEY + - TASK_BOARD_URL + +concurrency: 1 +max_rounds: 3 + +dispatch: + dependency_aware: true # INV-029: hold dependents at intake + # when their `dependencies:` frontmatter + # block points at issues that are open / + # closed-but-not-on-main / orphaned-without-merge. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..301c49f --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,372 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Principles + +`PRINCIPLES.md` at the repo root carries the four Corviduo engineering +principles (Excellence over uniqueness · Explicit over implicit · +Elegance is a byproduct · Action-relevance over thoroughness). These +are *what to optimize for* across all work — distinct from this file's +behavioral guidelines (*how to operate session-to-session*) and the +workflow mechanics below (*project-level mechanics*). The principles +also live at `~/.claude/CLAUDE.md` § Core principles; this repo copy +is the load-bearing version when human contributors or non-Claude-Code +AI tooling read the codebase. + +## Canonical Corviduo specifications + +This project depends on cross-cutting Corviduo specifications that +live canonically at `~/development/corviduo-project-template/` (the +spec-stewardship repository, Brokkr-managed as of 2026-05-15). When +this file is read inside the template repository itself, the +canonicals live here directly; when seeded into a derivative project, +pinned copies of the canonicals live in the derivative. + +### What lives canonically in the template + +| Spec | Canonical path | Covers | +|---|---|---| +| `CONTRACT-FORMAT.md` | `docs/contracts/CONTRACT-FORMAT.md` | Format spec for `.contract.md` files (v2.x). | +| `contract_parser.py` | `docs/contracts/contract_parser.py` | Validator/extractor for `.contract.md`. Python ≥3.11, stdlib only. | +| `contract_drift_check.py` | `scripts/contract_drift_check.py` | Verifies issue-scoped contracts' `prd:` block hashes match live tracker state. | +| `PRINCIPLES.md` | `PRINCIPLES.md` | Four engineering principles. | +| `methodology.md` | `docs/methodology.md` | The Corviduo preferred development pipeline ((optional) Vor → (required) Contract → branch (direct or AFK TDD) → verify → merge/commit). | +| `PRACTICES.md` | (to migrate from `galdrabok/conventions/`) | Engineering practices (contract-first, TDD, no-backwards-compat, atomic commits). | +| `CONTEXT-FORMAT.md` | (to migrate from `galdrabok/conventions/`) | Format spec for `CONTEXT.md` glossary files. | +| `ADR-FORMAT.md` | (to migrate from `galdrabok/conventions/`) | Format spec for Architecture Decision Records. | + +### What to seed into a new project, by stack + +When you seed a new Corviduo project from this template, copy the +canonical files that apply to your target stack. The drift-check +mechanism (below) keeps consumer copies in sync as canonicals evolve. + +**Every project (cross-language baseline):** + +- `CONTRACT-FORMAT.md`, `PRINCIPLES.md`, `PRACTICES.md` — applies + regardless of implementation language. +- `contract_parser.py`, `contract_drift_check.py` — Python + meta-tooling. Invoke via `uv run python scripts/...` in non-Python + projects. + +**Python projects** (Worldtree, Sleipnir, Brokkr-Smithy, Skaldsong, +Vor backend): + +- All baseline files. +- `env.sh.template` (Python-shape: `PYTHONPATH`, uv-managed venv + expectations). +- `.gitignore` (Python-shape: `__pycache__/`, `.venv/`, ruff / + mypy / pytest / uv caches). +- **Preferred tooling**: `uv` exclusively (`uv venv`, `uv pip + install`, `uv run`, `uv tool install`). Ruff for lint. Mypy for + types. Pytest for tests. No `pip` / `pipx` / `poetry` unless + the project clearly already uses something else. + +**TypeScript / Bun projects** (Mead Hall, Vor UI, future client +apps): + +- All baseline files. `CONTRACT-FORMAT.md` applies cross-language — + `.contract.md` files describe code regardless of implementation + language. +- `env.sh.template` (TS/Bun-shape: project-name-uppercase root, + per-app env vars, no Python venv references). +- `.gitignore` (TS/Bun-shape: `node_modules/`, `dist/`, + `bun.lockb` is committed; no Python caches). +- **Preferred tooling**: Bun exclusively (no npm / yarn / pnpm). + Bun's native bundler. Bun's native test runner (`bun test`). + TypeScript strict mode. React + Vite for UI projects where + applicable. + +**Mixed-stack projects** (Python backend + TypeScript frontend in +one repo): take both stacks' files; document which directory is +which in `docs/architecture.md`. + +### Keeping canonicals in sync + +Two tools at `scripts/` handle drift detection and sync for +cross-cutting Corviduo canonicals. Both Python ≥3.11 stdlib-only; +invoke via `uv run python scripts/...` in non-Python projects. + +- **`scripts/canonical_sync.py`** — operator-triggered. Fetches each + pinned canonical from `~/development//`, + writes the consumer copy, bumps the pin's `pinned_sha256_16` + + `pinned_at` in the manifest. Reports per-pin: `OK` / `SYNCED` / + `WOULD` (dry-run) / `ERROR`. Run after the architect broadcasts a + canonical update on althing. +- **`scripts/canonical_drift.py`** — read-only verifier suitable for + CI gating. Detects drift between pinned canonicals and their + sources, and consumer-copy divergence from the pinned hash. Exits + non-zero on staleness, divergence, or missing canonical. + +Each consumer carries a `.corviduo-canonicals.toml` manifest at the +repo root pinning each canonical: + +```toml +[[pins]] +id = "contract-format-v2" +canonical_source = "corviduo-project-template" +canonical_path = "docs/contracts/CONTRACT-FORMAT.md" +consumer_path = "docs/contracts/CONTRACT-FORMAT.md" +pinned_sha256_16 = "..." +pinned_at = "2026-05-15T..." +``` + +The pattern generalises beyond template-only canonicals. Same shape +handles Worldtree's `conversation-api-client-spec-v2.md`, Bifrost's +protocol spec, and any future cross-cutting canonical — set +`canonical_source` to whichever `~/development//` directory +hosts the truth. + +Optional per-pin `tolerate_drift = true` makes `canonical_drift.py` +warn-only for that pin when `--allow-warn` is set (useful for pins +the consumer deliberately allows to lag). + +## Persistent memory + +`persistent-memory.md` at the repo root captures durable intent and +supporting evidence (goals, decisions, foot-gun warnings, in-flight +state) across context resets. Read it at session start; treat it as +one input alongside this CLAUDE.md and the auto-memory system, not +as the single source of truth. When durable state shifts enough to +warrant capture, run `/snapshot` and commit alongside the next commit. + +## Architecture map + +`docs/architecture.md` is the top-level module map (dependency graph, +session-load boundaries, execution order). Each core module gets its own +`CLAUDE.md` at `core/{module}/CLAUDE.md` once it grows past trivial. +When a development session focuses on one module, load that module's +`CLAUDE.md` plus the stable-infrastructure dependencies listed there. + +## BEHAVIORAL GUIDELINES + +**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment. + +### 1. Think Before Coding + +**Don't assume. Don't hide confusion. Surface tradeoffs.** + +Before implementing: +- State your assumptions explicitly. If uncertain, ask. +- If multiple interpretations exist, present them — don't pick silently. +- If a simpler approach exists, say so. Push back when warranted. +- If something is unclear, stop. Name what's confusing. Ask. + +### 2. Simplicity First + +**Minimum code that solves the problem. Nothing speculative.** + +- No features beyond what was asked. +- No abstractions for single-use code. +- No "flexibility" or "configurability" that wasn't requested. +- No error handling for impossible scenarios. +- If you write 200 lines and it could be 50, rewrite it. + +Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. + +### 3. Surgical Changes + +**Touch only what you must. Clean up only your own mess.** + +When editing existing code: +- Don't "improve" adjacent code, comments, or formatting. +- Don't refactor things that aren't broken. +- Match existing style, even if you'd do it differently. +- If you notice unrelated dead code, mention it — don't delete it. + +When your changes create orphans: +- Remove imports/variables/functions that YOUR changes made unused. +- Don't remove pre-existing dead code unless asked. + +The test: Every changed line should trace directly to the user's request. + +### 4. Goal-Driven Execution + +**Define success criteria. Loop until verified.** + +Transform tasks into verifiable goals: +- "Add validation" → "Write tests for invalid inputs, then make them pass" +- "Fix the bug" → "Write a test that reproduces it, then make it pass" +- "Refactor X" → "Ensure tests pass before and after" + +For multi-step tasks, state a brief plan: +``` +1. [Step] → verify: [check] +2. [Step] → verify: [check] +3. [Step] → verify: [check] +``` + +Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification. + +--- + +## Project conventions + +### Naming hierarchy + +``` +Corviduo.ai (company) +└─ ratatoskr (project / repo — this codebase) + └─ ... (modules, agents, components — fill in as the project grows) +``` + +Sibling Corviduo projects: `Worldtree` (multi-agent framework), `Skaldsong` (story-generation engine), `vastai-app` (Vast Blue SEA — multi-agent web app), `sleipnir` (AFK orchestrator), `Vor` (pre-contract design partner UI), `Galdrabok` (Sleipnir-preflight skill author), `task-board` (per-session web UI), `althing`. + +Norse / Germanic naming convention is the house style for Corviduo projects. Module and agent names follow the same vein. + +### No backwards compatibility until v1.x + +This project is pre-release. When something is redesigned, everything gets updated and everything old gets ripped out. **No shims, no compat layers, no deprecation periods.** If a new approach replaces an old one, the old code is deleted and all references are updated in the same commit. This keeps the codebase clean and avoids accumulating dead paths. + +### Contract-first workflow + +Contracts at `docs/contracts/` are machine-parseable architectural pseudocode specs. They serve as unambiguous work specifications: a planning pass writes the contract, then the implementation pass translates it to code, then an audit pass verifies compliance. + +**Workflow: contracts come before code.** When implementing a new module or making non-trivial changes to an existing one, write or update the `.contract.md` file first. The contract captures function signatures, preconditions, postconditions, error handling, steps, and invariants — settling design questions before writing code. Contracts are not documentation of existing code; they are the source of truth that code is written to satisfy. + +- Format spec: [`docs/contracts/CONTRACT-FORMAT.md`](docs/contracts/CONTRACT-FORMAT.md) (v2.1 is current; v2.0 and v1 contracts remain accepted) +- Parser: [`docs/contracts/contract_parser.py`](docs/contracts/contract_parser.py) — `--validate` checks required fields; `--list` shows function signatures; `--dir` batch processes +- Drift checker: [`scripts/contract_drift_check.py`](scripts/contract_drift_check.py) — verifies issue-scoped contracts' `prd:` block hashes match the live tracker state +- File convention: `docs/contracts/{module_name}.contract.md` for module contracts; `docs/contracts/issues/{N}.contract.md` for issue-scoped contracts + +When to write a contract: +- **New module** — always write a contract before implementation +- **Complex changes** — new function blocks, new error handling, new state transitions +- **Cross-module interfaces** — when the change affects `depends_on` / `used_by` relationships + +When a contract is not needed: +- Bug fixes that don't change function signatures or behavior +- Trivial helpers, one-liners, config changes +- Documentation-only changes + +### Issue → AFK dispatch hygiene (Sleipnir) + +Before marking any issue `ready-for-agent` (the label that gates AFK +dispatch via Sleipnir), it MUST have an issue-numbered contract at +`docs/contracts/issues/.contract.md` AND that contract MUST carry +a `prd:` block in its YAML frontmatter pinning it to: + +- the issue body (SHA-256, first 16 hex chars) +- the lock-in comment id + content SHA (or `null` if the issue body + alone is the spec) +- a `pinned_at` timestamp + +This is hard policy, not a suggestion. Without the contract Sleipnir's +gate refuses dispatch with `blocked-needs-contract`. Without the `prd:` +block PRD-↔-ship drift becomes invisible — the audit infrastructure +that detects it is a no-op. + +Verify before applying the label: + +- contract file exists at the expected path +- frontmatter has a populated `prd:` block +- `python scripts/contract_drift_check.py --contract ` returns clean + +The full convention — frontmatter shape, required fields, reasoning +behind the four drift entry points — lives in +[`docs/contracts/CONTRACT-FORMAT.md`](docs/contracts/CONTRACT-FORMAT.md). + +### TDD development cycle + +Contract → failing test → minimal code to pass → run test. Tests written +before/alongside code, never deferred. Vertical slices (tracer bullet first, +then incremental loop), not horizontal slices (don't write all tests then all +code — that produces tests of *imagined* behavior). + +### Persistent-memory + housekeeping commit-along rule + +When committing changes, also commit `persistent-memory.md` if it's dirty. +Same applies to housekeeping files (`HANDOFF-SUMMARY.md`, `NEXT-STEPS.md`, +`README.md`, `State-Of-Development.md`) where they exist. The repo state +should always be coherent across these files at every commit. + +--- + +## Tooling conventions + +### Issues + +For Worldtree-and-friends Gitea-tracked issues, prefer `python -m core.issues` +(the project's own CLI wrapping the Gitea API) over `gh` or `tea`. Until +this project grows its own `core/issues/` adapter, fall back to `tea` +(local Gitea CLI at `/usr/local/bin/tea`, v0.14.0) for simple operations. + +### Tea CLI + +`tea` is the local Gitea CLI. Use for PRs, releases, branch/repo ops. For +issues prefer `python -m core.issues` when it exists. **Note:** +`tea issues edit --add-labels