Conversation
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> AI-Agent: Claude-Code/2.1.42 AI-Model: claude-opus-4-5-20251101 AI-Fact: add claude to test.txt. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> AI-Confidence: low AI-Lifecycle: project AI-Memory-Id: f5ea4bbd AI-Source: heuristic
AI-Confidence: low AI-Lifecycle: project AI-Memory-Id: 1980faea AI-Source: heuristic
When git-mem hooks run in plain terminal shells (e.g., post-commit), no AI environment variables are set. This causes agent/model detection to fail, meaning commits made during AI sessions don't get properly attributed with AI-Agent and AI-Model trailers. Solution: - Write runtime.json to .git-mem/ on session-start with detected agent/model - AgentResolver reads this file as fallback when env vars are empty - Delete runtime.json on session-stop to prevent stale attribution - TTL enforcement (2h default) prevents stale data from being used Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> AI-Agent: Claude-Code/2.1.42 AI-Model: claude-opus-4-5-20251101 AI-Decision: add runtime.json for cross-hook agent/model detection (GIT-123). When git-mem hooks run in plain terminal shells (e.g., post-commit), AI-Confidence: medium AI-Tags: application, handlers, domain, infrastructure, services, tests, unit AI-Lifecycle: project AI-Memory-Id: 42af7fc0 AI-Source: heuristic
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughThe changes enhance session stop handling by integrating runtime deactivation, improve data validation in RuntimeService, refine test robustness with deterministic failure paths, and ensure proper working directory context in hook command execution. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant SessionStopHandler
participant RuntimeService
participant Logger
Client->>SessionStopHandler: handle(args)
activate SessionStopHandler
SessionStopHandler->>SessionStopHandler: capture session
alt Capture Success
SessionStopHandler->>SessionStopHandler: set success result
else Capture Failure
SessionStopHandler->>SessionStopHandler: set error result
end
SessionStopHandler->>SessionStopHandler: finally block
alt RuntimeService available
SessionStopHandler->>RuntimeService: deactivate(cwd)
activate RuntimeService
alt Deactivation Success
RuntimeService-->>SessionStopHandler: success
SessionStopHandler->>Logger: log success
else Deactivation Error
RuntimeService-->>SessionStopHandler: error
SessionStopHandler->>Logger: log warning
end
deactivate RuntimeService
end
SessionStopHandler-->>Client: return unified handlerResult
deactivate SessionStopHandler
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Adds a repo-local .git-mem/runtime.json mechanism to persist detected agent/model across hook invocations when environment variables aren’t present, wiring it through DI and session start/stop handlers.
Changes:
- Introduces
IRuntimeService+RuntimeServiceto write/read/delete.git-mem/runtime.jsonwith a 2-hour TTL. - Updates
AgentResolverto fall back toruntime.jsonwhen env-based detection returns empty. - Activates runtime data on
session:startand deactivates it onsession:stop, plus adds unit tests for the new behavior.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/infrastructure/services/RuntimeService.test.ts | Adds unit coverage for activate/deactivate/read and TTL behavior. |
| tests/unit/application/handlers/SessionStopHandler.test.ts | Verifies runtime deactivation behavior on session stop. |
| tests/unit/application/handlers/SessionStartHandler.test.ts | Verifies runtime activation behavior on session start. |
| test.txt | Adds a standalone text file (appears unrelated to feature). |
| src/infrastructure/services/RuntimeService.ts | Implements read/write/delete of .git-mem/runtime.json with TTL. |
| src/infrastructure/services/AgentResolver.ts | Adds runtime.json fallback when env detection is missing. |
| src/infrastructure/di/types.ts | Registers runtimeService in the DI cradle types. |
| src/infrastructure/di/container.ts | Wires RuntimeService and injects it into AgentResolver + handlers. |
| src/domain/interfaces/IRuntimeService.ts | Defines runtime data + service contract. |
| src/application/handlers/SessionStopHandler.ts | Deactivates runtime on session stop. |
| src/application/handlers/SessionStartHandler.ts | Activates runtime on session start using resolved agent/model. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/application/handlers/SessionStopHandler.ts (1)
23-58:⚠️ Potential issue | 🟠 MajorEnsure runtime deactivation runs even when capture fails.
Right now deactivation is skipped ifcapture()throws, which can leave stale runtime.json behind. Move the call into afinallyblock (or invoke in both success and error paths).🔧 Suggested fix (ensure deactivation always runs)
async handle(event: ISessionStopEvent): Promise<IEventResult> { - try { + let result: IEventResult; + try { this.logger?.info('Session stop handler invoked', { sessionId: event.sessionId, cwd: event.cwd, }); @@ - this.deactivateRuntime(event.cwd); - - return { + result = { handler: 'SessionStopHandler', success: true, output: result.summary, }; } catch (error) { const err = error instanceof Error ? error : new Error(String(error)); this.logger?.error('Session stop handler failed', { error: err.message, stack: err.stack, }); - return { + result = { handler: 'SessionStopHandler', success: false, error: err, }; + } finally { + // Always attempt to deactivate runtime.json on session stop + this.deactivateRuntime(event.cwd); } + return result; }Also applies to: 62-79
🤖 Fix all issues with AI agents
In `@src/application/handlers/SessionStartHandler.ts`:
- Around line 113-122: detectSource() currently only reports environment
variables and will return 'env:unknown' even when agentResolver.resolveAgent()
ends up using runtime.json; update the detection so the reported source reflects
the actual resolution path: either modify detectSource(sourceOverride?: string)
to prefer a provided resolved source (e.g., pass the result or a tag from
agentResolver.resolveAgent()) or extend detectSource to check the runtime
fallback (runtime.json) presence/contents before returning 'env:unknown'. Make
the change in SessionStartHandler where detectSource() is called so the final
reported source matches the actual agent/model selection (reference detectSource
and agentResolver.resolveAgent).
In `@src/infrastructure/di/container.ts`:
- Around line 70-76: The container is being created without the hook's cwd so
AgentResolver falls back to process.cwd(); update the hook entry point where
createContainer(...) is called to thread the event cwd into createContainer
(e.g., createContainer({ scope: `hook:${eventName}`, llm: config.llm, cwd }))
and ensure the container options propagate into AgentResolver construction so
the AgentResolver(...) call uses options?.cwd rather than process.cwd() for
fallback reads.
In `@src/infrastructure/services/RuntimeService.ts`:
- Around line 62-73: The timestamp string validation in RuntimeService (the
method handling data.timestamp, using ttlMs and DEFAULT_TTL_MS) only checks type
but not parseability, so parse the timestamp (new
Date(data.timestamp).getTime()) and reject the payload if the result is NaN or
not finite; also treat negative age (timestamp in the future) as invalid or
guard it with an allowable skew (e.g. reject if age < -allowedSkew), returning
undefined in those cases before the TTL check so invalid or future timestamps
cannot bypass TTL. Ensure you reference the existing variables/data flow
(data.timestamp, timestamp, age, ttlMs, DEFAULT_TTL_MS) when adding the
validation.
- RuntimeService: reject NaN/invalid timestamps and future timestamps (with 1 minute skew allowance) - SessionStopHandler: move deactivateRuntime to finally block so it runs even when capture fails - SessionStartHandler: use env-only detection functions to avoid runtime.json loop when activating - AgentResolver: cache runtime.json read to avoid repeated file I/O - IRuntimeService: fix doc comment (errors silently ignored, not logged) - Remove accidental test.txt file Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> AI-Agent: Claude-Code/2.1.42 AI-Model: claude-opus-4-5-20251101 AI-Convention: runtime.json loop when activating AI-Confidence: medium AI-Tags: application, handlers, domain, infrastructure, services, tests, unit, pattern:avoid AI-Lifecycle: project AI-Memory-Id: 70a43cb3 AI-Source: heuristic
|
Addressed all review comments in commit 45c8607:
All 585 unit tests pass. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@tests/unit/infrastructure/services/RuntimeService.test.ts`:
- Around line 93-100: The test using an absolute path should be made
deterministic by creating a temporary working directory and then creating a
regular file at the location where the code will try to mkdir (so mkdirSync
fails), instead of relying on '/nonexistent/...'; update the test in
RuntimeService.test.ts to create a temp dir (or use a tmp file), write an
ordinary file at the intended cwd subpath (the `.git-mem/` parent) before
calling service.activate(createRuntimeData(), <that temp path>), then call
service.activate and assert it does not throw; this targets the service.activate
and createRuntimeData() usage so the write error is reliably reproduced in all
environments.
AI-Agent: Codex/0.92.0 AI-Model: gpt-5.3-codex AI-Gotcha: address remaining PR #88 review comments AI-Confidence: medium AI-Tags: commands, tests, unit, infrastructure, services AI-Lifecycle: project AI-Memory-Id: 3e255327 AI-Source: heuristic
AI-Agent: Codex/0.92.0 AI-Model: gpt-5.3-codex AI-Gotcha: address remaining PR #88 review comments AI-Confidence: medium AI-Tags: commands, tests, unit, infrastructure, services AI-Lifecycle: project AI-Memory-Id: 3e255327 AI-Source: heuristic
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/unit/infrastructure/services/RuntimeService.test.ts`:
- Around line 17-29: The tests create a temp dir in beforeEach and call
cleanup() manually in try/finally inside each test; replace the empty after()
hook with afterEach(cleanup) so cleanup() runs automatically after every test
(and still handles existence checks), then remove the per-test try/finally
blocks; reference the beforeEach, after/afterEach, and cleanup() symbols to
locate and update the test file and ensure no leftover empty hooks remain.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
AI-Agent: Codex/0.92.0 AI-Model: gpt-5.3-codex AI-Gotcha: address latest PR #88 review and sonar issues AI-Confidence: medium AI-Tags: infrastructure, services, tests, unit AI-Lifecycle: project AI-Memory-Id: f7811177 AI-Source: heuristic
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/infrastructure/services/AgentResolver.ts`:
- Around line 54-60: In getRuntimeData(), remove the redundant nullish
coalescing assignment and replace "this.cachedRuntimeData ??=
this.runtimeService?.read(this.cwd);" with a straightforward assignment so the
code reads "this.cachedRuntimeData = this.runtimeService?.read(this.cwd);"; keep
use of the guards runtimeDataLoaded, runtimeService, and cwd as-is and ensure
cachedRuntimeData and runtimeDataLoaded remain the same fields on the class.
In `@tests/unit/infrastructure/services/RuntimeService.test.ts`:
- Line 93: Remove the no-op assertion `assert.ok(true, 'activate should not
throw on write errors')` from the test in RuntimeService.test.ts (the test that
calls service.activate) because it provides no value—if service.activate throws
the test will already fail; either delete the assertion or replace it with a
short inline comment like "// ensure activate does not throw on write errors" to
document intent while keeping the test logic unchanged.
AI-Agent: Codex/0.92.0 AI-Model: gpt-5.3-codex AI-Confidence: low AI-Tags: hooks, infrastructure, services, tests, unit AI-Lifecycle: project AI-Memory-Id: 63e2c401 AI-Source: heuristic
AI-Agent: Codex/0.92.0 AI-Model: gpt-5.3-codex AI-Convention: No-op assertions (like assert.ok(true)) should be removed from tests as they provide no value and can be misleading about test coverage. AI-Confidence: high AI-Tags: testing, test-quality, assertions, runtime-service, exception-handling, test-framework AI-Lifecycle: project AI-Memory-Id: cd0e49b8 AI-Source: llm-enrichment
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| !data || | ||
| typeof data.timestamp !== 'string' || | ||
| typeof data.sessionId !== 'string' || | ||
| typeof data.source !== 'string' |
There was a problem hiding this comment.
The validation added here checks for required fields (timestamp, sessionId, source) but doesn't validate the type of the optional agent and model fields. According to IRuntimeData, these must be string or undefined, but if the JSON contains non-string values (e.g., numbers, objects), they will pass validation and be returned with incorrect types. Consider adding type checks: (data.agent === undefined || typeof data.agent === 'string') && (data.model === undefined || typeof data.model === 'string').
| typeof data.source !== 'string' | |
| typeof data.source !== 'string' || | |
| (data.agent !== undefined && typeof data.agent !== 'string') || | |
| (data.model !== undefined && typeof data.model !== 'string') |



Summary
IRuntimeServiceinterface andRuntimeServiceimplementation for persisting session data to.git-mem/runtime.jsonAgentResolverto read runtime.json as fallback when env vars are emptySessionStartHandleractivates runtime on session start with detected agent/modelSessionStopHandlerdeactivates runtime on session stop to prevent stale attributionTest plan
npm run type-checkpassesnpm run lintpasses.git-mem/runtime.jsoncreatedCloses GIT-123
🤖 Generated with Claude Code
Summary by CodeRabbit