CommitFest 7308 Review: VACUUM Truncation Threshold Adjustment

report · generated from canonical Markdown

Type
report
Source
postgresql-master
Slug
commitfest-7308-review
Tags
commitfest-7308needs-reviewpg-review-harnessreview-report-v2truncationvacuum
Updated
2026-09-18T06:53:32.552241+00:00
Verdict / Summaryneeds-review
Front matter
schema: review-report-v2
target: CommitFest 7308: VACUUM truncation threshold adjustment (should_attempt_truncation)
base: 926627bf9021137af66ac3bf21d1595af64ec66b
head: 926627bf9021137af66ac3bf21d1595af64ec66b
verdict: needs-review
blocker_counts:
  verified: 0
  unverified: 0
scope_identity: diff-0ce64c3fe313dc85ee4aadd4397a2196d436ac8fc1bb796f93e3c795bdcbff21
source: postgresql-master
slug: commitfest-7308-review
commitfest: 7308
tags: [commitfest-7308, pg-review-harness, review-report-v2, needs-review, vacuum, truncation]

CommitFest 7308 Review: VACUUM Truncation Threshold Adjustment

Summary

Target: CommitFest 7308: VACUUM truncation threshold adjustment (should_attempt_truncation) Base/Head (full HEAD): 926627bf9021137af66ac3bf21d1595af64ec66b Scope identity: diff-0ce64c3fe313dc85ee4aadd4397a2196d436ac8fc1bb796f93e3c795bdcbff21 Verdict: needs-review Blocker counts: verified=0, unverified=0 Changed file: src/backend/access/heap/vacuumlazy.c (staged, uncommitted; 3 insertions, 5 deletions) Model: glm-5.2:cloud (explicit user override)

Patch under review

The staged diff lowers REL_TRUNCATE_MINIMUM from 1000 to 2 and REL_TRUNCATE_FRACTION from 16 to 32, and rewrites the should_attempt_truncation predicate from an OR of two independent thresholds (possibly_freeable >= MINIMUM || possibly_freeable >= rel_pages/FRACTION, guarded by possibly_freeable > 0) to a single additive threshold (possibly_freeable >= rel_pages/REL_TRUNCATE_FRACTION + REL_TRUNCATE_MINIMUM). The intent (per the pgsql-hackers thread) is to reduce unnecessary AccessExclusiveLock truncation attempts on relations where the freeable tail is small relative to the relation size.

diff
-#define REL_TRUNCATE_MINIMUM	1000
-#define REL_TRUNCATE_FRACTION	16
+#define REL_TRUNCATE_MINIMUM	2
+#define REL_TRUNCATE_FRACTION	32
...
-	if (possibly_freeable > 0 &&
-		(possibly_freeable >= REL_TRUNCATE_MINIMUM ||
-		 possibly_freeable >= vacrel->rel_pages / REL_TRUNCATE_FRACTION))
+	if (possibly_freeable >= vacrel->rel_pages / REL_TRUNCATE_FRACTION + REL_TRUNCATE_MINIMUM)

Findings

F1

— BLOCKER (correctness) — refuted

Location: src/backend/access/heap/vacuumlazy.c:3152 Mechanism: unsigned-wrap: removal of possibly_freeable > 0 guard on BlockNumber (uint32) allows wrapped subtraction to spuriously trigger truncation when nonempty_pages > rel_pages Failure: spurious should_attempt_truncation true on wrapped possibly_freeable, leading to unnecessary AccessExclusiveLock acquire and rescan in lazy_truncate_heap Verification status: refuted Verification method: static-analysis-plus-compiled-reproducer Verification evidence: Compiled C reproducer (gcc) confirms that on uint32, x > 0 is equivalent to x != 0; a wrapped value (e.g. 4294967291) is non-zero so possibly_freeable > 0 evaluates TRUE for the wrap in BOTH old and new code. The old guard never rejected unsigned wraparound. The zero case (nonempty_pages == rel_pages) is correctly rejected by both old (0 > 0 = false) and new (0 >= 2 = false) code. The downstream lazy_truncate_heap AE-lock recheck (vacuumlazy.c:3228-3239) handles the wrap case identically in both versions. No regression introduced by this patch.

Repair: not-applicable-refuted (all repair sub-objects carry status: not-applicable-refuted).

F2

— MAJOR (correctness) — verified

