Skip to content

curate Phase 1: resolve MEM_DIR via --project-link-state, not a hand-rolled sed slug (breaks on Windows) #58

Description

@s-annam

Problem

skills/okfmem-curate/SKILL.md Phase 1 derives the memory dir by hand-rolling the project slug:

# skills/okfmem-curate/SKILL.md:54-56
# project slug = absolute cwd with / replaced by -
SLUG=$(pwd -P | sed 's|/|-|g')
MEM_DIR="$HOME/.claude/projects/$SLUG/memory"

This reimplements memory_init.encode_root — badly — and is wrong in two ways.

1. It eats the drive colon on Windows

encode_root encodes both the separator and the drive colon:
C:\Users\you\okfmemC--Users-you-okfmem (the : becomes the second dash). The sed
above replaces only /, so on a Windows checkout it produces a slug that does not match any
directory under ~/.claude/projects/. Phase 1's own guard then fires — "If $MEM_DIR doesn't
exist, tell the user and stop"
— so /okfmem-curate cannot run at all on Windows unless
the user passes an explicit path argument every time. Silent-ish (it reports a missing dir),
but the reported cause is wrong: the dir exists, the slug is.

This is the same drive-colon-shape footgun documented across the repo's Windows work
(#14/#16/#17, #41, #42) — a hand-rolled separator swap standing in for encode_root.

2. It ignores project renames

encode_root is only half the resolution. The registry (~/okfmem-store/registry.json) maps a
repo root to its store project name, which honours renames and per-project overrides. A raw
cwd-to-slug transform can't see any of that, so a renamed project resolves to a stale or
missing directory even on POSIX, where the sed is otherwise correct.

Fix

okfmem init --project-link-state already exists (shipped #48) as the shared read-only probe
for exactly this question. It returns one of linked <name> / unlinked <name> /
not-a-repo / no-claude, resolves the name through the registry, and never re-derives the
encoded path in shell. skills/okfmem-save/SKILL.md Step 1 already uses it — this is a
straight port of that block, and it kills the sed for the same stated reason.

Replace Phase 1's derivation with:

# Ask the engine, rather than re-deriving the encoded path: `encode_root` also
# encodes the drive colon on Windows, so a hand-rolled sed resolves to the wrong dir.
LINK_STATE="$(okfmem init --project-link-state)"   # linked <name> | unlinked <name> | not-a-repo | no-claude
STORE="${OKFMEM_STORE:-$HOME/okfmem-store}"
MEM_DIR="$STORE/projects/$(echo "$LINK_STATE" | awk '{print $2}')"

and give each non-linked state a specific message rather than the generic
"$MEM_DIR doesn't exist":

State What Phase 1 should say
linked <name> proceed
unlinked <name> this repo has no memory link — run okfmem init from the repo root, then re-run
not-a-repo cd to the project root first
no-claude the harness isn't installed; nothing to curate

Keep the existing behaviour that an explicit path argument overrides everything — that is
the escape hatch that made this bug survivable, and audit <path> depends on it.

Phase 1's symlink-detection step (readlink "$MEM_DIR") is unaffected and stays as-is.

Acceptance criteria

  • skills/okfmem-curate/SKILL.md Phase 1 no longer contains pwd -P | sed; it resolves
    MEM_DIR via okfmem init --project-link-state.
  • The resolved path is correct on Windows (drive colon encoded) — verified against
    encode_root, not against a hand-typed expectation.
  • A renamed project resolves through the registry to its current store project name.
  • Each of the four probe states has its own message; unlinked names okfmem init as the
    remedy rather than reporting a missing directory.
  • An explicit path argument still overrides the probe (/okfmem-curate audit <path> works
    outside a repo).
  • python3 scripts/check-leaks.py exits 0 — the snippet uses ~/okfmem-store /
    $OKFMEM_STORE, never a real home path.

Notes for the implementer

  • This is a skill-text change, not engine code — no new Python, no new subcommand. The probe
    it calls already ships.
  • Grep for other hand-rolled slug derivations while you're here. okfmem-save was already
    ported on feat(init): seed store project on init + nag unwired repos on four surfaces #48; curate is the known remaining one, but a third would be the same bug.
  • Phases 4 and 5 remain gated on explicit user approval (CLAUDE.md confirmation discipline);
    nothing in this change touches that.

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingwindows

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions