Two small silent-failure bugs, same shape: an operation that cannot succeed reports as though it did.
1. writeState throws away its write error
watch/events.go:675:
stateFile := filepath.Join(d.root, ".codemap", "state.json")
os.WriteFile(stateFile, data, 0644) // return value discarded
}
If that write fails — read-only .codemap, disk full, permissions, a transient error — the daemon proceeds as though state was persisted. Every hook that later calls ReadState gets the stale file or nothing, and silently falls back to a scan.
writeState is called from the debounce loop and on every control event, so a persistent failure repeats indefinitely with no signal anywhere. The daemon believes it is publishing state; nothing is.
Minimal fix: check the error and surface it once (log under d.verbose, or record it so codemap watch status can report a degraded daemon). Failing loudly once is better than failing silently forever.
2. get_file_context answers confidently about files that do not exist
Driving the MCP server against a path that is not in the repo:
$ get_file_context {"file": "totally/made/up.go"}
=== File Context: totally/made/up.go ===
IMPORTS: none (leaf file)
IMPORTED BY: none (entry point or unused)
CONNECTED: 0 files in dependency graph
No error, no caveat. The parenthetical explanations make it worse than bare zeros: "leaf file" and "entry point or unused" are assertions about a file that was never scanned and does not exist. An agent that mistypes a path, or asks about a file from a stale mental model, is told the file is a harmless leaf.
Minimal fix: check membership in the scanned inventory first and return "not found in the scanned file set" — distinguishing unknown from known-and-empty. The index already has the file list, so this is a lookup, not new machinery.
Why these are worth filing despite being small
Both are the house failure mode: a confident answer where the honest one is "I don't know" or "that failed." (2) is a near-duplicate of the --importers problem in #138 on a different surface, which suggests the fix belongs at the layer where an answer about a file is assembled, not in each command.
Related: #138 (same class, --importers), #137 (the disclosure channel these should use), #111.
Two small silent-failure bugs, same shape: an operation that cannot succeed reports as though it did.
1.
writeStatethrows away its write errorwatch/events.go:675:If that write fails — read-only
.codemap, disk full, permissions, a transient error — the daemon proceeds as though state was persisted. Every hook that later callsReadStategets the stale file or nothing, and silently falls back to a scan.writeStateis called from the debounce loop and on every control event, so a persistent failure repeats indefinitely with no signal anywhere. The daemon believes it is publishing state; nothing is.Minimal fix: check the error and surface it once (log under
d.verbose, or record it socodemap watch statuscan report a degraded daemon). Failing loudly once is better than failing silently forever.2.
get_file_contextanswers confidently about files that do not existDriving the MCP server against a path that is not in the repo:
No error, no caveat. The parenthetical explanations make it worse than bare zeros: "leaf file" and "entry point or unused" are assertions about a file that was never scanned and does not exist. An agent that mistypes a path, or asks about a file from a stale mental model, is told the file is a harmless leaf.
Minimal fix: check membership in the scanned inventory first and return "not found in the scanned file set" — distinguishing unknown from known-and-empty. The index already has the file list, so this is a lookup, not new machinery.
Why these are worth filing despite being small
Both are the house failure mode: a confident answer where the honest one is "I don't know" or "that failed." (2) is a near-duplicate of the
--importersproblem in #138 on a different surface, which suggests the fix belongs at the layer where an answer about a file is assembled, not in each command.Related: #138 (same class,
--importers), #137 (the disclosure channel these should use), #111.