Skip to content

v2: missing symbol-extractor.js silently disables post-write hook (+3 related defects) #68

Description

@Laptopcorei7

OpenWolf 2.0.1 — four hook defects (one silently disables write tracking)

Observed on v2.0.1 (latest published). Defect 1 is also present in v2.0.0
its post-write.js carries the same import while init.js omits the file from
the copy list — so the whole v2 line is affected, on every platform.
Defects 1-3 are platform-independent; defect 4 is Windows-only.

Environment: Windows 11, Node v22.17.0, Claude Code.

Line numbers refer to the published build under dist/src/.


1. symbol-extractor.js is never installed — post-write.js dies on every write

Severity: high. Silently disables write tracking, memory.md logging and
anatomy.md updates in every project, on every write, from install onward.

src/hooks/post-write.js:6 imports ./symbol-extractor.js, but that filename is
absent from all three hook file lists:

File Line List
src/cli/init.js ~550 hookFiles — install
src/cli/update.js ~413 hookFiles — update
src/cli/status.js ~32 hookFiles — health check

The package ships the file (dist/hooks/ and dist/src/hooks/); only the copy
lists omit it. Result:

Error [ERR_MODULE_NOT_FOUND]: Cannot find module '.../.wolf/hooks/symbol-extractor.js'
    imported from .../.wolf/hooks/post-write.js

Two things make this hard to notice:

  • A missing ESM import fails at module load, before post-write.js's own
    try/catch blocks and before main().catch(() => process.exit(0)). The hook
    exits 1 and the agent harness surfaces nothing.
  • post-write.js is the only hook importing it, so reads keep being tracked
    and only writes go quiet — which reads as "idle", not "broken".

Because it is also missing from status.js, openwolf status reports healthy
hooks on a fully broken install
(✓ All 7 hook scripts present).

Fix: add "symbol-extractor.js" to all three lists. After the fix
openwolf status reports 8 and detects the failure.


2. countSemanticEntries can never match — reminder fires unconditionally

Severity: medium. Stop hook emits ACTION REQUIRED: N files were modified this session but no semantic summary was written to memory.md on every turn,
regardless of what the user writes.

src/hooks/shared.js:~620:

const today = new Date().toISOString().slice(0, 10);
const todayPrefix = `| ${today}`;               // "| 2026-08-05"
if (line.startsWith(todayPrefix) && !mechanical.test(line)) count++;

But memory.md rows use | HH:MM | — the format mandated by the project
template and by the reminder string in stop.js:~211 itself. Measured on one
real project: 543 rows in HH:MM format, 0 matching todayPrefix,
countSemanticEntries()0.

The hook therefore demands a format its own checker rejects. Note the
mechanical regex one line above (^\|\s*[\d:]+) does anticipate HH:MM, so
only the todayPrefix filter is inconsistent.

Fix: scope to the newest ## Session: <today> heading and count
non-mechanical rows beneath it; fall back to date-prefixed rows when no heading
for today exists, so older history is not miscounted.


3. Buglog reminder checks a list the file can never appear in

Severity: medium. ACTION REQUIRED: ... buglog.json was not updated fires
even immediately after buglog.json is updated.

src/cli/../hooks/stop.js:~153:

const buglogWritten = session.files_written.some(w => w.file.includes("buglog.json"));

buglog.json lives under .wolf/, and post-write.js:39-44 returns early for
every .wolf/ path before the session-tracker step. It can never be present
in files_written, so buglogWritten is always false.

Fix: compare buglog.json's mtime against session start. This also catches
updates made outside the Write/Edit tools (scripts, editors).


4. Out-of-project guard fails across Windows drives

Severity: low-medium. Pollutes anatomy-index.json with files outside the
project.

src/hooks/post-write.js:48:

const relPath = normalizePath(path.relative(projectRoot, absolutePath));
if (relPath.startsWith("..")) { process.exit(0); }

On Windows, path.relative() between different drives cannot produce a
relative path and returns the target as an absolute path:

path.relative('D:/projects/app', 'C:/Users/<user>/notes.md')
// → 'C:\\Users\\me\\notes.md'   .startsWith('..') → false

The guard passes and the out-of-tree file is indexed — exactly what the comment
above it says it prevents. Reproducible only when project and target are on
different drives, which is likely why it shipped.

Fix: if (relPath.startsWith("..") || path.isAbsolute(relPath)).


Suggested regression tests

  1. Install into a scratch project, assert every file imported by a hook exists
    in .wolf/hooks/ — this catches defect 1 generically, not just this filename.
  2. countSemanticEntries against a fixture using the documented HH:MM format
    → expect non-zero; mechanical-rows-only → expect zero.
  3. stop.js after touching buglog.json → expect no reminder.
  4. post-write.js with a file_path on a different drive letter → assert no
    anatomy-index.json entry. (Windows-only; skip elsewhere.)

Notes

All four were verified fixed locally by patching the installed package and
running a real openwolf update, which then distributed the corrected hooks to
all 7 registered projects with user data (buglog.json, cerebrum.md,
memory.md) intact.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions