Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

bug(hooks): with an external Engram server, Claude/Codex hooks register sessions under the server-side folder-name project, so explicit writes to the real project are denied (session_project_mismatch)

Open
#1,911 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
5/5
Estimated time
Over a week
Newbie friendliness
15/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Active
Tech stack
go, shell
Domain
backend, devtools

Research direction

Start with the server-side GET /project/current?cwd= call in cmd/engram/hook_claude.go (confirmHookSession) and resolve_project in plugin/claude-code/scripts/_helpers.sh, then trace how the resulting project reaches POST /sessions and the mem_save write gate. Done means that with ENGRAM_URL pointing at an external server, the session is registered with the workspace's real project and an explicit mem_save to that project succeeds. The fix needs a design decision on who resolves the project, so confirm the direction with maintainers first.

Written by the indexing model from the issue text.

Description

status:needs-review type:bug
Existing Issue Search
  • I searched existing issues and will add evidence to a canonical tracker when applicable.

Related, but not the same root/scope:

  • #1532 (Streamable HTTP transport): a comment there describes this root cause as a workaround for an HTTP-transport shared server; the proposed X-Engram-Subproject header would apply to HTTP mode only, not to stdio MCP over SSH or HTTP hooks reached through ENGRAM_URL.
  • #1720 (update/delete ownership checked against the MCP process project): same shared-server family, update/delete only, fixed.
  • #1716 / #1717 (PreToolUse write gate when the shell cwd drifts): same gate, single-machine cwd drift.
  • #1591 (explicit cross-project saves vs session ownership): fixed for Pi only via satellite sessions.
  • #262 / #1656 (explicit cwd / directory for MCP project discovery): still resolved on the server's filesystem.
📝 Observed Consequence

When the Engram server runs on a different machine than the agent workspace (supported via ENGRAM_URL), hook-registered sessions and captured prompts are filed under a project derived from the folder name instead of the workspace's real project.

Because 3.x binds writes to the hook-registered session, an agent's explicit, correct mem_save(project: "<real project>") is denied:

  • Claude Code: session_project_mismatch
  • Codex: PreToolUse denial "host session registration could not be confirmed"

The only write that succeeds is one that targets the folder-name project, so an agent that retries after the denial is pushed toward filing durable memory in a non-canonical project.

🔄 Steps to Reproduce
  1. Host B: run engram serve. Host A: no local server; ENGRAM_URL=http://127.0.0.1:7437 forwarded to host B over SSH. MCP configured as ssh hostB engram mcp --tools=agent.
  2. Host A: a repository whose canonical Engram project is myproj (bound via .engram/config.json or its git remote), checked out as a worktree folder named feature-x.
  3. Start Claude Code (official plugin) in /home/me/work/myproj-worktrees/feature-x.
  4. SessionStart hook calls GET /project/current?cwd=/home/me/work/myproj-worktrees/feature-x. Host B cannot see that path and answers {"project":"feature-x","project_source":"dir_basename"}; the hook then sends POST /sessions {project:"feature-x", ownership_mode:"project_owned"}.
  5. The agent calls mem_save(project: "myproj", ...).
  6. Optional: mem_current_project returns the basename of the SSH login directory on host B (e.g. me).
✅ Expected Behavior

In external-server mode, the session/prompt project is resolved where the workspace actually lives (host A), and mem_save(project: "myproj") succeeds.

If host A cannot resolve a project, registration fails closed (registration skipped, or the write denied with an actionable hint) instead of silently adopting a folder name.

❌ Actual Behavior
  • The session and its prompts are attributed to feature-x.
  • mem_save(project: "myproj") returns error_code: session_project_mismatch ("Use a project that matches the existing session, or omit session_id and write to a different project.").
  • Codex, same setup: PreToolUse denial "Codex host session registration could not be confirmed".
  • A write with project: "feature-x" succeeds.

To be precise about causality: Engram does not move or misfile memory on its own here. In our setup, durable memory ended up in a folder-name project only when an agent, after its correct write was denied, retried with the folder-derived project; Engram accepted that explicit value as designed. The bug is the wrong session attribution and the resulting denial of the correct write.

Operating System

macOS

Release Version (Report Context)

3.0.0 (server and clients). Checked against v3.4.0 and current main source: hooks still resolve via server-side GET /project/current?cwd= (cmd/engram/hook_claude.go, plugin/claude-code/scripts/_helpers.sh).

Agent / Client

Claude Code (official plugin 0.1.5); also observed with Codex (official plugin).

📋 Relevant Logs
GET /project/current?cwd=/home/me/work/myproj-worktrees/feature-x
-> {"project":"feature-x","project_source":"dir_basename", ...}

mem_save(project: "myproj")
-> {"error_code":"session_project_mismatch","hint":"Use a project that matches the existing session, or omit session_id and write to a different project.", ...}

Codex: Tool call blocked by PreToolUse hook: Codex host session registration could not be confirmed. Tool: mcp__engram__mem_save
💡 Additional Context

Why the server's cwd cannot work here. /project/current?cwd= runs the server's own detection (.engram/config.json → git → child repo → basename) against its own filesystem. With the server on another machine the path does not exist there, so every rule except the basename fails, and the basename is often wrong (a worktree name, an agent name, or a repo folder that differs from the project). The MCP process launched over SSH has the same problem: its cwd is the remote login directory, unrelated to the workspace.

Why engram mcp --project / ENGRAM_PROJECT alone is not enough.

  1. It only sets the MCP process default; hooks still register sessions and prompts over HTTP with a server-resolved project.
  2. The write gate checks against the hook-registered session, so a correct --project MCP default still produces session_project_mismatch.
  3. MCP configs are usually global per host, so one static value would misroute every other repository unless each launch is wrapped.

Proposed direction (smallest change). When ENGRAM_URL points to an external server, resolve the project on the client and send it, instead of asking the server to resolve the client's path:

  • Hooks (_helpers.sh resolve_project and the Go confirmHookSession) run the local engram binary's detection on host A (the PreToolUse hook already invokes the local binary) and pass the result in the existing project field of POST /sessions / POST /prompts. No new server endpoint is needed for that path.
  • The server keeps all validation (normalization, known-project rules, ownership); the client value only replaces the path-based detection the server cannot perform.
  • No client resolution → fail closed (no folder-name registration) in external mode.
  • Optional, for cross-repo work: extend the #1591 satellite-session approach to Claude/Codex, so an explicit write to a different known project uses an isolated project-owned satellite session instead of a denial.
  • Optional: an MCP initialization parameter or forwarded env var for the process default (cosmetic once sessions are correct).

Compatibility.

  • Opt-in by condition: only when ENGRAM_URL is external; local single-machine behavior is unchanged.
  • Server validation is unchanged; an older server receiving a client-resolved project behaves as it does today.
  • Aligns with #1532's per-request project header, so the HTTP-transport and SSH/tunnel setups share one rule: the project comes from the client.
  • Explicitly not proposed: trusting arbitrary client strings without the server's normal validation, or changing session ownership semantics.
Dominant language
Go
Stars
7.1k
Forks
729
Avg merge
10h 43m
Merged PRs (30d)
332

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from Gentleman-Programming/engram

All issues in Gentleman-Programming/engram

Similar issues

More Go issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.