Skip to content

watch: writeState discards the os.WriteFile error, and get_file_context reports confident zeros for files that do not exist #140

Closed

Description

@JordanCoin

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.

Activity

  1. added 2 commits that reference this issue on Oct 7, 2026
    062e774
    5844113
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions