Skip to content

feat(skill-memory): per-skill cross-session recall + historian auto-extraction - #181

Open
iceteaSA wants to merge 22 commits into
cortexkit:masterfrom
iceteaSA:skill-memory-pr
Open

feat(skill-memory): per-skill cross-session recall + historian auto-extraction#181
iceteaSA wants to merge 22 commits into
cortexkit:masterfrom
iceteaSA:skill-memory-pr

Conversation

@iceteaSA

@iceteaSA iceteaSA commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds skill-memory — per-skill "motor memory" that gives a skill cross-session recall of its own hard-won lessons (gotchas, discoveries, fixes, workflow steps). When a skill's SKILL.md declares skill-memory: { enabled: true }, accumulated notes for that skill surface automatically in a <skill-memory> block appended to the skill tool's result on every load — and, with the historian extension, the historian writes those notes automatically during compaction (no agent action required).

It's fully opt-in per skill and cache-safe by construction: the block rides the tool-result tail (conversation), never the cached system/m[0] prefix, so it can't bust the prompt cache.

How it works

  • Transparent recall — three-hook augmentation: tool.definition advertises an intent param; tool.execute.before stashes the intent (bounded TTL); the after-hook parses the skill's Base directory, reads its SKILL.md frontmatter, and formats the recall block. Lands in the tool RESULT (cache-safe).
  • Write pathsctx_skill_note (agent-authored) and the historian (auto-extracted). Both dedup on a normalized hash.
  • Intent-scoped ranking — a recall cascade: model-matched embeddings → cosine blend over intent_embedding + delta_embedding (relevance/recency/hit weights tunable per skill via ranking_* frontmatter); FTS5 fallback over a content-linked skill_memory_fts vtable; flat recency×hit fallback.
  • Historian auto-extraction — the historian sees TC: skill(<name>) markers in its chunk and emits a <skill_observations> block; both the OpenCode and Pi runners promote those post-commit as global notes (project_identity='*', source_type='historian'), recallable from any project.

Review units (4 commits)

The branch is organized into four coherent, reviewable phases:

  1. P1 — transparent per-skill recall: skill_memory table migration, the three-hook augmentation, flat recall + storage, ctx_skill_note/ctx_skill_recall tools, opt-in distill-skill-memory dreamer task, TUI/ctx-status stats, docs.
  2. P2 — embeddings + intent-scoped recall: delta_embedding + recall_count columns + skill_memory_fts FTS5 vtable; embed-on-write; the cosine/FTS recall cascade; a programmatic (no-LLM) reembed pre-step.
  3. P3a — historian-extraction foundation: the TC: skill(<name>) marker keystone; origin_project + source_type columns; global-tier notes unified under project_identity='*' (collision-merge); partitionKey routing.
  4. P3b — historian auto-extraction pipeline: prompt emits <skill_observations>, parser, validated-result threading, both runners promote via the shared promoteSkillObservations helper, plus an initializeDatabase self-heal net (re-creates skill_memory + ensureColumn so an upgraded DB recovers even if a migration row is lost).

Schema / migrations

Three migrations (numbered after the current master ceiling): skill_memory table, then delta_embedding/recall_count/FTS, then origin_project/source_type/* unification. LATEST_SUPPORTED_VERSION bumped in lockstep (the schema-version-fence test enforces it). Migration bodies are ensureColumn/IF NOT EXISTS idempotent.

Testing

Full plugin + Pi suites pass (one unrelated pre-existing full-suite ordering flake in tui-config.test.ts that passes in isolation), tsc clean both packages, lint clean, build produces all bundles. Dedicated coverage for the migrations (coexistence + fence), recall rungs, the tools, FTS triggers/backfill, the '*' collision-merge, and the historian promotion path on both runners. Verified working live: the historian auto-extraction writes genuine source_type='historian' notes, embeddings populate, and the read-side recall_count increments on surfacing.

Notes for reviewers

  • Migration version numbers are placeholders relative to this fork's base — happy to renumber to whatever slots are free at merge time.
  • The four commits are independently meaningful; if you'd prefer this as stacked PRs (P1 / P2 / historian), I can split it.

View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Adds per-skill cross-session memory with intent-aware embeddings, FTS ranking, and historian auto-extraction. Fork-lane migrations and an opt‑in distill-skill-memory task were added; notes surface in a <skill-memory> block and project stats now appear in status/TUI.

  • New Features

    • Opt-in per-skill recall via SKILL.md (skill-memory: { enabled: true }, inline or block); the skill tool advertises optional intent; before/after hooks stash intent, resolve provenance, and append a <skill-memory> block; ctx_skill_recall exposes on-demand recall without re-loading; ctx_skill_note writes with embed‑on‑write and hash/cosine dedup.
    • Ranking cascade: cosine over intent_embedding + delta_embedding with FTS5 fallback; recall_count tracked; distill-skill-memory (opt‑in) runs a pre‑embed sweep for NULL/stale vectors, then emits a read‑only corpus health report; CLI/schema/dashboard expose the task (default off).
    • Historian auto-extraction: TC: skill(<name>) markers → <skill_observations>; parser threads to runners; both OpenCode and Pi promote to global '*' notes (source_type='historian'), recallable from any project.
    • Status/TUI: adds a “Skill memory” section with per-project totals (notes/skills/pinned); RPC returns skillMemory stats; ctx-status awaits executeStatus; system prompt includes concise ctx_skill_note/ctx_skill_recall guidance.
    • Storage: initializeDatabase creates/repairs skill_memory, the FTS vtable + triggers, and rebuilds FTS when empty; fork-lane migrations v10000–v10002 (table, embeddings/FTS, historian columns + global '*') run via runForkMigrations without advancing the upstream watermark.
  • Bug Fixes

    • Recall scope: queries union the skill’s project partition with global '*' so project-local skills surface historian notes; cold‑start tier derivation fixed.
    • Provenance/resolve: accept plain filesystem paths and file://; anchor + last‑match parse; Windows path normalization; name‑based fallback when the provenance line is truncated; project-skill walk stops at the repository worktree root; global discovery handles skill/ and skills/.
    • Stability/guardrails: no crash when the plugin is disabled; session‑keyed intent stash with TTL and per‑session pruning; log recall errors; count per‑note XML framing to stay within max_tokens; clamp pinned budget to min(max_pinned_tokens, max_tokens); reject ctx_skill_note when disabled in frontmatter; TC: skill(<name>) preserves identity (drops only CR/LF/tab).

Written for commit 5d9f58c. Summary will update on new commits.

Review in cubic

Greptile Summary

This PR adds skill-memory — per-skill cross-session recall backed by SQLite, with an optional historian auto-extraction pipeline. The implementation is substantial (~7 k lines) and ships with comprehensive tests and a four-phase commit structure that maps cleanly to its stated design phases.

  • Transparent recall: three-hook augmentation on the skill tool adds an optional intent param, stashes it pre-validation, and appends a <skill-memory> block to the tool result using a cosine/FTS5/flat recall cascade.
  • Storage & migrations: fork-lane migrations (v10000–10002) create skill_memory + FTS5 vtable + historian columns; initializeDatabase now also carries the full schema and a guarded FTS self-heal, so both fresh and upgraded databases converge correctly.
  • Historian auto-extraction: TC: skill(<name>) markers cause the historian to emit <skill_observations> blocks; both the OpenCode and Pi runners promote those as global '*' notes; the previous P1 (project-local skills never seeing historian notes) was fixed by unioning the skill's own partition with the global '*' partition in all four recall-path query functions.

Confidence Score: 5/5

  • This PR is safe to merge. The four previous blocking issues (cross-session intent eviction, orphaned global notes for project-local skills, FTS self-heal gap, disabled-skill writes) were all resolved in follow-up commits and are verified by regression tests.
  • All architectural risks in the initial round of review have been addressed: the intent stash now uses composite sessionID:callID keys, the recall path unions own-partition and global '*' for every query function, the FTS self-heal fires a guarded rebuild on upgrade, and ctx_skill_note rejects notes for disabled skills before any DB write. The remaining findings are style-level: a redundant pure-function call, a footer appended to an explicit tool result, and a missing index for a background dreamer task that is off by default. The codebase is well-tested (dedicated test files for each phase, migration coexistence tests, historian promotion regression tests) and the design is clearly documented.
  • No files require special attention. The storage.ts double-call to recallPartitionPredicate in searchSkillMemoryFts and the missing reembed index are worth addressing but do not block merge.

Important Files Changed

Filename Overview
packages/plugin/src/features/magic-context/skill-memory/storage.ts Core storage layer for skill_memory. Correct partition predicate unioning own + global '*' partition for recall, while keeping write/dedup paths exact-partition. Minor: searchSkillMemoryFts calls recallPartitionPredicate twice (pure but redundant).
packages/plugin/src/features/magic-context/skill-memory/recall.ts Correct 4-rung recall cascade (embedding cosine → FTS5 fallback → flat recency×hit). Token budget fill handles pinned notes with dual constraints. recallSkillMemoryBlock logs errors instead of swallowing silently (previous review finding addressed).
packages/plugin/src/features/magic-context/fork-migrations.ts Fork migration runner correctly uses per-row presence checks rather than watermark, preventing version-collision skips. Migrations are idempotent (IF NOT EXISTS + columnExists guards). Ordering is enforced via sort, and each migration runs in its own immediate transaction.
packages/plugin/src/tools/ctx-skill-note/tools.ts Enabled-guard added (previous P1 addressed). Correct dedup sequence: hash dedup → semantic cosine dedup → insert. Note: passes pre-computed partition key where raw projectIdentity is expected in several storage calls — idempotent due to partitionKey's design but inconsistent with the function signatures.
packages/plugin/src/tools/ctx-skill-recall/tools.ts Registry-first + disk-fallback resolution works correctly. Correctly passes raw projectIdentity to recallSkillMemoryBlock (correct pattern). The returned block includes the buildSkillMemoryBlock footer reminder ("call ctx_skill_note after using this skill") even on an explicit recall request.
packages/plugin/src/hooks/magic-context/hook-handlers.ts Intent stash uses composite sessionID:callID keys (previous P1 addressed). Before-hook correctly fires pre-schema-validation. After-hook populates skillLoadRegistry and injects recall block in one pass. Non-fatal error handling throughout.
packages/plugin/src/features/magic-context/storage-db.ts skill_memory and skill_memory_fts tables created in initializeDatabase with full column set. Guarded FTS self-heal (ftsCount===0 && rowCount>0) added (previous P1 addressed). Upgrade path correct: IF NOT EXISTS + fork migrations handle both new and existing DBs.
packages/plugin/src/features/magic-context/skill-memory/reembed.ts Correctly identifies stale notes (NULL embeddings OR model version mismatch). Uses IS NOT ? for NULL-safe model version comparison. No dedicated index on project_identity alone may cause a full table scan on very large databases, but bounded by LIMIT 200.

Sequence Diagram

sequenceDiagram
    participant Model
    participant Hook as tool.execute hooks
    participant Registry as SkillLoadRegistry
    participant DB as skill_memory (SQLite)
    participant Recall as recallSkillMemoryBlock

    Model->>Hook: tool.execute.before (skill, intent)
    Hook->>Registry: stashIntent(sessionID:callID, intent)

    Model->>Hook: tool.execute.after (skill result)
    Hook->>Hook: parseSkillProvenance(output)
    Hook->>DB: read SKILL.md frontmatter
    Hook->>Registry: set(sessionID:skillId, provenance+config)
    Hook->>Recall: maybeInjectSkillMemory(db, tier, projectIdentity, intent)
    Recall->>DB: embedTextForProject(intent) → vector
    alt Embedding match found
        Recall->>DB: getRankingCandidates → cosine rank (Rung 1)
    else No embedding match
        Recall->>DB: searchSkillMemoryFts → BM25 rank (Rung 3)
    else No intent
        Recall->>DB: getSkillMemoryNotes → recency×hit (Rung 2/4)
    end
    Recall->>DB: bumpRecallCountByIds
    Recall-->>Hook: "<skill-memory> block"
    Hook-->>Model: skill result + appended block

    Note over Model,DB: Write path (ctx_skill_note)
    Model->>DB: ctx_skill_note(skill, kind, delta, intent)
    DB->>DB: hash dedup → cosine dedup → insert
    DB->>DB: FTS5 triggers sync skill_memory_fts

    Note over DB: Historian auto-extraction
    DB->>DB: promoteSkillObservations(observations)
    DB->>DB: "insert tier=global, project_identity='*', source_type='historian'"
Loading

Reviews (29): Last reviewed commit: "refactor(migrations): move skill-memory ..." | Re-trigger Greptile

@iceteaSA
iceteaSA force-pushed the skill-memory-pr branch 4 times, most recently from dc83db6 to 4034019 Compare June 25, 2026 11:37
@iceteaSA
iceteaSA marked this pull request as ready for review June 25, 2026 13:18

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 issues found across 78 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/plugin/src/features/magic-context/dreamer/task-executor.ts">

<violation number="1" location="packages/plugin/src/features/magic-context/dreamer/task-executor.ts:419">
P2: Re-embed pre-step errors are suppressed, allowing successful task completion reporting despite a failed prerequisite data maintenance step.</violation>
</file>

Note: This PR contains a large number of files. cubic only reviews up to 40 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.

Re-trigger cubic

Comment thread CONFIGURATION.md Outdated
Comment thread packages/plugin/src/features/magic-context/skill-memory/promote.ts Outdated
Comment thread packages/plugin/src/index.ts Outdated
Comment thread packages/plugin/src/tools/ctx-skill-recall/types.ts
Comment thread packages/plugin/src/features/magic-context/dreamer/task-executor.ts
Comment thread packages/plugin/src/hooks/magic-context/read-session-formatting.ts Outdated
Comment thread packages/plugin/src/hooks/magic-context/read-session-formatting.ts
Comment thread packages/plugin/src/features/magic-context/skill-memory/frontmatter.ts Outdated
Comment thread packages/plugin/src/features/magic-context/skill-memory/frontmatter.ts Outdated
Comment thread packages/plugin/src/hooks/magic-context/hook.ts Outdated
Comment thread packages/plugin/src/hooks/magic-context/hook-handlers.ts
Comment thread packages/plugin/src/features/magic-context/storage-db.ts

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 15 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/plugin/src/features/magic-context/dreamer/task-executor.ts">

<violation number="1" location="packages/plugin/src/features/magic-context/dreamer/task-executor.ts:419">
P2: Re-embed pre-step errors are suppressed, allowing successful task completion reporting despite a failed prerequisite data maintenance step.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/plugin/src/features/magic-context/dreamer/task-prompts.ts Outdated
Comment thread packages/plugin/src/features/magic-context/skill-memory/frontmatter.ts Outdated
Comment thread packages/plugin/src/hooks/magic-context/read-session-formatting.ts Outdated
Comment thread packages/plugin/src/features/magic-context/skill-memory/frontmatter.test.ts Outdated
Comment thread packages/plugin/src/features/magic-context/skill-memory/promote.ts
@iceteaSA
iceteaSA marked this pull request as draft June 25, 2026 14:55
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jun 25, 2026
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jun 25, 2026
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jun 25, 2026
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
@iceteaSA
iceteaSA marked this pull request as ready for review June 25, 2026 16:09

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 78 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/plugin/src/features/magic-context/dreamer/task-executor.ts">

<violation number="1" location="packages/plugin/src/features/magic-context/dreamer/task-executor.ts:419">
P2: Re-embed pre-step errors are suppressed, allowing successful task completion reporting despite a failed prerequisite data maintenance step.</violation>
</file>

Note: This PR contains a large number of files. cubic only reviews up to 40 files per PR, so some files may not have been reviewed. cubic prioritizes the most important files to review.
On a pro plan you can use ultrareview for larger PRs.

Re-trigger cubic

Comment thread ARCHITECTURE.md Outdated
Comment thread ARCHITECTURE.md Outdated
Comment thread packages/plugin/src/hooks/magic-context/read-session-formatting.ts
Comment thread packages/plugin/src/features/magic-context/skill-memory/provenance.ts Outdated
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jun 25, 2026
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jun 26, 2026
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
@iceteaSA
iceteaSA force-pushed the skill-memory-pr branch 2 times, most recently from 9c62039 to 3e28417 Compare July 2, 2026 15:22
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jul 2, 2026
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
Comment thread packages/plugin/src/tools/ctx-skill-note/tools.ts
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jul 2, 2026
…d in frontmatter

Counterpart to ctx_skill_recall's enabled-guard (greptile review, PR cortexkit#181):
without it, notes for skills that never opted in inserted successfully but
were permanently orphaned — recallSkillMemoryBlock returns "" when
frontmatter is disabled, while the agent saw a convincing 'Skill note
saved' response. Now returns an actionable error before any insert.

Red-checked: new regression test fails without the guard (orphan row
inserted + 'saved' response), passes with it (no row, 'not enabled').
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jul 2, 2026
…d in frontmatter

Counterpart to ctx_skill_recall's enabled-guard (greptile review, PR cortexkit#181):
without it, notes for skills that never opted in inserted successfully but
were permanently orphaned — recallSkillMemoryBlock returns "" when
frontmatter is disabled, while the agent saw a convincing 'Skill note
saved' response. Now returns an actionable error before any insert.

Red-checked: new regression test fails without the guard (orphan row
inserted + 'saved' response), passes with it (no row, 'not enabled').

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jul 6, 2026
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Jul 6, 2026
…d in frontmatter

Counterpart to ctx_skill_recall's enabled-guard (greptile review, PR cortexkit#181):
without it, notes for skills that never opted in inserted successfully but
were permanently orphaned — recallSkillMemoryBlock returns "" when
frontmatter is disabled, while the agent saw a convincing 'Skill note
saved' response. Now returns an actionable error before any insert.

Red-checked: new regression test fails without the guard (orphan row
inserted + 'saved' response), passes with it (no row, 'not enabled').
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Aug 7, 2026
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Aug 7, 2026
…d in frontmatter

Counterpart to ctx_skill_recall's enabled-guard (greptile review, PR cortexkit#181):
without it, notes for skills that never opted in inserted successfully but
were permanently orphaned — recallSkillMemoryBlock returns "" when
frontmatter is disabled, while the agent saw a convincing 'Skill note
saved' response. Now returns an actionable error before any insert.

Red-checked: new regression test fails without the guard (orphan row
inserted + 'saved' response), passes with it (no row, 'not enabled').
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Aug 7, 2026
…ath provenance case

cubic P2 (PR cortexkit#181): the 'PLAIN filesystem path for a PROJECT skill' test
asserted tier/skillSource but not resolvedPath — the only plain-path
project-skill test, so a path-join regression (missing /SKILL.md suffix,
bad concat) would go undetected. Added the resolvedPath assertion to match
the coverage pattern of every sibling test.
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Aug 7, 2026
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
iceteaSA pushed a commit to iceteaSA/magic-context that referenced this pull request Aug 7, 2026
…d in frontmatter

Counterpart to ctx_skill_recall's enabled-guard (greptile review, PR cortexkit#181):
without it, notes for skills that never opted in inserted successfully but
were permanently orphaned — recallSkillMemoryBlock returns "" when
frontmatter is disabled, while the agent saw a convincing 'Skill note
saved' response. Now returns an actionable error before any insert.

Red-checked: new regression test fails without the guard (orphan row
inserted + 'saved' response), passes with it (no row, 'not enabled').
Tehan added 22 commits August 7, 2026 17:51
Per-skill "motor memory": when a skill's SKILL.md declares
`skill-memory: { enabled: true }`, accumulated gotchas/discoveries/fixes/
workflow-steps surface in a <skill-memory> block appended to the skill
tool's RESULT on every load (cache-safe — rides the tool-result tail).
Agents write back via ctx_skill_note; ctx_skill_recall is the explicit
companion to the transparent after-hook.

- migration: skill_memory table (per-skill; tier project/global; UNIQUE on
  skill_id/tier/project_identity/normalized_hash) + lookup indexes.
- three-hook augmentation: tool.definition advertises an `intent` param;
  tool.execute.before stashes intent (bounded TTL); after-hook parses the
  skill's Base directory, reads SKILL.md frontmatter, formats the block.
- flat recency×hit recall + storage layer; ctx_skill_note / ctx_skill_recall.
- opt-in distill-skill-memory dreamer task; agent-prompt guidance; TUI/ctx-status stats.
- docs: ARCHITECTURE / STRUCTURE / CONFIGURATION / README.
Upgrade recall from flat recency×hit to a multi-rung cascade: intent +
model-matched embeddings → cosine blend across intent_embedding +
delta_embedding (relevance/recency/hit weights tunable per skill via
ranking_* frontmatter); intent + no model match → FTS5 fallback over the
content-linked skill_memory_fts vtable; empty → flat fallback.

- migration: delta_embedding + recall_count columns + skill_memory_fts FTS5 vtable.
- embed-on-write in insertSkillMemoryNote; delta-only semantic dedup.
- programmatic, no-LLM reembed pre-step for the distill-skill-memory dreamer task.
- read-side recall_count (distinct from write-side hit_count).
- canonical vector serde + dedup/ranking/FTS query helpers.
…ication (P3a)

Foundation for the historian to auto-capture skill notes cross-project.

- surface the skill name in the historian chunk as a `TC: skill(<name>)`
  marker (the keystone — the tool input name was previously dropped).
- migration: origin_project + source_type columns; unify global-tier notes
  under project_identity='*' (collision-merge) so a global note is one row
  recallable from any repo.
- partitionKey helper routes global write/recall/reembed/stats through '*';
  recall reads global-tier from '*' (cross-project); reembed sweeps '*'.
Close the loop so the historian writes skill notes during compaction
without an agent volunteering ctx_skill_note.

- historian prompt emits a <skill_observations> block; parser extracts it;
  threaded through the validated historian result.
- both runners (OpenCode + Pi) promote skill observations post-commit via
  the shared promoteSkillObservations helper, gated by
  promotionActive && !discardedLast, writing global '*' notes with
  source_type='historian'.
- self-heal net: initializeDatabase re-creates skill_memory + ensureColumn
  so an upgraded DB recovers even if a migration row is lost.
- Remove committed <<<<<<< HEAD conflict marker in CONFIGURATION.md (P1)
- Move injectSkillIntentParam before the lastChatContext guard so the intent
  param is advertised even on tool.definition flights before first chat.message
- Key intentByCallId by sessionID:callID + prefix-prune on session delete so a
  concurrent session's delete can't evict another session's in-flight intents
- Log silent catch in promoteSkillObservations (observability for dropped writes)
- Anchor frontmatter regex to start-of-file (drop m flag) so a later --- rule
  can't be misparsed; strip inline # comments from unquoted YAML scalars + block header
- Scope distill report SQL to ('<identity>','*') instead of non-deterministic LIMIT 1
- Don't truncate skill name in TC: skill(<name>) marker (identity key); sanitize
  newlines/control chars
- Normalize backslash->slash after fileURLToPath for Windows provenance checks
- FTS self-heal rebuild in initializeDatabase when skill_memory_fts is empty but
  skill_memory has rows
- Move ctx_skill_recall _test* DI fields to a separate test-only deps type
- Hoist the shared registryKey dynamic import (one import, both blocks)

Pushback: reembed pre-step errors are already logged (task-executor.ts) — the
non-blocking try/catch is by design (failure leaves notes on the FTS rung).
…2/P3)

- P1: recall now unions the skill's own partition with the global '*' partition
  (recallPartitionPredicate helper) so a PROJECT-LOCAL skill surfaces
  historian-written global notes — previously orphaned (tier='project' query
  never matched tier='global'/'*'). Write/dedup paths stay exact-partition.
- Escape apostrophes in projectPath before SQL string interpolation in the
  distill prompt template.
- Frontmatter regex tolerates a leading UTF-8 BOM / whitespace (still start-anchored).
- TC: skill(<name>) marker emits the name VERBATIM when marker-safe, else drops
  it — never mutates the identity key (recall keys on raw input.name).
- Fix misleading frontmatter test: now actually exercises a '#' inside a quoted
  scalar (preserved) vs unquoted (comment-stripped).

Regression tests: project-local skill recalls a global historian note; agent
project note + historian global note both surface for the same skill.
…routing/parser gaps)

Council review (deepseek/sonnet/gpt-5.5) of PR cortexkit#181:

- Must (consensus rev-2+rev-3): the ctx_skill_note fail-loud guard threw during
  plugin init when the plugin is disabled (enabled:false OR conflict-disabled) —
  createSessionHooks returns {magicContext:null} by design, so the unconditional
  guard crashed the entry module on the disabled path. Gate it on
  pluginConfig.enabled; pass a throwaway Map to createToolRegistry (which
  early-returns {} when disabled and never reads it).
- Must (rev-3): singular ~/.config/opencode/skill/ global path was misclassified
  as project tier (opencode's pattern is {skill,skills}/**/SKILL.md) — fixed in
  deriveSkillTier/deriveSkillSource + the ctx_skill_recall cold-start search list.
