Skip to content
Closed
2 changes: 1 addition & 1 deletion .github/workflows/sync-blocks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
B=skills/memory-audit/SKILL.md
C=SYNC-BLOCKS.md
overall=0
for tag in capture-rules strip-the-anchors applies-to; do
for tag in capture-rules strip-the-anchors applies-to forcing-function; do
echo "=== $tag ==="
tag_fail=0
for f in "$A" "$B" "$C"; do
Expand Down
55 changes: 54 additions & 1 deletion SYNC-BLOCKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,59 @@ When a memory genuinely applies to multiple projects, list them comma-separated

---

## Block 4: Forcing-function test

Locations:
- `skills/continuous-learning/SKILL.md` — inside Step 1, Stage A
- `skills/memory-audit/SKILL.md` — criterion B.1

Capture frames the failure as "skip", audit as "DROP"; both verbs stay outside the fence.

```markdown
<!-- SYNC:forcing-function -->
**The forcing-function test.** *"Would a future session act differently in this project because this memory exists?"* The memory qualifies only if nothing else already drives that behavior.

Four things can already be driving it, and they are not equally reliable:

- **The code itself** — the current source reads correctly, and a future session consults the code, not the memory.
- **A comment the fix left behind** — a doc comment or inline note at the cited symbol saying the same thing. Read the file; don't just grep for the symbol.
- **An auto-loaded instruction file** — `CLAUDE.md` / `AGENTS.md` load every session, so a rule written there does displace the memory.
- **A mechanical enforcer, but only where it actually gates.** Read a lint rule's `severity:` *and* how CI treats it: a `warning` blocks under warnings-as-errors or a zero-warning threshold, and is advisory otherwise. An installed skill loads only when its description matches the task, so it displaces nothing for work that never triggers it. Version control records the history only where the project is under version control at all.

Presence is not redundancy — ask which of them would actually fire on the work this memory covers. And none of them can carry the **wrong turn**: the approach tried and rejected, the fix that looks obvious and silently no-ops, why the wrong pattern keeps reappearing. Where the mechanism is redundant but the wrong turn is not, keep the warning and cut the rest.
<!-- /SYNC -->
```

---

## Paired sections (not CI-enforced)

These sections say the same thing from two sides — capture decides whether to *write* a memory, audit decides whether to *keep* one. They are deliberately **not** locked blocks, for two reasons:

- The two skills need different shapes. Capture wants a one-line anti-example; audit wants the category plus its "keep only when…" exception. Forcing them byte-identical would strip the exceptions.
- Capture cannot point at audit instead. `techpack.yaml` marks `continuous-learning` as `isRequired: true` and `memory-audit` as optional, so capture must stand alone on machines where the audit skill is not installed.

So this is a review checklist, not a guarantee. **Change one side, check the other.**

| Concept | continuous-learning | memory-audit |
|---|---|---|
| Naming convention | `## Memory Categories` headings | `C.1 Naming Convention` |
| Historical record of a shipped change | "Do Not Save" → *Historical record of a one-time shipped change* | DROP category **B** |
| Naming / style decision once enforced | "Do Not Save" → *Naming/prefix decision once enforced* | DROP category **C** |
| Self-evident one-time bug fix | "Do Not Save" → *One-time bug fix self-evident in current code* | DROP category **D** |
| Generic wisdom with a token example | "Do Not Save" → *Generic engineering wisdom with a token project example* | DROP category **E** |
| Research artifact for dormant work | "Do Not Save" → *Research artifact for deferred or dormant work* | DROP category **F** |
| One-liner belonging in CLAUDE.md | "Do Not Save" → *One-line rule that belongs in CLAUDE.md* | DROP category **G** |
| A memory asserting an API that never shipped | Step 4 → *Check 3: Symbol existence* | DROP category **I** |
| Staleness | `## Staleness Prevention` | `C.5 Staleness Signals` |

**Intentionally unpaired — do not "fix" these:**

- Audit categories **A** (self-marked superseded) and **H** (scope covered by a sibling) have no capture counterpart. A memory cannot be superseded at the moment it is written, and capture handles sibling overlap at Step 2 instead.
- Audit criterion **C.3** deliberately stopped enumerating template headings. Capture owns `references/templates.md`, and the audit judges structural completeness, so heading names can change on one side without breaking the other.

---

## Drift verification (CI-ready)

```sh
Expand All @@ -68,7 +121,7 @@ A=skills/continuous-learning/SKILL.md
B=skills/memory-audit/SKILL.md
C=SYNC-BLOCKS.md
overall=0
for tag in capture-rules strip-the-anchors applies-to; do
for tag in capture-rules strip-the-anchors applies-to forcing-function; do
echo "=== $tag ==="
tag_fail=0
for f in "$A" "$B" "$C"; do
Expand Down
57 changes: 33 additions & 24 deletions skills/continuous-learning/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,20 @@ After completing any task, evaluate in two stages.
- Did this require non-obvious investigation or debugging?
- Was a choice made about architecture, patterns, or approach?
- Is there an established project convention worth documenting?
- **Forcing-function (hard gate):** without this memory, would a future session act differently in the project? If the code, `git log`, lint, or the formatter already drives the behavior → skip. The "Do Not Save" table cites this as `[Forcing-function]`.
- **Forcing-function (hard gate)** — apply the test below. If something else already drives the behavior → skip. The "Do Not Save" table cites this as `[Forcing-function]`.

<!-- SYNC:forcing-function -->
**The forcing-function test.** *"Would a future session act differently in this project because this memory exists?"* The memory qualifies only if nothing else already drives that behavior.

Four things can already be driving it, and they are not equally reliable:

- **The code itself** — the current source reads correctly, and a future session consults the code, not the memory.
- **A comment the fix left behind** — a doc comment or inline note at the cited symbol saying the same thing. Read the file; don't just grep for the symbol.
- **An auto-loaded instruction file** — `CLAUDE.md` / `AGENTS.md` load every session, so a rule written there does displace the memory.
- **A mechanical enforcer, but only where it actually gates.** Read a lint rule's `severity:` *and* how CI treats it: a `warning` blocks under warnings-as-errors or a zero-warning threshold, and is advisory otherwise. An installed skill loads only when its description matches the task, so it displaces nothing for work that never triggers it. Version control records the history only where the project is under version control at all.

Presence is not redundancy — ask which of them would actually fire on the work this memory covers. And none of them can carry the **wrong turn**: the approach tried and rejected, the fix that looks obvious and silently no-ops, why the wrong pattern keeps reappearing. Where the mechanism is redundant but the wrong turn is not, keep the warning and cut the rest.
<!-- /SYNC -->

If the forcing-function gate fails, or no other prompt answers yes → skip. Otherwise continue to Stage B.

Expand Down Expand Up @@ -116,6 +129,14 @@ Decide what to do, in this order of preference:

Use `Related:` for memories that share root causes, build on each other, contradict each other, or supersede older decisions. Don't cross-link every vaguely overlapping memory.

**Log the outcome in one line before moving on.** An unlogged search is indistinguishable from a skipped one — the same reason the Step 4 checks print their work. Duplicates that slip past here are what an audit later has to clean up.

```
KB search: "<query>" -> <n> hits, <what they covered> -> <branch taken, and the file edited or created>
```

e.g. `KB search: "retry backoff" -> 2 hits, both on request timeouts -> branch 3, new memory, Related: learning_networking_timeout_tuning`

### Step 3: Research (When Appropriate)

**For general topics** — search available documentation sources first (the user may have MCP servers providing official docs for frameworks or libraries), then fall back to web search:
Expand Down Expand Up @@ -143,7 +164,7 @@ When a memory genuinely applies to multiple projects, list them comma-separated

#### Mandatory pre-`Write` checks

Run both checks as visible output before any `Write` to `<project>/.claude/memories/`. Hidden reasoning is easy to skip; printed output is reviewable.
Run all three checks as visible output before any `Write` to `<project>/.claude/memories/`. Hidden reasoning is easy to skip; printed output is reviewable.

**Check 1: Strip-the-anchors (routing).**

Expand All @@ -170,7 +191,11 @@ This shape forces the test to happen — you cannot list anchors without finding

Scan the drafted content for personal identifiers. Look for `@` characters (handles, emails), `<word>/<TICKET>-` and `<word>/<ticket>-description` branch-name shapes, `<word>@<word>` email shapes, and any first-name-looking tokens in examples, commit references, or narration. Any hit → rewrite to describe the artifact (the bug, pattern, decision) without the actor, or skip the save. Mechanical grep, not a vibe check.

**Save (only after both checks pass):**
**Check 3: Symbol existence.**

Grep every symbol, path, API, or config key the draft names in the present tense. A draft asserting that "hiding is handled by `setFooHidden()`" must be able to point at `setFooHidden()` in the code. Where a name does not resolve, either cut the claim or mark it explicitly as proposed and not yet implemented — never record an intended design in the present tense. This is the cheapest place to stop a memory that prescribes an API which never shipped; once saved, only a later audit will catch it.

**Save (only after all three checks pass):**
```
Write(file_path: "<project>/.claude/memories/<category>_<topic>_<specific>.md", content: "<structured markdown>")
```
Expand All @@ -193,9 +218,7 @@ Before saving any memory, verify:
- [ ] Content is specific enough to be actionable
- [ ] Content is general enough to be reusable
- [ ] No sensitive information (credentials, internal URLs)
- [ ] Does not duplicate existing memories
- [ ] References included if external sources were consulted
- [ ] No brittle references that rot quickly (see Staleness Prevention below)

### Do Not Save

Expand All @@ -209,11 +232,11 @@ Anti-examples, generalized — do not create memories like these:
| Public API reference | "Public Git hosting API rate limit is N/hr authenticated" | **[Rule 1]** Public API docs cover this — no project-specific twist. |
| Personal identifier | Problem section narrates a specific engineer hitting a cache bug | **[Rule 2]** Names an engineer. |
| Personal preference without project evidence | "Prefer early returns" with no lint rule, consistent codebase usage, or team agreement | **[Rule 3]** Taste, not pattern. |
| Historical record of a one-time shipped change | "We renamed folder `Install/` to `Sync/` after the command rename" | **[Forcing-function]** Once shipped, `git log` answers this. Future sessions read the current code, not the migration story. The memory drives no future behavior. |
| Historical record of a one-time shipped change | "We renamed folder `Install/` to `Sync/` after the command rename" | **[Forcing-function]** Once shipped, `git log` answers this. Future sessions read the current code, not the migration story. The memory drives no future behavior — unless the project has no version control, where nothing else records the change. |
| Generic engineering wisdom with a token project example | "Extract methods over condensing for lint compliance" with one PR cited | **[Rule 1]** Strip the example — what is left is universal advice that fits any project. Belongs in a coding-style doc, not a per-project KB. |
| One-line rule that belongs in CLAUDE.md | A single-sentence convention with no Context / Options / Consequences | **[Scope]** If it fits in one bullet under "Conventions" in CLAUDE.md, put it there. A standalone memory file is overhead for content that cannot grow. |
| Naming/prefix decision once enforced | "We kept the `External` prefix on adapter types" | **[Forcing-function]** Once the type system, lint, or formatter enforces it, the decision lives in the code. Future sessions read the code, not the memory. |
| One-time bug fix self-evident in current code | "Bug X used `dropFirst()`; we changed to a guarded check" | **[Forcing-function]** The fix is a small diff; the code reads correctly today. Save only if the bug class is recurring and the memory teaches the *avoidance pattern*, not the one fix. |
| One-time bug fix self-evident in current code | "Bug X skipped the first element instead of the matching one; we changed the filter to compare identity" | **[Forcing-function]** The fix is a small diff; the code reads correctly today. Save only if the bug class is recurring and the memory teaches the *avoidance pattern*, not the one fix. |
| Research artifact for deferred or dormant work | "Cross-platform audit / options-considered for feature X (deferred indefinitely)" | **[Forcing-function]** Useful when the work resumes — but it belongs in a planning doc or `docs/`, not the memory KB. The KB is for things that change how a session works on the active codebase today. |

**Internal docs are fair game.** A memory summarizing a Confluence page, ADR, RFC, or team-wiki entry is project knowledge — those sources aren't "documentation anyone can look up." Always include the source URL in `References:` so the memory points at the canonical version and readers can check for drift.
Expand All @@ -235,11 +258,11 @@ Before saving, check memory content against these rules:

- **No line numbers.** Reference symbols (types, functions, methods) instead — they survive refactors.
- **Prefer module-level paths** over deep file paths. Use full paths only for stable, well-known files.
- **Use semantic anchors** — method signatures, protocol names, and architectural concepts are durable.
- **Use semantic anchors** — method signatures, interface and type names, and architectural concepts are durable.
- **Omit transient details** — feature flags being removed, in-progress PR numbers, temporary workarounds.

**Good:** `SessionManager.refreshToken(forceExpiry:)` in the `Auth` module
**Bad:** `SessionManager.swift:142` at `Sources/Features/Auth/Session/SessionManager.swift`
**Good:** `SessionManager.refreshToken` in the `Auth` module
**Bad:** `src/features/auth/session/SessionManager.<ext>:142`

---

Expand All @@ -255,17 +278,3 @@ When the user asks to "run a retrospective", "extract learnings from this sessio
4. Save the top 1–3 highest-value candidates that pass, following Step 4's pre-`Write` checks.
5. Report what was created and why in a brief summary.

---

## Tool Reference

| Tool | Purpose |
|------|---------|
| `mcp__docs-mcp-server__search_docs` | **Primary:** Semantic search across docs and memories |
| `mcp__docs-mcp-server__list_libraries` | List indexed libraries |
| `Glob` | **Fallback:** List all memory files (`.claude/memories/*.md`) |
| `Read` | Read a specific memory file |
| `Write` | Create new memory file |
| `Edit` | Update existing memory file |
| `Bash` | Resolve git repo name for `Applies to:` (`git remote get-url origin`) |
| `WebSearch` | Built-in web search for general topics |
Loading