Location: src/backend/access/heap/hio.c:335 Mechanism: cross-file invariant breakage: REL_TRUNCATE_MINIMUM lowered from 1000 to 2 is no longer 'way larger' than MAX_BUFFERS_TO_EXTEND_BY (64) as documented in hio.c:335-337 comment; the comment is now false and misleading Failure: stale safety comment misleads future maintainers; increased AccessExclusiveLock contention on small busy tables as autovacuum may consider truncating just-extended pages (data loss prevented by lazy_truncate_heap AE-lock recheck at vacuumlazy.c:3228-3239) Verification status: verified Verification method: source-inspection Verification evidence: hio.c:239 defines MAX_BUFFERS_TO_EXTEND_BY=64; hio.c:335-337 comment states 'REL_TRUNCATE_MINIMUM is way larger'; staged diff sets REL_TRUNCATE_MINIMUM=2 < 64. The invariant is inverted. lazy_truncate_heap re-reads RelationGetNumberOfBlocks under AccessExclusiveLock (vacuumlazy.c:3228) and bails if new_rel_pages != orig_rel_pages (3229), preventing data loss. Repair patch compiles clean with gcc 15.2.0 full project CFLAGS.

Repair instruction: Update the hio.c comment to state REL_TRUNCATE_MINIMUM is no longer guaranteed larger than MAX_BUFFERS_TO_EXTEND_BY and document the AE-lock recheck safety mechanism. Update the vacuumlazy.c comment to describe the new additive threshold predicate and its integer-division step behavior. Files: src/backend/access/heap/hio.c, src/backend/access/heap/vacuumlazy.c Postcondition: The hio.c comment accurately describes the relationship between MAX_BUFFERS_TO_EXTEND_BY and REL_TRUNCATE_MINIMUM and documents the safety mechanism relied upon. The vacuumlazy.c comment accurately describes the new predicate.

Code example (exact-postimage, `c):

c
	 * Note: REL_TRUNCATE_MINIMUM is no longer guaranteed to be larger than
	 * MAX_BUFFERS_TO_EXTEND_BY.  A concurrent autovacuum may therefore
	 * consider truncating pages that were just bulk-extended; that is safe
	 * because lazy_truncate_heap re-reads the relation size under
	 * AccessExclusiveLock and rescans the tail before actually truncating.
	 */

Patch: unified-diff, applies_to_head=926627bf9021137af66ac3bf21d1595af64ec66b, sha256=ae5fc6d32d43a4f80bf9194a5fc0d2a786911cf5ac79c8a940408b4f54c5dff9

Repair validation: status=passed, methods=apply-check, diff-check, compile, static-check Commands: git worktree add --detach <scratch>/verify-wt 926627bf9021137af66ac3bf21d1595af64ec66b; git apply --check repair_combined.diff; git apply repair_combined.diff; git diff --check; gcc -Wall -Wmissing-prototypes ... -I<worktree>/src/include -D_GNU_SOURCE -c -o hio.o src/backend/access/heap/hio.c; gcc -Wall ... -c -o vacuumlazy.o src/backend/access/heap/vacuumlazy.c; grep -c 'way larger' src/backend/access/heap/hio.c Evidence: Both hio.o and vacuumlazy.o compiled cleanly with gcc 15.2.0 and the full PostgreSQL project warning set (-Wall -Werror=vla -Wstrict-prototypes etc). No warnings emitted. Static check confirms stale 'way larger' phrase removed. Patch applies cleanly to frozen HEAD with no whitespace errors.

Test example: vehicle=inspection, files=src/backend/access/heap/hio.c Oracle: grep -c 'way larger' src/backend/access/heap/hio.c returns 0 Baseline: 1 (stale comment present before repair) → Patched: 0 (stale comment removed after repair)

F3

— MINOR (maintainability) — verified

Location: src/backend/access/heap/vacuumlazy.c:165 Mechanism: stale comment: comment describes old OR 'whichever is less' predicate but code now uses additive sum (rel_pages / REL_TRUNCATE_FRACTION + REL_TRUNCATE_MINIMUM) Failure: comment actively misdescribes the should_attempt_truncation predicate logic Verification status: verified Verification method: source-inspection Verification evidence: vacuumlazy.c:165-167 comment says 'REL_TRUNCATE_MINIMUM or (relsize / REL_TRUNCATE_FRACTION) (whichever is less)' describing the old OR predicate; vacuumlazy.c:3153 code is 'possibly_freeable >= vacrel->rel_pages / REL_TRUNCATE_FRACTION + REL_TRUNCATE_MINIMUM' which is an additive sum. Comment is stale.

Repair instruction: Replace the three comment lines describing the OR/whichever-is-less predicate with a description of the new sum predicate and its integer-division behavior. Also update the hio.c comment to reflect the new REL_TRUNCATE_MINIMUM relationship. Files: src/backend/access/heap/hio.c, src/backend/access/heap/vacuumlazy.c Postcondition: The comment accurately describes the should_attempt_truncation predicate.

Code example (exact-postimage, `c):

