Memoize quick gate on tree

Task 5520 · Done · Coder · created · updated

Epic: Merge lane — CP-scheduled verification queue, plan-gated. Planner Task 5517; the full PRD is BA Task 5515 and the full architecture is Architect Task 5516. Read both with `task_read` for any clamped detail. Operator ask (verbatim, excerpt): "Currently when we have multiple runners, each of them runs quick gates many times, then full gate in runner [...] That creates load on the nodes [...]" This child is INDEPENDENT of the plan flag. It is a pure saving for every project. ## Requirement (work item 3, verbatim) "Quick-gate memo: `tools/run_gate.py` skips the quick tier when the tree hash + gate version already passed; Stop hook and pre-commit benefit. Independent of the feature flag (pure saving)." ## Approach (architecture L13, settled) - **Memo key.** Build it from a temporary-index `write-tree`: 1. Copy `.git/index` into a temporary `GIT_INDEX_FILE`. 2. Run `git add -A` against it. 3. Run `git write-tree`. - The result covers tracked, modified and untracked non-ignored files exactly. The real index and the tree stay untouched. - **Gate version.** `sha256(gate_quick_cmd)`, plus `sha256` of the content of any argv token of that command that resolves to an existing file OUTSIDE the worktree (packaged scripts). In-tree scripts are already covered by the tree hash. - **Store.** `$(git rev-parse --git-dir)/jaah-gate-memo/quick.json` holding `{key, tree, version, passed_at}`. It is per-worktree and outside the tree. - **Scope.** Applies to `--tier quick` ONLY. - On a hit, print ONE line giving the tree hash and the earlier green time, then exit 0. - Write the memo only on exit 0. - Fail open: any read or parse error means run the gate. - Rejected: keying on `HEAD^{tree}` (decision D5). The Stop hook runs on uncommitted edits. - Current code: `run_gate` at `tools/run_gate.py:82-126`, `gate_command` at :74-79. The Stop hook is `infra/host/fleet-harness/settings.json:174` and pre-commit is `.claude/hooks/pre-commit.sh:61-62`. Both call `run_gate.py`, so they benefit without edits. ## Acceptance (AC3.1–AC3.5, verbatim) - AC3.1 `tools/run_gate.py --tier quick` skips running the command when the memo key has already **passed** in this worktree, and prints one line saying it was skipped, giving the key's tree hash and the time of the earlier green run. It then exits 0. - AC3.2 The memo key is the hash of the **exact working-tree content the gate would see**: tracked files with uncommitted changes plus untracked non-ignored files, not just `HEAD^{tree}`. It is combined with the **gate version**, which is the resolved `gate_quick_cmd` string together with the content hash of the gate entry script(s) the project row names. Any byte change to any of these is a miss. - AC3.3 Only green results are memoized. A red or refused run (exit 78) is never memoized and always reruns. The full tier is **never** memoized by this item. - AC3.4 The memo is stored per worktree, outside the tracked tree (for example under the git dir). It never makes the tree dirty, and a slot reset discards it. - AC3.5 With no memo, or a memo that cannot be read or is corrupt, the gate runs, failing open to running. The memo can never turn a would-be failure into a pass. Credentials & reach: none. Every oracle is worktree-local unit tests (verified; no external reach). Out of scope: - Epic (verbatim): "GitHub merge queue / merge_group; any CP-side forge calls; changing the manual develop → master promotion; cgroup tuning of GHA runners; squash merges." - BA-derived (verbatim): "No full-tier memo: only quick is memoized." - Do NOT edit the Stop hook settings, the pre-commit hook, `tools/project_config.py` or `gate_directive`. Do NOT change the full tier's behaviour in any way. Done means: - AC3.1–AC3.5 Done-means oracle — in `tools/tests/test_run_gate.py`, use a fake gate command that counts invocations: - Run the Stop-hook path twice on an unchanged tree: the counter is 1, and the second run prints the skip line. - Edit a tracked file, then an untracked file, then the gate command, in turn: each edit raises the counter by 1. - Run a failing fake gate twice: the counter rises by 2. - A corrupt `quick.json` makes the gate run. - `git status --porcelain` is unchanged by the memo. - `--tier full` run twice: the counter is 2. - On a real slot, two consecutive Stop-hook firings with no edit print the skip line on the second firing. Required checks: gate, review-lane

Outcome

The quick gate now skips a re-run when this worktree already passed on the same files with the same gate. Key: worktree tree, index tree, HEAD, base branch, gate version, runner identity. The full tier is never memoized. `./dc check full` passed, PR #4811 merged, Jaah Deploy succeeded.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Runner ended (done)