Files
pom-omp/AGENTS.md

161 lines
21 KiB
Markdown

# Repository Guidelines
## Project Overview
- identity: `@jamminrebel/pom-omp` 2.0.0, MIT, ESM — POM 2 "Produce Magnum Opus", an Oh My Pi (OMP) extension.
- purpose: turn OMP's native models, tools, task agents, IRC, todo, sessions, approvals, compaction and TUI into an evidence-gated production operating system for long-form creative work (README.md:3).
- governs, never replaces: OMP performs execution; POM adds stage contracts, canonical artifacts, prompt provenance, deterministic validation, Git checkpoints, and an Obsidian/Quartz-compatible vault (README.md:5, POM.yml:6-9, docs/ARCHITECTURE.md:3-27).
- ships: 9 stages (0..8), 7 `pom_*` tools, 14 task agents, 13 prompt fragments, 3 rules, 2 themes, native task+IRC Hive planning, optional Swarm DAGs, verified ZIP delivery (README.md:9-20).
- operator loop: `/pom new "Title"` -> `/pom run next` -> `/pom hive stage` -> `/pom evidence` -> `/pom check stage` -> model calls `pom_stage action=pass` only after a PASS report -> `/pom export final` (README.md:44-56). `/pom` opens the command center; `Alt+P` is the shortcut.
- this file is the repository contributor guide. Generated POM projects get their own `AGENTS.md` written by `bootstrapProject` (src/persistence.ts:241-307) — different file, different audience.
## Architecture & Data Flow
- flow: `/pom` command -> parser -> stage machine -> persistence + git + router -> prompt-stack injection -> OMP model turn -> `pom_*` tools -> validators -> exporter.
- entry point: default export `pomExtension(pi: ExtensionAPI)` in `src/index.ts`; sets label `POM · Produce Magnum Opus`, builds the runtime, then registers in this order: `registerPomTools`, `registerPomRenderers`, `registerPomEvents`, `registerPomCommand` (src/index.ts:13-49). Keep registration centralized there.
- runtime boundary: `src/runtime.ts` `PomRuntime` = `get`, `set`, `restore`, `getConfig`, `refreshConfig`, `telemetry`.
- `set` clones state, stamps `updatedAt`, appends a session branch entry of type `pom-state`, writes `00_admin/project_state.json`, and syncs generated knowledge files when `state.initialized` (src/index.ts:25-31). Never persist state around it.
- `restore` precedence: session branch via `restoreLatest` -> `<cwd>/00_admin/project_state.json` -> fresh uninitialized state (src/index.ts:33-42).
- command layer: `src/command.ts` registers `pom` plus shortcut `alt+p` and owns all verbs (`new`, `run`, `hive`, `swarm`, `evidence`, `check`, `vault`, `export`, `status`, `prompt`, `tools`, `theme`, `settings`, `doctor`, `help`). `src/parser.ts` tokenizes quotes/escapes, defaults the verb to `dashboard`, and parses `--flag[=value]` into a Map.
- stage run: `runStage` = `startStage(gotoStage(...))` -> `runtime.set` -> `enterStageBranch` -> `routeStage` -> `updateChrome` -> `pi.sendUserMessage(await renderStagePrompt(...), { deliverAs: "followUp" })` (src/command.ts:94-105).
- routing: `src/router.ts` applies the stage tool profile when `autoToolProfiles`, resolves `spec.modelRole` via `ctx.models.resolve`, calls `pi.setModel`, and sets thinking `xhigh` for every stage except stage 7 (`high`).
- domain: `src/domain.ts` holds `POM_VERSION` / `POM_SCHEMA_VERSION` (both "2.0.0"), `STAGES: readonly StageSpec[]`, `ArtifactRecord`, `EvidenceRecord`, `StageBlocker`, `StageRunState`, `HiveTask`, `HiveMission`, `PomState`, `PomConfig`, `DEFAULT_CONFIG`.
- stages: 0 init, 1 genre, 2 voice, 3 store, 4 bible, 5 outline, 6 chapters, 7 draft, 8 delivery. Each `StageSpec` owns `id`, `slug`, `name`, `icon`, `toolProfile`, `modelRole`, `agents`, `artifacts`, `acceptance`, `todos`.
- state machine: `src/state.ts`, clone-then-mutate.
- `gotoStage` rejects incomplete prerequisites; `startStage` rejects complete stages and unresolved blockers; `passStage` requires an active run, a validation report path, zero unresolved blockers, and evidence for every acceptance criterion (src/state.ts:97-188).
- `recordEvidence` checks the criterion belongs to the active stage, demands a non-empty summary plus >=1 path, de-duplicates normalized paths, replaces prior evidence for that criterion (src/state.ts:144-168).
- canonical state path: `<projectRoot>/00_admin/project_state.json` via `statePath`.
- persistence: `src/persistence.ts` serializes writes per absolute path (`writeQueues`), writes a temp file then `rename`, and appends a trailing newline to JSON.
- manifest `00_admin/artifact_manifest.json`: entries need safe unique relative paths, stage 0..8, semver version, valid status, SHA-256, positive bytes, parseable `updatedAt`, valid rights status.
- `registerArtifact`: `secureExistingPath` -> reject empty file -> `relativeProjectPath` -> SHA-256 -> manifest save -> `artifact.registered` in `00_admin/artifact_events.jsonl` -> knowledge sync (src/persistence.ts:123-163).
- `bootstrapProject` creates `00_admin`, `01_planning`, `02_story_bible`, `03_manuscript/chapters`, `04_images/{references,generated}`, `05_research`, `06_ledgers`, `07_exports`, `.omp/prompts`, `content`, and refuses a non-empty unrecognized directory that has no `00_admin/project_state.json` (src/persistence.ts:216-239).
- events: `src/events.ts`.
- `resources_discover` advertises bundled `../skills`, `../prompts`, `../themes` via `fileURLToPath(new URL(...))`; it does not advertise `agents/` or `rules/`.
- `before_agent_start` injects the composed prompt stack into `event.systemPrompt` and returns a hidden `pom:event` message.
- native `task` / `irc` are observed only: `tool_call` / `tool_result` update Hive mission status plus telemetry and append durable `native.task.started`, `native.task.finished`, `native.irc.send`, `native.irc.result` ledger events.
- prompts: `src/prompts.ts` loads bundled `../prompts` plus project `.omp/prompts`; id = markdown basename; project fragments override bundled by id; every fragment is SHA-256 stamped in an HTML comment (id/source/hash); a stack is immutable law -> `personality-${state.mode}` -> requested ids, and each use is logged to the prompt ledger.
- delegation: `src/studio.ts` builds `HiveMission` ids `s${spec.id}-${randomUUID().slice(0,8)}`, caps agents at `maxStudioAgents`, and converts a mission into one native task batch whose shared context tells agents to coordinate over native IRC and never pass the stage. `src/swarm.ts` optionally writes `00_admin/swarms/<missionId>.yaml` (specialists plus an `integrator`) through `secureFuturePath`.
- validation: `src/validators.ts` `validatePom(state, scope, opts)`; scopes `quick`, `stage`, `canon`, `continuity`, `knowledge`, `files`, `delivery`, `all`; report schema "1.0.0"; `passed` is true only when no check has status `fail`; reports land in `00_admin/validation/stage-<n>-<scope>-<stamp>.json` when initialized.
- delivery: `src/exporter.ts` writes `00_admin/SHA256SUMS.txt`, a delivery report under `07_exports`, then `07_exports/<projectSlug>-<kind>.zip`, verifies it and writes `<base>.verification.json`. `src/zip.ts` (yazl create, yauzl read, `crc-32`) rejects encryption, compression methods other than 0/8, duplicate members, unsafe names, empty archives, size drift and CRC mismatch, and returns the archive SHA-256.
- terminal: `src/ui.ts` writes statuses `pom-stage`, `pom-progress`, `pom-gate`, `pom-hive` and widget `pom-hud`; `src/visual.ts` renders the HUD with width-safe truncation and caches unchanged frames; `src/renderers.ts` renders custom message types `pom:event`, `pom:validation`, `pom:hive`, `pom:milestone` and adds a thinking lane when enabled and view is not minimal/compact.
- git: `src/git.ts` shells `execFile("git", ["-C", root, ...])`, initializes when needed, uses branches `pom/stage-<id>-<slug>`, and checkpoints with `git add -A` + `git commit` unless `gitMode` disables it.
## Key Directories
- src/: extension source, 25 modules, no build step (OMP loads TypeScript directly).
- tests/: 8 `*.test.ts` files on `node:test`, importing production code.
- scripts/: `verify.mjs` (structural gates), `release-manifest.mjs` (RELEASE.json generate/check), `smoke.ts` (runtime smoke), `install.sh` / `install.ps1`.
- agents/: 14 `pom-*.md` specialist task-agent definitions.
- prompts/: 13 prompt fragments; ids are basenames; `personality-*.md` map to `PomMode`.
- rules/: 3 scoped production rules (`pom-artifact-integrity`, `pom-continuity`, `pom-rights`).
- skills/pom/: `SKILL.md` plus `references/` (`core-law.jml`, `workflow.md`, `tool-routing.md`, `todo-blueprints.md`, `artifacts.md`, `agent-studios.md`, `commands.md`, `qa.md`, `ui.md`).
- themes/: `pom-nocturne.json` (dark), `pom-parchment.json` (light).
- config/: shipped templates — `config.yml` (OMP host settings), `pom.json` (POM defaults), `APPEND_SYSTEM.md`, `mcp.json.example`.
- docs/: `ARCHITECTURE.md`, `SETTINGS.md`, `TOOL_MATRIX.md`, `INVENTORY.md`, `AUDIT.md`, `DESIGN_REVIEW.md`, `OFFICIAL_SOURCES.md`, `POM_ARCHITECTURE.mmd`.
## Development Commands
- setup: `npm install`
- typecheck: `npm run check` -> `tsc --noEmit` (tsconfig includes `src/**/*.ts` only; tests are not typechecked)
- tests: `npm test` -> `node --import tsx --test tests/*.test.ts`
- one file: `node --import tsx --test tests/state.test.ts`
- one case: `node --import tsx --test --test-name-pattern="<regex>" tests/state.test.ts`
- runtime smoke: `npm run smoke` -> `node --import tsx scripts/smoke.ts`
- structural gates: `node scripts/verify.mjs`
- release manifest: `npm run release:manifest` (write), `npm run release:check` (fail if stale)
- full gate: `npm run verify` = check -> test -> smoke -> scripts/verify.mjs -> release:check -> `npm pack --dry-run`
- host install: `npm run link` (`omp plugin link .`), then restart OMP or `/reload-plugins`, then `/pom theme install`
- host check: `npm run doctor` (`omp plugin doctor`; needs Bun + OMP installed)
## Code Conventions & Common Patterns
- imports:
- relative source imports are extensionless: `./state`, `./domain`, `./tool-profiles` — never `.ts` / `.js`, despite `allowImportingTsExtensions: true`.
- Node builtins use the `node:` prefix; OMP SDK types come in as `import type { ExtensionAPI, ExtensionContext } from "@oh-my-pi/pi-coding-agent"`.
- errors: throw `Error` for precondition, schema, config, manifest and path violations (`assertState`, `loadPomConfig`, `secureExistingPath`); represent observable tool failures as `isError: true` results, not throws.
- tools: defined in `src/tools.ts`; ids are exactly `pom_state`, `pom_stage`, `pom_artifact`, `pom_validate`, `pom_prompt`, `pom_hive`, `pom_export`.
- schemas use `const { z } = pi.zod` with inline `z.object` / `z.enum`; one targeted `@ts-expect-error` covers OMP 16.4.x + Zod v4 instantiation depth (src/tools.ts:58-61) — keep it narrow.
- every result goes through `textResult(text, details?, isError = false)` -> `{ content: [{ type: "text", text }], details, isError }`.
- mutators declare `approval: "write"`; `pom_validate` and `pom_prompt` declare `approval: "read"`.
- every execute handler returns `textResult("Cancelled", undefined, true)` when `signal?.aborted`.
- state: clone with `structuredClone` before nested mutation, then persist through `runtime.set`.
- async: `async`/`await` everywhere for FS, OMP hooks, tools, config and validation; no callback style.
- naming: kebab-case multiword filenames (`tool-profiles.ts`); camelCase functions, PascalCase types; POM-owned identifiers keep the `pom` / `pom_` / `pom-` prefix per surface (tool ids `pom_*`, statuses `pom-*`, message types `pom:*`, agents `pom-*`).
- tool profiles: `src/tool-profiles.ts` appends POM tools to host tools for non-`auto` profiles, always keeps `read`, and stores the baseline in a `WeakMap` keyed by `pi` — use `resetToolBaselineForTests()` rather than assuming a fresh baseline.
- config: `src/config.ts` merges built-in `DEFAULT_CONFIG` -> `~/.omp/agent/pom.json` -> `<project>/.omp/pom.json`, validates booleans/enums/bounded integers and throws `Invalid POM setting ...`; writes go temp-file-then-rename.
- keys and defaults: `autoToolProfiles` true, `autoModelRouting` true, `dashboardWidget` true, `stopGuard` true, `defaultMode` "autopilot", `defaultView` "cinematic", `defaultMotion` "subtle", `defaultToolProfile` "auto", `maxStudioAgents` 6, `projectRootMode` "child", `destructiveProjectCommands` "block", `gitMode` "branch-per-stage", `hudPlacement` "aboveEditor", `showThinkingLane` true (src/domain.ts:471-486, docs/SETTINGS.md:31-46).
- OMP host settings belong in `<project>/.omp/config.yml`; the shipped template is `config/config.yml` and `bootstrapProject` writes the same values (theme `pom-nocturne`/`pom-parchment`, `task.maxConcurrency: 8`, `compaction.strategy: snapcompact`, `defaultThinkingLevel: high`).
- asset shapes:
- agents: frontmatter `name: pom-<kebab>`, non-empty `description`, `tools:` list that includes `yield`, plus `model`, `thinkingLevel`, `read-summarize`; enforced by `scripts/verify.mjs:58-64`.
- rules: frontmatter `description` plus `globs` array.
- skill: `skills/pom/SKILL.md` frontmatter `name: pom`, `description`, `alwaysApply: false`.
- prompts: frontmatter optional — personality fragments carry `description`, operational fragments start with prose; the loader accepts any `.md`.
- themes: keep `$schema`, `name` (must equal filename stem), `vars`, `colors`, `export`; `/pom theme install` copies `themes/*.json` into `getCustomThemesDir()` from `@oh-my-pi/pi-utils`.
- POM.yml is the human-facing project/release manifest (boundaries, stages, gate, commands, tools, delegation, terminal, knowledge, integrity, resources, release) — not a runtime schema; nothing loads it at runtime.
## Important Files
- src/index.ts: extension entry, runtime construction, registration order.
- src/domain.ts: schema versions, `STAGES`, artifact/evidence/blocker/hive/state/config types, `DEFAULT_CONFIG`.
- src/state.ts: stage lifecycle invariants, evidence rules, canonical state path.
- src/persistence.ts: workspace layout, atomic writes, manifest/ledger/knowledge persistence, bootstrap.
- src/tools.ts: the 7 `pom_*` tool contracts, approvals, result shape.
- src/events.ts: OMP event surface, resource discovery, prompt injection, native task/IRC observation.
- src/validators.ts: deterministic checks and report contract.
- src/paths.ts: `canonicalRoot`, `secureExistingPath`, `secureFuturePath`, `relativeProjectPath`, `assertSafeArchivePath`.
- scripts/verify.mjs: structural release gates (see Testing & QA).
- scripts/release-manifest.mjs: RELEASE.json generation and `--check` staleness semantics.
- package.json: OMP entry `omp.extensions: ["./src/index.ts"]`, scripts, peer window, ship list.
- tsconfig.json: `target ES2022`, `module ESNext`, `moduleResolution Bundler`, `strict`, `noEmit`, `skipLibCheck`, `allowImportingTsExtensions`.
## Runtime/Tooling Preferences
- package manager: npm, authoritative `package-lock.json` (lockfileVersion 3). Do not introduce bun/pnpm/yarn lockfiles.
- local scripts run on Node + `tsx` + `tsc`; `engines.bun >= 1.3.14` describes the OMP host, and `npm run doctor` is the only Bun+OMP gate.
- no build step and no `dist/`: OMP loads `src/index.ts` directly.
- SDK window: `@oh-my-pi/pi-coding-agent`, `pi-tui`, `pi-utils` are peers at `>=16.4.6 <17`. Do not use APIs that require OMP 17.x.
- runtime dependencies are deliberately three: `crc-32`, `yauzl`, `yazl` (ZIP integrity). Add a dependency only when the host SDK and Node builtins genuinely cannot do the job.
- `.gitignore` covers `node_modules/`, `*.tgz`, `*.zip`, `coverage/`, `dist/`, `.DS_Store`, `*.log`.
## Testing & QA
- framework: Node's built-in runner, `import test from "node:test"` + `import assert from "node:assert/strict"`, flat `test("...")` calls, no `describe`.
- tests import production code directly with extensionless specifiers (`../src/state`); never copy implementation into a test.
- filesystem tests isolate with `mkdtemp(join(tmpdir(), ...))` and clean up in `finally` via `rm({ recursive: true, force: true })`.
- test-file names do not map 1:1 to modules: `tests/command.test.ts` exercises `src/parser.ts`, `tests/events.test.ts` exercises `src/policy.ts`.
- covered by unit tests: parser, policy, paths, state, domain, persistence, studio, swarm, validators, zip. Not directly covered: `src/tools.ts`, `src/visual.ts` — add tests there when changing behavior.
- `scripts/smoke.ts` is the end-to-end guard: bootstrap a temp project with `projectRootMode: "cwd"`, assert generated `.omp/config.yml` and `content/production-board.md` content, then assert `validatePom(state, "quick", { writeReport: false }).passed === true`.
- `scripts/verify.mjs` enforces, and will fail a change that breaks any of these:
- required files exist, including `src/index.ts`, `src/events.ts`, `src/tools.ts`, `src/visual.ts`, `skills/pom/SKILL.md`, `config/APPEND_SYSTEM.md`, `prompts/stage-run.md`, both themes, `scripts/smoke.ts`.
- `RELEASE.json.version === package.json.version`; release inventory stages/customTools/behavioralTests = 9/7/9; `pkg.omp.extensions` deep-equals `["./src/index.ts"]`; `pkg.version === "2.0.0"`; every `package.json.files` entry exists.
- exactly one `registerCommand("pom"` and one `registerShortcut("alt+p"` in `src/command.ts`; exactly nine `id: 0..8` stage ids in `src/domain.ts`; exactly seven `name: "pom_*"` tools in `src/tools.ts`.
- `src/tools.ts` contains none of `allowArgs`, `formatApprovalDetails`, `concurrency: "exclusive"`, `ExtensionAPI = any`; `src/omp-shim.d.ts` and `src/node-shim.d.ts` must not exist.
- asset counts 14 agents / 13 prompts / 3 rules / 2 themes, agent frontmatter shape, theme names and required color keys (read from the installed `@oh-my-pi/pi-coding-agent` theme schema, so `npm install` is a prerequisite), and the nine required directories.
- after changing any shipped file (anything in `package.json.files` plus `package.json`), run `npm run release:manifest`; `npm run release:check` compares rendered bytes and fails with "RELEASE.json is stale".
- run `npm run verify` before packaging or linking. Host-level acceptance stays `npm run doctor` plus an interactive OMP session: `/pom` overlay, HUD, statuses, cards, `/pom theme install`, and a smoke project taken through stage 0 validation plus a checkpoint archive.
## Governance Invariants
Preserve these when changing runtime behavior (source: the shipped contributor guide, `rules/*.md`, `skills/pom/references/core-law.jml`).
- do not duplicate host primitives: task, IRC, todo, sessions, approvals, compaction, memory, model execution, TUI. Observe and route them; never build a parallel system.
- stage completion requires active execution, complete evidence, zero open blockers, and a passing validation report. Never mark a stage complete by direct state mutation.
- truth hierarchy: system + explicit user requirements > canonical project files and `00_admin/project_state.json` > manifest / evidence / ledgers > live OMP execution > vault projection > memory. The vault is a navigable projection, not a source of truth.
- keep canonical state and artifacts under the project root; reject traversal and symlink escape through `src/paths.ts` helpers (lexical plus realpath containment) — do not substitute plain `join`/`resolve` checks.
- never fake tool use, files, citations, images, counts, hashes, checks, ZIP tests, or completion. Partial, missing and blocked states stay disclosed as `PARTIAL` / `BLOCKED`.
- artifact claims need real non-empty regular files with measured bytes, SHA-256, category, stage, semver version, status, rights, source and notes recorded in the manifest.
- delivery claims need a fresh archive with safe member names, no duplicates/encryption/unsupported methods, central-directory parsing, decompression, size and CRC-32 checks, an archive hash, and an external verification report.
- continuity: story changes must preserve or explicitly revise canon, timeline, knowledge, relationships, props, injuries, rules, mysteries, setups and payoffs; contradictions require explicit canon selection, dependency mapping, revision logging and revalidation.
- rights: no living-author imitation, fabricated quotations, unauthorized asset redistribution, invented private-identity facts, or unsupported rights claims.
- use OMP renderers and UI hooks (`setWidget`, `setStatus`, `setWorkingMessage`, message cards) instead of printing raw terminal control sequences.
- delegation: one widest safe native task batch per independent wave, each task carrying objective, owned files and acceptance conditions; keep IRC terse with the `CLAIM / QUERY / ANSWER / ALERT / HANDOFF / DONE` vocabulary; durable findings go into project files or ledgers, not chat.
- tool routing: native tool first — `read` for files/directories/archives/static URLs, `glob` for bounded discovery, `grep` for bounded regex, AST/LSP for symbol work, browser only for authenticated/interactive/JS-only pages, `ask` only after exhausting sources.
## Known Discrepancies
Verified inconsistencies in the current tree — do not "fix" them silently as part of unrelated work, and do not describe them as working.
- `pom_stage` accepts an `inspect` action in its schema with no dedicated branch; it falls through to the sync/refresh path and returns summary state. **Decision**: keep as summary fallback; if dedicated inspect behavior is needed, add a branch and update schema/tests.
- `resources_discover` advertises only skills, prompts and themes; `agents/` and `rules/` are shipped but not advertised through that event. **Decision**: agents and rules are packaging-only resources; add them to resources_discover if/when agent-facing discovery is required.
- `docs/INVENTORY.md` claims "9 behavioral tests across 8 test files"; the tree contains 8 test files with 9 test declarations. `scripts/verify.mjs` asserts the manifest number (9) without distinguishing file count from declaration count.