Skip to content

Single-source the shared helpers in the three transcript-walking instruments #1900

Description

@The01Geek

Dependencies

Blocked by #1899 — that issue fixes a defect living inside two of the helpers this issue moves. Extracting first means fixing the same defect a second time in the extracted copy, and it risks levelling the correct implementation down to match the two broken ones.

Problem Statement

Three scripts read a run's transcript and measure how much context it used. Each carries its own private copy of the same five helpers. The copies drift, and the drift is not hypothetical: it produced a real defect in which two of the three instruments reported an unmeasured turn as a real zero while the third reported it as unknown. The rule of three is reached, so the shared code moves into one place that all three read.

Current Behavior

Five names are defined separately in each of scripts/create_issue_eval.py, scripts/implement-context-eval.py, and scripts/review-context-eval.py: _iter_session_files, _median, _context_tokens, the per-field usage reader, and the UNESTABLISHED sentinel constant. Compared with docstrings and comments stripped, _iter_session_files and _median are functionally identical across all three, differing only in one line wrap.

The three files are 2562, 928, and 554 lines. UNESTABLISHED is additionally defined in scripts/create_issue_benchmark.py, which is a fourth consumer that does not walk transcripts.

  • Verified: git grep -ln "^UNESTABLISHED" -- scripts lib returns the three instruments plus scripts/create_issue_benchmark.py. Re-derive with that command.
  • Verified: each of the three files defines _iter_session_files, _median, and _context_tokens. Re-derive with git grep -n "def _iter_session_files\|def _median\|def _context_tokens" -- scripts.
  • Environment fact: the duplication happens regardless of environment. It is the shape of the three files.

Desired Behavior

Each of the five shared names has exactly one definition, in a module the three instruments import. All three instruments keep the strict behavior for an unmeasured turn, an empty population, and a non-finite number that issue #1899 establishes. Each instrument's own focused test keeps passing with its assertions about instrument output unchanged.

User Impact

A maintainer fixing one of these helpers fixes it once. A defect found in one instrument cannot silently survive in the other two.

Technical Context

Scope note: The files and details below are the known starting points, not the full list. Before implementing, trace the change through the codebase to find every affected call site, consumer, and layer — this issue maps the work, it does not bound it.

  • Relevant Classes/Filesscripts/create_issue_eval.py, scripts/implement-context-eval.py, scripts/review-context-eval.py each define the five shared names. scripts/create_issue_benchmark.py defines the sentinel constant separately. scripts/create-issue-context-eval.py is a small compatibility shim that loads create_issue_eval through importlib; it is not a fourth instrument. lib/test/modules/coverage-map.json carries a row for each of the three instruments.
  • Architecture Alignment — this repository's stated default is that a new fact lives in exactly one place and every other mention points at it. Physical copies held under a coupled-invariant test are recorded as backlog to collapse, not as a target state. So the shared code is extracted into a module, and no coupled-invariant test pinning three copies identical is written.
  • Dependencies — the Python standard library only. The extracted module imports nothing outside it, so the instruments keep running against the tools lib/preflight.sh guarantees.
  • Data/Schema Considerations — none. No stored record changes shape.
  • Cross-layer Impact — the three instrument scripts, their three focused tests, and the coverage map that routes to those tests.
  • Provenance: recorded as section C3 of umbrella issue Umbrella: findings and learnings from the #1850 / #1852 / #1443 shepherding session — to be split before implementation #1890.
  • The extraction set named in that umbrella included _force_utf8_streams, and this issue drops it. That helper has 63 separate definitions across scripts/ and lib/, so it is a repository-wide idiom rather than a three-instrument duplication, and folding it in turns a bounded refactor into an unbounded one. — assumption, confirm before implementing

Acceptance Criteria

  • Each of these five names has exactly one definition reachable by all three instruments: _iter_session_files, _median, _context_tokens, the per-field usage reader, and the UNESTABLISHED sentinel. That list is complete for this change.
  • After the change, all three instruments return a non-numeric unestablished result for an empty usage object, raise on an empty median population, and return a non-numeric unestablished result for a non-finite token count. No instrument returns zero for any of the three.
  • lib/test/test_create_issue_context_eval.py, lib/test/test_implement_context_eval.py, and lib/test/test_review_context_eval.py all pass, with their assertions about instrument output unchanged by this work.
  • lib/test/modules/coverage-map.json names a covering test for the extracted module.
  • _force_utf8_streams keeps its per-file definitions and is not moved into the extracted module.

Implementation Notes

  • Approach — create one shared module under scripts/ holding the five names, and have each of the three instruments import from it instead of defining its own copy. Take the post-Report an unmeasured turn as unestablished in the create-issue and implement context instruments #1899 implementation as the single definition, which is the strict one. Leave scripts/create_issue_benchmark.py's own sentinel constant alone unless importing the shared one is a clean drop-in.
  • Relevant files — this touches scripts/create_issue_eval.py, scripts/implement-context-eval.py, scripts/review-context-eval.py, a new shared module beside them, and lib/test/modules/coverage-map.json. It plausibly touches scripts/create_issue_benchmark.py and scripts/create-issue-context-eval.py, the importlib shim, whose loading path must keep working.
  • Code Patterns — the repository already ships shared Python helpers under scripts/ that other scripts import directly, so no new packaging pattern is needed. Every new Python file carries the standard licence header.
  • Testing Strategy — the three existing focused tests are the primary evidence, and they run unchanged. Add one test on the extracted module itself covering the three strict behaviors: an empty usage object, an empty median population, and a non-finite token count. Add one test that loads scripts/create-issue-context-eval.py's shim path and asserts it still resolves, since that indirection is the one call site an import change can quietly break.
  • Documentation Needed — none. The three instruments are maintainer tools documented in their own module docstrings, which the change updates in place.
  • Potential Gotchas — the strict implementation is the one to keep. Taking the shortest copy, the most-used copy, and equally the first copy read will level the correct behavior down to the broken one, which is the exact defect Report an unmeasured turn as unestablished in the create-issue and implement context instruments #1899 exists to remove. The importlib shim at scripts/create-issue-context-eval.py loads a sibling by file path rather than by module name, so an import added to the loaded file must resolve under that loading style.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions