Commit Graph
3940 Commits
Author SHA1 Message Date
Claudeandclaude[bot] d327ec3d93 feat(process): a failed show verdict re-routes to ask without a fresh budget (#726)
A non-green show verdict that found "something to decide" went down the same
path as "fix the code": S6 with a limit of 2. Promoting the task to track:ask
was left to the agent's memory, with no named criterion and no trace, and the
exhausted budget only surfaced on the next S7 - after a fix nobody would read.

The structured verdict now carries `route` (fix | reclassify) and an optional
`criterion` (one of the six show criteria of PROCESS.md section 5). The trust
boundary reads a missing route as fix, rejects one outside the dictionary and
rejects reclassify on a green verdict. `reviewRoute` in process-track.mjs is
the single decision: on a code review of an unconfirmed show it moves the task
to track:ask and S3-spec; on an owner-confirmed show it adds `blocked` and asks
the owner; anywhere else reclassify degrades to fix with a note. The verdict
that spends the last cycle sets review-4 at once; the stage budget is shared
across tracks, so promotion changes the limit (4), not the count.

The "Решение по вердикту" step makes one `process-track.mjs route` call (from
dev, like the track step) and only executes its output: comment from a file,
labels from add/remove lists, status via status-label.mjs as before. The track
step also emits `confirmed` and a `route_note` for the review prompt; the
review document anchor gains a route tail that the old reader still parses;
wait-verdict reports the two new pipeline comments. The guard's own
spent >= limit check stays as the safety net.

Issue: #726
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-10-01 03:06:49 +00:00
claude[bot] 56be779ca7 docs(reviews): индекс после сдвига каталога (#728)
Issue: #728
User-Visible: no
2026-10-01 03:03:28 +00:00
claude[bot] 5e430d9ede docs: review document for #728
Issue: #728
User-Visible: no
2026-10-01 03:03:28 +00:00
Claudeandclaude[bot] 0cc7c60e8b feat(process): process metrics by track — segments, returns, ship findings, job minutes (#728)
The weekly report could not say whether the tracks of #695/#696 paid off:
it read only the first S4/S5/S7/S8 placements of closed issues, no track,
no waiting, no reason for a return, and the CLI never passed jobs, so the
"Job-минуты" line never printed. The owner decides on these numbers, so the
definitions are spelled out in the report headers and anything unknown is
printed as such.

scripts/process-metrics.mjs (pure functions over the snapshot):
- K1 trackAt/trackPath: track at a moment from the labels set before it,
  resolved by process-track.mjs (labelTrack) — one rule with the pipeline;
  infra = no class A file in the issue's commits (Release: commits aside).
  The issue's track is the one at its first S8-merged.
- K2 issueSegments: queue/spec/work/review/rework/blocked from the first
  status label to the first S8, summing to lead; blocked is taken out of
  the segment under it; S7 over S7 is neither a return nor a new segment.
- K3 returnSignal/returnReason: S7 -> S6/S3 and S4 -> S3 returns, reason
  from the last comment with a sign between the review placement and the
  return. merge and the "not run" family come from PIPELINE_EVENTS, the
  verdicts from verdictDeclaration with the issue's own document; the two
  continuations have no pipeline constant, so NOT_RUN_VALIDATE_RE and
  NOT_RUN_CONFLICT_RE are exported copies held by a contract test on the
  _process.yml templates. Anything else is unknown; hp:route (#726)
  gives reclassify/owner-question when present.
- K4 shipFindings: High/Medium/Low of SHIP-REVIEW-*.md (docs/reviews and
  legacy/reviews) by the anchor block, summed per issue; the track table
  counts each document once.
- K5 stageMinutes: jobs of process and Validate runs (skipped runs aside,
  at most 600, "усечено: N из M" beyond), stages by job name, per track at
  run time, Validate per event; unavailable jobs are "нет данных", not 0.
  jobMinutes gets the same data and prints again.
- K6 tokenUsage: "Токены: нет данных (…)" until the pipeline records usage
  (issue F); the hp:usage line format is provisional.
- K7 compareCohorts: issues with the first S8 within 28 days before and
  after 2026-09-28 (--compare, --compare-days), cohort = track x volume
  bucket (<=30/31-200/201-1000/>1000 lines of Issue-trailer commits without
  Release:, class D and docs/reviews/**); n < 3 on a side is "мало данных".
- fetchSnapshot: issues state=all since the earliest window (the old
  selection is still "closed in the window"), timelines up to 10 pages
  (beyond: "таймлайн усечён"), jobs, ship and usage review docs, git log
  --numstat of origin/dev.

_process-metrics.yml: full history (fetch-depth: 0) for K1/K7 and a 30
minute ceiling. The thin process-metrics.yml is unchanged. PROCESS.md §5
points at the report. Old sections and their tests are unchanged.

Issue: #728
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-10-01 03:03:28 +00:00
Claude b3dd9444b5 test(daycycle): judge pinch frames, not the first frames of the recording (#734)
presentedFramesHaveNoWhiteTile in smoke_daycycle_layer_budget turned red now
and then on frames that are not pinch frames: the first one or two frames of
the screencast sometimes show the room before its fill (white paper, 0.997
near-white), before any pinch move, and the next frames are light grey. The
check judged every recorded frame, so a stale opening frame failed a gesture
that painted correctly.

The check now judges pinch frames only. Not judged: a frame whose swap
time (screencast metadata) is earlier than the first pinch move, and a frame
before the first one that shows the room filled. The guard keeps its power:
a white tile during the pinch comes after a filled frame and stays red; a
room white from the recording start through the whole gesture leaves no
judged frame inside the gesture, which fails both the frame count and the
white-tile check (an empty set no longer passes `every`). The frame count
counts judged frames inside the gesture, not every recorded frame.

Why the fill appears later is out of scope.

Issue: #734
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-10-01 05:33:49 +03:00
claude[bot] 52dc08a0e5 docs(reviews): индекс после сдвига каталога (#694)
Issue: #694
User-Visible: no
2026-10-01 00:22:26 +00:00
claude[bot] 78acf2c6ba docs(reviews): индекс после сдвига каталога (#694)
Issue: #694
User-Visible: no
2026-10-01 00:22:26 +00:00
claude[bot] dcb8f80de2 docs: review document for #694
Issue: #694
User-Visible: no
2026-10-01 00:22:26 +00:00
Claudeandclaude[bot] 6f17b6cd4c perf(card): a floor switch stops re-querying the same subtrees (#694)
A floor switch replaces the whole stage, so the card's pointer-hover
MutationObserver receives hundreds of records whose targets are the same
few containers. Each record re-ran `matches` and a `.devlayer` subtree
`querySelector` on its target, and kept doing so after the device layer
had already been found. The batch logic moves to `deviceLayerMutated` in
device-hit-owner.ts: a node is checked at most once per batch, the first
hit ends the checks, and every added node still goes through
`_syncPointerHoverSubtree` in record order. The card shrinks by 12 lines.

The View stair layer read the card's `_model` getter once more for every
navigable stair; the getter rebuilds the config fingerprint on each read.
`renderLayer` now reads it once.

`languageRenderGate` wrote `lang` on the host on every render. It now
writes it only when the value differs (language switch, English fallback,
a foreign value); an unchanged value is left alone.

No behaviour changes: DOM, tooltips and pixels are the same. Unit tests
count subtree queries per node, `_model` reads per render and `lang`
writes; one mutant per change restores the old behaviour.

Issue: #694
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-10-01 00:22:26 +00:00
claude[bot] 153fa7a680 docs: review document for #728
Issue: #728
User-Visible: no
2026-10-01 00:19:58 +00:00
claude[bot] fc835cb97d docs: review document for #727
Issue: #727
User-Visible: no
2026-10-01 00:18:39 +00:00
claude[bot] 499c0860df docs(reviews): индекс после сдвига каталога (#707)
Issue: #707
User-Visible: no
2026-10-01 00:15:55 +00:00
claude[bot] 8df4ef710c docs: review document for #707
Issue: #707
User-Visible: no
2026-10-01 00:15:51 +00:00
Claudeandclaude[bot] 1d51beade1 feat(process): risk by changed hunks decides ship and informs show (#707)
The ship limits count lines and files but not what was touched: a
12-line pointerdown handler passed them like a typo and merged unread.
The track rule also lived twice - the guard computed the cycle limit in
bash while process-track.mjs computed the track, and the two disagreed
on multiple track labels. The packet still told authors to rebase
show/ship branches that merge cleanly.

- scripts/change-risk.mjs: one pure classifier over `git diff -U0` from
  the merge base. Class A lines only; comments, blank lines and pure
  renames give no risk; deletions do. Area and token rules per class
  (geometry, touch, migration, devices, perf, ux, visual render/ui),
  evidence as path:line, five per class.
- process-track.mjs: owner confirmation is a comment line
  "Трек: <x> — решение владельца" by the repo owner (latest wins, only
  for the current track); several track labels read as the strictest
  with a warning; cycleLimit, guardLimit and rebaseBeforeReview are the
  single source. `stage` makes the whole S7 track decision in one call:
  ship with risk and no confirmation is raised to show with evidence,
  a confirmed ship keeps merging without the model and records the risk
  for the batch review; show/ask get a risk note for the reviewer.
- _process.yml: the guard asks process-track.mjs for the limit and keeps
  no track logic; the track step calls the script once and only
  executes its raise flag and comment file; risk_note reaches the
  Review prompt, ship_risk reaches the hp:ship-merge comment (marker
  line unchanged).
- task-packet.mjs: track basis, limit and rebase policy; next step
  without the stale rebase line; risk with its consequence per track;
  required checks with reasons (ci:golden only on render risk);
  changelog and visual evidence - from the same exports.
- ship-review.mjs: the batch brief prints the risk line of a ship merge.
- Canon: PROCESS.md §5, §5.1, §10.4, §11.7, both digests, AGENTS.md.
- Registry anchors that watched the moved code are moved, not dropped.

Issue: #707
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-10-01 00:03:35 +00:00
claude[bot] a49f7095ce docs: review document for #728
Issue: #728
User-Visible: no
2026-09-30 23:34:30 +00:00
claude[bot] 3847baa5a0 docs: review document for #727
Issue: #727
User-Visible: no
2026-09-30 23:33:52 +00:00
claude[bot] 5ce83feffa docs: review document for #726
Issue: #726
User-Visible: no
2026-09-30 23:33:09 +00:00
claude[bot] fd082ad460 docs: review document for #725
Issue: #725
User-Visible: no
2026-09-30 23:31:13 +00:00
claude[bot] 85598638c1 docs(reviews): индекс после сдвига каталога (#732)
Issue: #732
User-Visible: no
2026-09-30 23:26:46 +00:00
claude[bot] 0f7ec02352 docs: review document for #732
Issue: #732
User-Visible: no
2026-09-30 23:26:46 +00:00
Claudeandclaude[bot] 715735d753 refactor(iso): remove the empty overlay renderers and dead fixture fields (#732)
After #714 and #724 the 2.5D overlays still carried stubs:

- renderIsoOverlayGrounds and renderIsoRaisedOverlays returned an empty SVG
  on every frame. They go with IsoFramePresentation.grounds/raised and the two
  bindings in the card. The iso-overlays-svg element itself stays, now empty:
  it is the inert camera-viewBox layer the contract and live-touch smokes
  measure screen-facing HTML against, so the 2.5D DOM keeps its elements.
- IsoOverlayRenderEntry.groundRadius was computed for every device, room label
  and lock and read only by the snapshot comparison that compared it.

The overlay test fixtures passed view, referenceView, stageSize and layers
(and one test selectedDeviceId), which IsoOverlaySceneInput does not have, and
asserted that changing them keeps the placement - a claim the signature makes
by itself. Those fields are gone from every fixture. The zoom/resize asserts of
"Stage 4 reuses pure overlay placements" and "#713 AC3" (renamed to what it
still checks) and the "#570 supersedes #473 W1" selection test go; the #724
AC2 test now zooms the way production does, through the live frame of
resolveIsoScene, and checks that the structural geometry and so the overlay
scene are reused. The #713 K8 fixture no longer passes stageSize, which
resolveIsoOverlayFitEnvelope does not read.

test/iso-overlay-fixture-types.test.mjs typechecks the overlay test files with
the TypeScript compiler: their fixture types (OverlaySceneFixture,
OverlayEntryFixture) are the keys of the production types with deliberately
loose values, so a partial fixture is fine and a field the type lacks is an
excess-property error. Three checks: no excess property in the fixture files;
a probe shows the fixture types resolve to the real inputs and reject view,
referenceView, stageSize, layers, selectedDeviceId and groundRadius; every
call of the scene builder gets its argument through a checked type (a literal
in overlayScene or a declaration of the fixture type). Each check is red when
a dead field is put back into a declared fixture, an override literal or an
entry, when a literal goes straight into the builder, when a fixture loses its
annotation, and when groundRadius returns to the entry type.

isometric-contract now asserts that nothing renders into the overlay surface
and that the removed renderers and groundRadius stay gone. No mutant is
anchored on the removed code; mutation-gate --check is unchanged (3 warnings).
The 19 2.5D golden scenes pass in capture on the accepted baselines.

Issue: #732
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 23:26:46 +00:00
Claudeandclaude[bot] 40607aa37f fix(scripts): isMainModule compares real paths of argv and module (#733)
process.argv[1] keeps the path as typed, so a script started through a
symlink (or from a symlinked directory) still carries the link path there,
while Node builds import.meta.url of the main module from the real path.
The two never matched, and every CLI guarded by isMainModule silently did
nothing and exited 0. Both sides are now resolved with realpathSync before
the pathToFileURL comparison; a path that does not exist is compared as is,
without throwing, exactly as before.

The unit test writes a CLI and a module it imports into a temporary
directory, launches the CLI directly, through a directory link (a junction
on Windows, no admin rights needed) and through a file symlink (skipped on
EPERM), and checks that only the launched script runs its main. It is red
on the previous implementation.

Issue: #733
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 23:19:47 +00:00
claude[bot] 34423a6236 docs(reviews): индекс после сдвига каталога (#724)
Issue: #724
User-Visible: no
2026-09-30 22:39:24 +00:00
claude[bot] 295d61f377 docs: review document for #724
Issue: #724
User-Visible: no
2026-09-30 22:39:24 +00:00
Claudeandclaude[bot] 5f8e8ca7e8 refactor(iso): drop the 2.5D overlay data nothing reads (#724)
After #714 the 2.5D overlay scene still carried what decides nothing:

- src/iso-overlays.ts: IsoOverlayPlacement loses tether and grounding (always
  invisible) and raisedScene (always equal to visualScene); IsoOverlayOwner
  loses area; IsoOverlayPlacementInput loses hovered, focused, selected and
  filtersSupported, which the resolver ignored. IsoWallSilhouette and
  tetherGeometry go with them.
- src/iso-scene-render.ts: the structural scene no longer projects wall
  silhouettes (isoWallSilhouettesOf and IsoSceneCacheEntry.wallSilhouettes)
  that served only as a cache key. The placement and render-scene caches are
  keyed by the wall geometry the scene is drawn with (IsoOverlaySceneInput.
  structure = scene.geometry): the structural LRU hands out the same object
  across zoom, stage resize and HA state, and a new one after any wall, room
  or opening edit. The resolveCollisions flag and its fit/live cache slots
  are gone: since #713 both held equal placements, and 2.5D renders only in
  View, where the fit probe and the live frame ask with the same devices, so
  they now read one snapshot.
- src/houseplan-card.ts: the fit call passes no flag; the overlay scene gets
  structural.geometry. data-hp-iso-nudged stays the constant "false" read by
  the golden requireOneRise preflight, the live-touch smoke and the benchmark.

Tests: iso-overlays pins the placement fields; iso-scene-render builds the
structure with buildIsoWallGeometry, the #714 zoom/resize and #711 state tests
stay, fit and live are asserted to share one snapshot, and two #724 AC2 tests
run the production path (createIsoStructuralSource -> resolveIsoScene ->
buildIsoOverlayRenderScene): a thicker wall with the same room rebuilds the
scene (red with a key without walls, e.g. keyed by the room rows), and a room
edit that moves the owner gives the new owner (red with a constant key). The
silhouette-construction test goes with the construction.

Mutants: #473 W2 (iso-placement-cache-survives-silhouette-change, id kept for
history) now keys the placement cache by a constant instead of input.structure
and its guard also runs the #724 AC2 tests; W6 patches the new structure line;
the W5 description no longer speaks of a nudge. The isometric-contract regex
checks the new key instead of the silhouette construction. docs/ISOMETRIC.md
names the key.

Live 2.5D output is unchanged: the 21 isometric golden scenes pass on the
accepted baselines.

Issue: #724
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 22:39:24 +00:00
claude[bot] 108427dc77 docs(reviews): индекс после сдвига каталога (#723)
Issue: #723
User-Visible: no
2026-09-30 22:26:44 +00:00
claude[bot] 3e7f7d3936 docs: review document for #723
Issue: #723
User-Visible: no
2026-09-30 22:26:44 +00:00
Claudeandclaude[bot] 0d85807157 fix(process): publish steps tell a GitHub push refusal from a moved branch (#723)
Two steps publish a commit and treated every failed push as a moved branch:
the release review job (release-review.yml) retried three times with "dev
went ahead", and the review document step (_process.yml) rebased and pushed
again. A refusal by GitHub itself - a token without the workflow right, a
branch rule, a hook - cannot be cured by a retry or a rebase, and the step
never said what GitHub answered.

Both pushes now keep stderr and hand it to the #705 classifier through the
same CLI the rebase guard uses (merge-candidate.mjs --push-refusal). Only a
stale lease (rejected / fetch first / stale info) keeps the old retry or
rebase. Any other outcome stops the step at once, without retries: the log
gets the git answer and the step summary gets the reason and the git answer,
both passed through redactSecrets (token, credential URL, Authorization).
The review document step takes the classifier from dev, as the rebase guard
does: a task branch behind dev may not carry it.

The summary text is written by the new --summary option (refusalSummary),
not by a multi-line string in run:, and both commit messages are now built
line by line into a file instead of a heredoc (PROCESS.md §10.4 item 4).
release-review.yml is dispatch-only and is not mirrored to main. PROCESS.md
names the rule next to the rebase guard; the #638 trailer witness in
test/release-review.test.mjs follows the line-by-line message.

test/publish-push-refusal.test.mjs runs both steps as they are with real
bash and real git in temporary repositories; only the push transport is
replaced: a moved branch is a real neighbour push, a GitHub refusal is a
recorded stderr carrying a token, a credential URL and an Authorization
header. On the old steps 9 of its 11 tests fail.

Issue: #723
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 22:26:44 +00:00
claude[bot] 5cd9d0419a docs: review document for #707
Issue: #707
User-Visible: no
2026-09-30 22:21:03 +00:00
Claudeandclaude[bot] e00f62dca0 test(summary): #509 AC3 counts masonry passes instead of milliseconds (#721)
The large-house AC3 witness asserted an absolute 2.5 s budget for one
floor's clean-floor total. On a loaded 2-CPU machine the healthy path
took 2.7-4.3 s and the test went red while the code was fine.

The property #509 AC3 protects is structural: the summary panel builds
the space's wall masonry once and hands it to innerContourForRoom
(shared.roomGeom / shared.multiWallNodes); without it the masonry is
rebuilt for every room. Every masonry build walks the contours of all
rooms, so the test now counts reads of room.poly and compares the
floor total against one explicit spaceWallGeometry pass of the same
floor in the same run. Healthy code costs ~1.3 passes; the registered
mutant summary-area-recomputes-walls-per-room costs 21.3 and is red,
and so are the half-regressions that drop only one of the two shared
arguments (4.6 and 18.0 passes).

The count is deterministic, so machine load no longer matters.

Issue: #721
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 22:16:19 +00:00
Claudeandclaude[bot] bbcc88caf1 test(i18n): the second-card locale check counts locale requests only (#722)
secondCardReusesPageLocale compared the count of all page requests before
and after the second card mounted. The only extra request is that card's own
plan image: under page.route the browser HTTP cache is off, so it is fetched
again, and whether it lands before the read is a race. The German locale file
itself is loaded exactly once per page. The check now counts requests for the
locale chunk only and still fails if the second card fetches it again.

Issue: #722
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 21:08:23 +00:00
claude[bot] 66ac7d23ec docs: review document for #707
Issue: #707
User-Visible: no
2026-09-30 21:07:49 +00:00
claude[bot] 2d735c56f9 docs(reviews): индекс после сдвига каталога (#716)
Issue: #716
User-Visible: no
2026-09-30 21:01:10 +00:00
claude[bot] 48b001c646 docs: review document for #716
Issue: #716
User-Visible: no
2026-09-30 21:01:10 +00:00
claude[bot] dccbb80890 docs: review document for #716
Issue: #716
User-Visible: no
2026-09-30 21:01:10 +00:00
Claudeandclaude[bot] d11ad9c1c2 ci: register ship-review and beta-derived as thin callers in main (#716)
`workflow_dispatch` runs the file from the chosen ref, but GitHub lists a
workflow and accepts a dispatch (button, `gh workflow run`, API) only when
its file exists on the default branch. `ship-review.yml` (#696) and
`beta-derived.yml` (#697) lived only in `dev`, so neither could be started
at all, and the comment "the file runs from `--ref dev`, no mirror in
`main` needed" was wrong. Both beta steps are needed before the next
promotion would bring them to `main`.

They now follow the #623 layout instead of a full copy in `main`: a thin
caller (trigger, dispatch inputs, run-name, permission ceiling, concurrency)
calls `_ship-review.yml` / `_beta-derived.yml` at `@dev` with
`secrets: inherit`. A full copy would either need a mirror on every edit or
drift silently, and a dispatch from `main` (the button's default) would run
the stale copy; the thin caller runs the dev body from any ref. The caller
ceiling is the union of the body jobs' permissions (#556): ship-review
`contents: read` + `issues: read`, beta-derived `contents: read` +
`actions: read`; writes to `dev` stay with HP_PROCESS_TOKEN as before.

`workflow_sync` in validate.yml now compares eight files, and
test/default-branch-workflows.test.mjs lists the two dispatch-only files
explicitly with the reason checked (only `workflow_dispatch`). Workflow
tests and the #697 provenance mutant read the bodies. PROCESS.md §10.4,
§8 and §11.7 say how these are run and that a new thin file is mirrored
into `main` before it is merged into `dev`.

Issue: #716
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 21:01:10 +00:00
claude[bot] 7188db8187 docs: review document for #694
Issue: #694
User-Visible: no
2026-09-30 20:59:45 +00:00
claude[bot] a5a73d1511 docs(reviews): индекс после сдвига каталога (#714)
Issue: #714
User-Visible: no
2026-09-30 20:36:03 +00:00
claude[bot] a312897527 docs(reviews): индекс после сдвига каталога (#714)
Issue: #714
User-Visible: no
2026-09-30 20:36:03 +00:00
claude[bot] 7ac24a03a0 docs: review document for #714
Issue: #714
User-Visible: no
2026-09-30 20:36:03 +00:00
Claudeandclaude[bot] 7574518575 refactor(iso): remove the dead #651 overlay placement search (#714)
Since #713 every raised device tile and lock badge is its floor anchor lifted
by one shared wall-top rise and room names stay on the floor, so the live
scene no longer called the #651 search. What was left of it only cost code,
build time and review attention:

- src/iso-overlays.ts: resolveIsoOverlayRigidGroups, resolveIsoOverlayCollisions
  with their boundary-candidate machinery, the nudge search in
  resolveIsoOverlayPlacement (the vector to the room safe point, the near-wall
  test, the zoom hint), the safe point itself, the nudge/nearWall/cleared/capped
  and status/reason fields, and ISO_OVERLAY_MAX_NUDGE_CSS_PX /
  ISO_OVERLAY_SAFETY_GAP_CSS_PX.
- src/iso-scene-render.ts: the zoom reuse fast path and the CSS-pixel scale it
  compared; a placement now depends only on anchor, owner, footprint and rise,
  so zoom and stage resize reuse it by signature. residualPairs is gone and the
  memo key is called layoutSignature.
- src/houseplan-card.ts: the overlay scene no longer receives the view, the
  reference view or the stage rect it only fed to that scale;
  data-hp-iso-nudged stays as the constant "false" that the golden
  requireOneRise preflight, the live-touch smoke and the Stage 4 benchmark read.

The #585/#651 unit tests and the seven mutants that guarded only the removed
code are deleted; kept tests drop their nudge assertions, and a stage resize is
now pinned as a non-layout event. docs/ISOMETRIC.md keeps #651 as history only.
Live 2.5D output is unchanged: the 21 isometric golden scenes pass on the
accepted baselines.

Issue: #714
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 20:36:03 +00:00
claude[bot] 12bdc0be68 docs(reviews): индекс после сдвига каталога (#704)
Issue: #704
User-Visible: no
2026-09-30 20:30:34 +00:00
claude[bot] 8b7628285a docs: review document for #704
Issue: #704
User-Visible: no
2026-09-30 20:30:34 +00:00
Claudeandclaude[bot] 37b1cbf74b fix(release): let the stable-line review run when release.yml queues it (#704)
release.yml dispatches release-review.yml with GITHUB_TOKEN, so the run is
started by github-actions[bot], and claude-code-action refused it: "Workflow
initiated by non-human actor: github-actions (type: Bot). Add bot to
allowed_bots list" (v1.78.0: release run 36468444979, review 36468505112).
The release went out and nobody learned that the review never ran.

The review step now allows exactly github-actions[bot]. At the pinned SHA
(9cdae7f0) the action compares allowed_bots entries and the actor
case-insensitively with the `[bot]` suffix stripped, so this entry matches
GITHUB_ACTOR; any other bot is still refused, and a human dispatch never
consults the list.

independent-review no longer stops at the dispatch: it looks the run up by
workflow, branch dev, event, time and run-name "Release review <tag>" for
up to three minutes and writes the link and status to the step summary.
A run that did not appear or did not start is a warning; the release is
not blocked.

Neither file is executed from main, so no mirror is needed (§10.4).

Issue: #704
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 20:30:34 +00:00
claude[bot] 40b226405d docs: review document for #718
Issue: #718
User-Visible: no
2026-09-30 20:30:04 +00:00
claude[bot] 2f1be1a86a docs: review document for #694
Issue: #694
User-Visible: no
2026-09-30 20:28:41 +00:00
claude[bot] 24cb6d113a docs(reviews): индекс после сдвига каталога (#705)
Issue: #705
User-Visible: no
2026-09-30 20:25:08 +00:00
claude[bot] 6e2c75afc9 docs: review document for #705
Issue: #705
User-Visible: no
2026-09-30 20:25:08 +00:00
Claudeandclaude[bot] e5c217111c fix(process): a GitHub push refusal is not a stale lease (#705)
merge-candidate treated any push stderr containing "rejected" as a stale
lease. A `! [remote rejected]` from GitHub itself - in #700 the rebased
candidate changed .github/workflows/ and the conveyor token has no workflow
permission (runs 36484993494, 36487044060) - became "the branch moved after
the reviewed material (#312)", and the stderr was never printed, so the
author was sent to look for a commit that did not exist.

classifyPushRefusal now tells three outcomes apart: a stale lease
(`[rejected] (stale info)`, `fetch first`, a server-side lock race) keeps
the old behaviour; GitHub's workflow refusal (PAT, OAuth App, GitHub App,
bot and integration wordings) and any other `[remote rejected]` get their
own outcome, S6-in-progress and a comment naming the reason. The workflow
comment says what to do: the author rebases and pushes, or the owner grants
the permission. The git answer goes to the log and the comment with tokens
and credential URLs cut out; the merge-step failure comment is redacted too.

The rebase guard in _process.yml parses its push refusal with the same code
(`merge-candidate.mjs --push-refusal`): a stale lease is the old error, a
workflow refusal returns the task to S6 without review like a conflict, and
material/reuse/gate skip the rebase that never reached the branch.

Mutant push-refusal-kinds-glued restores the old regex; guard: #705 AC1.

Issue: #705
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 20:25:08 +00:00
Claudeandclaude[bot] 84ed38e3d5 test(perf): the Stage 4 dense contract requires no nudged overlay (#719)
The isometric-stage3-dense-v1 runner still demanded at least one bounded
#651 nudge. Since #713 every raised device tile and lock badge is lifted by
the one shared wall-top rise and carries data-hp-iso-nudged="false", so the
Full Performance profile failed its input contract before any timing.

The contract is inverted: a single nudged raised root now fails the sample,
matching the golden requireOneRise preflight. The performance README states
the current contract, and the #570 runner-contract unit pins the new failure
text.

Issue: #719
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-30 20:17:15 +00:00