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.