Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Gemini-specific event handling:
`agent-note init` installs three git hooks alongside the agent's hook config:

- **`prepare-commit-msg`**: Checks heartbeat freshness (< 1 hour) and file evidence (`changes.jsonl` or `pre_blobs.jsonl`) before injecting an `Agentnote-Session` trailer for plain git commits. Prompt-only sessions do not get plain git hook trailers. Agent `PreToolUse git commit` hooks may still inject trailers for prompt-only rescue because the commit command itself came from the agent. Skips amends.
- **`post-commit`**: Reads session ID from HEAD's trailer, calls `agent-note record <sid>` to write git note. If `prepare-commit-msg` marked a long-running session as too stale for trailer injection, it calls `agent-note record --fallback-head`, which records only when a session post-edit blob matches a committed HEAD blob.
- **`post-commit`**: Reads session ID from HEAD's trailer, calls `agent-note record <sid>` to write git note. If `prepare-commit-msg` marked a long-running session as too stale for trailer injection, it calls `agent-note record --fallback-head`, which records only when a session post-edit blob matches a committed HEAD blob. If no trailer or stale marker exists but the current process exposes an adapter-supported session environment such as `CODEX_THREAD_ID`, it calls `agent-note record --fallback-env` to recover a fresh Codex transcript without trusting a stale active-session pointer. Env fallback prefers transcript rows tied to current commit files, ignores rows after HEAD, can recover work prepared just before the previous commit when no newer match exists, and uses commit-level attribution only for mutating shell-only work without exact `files_touched`.
- **`pre-push`**: Auto-pushes `refs/notes/agentnote` to remote. Uses `AGENTNOTE_PUSHING` recursion guard.

Existing hooks are backed up and chained. Compatible with husky/lefthook.
Expand Down Expand Up @@ -157,6 +157,6 @@ Each `UserPromptSubmit` increments a turn counter. File changes inherit the curr
- **Never break git commit.** All agent-note recording is wrapped in try/catch. If agent-note fails, the commit must still succeed.
- **All source code in English.** Comments, variable names, CLI output, test descriptions — everything in English.
- **PreToolUse hooks are synchronous.** Must write JSON to stdout, must not be marked `async: true`.
- **Input validation.** Session IDs must match UUID v4. `transcript_path` must be under the agent's home directory (e.g. `~/.claude/` for Claude Code, `~/.gemini/` for Gemini CLI).
- **Input validation.** Environment-provided session IDs must use canonical UUID format. `transcript_path` must be under the agent's home directory (e.g. `~/.claude/` for Claude Code, `~/.gemini/` for Gemini CLI).
- **Git notes for persistent storage.** Entry data goes to `refs/notes/agentnote`, not to files.
- **Biome for lint + format.** Run `npm run lint` (biome check) and `npm run typecheck` (tsc) separately. Both must pass in CI.
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Gemini-specific event handling:
`agent-note init` installs three git hooks alongside the agent's hook config:

- **`prepare-commit-msg`**: Checks heartbeat freshness (< 1 hour) and file evidence (`changes.jsonl` or `pre_blobs.jsonl`) before injecting an `Agentnote-Session` trailer for plain git commits. Prompt-only sessions do not get plain git hook trailers. Agent `PreToolUse git commit` hooks may still inject trailers for prompt-only rescue because the commit command itself came from the agent. Skips amends.
- **`post-commit`**: Reads session ID from HEAD's trailer, calls `agent-note record <sid>` to write git note. If `prepare-commit-msg` marked a long-running session as too stale for trailer injection, it calls `agent-note record --fallback-head`, which records only when a session post-edit blob matches a committed HEAD blob.
- **`post-commit`**: Reads session ID from HEAD's trailer, calls `agent-note record <sid>` to write git note. If `prepare-commit-msg` marked a long-running session as too stale for trailer injection, it calls `agent-note record --fallback-head`, which records only when a session post-edit blob matches a committed HEAD blob. If no trailer or stale marker exists but the current process exposes an adapter-supported session environment such as `CODEX_THREAD_ID`, it calls `agent-note record --fallback-env` to recover a fresh Codex transcript without trusting a stale active-session pointer. Env fallback prefers transcript rows tied to current commit files, ignores rows after HEAD, can recover work prepared just before the previous commit when no newer match exists, and uses commit-level attribution only for mutating shell-only work without exact `files_touched`.
- **`pre-push`**: Auto-pushes `refs/notes/agentnote` to remote. Uses `AGENTNOTE_PUSHING` recursion guard.