- Must (rev-3): the frontmatter parser rejected the inline flow-mapping form
  'skill-memory: { enabled: true }' — the EXACT form the ctx_skill_recall
  remediation message and ARCHITECTURE/CONFIGURATION/README advertise. Added
  inline-mapping parsing so guidance and parser agree.
- Should (consensus rev-1+rev-3): recallSkillMemoryBlock swallowed all errors
  silently — added a log() so FTS/blob corruption is diagnosable (still no-throw).

Regression tests: inline frontmatter form (3 cases), singular skill/ global path
(2 cases).
- budgetFill now counts per-note XML framing (~20 tokens) so the rendered
  <skill-memory> block stays within max_tokens instead of ~13% overshoot (rev-1).
- clamp effective pinned budget to min(max_pinned_tokens, max_tokens) so the
  default 4000>1500 can't imply pinned gets more room than the whole block (rev-2).
- ctx_skill_recall: derive tier via dirname(resolvedPath) instead of a fragile
  .replace('/SKILL.md','') (rev-2).
Updated the budget-truncation test for the framing-inclusive math.
- provenance.ts: anchor the Base-directory regex to line-start (^…/gm) and take
  the LAST match — opencode appends the provenance line at the END of tool
  output, so a skill whose CONTENT echoes 'Base directory for this skill:' (e.g.
  a skill documenting skill-memory) would otherwise shadow the real line and
  misdirect recall to a bogus identity.
