diff --git a/.kiro/specs/pre-push-safety-net/tasks.md b/.kiro/specs/pre-push-safety-net/tasks.md index ba91f0c..c4444e7 100644 --- a/.kiro/specs/pre-push-safety-net/tasks.md +++ b/.kiro/specs/pre-push-safety-net/tasks.md @@ -571,7 +571,7 @@ Preserve all completed task evidence below. - _Diagnosed by probe, not by timestamp._ On one PMPro graph from the production request builder, context costs ~20% more engine time per request -- not the wall. The wall is OUTPUT: each request itemised every method its scope reached, up to 1,845 rows; 200 requests into the bundled Braintree SDK returned 52,831 rows, 9.4 MB, against 211 without context. `affected_context` only ever attaches a location in a CHANGED file and discards every other row after checking it, so nearly all of it was shipped to be thrown away. Fixed in `3c195c9`: the engine itemises the changed files and summarises the rest into one row carrying what the check reads. On PMPro the same 2,440 relationships and zero gaps are decided from both forms, 52,831 rows down to 3,241 with the most-referenced file as the change. Probe: `benchmarks/push/context_cost_probe.py`. The replay is re-running on frozen `3c195c9`. - _Corrected the same day: that was real but it was not the wall._ The re-run on `3c195c9` showed the same 13 → 31 → 51-minute portions. Sampled in flight, no engine was running for minutes at a time while the `pre-push` Python process sat at 83% CPU. The cause was the reuse cache: every publication listed and stat'ed the WHOLE cache to total its size, and a push publishes one answer per question, so the pass was quadratic. Measured on the live cache, one scan took 0.68 s at 12,238 entries, ~70 minutes of scanning by then and ~2.3 hours to finish the pass. The growing portion times were the quadratic, not the engine. Fixed in `7cf256b`: the scan is remembered, keyed by the cache root's modification stamp, so another writer still forces a rescan and the size limit stays exact. The probe did not catch it because it called the engine directly, without the cache -- the instrument lesson again: a probe that skips a layer cannot clear that layer. The output fix stays: it is still 16× less data per push. The replay is re-running on frozen `7cf256b`. - _The loop, named and broken, 2026-09-23._ Walls kept appearing one per two-hour run -- the cache rescan, then the harness losing a killed case's output (`982cb14`), then a linear-list relationship check that also overran the deadline by >60 s (`e7e2941`) -- because every one was Python bookkeeping between engine calls, every one grew with the repository, and the only full-scale instrument was a Joern replay. Two instruments replace that: an in-process sampling profiler (`OUSAST_SAMPLE_PROFILE`, on for every replay case) and `benchmarks/push/python_overhead.py`, which runs the real `replay()` on the real PMPro pins with an instant stub engine answering real recorded rows. Its first pass found the remaining two walls at once: discovery re-parsed the ruleset per region (89,127 TOML parses, 339 s → 7 s, output digest identical) and change attribution decoded every line anchor per row (78% of a stubbed push; now indexed). The whole stubbed PMPro push now costs 58.5 s of Python; per-entry cache fsync is gone too (`b4b9308`, `6b85a60`). - - _And the budget itself was never feasible._ The stub run shows the push's head scan can select 2,500 questions (500 regions × 5 families) when change context leaves them incomplete, because tier-zero pruning never prunes an incomplete question; at ~9 s per arbitrated PHP question that is ~6 hours, so no 7,200 s case could finish whatever else was fixed. The census asked 406 at the same setting because it prunes. Case 1 is re-running alone at a 14,400 s deadline to MEASURE the real count and stage costs; cases 2 and 3 get a budget sized from that, stated as a limit of the run. + - _The budget, stated correctly._ The push's head scan can select 2,500 questions (500 regions × 5 families) where change context leaves them incomplete, because tier-zero pruning never prunes an incomplete question -- ~6 hours of engine at ~9 s per PHP question. That does NOT make a 7,200 s case infeasible: contributor-scan 7.7–7.9 portion the arbitration phase in ranked order and hold time back for arbitration, so a case answers what fits and reports the rest as `query_budget_reserved`. What defeated every earlier case was the phases BEFORE portioning -- discovery, the cache, change attribution -- each spending the deadline before arbitration began. With those fixed (preparation 290 s → 19.5 s in the real run), the three cases re-run at 7,200 s on frozen `6b85a60`; completion is expected to be partial and stated, and the declared target's file ranks 19th of 86. - _Boundary: Evaluation harness, full-run integration_ - _Depends: 13.2, contributor-scan 7.3, contributor-scan 7.8_ - _Requirements: 6.2, 6.3, 8.1, 8.2_