Summary
computeProjectHash (src/path-utils.ts) preserves . inside path segments, but Claude Code's project-directory slug replaces every . with -. Result: any workdir whose path includes a dotted segment (e.g. .claude, .local, .config, .git-worktrees) gets a project-hash that does NOT match the on-disk CC directory.
Downstream effect: discoverFromFilesystemFallback (session-transcripts.ts:496-531) looks in the wrong ~/.claude/projects/<hash>/ folder, never finds the JSONL, never sets session.jsonlPath. The monitor's readMessagesForMonitor reads zero entries → forwardMessage never runs → channels.message('message.assistant', ...) never fires.
Impact: assistant replies from Claude Code do not reach any notification channel (Telegram, Slack, Webhooks, email) and do not appear in /v1/sessions/:id/transcript for sessions whose workdir contains a dotted segment.
Reproduction
AEGIS_TG_TOKEN=... AEGIS_TG_GROUP=... node dist/cli.js
POST /v1/sessions { workDir: ".../<repo>/.claude/worktrees/x", prompt: "Reply: HELLO" }
- Session created, prompt delivered (
promptDelivery.delivered=true), Claude writes the assistant reply to the JSONL.
- Telegram topic is created and shows
🟢 session-created, 👤 User …, ✅ killed — no assistant message.
GET /v1/sessions/:id/transcript returns messages: [].
Aegis computed slug vs reality:
| Workdir |
computeProjectHash |
CC actual dir |
/Users/x/Documents/aegis/.claude/worktrees/verify-tg |
-Users-x-Documents-aegis-.claude-worktrees-verify-tg |
-Users-x-Documents-aegis--claude-worktrees-verify-tg |
The two differ by aegis-.claude vs aegis--claude.
Fix
In src/path-utils.ts, when sanitizing segments, also replace . with -:
.map((segment) => segment.replace(/:/g, '').replace(/\s+/g, '-').replace(/\./g, '-'))
Add a unit test asserting computeProjectHash('/home/me/.claude/repo') === '-home-me--claude-repo'.
Why this hasn't surfaced before
Likely most existing flows use claudeSessionId set via the hook (session.ts:699), which bypasses the filesystem-fallback path. Workflows that rely on the fallback (no hook, or pre-hook timing) and use a workdir with . segments are affected.
Phase / scope
Phase 3.5 — ACP backend migration. Surfacing this through Telegram channel verification; the same bug breaks any out-of-band consumer relying on forwardMessage.
Summary
computeProjectHash(src/path-utils.ts) preserves.inside path segments, but Claude Code's project-directory slug replaces every.with-. Result: any workdir whose path includes a dotted segment (e.g..claude,.local,.config,.git-worktrees) gets a project-hash that does NOT match the on-disk CC directory.Downstream effect:
discoverFromFilesystemFallback(session-transcripts.ts:496-531) looks in the wrong~/.claude/projects/<hash>/folder, never finds the JSONL, never setssession.jsonlPath. The monitor'sreadMessagesForMonitorreads zero entries →forwardMessagenever runs →channels.message('message.assistant', ...)never fires.Impact: assistant replies from Claude Code do not reach any notification channel (Telegram, Slack, Webhooks, email) and do not appear in
/v1/sessions/:id/transcriptfor sessions whose workdir contains a dotted segment.Reproduction
AEGIS_TG_TOKEN=... AEGIS_TG_GROUP=... node dist/cli.jsPOST /v1/sessions { workDir: ".../<repo>/.claude/worktrees/x", prompt: "Reply: HELLO" }promptDelivery.delivered=true), Claude writes the assistant reply to the JSONL.🟢 session-created,👤 User …,✅ killed— no assistant message.GET /v1/sessions/:id/transcriptreturnsmessages: [].Aegis computed slug vs reality:
computeProjectHash/Users/x/Documents/aegis/.claude/worktrees/verify-tg-Users-x-Documents-aegis-.claude-worktrees-verify-tg-Users-x-Documents-aegis--claude-worktrees-verify-tgThe two differ by
aegis-.claudevsaegis--claude.Fix
In
src/path-utils.ts, when sanitizing segments, also replace.with-:Add a unit test asserting
computeProjectHash('/home/me/.claude/repo') === '-home-me--claude-repo'.Why this hasn't surfaced before
Likely most existing flows use
claudeSessionIdset via the hook (session.ts:699), which bypasses the filesystem-fallback path. Workflows that rely on the fallback (no hook, or pre-hook timing) and use a workdir with.segments are affected.Phase / scope
Phase 3.5 — ACP backend migration. Surfacing this through Telegram channel verification; the same bug breaks any out-of-band consumer relying on
forwardMessage.