From 1aa4867276a3b539ada7cd9c4a92ceb3839da47e Mon Sep 17 00:00:00 2001 From: NagyVikt Date: Tue, 12 May 2026 10:46:46 +0200 Subject: [PATCH] Keep Claude edit tracking compatible with Python 3.10 Claude Code invokes repo-local PostToolUse hooks through system python3, which can still be Python 3.10. The edit tracker imported datetime.UTC, so the hook failed before it could safely ignore or record edit payloads. Using timezone.utc keeps the same timestamp semantics without requiring Python 3.11. Constraint: Claude hook command uses python3 from the local shell environment Rejected: Pin the hook to a newer interpreter | Claude settings should not assume a repo-local Python runtime Confidence: high Scope-risk: narrow Directive: Keep .claude and .codex hook copies in parity for shared agent-hook behavior Tested: node --test test/post-edit-tracker-hook.test.js Tested: python3 -m py_compile .claude/hooks/post_edit_tracker.py .codex/hooks/post_edit_tracker.py Tested: openspec validate agent-codex-codex-task-2026-05-12-10-39 --type change --strict Tested: openspec validate --specs --- .claude/hooks/post_edit_tracker.py | 4 +-- .codex/hooks/post_edit_tracker.py | 4 +-- .../.openspec.yaml | 2 ++ .../proposal.md | 14 ++++++++ .../specs/codex-task/spec.md | 9 +++++ .../tasks.md | 34 +++++++++++++++++++ test/post-edit-tracker-hook.test.js | 24 +++++++++++++ 7 files changed, 87 insertions(+), 4 deletions(-) create mode 100644 openspec/changes/agent-codex-codex-task-2026-05-12-10-39/.openspec.yaml create mode 100644 openspec/changes/agent-codex-codex-task-2026-05-12-10-39/proposal.md create mode 100644 openspec/changes/agent-codex-codex-task-2026-05-12-10-39/specs/codex-task/spec.md create mode 100644 openspec/changes/agent-codex-codex-task-2026-05-12-10-39/tasks.md create mode 100644 test/post-edit-tracker-hook.test.js diff --git a/.claude/hooks/post_edit_tracker.py b/.claude/hooks/post_edit_tracker.py index 54d2918c..935cfbd0 100755 --- a/.claude/hooks/post_edit_tracker.py +++ b/.claude/hooks/post_edit_tracker.py @@ -8,7 +8,7 @@ import json import sys -from datetime import UTC, datetime +from datetime import datetime, timezone from pathlib import Path try: @@ -80,7 +80,7 @@ def main() -> None: files.append(file_path) state["files"] = files state["modified"] = True - state["last_modified"] = datetime.now(UTC).isoformat() + state["last_modified"] = datetime.now(timezone.utc).isoformat() with open(state_path, "w") as f: json.dump(state, f, indent=2) diff --git a/.codex/hooks/post_edit_tracker.py b/.codex/hooks/post_edit_tracker.py index 54d2918c..935cfbd0 100755 --- a/.codex/hooks/post_edit_tracker.py +++ b/.codex/hooks/post_edit_tracker.py @@ -8,7 +8,7 @@ import json import sys -from datetime import UTC, datetime +from datetime import datetime, timezone from pathlib import Path try: @@ -80,7 +80,7 @@ def main() -> None: files.append(file_path) state["files"] = files state["modified"] = True - state["last_modified"] = datetime.now(UTC).isoformat() + state["last_modified"] = datetime.now(timezone.utc).isoformat() with open(state_path, "w") as f: json.dump(state, f, indent=2) diff --git a/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/.openspec.yaml b/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/.openspec.yaml new file mode 100644 index 00000000..40cc12f4 --- /dev/null +++ b/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-12 diff --git a/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/proposal.md b/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/proposal.md new file mode 100644 index 00000000..dd9e09cb --- /dev/null +++ b/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/proposal.md @@ -0,0 +1,14 @@ +## Why + +- Claude Code reports a non-blocking `PostToolUse` hook traceback after edits because the repo-local Python hook imports `datetime.UTC`, which is unavailable under Python 3.10. +- The hook should run cleanly on the Python versions commonly exposed as `python3` in agent shells. + +## What Changes + +- Replace `datetime.UTC` with `datetime.timezone.utc` in the Claude and Codex edit-tracker hook copies. +- Add focused regression coverage that executes the Claude `PostToolUse` edit tracker through system `python3`. + +## Impact + +- Affects only local agent hook bookkeeping for edited files. +- No runtime package behavior changes outside hook compatibility. diff --git a/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/specs/codex-task/spec.md b/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/specs/codex-task/spec.md new file mode 100644 index 00000000..ce1af644 --- /dev/null +++ b/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/specs/codex-task/spec.md @@ -0,0 +1,9 @@ +## ADDED Requirements + +### Requirement: PostToolUse edit tracker Python compatibility +The `PostToolUse` edit-tracker hook SHALL run successfully under the system `python3` used by Claude Code when that interpreter is Python 3.10 or newer. + +#### Scenario: Claude edit tracker hook starts cleanly +- **WHEN** Claude Code invokes `.claude/hooks/post_edit_tracker.py` with a valid `PostToolUse` payload +- **THEN** the hook exits with status `0` +- **AND** no `ImportError` traceback is emitted for `datetime.UTC`. diff --git a/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/tasks.md b/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/tasks.md new file mode 100644 index 00000000..851f11e9 --- /dev/null +++ b/openspec/changes/agent-codex-codex-task-2026-05-12-10-39/tasks.md @@ -0,0 +1,34 @@ +## Definition of Done + +This change is complete only when **all** of the following are true: + +- Every checkbox below is checked. +- The agent branch reaches `MERGED` state on `origin` and the PR URL + state are recorded in the completion handoff. +- If any step blocks (test failure, conflict, ambiguous result), append a `BLOCKED:` line under section 4 explaining the blocker and **STOP**. Do not tick remaining cleanup boxes; do not silently skip the cleanup pipeline. + +## Handoff + +- Handoff: change=`agent-codex-codex-task-2026-05-12-10-39`; branch=`agent//`; scope=`TODO`; action=`continue this sandbox or finish cleanup after a usage-limit/manual takeover`. +- Copy prompt: Continue `agent-codex-codex-task-2026-05-12-10-39` on branch `agent//`. Work inside the existing sandbox, review `openspec/changes/agent-codex-codex-task-2026-05-12-10-39/tasks.md`, continue from the current state instead of creating a new sandbox, and when the work is done run `gx branch finish --branch agent// --base dev --via-pr --wait-for-merge --cleanup`. + +## 1. Specification + +- [x] 1.1 Finalize proposal scope and acceptance criteria for `agent-codex-codex-task-2026-05-12-10-39`. +- [x] 1.2 Define normative requirements in `specs/codex-task/spec.md`. + +## 2. Implementation + +- [x] 2.1 Implement scoped behavior changes. +- [x] 2.2 Add/update focused regression coverage. + +## 3. Verification + +- [x] 3.1 Run targeted project verification commands. +- [x] 3.2 Run `openspec validate agent-codex-codex-task-2026-05-12-10-39 --type change --strict`. +- [x] 3.3 Run `openspec validate --specs`. + +## 4. Cleanup (mandatory; run before claiming completion) + +- [ ] 4.1 Run the cleanup pipeline: `gx branch finish --branch agent// --base dev --via-pr --wait-for-merge --cleanup`. This handles commit -> push -> PR create -> merge wait -> worktree prune in one invocation. +- [ ] 4.2 Record the PR URL and final merge state (`MERGED`) in the completion handoff. +- [ ] 4.3 Confirm the sandbox worktree is gone (`git worktree list` no longer shows the agent path; `git branch -a` shows no surviving local/remote refs for the branch). diff --git a/test/post-edit-tracker-hook.test.js b/test/post-edit-tracker-hook.test.js new file mode 100644 index 00000000..a928b1fe --- /dev/null +++ b/test/post-edit-tracker-hook.test.js @@ -0,0 +1,24 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const cp = require('node:child_process'); +const path = require('node:path'); + +const repoRoot = path.resolve(__dirname, '..'); + +test('PostToolUse edit tracker runs on Python 3.10 compatible datetime APIs', () => { + const payload = JSON.stringify({ + session_id: 'python-compat', + cwd: repoRoot, + tool_input: { + file_path: path.join(repoRoot, 'README.md'), + }, + }); + + const result = cp.spawnSync('python3', ['.claude/hooks/post_edit_tracker.py'], { + cwd: repoRoot, + input: payload, + encoding: 'utf8', + }); + + assert.equal(result.status, 0, result.stderr || result.stdout); +});