[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M77K1MPPCWJMNKZPPDK4EXZP":3,"skill-same:01M77K1MPPCWJMNKZPPDK4EXZP":59},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":19,"linkOnly":20,"markdownContent":21,"markdownBody":22,"frontMatter":23,"refName":32,"commitSha":33,"skillDirectory":34,"forksCount":35,"repositoryHtmlUrl":36,"repositoryHomepageUrl":6,"ownerHtmlUrl":37,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":38,"install":39,"resources":52},"01M77K1MPPCWJMNKZPPDK4EXZP","memory",null,"Provider-agnostic durable-memory access for the Shipwright workflow. Use when loading context at phase start or storing distilled memories at phase end via \u002Fsw-memory-sync. Routes through configured provider; never calls providers directly.","grdavies\u002Fshipwright","grdavies","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F2638082?v=4","01M77JZCZJTWB8WBWX4NNW8MNP","Ross Davies","01M77JZD0S2Q4JZEM0310T9A73",2,"MIT","https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fmemory\u002FSKILL.md",[],[],"2026-11-15 14:19:17",false,"---\nname: memory\ndescription: Provider-agnostic durable-memory access for the Shipwright workflow. Use when loading context at phase start or storing distilled memories at phase end via \u002Fsw-memory-sync. Routes through configured provider; never calls providers directly.\nmetadata:\n  shipwright-capability:\n    version: 1\n    triggers:\n      -\n        type: phase_default\n        selectionFamily: memory\n        command: sw-memory-sync\n    metadata:\n      skill: memory\n      selectionFamily: memory\n---\n# memory-preflight\n\nThe single entry point every Shipwright command uses to read and write durable memory. It hides the\nprovider behind the capability spec in [`CAPABILITIES.md`](CAPABILITIES.md), so swapping providers is a\nconfig change, never a command edit.\n\n\n**Model tier:** cheap — resolve via `python3 scripts\u002Fsw_bootstrap.py resolve-model-tier.py -- --skill memory`. When using the Task tool for subagent dispatch, resolve concrete model IDs from `models.tiers` in config (never semantic tier names in subagent `model:` frontmatter).\n\n## Resolve the provider (first step, always)\n\n1. Load workflow config via `shipwright_paths.load_workflow_config(root)` (PRD 342 path authority) and read\n   `memory.provider`, `memory.project`, `memory.defaultScope`. When no config exists, check\n   `.cursor\u002Fsw-memory.provider` (per-repo marker) — if present **and** the marker text is literally\n   `in-repo`, provider is `in-repo` with project = workspace basename.\n2. **Write binding hard-cut (PRD 279 R9–R12 \u002F R14–R17; shared brainstorm numbering with PRD 278):**\n   mutating paths (`\u002Fsw-memory-sync` store, ordinary `store`\u002F`import` when `root=` is set) MUST call\n   `scripts\u002Fmemory_preflight.py` write assert (`assert_write_binding` \u002F\n   `memory_sync_store_path` \u002F `resolve_provider --for-write`) **immediately before** adapter dispatch.\n   Binding is either config `memory.provider` + non-empty `memory.project`, or marker\n   `.cursor\u002Fsw-memory.provider` = `in-repo` (basename project only). Unbound writes **refuse closed** with\n   a typed cause (`memory-write-unbound`, `memory-write-marker-remote-needs-project`,\n   `memory-write-project-missing`, `memory-write-global-refused`) and an audit line under\n   `.cursor\u002Fsw-memory-write-audit.jsonl`. There is **no** ambient Recallium write default and **no**\n   soft-warn write window. `__global__` is explicit-config only.\n3. **Read \u002F display resolve (D2):** unbound reads\u002Fpreflight may resolve for display without authorizing\n   writes (`resolve_provider` without `--for-write` → `source=unbound`, `displayGuidance=in-repo`,\n   `writeAuthorized=false`). Do **not** ambient-default display resolve to `recallium` when unbound.\n4. **Catalog + validator (before adapter or rule-fetch):** resolve the provider id through\n   `scripts\u002Fmemory_provider_register.py` against `.sw\u002Fmemory-provider-catalog.json` (emit:\n   `core\u002Fsw-reference\u002Fmemory-provider-catalog.json`). This runs on config write, startup\u002Fpreflight, and hook\n   trust — **unknown, malformed, or unregistered ids fail closed** (no cross-provider fallback).\n5. Load the adapter at `providers\u002F\u003Cmemory.provider>.md` from the plugin **only after** registration passes.\n   It defines the concrete tool calls and declares capability flags.\n6. **Hook \u002F rule-fetch trust:** catalog membership alone does **not** grant hook trust — `capability_trust.py`\n   gates and `validate_registration` must pass before `rules_script_for_provider` resolves the out-of-band\n   rules script. There is no silent swap to another provider's rule-fetcher.\n7. **Reachability vs empty:** provider **unreachable** (cannot confirm guardrail state) → degrade per surface\n   contract; hook `beforeSubmitPrompt` **blocks** when opted in. Provider **reachable with zero rule-class\n   memories** → **pass** (confirmed empty is normal for greenfield repos).\n\n## Pre-work search (mandatory)\n\nEvery **work-performing** command MUST run a scoped `memory-preflight` **search** (read mode) before its\nfirst substantive mutation. The enumerated surfaces are: `\u002Fsw-execute`, `\u002Fsw-debug` (`rca-core` entry),\n`\u002Fsw-prd`, `\u002Fsw-brainstorm`, `\u002Fsw-amend`, `\u002Fsw-review`, `\u002Fsw-stabilize`. Route through this skill +\n`providers\u002F\u003Cmemory.provider>.md` only — never call a provider tool directly (`sw-guardrails`).\n\n### Scoped read recipe\n\nFollow `CAPABILITIES.md` **Read recipe**, scoped to the surface being touched:\n\n1. **Recency OFF** (`recentOnly: false`) — durable facts are not recent-only.\n2. Run **scoped searches**, not one broad query:\n   - **file-path** search on the paths the command will touch,\n   - **semantic** search on the change type \u002F PRD \u002F feature \u002F surface,\n   - **category** narrowing when the adapter supports `categoryFilter`.\n3. Search these classes: `rule`, `decision`, `learning`, `code-context`, `playbook`, `design`.\n4. `expand` only the handful of ids that look relevant before mutation.\n\nPer-command scope hints remain in the table under **Read mode (preflight)** below; the obligation and\nrecipe above apply to every enumerated work-performing surface.\n\n### Surface and reconcile\n\nHits MUST be **surfaced to the acting agent before mutation** and **reconciled** against found\nrules\u002Fdecisions:\n\n- An **applicable `rule`** or a **contradicting prior `decision`** is a reconcile obligation — record\n  alignment or an explicit conflict + how it is resolved; never silently ignore.\n- A direct conflict with a **frozen** decision\u002Frule that cannot be reconciled is a **blocker** — halt per\n  the invoking command's halt contract (do not proceed with mutation).\n- Memory remains an input, not an authority — except `decision`-class SoT per **Source of truth\n  resolution** when memory-SoT is active.\n\n### Recording (mechanical)\n\nRecord the pre-work search breadcrumb before the first substantive mutation:\n\n```bash\npython3 scripts\u002Fwave.py memory prework record \\\n  --surface sw-execute \\\n  --scope \"core\u002Fskills\u002Fmemory\u002FSKILL.md\" \\\n  --classes rule,decision,learning,code-context,playbook,design \\\n  [--hit-count N]\n```\n\nThe shared recorder (`scripts\u002Fwave_memory_prework.py`) writes a redacted per-surface artifact to\n`.cursor\u002Fhooks\u002Fstate\u002Fmemory-prework-search.json` and appends an auditable line to\n`.cursor\u002Fsw-deliver-runs\u002Frun.log`. Outcomes:\n\n| Probe \u002F search | `outcome` | Gate behavior |\n| --- | --- | --- |\n| Provider unreachable (mechanical probe) | `memory:offline` | Satisfies degrade-open gate (R6) |\n| Search completed, zero relevant hits | `memory:none` | Satisfies gate (R7) |\n| Search completed with hits | `memory:hits` | Satisfies gate; hits surfaced + reconciled (R5) |\n\nOffline is **probe-gated** — never agent-asserted. Enforcement at the first file-mutating tool call\nreuses the PRD 017 `preToolUse` deny path (Phase 3).\n\n## Load-context orientation (R20)\n\nFor `load-context`, read the store's derived `index.md` **before** any search or expand. The index\ngroups memories by canonical category with title\u002Ffirst-line plus id for cheap orientation. When\n`index.md` is absent, run `python3 scripts\u002Fin-repo-memory-search.py maintain-derived --store \u003Cdir>`\nonce, then read the index.\n\n## Read mode (preflight)\n\nRun before doing the command's real work. Follow the read recipe in `CAPABILITIES.md`:\n\n- recency OFF unless the task is explicitly \"recent\",\n- scoped searches (file-path + semantic + optional category), not one broad query,\n- `expand` only the few relevant hits.\n\nPer-command read templates:\n\n| Command | What to retrieve |\n| --- | --- |\n| `\u002Fphase-execute` | PRD\u002Ftask memories; `code-context` for the target files (file-path search); domain `decision`\u002F`learning`. |\n| `\u002Fcoderabbit` | known false-positives; review patterns; `code-context` for changed paths. |\n| `\u002Fstabilize-pr` | prior CI-failure `debug`; bot-pattern `learning`; changed-path context. |\n| `\u002Fwatch-ci` | lazy — only search on a red run, scoped to the failing job\u002Fcheck. |\n| `\u002Fphase-start` | only on a non-routine branch decision (parent ambiguity, prior workflow correction). |\n\nMemory is an input, not an authority: git state, the per-repo `stateFile`, `agentsFile`, and repo\ndoctrine remain the sources of truth — except for the `decision` doc class when memory-SoT is active\n(see **Source of truth resolution** below).\n\n## Source of truth resolution (decision class only — R1–R3)\n\nThe `decision` doc class has a provider-conditional authoritative side. All other classes\n(`learning`, `design`, `debug`, etc.) remain **distillation-only** — the SoT switch does not apply.\n\nSingle source for freeze, compound, and audit:\n\n```bash\npython3 scripts\u002Fmemory-sot.py resolve --class decision --json\n# effective: repo | memory\n\npython3 scripts\u002Fmemory-sot.py resolve --class learning --json\n# effective: distillation (scope guard)\n```\n\n**Config:** `memory.sourceOfTruth` — `repo` | `memory` | `auto` (default `auto`).\n\n| Knob | Provider | Effective SoT |\n| --- | --- | --- |\n| `auto` | `recallium` (external) | `memory` |\n| `auto` | `in-repo` \u002F none | `repo` |\n| `repo` | any | `repo` |\n| `memory` | any | `memory` |\n\nDefault `auto` + `in-repo` preserves today's R32\u002FKTD3 behavior (no change for existing repos).\n\n### Migration gate (R30 — memory-authoritative providers)\n\nWhen `memory.sourceOfTruth` is **omitted** and the active provider's catalog row has\n`sourceOfTruthClass: memory-authoritative`, Shipwright **fail-closed** until the operator exports\ndecision bodies and sets an explicit knob:\n\n```bash\npython3 scripts\u002Fmemory-sot.py resolve --class decision --json\npython3 scripts\u002Fplanning-doctor.py --root .   # surfaces migration-required classification\n```\n\nRemediation: run `\u002Fsw-memory-export` (or `python3 scripts\u002Fmemory-sot.py` migration export command) to\nmaterialize provider decision bodies, then set `memory.sourceOfTruth` to `repo` or `memory` explicitly in\n`workflow.config.json`. Repo-authoritative providers (`in-repo`) with an omitted key resolve as\nimplicit-repo-default without migration.\n\n## Memory envelope v2 (R29)\n\nDistilled memories use the canonical v2 envelope (`scripts\u002Fmemory_envelope_v2.py`,\n`schemaVersion: 2`). Supersession creates a **new** envelope and marks the prior record `superseded` —\nno in-place mutation. Provider adapters map native storage onto this codec at read\u002Fwrite boundaries;\ncatalog `envelopeFields` (native \u002F sideChannel \u002F lossy) records per-provider fidelity.\n\n| Field | Role |\n| --- | --- |\n| `stableId` | Canonical memory id (filename stem for in-repo) |\n| `projectId` | `memory.project` scope |\n| `category` | Canonical doc class (`decision`, `learning`, `rule`, …) |\n| `status` | `active` or `superseded` |\n| `scope` | `{repos, paths, tags}` binding |\n| `evidenceRefs` | Citations \u002F backlinks |\n| `confidence` | Distillation confidence (0–1) |\n| `observedAt` | ISO timestamp of observation |\n| `lastValidatedAt` | Last validation stamp (nullable) |\n| `validUntil` | Optional expiry (nullable) |\n| `supersedes` | Prior `stableId` chain |\n| `contentHash` | SHA-256 over semantic body |\n| `schemaVersion` | Always `2` for new records |\n| `sensitivity` | `public` \\| `internal` \\| `private` \\| `secret` |\n| `appliedRedaction` | Provenance record from the redaction chokepoint |\n\nv1 records upgrade via `scripts\u002Fmemory_envelope_upgrade.py`; alias merges persist in\n`.cursor\u002Fsw-memory\u002Falias-ledger\u002F`.\n\n## Destination-aware redaction (R29\u002FR41 — mandatory before persist\u002Fre-inject)\n\nRedaction is **destination-tier aware**. The `--destination` argument is **required** — there is no\ndefault. Valid tiers: `local`, `committed`, `external`, `cross-project`, `logs`. Learning-store \u002F\nexogenous-routing derivations (PRD 272) must pass the same `scripts\u002Fmemory_redact.py` chokepoint before\nadmission — provenance fields may retain pointers; raw payloads never skip the filter.\n\n```bash\npython3 scripts\u002Fsw_bootstrap.py memory-redact.py --destination external \u003C\u003C'EOF'\n\u003Cpayload>\nEOF\n```\n\n| Destination | Residual secrets |\n| --- | --- |\n| `external`, `cross-project`, `logs` | **Fail-closed** — non-zero exit when patterns remain |\n| `local`, `committed` | Advisory — residuals logged; write may proceed |\n\nNamed egress enforcement points (`\u002Fsw-memory-sync`, cross-project copy) call\n`enforce_egress(..., destination_tier=...)` and stamp `appliedRedaction` via\n`scripts\u002Fmemory_redaction_provenance.py`. See `rules\u002Fmemory-guardrails.mdc` for the full contract.\n\n## Portability and audit commands\n\n| Command | Doc | Purpose |\n| --- | --- | --- |\n| `\u002Fsw-memory-export` | `core\u002Fcommands\u002Fsw-memory-export.md` | Neutral JSONL\u002FOKF export for backup or provider swap |\n| `\u002Fsw-memory-import` | `core\u002Fcommands\u002Fsw-memory-import.md` | Import neutral interchange (human-gated for `rule` class) |\n| `\u002Fsw-memory-sync` | `core\u002Fcommands\u002Fsw-memory-sync.md` | Distill transcript deltas into the provider |\n| `\u002Fsw-memory-audit` | `core\u002Fcommands\u002Fsw-memory-audit.md` | Inspect, propose cleanup, human-gated rule promotion |\n\nRoute all four through this skill + `providers\u002F\u003Cmemory.provider>.md` — never direct provider tool calls.\n\n### Rule promote integrity (PRD 277 D5–D6)\n\n**D5 — needs-reconcile on partial failure.** `scripts\u002Fmemory_rules_promote.py` writes through the\nadapter first. Allowlist commit happens only after `verdict: ok`. A failed or incomplete provider write\nrecords `.cursor\u002Fsw-memory\u002Fneeds-reconcile.json` and **does not** append the rule id to the allowlist.\n`memory-preflight` `rules-load` fail-closes while that file exists (`cause=needs-reconcile`).\n\n**D6 — integrity is id + hash.** Audit approval binds `ruleId` and `contentHash` (SHA-256 of the body).\nA hash or id mismatch refuses the write (`rule-write-hash-mismatch` \u002F `rule-write-id-mismatch`); the\noperator must re-approve the current body.\n\n## Redaction chokepoint (R41 — mandatory before persist\u002Fre-inject)\n\nBefore **any** `store`, transcript distillation (`\u002Fsw-memory-sync`), or compounding write, pipe content\nthrough the executable filter:\n\n```bash\npython3 scripts\u002Fsw_bootstrap.py memory-redact.py --destination committed \u003C\u003C'EOF'\n\u003Cpayload>\nEOF\n```\n\nOr: `python3 scripts\u002Fsw_bootstrap.py memory-redact.py --destination committed -- path\u002Fto\u002Ffile`. The filter is deterministic (same input → same output),\nruns offline, and scrubs the named corpus in `rules\u002Fmemory-guardrails.mdc` (AWS keys, GitHub PATs, JWTs,\n`Bearer` tokens, PEM private keys, emails). Never persist or re-inject unredacted content.\n\n**Fail-closed (R10):** if `memory-redact.py` exits non-zero, abort the provider write **and** any snapshot\nwrite (`scripts\u002Fmemory-decision-snapshot.py write` propagates the failure). A provider outage after a\nsuccessful snapshot stamp degrades to the committed snapshot with a warning — never block freeze\u002FCI.\n\n## Write mode (after substantive work)\n\nStore distilled memories per the write contract in `CAPABILITIES.md`:\n\n- **assert write binding first** (PRD 279): `python3 scripts\u002Fmemory_preflight.py assert-sync-store`\n  (or `resolve-provider --for-write`) — refuse unbound; never ambient-default to Recallium,\n- pick the canonical category (decision \u002F learning \u002F debug \u002F design \u002F code-context \u002F research \u002F\n  discussion); never a generic catch-all,\n- set `relatedFiles` + stable tags (`prd-\u003Cn>`, `task-\u003Cn>`, `surface:\u003Ccmd>`),\n- **run `scripts\u002Fmemory-redact.py` on the payload first** (R41 chokepoint),\n- search before store; `modify` a near-duplicate instead of adding a second,\n- project scope by default; global only on explicit user direction (`__global__` requires explicit\n  config binding),\n- store the distilled substance, never a raw transcript dump.\n- populate `title` and `description` frontmatter at store time from the distilled first line when omitted (`memory-preflight` write path).\n\nFor **`decision`-class** writes, resolve the inverted pointer recipe first (R6):\n\n```bash\npython3 scripts\u002Fmemory-sot.py pointer-recipe --path docs\u002Fdecisions\u002F\u003Cn>-\u003Cslug>.md [--memory-id \u003Cprovider-id>] --json\n```\n\n| Effective SoT | Provider write | Git snapshot |\n| --- | --- | --- |\n| `repo` | Pointer only — `relatedFiles: [docs\u002Fdecisions\u002F...]`; never the record body | Authoritative (`snapshotRole: authoritative`) |\n| `memory` | Content-bearing authoritative record (redacted) | Pointer (`snapshotRole: pointer`, `memoryPointer`) |\n\nExactly one side is authoritative at a time; the recipe JSON names `authoritative` vs `nonAuthoritative`.\n\nStore on: a decision with rationale, a non-obvious lesson, a bug root-cause+fix, an architecture choice,\na notable review\u002FCI pattern, or a distilled session recap. Do not store routine, recoverable steps.\n\n## Capability degradation\n\nCheck the adapter's flags and adjust:\n\n- no `semanticSearch` → run `scripts\u002Fin-repo-memory-search.py` (keyword + frontmatter filters) instead of\n  vector search; results feed `expand`,\n- no `tasks` → the phase board uses the local registry fallback,\n- no `filePathSearch` → semantic search on the path string,\n- no `categoryFilter` → skip category narrowing \u002F post-filter,\n- no `export` → provider swap relies on re-distillation from raw transcripts.\n\n## In-repo provider specifics (`memory.provider: in-repo`)\n\n**Read:** when `semanticSearch:false`, call:\n\n```bash\npython3 scripts\u002Fin-repo-memory-search.py \\\n  --store .cursor\u002Fsw-memory \\\n  --query \"\u003Cterms>\" \\\n  [--category decision] [--tag prd-1] [--file-glob src\u002Fauth.ts]\n```\n\nThen `expand` via `python3 scripts\u002Fin-repo-memory-search.py expand --store \u003Cdir> --ids \u003Cid>` (full body + backlinks), or read `memories\u002F\u003Cid>.md` directly.\n\n**Write:**\n\n1. Lazy-create store dirs on first write: `mkdir -p .cursor\u002Fsw-memory\u002Fmemories .cursor\u002Fsw-memory\u002Frules`.\n2. Pipe payload through `scripts\u002Fmemory-redact.py` (R41) before any file write.\n3. Default `commitMode: committed` → write non-rule files under `.cursor\u002Fsw-memory\u002Fmemories\u002F`.\n4. `commitMode: local` → non-rule files under `.cursor\u002Fsw-memory-local\u002Fmemories\u002F` (gitignored).\n5. **`category: rule` always writes to `.cursor\u002Fsw-memory\u002Frules\u002F`** — offline hook reads committed rules.\n6. Never auto-seed starter rules; store starts empty.\n\n## Planning store memory backend (PRD 034 R11\u002FR23; PRD 057 R21 — 21a local cache + 21b provider round-trip)\n\nWhen `planning.store.backend` is `memory`, unit **bodies** are cached under a **gitignored** directory\n(`.cursor\u002Fsw-memory\u002Fplanning-bodies\u002F\u003Cmemory.project>\u002F`, `MemoryLocalCacheBackend` in\n`scripts\u002Fplanning_store.py`) that always passes content through `scripts\u002Fmemory-redact.py` on read and\nwrite — **body storage only**; the memory backend does not alter source-of-truth for any planning class.\n\nThis local cache is always written unconditionally regardless of whether a memory provider is configured\n(21a — PRD 057 R21), and is never bypassed: it remains the fast-path read and the fallback whenever the\nprovider round-trip below is unavailable. On top of it, 21b adds a real round-trip through the configured\nprovider's REST adapter (Recallium only, at present): `put()` best-effort mirrors the redacted body to a\ndedicated `\u002Fplanning-bodies\u002F\u003CunitId>` REST resource guarded by the same loopback-only base-URL check as\n`providers\u002Frecallium-rules.py`, and `get()` recovers content through that same adapter when the local cache\nis absent (e.g. a fresh checkout on another machine). Each cached body's frontmatter records\n`configuredProvider` (informational — names whichever provider is configured for this skill's other memory\noperations), plus `providerRoundTrip`\u002F`providerRoundTripReason` (whether *this* body actually round-tripped,\nand why not when it didn't). See `core\u002Fproviders\u002Fplanning-store\u002Fmemory.md` for the full contract and\n`scripts\u002Ftest\u002Ffixtures\u002Fmemory-roundtrip\u002Fharness.py` for the offline fixture covering both the round-trip and\nevery fallback path.\n\nDecision-class units under `docs\u002Fplanning\u002Fdecision\u002F` still follow the PRD-015 committed redacted snapshot +\npointer flow regardless of `visibility`. The authoritative decision record paths remain\n`docs\u002Fdecisions\u002F\u003Cn>-\u003Cslug>.md` (repo-SoT) or the provider record (memory-SoT) per **Source of truth\nresolution** below — the planning-store memory backend never replaces that contract.\n\n## Decision records (file-linked deliverables)\n\n**Boundary rule (R32 \u002F KTD3 + provider-conditional SoT):**\n\nResolve authority first: `python3 scripts\u002Fmemory-sot.py resolve --class decision --json`.\n\n| Effective SoT | Authoritative artifact | Memory role |\n| --- | --- | --- |\n| `repo` | Decision record (`docs\u002Fdecisions\u002F\u003Cn>-\u003Cslug>.md`) | Pointer via `relatedFiles` only |\n| `memory` | Provider `decision` record (redacted) | Content-bearing; git snapshot is pointer |\n\n| Artifact | Role | Mutable | CI freeze |\n|----------|------|---------|-----------|\n| Decision record (`docs\u002Fdecisions\u002F\u003Cn>-\u003Cslug>.md`) | Up-front, reviewed-before-build deliverable | Only via amendment | Yes |\n| `decision`-class memory | Retrospective knowledge distillation | Yes | No |\n\nWhen repo-SoT is active (default for `in-repo`):\n\n- Read: load the record from git; memory may point at it via `relatedFiles` but is not authoritative.\n- Write: store a pointer (`relatedFiles: [docs\u002Fdecisions\u002F...]`), never the record body.\n- Flag content-bearing `decision` memories that duplicate an existing record — they should become pointers.\n\nWhen memory-SoT is active, the provider record is authoritative; the committed git snapshot carries a\nforward pointer (`memoryPointer` in frontmatter — see `scripts\u002Fmemory-decision-snapshot.py write`).\n\n**Supersede reconciliation (`docs\u002Fdecisions\u002FSUPERSEDED.log`):**\n\nOn record-level supersede, append the superseded path via:\n\n```bash\npython3 scripts\u002Freconcile.py append-superseded --path docs\u002Fdecisions\u002F\u003Cold>.md --replacement docs\u002Fdecisions\u002F\u003Cnew>.md\n```\n\n`\u002Fsw-memory-sync` runs `python3 scripts\u002Freconcile.py supersede-reconcile --json` after distillation and\nbest-effort re-points the **non-authoritative** side per the active SoT (provider `relatedFiles` under\nrepo-SoT; git snapshot pointer under memory-SoT). Pointer freshness is **auditable, not transactional**\n(provider out of CI reach).\n\n\n## Issue-store brainstorm distillation (PRD 043 R19)\n\nWhen `planning.store.backend` is `issue-store` and a PRD is frozen via `planning_store.py freeze`:\n\n1. Linked brainstorm content (from `sw-edges` \u002F `link-brainstorm-prd`) is excerpted and piped through\n   `scripts\u002Fmemory-redact.py` — no raw transcript.\n2. Distilled `research` entry is stored via the memory backend's local-only cache\n   (`MemoryLocalCacheBackend` \u002F `planning.store.backend: memory` bodies path — 21a, see above).\n3. A `sw-memory-pointer` comment on the brainstorm issue links PRD ↔ memory ↔ brainstorm.\n4. Brainstorm issue is **closed+linked**, never deleted.\n\nFailure at distillation → `sw:freeze-incomplete` on the PRD issue; deliver halts fail-closed.\n\n## Cross-project recall (PRD 046 R90 \u002F PRD 043 R27)\n\nWhen issue-store is active, rationale and distilled learnings may be recalled across `projectKey`\nboundaries via memory pointers — never by duplicating deliverable bodies into memory.\n\n```bash\npython3 scripts\u002Fplanning_cross_project_recall.py recall   --payload-json '{\"sourceProjectKey\":\"proj-a\",\"callerProjectKey\":\"proj-b\",\"query\":\"rationale\",\"pointers\":[...],\"authorizedProjects\":[\"proj-a\"]}'\n```\n\n- **Scope:** queries are keyed by source `projectKey` + caller authorization (`authorizedProjects`).\n- **Redaction:** pointer dereference passes through `scripts\u002Fmemory-redact.py` on read; `private`\u002F`memory`\n  visibility emits opaque excerpts (`{unitId}: [private]`) — project B cannot read project A private rationale.\n- **Ranking:** deterministic tie-break (`projectKey`, `unitId`, `memoryId`).\n- **No duplication:** deliverable content stays in planning artifacts; memory holds pointers and redacted\n  distillations only.\n\nRoute all cross-project recall through this skill + `providers\u002F\u003Cmemory.provider>.md` — never direct provider\ncalls from deliver or planning-graph code.\n\n\n## Boundaries\n\n- Never call a provider tool directly from a command; always go through this skill + the adapter.\n- Never write `rule`-category memories unless the user explicitly asks (\"remember this\", \"add a rule\").\n- Never store secrets, credentials, tokens, or raw transcript content.\n","# memory-preflight\n\nThe single entry point every Shipwright command uses to read and write durable memory. It hides the\nprovider behind the capability spec in [`CAPABILITIES.md`](CAPABILITIES.md), so swapping providers is a\nconfig change, never a command edit.\n\n\n**Model tier:** cheap — resolve via `python3 scripts\u002Fsw_bootstrap.py resolve-model-tier.py -- --skill memory`. When using the Task tool for subagent dispatch, resolve concrete model IDs from `models.tiers` in config (never semantic tier names in subagent `model:` frontmatter).\n\n## Resolve the provider (first step, always)\n\n1. Load workflow config via `shipwright_paths.load_workflow_config(root)` (PRD 342 path authority) and read\n   `memory.provider`, `memory.project`, `memory.defaultScope`. When no config exists, check\n   `.cursor\u002Fsw-memory.provider` (per-repo marker) — if present **and** the marker text is literally\n   `in-repo`, provider is `in-repo` with project = workspace basename.\n2. **Write binding hard-cut (PRD 279 R9–R12 \u002F R14–R17; shared brainstorm numbering with PRD 278):**\n   mutating paths (`\u002Fsw-memory-sync` store, ordinary `store`\u002F`import` when `root=` is set) MUST call\n   `scripts\u002Fmemory_preflight.py` write assert (`assert_write_binding` \u002F\n   `memory_sync_store_path` \u002F `resolve_provider --for-write`) **immediately before** adapter dispatch.\n   Binding is either config `memory.provider` + non-empty `memory.project`, or marker\n   `.cursor\u002Fsw-memory.provider` = `in-repo` (basename project only). Unbound writes **refuse closed** with\n   a typed cause (`memory-write-unbound`, `memory-write-marker-remote-needs-project`,\n   `memory-write-project-missing`, `memory-write-global-refused`) and an audit line under\n   `.cursor\u002Fsw-memory-write-audit.jsonl`. There is **no** ambient Recallium write default and **no**\n   soft-warn write window. `__global__` is explicit-config only.\n3. **Read \u002F display resolve (D2):** unbound reads\u002Fpreflight may resolve for display without authorizing\n   writes (`resolve_provider` without `--for-write` → `source=unbound`, `displayGuidance=in-repo`,\n   `writeAuthorized=false`). Do **not** ambient-default display resolve to `recallium` when unbound.\n4. **Catalog + validator (before adapter or rule-fetch):** resolve the provider id through\n   `scripts\u002Fmemory_provider_register.py` against `.sw\u002Fmemory-provider-catalog.json` (emit:\n   `core\u002Fsw-reference\u002Fmemory-provider-catalog.json`). This runs on config write, startup\u002Fpreflight, and hook\n   trust — **unknown, malformed, or unregistered ids fail closed** (no cross-provider fallback).\n5. Load the adapter at `providers\u002F\u003Cmemory.provider>.md` from the plugin **only after** registration passes.\n   It defines the concrete tool calls and declares capability flags.\n6. **Hook \u002F rule-fetch trust:** catalog membership alone does **not** grant hook trust — `capability_trust.py`\n   gates and `validate_registration` must pass before `rules_script_for_provider` resolves the out-of-band\n   rules script. There is no silent swap to another provider's rule-fetcher.\n7. **Reachability vs empty:** provider **unreachable** (cannot confirm guardrail state) → degrade per surface\n   contract; hook `beforeSubmitPrompt` **blocks** when opted in. Provider **reachable with zero rule-class\n   memories** → **pass** (confirmed empty is normal for greenfield repos).\n\n## Pre-work search (mandatory)\n\nEvery **work-performing** command MUST run a scoped `memory-preflight` **search** (read mode) before its\nfirst substantive mutation. The enumerated surfaces are: `\u002Fsw-execute`, `\u002Fsw-debug` (`rca-core` entry),\n`\u002Fsw-prd`, `\u002Fsw-brainstorm`, `\u002Fsw-amend`, `\u002Fsw-review`, `\u002Fsw-stabilize`. Route through this skill +\n`providers\u002F\u003Cmemory.provider>.md` only — never call a provider tool directly (`sw-guardrails`).\n\n### Scoped read recipe\n\nFollow `CAPABILITIES.md` **Read recipe**, scoped to the surface being touched:\n\n1. **Recency OFF** (`recentOnly: false`) — durable facts are not recent-only.\n2. Run **scoped searches**, not one broad query:\n   - **file-path** search on the paths the command will touch,\n   - **semantic** search on the change type \u002F PRD \u002F feature \u002F surface,\n   - **category** narrowing when the adapter supports `categoryFilter`.\n3. Search these classes: `rule`, `decision`, `learning`, `code-context`, `playbook`, `design`.\n4. `expand` only the handful of ids that look relevant before mutation.\n\nPer-command scope hints remain in the table under **Read mode (preflight)** below; the obligation and\nrecipe above apply to every enumerated work-performing surface.\n\n### Surface and reconcile\n\nHits MUST be **surfaced to the acting agent before mutation** and **reconciled** against found\nrules\u002Fdecisions:\n\n- An **applicable `rule`** or a **contradicting prior `decision`** is a reconcile obligation — record\n  alignment or an explicit conflict + how it is resolved; never silently ignore.\n- A direct conflict with a **frozen** decision\u002Frule that cannot be reconciled is a **blocker** — halt per\n  the invoking command's halt contract (do not proceed with mutation).\n- Memory remains an input, not an authority — except `decision`-class SoT per **Source of truth\n  resolution** when memory-SoT is active.\n\n### Recording (mechanical)\n\nRecord the pre-work search breadcrumb before the first substantive mutation:\n\n```bash\npython3 scripts\u002Fwave.py memory prework record \\\n  --surface sw-execute \\\n  --scope \"core\u002Fskills\u002Fmemory\u002FSKILL.md\" \\\n  --classes rule,decision,learning,code-context,playbook,design \\\n  [--hit-count N]\n```\n\nThe shared recorder (`scripts\u002Fwave_memory_prework.py`) writes a redacted per-surface artifact to\n`.cursor\u002Fhooks\u002Fstate\u002Fmemory-prework-search.json` and appends an auditable line to\n`.cursor\u002Fsw-deliver-runs\u002Frun.log`. Outcomes:\n\n| Probe \u002F search | `outcome` | Gate behavior |\n| --- | --- | --- |\n| Provider unreachable (mechanical probe) | `memory:offline` | Satisfies degrade-open gate (R6) |\n| Search completed, zero relevant hits | `memory:none` | Satisfies gate (R7) |\n| Search completed with hits | `memory:hits` | Satisfies gate; hits surfaced + reconciled (R5) |\n\nOffline is **probe-gated** — never agent-asserted. Enforcement at the first file-mutating tool call\nreuses the PRD 017 `preToolUse` deny path (Phase 3).\n\n## Load-context orientation (R20)\n\nFor `load-context`, read the store's derived `index.md` **before** any search or expand. The index\ngroups memories by canonical category with title\u002Ffirst-line plus id for cheap orientation. When\n`index.md` is absent, run `python3 scripts\u002Fin-repo-memory-search.py maintain-derived --store \u003Cdir>`\nonce, then read the index.\n\n## Read mode (preflight)\n\nRun before doing the command's real work. Follow the read recipe in `CAPABILITIES.md`:\n\n- recency OFF unless the task is explicitly \"recent\",\n- scoped searches (file-path + semantic + optional category), not one broad query,\n- `expand` only the few relevant hits.\n\nPer-command read templates:\n\n| Command | What to retrieve |\n| --- | --- |\n| `\u002Fphase-execute` | PRD\u002Ftask memories; `code-context` for the target files (file-path search); domain `decision`\u002F`learning`. |\n| `\u002Fcoderabbit` | known false-positives; review patterns; `code-context` for changed paths. |\n| `\u002Fstabilize-pr` | prior CI-failure `debug`; bot-pattern `learning`; changed-path context. |\n| `\u002Fwatch-ci` | lazy — only search on a red run, scoped to the failing job\u002Fcheck. |\n| `\u002Fphase-start` | only on a non-routine branch decision (parent ambiguity, prior workflow correction). |\n\nMemory is an input, not an authority: git state, the per-repo `stateFile`, `agentsFile`, and repo\ndoctrine remain the sources of truth — except for the `decision` doc class when memory-SoT is active\n(see **Source of truth resolution** below).\n\n## Source of truth resolution (decision class only — R1–R3)\n\nThe `decision` doc class has a provider-conditional authoritative side. All other classes\n(`learning`, `design`, `debug`, etc.) remain **distillation-only** — the SoT switch does not apply.\n\nSingle source for freeze, compound, and audit:\n\n```bash\npython3 scripts\u002Fmemory-sot.py resolve --class decision --json\n# effective: repo | memory\n\npython3 scripts\u002Fmemory-sot.py resolve --class learning --json\n# effective: distillation (scope guard)\n```\n\n**Config:** `memory.sourceOfTruth` — `repo` | `memory` | `auto` (default `auto`).\n\n| Knob | Provider | Effective SoT |\n| --- | --- | --- |\n| `auto` | `recallium` (external) | `memory` |\n| `auto` | `in-repo` \u002F none | `repo` |\n| `repo` | any | `repo` |\n| `memory` | any | `memory` |\n\nDefault `auto` + `in-repo` preserves today's R32\u002FKTD3 behavior (no change for existing repos).\n\n### Migration gate (R30 — memory-authoritative providers)\n\nWhen `memory.sourceOfTruth` is **omitted** and the active provider's catalog row has\n`sourceOfTruthClass: memory-authoritative`, Shipwright **fail-closed** until the operator exports\ndecision bodies and sets an explicit knob:\n\n```bash\npython3 scripts\u002Fmemory-sot.py resolve --class decision --json\npython3 scripts\u002Fplanning-doctor.py --root .   # surfaces migration-required classification\n```\n\nRemediation: run `\u002Fsw-memory-export` (or `python3 scripts\u002Fmemory-sot.py` migration export command) to\nmaterialize provider decision bodies, then set `memory.sourceOfTruth` to `repo` or `memory` explicitly in\n`workflow.config.json`. Repo-authoritative providers (`in-repo`) with an omitted key resolve as\nimplicit-repo-default without migration.\n\n## Memory envelope v2 (R29)\n\nDistilled memories use the canonical v2 envelope (`scripts\u002Fmemory_envelope_v2.py`,\n`schemaVersion: 2`). Supersession creates a **new** envelope and marks the prior record `superseded` —\nno in-place mutation. Provider adapters map native storage onto this codec at read\u002Fwrite boundaries;\ncatalog `envelopeFields` (native \u002F sideChannel \u002F lossy) records per-provider fidelity.\n\n| Field | Role |\n| --- | --- |\n| `stableId` | Canonical memory id (filename stem for in-repo) |\n| `projectId` | `memory.project` scope |\n| `category` | Canonical doc class (`decision`, `learning`, `rule`, …) |\n| `status` | `active` or `superseded` |\n| `scope` | `{repos, paths, tags}` binding |\n| `evidenceRefs` | Citations \u002F backlinks |\n| `confidence` | Distillation confidence (0–1) |\n| `observedAt` | ISO timestamp of observation |\n| `lastValidatedAt` | Last validation stamp (nullable) |\n| `validUntil` | Optional expiry (nullable) |\n| `supersedes` | Prior `stableId` chain |\n| `contentHash` | SHA-256 over semantic body |\n| `schemaVersion` | Always `2` for new records |\n| `sensitivity` | `public` \\| `internal` \\| `private` \\| `secret` |\n| `appliedRedaction` | Provenance record from the redaction chokepoint |\n\nv1 records upgrade via `scripts\u002Fmemory_envelope_upgrade.py`; alias merges persist in\n`.cursor\u002Fsw-memory\u002Falias-ledger\u002F`.\n\n## Destination-aware redaction (R29\u002FR41 — mandatory before persist\u002Fre-inject)\n\nRedaction is **destination-tier aware**. The `--destination` argument is **required** — there is no\ndefault. Valid tiers: `local`, `committed`, `external`, `cross-project`, `logs`. Learning-store \u002F\nexogenous-routing derivations (PRD 272) must pass the same `scripts\u002Fmemory_redact.py` chokepoint before\nadmission — provenance fields may retain pointers; raw payloads never skip the filter.\n\n```bash\npython3 scripts\u002Fsw_bootstrap.py memory-redact.py --destination external \u003C\u003C'EOF'\n\u003Cpayload>\nEOF\n```\n\n| Destination | Residual secrets |\n| --- | --- |\n| `external`, `cross-project`, `logs` | **Fail-closed** — non-zero exit when patterns remain |\n| `local`, `committed` | Advisory — residuals logged; write may proceed |\n\nNamed egress enforcement points (`\u002Fsw-memory-sync`, cross-project copy) call\n`enforce_egress(..., destination_tier=...)` and stamp `appliedRedaction` via\n`scripts\u002Fmemory_redaction_provenance.py`. See `rules\u002Fmemory-guardrails.mdc` for the full contract.\n\n## Portability and audit commands\n\n| Command | Doc | Purpose |\n| --- | --- | --- |\n| `\u002Fsw-memory-export` | `core\u002Fcommands\u002Fsw-memory-export.md` | Neutral JSONL\u002FOKF export for backup or provider swap |\n| `\u002Fsw-memory-import` | `core\u002Fcommands\u002Fsw-memory-import.md` | Import neutral interchange (human-gated for `rule` class) |\n| `\u002Fsw-memory-sync` | `core\u002Fcommands\u002Fsw-memory-sync.md` | Distill transcript deltas into the provider |\n| `\u002Fsw-memory-audit` | `core\u002Fcommands\u002Fsw-memory-audit.md` | Inspect, propose cleanup, human-gated rule promotion |\n\nRoute all four through this skill + `providers\u002F\u003Cmemory.provider>.md` — never direct provider tool calls.\n\n### Rule promote integrity (PRD 277 D5–D6)\n\n**D5 — needs-reconcile on partial failure.** `scripts\u002Fmemory_rules_promote.py` writes through the\nadapter first. Allowlist commit happens only after `verdict: ok`. A failed or incomplete provider write\nrecords `.cursor\u002Fsw-memory\u002Fneeds-reconcile.json` and **does not** append the rule id to the allowlist.\n`memory-preflight` `rules-load` fail-closes while that file exists (`cause=needs-reconcile`).\n\n**D6 — integrity is id + hash.** Audit approval binds `ruleId` and `contentHash` (SHA-256 of the body).\nA hash or id mismatch refuses the write (`rule-write-hash-mismatch` \u002F `rule-write-id-mismatch`); the\noperator must re-approve the current body.\n\n## Redaction chokepoint (R41 — mandatory before persist\u002Fre-inject)\n\nBefore **any** `store`, transcript distillation (`\u002Fsw-memory-sync`), or compounding write, pipe content\nthrough the executable filter:\n\n```bash\npython3 scripts\u002Fsw_bootstrap.py memory-redact.py --destination committed \u003C\u003C'EOF'\n\u003Cpayload>\nEOF\n```\n\nOr: `python3 scripts\u002Fsw_bootstrap.py memory-redact.py --destination committed -- path\u002Fto\u002Ffile`. The filter is deterministic (same input → same output),\nruns offline, and scrubs the named corpus in `rules\u002Fmemory-guardrails.mdc` (AWS keys, GitHub PATs, JWTs,\n`Bearer` tokens, PEM private keys, emails). Never persist or re-inject unredacted content.\n\n**Fail-closed (R10):** if `memory-redact.py` exits non-zero, abort the provider write **and** any snapshot\nwrite (`scripts\u002Fmemory-decision-snapshot.py write` propagates the failure). A provider outage after a\nsuccessful snapshot stamp degrades to the committed snapshot with a warning — never block freeze\u002FCI.\n\n## Write mode (after substantive work)\n\nStore distilled memories per the write contract in `CAPABILITIES.md`:\n\n- **assert write binding first** (PRD 279): `python3 scripts\u002Fmemory_preflight.py assert-sync-store`\n  (or `resolve-provider --for-write`) — refuse unbound; never ambient-default to Recallium,\n- pick the canonical category (decision \u002F learning \u002F debug \u002F design \u002F code-context \u002F research \u002F\n  discussion); never a generic catch-all,\n- set `relatedFiles` + stable tags (`prd-\u003Cn>`, `task-\u003Cn>`, `surface:\u003Ccmd>`),\n- **run `scripts\u002Fmemory-redact.py` on the payload first** (R41 chokepoint),\n- search before store; `modify` a near-duplicate instead of adding a second,\n- project scope by default; global only on explicit user direction (`__global__` requires explicit\n  config binding),\n- store the distilled substance, never a raw transcript dump.\n- populate `title` and `description` frontmatter at store time from the distilled first line when omitted (`memory-preflight` write path).\n\nFor **`decision`-class** writes, resolve the inverted pointer recipe first (R6):\n\n```bash\npython3 scripts\u002Fmemory-sot.py pointer-recipe --path docs\u002Fdecisions\u002F\u003Cn>-\u003Cslug>.md [--memory-id \u003Cprovider-id>] --json\n```\n\n| Effective SoT | Provider write | Git snapshot |\n| --- | --- | --- |\n| `repo` | Pointer only — `relatedFiles: [docs\u002Fdecisions\u002F...]`; never the record body | Authoritative (`snapshotRole: authoritative`) |\n| `memory` | Content-bearing authoritative record (redacted) | Pointer (`snapshotRole: pointer`, `memoryPointer`) |\n\nExactly one side is authoritative at a time; the recipe JSON names `authoritative` vs `nonAuthoritative`.\n\nStore on: a decision with rationale, a non-obvious lesson, a bug root-cause+fix, an architecture choice,\na notable review\u002FCI pattern, or a distilled session recap. Do not store routine, recoverable steps.\n\n## Capability degradation\n\nCheck the adapter's flags and adjust:\n\n- no `semanticSearch` → run `scripts\u002Fin-repo-memory-search.py` (keyword + frontmatter filters) instead of\n  vector search; results feed `expand`,\n- no `tasks` → the phase board uses the local registry fallback,\n- no `filePathSearch` → semantic search on the path string,\n- no `categoryFilter` → skip category narrowing \u002F post-filter,\n- no `export` → provider swap relies on re-distillation from raw transcripts.\n\n## In-repo provider specifics (`memory.provider: in-repo`)\n\n**Read:** when `semanticSearch:false`, call:\n\n```bash\npython3 scripts\u002Fin-repo-memory-search.py \\\n  --store .cursor\u002Fsw-memory \\\n  --query \"\u003Cterms>\" \\\n  [--category decision] [--tag prd-1] [--file-glob src\u002Fauth.ts]\n```\n\nThen `expand` via `python3 scripts\u002Fin-repo-memory-search.py expand --store \u003Cdir> --ids \u003Cid>` (full body + backlinks), or read `memories\u002F\u003Cid>.md` directly.\n\n**Write:**\n\n1. Lazy-create store dirs on first write: `mkdir -p .cursor\u002Fsw-memory\u002Fmemories .cursor\u002Fsw-memory\u002Frules`.\n2. Pipe payload through `scripts\u002Fmemory-redact.py` (R41) before any file write.\n3. Default `commitMode: committed` → write non-rule files under `.cursor\u002Fsw-memory\u002Fmemories\u002F`.\n4. `commitMode: local` → non-rule files under `.cursor\u002Fsw-memory-local\u002Fmemories\u002F` (gitignored).\n5. **`category: rule` always writes to `.cursor\u002Fsw-memory\u002Frules\u002F`** — offline hook reads committed rules.\n6. Never auto-seed starter rules; store starts empty.\n\n## Planning store memory backend (PRD 034 R11\u002FR23; PRD 057 R21 — 21a local cache + 21b provider round-trip)\n\nWhen `planning.store.backend` is `memory`, unit **bodies** are cached under a **gitignored** directory\n(`.cursor\u002Fsw-memory\u002Fplanning-bodies\u002F\u003Cmemory.project>\u002F`, `MemoryLocalCacheBackend` in\n`scripts\u002Fplanning_store.py`) that always passes content through `scripts\u002Fmemory-redact.py` on read and\nwrite — **body storage only**; the memory backend does not alter source-of-truth for any planning class.\n\nThis local cache is always written unconditionally regardless of whether a memory provider is configured\n(21a — PRD 057 R21), and is never bypassed: it remains the fast-path read and the fallback whenever the\nprovider round-trip below is unavailable. On top of it, 21b adds a real round-trip through the configured\nprovider's REST adapter (Recallium only, at present): `put()` best-effort mirrors the redacted body to a\ndedicated `\u002Fplanning-bodies\u002F\u003CunitId>` REST resource guarded by the same loopback-only base-URL check as\n`providers\u002Frecallium-rules.py`, and `get()` recovers content through that same adapter when the local cache\nis absent (e.g. a fresh checkout on another machine). Each cached body's frontmatter records\n`configuredProvider` (informational — names whichever provider is configured for this skill's other memory\noperations), plus `providerRoundTrip`\u002F`providerRoundTripReason` (whether *this* body actually round-tripped,\nand why not when it didn't). See `core\u002Fproviders\u002Fplanning-store\u002Fmemory.md` for the full contract and\n`scripts\u002Ftest\u002Ffixtures\u002Fmemory-roundtrip\u002Fharness.py` for the offline fixture covering both the round-trip and\nevery fallback path.\n\nDecision-class units under `docs\u002Fplanning\u002Fdecision\u002F` still follow the PRD-015 committed redacted snapshot +\npointer flow regardless of `visibility`. The authoritative decision record paths remain\n`docs\u002Fdecisions\u002F\u003Cn>-\u003Cslug>.md` (repo-SoT) or the provider record (memory-SoT) per **Source of truth\nresolution** below — the planning-store memory backend never replaces that contract.\n\n## Decision records (file-linked deliverables)\n\n**Boundary rule (R32 \u002F KTD3 + provider-conditional SoT):**\n\nResolve authority first: `python3 scripts\u002Fmemory-sot.py resolve --class decision --json`.\n\n| Effective SoT | Authoritative artifact | Memory role |\n| --- | --- | --- |\n| `repo` | Decision record (`docs\u002Fdecisions\u002F\u003Cn>-\u003Cslug>.md`) | Pointer via `relatedFiles` only |\n| `memory` | Provider `decision` record (redacted) | Content-bearing; git snapshot is pointer |\n\n| Artifact | Role | Mutable | CI freeze |\n|----------|------|---------|-----------|\n| Decision record (`docs\u002Fdecisions\u002F\u003Cn>-\u003Cslug>.md`) | Up-front, reviewed-before-build deliverable | Only via amendment | Yes |\n| `decision`-class memory | Retrospective knowledge distillation | Yes | No |\n\nWhen repo-SoT is active (default for `in-repo`):\n\n- Read: load the record from git; memory may point at it via `relatedFiles` but is not authoritative.\n- Write: store a pointer (`relatedFiles: [docs\u002Fdecisions\u002F...]`), never the record body.\n- Flag content-bearing `decision` memories that duplicate an existing record — they should become pointers.\n\nWhen memory-SoT is active, the provider record is authoritative; the committed git snapshot carries a\nforward pointer (`memoryPointer` in frontmatter — see `scripts\u002Fmemory-decision-snapshot.py write`).\n\n**Supersede reconciliation (`docs\u002Fdecisions\u002FSUPERSEDED.log`):**\n\nOn record-level supersede, append the superseded path via:\n\n```bash\npython3 scripts\u002Freconcile.py append-superseded --path docs\u002Fdecisions\u002F\u003Cold>.md --replacement docs\u002Fdecisions\u002F\u003Cnew>.md\n```\n\n`\u002Fsw-memory-sync` runs `python3 scripts\u002Freconcile.py supersede-reconcile --json` after distillation and\nbest-effort re-points the **non-authoritative** side per the active SoT (provider `relatedFiles` under\nrepo-SoT; git snapshot pointer under memory-SoT). Pointer freshness is **auditable, not transactional**\n(provider out of CI reach).\n\n\n## Issue-store brainstorm distillation (PRD 043 R19)\n\nWhen `planning.store.backend` is `issue-store` and a PRD is frozen via `planning_store.py freeze`:\n\n1. Linked brainstorm content (from `sw-edges` \u002F `link-brainstorm-prd`) is excerpted and piped through\n   `scripts\u002Fmemory-redact.py` — no raw transcript.\n2. Distilled `research` entry is stored via the memory backend's local-only cache\n   (`MemoryLocalCacheBackend` \u002F `planning.store.backend: memory` bodies path — 21a, see above).\n3. A `sw-memory-pointer` comment on the brainstorm issue links PRD ↔ memory ↔ brainstorm.\n4. Brainstorm issue is **closed+linked**, never deleted.\n\nFailure at distillation → `sw:freeze-incomplete` on the PRD issue; deliver halts fail-closed.\n\n## Cross-project recall (PRD 046 R90 \u002F PRD 043 R27)\n\nWhen issue-store is active, rationale and distilled learnings may be recalled across `projectKey`\nboundaries via memory pointers — never by duplicating deliverable bodies into memory.\n\n```bash\npython3 scripts\u002Fplanning_cross_project_recall.py recall   --payload-json '{\"sourceProjectKey\":\"proj-a\",\"callerProjectKey\":\"proj-b\",\"query\":\"rationale\",\"pointers\":[...],\"authorizedProjects\":[\"proj-a\"]}'\n```\n\n- **Scope:** queries are keyed by source `projectKey` + caller authorization (`authorizedProjects`).\n- **Redaction:** pointer dereference passes through `scripts\u002Fmemory-redact.py` on read; `private`\u002F`memory`\n  visibility emits opaque excerpts (`{unitId}: [private]`) — project B cannot read project A private rationale.\n- **Ranking:** deterministic tie-break (`projectKey`, `unitId`, `memoryId`).\n- **No duplication:** deliverable content stays in planning artifacts; memory holds pointers and redacted\n  distillations only.\n\nRoute all cross-project recall through this skill + `providers\u002F\u003Cmemory.provider>.md` — never direct provider\ncalls from deliver or planning-graph code.\n\n\n## Boundaries\n\n- Never call a provider tool directly from a command; always go through this skill + the adapter.\n- Never write `rule`-category memories unless the user explicitly asks (\"remember this\", \"add a rule\").\n- Never store secrets, credentials, tokens, or raw transcript content.\n",{"name":5,"metadata":24,"description":7},{"shipwright-capability":25},{"version":26,"metadata":27,"triggers":28},1,{"skill":5,"selectionFamily":5},[29],{"type":30,"command":31,"selectionFamily":5},"phase_default","sw-memory-sync","main","2633b37cdc50086084cc9c0ce6c994871d3e7aad","core\u002Fskills\u002Fmemory",0,"https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright","https:\u002F\u002Fgithub.com\u002Fgrdavies","2026-11-03 23:44:15",{"source":8,"skillName":5,"commands":40,"prompt":50,"zipAvailable":51},[41,44,47],{"tool":42,"command":43},"npx","npx skills add grdavies\u002Fshipwright --skill memory",{"tool":45,"command":46},"bunx","bunx skills add grdavies\u002Fshipwright --skill memory",{"tool":48,"command":49},"pnpm","pnpm dlx skills add grdavies\u002Fshipwright --skill memory","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fmemory\u002FSKILL.md",true,[53],{"path":54,"resourceType":55,"resourceTypeDesc":56,"sizeBytes":57,"sourceHtmlUrl":58},"CAPABILITIES.md","REFERENCE","参考文档",15275,"https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright\u002Fblob\u002Fmain\u002Fcore\u002Fskills\u002Fmemory\u002FCAPABILITIES.md",[60,67,74,81,87,94],{"skillNo":61,"name":62,"nameZh":6,"description":63,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":64,"categories":65,"keywords":66,"publishedAt":19},"01M77K4Z004VAMY705XQKJAH4Y","wrong-name","Missing use when phrase and wrong directory name for critical lint failure.","https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright\u002Fblob\u002Fmain\u002Fscripts\u002Ftest\u002Ffixtures\u002Fworkflow-pack-sdk\u002Fnegative\u002Fbad-instruction\u002Fskills\u002Fbad-skill\u002FSKILL.md",[],[],{"skillNo":68,"name":69,"nameZh":6,"description":70,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":71,"categories":72,"keywords":73,"publishedAt":19},"01M77K4WR3WP22SBT6053PYWAK","demo-skill","Demo workflow pack skill fixture. Use when validating workflow-pack-sdk golden paths in CI.","https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright\u002Fblob\u002Fmain\u002Fscripts\u002Ftest\u002Ffixtures\u002Fworkflow-pack-sdk\u002Fgolden\u002Fvalid-pack\u002Fskills\u002Fdemo-skill\u002FSKILL.md",[],[],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":80,"publishedAt":19},"01M77K4SV48XDDDQ81PD09G9N6","good-skill","Validates Agent Skills spec conformance for fixture trees. Use when testing skills-spec-guard pass paths across core and dist mirrors. Does not ship production skills.","https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright\u002Fblob\u002Fmain\u002Fscripts\u002Ftest\u002Ffixtures\u002Fskills-spec\u002Fpass-valid\u002Fcore\u002Fskills\u002Fgood-skill\u002FSKILL.md",[],[],{"skillNo":82,"name":62,"nameZh":6,"description":83,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":84,"categories":85,"keywords":86,"publishedAt":19},"01M77K4PWVT96VMTMMFE2Y80GJ","Fixture with name\u002Fdir mismatch for guard failure tests. Use when verifying skills-spec-guard rejects invalid identity. Does not ship production skills.","https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright\u002Fblob\u002Fmain\u002Fscripts\u002Ftest\u002Ffixtures\u002Fskills-spec\u002Ffail-name-mismatch\u002Fcore\u002Fskills\u002Fgood-skill\u002FSKILL.md",[],[],{"skillNo":88,"name":89,"nameZh":6,"description":90,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":91,"categories":92,"keywords":93,"publishedAt":19},"01M77K4KSS5Z0Q0DEAA8PV9316","bad-description","Missing the required when trigger phrase in this fixture skill.","https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright\u002Fblob\u002Fmain\u002Fscripts\u002Ftest\u002Ffixtures\u002Fskills-spec\u002Ffail-description-shape\u002Fcore\u002Fskills\u002Fbad-description\u002FSKILL.md",[],[],{"skillNo":95,"name":96,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":97,"categories":98,"keywords":99,"publishedAt":19},"01M77K4H9HK5FDF84D65228XFA","dropin-demo","https:\u002F\u002Fgithub.com\u002Fgrdavies\u002Fshipwright\u002Fblob\u002Fmain\u002Fscripts\u002Ftest\u002Ffixtures\u002Fcapability-select\u002Fdropin\u002Fcore\u002Fskills\u002Fdropin-demo\u002FSKILL.md",[],[]]