c
 * To consider truncating the relation, we require at least
 * (relsize / REL_TRUNCATE_FRACTION) + REL_TRUNCATE_MINIMUM
 * potentially-freeable pages.  Because relsize / REL_TRUNCATE_FRACTION uses
 * integer division, the threshold collapses to a constant REL_TRUNCATE_MINIMUM
 * for relations smaller than REL_TRUNCATE_FRACTION pages; relations of
 * REL_TRUNCATE_FRACTION pages or more add one page per
 * REL_TRUNCATE_FRACTION pages of size.
 */

Patch: unified-diff, applies_to_head=926627bf9021137af66ac3bf21d1595af64ec66b, sha256=ae5fc6d32d43a4f80bf9194a5fc0d2a786911cf5ac79c8a940408b4f54c5dff9

Repair validation: status=passed, methods=apply-check, diff-check, compile, static-check Commands: git apply --check repair_combined.diff; git apply repair_combined.diff; gcc -Wall ... -c -o vacuumlazy.o src/backend/access/heap/vacuumlazy.c; grep -c 'whichever' src/backend/access/heap/vacuumlazy.c Evidence: vacuumlazy.o compiled cleanly with full project CFLAGS. Comment-only change. Static check confirms 'whichever' removed. No warnings.

Test example: vehicle=inspection, files=src/backend/access/heap/vacuumlazy.c Oracle: grep -c 'whichever' src/backend/access/heap/vacuumlazy.c returns 0 Baseline: 1 (stale comment present) → Patched: 0 (stale comment removed)

F4

— MINOR (maintainability) — verified

Location: src/backend/access/heap/vacuumlazy.c:3153 Mechanism: undocumented integer-division step function: rel_pages / REL_TRUNCATE_FRACTION (integer division of uint32 by 32) collapses to 0 for rel_pages < 32, making the threshold a constant 2 for all relations under 32 pages Failure: threshold is a step function (constant 2 for <32 pages, +1 per 32 pages above) that is not documented; the old 1000-page minimum masked this behavior Verification status: verified Verification method: compiled-reproducer Verification evidence: Compiled C reproducer enumerating rel_pages 0..40 confirms rel_pages/32 is 0 for rel_pages < 32 and steps up by 1 every 32 pages. Threshold table: rel_pages=0..31 -> threshold=2; rel_pages=32..63 -> threshold=3; rel_pages=64..95 -> threshold=4. The F3 comment repair now documents this explicitly.

Repair instruction: Document the integer-division step function in the vacuumlazy.c comment (same edit as F3). Also update the hio.c comment. Files: src/backend/access/heap/hio.c, src/backend/access/heap/vacuumlazy.c Postcondition: The comment documents that the threshold is constant REL_TRUNCATE_MINIMUM for relations smaller than REL_TRUNCATE_FRACTION pages.

Code example (exact-postimage, `c):

c
 * integer division, the threshold collapses to a constant REL_TRUNCATE_MINIMUM
 * for relations smaller than REL_TRUNCATE_FRACTION pages; relations of
 * REL_TRUNCATE_FRACTION pages or more add one page per
 * REL_TRUNCATE_FRACTION pages of size.
 */

Patch: unified-diff, applies_to_head=926627bf9021137af66ac3bf21d1595af64ec66b, sha256=ae5fc6d32d43a4f80bf9194a5fc0d2a786911cf5ac79c8a940408b4f54c5dff9

Repair validation: status=passed, methods=apply-check, diff-check, compile, static-check Commands: git apply --check repair_combined.diff; git apply repair_combined.diff; gcc -Wall ... -c -o vacuumlazy.o src/backend/access/heap/vacuumlazy.c; grep -c 'collapses to a constant REL_TRUNCATE_MINIMUM' src/backend/access/heap/vacuumlazy.c Evidence: Comment-only change documents the step function; compiles cleanly. Static check confirms documentation added.

Test example: vehicle=inspection, files=src/backend/access/heap/vacuumlazy.c Oracle: grep -c 'collapses to a constant REL_TRUNCATE_MINIMUM' returns >= 1 Baseline: 0 (not documented) → Patched: 1 (documented in comment)

F5

— MINOR (tests) — verified

Location: src/test/regress/sql/vacuum.sql:192 Mechanism: no regression test added for the threshold change; existing vacuum.sql tests vacuum_truncate reloption but not the should_attempt_truncation threshold boundary Failure: threshold logic is otherwise untested; the arithmetic that regresses silently has no dedicated test Verification status: verified Verification method: test-suite-inspection Verification evidence: src/test/regress/sql/vacuum.sql lines 192-210 test vacuum_truncate reloption (TRUNCATE FALSE) but no test asserts that a relation with N trailing empty pages is/isnt truncated based on the threshold. No test covers the new rel_pages/32+2 formula.

Repair instruction: Add a regress test that creates a temp table with autovacuum_enabled=false, inserts 5000 rows, deletes id > 1000, vacuums, and asserts the relation shrank. Add a second case: insert 200 rows, delete all, vacuum, and assert the relation still has pages (behavior change). Add test: vacuum_truncate_threshold to parallel_schedule. Files: src/test/regress/sql/vacuum_truncate_threshold.sql, src/test/regress/expected/vacuum_truncate_threshold.out, src/test/regress/parallel_schedule Postcondition: pg_regress vacuum_truncate_threshold passes with expected output matching.

Code example (exact-postimage, `sql):

sql
INSERT 0 5000

Patch: unified-diff, applies_to_head=926627bf9021137af66ac3bf21d1595af64ec66b, sha256=22af8e4139ea16ca4a30083c3399019d9e6c69b3fff9d55845c2e138766b9180

Repair validation: status=passed, methods=apply-check, diff-check, targeted-test Commands: git apply --check repair_f5_test.diff; git apply repair_f5_test.diff; psql -p 54335 -d postgres -f vacuum_truncate_threshold.sql Evidence: Test applies cleanly to frozen HEAD. Executed against patched PostgreSQL 18devel cluster: 5000-row case truncates (before=23 pages, after=5 pages, truncated=t). 200-row case confirms behavior change (relation retains pages). Output deterministic across two consecutive runs.

Test example: vehicle=regress, files=src/test/regress/sql/vacuum_truncate_threshold.sql, src/test/regress/expected/vacuum_truncate_threshold.out Oracle: truncated=t (23->5 pages); not_fully_truncated=t (200-row table retains pages after VACUUM due to threshold >= 2) Baseline: n/a (new test) → Patched: truncated=t, not_fully_truncated=t

F6

— MAJOR (tests) — verified

Location: src/test/regress/expected/vacuum.out:242 Mechanism: existing regression test breakage: vacuum.sql lines 199 and 209 assert pg_relation_size('vac_truncate_test') = 0 is true after VACUUM, but the new threshold (rel_pages/32 + 2 >= 2) prevents truncation of tiny (1-page) relations that the old threshold (min(1000, rel_pages/16) = 0 for 1 page) allowed Failure: make check-tests TESTS=vacuum fails with 2 assertion mismatches: expected t got f at vacuum.out:242 and vacuum.out:269 Verification status: verified Verification method: targeted-test Verification evidence: Ran make -C src/test/regress check-tests TESTS=vacuum against patched code: 1 of 1 tests failed. regression.diffs shows 2 hunks where 't' expected but 'f' produced for pg_relation_size('vac_truncate_test') = 0. After applying the expected-output repair, the vacuum test passes (ok 1 - vacuum). The behavior change is intentional per the thread but the existing test was not updated.

Repair instruction: Update the two pg_relation_size('vac_truncate_test') = 0 expected results from ' t' to ' f' to reflect that tiny relations no longer truncate to 0 pages under the new threshold. Files: src/test/regress/expected/vacuum.out Postcondition: make check-tests TESTS=vacuum passes with the patched code.