Existing hooks are backed up and chained. Compatible with husky/lefthook.
Expand Down Expand Up @@ -157,6 +157,6 @@ Each `UserPromptSubmit` increments a turn counter. File changes inherit the curr
- **Never break git commit.** All agent-note recording is wrapped in try/catch. If agent-note fails, the commit must still succeed.
- **All source code in English.** Comments, variable names, CLI output, test descriptions — everything in English.
- **PreToolUse hooks are synchronous.** Must write JSON to stdout, must not be marked `async: true`.
- **Input validation.** Session IDs must match UUID v4. `transcript_path` must be under the agent's home directory (e.g. `~/.claude/` for Claude Code, `~/.gemini/` for Gemini CLI).
- **Input validation.** Environment-provided session IDs must use canonical UUID format. `transcript_path` must be under the agent's home directory (e.g. `~/.claude/` for Claude Code, `~/.gemini/` for Gemini CLI).
- **Git notes for persistent storage.** Entry data goes to `refs/notes/agentnote`, not to files.
- **Biome for lint + format.** Run `npm run lint` (biome check) and `npm run typecheck` (tsc) separately. Both must pass in CI.
12 changes: 7 additions & 5 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,13 +388,15 @@ Three git hooks handle commit integration and notes sharing:
| Git hook | When | What it does |
|---|---|---|
| `prepare-commit-msg` | Before commit message editor opens | Checks session freshness and file evidence (`changes.jsonl` or `pre_blobs.jsonl`), then appends `Agentnote-Session` trailer. Prompt-only active sessions are skipped for plain git commits. Skips amend/reuse (`$2=commit`). |
| `post-commit` | After commit succeeds | Reads session ID from the finalized trailer on HEAD, calls `agent-note record <session-id>` to write git note. If `prepare-commit-msg` explicitly marked a stale-heartbeat fallback, calls `agent-note record --fallback-head`, which only records when a session post-edit blob matches a committed HEAD blob. Idempotent — skips if note already exists. |
| `pre-push` | Before push to remote | Auto-pushes `refs/notes/agentnote` to the actual remote (`$1`) in background. Recursion-guarded via `AGENTNOTE_PUSHING` env var. |
| `post-commit` | After commit succeeds | Reads session ID from the finalized trailer on HEAD, calls `agent-note record <session-id>` to write git note. If `prepare-commit-msg` explicitly marked a stale-heartbeat fallback, calls `agent-note record --fallback-head`, which only records when a session post-edit blob matches a committed HEAD blob. If no trailer or marker exists but the current process exposes an adapter-supported session environment such as `CODEX_THREAD_ID`, calls `agent-note record --fallback-env`; fresh mutating transcript work can become commit-level attribution even when exact `files_touched` is unavailable. Idempotent — skips if note already exists. |
| `pre-push` | Before push to remote | Pushes `refs/notes/agentnote` to the actual remote (`$1`) and waits for `push-notes` to finish. Recursion-guarded via `AGENTNOTE_PUSHING` env var. |

Session freshness is verified via per-session heartbeat file (`sessions/<id>/heartbeat`). Heartbeat is refreshed by normalized hook events during long turns. `Stop` does NOT invalidate the heartbeat — it fires when the AI finishes responding, not when the session ends. Gemini `SessionEnd` is a real session termination and removes the heartbeat. Missing heartbeat in `prepare-commit-msg` skips trailer injection. Stale heartbeat writes a one-shot fallback marker for brand-new commits only; `post-commit` consumes that marker and records only if the active session has post-edit blob evidence that matches the committed HEAD blobs.
Session freshness is verified via per-session heartbeat file (`sessions/<id>/heartbeat`). Heartbeat is refreshed by normalized hook events during long turns. `Stop` does NOT invalidate the heartbeat — it fires when the AI finishes responding, not when the session ends. Gemini `SessionEnd` is a real session termination and removes the heartbeat. Missing heartbeat in `prepare-commit-msg` skips trailer injection. Stale heartbeat writes a one-shot fallback marker for brand-new commits only; `post-commit` consumes that marker and records only if the active session has post-edit blob evidence that matches the committed HEAD blobs. Agent-hosted terminals may also expose the current session through adapter-specific environment variables. Today, Codex exposes `CODEX_THREAD_ID`, which lets `post-commit` recover a fresh Codex transcript even when `.git/agentnote/session` points at a stale or unrelated session.

Plain git hook trailer injection also requires file evidence. File-change records or pre-edit blobs count as safe evidence because they can be matched back to committed files. Prompts alone are not enough for plain git hooks: a fresh prompt-only active session might belong to another agent or terminal workflow. Agent hook trailer injection can still preserve prompt-only work because the commit command itself was observed inside the agent. Transcript paths are supporting metadata, not recordable data by themselves. Heartbeat, `SessionStart`, and `transcript_path` metadata alone do not receive dangling `Agentnote-Session` trailers.

Environment fallback is narrower than trailer injection. It does not trust `.git/agentnote/session`; it trusts only an adapter-provided current process environment session id, validates the session id, discovers the agent transcript through the adapter, and requires a fresh heartbeat or fresh transcript mtime before recording. This helps terminals or agent hosts such as cmux, where the current Codex process may expose `CODEX_THREAD_ID` even if the repository active-session pointer was not updated. If the trusted transcript has direct file matches, Agent Note may ignore stale repository-local prompt logs and prefer the newest transcript rows after the parent commit that cover the commit files. If no newer matching row exists, it can still recover matching transcript work prepared just before the previous commit was finalized. Rows after the target commit are always ignored. If the trusted transcript has current mutating shell work but no exact per-prompt file touches, Agent Note records commit-level attribution by marking the commit files as AI-assisted while leaving `files_touched` empty. Read-only shell activity such as status checks is not enough for env fallback attribution.

### Git hook installation

`agent-note init` installs git hooks respecting the repository's hook directory:
Expand Down Expand Up @@ -623,7 +625,7 @@ Notes are automatically pushed to the remote via the `pre-push` git hook install
- **All source in English.** Comments, output, tests.
- **Git hooks for commit ops.** Trailer injection (`prepare-commit-msg`) and note recording (`post-commit`) use git hooks, not agent hooks. Respects `core.hooksPath` and chains with existing hooks.
- **No telemetry, no auth, no external services.** Data stays local until pushed. The `pre-push` git hook (installed by `agent-note init`) auto-pushes notes alongside code on every `git push`.
- **Input validation.** Session IDs must match `/^[0-9a-f-]{36}$/` (UUID v4). `transcript_path` must be under `~/.claude/` (or agent equivalent). Reject anything else silently.
- **Input validation.** Environment-provided session IDs must use canonical UUID format. `transcript_path` must be under `~/.claude/` (or agent equivalent). Reject anything else silently.
- **Full response storage.** AI responses are stored in full. Git notes blobs are compressed and well within GitHub limits.

## Security
Expand All @@ -642,7 +644,7 @@ Agent Note records prompts, optional display-only context excerpts, and AI respo
| Threat | Mitigation |
|---|---|
| Secrets in prompts/context/responses | **Not automatically redacted.** Users are responsible for reviewing notes before pushing them to public repos. |
| Command injection via session ID | Session ID validated as UUID v4 before trailer injection. Non-matching IDs are silently dropped. |
| Command injection via session ID | Environment-provided session IDs are validated as canonical UUIDs before fallback recording. Non-matching IDs are silently dropped. |
| Transcript path traversal | `transcript_path` must be under `~/.claude/` (or agent equivalent). Paths outside are rejected. |
| git notes tampering | Anyone with repo write access can modify or delete notes. Notes are **not signed or encrypted**. Treat them as advisory, not as audit trail. |
| GitHub Action markdown injection | PR Report renders prompts/context/responses as markdown inside the PR body. Treat git notes as trusted repository data; do not push untrusted prompt content to public notes. |
Expand Down
5 changes: 3 additions & 2 deletions docs/knowledge/agent-skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,9 @@ not make them the primary user action.
confirmation for every commit.
- Preserve existing hooks and workflows. If a hook or workflow exists, update it
carefully rather than replacing it blindly.
- Never infer AI-authored files from shell-only evidence. Follow Agent Note's
recorded note data.
- Do not invent per-prompt `files_touched` from shell command text. Follow
Agent Note's recorded note data; commit-level attribution may still mark
files AI-assisted when the current Agent transcript is trusted.
- When troubleshooting missing data, distinguish "no tracked commits" from a
true `0%` AI Ratio.

Expand Down
5 changes: 3 additions & 2 deletions docs/knowledge/agent-support-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,8 @@ Codex CLI は `Supported` とする。
- patch 行数が commit と一致したときの safe line-level upgrade は成立している
- 通常は file-level attribution で説明可能である
- transcript が読めない、または不確かな場合は note を作らず安全側に倒れる
- shell-only の変更を transcript だけから AI-authored file と推測しない
- shell command text だけから per-prompt `files_touched` は作らない
- current Agent transcript を信頼できる場合は commit-level attribution として commit files を AI-assisted 扱いできる

判断:

Expand Down Expand Up @@ -235,7 +236,7 @@ Status: 完了。

- transcript path validation の hardening
- parser の fixture 強化
- shell-only change recovery の到達範囲を docs に明示
- shell-only change recovery と commit-level attribution の到達範囲を docs に明示
- `status` で Codex の capture 詳細を表示するか検討し、必要なら追加する
- README と website の `Codex CLI | Supported` を維持する

Expand Down
Loading
Loading