- read-session-formatting.ts: narrow the marker-safe exclusion to CR/LF/tab only
  — a ')' does not break the single-line TC: skill(<name>) marker and the
  historian reads it as natural language, so a ')'-containing name is preserved
  verbatim (identity key) instead of dropped.
- ARCHITECTURE.md: update the 'Skill-memory (motor memory)' Key Abstraction to
  the shipped reality (v50/51/52, multi-rung embedding+FTS recall, global-'*'
  union) — was stale (v37, 'P2 TODO'). Remove the PR-added duplicate
  'Tag Identity (v3.3.1+)' section (upstream owns the lean '## Tag identity';
  Tag Identity is unrelated to skill-memory — rebase scope-creep).

Regression tests: provenance last-match + mid-line rejection; ')' name preserved
+ CR/LF/tab still dropped.
…emory off

Rebase-onto-v0.29.0 resolution completion. Upstream ab4f01c added a
memory.enabled gate that drops ALL ctx_memory mentions from the system
prompt when memory is off (ctx_memory is then unregistered). The skill-memory
guidance carried a 'those belong in ctx_memory' cross-reference that violated
the new contract (buildMagicContextSection memory-gating tests). Parameterized
ctxSkillMemoryGuidance(memoryEnabled) so the cross-ref drops when memory is off;
skill-memory itself stays ungated (independent store).
…d in frontmatter

Counterpart to ctx_skill_recall's enabled-guard (greptile review, PR cortexkit#181):
without it, notes for skills that never opted in inserted successfully but
were permanently orphaned — recallSkillMemoryBlock returns "" when
frontmatter is disabled, while the agent saw a convincing 'Skill note
saved' response. Now returns an actionable error before any insert.

Red-checked: new regression test fails without the guard (orphan row
inserted + 'saved' response), passes with it (no row, 'not enabled').
…opencode #33580)

opencode's skill tool changed the 'Base directory for this skill:' line from a
file:// URL to a plain filesystem path (upstream #33580). Our parser hard-required
file:/// so parseSkillProvenance returned null on current opencode -> skill-load
registry never populated -> every agent ctx_skill_note failed with a provenance
parse error. Agent-written notes silently stopped 2026-07-02 (only historian-path
notes, which bypass this parser, continued).

Widen BASE_DIR_REGEX to capture the rest of the line and branch on the value:
file:// -> fileURLToPath (legacy/back-compat); otherwise treat as a plain path.
Keeps the line-anchor + last-match decoy-rejection invariant. +4 regression tests
(plain global, plain project, plain decoy last-match, plain mid-line-ignore); all
existing file:// tests unchanged.
… v50 collision

Upstream v0.31.0 added its own migration v50 (ctx-wrapup durable marker),
colliding with skill-memory's v50/51/52. Renumbered skill migrations to
v51 (skill_memory table) / v52 (embeddings+FTS) / v53 (historian extraction),
bumped LATEST_SUPPORTED_VERSION to 53, and rotated the migration test files
(v42/v51/v52 -> v51/v52/v53) with corrected internal version refs + fence
assertions.
…ath provenance case