Code example (exact-postimage, `diff):

diff
f (1 row) DROP TABLE vac_truncate_test;

Patch: unified-diff, applies_to_head=926627bf9021137af66ac3bf21d1595af64ec66b, sha256=f9f6d0d6cb7aad47e013be16fd716ed2b31b749197adfbf982eaa43f81398e4a

Repair validation: status=passed, methods=apply-check, diff-check, targeted-test Commands: git apply --check repair_f6_vacuum_expected.diff; git apply repair_f6_vacuum_expected.diff; make -C src/test/regress check-tests TESTS=vacuum Evidence: Applied the expected-output patch on top of the staged diff; make check-tests TESTS=vacuum passes: 'ok 1 - vacuum 416 ms' / 'All 1 tests passed.' The 2 assertions now correctly expect f.

Test example: vehicle=regress, files=src/test/regress/expected/vacuum.out Oracle: pg_relation_size('vac_truncate_test') = 0 returns f (relation retains >= 1 page) Baseline: t (old code truncated tiny relations to 0) → Patched: f (new code does not truncate tiny relations to 0)

Verification Summary

FindingSeverityClassVerificationRepair Validation
F1BLOCKERcorrectnessrefutedn/a (refuted)
F2MAJORcorrectnessverifiedpassed
F3MINORmaintainabilityverifiedpassed
F4MINORmaintainabilityverifiedpassed
F5MINORtestsverifiedpassed
F6MAJORtestsverifiedpassed

Repair Validation Summary

All repair patches were validated in a disposable git worktree at frozen HEAD 926627bf9021137af66ac3bf21d1595af64ec66b (never touching the read-only review worktree).

Changed-Code Coverage

Status: measured Counter epoch: fresh Head: 926627bf9021137af66ac3bf21d1595af64ec66b Execution lane: host-disposable (passed)

Execution evidence

Disposable git worktree at frozen HEAD 926627bf902 with staged diff applied; backend compiled with gcc 15.2.0 -O0 -g --coverage -fprofile-arcs -ftest-coverage (vacuumlazy.c instrumented); PostgreSQL 18devel cluster started on port 54335; F5 threshold test executed (5000-row insert, delete 4000, VACUUM; 200-row insert, delete all, VACUUM); gcov -b -o src/backend/access/heap vacuumlazy.c produced coverage data showing should_attempt_truncation called 2 times, line 3153 both branches taken (50%/50%), line 3154 return true covered (1 hit), line 3156 return false covered (1 hit). Existing vacuum.sql regress test also run, revealing 2 assertion failures (F6).

Test commands

Metrics

MetricValue
changed_executable_lines1
covered_changed_lines1
compiler_uncovered_changed_lines0
uncovered_changed_lines0
structural_uncovered_changed_lines0
changed_lines_without_coverage_coordinates2
changed_branches2
covered_changed_branches2
integrity_errors[]

Touched functions

Excluded changed coordinates

Uncovered regions / branches

None. The single changed executable line (3153) and both of its branches (true→3154, false→3156) are fully covered by the F5 threshold test execution. The only uncovered line in should_attempt_truncation is line 3150 (return false on !do_rel_truncate || VacuumFailsafeActive), which is unchanged context, not a changed line.

Coverage finding (F6)

During coverage execution, running the existing vacuum.sql regression test against the patched code revealed 2 assertion failures (F6): pg_relation_size('vac_truncate_test') = 0 returns f instead of t at two sites. This is a behavior change — tiny relations (1 page) no longer truncate to 0 pages because the new threshold rel_pages/32 + 2 = 2 exceeds possibly_freeable = 1. The old threshold min(1000, rel_pages/16) = 0 allowed truncation. This is intentional per the thread but the existing test expected output was not updated.

pgsql-hackers Thread Ledger

Status: reconciled Ledger report slug: commitfest-7308-review-thread-ledger Reconstruction methods: amauta-mail-search-BM25, thread-id-grouping, message-content-inspection Summary: Located the pgsql-hackers thread 'Optimize truncation logic to reduce AccessExclusive lock impact' (Thread-ID 607107) via BM25 search on REL_TRUNCATE_MINIMUM and should_attempt_truncation. The thread has 4 messages: the original patch proposal by Stepan Neretin (slpmcf@gmail.com, 2025-03-18), a review comment by David Rowley (dgrowleyml@gmail.com, 2025-03-19) noting the comment becomes stale, and two replies by Yura Sokolov (y.sokolov@postgrespro.ru, 2025-03-19 and 2025-03-21) discussing patch authorship and large-relation behavior. The thread does NOT raise the unsigned-wrap guard (F1, refuted), the hio.c invariant breakage (F2), or the existing-test breakage (F6) — these are new findings from this review. David Rowley's message hints at the stale comment (F3) by quoting the comment block.

Thread 607107: Optimize truncation logic to reduce AccessExclusive lock impact

Participants: slpmcf@gmail.com, dgrowleyml@gmail.com, y.sokolov@postgrespro.ru

DateSenderKindSummary
2025-03-18T06:03:38+00:00slpmcf@gmail.compatchStepan Neretin proposes modifying should_attempt_truncation to require at least 3% of relation size + 2 pages freeable b...
2025-03-19T02:09:07+00:00dgrowleyml@gmail.comreviewDavid Rowley traces the code through DropRelationBuffers and comments that the patch seems to answer Yes to the question...
2025-03-19T16:05:13+00:00y.sokolov@postgrespro.ruauthor-replyYura Sokolov (patch author Yuriy Sokolov) agrees the patch works for smaller relations but notes it does not fix large r...
2025-03-21T11:50:22+00:00y.sokolov@postgrespro.ruauthor-replyYura Sokolov clarifies that for large tables truncated on a small number of pages, knowing old and new size allows preci...

Finding links

FindingDispositionThread referencesRationale
F1refuted(none)The unsigned-wrap guard removal was not raised in the thread. The finding was refuted by compiled-re...
F2new(none)The hio.c invariant breakage (REL_TRUNCATE_MINIMUM no longer way larger than MAX_BUFFERS_TO_EXTEND_B...
F3already-raisedCAApHDvrfngqCpQ09LKdr-BnJEVHW0=wAhxfRBnw=HJ2645cJPg@mail.gmail.comDavid Rowley's review message quotes the stale comment block, implicitly noting it describes the old...
F4new(none)The integer-division step-function collapse for rel_pages < 32 is not discussed in the thread....
F5new(none)No test coverage for the threshold change is discussed in the thread....
F6new(none)The breakage of existing vacuum.sql regression test assertions is not discussed in the thread. The a...

Deterministic Verdict

Verdict: needs-review

Verdict logic (post-verification):

Blocker split: verified=0, unverified=0.

The patch requires updates before it is committer-ready: (1) update the stale hio.c comment (F2), (2) update the stale vacuumlazy.c comment and document the integer-division step function (F3/F4), (3) add a regression test for the threshold change (F5), and (4) update the existing vacuum.sql expected output to reflect the behavior change (F6). The F1 BLOCKER (unsigned-wrap guard removal) was refuted and requires no change.

Aggregated Test Plan

  1. New regress test vacuum_truncate_threshold.sql (F5): exercises truncation on a 23-page table (delete 4000 of 5000 rows, VACUUM, assert truncation) and on a small table (200 rows, delete all, VACUUM, assert relation retains pages — behavior change).
  2. Updated expected output vacuum.out (F6): 2 assertions updated from t to f for pg_relation_size('vac_truncate_test') = 0.
  3. Static inspection oracles: grep -c 'way larger' hio.c → 0; grep -c 'whichever' vacuumlazy.c → 0; grep -c 'collapses to a constant' vacuumlazy.c → ≥1.

Residual Risk

Provenance

SeatModelHandoff report slugmarkdown_sha256
pg-patch-reviewerglm-5.2:cloudcommitfest-7308-review-handoff-patch-reviewer9b177fca8afa7bd47a113ce4c69ce7277c7ef1621d191b7d04cffc84eb007861
pg-security-reviewerglm-5.2:cloudcommitfest-7308-review-handoff-security-reviewerb735e4cf4719452afd6ff4f5b326e5d314574984ab77dc0d8037e35906386870
pg-finding-verifierglm-5.2:cloudcommitfest-7308-review-handoff-verifierea76bb16ca6f48a7ba099e0896a074b02211a94d0748414c4d1983c05b3d9717
pg-change-coverage-reviewer (host-disposable)glm-5.2:cloud(inline, coverage executed by outer harness)—

Router policy: version=1, hash=997c035512fa1b35aa17149ea386a861af531ec05839ce06810f38268a3caebe. User override: glm-5.2:cloud.

Artifact validation

The structured pg-review-artifact-v2 JSON artifact was validated with the supplied validator (the supplied run-scoped validator) and passed: ok: pg-review-artifact-v2 is structurally complete. Artifact path: the supplied run-scoped artifact path (review-artifact-v2.json).