Section 10.1 promised pre-push as the blocking gate that replaces pull requests.
The hook did not exist, so the document promised a check that was not there —
worse than saying nothing, because a promise like that gets relied on. Until now
process-gate ran only as the catch-up job in CI, which reports after the code is
already in dev.
The hook skips branch deletions and tags, and for a branch the remote has not
seen it measures from the merge-base with dev rather than from the root, or every
violation committed before the gate existed would make it impossible to pass. A
missing script does not block a push: old checkouts and worktrees have to stay
usable.
gh is optional on purpose. Reading issue status needs the network, and a hook
that cannot work on a train is a hook people switch off; offline it runs what it
can and CI does the strict pass.
The executable bit is the quiet part. Git skips a hook without +x and says
nothing — the gate reports success by being absent. Measured on a real push:
mode 644 produces zero lines from the gate and the push goes through, 755 stops
it. The API cannot set the bit, so install-hooks restores it on every install.
Issue: #121
User-Visible: no
Check 2 compared the Issue trailers against whatever branch the working tree
happened to be on, over whatever range it was given. Those two are not the same
set. After a rebase the CI range widens — `before` points at a discarded commit,
the merge-base slides back, and commits that belong to dev arrive carrying other
issue numbers. Every one of them then looks like a violation.
Running the gate over real history from issue/89 with a dev range produced 26
false refusals out of 26 commits, which would have reddened Validate on the next
force-push of any task branch.
The rule now reads origin/dev..HEAD for its own verdict and leaves the event
range to the other checks. A commit that genuinely carries the wrong trailer for
its branch is still caught; the integration test covers both directions.
Issue: #105
User-Visible: no
PROCESS.md 10.2 describes scripts/process-gate.mjs; the script never existed.
Commits go straight to dev without PRs and GitHub blocks nothing on its side, so
until now the only thing standing between the process and rule #1 was the good
faith of whoever was committing. Hooks catch a violation on the author's machine
but --no-verify walks past them; this job is the catch-up pass that cannot be
skipped locally.
Checks 1-7 offline, 8 through gh, plus the escalation of check 3: a class A
commit with neither a spec file nor the `small` label is a failure, not a
warning. Check 8 is fail closed — an unreachable or closed issue is a refusal,
never a silent pass.
Two things surfaced while wiring it up and are recorded in the script header.
S8-merged had to join the allowed statuses: the pipeline merges into dev before
it moves the label, so Validate reads the issue already advanced and a strict set
would redden every accepted task. And the status question now applies only to
class A/B commits — asking it of a review document would fail every time, since
that document lands while the issue sits in S4-spec-review or S7-code-review.
Issue: #105
User-Visible: no
R5-1: the backend signs each path independently and answers successfully with
whatever it managed, skipping (and logging) the rest. The card read any
successful call as 'the batch is done', cleared the backoff for every path in
it, then wrote only the urls that came back — so a path the backend kept
skipping was asked for again on every render, the exact amplification the
backoff was added to stop. A path now counts as signed only when the answer
carries a url for it; the others back off individually, keys that were not
requested are ignored, and onUpdate fires only when a new signature landed.
R5-2: docs/STATUS.md still described main as holding releases up to v1.40.1 and
quoted test counts several releases old, while the version line beside them was
kept current — a handoff reader got a wrong branch model and less coverage than
exists. Branch roles are now accurate, and the counts are gone rather than
corrected: scripts/inventory.mjs (npm run inventory) prints them from the tree,
so there is nothing left to drift.
Tests: three unit cases for empty/partial/foreign-key answers, verified to fail
against a v1.45.3 checkout; a backend test pinning the partial-success contract
by making async_sign_path raise for one path of two.
Docs: CHANGELOG.md + CHANGELOG.ru.md + TESTING.md + STATUS.md.
- 12-test buildDevices suite on a fake hass (curation, markers, groups, dedup,
icon rules, lock override, device_class fallback, primary/LQI/temp)
- t() substitutes all placeholder occurrences (pure subst() + regression test)
- _saveConfigNow resyncs config on rev conflict before rethrowing
- pointercancel clears the long-press timer (phantom info card on touch)
- repairs.py: plan-file check re-runs after every config save
- test build compiles devices.ts/types.ts; fix-test-build.mjs adds .js to ESM imports