Skip to content

Latest commit

 

History

History
163 lines (125 loc) · 8.48 KB

File metadata and controls

163 lines (125 loc) · 8.48 KB

pi-outpost — AI context (generated by openlore)

@.openlore/analysis/CODEBASE.md

openlore MCP workflow

Follow this sequence for every task:

  1. orient "<task description>" — always start here. Returns relevant functions, files, spec domains, call paths, and insertion points in one call.
  2. If the task involves data models, APIs, or config — call the relevant inventory tool: get_schema_inventory · get_route_inventory · get_env_vars · get_ui_components · get_middleware_inventory
  3. If debugging a call flow ("how does X reach Y?") — trace_execution_path
  4. Before modifying a functionget_subgraph to understand blast radius
  5. Before opening a PRcheck_spec_drift

On-demand (when orient's results aren't enough): search_code · suggest_insertion_points · get_spec <domain> · search_specs · analyze_impact · get_function_body · get_function_skeleton

Architectural decisions

When making a significant design choice, call record_decision before writing the code.

Significant choices: data structure, library/dependency, API contract, auth strategy, module boundary, database schema, caching approach, error handling pattern.

record_decision({
  title: "Use JWTs for stateless auth",         // short imperative
  rationale: "Avoids session store in infra",   // why this choice
  consequences: "Tokens can't be revoked early", // trade-offs
  affectedFiles: ["src/auth/middleware.ts"],    // optional
  supersedes: "a1b2c3d4"                        // 8-char ID of prior decision being reversed
})

Decisions are consolidated in the background immediately after record_decision is called — the pre-commit gate reads the already-consolidated store and adds no LLM latency.

Performance note: if you skip record_decision, the gate detects unrecorded source changes at commit time and triggers a slow LLM extraction on the next commit (~10-30s). Calling record_decision proactively keeps every commit instant. Do not record trivial choices (variable names, formatting).

This project uses OpenLore for persistent architectural memory.

ALWAYS call orient() (via the openlore MCP server, or npx openlore orient --json) before reading source files when starting a new task. This returns the relevant functions, callers, spec sections, and insertion points for the task at hand — one structural lookup instead of file-by-file rediscovery.

OpenLore prefixes tool responses with a brief, factual freshness note (the Epistemic Lease) once your cached context has aged or the repo has moved since your last orient(). It is informational — re-orient() if you are relying on cached cross-module structure; otherwise carry on.

For the MCP setup, ensure openlore mcp is configured as an MCP server. See https://github.com/clay-good/OpenLore for details.

OpenSpec exploration with OpenLore

OpenSpec and OpenLore are independent tools with complementary responsibilities:

  • OpenSpec captures intended behaviour, requirements, scenarios, design decisions, and implementation tasks.
  • OpenLore provides evidence about the existing implementation: entry points, ownership, call paths, dependencies, tests, specifications, and likely impact.

When exploring, proposing, reviewing, or updating an OpenSpec change for existing functionality, call orient() early, even if no source file has been opened yet. Describe both the intended capability and the relevant OpenSpec change when known.

Use the result to:

  1. identify existing entry points and responsible modules;
  2. find behaviour or constraints missing from the OpenSpec artifacts;
  3. detect reuse opportunities, conflicts, and overlap with other functionality;
  4. locate relevant tests and existing specifications;
  5. estimate the implementation surface and risks.

Reconcile three sources of truth explicitly:

  1. the requested intent;
  2. the OpenSpec artifacts;
  3. the implementation evidence returned by OpenLore.

OpenLore evidence informs the exploration but does not replace requirements or silently redefine the intended behaviour. When the sources disagree, report the discrepancy and resolve it in the OpenSpec artifacts before implementation.

For a purely greenfield or conceptual exploration, orient() may return little useful evidence; continue with OpenSpec and state that no relevant implementation surface was found. If OpenLore is unavailable, use targeted repository inspection and report the fallback.

Tooling & CLI Constraints

  • ALWAYS use rg (ripgrep) instead of grep for code search and file inspection.
  • NEVER run recursive grep -r commands. rg is faster and respects .gitignore.

Spec scenario coverage

Before calling a feature complete, prove that every applicable OpenSpec scenario is covered by testing:

  1. Follow the OpenSpec exploration with OpenLore workflow above, then enumerate every #### Scenario: in the relevant main and delta specs. Verify the list with rg '^#### Scenario:' openspec/ so scenarios cannot be silently omitted.
  2. Produce an explicit scenario-to-test matrix. Classify every scenario as covered, partial, or uncovered, and include the test file and test name. Prefer the exact scenario name in the test title or a machine-readable openlore coverage annotation when the test framework supports it.
  3. Read the assertions, not only the test names or suite result. A scenario is covered only when its GIVEN/WHEN/THEN contract would make the test fail if broken. Timing, ordering, negative cases, security boundaries, and observable outcomes must be checked at the real boundary described by the spec.
  4. Use OpenLore's available coverage, inventory, impact, and spec-drift tools when relevant. If an MCP tool is unavailable, use its CLI or rg fallback and report that fallback.
  5. For enumerated surfaces such as routes, commands, or schemas, compare tests against the production inventory/source of truth. A hand-maintained test-only list is not sufficient proof that newly added cases are covered.
  6. Run focused tests first, then the relevant suites and strict OpenSpec validation. UI or agent-behaviour scenarios must also follow the Playwright running-app rule below.

Do not mark OpenSpec tasks complete or report the feature done while a required scenario is partial or uncovered. If a correct test exposes a product bug, keep the assertion strong: fix the implementation when in scope, otherwise report the blocker instead of weakening the test.

UI and UX changes: test them in the running app

Any change that touches the interface or the way it is used — a component, a tool the agent calls, a tool's description, a message the model reads — must be exercised in the running app with Playwright before it is called done. Unit tests are necessary and they are not sufficient.

Three failures from one session, none of which any suite caught:

  • A PDF viewer that released documents through the wrong object. The throw landed in an effect cleanup, so the whole application unmounted and the user saw a blank page. The test fake had grown a method the real API never had.
  • A file-creation input that closed itself on a refused duplicate, swallowing the error. It inferred success from a side effect the failure produces too.
  • An extraction tool that worked perfectly when driven directly, while the agent kept not using the option that made it worth having. The mechanism was right; the behaviour was not.

The pattern: a fake is kinder than reality, an outcome is inferred rather than observed, or the code is correct and the use of it is not. Only the running app shows those.

What "exercised" means: drive the actual feature — create the file, open the document, ask the agent for the thing — then read back the DOM, the filesystem, or the session transcript to check what really happened. A screenshot is not a check.

How: npm run bench (scripts/embed-bench.mts) leaves a real widget running — host page on its own origin with hostile CSS, two servers behind it, a seeded transcript with diagrams and tables. Fixed ports: host 4321, plain server 4322, seeded transcript 4323; use 127.0.0.1, not localhost, because that is the origin the server allows.

Rebuild first: web, then @pi-outpost/embed, then build:e2e-host. The bench serves dist/, so an unbuilt fix is invisible and you will debug a fix that is already correct.