Skip to content

fix(profiles): a Codex apply_patch counts as an edit in the style observation (#727, slice 1) - #882

Merged
cdeust merged 2 commits into
mainfrom
fix/727-tool-operation-signal
Nov 6, 2026
Merged

cdeust merged 2 commits into
mainfrom
fix/727-tool-operation-signal

Conversation

@cdeust

@cdeust cdeust commented Nov 5, 2026

Copy link
Copy Markdown
Owner

Summary

First slice of #727. The incremental style observation (core/profile_builder._build_style_observation) recognized Claude tool names only (Edit/Write as edits, Read/Grep as reads). A Codex rollout records its edits as apply_patch (infrastructure/transcript_rollout.py), so the issue's replay holds: a one-tool session with duration=None gave activeReflective=0.3 for Edit and an empty observation for apply_patch.

Root cause: the operation an observation reads was inferred from a Claude tool name, not from a host-independent operation. Fix: new pure module core/tool_operation.py maps each native name to the operation it makes explicit (apply_patch → edit, as hooks/host_patch.py already does on the hook path). Native names stay as recorded in toolsUsed (provenance). exec (an arbitrary program) stays unclassified (None): it is not guessed into a read.

The function's final clamp to [-1, 1] is removed: activeReflective is one of {0.5, 0.0, -0.5} plus at most +0.3 and -0.3, so it stays within [-0.8, 0.8]. The clamp never acted, and its 4 mutants were equivalent; update_style_ema clamps the blended value anyway.

Refs #727

Remaining slices (not in this PR): persona_vector (reads toolPreferences keyed by native name), sparse_dictionary_activation and style_classifier (full rebuild path), with a cross-client test on transcript-derived EMA, persona and feature activation.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Test plan

  • pytest tests_py/core/test_tool_operation.py tests_py/core/test_profile_builder.py: 31 passed. The new test test_apply_patch_session_observation_equals_edit_session fails on main (checked with the fix stashed) and passes here.
  • pytest tests_py/core/: 3729 passed, 1 failed (test_wiki_coverage_dashboard.py::test_unreadable_page_is_skipped_not_fatal). It fails the same way on main in this container, which runs as root, so a chmod'd file stays readable.
  • Diff-scoped mutation gate (ADR-1122), GITHUB_BASE_REF=main python scripts/ci_mutation_diff.py: 2 file(s), 0 failed, 0 untested (_build_style_observation: 0 survivors in scope; tool_operation.py: 5/5 killed).
  • ruff check / ruff format --check on the changed files: clean. python scripts/check_craftsmanship.py: OK.
  • Not verified here: the full suite with PostgreSQL and the torch-dependent extras (the container cannot reach download-r2.pytorch.org). No retrieval benchmark applies: no retrieval or memory path changed.

Audit notes

  • No-source check: the edit/read groups are the ones ADR-0228 already used; apply_patch → edit follows hooks/host_patch.py; exec is left unknown, as the issue requires.

Coding-standards compliance

  • §2.2 Layer dependency direction preserved (core/tool_operation.py imports nothing).
  • §4.1 / §4.2 within limits (craftsmanship gate OK).
  • §9 No dead code (the unreachable clamp is removed).

Reviewer checklist

  • CHANGELOG.md updated under Unreleased / Fixed.
  • Documentation updated (docs/module-inventory.md).
  • No secrets / credentials / PII in the diff.

Generated by Claude Code

…ervation (#727, slice 1)

The incremental style observation matched Claude tool names only
(Edit/Write, Read/Grep), so a Codex session whose edits arrive as
apply_patch produced an empty observation where the same Claude edit
gave activeReflective=0.3. core/tool_operation.py names the common
operation a native tool stands for; native names stay in toolsUsed for
provenance, and the opaque exec stays unclassified.

Refs #727

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018kyZYtyEZfhpzqaqPcdkeA
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-06T00:30:40.226492Z 12ec495 New commits
🔒 Security Review ✅ Completed 2026-11-06T00:32:39.602096Z 12ec495 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@claude

claude Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Claude finished @cdeust's task in 1m 15s —— View job


I'll analyze this and get back to you.

@claude

claude Bot commented Nov 6, 2026

Copy link
Copy Markdown

Review 1 of 3: PR #882 (head 12ec495)

Five reviews ran: code quality, test coverage, documentation accuracy, security and performance.

Blockers: none.

What was checked:

  • Clamp removal. The duration step gives 0.5, 0.0 or -0.5. Each tool-mix branch fires at most once and shifts by +0.3 or -0.3, so activeReflective stays within [-0.8, 0.8]. update_style_ema still clamps the blended value (style_classifier_ema.py:14-16). The removed clamp could never act, so removing it is correct.
  • Craftsmanship gate. The # source: rule only checks numeric module constants, so the string constants and the mapping dict in tool_operation.py don't trip it.
  • Layering. core/tool_operation.py imports nothing.
  • Test values. The expected values hold at the boundaries (600_000, 1_800_000, a ratio of exactly 2/5).
  • Security. No security-relevant change.
  • Performance. _build_style_observation runs once per session end and stays O(n).

Notes, no action before merge

  • tests_py/core/test_tool_operation.py: no case has a ratio strictly between 0.4 and 0.5 or has edits and reads both dominant (e.g. ["Edit","Read"] gives 0.0), so a change to _TOOL_MIX_DOMINANCE or to the cancelling path would pass every test.
  • tool_operation.py:4-5,18: neither transcript_rollout.py nor ADR-1081 names apply_patch (only exec), so that citation doesn't back the claim that rollouts record apply_patch; hooks/host_patch.py does.
  • tool_operation.py:18-19: ADR-0228 defines the dominance threshold, not the Edit/Write and Read/Grep groups; those came from the earlier code.

I didn't run pytest, ruff or the craftsmanship gate myself: running Python needed approval in this runner. These results come from reading the code.

Verdict: approve

@cdeust
cdeust merged commit 723924f into main Nov 6, 2026
18 checks passed
@cdeust
cdeust deleted the fix/727-tool-operation-signal branch November 6, 2026 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants