Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions .lore.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@
* **opencode cumulative branch tracks upstream dev with custom patches**: opencode cumulative branch rebases on \`origin/dev\`. Custom patches: (1) plan mode markdown display with collapsible modal in \`plan\_exit\` tool, (2) collapsible question modal/dock (\`origin/collapse-questions\`, not yet merged to dev), (3) unarchive stickiness fix, (4) plan\_exit/plan\_enter deny rules for subagents in task.ts tools map. Upstream renamed \`session/index.ts\` → \`session/session.ts\`. Upstream removed \`cleanupSessionCaches\` from \`session.updated\` (only \`session.deleted\` remains). During rebase, task.ts conflicts: keep upstream's \`deriveSubagentSessionPermission\`, add plan\_exit/plan\_enter inline permission checks in tools map region. Session-level deny alone is insufficient — must also deny in tools map passed to \`SessionPrompt.prompt()\`.

<!-- lore:019e5922-4280-76aa-9b79-5ebeae433d7a -->
* **Sub-agent session detection, isolation, and differential treatment**: Sub-agent session detection via \`x-parent-session-id\` header (pipeline.ts). Each sub-agent gets independent session: temporal storage, gradient state, distillation pipeline, LTM injection, cost tracking. Key differential treatment: (1) cache warming ALWAYS skipped — \`shouldWarm()\` returns false unconditionally; (2) \`findRotationPredecessor()\` skips sub-agents from Tier 1b rotation; (3) idle distillation/curation runs normally. \`isSubagent\`/\`parentSessionId\` persisted to DB (migration v26). Dashboard: collapsible cost-rollup tree via \`loadParentChildMap()\` + \`buildLiveSessionRows()\`; \`rollUp()\` propagates grandchildren costs bottom-up. \*\*Gap\*\*: OpenCode plugin \`chat.headers\` hook does NOT set \`x-parent-session-id\` — gateway can't distinguish parent from sub-agent.

<!-- lore:019e4bbd-ade4-7355-8fb1-87df54c8ea69 -->
* **X-Lore-\* headers: project path injection and resolution priority**: X-Lore-\* headers and project path resolution: \`getProjectPath(systemPrompt, headers)\` in config.ts: (1) \`X-Lore-Project\` header → source \`"header"\`; (2) \`inferProjectPath(systemPrompt)\` → source \`"inferred"\`; (3) \`discoverWorkspaceRoot(process.cwd())\` → source \`"cwd"\`. \`gitRemote\` extracted independently via \`X-Lore-Git-Remote\` header, travels alongside whichever path source wins. \`extractProjectHeader()\`: strips control chars, must start with \`/\`, strips trailing slashes, max 1024 chars. \`extractGitRemoteHeader()\`: strips control chars, calls \`normalizeRemoteUrl()\`, max 512 chars. \`PROJECT\_PATH\_PATTERNS\`: 4 regexes ordered most-specific→most-general: JSON cwd key, \`Working directory:\` prefix, \`.lore.md\`/\`CLAUDE.md\`/\`AGENTS.md\` path (takes dir), generic \`/home/\`/\`/Users/\` path. \`"header"\`/\`"inferred"\` = confident; \`"cwd"\` = provisional — distinction enforced in pipeline.ts. cwd fallback does NOT log warning in config.ts — callers log after post-hoc upgrades so warning only fires when fallback truly sticks. \`unattributedBucketPath(sessionID)\` = \`${UNATTRIBUTED\_PREFIX}/${sessionID}\`. \`UNATTRIBUTED\_PROJECT\_PREFIX = "/\_\_lore\_unattributed\_\_"\`. Hosted mode (\`LORE\_HOSTED\_MODE\`) → \`remoteG \[truncated — entry too long]
* **Sub-agent session detection, isolation, and differential treatment**: Sub-agent session detection via \`x-parent-session-id\` header (\`pipeline.ts\`). Each sub-agent gets independent session: temporal storage, gradient state, distillation pipeline, LTM injection, cost tracking. Key differential treatment: (1) cache warming ALWAYS skipped — \`shouldWarm()\` returns false unconditionally; (2) \`findRotationPredecessor()\` skips sub-agents from Tier 1b rotation; (3) idle distillation/curation runs normally. \`isSubagent\`/\`parentSessionId\` persisted to DB (migration v26). Parent resolved via \`headerSessionIndex\` (Tier 1+2 headers only — Tier 3 fingerprint-only parents unresolvable, warning logged, \`isSubagent:true\` persisted without \`parentSessionId\`). Dedup via \`subagentParentPendingLogged\` Set (\`${sessionID}:${parentClientId}\`) prevents 50+ identical log lines. Dashboard: collapsible cost-rollup tree via \`loadParentChildMap()\` + \`buildLiveSessionRows()\`; \`rollUp()\` propagates grandchildren costs bottom-up. \*\*Gap\*\*: OpenCode plugin \`chat.headers\` hook does NOT set \`x-parent-session-id\`.

### Gotcha

Expand Down Expand Up @@ -61,26 +58,29 @@

### Preference

<!-- lore:019eb36d-af40-737c-92c3-2a2095a45dfa -->
* **Always document investigation findings with structured timestamped bullet points using colored status indicators**: When reporting investigation results, the user expects findings organized as timestamped bullet points with emoji status indicators (🔴 for critical/user-stated rules, 🟡 for discovered facts). Each bullet should include: file path with line numbers, exact code snippets or values, and clear cause-effect relationships. Group related findings by topic (e.g., root cause, fallback path, architecture). This format applies to bug investigations, code reviews, and architectural explorations. The assistant should produce similarly structured output when summarizing findings rather than prose paragraphs.

<!-- lore:019e920f-0876-7aad-a306-8129434a3d6b -->
* **Always embed behavioral directives as code comments to assert invariants**: Behavioral directives: (1) Write invariant assertions as imperative code comments at enforcement points. (2) 🔴=durable/identity facts; 🟡=session-scoped — never promote 🟡→🔴 without confirmation. (3) After bulk type migrations, run typecheck and update ALL affected test files. (4) Review plan before execution — survey tsconfig, package.json; enumerate all consumers of shared state; prefer surgical minimal-churn. (5) Bug investigation: numbered hypotheses, exact line numbers, quote file comments verbatim. (6) Always update .lore.md alongside every PR; run \`git add .lore.md\` before any git commit. (7) Address all code review comments completely — fix root cause, add missing tests, fix biome lint/format errors before pushing. (8) Always do thorough read-only codebase exploration before making changes (5-10 modules, structured findings with file:line refs). (9) Always call \`plan\_exit\` at end of every planning session. (10) After DB migrations, bump schema version assertion in db.test.ts. (11) Never manually bump versions or edit CHANGELOG.md — release trigger: \`gh workflow run release.yml -f version=auto\`. (12) Prefer \`bash\` tool when unable to determine git remote; fall back to cwd \[truncated — entry too long]
<!-- lore:019eb345-56a2-76bb-ae14-3729ad6681d1 -->
* **Always investigate codebases with precise file:line references before implementing**: Before implementing any feature or fix, produce findings with exact file paths and line numbers (e.g., \`curator.ts:277-288\`). Investigations must cover all relevant modules, document function signatures, field names, call sites, and cross-module dependencies. Note what exists, what is dead code, and what precedents exist. Implementation only follows after the codebase is fully mapped. When designing heuristics/schemas that must generalize, survey actual implementations from multiple real codebases before writing logic. Always investigate existing codebase state before designing new features — exhaustive searches across all relevant packages to avoid duplication or conflicts.

<!-- lore:019eb370-88bd-7b92-abef-219a4950576c -->
* **Always record detailed technical findings as timestamped bullet points in session logs**: The user consistently documents investigation findings as structured session logs with timestamped 🟡/🔴 bullet points. Each bullet captures specific technical details: exact file paths, line numbers, variable names, constants, logic conditions, and implementation specifics. Findings are grouped by source file or topic. The user expects the assistant to contribute to and maintain this log format during investigations, recording what was found (not just conclusions) with enough detail to resume work later without re-reading source files. Red 🔴 bullets mark corrections or assertions; yellow 🟡 bullets mark neutral findings.
* **Always record detailed technical findings as timestamped bullet points in session logs**: Document investigation findings as structured session logs with timestamped 🟡/🔴 bullet points. Each bullet captures: exact file paths, line numbers, variable names, constants, logic conditions. Group findings by source file or topic. 🔴 bullets mark corrections/assertions; 🟡 bullets mark neutral findings. Include file path with line numbers, exact code snippets, and clear cause-effect relationships. This format applies to bug investigations, code reviews, and architectural explorations.

<!-- lore:019e9f06-597e-75c0-8f6a-2d2b4c144bc8 -->
* **Always remove all Bun-specific code and build targets**: Always remove all Bun-specific code and build targets: Project has fully migrated to Node.js >= 22.15 + pnpm + Vitest. Never add new Bun-specific code. Replace Bun APIs with Node.js equivalents: \`node:worker\_threads\`, \`node:fs\`, \`node:zlib\`. Sentry: \`@sentry/bun\` → \`@sentry/node\` via \`sentryBunToNodePlugin\`; Bun's \`node:util\` lacks \`getSystemErrorMap()\` — polyfill BEFORE \`Sentry.init()\` in \`packages/gateway/instrument.ts\`. Always use \`vitest run\` (never \`bun test\`). Eval run commands use \`npx tsx\`. Install examples use \`pnpm add\`. TRAP: \`DecompressionStream('zstd')\` is Bun-only — Node.js throws at construction (WHATWG enum only supports \`deflate\`/\`deflate-raw\`/\`gzip\`). \`bspatch.ts:157-178\` \`createZstdStreamReader()\` used this broken pattern — fix by replacing with \`createZstdDecompress\` + \`Readable.toWeb\` (true streaming, back-pressure preserved). Control block in same file already correctly uses \`zstdDecompressSync\` from \`node:zlib\`.
* **Always remove all Bun-specific code and build targets**: Project fully migrated to Node.js >= 22.15 + pnpm + Vitest. Never add new Bun-specific code. Replace Bun APIs with Node.js equivalents: \`node:worker\_threads\`, \`node:fs\`, \`node:zlib\`. Sentry: \`@sentry/bun\` → \`@sentry/node\` via \`sentryBunToNodePlugin\`; Bun's \`node:util\` lacks \`getSystemErrorMap()\` — polyfill BEFORE \`Sentry.init()\` in \`packages/gateway/instrument.ts\`. Always use \`vitest run\` (never \`bun test\`). Eval run commands use \`npx tsx\`. Install examples use \`pnpm add\`. TRAP: \`DecompressionStream('zstd')\` is Bun-only — Node.js throws at construction. \`bspatch.ts:157-178\` \`createZstdStreamReader()\` used this broken pattern — fix by replacing with \`createZstdDecompress\` + \`Readable.toWeb\`. NOTE: \`@loreai/gateway\` devDependencies still reference \`@sentry/bun\` and \`@types/bun\` — these are legacy artifacts, not active usage.

<!-- lore:019eb19e-49a3-7756-9068-eaed09c05063 -->
* **Always request harsh multi-pass code review covering logic flaws, edge cases, and invariant violations**: Always request harsh multi-pass code review covering logic flaws, edge cases, and invariant violations: PRs require harsh multi-pass self-review before merging. Check: (1) logic flaws and incomplete allowlists, (2) fragile assumptions/edge cases, (3) invariants preserved, (4) CI/publish path correctness, (5) PR description accuracy vs actual diff. Independently verify claims (grep symbols, trace execution paths). Produce structured PASS/FAIL/NIT verdicts. Fix all failing tests before pushing. Run full typecheck (zero errors) and full vitest suite before committing. Before implementing, enumerate 5–10 distinct targets upfront with exact file:line references. Always push branch and create PR as last step — confirm PR URL. Always stash/exclude \`.lore.md\` from PR commits. Investigations should cover all relevant modules, document function signatures, field names, call sites, and cross-module dependencies. Note what exists, what is dead code, and what precedents exist. Also check: flags set in memory but never persisted to DB; flag lifecycle (set→persist→restore→consume→persist-false); dead code or always-truthy guards; missing tests for new lifecycle paths.
* **Always request harsh multi-pass code review covering logic flaws, edge cases, and invariant violations**: Always perform harsh multi-pass self-review before merging PRs. Check: (1) logic flaws and incomplete allowlists, (2) fragile assumptions/edge cases, (3) invariants preserved, (4) CI/publish path correctness, (5) PR description accuracy vs actual diff. Independently verify claims (grep symbols, trace execution paths). Produce structured PASS/FAIL/NIT verdicts. Fix all failing tests before pushing. Run full typecheck (zero errors) and full vitest suite before committing. Before implementing, enumerate 5–10 distinct targets upfront with exact file:line references. Always push branch and create PR as last step — confirm PR URL. Always stash/exclude \`.lore.md\` from PR commits. Check: flags set in memory but never persisted to DB; flag lifecycle (set→persist→restore→consume→persist-false); dead code or always-truthy guards; missing tests for new lifecycle paths.

<!-- lore:019eadd8-c8fd-7785-9764-dfbf885563c9 -->
* **Always verify local CI checks (lint, typecheck, check scripts) pass before committing**: Always verify local CI checks pass before committing. Work sequence: implement → verify checks pass → commit → push. Required CI jobs before merging: \`test\`, \`actionlint\`, \`changes\`, \`preview\`, \`CI Status\`, \`check-docs\`, \`check-links\`, \`binary smoke (darwin-arm64)\`, \`binary smoke (windows-x64)\`. New CI jobs MUST appear in \`ci-status\` \`needs:\` array or they never block merge. Path filters in dorny/paths-filter must include all relevant files or test jobs silently skip. Main/release pushes always run full CI via \`Force full CI\` step; \`dorny/paths-filter\` only matters for PRs. \`actionlint\` runs unconditionally — workflow bugs must never slip through. vitest.config.ts \`resolve.alias\` is top-level, NOT nested under \`test:\`. Delete stale \`.tsbuildinfo\` files to clear phantom type errors.
<!-- lore:019eb3e3-c46d-79bd-b511-f6d1b0c94c3f -->
* **Always run full test suite and CI checks before merging PRs**: Before merging or considering work complete: run full local test suite (all files), typecheck, lint (biome), and verify CI checks pass on the PR. CI jobs include actionlint, smoke tests, doc checks, etc. All checks must be green before proceeding. When making changes, verify related tests are updated to match refactored APIs. Never consider a feature done without confirming both local tests and CI pass. Also run \`git add .lore.md\` before any git commit — \`.lore.md\` must always be staged and committed alongside other changes.

<!-- lore:019ead9d-9031-71d5-9f83-1e321b570f28 -->
* **Enforce hard constraints via explicit comments and dedicated schema columns**: Hard architectural constraints (🔴 = durable): (1) Workers NEVER fall back to \`cfg.model\`. (2) Never cross-provider batch. (3) Always honor Retry-After. (4) Dedicated indexable columns over JSON blobs. (5) API surface takes providerID. (6) Useful path is default; expose \`--no-X\` escape hatch. (7) Wire protocol from \`resolveProviderRoute(providerID)?.protocol\`. (8) Hosted mode NEVER watches client-controlled paths. (9) \`isOpenAI\` uses \`model.providerID === 'openai'\`. (10) Provider mismatch guard bypassed when \`upstreamOverride\` active. (11) \`sessionID\` always set for gateway worker calls but typed optional. (12) Sustained failures end on same status code they started with. (13) \`initIfNeeded()\` called only AFTER \`resolveSessionProjectPath()\`. (14) Confident path (\`header\`/\`inferred\`) always binds session and clears provisional flag; \`cwd\` result NEVER overwrites confident binding. (15) \`reattributeProvisionalProject\` never throws. (16) In hosted mode, never run git subprocesses with client-controlled cwd. (17) \`GatewayToolResultBlock.content\` is ALWAYS a normalized block array. (18) \*\*Flags set in memory MUST be included in \`saveSessionTracking()\` batched save\*\* — omitting a flag (e \[truncated — entry too long]
<!-- lore:019eb3df-2f54-7ae2-b304-f4524a1e9474 -->
* **Always track work on dedicated feature/fix branches with descriptive conventional-commit-style names**: Always create dedicated branches for each fix or feature (e.g., \`fix/delta-upgrade-zstd\`, \`ci/codecov-coverage\`), using kebab-case conventional-commit-style names. Push to origin and open a PR targeting \`main\`. When a branch becomes stale (commits already merged), switch to a fresh branch. Always propose or create a properly named branch before committing work.

<!-- lore:019eb3df-2f47-7149-ae81-9b2489fbe095 -->
* **Always verify glob patterns don't accidentally exclude core business logic files**: When reviewing or writing file glob patterns (especially in coverage excludes, lint configs, or build configs), the user checks that patterns don't silently exclude important source files. Specifically: prefer single-star globs (e.g., \`\*.config.ts\`) over double-star globs (e.g., \`\*\*/\*.config.ts\`) when the intent is to match only root-level config files. Always verify that exclude patterns only match their intended targets (e.g., tooling config files) and not similarly-named source files deep in the package tree (e.g., \`packages/core/src/config.ts\`). When proposing glob patterns, confirm which actual files they match before finalizing.

<!-- lore:019e9d98-e22f-7f0d-87d3-91c4f8b9a5e3 -->
* **OpenCode plugin: per-project state map for project path header injection**: OpenCode plugin per-project state: \`projectState\` Map keyed by \`ctx.project.id\` → \`{ projectPath, gitRemote, lastSeenAt }\`; TTL \`SESSION\_STATE\_TTL\_MS = 24h\`. \`reapStaleProjectState()\` called on every new entry. \`chat.headers\` hook uses per-project Map entry to avoid cross-project clobbering. \`loreInitPromise\` memoizes concurrent plugin calls racing on probe→spawn. Gateway discovery order: (0) \`LORE\_REMOTE\_URL\`; (1) \`LORE\_GATEWAY\_URL\`; (2) port file (dynamic import \`@loreai/gateway\` → \`readPortFile()\`); (3) known ports \`\[3207, 5673]\`; (4) in-process \`startGateway({ quiet:true, local:true })\`. \`remoteGateway\` auto-detection: (1) \`LORE\_REMOTE\_GATEWAY\` env; (2) \`LORE\_HOSTED\_MODE\`; (3) non-loopback bind; (4) default=true. \`--local\` CLI flag ALWAYS wins. In-process callers MUST pass \`local:true\`. Fallback globals \`currentProjectPath\`/\`currentGitRemote\` used by fetch interceptor when \`chat.headers\` hook is bypassed. \*\*Gap\*\*: \`chat.headers\` injects \`x-lore-session-id\`/\`x-lore-agent\` but NOT \`x-parent-session-id\`. \`tool: {}\` at line 286 — empty map, no tool hooks implemented yet.
* **OpenCode plugin: per-project state map for project path header injection**: OpenCode plugin per-project state: \`projectState\` Map keyed by \`ctx.project.id\` → \`{ projectPath, gitRemote, lastSeenAt }\`; TTL \`SESSION\_STATE\_TTL\_MS = 24h\`. \`reapStaleProjectState()\` called on every new entry. \`chat.headers\` hook uses per-project Map entry to avoid cross-project clobbering. Gateway discovery order: (0) \`LORE\_REMOTE\_URL\`; (1) \`LORE\_GATEWAY\_URL\`; (2) port file; (3) known ports \`\[3207, 5673]\`; (4) in-process \`startGateway\`. \`remoteGateway\` auto-detection: (1) \`LORE\_REMOTE\_GATEWAY\` env; (2) \`LORE\_HOSTED\_MODE\`; (3) non-loopback bind; (4) default=true. \`--local\` CLI flag ALWAYS wins. In-process callers MUST pass \`local:true\`. \*\*Gap\*\*: \`chat.headers\` injects \`x-lore-session-id\`/\`x-lore-agent\` but NOT \`x-parent-session-id\`. \`tool: {}\` at line 286 — empty map, no tool hooks implemented yet.

<!-- lore:019eb3d9-f4b0-7f5c-90f7-465c8d364271 -->
* **Prefers recall over assuming you don't have the information**: prefer recall over assuming you don't have the information.
Loading
Loading