Skip to content

test: concurrent revision-authority stress scenario (#236) - #505

Merged
mberrys merged 4 commits into
devfrom
feat/236-revision-stress
Sep 2, 2026
Merged

test: concurrent revision-authority stress scenario (#236)#505
mberrys merged 4 commits into
devfrom
feat/236-revision-stress

Conversation

@mberrys

@mberrys mberrys commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Why

#236 makes PDFDocumentContext the single revision authority for caches and asynchronous results, and states the issue stays open until "identity-separation tests, revision-bound cache/job tests, stale-result rejection, and the concurrent stress scenario pass".

Reading dev, the first three are already covered by work that landed from PRs #254/#258:

  • PDFDocumentContext / PDFRevisionIdentity (alias PDFRevisionToken) own the fence, separate from persisted PDFArtifactIdentity (UnitTestsIdentitySeparation);
  • PDFDocumentSession cache keys carry the revision, and PDFJobScheduler discards results whose revision is no longer current (UnitTestsDocumentSession, UnitTestsJobScheduler);
  • the interaction layer publishes the fence through IJobSubmitter and keys surface caches by revision.

The acceptance criterion with no coverage was the stress scenario itself. The existing UnitTestsDocumentSession/concurrentScheduledResults_rejectSupersededRevisions proves the fence in one orchestrated round — every producer is released after the mutation — which is not the hostile workload the issue describes.

What changed

UnitTests/tst_revisionstresstest.cpp (new target UnitTestsRevisionStress) runs the acceptance scenario: render, preflight, thumbnail, and repair-plan jobs in flight together across 64 rounds while the document is mutated and the effective profile changes at points the producers do not observe. Results are admitted through a single revision-gated consumer that models the findings model, the tile presenter, and the evidence cache under one mutex covering the fence and the retained entries together.

It asserts the four properties the issue names:

  1. zero stale findings applied;
  2. zero stale tiles presented as current past an invalidation boundary;
  3. deterministic cancellation — cancelled work is terminal, is never success, and publishes nothing;
  4. no cache read ever returns a result for the wrong revision.

A stress test that quietly stops exercising the fence is worse than none, so the non-vacuity checks are deterministic rather than timing-based:

  • one phase submits results against the current revision and asserts they are admitted and read back as current (a fence that rejected everything would otherwise pass);
  • a second phase holds one producer per job kind inside its work function, mutates the document underneath all of them, and only then releases them — asserting both the consumer-side rejection and the scheduler-side Stale outcome.

A third test walks PDFDocumentSession across document mutations and profile changes, asserting the session follows the context and drops its compiled-page cache rather than reconciling it.

Also: policy test mapping (agent-policy.json), the generated architecture catalog and Phase 5 widgets inventory, the pinned Phase 5 target count (67 → 68), the changelog fragment, and the #236 rows in docs/REVISION_CONTEXT.md, docs/SEMANTIC_TRUST_ENGINE_ACCEPTANCE.md, and docs/UPSTREAM_DIVERGENCE.md.

No production code changed.

Proof

All checks are green on 8a6cb91. The agent-fast / build job ran check-change.py end to end with "status": "pass":

  • build:UnitTestsRevisionStress — pass (compiles and links against Qt 6.11.1);
  • focused_tests — 25/25 ctest targets passed, including UnitTestsRevisionStress;
  • clang_tidy:UnitTests/tst_revisionstresstest.cpp — pass;
  • changelog, source integrity, architecture catalog, and policy adapters — pass.

source_integrity, policy, architecture-docs, and CodeQL are also green.

The earlier revision of this description said the build and test evidence was unproven; that was true when the PR was opened (this session has no Qt toolchain, so check-change.py could only run the source-level checks locally) and CI has since supplied it. The two red checks along the way were both generated-evidence bookkeeping that follows from adding a target — a stale docs/generated/phase5-widgets-inventory.json and the Phase 5 contract test's pinned target count — fixed in f4ba214 and 8a6cb91.

UnitTests/CMakeLists.txt is a protected path; the change to it is additive (one new test executable plus its add_test) and introduces no new public contract.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MaL7wNZgrcZibFiBZenb6t

claude and others added 4 commits September 1, 2026 23:37
Issue #236 requires that DocumentContext be the single revision authority
for caches and asynchronous results, and holds the issue open until the
concurrent stress scenario passes. The fence, the identity separation, and
the revision-keyed session and job caches already landed; the acceptance
scenario itself was the one criterion with no coverage.

tst_documentsessiontest.cpp proves the fence in one orchestrated round,
with every producer released after the mutation. This adds the acceptance
scenario instead: render, preflight, thumbnail, and repair-plan jobs in
flight together while the document is mutated at points the producers do
not observe, and asserts the four correctness properties named in the
issue - zero stale findings applied, zero stale tiles presented past an
invalidation boundary, deterministic cancellation, and no cache serving a
result for the wrong revision.

A stress test that quietly stops exercising the fence is worse than none,
so the non-vacuity checks are deterministic rather than timing-based: a
final phase holds one producer per job kind inside its work function,
mutates the document underneath all of them, and only then releases them,
asserting both the consumer-side rejection and the scheduler-side Stale
outcome.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaL7wNZgrcZibFiBZenb6t
The source_integrity job regenerates docs/generated/phase5-widgets-inventory.json
and compares it against the tree; adding UnitTestsRevisionStress made the
tracked copy stale (67 -> 68 targets).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaL7wNZgrcZibFiBZenb6t
The Phase 5 widgets contract test pins the generated target count, so a new
test executable has to move it with the regenerated inventory. Verified by
running the full scripts/ci unittest discovery (219 tests) locally.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaL7wNZgrcZibFiBZenb6t
@mberrys
mberrys marked this pull request as ready for review September 2, 2026 02:29
@mberrys
mberrys merged commit 5493a49 into dev Sep 2, 2026
8 checks passed
@mberrys
mberrys deleted the feat/236-revision-stress branch September 2, 2026 02:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants