Repository navigation
Conversation
A completed cycle's required-role check scanned artifacts[] once per revision, which is quadratic in the number of files a cycle holds. A code stage whose evidence folder kept 1.43 million files (a 1.45 GB manifest, about 700k artifacts) needed roughly five hours per validate_lineage call by the measured growth. The runtime settlement that runs after an owner exits goes through this check, so the dispatch-reap-watch for that owner stayed at 100% CPU for 25 hours (RSS up to 15.7 GB), and `dispatch_terminal_commit.py inspect` stopped on the same line. Build one artifact_id -> role table first, keeping the first row per id as the scan did, and look each revision up in it. 20,000 artifacts now validate in 0.11 s instead of 15.9 s, and 320,000 in 3.2 s. The tests pin the role semantics (roles with a revision complete the cycle; a repeated artifact id keeps its first row's role) and bound the reads of artifact rows: the per-revision scan read about two million rows for 2,000 artifacts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
validate_lineagechecks a completed cycle's required artifact roles by scanningartifacts[]once per revision (anext(a for a in artifacts if ...)inside the loop overartifact_revisions). The cost grows with the square of the number of files a cycle holds.Measured on unmodified main
c0119a79awith a synthetic completed cycle (one revision per artifact):validate_lineageHow it showed up
A code stage kept its gate evidence under the cycle's
artifacts/evidence/gates/: 1.43 million files (wav, jsonl and json; 77 GB). The sealed manifest grew to 1.45 GB with about 700k artifacts, so by the growth above onevalidate_lineagecall on it takes roughly five hours.dispatch-reap-watchdrained the process group and then reached this check through the runtime settlement (materialize_after_terminal_close→settle_owner_completion→ sealed-cycle verification). With every watched process already gone, the watcher stayed at 100% CPU for 25 hours (RSS up to 15.7 GB) until it was stopped by hand.dispatch_terminal_commit.py inspectfor the same attempt goes through the same verification. Stack samples put it invalidate_lineageat the per-revisionnext(...)scan.Fix
Build one
artifact_id -> roletable first, keeping the first row per id as the scan did, and look each revision up in it. The check is now linear: 20,000 artifacts validate in 0.11 s and 320,000 in 3.2 s. Before the change, the old and new functions returned the same violations on 600 randomized documents, including repeated artifact ids, revisions whose artifact is missing, and bothfirst_closevalues.Tests
TestCompletedCycleRolesinutilities/artifact_manifest.test.py:next(...)behavior.Checks run on this branch (
1e2e28812), on a host whose harness Python is conda CPython 3.13.11:utilities/artifact_manifest.test.py: 103 tests pass on CPython 3.13.11 and on the system CPython 3.12.3.artifact_manifest, run withtools/run-tests.pyon this branch and then on unmodified main, one tree at a time: 17 pass on both.runtime_observation_cpu.test.py::test_isolated_production_observer_workloadfails the same way on both trees (timed out waiting for all watchers pending on actual registered work after native idle), so it is a host issue and not caused by this change.adaptation-boundaryjob's checks all pass:generate.py --check,render-landing.py --check,check-adaptation-boundary.sh,check-model-config.py,check-unit-config.pyandcheck-scope-placeholders.py. So docheck_destructive_calls.py tools/installandcheck-installed-layout-trigger.py(fired=none).install-lifecyclesuites: 14 of 15 pass.projection-completeness.test.shfails on this host only because PyYAML is installed in the user site and the fixture's private HOME hides it (host.pyyaml). It fails the same way on unmodified main.Not in this PR
The manifest still lists every file. A large directory becomes a single descriptor only when it is named
raw-results, so a stage that writes many files anywhere else still gets a very large manifest; here that was 1.45 GB plus a snapshot of the same size. Even with this fix, loading and digesting a manifest of that size takes minutes and several GB of memory on each pass. Whether a large subtree should be bundled automatically is a separate design question, left for a follow-up.🤖 Generated with Claude Code