cubic P2 (PR cortexkit#181): the 'PLAIN filesystem path for a PROJECT skill' test
asserted tier/skillSource but not resolvedPath — the only plain-path
project-skill test, so a path-join regression (missing /SKILL.md suffix,
bad concat) would go undetected. Added the resolvedPath assertion to match
the coverage pattern of every sibling test.
…(use provider-factory seam)

Bun mock.module is process-global and mock.restore() cannot undo it
cross-file in Bun 1.3.14. The skill-memory test files (reembed, recall,
ctx-skill-note) and promotion.test.ts each globally mocked the embedding
barrel, which bled into ctx-memory's provider-coordination tests (5s
timeouts) and into each other under CI worker sharding.

Converted all four files to the non-global seam that ctx-memory's own
tests use: _setTestProviderFactoryForProject + registerProjectEmbedding
with mandatory afterEach reset. Zero mock.module calls for any embedding
barrel remain in the test suite.
…runcated

Large skills (e.g. delegating at ~53KB) exceed opencode's MAX_BYTES=51200
tool-output truncation. The 'Base directory for this skill:' provenance line
sits after the full SKILL.md content, so it lands in the dropped tail →
parseSkillProvenance returns null → skillLoadRegistry never populates →
ctx_skill_note hard-fails and the transparent <skill-memory> injection no-ops.

Add resolveSkillPathByName (shared disk-walk in provenance.ts), wired into the
after-hook as a fallback when the primary parse returns null. The skill name is
always in the tool args (never truncated), so the fallback never depends on
parsing truncatable output. ctx_skill_recall's cold-start walk refactored to
reuse the same helper (removes duplication).

Cross-family reviewed (M3): APPROVE must=0.
- Fallback only resolves project-tier candidates when the session directory
  is authoritative (sessionDirectoryBySession hit); a launch-dir guess must
  not register a wrong same-named project skill (cubic P1). Global tier
  resolves from HOME regardless.
- Ancestor walk for project-tier candidates (nearest-first, bounded at 20
  levels, stops at $HOME/root) — sessions rooted in a worktree subdir now
  find repo-root project skills, matching opencode's discoverSkills walk-up.
- DB cleanup (try/finally closeQuietly) in the truncation tests.
- Drop redundant test-provider reset in reembed.test.ts.
The fallback's global-dir walk reads $HOME at call time; a developer
machine with a same-named global skill would flip the negative-registry
assertions. Override HOME to an empty tmpdir for the describe block.
Upstream v0.33.0 added migrations v54-v69 (authority identity, mirror
cursors, live-memory resnapshots, mural, message-FTS convergence),
colliding with the skill-memory slots. Renumbered skill P1/P2/P3a to
v70/71/72; LATEST_SUPPORTED_VERSION 69 -> 72.

Also adapts our tests to two upstream contract changes:
- executeStatus is async on this branch; upstream's new cortexkit#241 clamp tests
  needed await (they were added against the sync signature).
- the historian output contract now requires the tiered paraphrase
  structure, so the skill_observations fixtures emit <p1> instead of a
  flat compartment body (same update upstream made to its e2e fixtures).
- promotion.test.ts: upstream's two new embedding tests used the global
  mock.module('./embedding') this branch removed (CI mock bleed); they
  now use the non-global provider-factory seam.
resolveSkillPathByName's project-tier ancestor walk diverged from
opencode's real discovery (skill/index.ts calls fsys.up({ start:
directory, stop: worktree }), and that helper has no depth cap and
breaks AFTER checking the stop level):

- the 20-ancestor cap let a deeply nested session dir stop early, miss
  the project skill, and fall through to a same-named GLOBAL skill —
  registering the wrong tier and path;
- without a worktree boundary the walk kept climbing toward $HOME, so a
  skill in a repo ABOVE the worktree could resolve as this project's.

Both corrupt the registry silently rather than failing loudly.

Stop at the worktree root, checked AFTER the pattern checks so the root
level stays inclusive (matching up()'s semantics). Detect it with
existsSync(<dir>/.git) — true for a normal clone's directory and a linked
worktree's file alike. Drop the depth cap; the walk is already bounded by
stripping one segment per step. $HOME/root backstops stay for sessions
outside any repo.

Regression tests red-verified against the old code: deep-nesting returned
null, and the boundary case leaked a parent-repo skill.
Upstream v0.34.0 claimed v73 (todowrite permission verdict) and v74
(detected context-limit provenance), so the three skill-memory migrations
renumber v73/74/75 -> v75/76/77 and LATEST_SUPPORTED_VERSION follows to 77.
Their test files move with them; upstream's own migrations-v73/74.test.ts
are taken as-is.

Adapts to four upstream changes:

- executeStatus grew a `dreamer` parameter. Ours added `directory` for the
  skill-memory section; both are kept, dreamer first (upstream's position),
  directory appended.
- The tool.execute.after hook now awaits flushIgnoredMessages and reads
  `agent` off the input; the skill-memory branch and the callID field are
  additive alongside it.
- getDreamTaskBacklog's switch is exhaustive over DreamTaskName, so
  distill-skill-memory needed an arm. Returns 0/0 to match its
  always-eligible gate — the distill pass is whole-corpus maintenance with
  no per-item queue, and reaching into skill_memory internals would couple
  the scheduler to a table it does not otherwise touch.
- promotion.test.ts: upstream fixed the mock.module bleed itself (7eb943e,
  our issue cortexkit#279) using the same provider-factory seam, so upstream's file
  is taken verbatim and our now-redundant version dropped. The three
  skill-memory test files keep their seam conversions.

Two fence assertions relaxed from equality to a floor: migrations-v72 and
-v74 asserted LATEST_SUPPORTED_VERSION was exactly their own version, which
cannot hold once any migration is appended above them. The lockstep
assertion (LATEST_SUPPORTED_VERSION === LATEST_MIGRATION_VERSION) is the
invariant that matters and is kept in both.
…fork lane

Upstream v0.34.2 claimed v75 ("persist mural cue validation rejection latches"),
colliding with skill-memory P1 within hours of the last renumber. That is the
seventh renumber for this feature (v38 -> v39 -> v42 -> v54 -> v70 -> v73 -> v75)
and the collision class has twice made the runner skip a real migration body,
needing live-DB surgery to repair.

Upstream shipped the fix in v0.34.1 (docs/migration-version-lanes.md): versions
>= 10000 are reserved for downstream forks sharing context.db, and fork rows are
invisible to the upstream watermark and schema fence. This moves skill-memory
there:

  fork 10000  P1  skill_memory table
  fork 10001  P2  delta_embedding + recall_count + skill_memory_fts
  fork 10002  P3a origin_project + source_type + global '*' unification

Fork migrations live in a new fork-migrations.ts rather than in MIGRATIONS, so
runMigrations() and the fence constant derived from MIGRATIONS stay byte-identical
to upstream; runForkMigrations() runs as a second pass from storage-db.ts's open
path. Selection is by per-row presence, not by the upstream watermark -- that is
what makes the lane immune to the collision-skip. Rationale in the module header.

LATEST_SUPPORTED_VERSION returns to 75, byte-identical to upstream. Divergence in
upstream-owned files is now:
  schema-version-fence.test.ts  0 lines (byte-identical)
  migrations.ts                 +7/-2 (two export keywords + comments)

migrations-v10000/1/2.test.ts (renamed from v75/76/77): the co-located
"LATEST_SUPPORTED_VERSION === newest migration" mirrors asserted an UPSTREAM-lane
contract a fork migration does not participate in. Replaced with the contract
that does hold: the migration is present in FORK_MIGRATIONS and the fence stays
below the floor.

storage-db.test.ts: upstream's downstream-rows test hand-seeds floor+0/+1, which
this branch now genuinely owns (P1/P2). Moved the seed to floor+9000/+9001 so the
test still measures what it means -- that hand-inserted downstream rows survive
and stay fence-invisible.

fork-migrations.test.ts covers lane placement, cross-lane uniqueness, idempotent
re-run, the ordering contract, and that fork rows never advance the upstream
watermark. The load-bearing test drives the real openDatabase() path and asserts
the DDL landed, not just the bookkeeping rows -- red-checked by removing both
runForkMigrations calls, which leaves every other test green (a dead-on-arrival
seam) and fails only that one.

Gates: plugin 3728/0, pi 734/0, cli 296 (2 skip), typecheck 0 x3, lint clean,
tui-compiled reproducible, marker sweep clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant