test(harness): close three smoke-select and fixture blind spots (#754)

Three independent blind spots in the test harness.

1. smoke-select read symbols only from changed lines of a --unified=0
   diff. An edit to the arguments of a multi-line call names nothing:
   #741 (d5bdfde9) changed only the arguments of
   runtime.resolveIsoOverlayFitEnvelope({ on the line above, and the
   selection answered "unproven" plus the visual minimum, although the
   callee is registered in smoke-links for smoke_iso_flat_parity and
   smoke_isometric_contract - the two smokes the #741 author ran by hand.
   The selection diff now carries CALL_CONTEXT_LINES = 3 lines of
   context; for each changed line parseDiff looks for the nearest
   unclosed "(" above it within the hunk, walking through a literal
   argument ({ or [ after "(", "," or "["), stopping at ";" on depth zero
   or any other unclosed brace. A callee from the symbol table joins
   symbols and the new callees field and is marked "(вызов)" in the
   report. Context lines never give direct symbols. task-packet takes a
   separate context diff for selectSmokes; change-risk keeps --unified=0.
   Over the last 80 src commits of dev: 16 commits gain a callee, 2 move
   from unproven to a proven link (#741, #724 5f8e8ca7), +15 smokes in
   total, at most 4 per commit, none lost.

2. The #732 dead-field check judged only scene-builder calls. The four
   resolveIsoOverlayFitEnvelope({...}) literals in iso-scene-render tests
   went straight into the test-build function, so stageSize: null (the
   field #741 removed) stayed green. They now go through overlayFit typed
   with OverlayFitFixture (keys of IsoOverlayFitEnvelopeInput); the check
   judges overlayFit/resolveIsoOverlayFitEnvelope calls like the scene
   builders, and its probe asserts that OverlayFitFixture rejects
   stageSize, so the type resolved to the real input and not to any.

3. smoke_backdrop's mode() called the private _setMode and slept 220 ms.
   It now enters a mode through __hpTest.setMode and waits for the end of
   the transition by the same markers as section 6b (#715): one page
   helper used by both. Oracles and the 59 check names are unchanged.

Witnesses: d5bdfde9 selects both iso smokes with no "unproven"; the same
fixture without context lines is unproven again; attribution disabled
reds both AC1 units. stageSize: null in an overlayFit call reds the first
#732 test; a direct resolveIsoOverlayFitEnvelope({...}) reds the third.
smoke_backdrop is green normally and with animation frames slowed to 60
and 150 ms; a stage animation that never ends fails with a named error.

Issue: #754
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
This commit is contained in:
Claude
2026-10-01 13:54:23 +03:00
parent 7ce3f6629f
commit 871a689ebf
8 changed files with 387 additions and 63 deletions
+4 -2
View File
@@ -27,7 +27,7 @@ import { classify } from './process-gate.mjs';
import {
classifyRisk, cycleLimit, hasTrackLabel, rebaseBeforeReview, riskClassLine, trackFromLabels, trackOrigin,
} from './process-track.mjs';
import { selectSmokes } from './smoke-select.mjs';
import { CALL_CONTEXT_LINES, selectSmokes } from './smoke-select.mjs';
export const STATUS_LABELS = ['S1-new', 'S2-analysis', 'S3-spec', 'S4-spec-review', 'S5-ready', 'S6-in-progress', 'S7-code-review', 'S8-merged'];
@@ -516,7 +516,9 @@ export function collectInputs({ number, repo = 'Matysh/houseplan-card', cwd = pr
const merge = behind > 0 ? readMergeState({ cwd, onto: 'origin/dev', ref }) : { clean: true, conflicts: [] };
let smokes = null;
try {
const selection = selectSmokes(diff);
// #754: выборке нужен контекст, чтобы приписать правку аргументов вызову;
// риск по участкам (`diff`) остаётся на `--unified=0`.
const selection = selectSmokes(sh('git', ['-c', 'core.quotePath=false', 'diff', `--unified=${CALL_CONTEXT_LINES}`, '-M', '--no-color', '--no-ext-diff', '--no-textconv', `${base}..${ref}`], { cwd }));
smokes = {
direct: selection.direct.filter((entry) => entry.strong).map(({ smoke, symbols }) => ({ smoke, symbols })),
registered: selection.registered.map(({ smoke, symbols }) => ({ smoke, symbols })),