personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs(agents): revamp AGENTS.md as a comprehensive coder guide

Full revamp of the repo-root AGENTS.md per vpe req_7szh7vce. Promotes
load-bearing invariants from docs/coding-standards.md inline so they
can't be skipped: L1–L9 layer hygiene (with the domain-ownership table
at the top), no-compat-shims, trust get_journal(), SPDX header, fail
loudly. Adds a start-here ordered reading list, a top-level repo map,
an annotated repo mental model, make-command coverage grouped by use
(37 targets with when-to-use notes), a testing quickstart, and an
annotated depth-doc map.

Relocates the misaudienced "Talent CLI Boundaries" runtime restrictions
out of AGENTS.md (coder audience) into talent/journal/references/cli.md
(cogitate audience, loaded via the journal skill).

Replaces the full Layer Hygiene section in docs/coding-standards.md
with a one-line redirect to AGENTS.md §7 — single source of truth for
the invariants, no drift.

Plan: vpe/workspace/solstone-agents-md-revamp-plan.md (extro repo).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

+314 -114
+293 -49
AGENTS.md
··· 1 - # solstone Developer Guide 1 + # solstone coder guide 2 + 3 + This file is the **coder guide** for the solstone repository. Read it before writing code. 4 + 5 + Audience: 6 + 7 + - **Coders** (cwd = repo root, editing `observe/`, `think/`, `convey/`, `apps/`, `talent/`, `tests/`) — you're in the right place. 8 + - **Cogitate talents** (cwd = `journal/`, running inside the live system) — your entry is `talent/journal/SKILL.md`, installed into `journal/.claude/skills/journal/` and `journal/.agents/skills/journal/`. 9 + - **Operators** debugging a running system — see `docs/DOCTOR.md`. 10 + 11 + `CLAUDE.md` and `GEMINI.md` at the repo root are symlinks to this file. 12 + 13 + ## 1. Start here 14 + 15 + Read, in order, when you enter the repo for a coding task: 16 + 17 + 1. **This file through §8** — the invariants must be in working memory before your first edit. 18 + 2. **`sol.py`** — the CLI entry point. Skim the `COMMANDS`, `ALIASES`, and `GROUPS` dicts. ~340 lines, scannable in one pass. You now know the whole top-level command surface. 19 + 3. **`think/top.py` (first ~100 lines)** — the interactive TUI. Ties callosum + supervisor + service status together in one vantage point. Good "oh, this is how it connects" moment. 20 + 4. **The area you're about to touch:** 21 + - User-visible feature or `sol call <app> <verb>` → `apps/<name>/call.py` + `apps/<name>/routes.py` + `apps/<name>/templates/`. 22 + - Think pipeline → `think/<module>.py` + its tests. 23 + - AI talent prompt or behavior → `talent/<name>.md` (+ optional `.py` post-hook). 24 + - Capture / observe → `observe/<module>.py`. 25 + 5. **Run `sol`** (no args) — prints current journal status + grouped command list. Orients you to live state. 26 + 6. **`make dev`** or **`make sandbox`** when you need a running stack to iterate against. 27 + 28 + > If you cannot state in one sentence **which module owns the data your change touches**, stop and re-read §7 L2 (the domain ownership table). Writing to a domain from the wrong module is how we got the 14 layer violations the April 2026 audit catalogued. 29 + 30 + ## 2. Repo map 31 + 32 + | Dir | Purpose | Go here when | Depth doc | 33 + |-----|---------|--------------|-----------| 34 + | `sol.py` | CLI entry point — `COMMANDS` / `ALIASES` / `GROUPS` dicts | adding a top-level `sol <cmd>` | `docs/SOLCLI.md` | 35 + | `observe/` | Multimodal capture — screen, audio, transcribe, describe, sense, transfer | capture-side bugs, new input modalities | `docs/OBSERVE.md` | 36 + | `think/` | Post-processing core — cortex, talent, callosum, indexer, entities, facets, activities, scheduler, heartbeat, supervisor | anything downstream of capture; most coder work lives here | `docs/THINK.md`, `docs/CORTEX.md`, `docs/CALLOSUM.md` | 37 + | `convey/` | Web app framework — app discovery, routing, bridge, screenshot tooling | layout / framework-level UI changes | `docs/CONVEY.md` | 38 + | `apps/` | Convey apps — each self-contained (`call.py` Typer sub-app + `routes.py` + `templates/`) | adding a user-facing feature, a `sol call <app>` verb, a UI surface | `docs/APPS.md` (required reading before modifying `apps/`) | 39 + | `talent/` | AI talent configs (markdown prompts + optional `.py` post-hooks) + `SKILL.md`s (journal, coder, partner, decisionalizer, …) | defining or tuning a talent; adding a journal-side skill | `talent/journal/SKILL.md`, `docs/PROMPT_TEMPLATES.md` | 40 + | `scripts/` | Repo maintenance scripts — `check_layer_hygiene.py`, `gate_agents_rename.py` | tooling that guards the codebase; wired into `make ci` | (none) | 41 + | `tests/` | Pytest suites + `tests/fixtures/journal/` mock journal | writing tests; debugging flakiness; `make dev` / `make sandbox` use fixtures as the journal | `docs/testing.md` | 42 + | `docs/` | All longform documentation | reference lookups; never your first stop | §10 below | 43 + | `journal/` | The live journal (user data). Git-ignored content; checked-in template (`AGENTS.md`, skills symlinks) | **rarely as a coder** — modify `think/`, `apps/`, or `talent/`, not journal data | `talent/journal/SKILL.md` | 44 + 45 + Top-level dirs intentionally not in the table: `.venv/`, `scratch/`, `logs/`, `tmp/`, `observers/`, `routines/`, `skills/` — not active coder surfaces. 46 + 47 + ## 3. Mental model 48 + 49 + **The pipeline:** `observe` (capture) → JSON transcripts in `journal/chronicle/YYYYMMDD/` → `think` (analyze) → SQLite index + derived artifacts → `convey` (web UI) and `sol call` CLIs. 50 + 51 + **Think is the center.** observe feeds it raw material; convey + apps render its outputs; talent prompts + cortex run AI against it; indexer makes it searchable. A change in `think/` usually ripples outward. 52 + 53 + **Key concepts, priority-ordered:** 54 + 55 + - **Journal** — the on-disk record rooted at `journal/` in the repo. Every day is a `journal/chronicle/YYYYMMDD/` directory. Segments (timestamped capture windows) are anchored to creation/modification time, not content "about" time. `get_journal()` from `think.utils` is the single source of truth for journal path resolution; trust it unconditionally. Never set `_SOLSTONE_JOURNAL_OVERRIDE` from application code (see §8). 56 + - **Talents** — AI processors (markdown prompt + optional Python post-hook). Each has a config in `talent/<name>.md` with frontmatter that declares hooks, priority, model, and output. Cortex spawns them as subprocesses. 57 + - **Callosum** — Unix-socket JSON message bus at `journal/health/callosum.sock`. Real-time event distribution across services (`tract` + `event` + payload). If components need to talk asynchronously, they talk through callosum. 58 + - **Cortex** — process manager for talent runs. Listens on callosum (`tract="cortex"`, `event="request"`), spawns `python -m think.talents` subprocesses, writes `<talent>/<ts>_active.jsonl` then renames to `<talent>/<ts>.jsonl` on completion, broadcasts all events back through callosum. Read `docs/CORTEX.md` before modifying talent execution. 59 + - **Facets** — project/context scopes (`work`, `personal`, …). Group related entities, activities, and relationships. Facet data lives under `journal/facets/<facet>/`. 60 + - **Entities** — tracked people / projects / tools. Extracted from transcripts and accumulated across time. Canonical records in `journal/entities/<slug>/entity.json`. 61 + - **Activities** — scheduled or observed "things that happen" (meetings, deadlines, anticipated events). Per-facet JSONL at `journal/facets/<facet>/activities/<day>.jsonl`. Sources: `anticipated` (from `talent/schedule.md`), `user` (manual), `cogitate` (talent-inferred). 62 + - **Indexer** — reads journal state, builds SQLite + FTS5 index. **Never** mutates source data (§7 L6). Rerunning on unchanged data is a no-op. 63 + - **Supervisor** — top-level process manager. Starts/restarts services, talks to callosum. `sol supervisor` / `sol start`. 64 + 65 + ## 4. The sol CLI 66 + 67 + Two surfaces: 68 + 69 + - **`sol <command>`** — top-level commands registered in `sol.py`'s `COMMANDS` dict (e.g., `sol import`, `sol think`, `sol indexer`, `sol supervisor`, `sol heartbeat`). `ALIASES` provides a couple of shorthand compound commands (`sol start` → `sol supervisor`, `sol up/down` → `sol service up/down`). 70 + - **`sol call <app> <verb>`** — routes to `think/call.py`, which discovers each `apps/*/call.py` Typer sub-app and mounts it as a subcommand. Example: `sol call entities list`, `sol call activities create`, `sol call journal search`. 71 + 72 + **Adding a top-level command:** add an entry to `COMMANDS` in `sol.py`; ensure the module has a `main()` function. 73 + 74 + **Adding a `sol call` sub-verb:** add it to the app's `apps/<app>/call.py` Typer sub-app. No central registration needed — `think/call.py` discovers apps automatically. 75 + 76 + Run `sol` (no args) for live status plus the full grouped command list. 77 + 78 + ## 5. Make commands 79 + 80 + Verified against `Makefile`. Grouped by use. 81 + 82 + ### Install 83 + 84 + | Target | When to use | 85 + |--------|-------------| 86 + | `make install` | First setup and whenever `pyproject.toml` or `uv.lock` changes. Creates `.venv/`, syncs deps, installs Playwright chromium, runs `make skills`. | 87 + | `make skills` | After adding or renaming a `SKILL.md` under `talent/` or `apps/*/talent/`. Rewrites the `.claude/` + `.agents/` skill symlinks into `journal/`. (`make install` depends on this; rarely run alone.) | 88 + | `make update` | Upgrade all deps to latest, regenerate `uv.lock`. Expect test churn. | 89 + | `make update-prices` | Refresh genai-prices model-cost data when adding a new provider model or when pricing tests fail. | 90 + | `make clean` | Remove build artifacts, caches, and the skill symlinks. Does not touch `.venv/`. | 91 + | `make clean-install` | Nuke `.venv/` and `.installed`, then reinstall. Recovery path when the venv is wedged. | 92 + 93 + ### Run the stack 2 94 3 - This is the developer-facing documentation for the solstone codebase. If you're an AI agent working **inside a journal**, read the journal template at `journal/AGENTS.md`; journal-side agents discover the full layout and CLI reference via `.claude/skills/journal/` or `.agents/skills/journal/`. 95 + | Target | When to use | 96 + |--------|-------------| 97 + | `make dev` | Start the full stack (supervisor + callosum + sense + cortex + convey) against `tests/fixtures/journal/`, no observers, no daily processing. Primary inner-loop for UI work. Ctrl-C to stop. | 98 + | `make sandbox` | Ephemeral background sandbox: copies fixtures to a temp journal, starts supervisor in the background, waits for readiness, writes `.sandbox.pid` / `.sandbox.journal`. Pair with verify targets below. Always follow with `make sandbox-stop`. | 99 + | `make sandbox-stop` | Terminate the backgrounded sandbox and clean up state files. | 100 + | `make sail` | Restart the **installed** solstone service via `sol service restart --if-installed`. No-op when no service is installed (typical in a dev worktree). | 101 + 102 + ### Format, lint, test 103 + 104 + | Target | When to use | 105 + |--------|-------------| 106 + | `make format` | Auto-fix formatting and imports with ruff. Safe to run anytime; modifies files. | 107 + | `make format-check` | Format dry-run. Part of `make ci`; rarely run alone. | 108 + | `make test` | Unit tests (`tests/`). Format-check runs first; failures block tests. Fast inner loop. | 109 + | `make test-apps` | Run all `apps/*/tests/` suites. | 110 + | `make test-app APP=<name>` | Run a single app's tests. | 111 + | `make test-only TEST=<path-or-pattern>` | Run a specific test file or pytest node id (`TEST="-k test_name"` also works). | 112 + | `make test-integration` | Full integration suite. Requires `.env` API keys. Slow; run before shipping AI-behavior changes. | 113 + | `make test-integration-only TEST=<path>` | Single integration test by path or pattern. | 114 + | `make test-all` | Everything — core + apps + integration. Pre-ship gate. | 115 + | `make coverage` | HTML coverage report under `htmlcov/`. Occasional. | 116 + | `make watch` | pytest-watch — reruns tests on file change. Useful during a test-heavy sprint. | 117 + | `make ci` | Format-check + ruff + agents-rename gate + layer-hygiene + tests. **Run before every commit.** | 118 + | `make verify` | Same steps as `make ci`. Either name is fine. | 119 + | `make install-checks` | The pre-test half of `make ci` (format-check + ruff + gates). Called by `ci` / `verify`. | 120 + | `make check-layer-hygiene` | Run `scripts/check_layer_hygiene.py` alone. Useful when iterating on an L1–L2 violation flagged by CI. | 121 + | `make gate-agents-rename` | Guard against reintroducing the old `agents/` naming. Part of `install-checks`. | 4 122 5 - ## Key Concepts 123 + ### Verification against a running sandbox 6 124 7 - - **Journal**: Central data structure organized as `journal/YYYYMMDD/` directories. All captured data, transcripts, and analysis artifacts are stored here. 8 - - **Facets**: Project/context organization system that groups related content and provides scoped views of entities, tasks, and activities. 9 - - **Entities**: Extracted information tracked over time across transcripts and interactions and associated with facets for semantic navigation. 10 - - **Talents**: AI processors with configurable prompts that analyze content, extract insights, and respond to queries. 11 - - **Callosum**: Message bus that enables asynchronous communication between components. 12 - - **Indexer**: Builds and maintains a SQLite database from journal data, enabling fast search and retrieval. 125 + | Target | When to use | 126 + |--------|-------------| 127 + | `make verify-api` | Start a sandbox, run `tests/verify_api.py` against its convey port, stop the sandbox. API-regression check. | 128 + | `make update-api-baselines` | Same, but update the baseline fixtures instead of failing on diff. Run after intentional API changes. | 129 + | `make verify-browser` | Start a sandbox, run `tests/verify_browser.py` (pinchtab-driven browser scenarios), stop the sandbox. UI-regression check. | 130 + | `make update-browser-baselines` | Browser-baselines equivalent of `update-api-baselines`. | 131 + | `make review` | Full product verification: sandbox + API verify + browser verify, in one command. Pre-ship gate for anything user-visible. Requires pinchtab. | 132 + | `make install-pinchtab` | One-time install of the pinchtab browser driver used by `make review` / `make verify-browser`. | 13 133 14 - ## Architecture 134 + ### Service management (systemd / launchd) 15 135 16 - **Core pipeline**: `observe` (capture) -> JSON transcripts -> `think` (analyze) -> SQLite index -> `convey` (web UI) 136 + | Target | When to use | 137 + |--------|-------------| 138 + | `make install-service` | Install `sol` as a systemd user service (Linux) or launchd agent (macOS), convey on port 5015 (override with `PORT=8000`). Makes the machine a live solstone host — rarely wanted in a worktree. | 139 + | `make uninstall-service` | Remove the installed service. | 140 + | `make service-logs` | Tail the installed service's logs. | 17 141 18 - **Data organization**: 19 - - Everything lives under `journal/YYYYMMDD/` daily directories. 20 - - Import segments are anchored to creation/modification time, not content "about" time. 21 - - Facets provide project-scoped organization and filtering. 22 - - Entities are extracted from transcripts and tracked across time. 23 - - The indexer builds a SQLite database for fast search and retrieval. 142 + ### Other 24 143 25 - **Component communication**: 26 - - Callosum enables async communication between services. 27 - - Cortex orchestrates AI talent execution via `sol cortex`, spawning talent subprocesses with talent configurations. 28 - - The unified CLI is `sol`. Run `sol` to see status and available commands. 144 + | Target | When to use | 145 + |--------|-------------| 146 + | `make pre-commit` | Install pre-commit hooks (optional; most coders rely on `make ci` directly). | 147 + | `make versions` | Print versions of Python, uv, and key deps. Diagnostic. | 29 148 30 - ## Quick Commands 149 + ### Don't use 31 150 32 - ```bash 33 - make install # Set up the repo-local dev environment and dependencies 34 - make skills # Discover and symlink Anthropic Skills from talent/ dirs 35 - make format # Auto-fix formatting, then report remaining issues 36 - make test # Run unit tests 37 - make ci # Full CI check (format check + lint + test) 38 - make dev # Start stack (Ctrl+C to stop) 151 + | Target | Why not | 152 + |--------|---------| 153 + | `make uninstall` | Disabled by design. Use `make uninstall-service` (for installed artifacts) or `make clean-install` (to rebuild the dev env). | 154 + 155 + ## 6. Testing quickstart 156 + 157 + - **Framework:** pytest. Files `test_*.py`, functions `test_*`. Shared fixtures in `tests/conftest.py`. 158 + - **Fixture journal:** `tests/fixtures/journal/` — a complete mock journal with facets, entities, segments, index state. Tests set `os.environ["_SOLSTONE_JOURNAL_OVERRIDE"] = "tests/fixtures/journal"` (or use `monkeypatch.setenv`); this is the **only** place that env var is valid (see §8). 159 + - **Run one test:** `make test-only TEST=tests/test_utils.py::test_foo` or `TEST="-k test_foo"`. 160 + - **Run app tests:** `make test-apps` or `make test-app APP=<name>`. 161 + - **Integration tests** (`tests/integration/`): hit real provider APIs, require `.env` keys, run via `make test-integration`. 162 + - **After editing `convey/` or `apps/`:** `sol restart-convey` to reload code in a running stack. 163 + - **Screenshots for UI review:** `sol screenshot <route>` (captures into `scratch/`). 164 + - **`make dev` + `make sandbox`** both write runtime artifacts into the fixtures journal; `tests/fixtures/journal/.gitignore` covers those — never commit them. 165 + 166 + Full depth: `docs/testing.md`. 167 + 168 + ## 7. Layer hygiene — required reading (L1–L9) 169 + 170 + **Why this lives here.** A codebase-wide audit in April 2026 found 14 layer-hygiene violations in `think/` and `apps/`. Infrastructure modules (indexer, importers, schedulers) were silently writing domain state; CLI read-verbs were mutating; get-prefixed functions were creating records on miss. These invariants encode the rules the audit distilled, so the same landmines don't get re-planted. They're inlined here because a one-click-away invariant is a routinely-skipped invariant. 171 + 172 + The low-bar grep enforcement is `scripts/check_layer_hygiene.py`, wired into `make ci`. Known audit-flagged files are allowlisted with audit-reference TODOs; the allowlist shrinks as remediation bundles ship. 173 + 174 + ### L1 — Layer boundaries are load-bearing 175 + 176 + Each module family has a declared responsibility. Infrastructure modules (indexer, importer, scheduler, search, graph, stats) may write **only their own output artifacts**. They may not create, modify, or delete domain state (entities, facets, observations, activities, events, chronicle day content). If an infrastructure module needs to trigger a domain mutation, it emits a callosum event or invokes a `sol call <domain> <verb>` subprocess — never writes domain state directly. 177 + 178 + ### L2 — Domain write ownership 179 + 180 + Each domain has exactly **one** write-owning module (or one tightly-scoped family of modules). No other module may call `atomic_write`, `json.dump`, `open("w")`, `Path.write_text`, `unlink`, `rmtree`, etc. on that domain's on-disk state. 181 + 182 + | Domain | Write-owning module(s) | 183 + |--------|------------------------| 184 + | Entities (`entities/*/entity.json`, `entities/*/*.npz`) | `think/entities/journal.py` + `think/entities/consolidation.py` + `think/entities/saving.py` + `think/entities/merge.py` + `apps/entities/call.py` | 185 + | Facets (`facets/*/facet.json`, `facets/*/relationships/`) | `think/facets.py` + `apps/facets/*` (if/when created) | 186 + | Observations (`observations.jsonl`) | `think/entities/observations.py` | 187 + | Activities (`facets/*/activities/*.jsonl`) | `think/activities.py` | 188 + | Chronicle day content (`chronicle/YYYYMMDD/**`) | The capturing module (observer, importer) per its declared outputs | 189 + | Index (SQLite, `indexer/*`) | `think/indexer/*` | 190 + 191 + If you're about to write to a domain from a module not in this table, stop and route through the owner. 192 + 193 + ### L3 — Naming is a contract 194 + 195 + Function and CLI-subcommand verbs signal read vs. write intent. 196 + 197 + **Read verbs** (functions and CLI subcommands): `load_*`, `get_*`, `read_*`, `scan_*`, `list_*`, `show_*`, `find_*`, `match_*`, `resolve_*`, `query_*`, `lookup_*`, `status_*`, `check_*`, `validate_*`, `discover_*`, `format_*`, `render_*`, `extract_*`, `parse_*`, `view_*`, `inspect_*`, `info_*`, `describe_*`, `search_*`. 198 + 199 + A read-verb function must not mutate on-disk state. No exceptions for caches. No exceptions for "create on miss." 200 + 201 + If a function needs create-on-miss semantics, split it: 202 + 203 + ```python 204 + entity = load_entity(eid) or create_entity(eid, ...) 39 205 ``` 40 206 41 - ## Talent CLI Boundaries 207 + This makes the write visible at every call site. 208 + 209 + **Write verbs** are the ones allowed to write — choose the right one: `save_`, `create_`, `add_`, `insert_`, `append_`, `attach_`, `delete_`, `remove_`, `update_`, `rename_`, `move_`, `promote_`, `merge_`, `seed_`, `consolidate_`, `bootstrap_`, `backfill_`, `dispatch_`, `record_`, `ingest_`, `import_`, `rebuild_`. 210 + 211 + ### L4 — CLI read-verbs are read-only 212 + 213 + CLI subcommands with read verbs (list, show, status, get, search, find, check, validate, discover, inspect, info, describe, read, view) must not write to journal domain state under any flag combination. If a command needs a write path, split it into two commands — a read-verb reader and a write-verb writer. 214 + 215 + ### L5 — Write-verb defaults 216 + 217 + CLI subcommands with write verbs default to safe. 218 + 219 + - Preferred: no default mutation; an explicit `--commit` (or `--apply`) flag is required to perform the write. 220 + - Acceptable alternative: `--dry-run` defaulting to `False` *only if* the subcommand name is unambiguously a write verb AND the command's primary user journey is the write (e.g., `sol call entities create`). 221 + 222 + "Bootstrap", "backfill", and "resolve-names" are not unambiguous — default them to dry-run. 223 + 224 + ### L6 — Indexers never mutate source data 225 + 226 + An indexer's job is to build indexes from source-of-truth data. Indexers may not mutate the source data they read. Re-running `sol indexer --rescan` on an unchanged journal must be a no-op for domain state. 227 + 228 + ### L7 — Importers only write to imports/ 229 + 230 + Importers write source material to `imports/` and the raw-content areas of `chronicle/`. They may not create or modify entities, facets, observations, or other cross-cutting state. If an importer needs to create an entity for deduplication, it calls a domain-owned `seed_entity()` function in `think/entities/` that surfaces the write explicitly. 231 + 232 + ### L8 — Hooks have declared outputs 233 + 234 + Post-processing hooks (`think/hooks.py`, `talent/*.py` hook functions) declare every path they will write in their frontmatter. The hook runner validates that all actual writes match the declaration. Writes outside the declared set fail loudly — raise at runtime; assert in tests. 235 + 236 + ### L9 — Event handlers are idempotent 237 + 238 + Any function that handles a callosum event, a scheduled tick, or a supervisor-started automation is idempotent w.r.t. on-disk state. Append-only history records dedupe by a natural key (usually `(day, segment)` or `(day, segment, ts)`). Before adding a write to an event handler, ask: "what happens if this event fires twice?" 239 + 240 + ## 8. Coding invariants 241 + 242 + The rules above govern *where* code lives. The rules below govern *how* code behaves. They exist because we got burned. 243 + 244 + - **No backwards-compatibility shims.** All code that depends on this project lives in this repository — never add fallback aliases, re-exports for moved symbols, deprecated-parameter handling, or legacy support code. When renaming or removing something, update every usage directly. For journal data-format changes, write a migration script (see `docs/APPS.md` for `maint` commands); do not add a compatibility layer. Cogitate agents default to adding shims; resist this. 245 + - **Trust `get_journal()` unconditionally.** `get_journal()` from `think.utils` is the single source of truth for journal path resolution. **Never** set `_SOLSTONE_JOURNAL_OVERRIDE` from application code, agent prompts, subprocess environments, or service files. The env var exists exclusively for two external contexts: test harnesses (`monkeypatch.setenv`) and Makefile sandboxes. If you think you need to override the path from app code, you don't — fix the actual problem. See `docs/environment.md`. 246 + - **SPDX header on every source file.** All Python (and other source) files begin with: 247 + 248 + ```python 249 + # SPDX-License-Identifier: AGPL-3.0-only 250 + # Copyright (c) 2026 sol pbc 251 + ``` 252 + 253 + (`//` for JavaScript.) Markdown, text, and prompt files don't need it. 254 + - **Fail loudly, not silently.** Raise specific exceptions with clear messages; use the `logging` module, not `print`. Validate inputs at module boundaries. A silent swallow in production costs days of forensics — an error at the boundary is free. 255 + - **Trust internal code.** Don't add defensive validation for things internal callers can't violate. Validate at system boundaries (user input, external APIs, imported files) — not between modules you control. 256 + 257 + Generic software principles (DRY, KISS, YAGNI, single responsibility, small focused commits) apply; see `docs/coding-standards.md` for the full list. 258 + 259 + ## 9. File headers, naming, dependencies 260 + 261 + - **SPDX header** as above — mandatory on source code files. 262 + - **Naming:** modules / functions / variables `snake_case`; classes `PascalCase`; constants `UPPER_SNAKE_CASE`; private members `_leading_underscore`. Full table in `docs/coding-standards.md`. 263 + - **Imports:** prefer absolute (`from think.utils import get_journal`), grouped stdlib → third-party → local, one per line. 264 + - **Type hints** on function signatures; `mypy` via `make check`. 265 + - **Dependencies:** managed by [uv](https://docs.astral.sh/uv/). `pyproject.toml` is authoritative; `uv.lock` is committed; `make install` syncs; `make update` refreshes. 266 + - **Python 3.10+.** 267 + 268 + ## 10. Commit hygiene 269 + 270 + - Small, focused commits with descriptive messages. 271 + - Run `make ci` before every commit. 272 + - Run `git` commands directly — not `git -C` — you're already in the repo. 273 + - Don't commit runtime artifacts written under `tests/fixtures/journal/` by `make dev` / `make sandbox` (`.gitignore` covers them; verify with `git status` anyway). 42 274 43 - Cogitate talents have access to all `sol` commands. The following infrastructure commands must never be called by talents because they manage services and data pipelines that should only be operated by the supervisor or a human operator: 275 + ## 11. Where to go deeper 44 276 45 - - `sol supervisor` / `sol start` 46 - - `sol think` except heartbeat's targeted `sol think --segment` 47 - - `sol import` 48 - - `sol config` 49 - - `sol cortex` 50 - - `sol providers check` 51 - - `sol callosum` 52 - - `sol observer` / `sol observe-*` 53 - - `sol sense` 54 - - `sol transcribe` / `sol describe` 55 - - `sol indexer --reset` 277 + Bare links don't motivate clicking. Each entry below says when you actually need the doc. 278 + 279 + | Doc | When to read | 280 + |-----|--------------| 281 + | `docs/APPS.md` | **Required before modifying `apps/`** — pattern catalog for Convey apps, hook-idempotency guidance, Typer sub-app conventions, `maint` commands for data migrations | 282 + | `docs/THINK.md` | Understanding the think-layer pipeline (importers, indexer, segment/stream processing) | 283 + | `docs/CORTEX.md` | Modifying talent execution, cortex lifecycle, talent process management | 284 + | `docs/CALLOSUM.md` | Adding a new tract/event, debugging message flow | 285 + | `docs/CONVEY.md` | Framework-level web changes (as opposed to an individual app) | 286 + | `docs/OBSERVE.md` | Capture-side work: new modalities, transcription, sensing | 287 + | `docs/SOLCLI.md` | Adding a new `sol <cmd>` or `sol call <app> <verb>` | 288 + | `docs/PROMPT_TEMPLATES.md` | Modifying talent prompt format or frontmatter | 289 + | `docs/PROVIDERS.md` | Adding a new AI provider; debugging model selection | 290 + | `docs/testing.md` | Writing integration tests; setting up fixtures; debugging test isolation | 291 + | `docs/environment.md` | Journal path resolution, service install details, `_SOLSTONE_JOURNAL_OVERRIDE` rules | 292 + | `docs/coding-standards.md` | Full naming conventions, ruff / mypy config, dep-management details — reference for everything not promoted into this file | 293 + | `docs/project-structure.md` | Canonical directory layout; resolving "where does this file go" debates | 294 + | `docs/DOCTOR.md` | Diagnostics and debugging a running system | 295 + | `docs/SCREEN_CATEGORIES.md` | Screen-understanding classifier taxonomy (observe side) | 296 + | `docs/INTEGRATION_TESTS.md` | Deep integration-test setup | 297 + | `docs/VENDOR.md` | Vendor-level integrations | 298 + | `docs/design/` | Per-subsystem design docs | 299 + | `docs/JOURNAL.md` | **Breadcrumb only** — redirects to `talent/journal/SKILL.md`, the progressive-disclosure journal-layout reference | 300 + | `talent/journal/SKILL.md` | Journal layout, vocabulary, and `sol call journal` CLI (loaded by cogitate talents on demand via skills) | 301 + | `talent/journal/references/cli.md` | Full `sol call journal` reference, including **Talent CLI Boundaries** (which infrastructure commands cogitate talents must not call) | 56 302 57 - Talents should use `sol call` commands for journal interaction and `sol health` / `sol talent logs` for diagnostics. 303 + `docs/BACKLOG.md` and `docs/ROADMAP.md` are product-planning docs — CPO/CEO reading, not coder reading. 58 304 59 - ## Reference 305 + ## 12. What this file is NOT 60 306 61 - For deeper material, see: 62 - - `docs/project-structure.md` 63 - - `docs/coding-standards.md` 64 - - `docs/testing.md` 65 - - `docs/environment.md` 307 + - **Not a runtime guide for cogitate talents.** Runtime CLI restrictions on talents live in `talent/journal/references/cli.md` § Talent CLI Boundaries. If you're tuning what a talent can or cannot call, look there, not here. 308 + - **Not the journal-layout reference.** `talent/journal/SKILL.md` + its `references/` is the cogitate-audience entry point. This file describes *how those commands are implemented*, not *which ones talents can't call*. 309 + - **Not an operations manual.** For debugging a live system see `docs/DOCTOR.md`; for service management, the `make install-service` family.
+3 -65
docs/coding-standards.md
··· 53 53 54 54 ## Layer Hygiene 55 55 56 - These invariants keep read paths pure, concentrate domain writes in one place per domain, and stop infrastructure modules (indexer, importer, scheduler, search, graph) from silently mutating cross-cutting state. They were derived from a codebase-wide audit of layer violations in April 2026 — see the motivating record at `vpe/workspace/solstone-layer-violations-audit.md` in the sol pbc internal extro repo (14 violations inventoried, remediation plan in flight). 57 - 58 - The low-bar grep enforcement is `scripts/check_layer_hygiene.py`, wired into `make ci`. Known audit-flagged files are allowlisted with audit-reference TODOs; the allowlist shrinks as remediation bundles ship. 59 - 60 - ### L1 — Layer boundaries are load-bearing 61 - 62 - Each module family has a declared responsibility. Infrastructure modules (indexer, importer, scheduler, search, graph, stats) may write **only their own output artifacts**. They may not create, modify, or delete domain state (entities, facets, observations, activities, events, chronicle day content). If an infrastructure module needs to trigger a domain mutation, it emits a callosum event or invokes a `sol call <domain> <verb>` subprocess — never writes domain state directly. 63 - 64 - ### L2 — Domain write ownership 65 - 66 - Each domain has exactly **one** write-owning module. No other module may call `atomic_write`, `json.dump`, `open("w")`, `Path.write_text`, `unlink`, `rmtree`, etc. on that domain's on-disk state. 67 - 68 - | Domain | Write-owning module(s) | 69 - |--------|------------------------| 70 - | Entities (`entities/*/entity.json`, `entities/*/*.npz`) | `think/entities/journal.py` + `think/entities/consolidation.py` + `think/entities/saving.py` + `think/entities/merge.py` + `apps/entities/call.py` | 71 - | Facets (`facets/*/facet.json`, `facets/*/relationships/`) | `think/facets.py` + `apps/facets/*` (if/when created) | 72 - | Observations (`observations.jsonl`) | `think/entities/observations.py` | 73 - | Activities (`facets/*/activities/*.jsonl`) | `think/activities.py` | 74 - | Chronicle day content (`chronicle/YYYYMMDD/**`) | The capturing module (observer, importer) per its declared outputs | 75 - | Index (SQLite, `indexer/*`) | `think/indexer/*` | 76 - 77 - ### L3 — Naming is a contract 78 - 79 - Function and CLI-subcommand verbs signal read vs. write intent. 80 - 81 - **Read verbs** (functions and CLI subcommands): `load_*`, `get_*`, `read_*`, `scan_*`, `list_*`, `show_*`, `find_*`, `match_*`, `resolve_*`, `query_*`, `lookup_*`, `status_*`, `check_*`, `validate_*`, `discover_*`, `format_*`, `render_*`, `extract_*`, `parse_*`, `view_*`, `inspect_*`, `info_*`, `describe_*`, `search_*`. 82 - 83 - A read-verb function must not mutate on-disk state. No exceptions for caches. No exceptions for "create on miss." 84 - 85 - If a function needs create-on-miss semantics, split it: 86 - 87 - ```python 88 - entity = load_entity(eid) or create_entity(eid, ...) 89 - ``` 90 - 91 - This makes the write visible at every call site. 92 - 93 - **Write verbs** are the ones allowed to write — choose the right one: `save_`, `create_`, `add_`, `insert_`, `append_`, `attach_`, `delete_`, `remove_`, `update_`, `rename_`, `move_`, `promote_`, `merge_`, `seed_`, `consolidate_`, `bootstrap_`, `backfill_`, `dispatch_`, `record_`, `ingest_`, `import_`, `rebuild_`. 94 - 95 - ### L4 — CLI read-verbs are read-only 96 - 97 - CLI subcommands with read verbs (list, show, status, get, search, find, check, validate, discover, inspect, info, describe, read, view) must not write to journal domain state under any flag combination. If a command needs a write path, split it into two commands — a read-verb reader and a write-verb writer. 98 - 99 - ### L5 — Write-verb defaults 100 - 101 - CLI subcommands with write verbs default to safe. 102 - 103 - - Preferred: no default mutation; an explicit `--commit` (or `--apply`) flag is required to perform the write. 104 - - Acceptable alternative: `--dry-run` defaulting to `False` *only if* the subcommand name is unambiguously a write verb AND the command's primary user journey is the write (e.g., `sol call entities create`). 105 - 106 - "Bootstrap", "backfill", and "resolve-names" are not unambiguous — default them to dry-run. 56 + The L1–L9 layer-hygiene invariants (read/write separation, domain write ownership, naming contracts, CLI verb polarity, indexer/importer/hook/event-handler rules) are inlined in `AGENTS.md` §7. They're promoted to the coder guide because a codebase-wide audit in April 2026 found 14 violations across `think/` and `apps/`, and a one-click-away invariant is a routinely-skipped invariant. 107 57 108 - ### L6 — Indexers never mutate source data 58 + Enforcement: `scripts/check_layer_hygiene.py`, wired into `make ci`. Known audit-flagged files are allowlisted with audit-reference TODOs; the allowlist shrinks as remediation bundles ship. 109 59 110 - An indexer's job is to build indexes from source-of-truth data. Indexers may not mutate the source data they read. Re-running `sol indexer --rescan` on an unchanged journal must be a no-op for domain state. 111 - 112 - ### L7 — Importers only write to imports/ 113 - 114 - Importers write source material to `imports/` and the raw-content areas of `chronicle/`. They may not create or modify entities, facets, observations, or other cross-cutting state. If an importer needs to create an entity for deduplication, it calls a domain-owned `seed_entity()` function in `think/entities/` that surfaces the write explicitly. 115 - 116 - ### L8 — Hooks have declared outputs 117 - 118 - Post-processing hooks (`think/hooks.py`, `talent/*.py` hook functions) declare every path they will write in their frontmatter. The hook runner validates that all actual writes match the declaration. Writes outside the declared set fail loudly — raise at runtime; assert in tests. 119 - 120 - ### L9 — Event handlers are idempotent 121 - 122 - Any function that handles a callosum event, a scheduled tick, or a supervisor-started automation is idempotent w.r.t. on-disk state. Append-only history records dedupe by a natural key (usually `(day, segment)` or `(day, segment, ts)`). Before adding a write to an event handler, ask: "what happens if this event fires twice?" 60 + **Go to `AGENTS.md` §7 for the full rules and the domain ownership table.**
+18
talent/journal/references/cli.md
··· 268 268 sol call journal news -d 20260115 # uses SOL_FACET 269 269 sol call journal news work --cursor 20260110 -n 5 270 270 ``` 271 + 272 + ## Talent CLI Boundaries 273 + 274 + Cogitate talents have access to all `sol` commands. The following infrastructure commands must never be called by talents, because they manage services and data pipelines that should only be operated by the supervisor or a human operator: 275 + 276 + - `sol supervisor` / `sol start` 277 + - `sol think` except heartbeat's targeted `sol think --segment` 278 + - `sol import` 279 + - `sol config` 280 + - `sol cortex` 281 + - `sol providers check` 282 + - `sol callosum` 283 + - `sol observer` / `sol observe-*` 284 + - `sol sense` 285 + - `sol transcribe` / `sol describe` 286 + - `sol indexer --reset` 287 + 288 + Talents should use `sol call` commands for journal interaction and `sol health` / `sol talent logs` for diagnostics.