Skip to content

feat: TraceDecay V2 delivery (PR8–PR13) + daemon performance, memory, and reliability overhaul - #707

Open
ScriptedAlchemy wants to merge 2449 commits into
masterfrom
codex/tracedecay-total-redesign-plan-reopened
Open

feat: TraceDecay V2 delivery (PR8–PR13) + daemon performance, memory, and reliability overhaul#707
ScriptedAlchemy wants to merge 2449 commits into
masterfrom
codex/tracedecay-total-redesign-plan-reopened

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Replacement review for #421 after its accidental merge was reverted from master in 52a9aab. This commit has the exact tree of #421 head d4c67d4. Do not merge until explicitly authorized.

Issues this PR fixes

Fixes #753
Fixes #792
Fixes #837
Fixes #838
Fixes #855
Fixes #862
Fixes #861
Fixes #810
Fixes #845
Fixes #856
Fixes #853
Fixes #799
Fixes #836
Fixes #830

Partially addressed here, tracked to completion on their own issues: #852, #800, #863, #843, #842, #848.

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Aug 24, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4b68faf

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Post-reopen correction pushed in a23f86a: source-neutral background reconcile no longer advances the scheduler epoch and cancel in-flight text activation; mounted hook overflow uses the explicit invalidating path. Evidence: RED reproduced 0/1, GREEN 1/1 for ordinary_background_reconcile_does_not_supersede_in_flight_text_work; cargo check --lib --locked passed.

@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

if [[ -n "${{ steps.target-runtime.outputs.runtime_library }}" ]]; then
companion_args+=(
--companion
"${{ steps.target-runtime.outputs.runtime_library }}=${{ steps.target-runtime.outputs.runtime_entry_name }}"

P1 Badge Install the bundled Linux runtime beside the binary

For Linux targets, this archive now contains libonnxruntime.so.1, and the verification step succeeds only while that companion remains beside the $ORIGIN-linked executable. However, install.sh lines 80-84 extracts the archive and copies only tracedecay into the install directory before deleting the temporary directory. On systems without a compatible system ONNX Runtime, binaries installed through the advertised script will therefore fail in the dynamic loader even for --version; install the companion library beside the executable or use a layout whose runtime search path matches the installed location.


{
"type": "json",
"path": "server.json",
"jsonpath": "$.version"

P1 Badge Advance the SDK version in release PRs

The new npm publication job derives its version from the packed sdks/typescript/package.json, but this release-please list updates only the root manifest, CLI manifest, and server.json; the SDK manifest and lockfile remain fixed at 0.1.0. Starting with the release after 0.1.0, the job will either fail its different-integrity check for @tracedecay/sdk@0.1.0 or no-op for identical bytes, so no SDK version corresponding to the new stable release is published.


const overview = useQuery({
queryKey: ['delivery', 'overview'],
queryFn: () => fetchEnvelope('/api/delivery/overview', DeliveryOverviewV1Schema),

P1 Badge Scope Delivery queries to the selected project

When the scope bar selects a project other than the active project, this query keeps the same cache key and continues fetching the unprefixed active-project route. The dashboard already provides scopeKey and scopedUrl, and the backend mounts /api/projects/{id}/delivery/overview; without using them here, the page labels the selected project while showing another project's Git, CI, and release data, and switching scopes does not trigger a refetch.


let digest = RegistrationDigest {
project_id: project_id.to_string(),
canonical_root: registration_root.to_path_buf(),
git_common_dir: git_common_dir.clone(),
tracked_branches,

P1 Badge Include Git remote identity in the registration digest

When origin changes via git remote set-url without touching branch metadata or store artifacts, this digest remains identical, so the cache returns at lines 130-131 before git_remote_url is recomputed at line 147. The registry consequently retains the old remote and its alias, causing remote-based cross-project resolution to reject the new identity or continue resolving the stale one until an unrelated artifact changes; include the normalized remote in the digest.

AGENTS.md reference: AGENTS.md:L159-L161


private sendCurrentDiagnostics(): void {
const uris = vscode.languages.getDiagnostics().map(([uri]) => uri);
this.sendChangedDiagnostics(uris);

P2 Badge Batch startup diagnostics instead of dropping the tail

At activation or language-client restart, getDiagnostics() can return more than 32 document URIs, but this passes the entire list to limitAdmittedNativeDiagnosticDocuments, which permanently slices it to the first 32. Documents after that boundary are never synchronized unless they later emit a diagnostic-change or open event, leaving TraceDecay's native diagnostic state incomplete indefinitely for larger workspaces; split the startup list into bounded batches instead.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

ScriptedAlchemy added a commit that referenced this pull request Aug 24, 2026
fix(clippy): clear the workspace clippy failures on #707
ScriptedAlchemy added a commit that referenced this pull request Aug 25, 2026
Removing the re-export shims left their module doc comments orphaned in
`context` and `graph`. The rest are pre-existing lints that only fire under
`--all-features`, which compiles the `test-transport` support surface as
non-test code:

- the registered-test-server constructor gets the file's existing explicit
  `expect_used` allow, since `lib.rs` denies it only outside `cfg(test)`;
- `http_application_registry` is read by the `cfg(test)` capacity journey and
  only reads as dead under `test-transport` alone, so the allow is gated to
  `not(test)` rather than renamed to a discard;
- the hotpath cadence helpers keep `#[inline(always)]` with an explicit allow,
  preserving the author's intent instead of silently downgrading it.

The `daemon_suite` restart wait is now the original helper recovered from the
PR #707 head rather than a reconstruction: it additionally proves the runtime
readiness receipt authorizes the exact configuration the SDK selected.
ScriptedAlchemy added a commit that referenced this pull request Aug 26, 2026
ScriptedAlchemy added a commit that referenced this pull request Aug 26, 2026
The relocation fix that anchored this test above the package allocated a
`PathBuf` only to hand out a reference, which `-D warnings` rejects as
`unnecessary_to_owned` and which failed the Clippy gate on #707.

`validate_requested_workload` takes `&Path`, and `CARGO_MANIFEST_DIR` is a
literal, so `Path::new` yields a `&'static Path` with no allocation and no
temporary to borrow from.
ScriptedAlchemy added a commit that referenced this pull request Aug 26, 2026
…707-20260826-a1

perf(index): integrate post-#721 catch-up delta into #707
@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

#733 landed Clippy-red. Helper branch (not merged, shared checkout untouched):

cursor/707-clippy-after-733 @ latest HEAD of this PR (186880e550)

Clears the #733 Clippy denials:

  • nest Unbound | Unavailable or-patterns
  • collapse nested hint/witness and session-drain ifs
  • box SessionGraphAttachmentStateV1::Attached so the enum is not 808B

Adopt when you want:

git fetch origin cursor/707-clippy-after-733
git merge --ff-only origin/cursor/707-clippy-after-733

I will not land this on #707.

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Clippy helper is now on the Codex worktrees (still not landed on this PR head):

  • relocate /fast/projects/tracedecay/.codex-worktrees/root-package-relocation @ 806b8c74ef (cherry-pick of 3293045f53)
  • post-721 /tmp/tracedecay-integrate-post721-707-a1 @ 127ab57fae (ff to 186880e550 then same cherry-pick)

Same change as cursor/707-clippy-after-733. I will not push/merge this onto #707.

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Isolated post-#721 verification (worktree /tmp/tracedecay-integrate-post721-707-a1, CARGO_TARGET_DIR=/tmp/td-post721-isolated-target):

Green (non-vacuous) on merge 3b1baaee82 / later 127ab57fae:

  • tracedecay-graph-db --lib 87
  • tracedecay-runtime-core --lib 677
  • tracedecay-usecases --lib 837 (+1 ignored)
  • tracedecay-code-index --lib 177 (+1 ignored)
  • sealed_publication_tests 3, daemon::scheduler 33, session_runtime 1, sealed_projection_deadline_tests 1
  • late-bind native_declared_topology_projection 12

Not green

  • --all-features cannot compile tracedecay-rusqlite-runtime (admission.rs:120): hotpath::mutex! vs std::sync::Mutex because graph-db enables hotpath/hotpath without tracedecay-rusqlite-runtime/hotpath.
  • daemon::tests::scheduler 11 passed / 11 failed. Failures are scheduler_config::*: ensure_worker_planprofile code-index worker plan was not installed during daemon bootstrap (plus one writer-gate timeout and one missing scheduler key). Production bootstrap.rs still calls install_profile_worker_plan; the test daemon path does not.

Clippy helper remains on this tree at 127ab57fae / relocate 806b8c74ef. Not pushed to this PR head.

ScriptedAlchemy added a commit that referenced this pull request Aug 27, 2026
Ports the missing pieces of commit b1e41b712 ("fix(build): land the
batching commit's missing consumers") onto #707: HookCompletedReadinessDistributions
moved into tracedecay-agent-hosts with pub(crate) fields, so
input_rows_processed, input_rows_dropped_at_cap, and events_considered
are no longer reachable outside the crate. Adds the three public
accessors.

source_event and input_rows_received (also added by the source commit)
are not ported: nothing on this branch reads them through an accessor
today - the root-crate benchmark still compares via
serde_json::to_value(&readiness_distributions)["field"], which does
not need them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@cursor
cursor Bot changed the base branch from master to cursor/vendor-split-707-d6ed August 27, 2026 05:09
cursor Bot pushed a commit that referenced this pull request Aug 27, 2026
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
@cursor
cursor Bot force-pushed the cursor/vendor-split-707-d6ed branch from 5d8f2a1 to c7fcf3f Compare August 27, 2026 05:52
ScriptedAlchemy added a commit that referenced this pull request Aug 27, 2026
…d6ed

chore(vendor): stacked vendor-only PR for #707 — generated vendor churn, land first
@ScriptedAlchemy
ScriptedAlchemy deleted the branch master August 27, 2026 06:24
@ScriptedAlchemy
ScriptedAlchemy changed the base branch from cursor/vendor-split-707-d6ed to master August 27, 2026 06:24
cursor Bot pushed a commit that referenced this pull request Aug 27, 2026
…rules

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>

Copy link
Copy Markdown
Owner Author

Architecture simplification review

Reviewed source at 0e87c01a1d783715d62664081b3d3b53dfe2af2e, the complete six-file #882 diff, the open release PR's scope, and relevant historical refactor PR descriptions. This is not a claim that every file in this PR or every historical PR diff was reviewed. No builds, tests, or live-profile measurements were run.

New scoped issues

Each issue includes pinned source, deletion scope, preserved invariants, and behavioral acceptance. They deliberately do not prescribe another framework, registry, contract-only phase, or arbitrary crate-count target.

Higher-impact work already has issue owners

#852 / #800 / #811: make the generation representation and work proportional to what is needed. The structural target is an authenticated manifest and immutable content-addressed segments, bounded borrowed/page readers, and a successor that reuses proven unchanged descriptors. Avoid coexisting full decoded generations, transformed JSON DOMs, whole-corpus identity maps, and encoder buffers. Do not weaken validation; retain proof of exact prior generation/source identity when reusing unchanged segments. A smaller batch does not bound a pipeline that still retains the full corpus at other stages.

#872 / #869: make registration lifetime follow the existing owner. Project-server eviction, invocation/scheduler retirement, and worktree replay-route release cannot be unrelated cleanup conventions. Prefer ownership/RAII registration handles attached to the actual project or retained-generation owner; derived lookup maps must not become extra lifetime authorities. Preserve genuine profile/project/worktree/generation lifetime differences and drain live response leases before retirement. Do not introduce a universal lifecycle framework or collapse all stores into one owner merely to reduce type count.

#882 / #753 / #837: bounded progress, not a very long timer, is the background-work design. The reported successful restart is useful evidence. It does not by itself prove bounded working set or cancellation of stalled corpus-scale work. The existing #882 inline review already flags the corpus-wide legacy identity maps and deadline change; I added the #886 linkage rather than duplicating those reports.

Preserve the simplifications already made

The historical direction is sound when a change deletes an authority or an unmounted path: #525's single verified graph publisher; #650's core-owner/alias split; #652's removal of a test-only runtime dispatch port; #523/#520's deletion of uncalled runtime/workflow machinery; #250's single plugin source tree; #666's single language-tier membership definition. Moving files or adding more adapter traits is not equivalent progress.

In particular, tracedecay-application is already a transport-neutral contracts crate with native Git optional. Its use by the protocol crate is not evidence of dragging the application runtime into clients. Likewise, consumer-owned ports are not inherently a dependency-direction defect; moving them into a new crate without reducing the actual dependency closure would just rearrange complexity.

Recommended sequence: finish bounded generation ownership and complete project/route retirement first; then #883; then #885 and #886 as contained deletion-oriented refactors. Keep the existing physical graph durability verification, exact identity, policy rechecks, and typed unavailable/reset states.

Copy link
Copy Markdown
Owner Author

Second deletion/slop pass, this time against current master (b9204dcf7de8faac4ef05af367a546aa29c32e9f) as well as the #707 architecture findings.

Filed three additional issues:

General rule for the cleanup work: delete a test when it only protects source shape, inventory count, prose wording, or another test's fixture; keep tests that falsifiably protect a shipped wire/persisted contract, failure mode, rollback/cancellation/lifetime invariant, or real production journey. Delete an abstraction when it has no independent policy/lifetime/authority. Do not replace removed slop with a new framework, compatibility alias, snapshot gate, or source scanner.

I did not run the workspace tests or performance suite during this source audit, so #889 does not claim measured bytes yet; it requires them as acceptance.

@ScriptedAlchemy ScriptedAlchemy left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current guidance before this becomes mergeable:

  • Land #882 only after its two unresolved P1s are fixed: remove corpus-wide identity-map cloning during legacy restore and restore a measured bounded restart deadline with cancellation/progress evidence. #882 is a stacked PR against this branch, so its fixes should be incorporated before final validation.
  • Rerun the complete integration gate on the final head. The latest head 0e87c01a1 has successful hotpath-profile/runtime-core, SDK conformance, and plugin validation, but the main CI and hotpath-coverage runs are cancelled. Do not treat that as green. Final acceptance should include the supported full nextest/CI lane and the relevant hotpath coverage lane on the exact merge candidate.
  • Keep the architectural cleanup issues as explicit follow-ups unless they are shown to be release blockers: #883 (remove the redundant blocking waiter in front of the SQLite writer), #885 (delete the process-global catalog callback), #887/#888 (dead-code and duplicate/prose-shape test deletion), #889 (resolver clone/index memory), #890-#892 (skill bootstrap/curator/body simplification). Do not expand #707 further just to absorb all of them; this PR is already extremely broad.
  • Do not close partially addressed issues as fixed. The PR body already distinguishes #852/#800/#863/#843/#842/#848 as partial; keep that discipline for any newly discovered simplification/perf work.
  • Before merge, reconcile the PR body against what actually lands. It currently claims fixes for many issues and has accumulated release, host, dashboard, daemon, storage, automation, and code-index work. Make the final body state the exact remaining known limitations, especially semantic activation and generation memory ownership, rather than implying V2 is generally complete.

Architecturally, I would not block this PR on crate-count reduction. The higher-value deletion work is reducing simultaneous generation representations, hidden lifetime authorities, redundant execution hops, and duplicated policy/tests.

ScriptedAlchemy and others added 11 commits September 5, 2026 08:34
Bounded route-cache eviction in `bind_or_insert_route_bounded` retires only
the evicted MCP server. It never releases that project's invocation runtime
owners, so the evicted project keeps its code-index scheduler worktree slot for
the life of the daemon. The scheduler registry is sized to
MAX_CACHED_PROJECT_SERVERS, so past eight distinct projects every further open
failed its mount with "code-index scheduler capacity is exhausted" and then
served with no code indexing at all -- the twelve-project production journey
saw project-8 publish no generation after 20s with scheduler_state=None.

Enter the release path on project-server cache saturation as well as graph
admission pressure: that path is the only one that drains the code-index
workers together with the server it retires. A refusal stays reserved for
exhausted graph admission; a merely saturated cache falls through to the
bounded eviction and the already-cached-key fast path behind it.

Refs #872

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Two acceptance tests drove arguments the shipped CLIs never parse, so both
failed before reaching what they meant to assert.

`exact_search_does_not_wait_for_semantic_projection` ran `tracedecay init
--quiet`. `init` has no `--quiet`, so clap refused the whole command with
"unexpected argument" and the project was never initialized.

`qualify_native_rejects_an_unparseable_candidate_without_writing_output`
passed `--candidate`. The native evaluator now runs inside the owning daemon
and `qualify-native` carries only --project-root/--profile/--output, so clap
exited 2 with an empty stdout and the test parsed nothing at all. Assert the
retained guarantee instead -- an incomplete request is refused before any
artifact is written -- and rename the test to what it now covers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`collectable_generations` was read off the newest-first
`superseded_generations` list, the same order the rollback reserve is
taken from. Every bounded collection unit therefore named the *newest*
collectable generation: `MAX_CODE_GENERATION_RETENTION_BATCH_V1` took
the 32 newest, and `plan_next`/`prepare_next` truncated that to the one
sealed a moment ago. The oldest sealed generation was never in a plan,
so a store that publishes at least as fast as maintenance collects never
released its floor, and after four edits the first source generation was
still uncollectable with `rollback_floor = 0`.

The reserve and the sweep are opposite questions over the same list.
Keep the reserve on the newest-first order and reverse the batch so it
reclaims from the oldest end.

Closes #897.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The run ledger is append-only with an enforced lifecycle: once a run reaches
a terminal status no further row for that run is legal. The fact-store curate
case appended the curate run's own terminal `skipped` record a second time
with an artifact attached, and `append_run_record` refused it with
"automation run ledger run '...' has an invalid lifecycle transition".

Cover `automation runs artifact` on a run of its own -- one terminal row that
already carries the artifact, which is the only shape the ledger accepts --
instead of fabricating a second terminal row for the curate run.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`authentic_callback_to_all_delivery_surfaces` required an LSP binding on the
feedback catalog contribution. The catalog publishes none by design -- LSP and
native delivery are an internal event path rather than a JSON-RPC method
binding -- and the publication-read assertion earlier in the same file already
pins the four callable surfaces (CLI, MCP, HTTP, Dashboard). The two
expectations contradicted each other, and the LSP one failed.

Assert the shipped set, including the absence of an LSP binding, so a future
binding change has to update one expectation rather than satisfy two opposed
ones.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The project's automation scheduler runs beside the manual `fact_store_curate`
call and takes the same curator lock. When the scheduler holds it, the manual
run terminates as `scheduler_lock_active` -- a legal transient outcome -- and
the assertion on the backend-disabled skip fails. Observed on a loaded machine
in cargo-hauler cc-5750.

Retry the manual run, bounded, while the terminal reason is
`scheduler_lock_active`, so the case observes the skip it is about instead of
whichever run won the lock.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`sqlite_writer_uses_production_wal_normal_policy` read `synchronous` and
`wal_autocheckpoint` from `read_connection()`. Both pragmas are
connection-scoped and production applies them only to the writer
(`apply_pragmas` under `ConnectionMode::Writer`), so the test measured the
reader's SQLite defaults -- FULL and 1000 -- and failed on synchronous == 2.

The writer already verifies synchronous == NORMAL and wal_autocheckpoint == 0
at open and refuses to publish otherwise, and no external crate can read a
pragma on the writer connection. Assert the database-level journal mode plus
what the disabled auto-checkpoint durably produces: a committed write through
the production writer broker leaves a retained write-ahead log.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Three session_suite cases still demanded behavior the branch retired, so
they failed against their own fixtures rather than against a regression.

* `workflow_projection_rolls_back_rebuilds_restarts_and_audits` expected
  the very next `project_observation` after an injected storage failure
  to succeed on the same mount. A storage failure arms the durable retry
  backoff — the bound `ea8c45012` introduced against the unbounded
  observation-projection retry spin — so that call is `RetryDeferred` by
  contract, and dropping the injected trigger does not rewind the
  backoff clock. Prove recovery the way production reaches it: remount
  and re-project. The two later reopen points now release their store
  handle before the runtime for the same reason.
* `diagnostic_runner_reaches_cargo_on_linux_and_macos` pinned the cargo
  argv from before `eebf0957a` retargeted the shipped runner. The
  repository root is a virtual workspace, so
  `scripts/run-session-temporal-benchmark.sh` must name `-p tracedecay`
  for `cargo bench` to resolve the harness at all.
* `frozen_temporal_page_returns_projected_occurrences_and_lineage` built
  its only copy edge from `parent_message_copy`, a reply link.
  `derive_retained_projection_relations` deliberately no longer treats
  conversation threading as copy evidence — collapsing a reply into its
  parent erases it from every current-mode retrieval surface — so the
  page returned zero copies. Add a third occurrence carrying real
  `CopiedFrom` anchor provenance with the matching explicit-assertion
  proof, and assert the reply link still fabricates nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`tracedecay hook-codex-user-prompt-submit` violated both halves of the
host output contract when no daemon is reachable. Reproduced directly
against the shipped binary with an isolated profile (every other native
hook was already clean):

    stdout {"hookSpecificOutput":{"additionalContext":"", ...}}
    stderr [tracedecay] user Codex ingest daemon call failed: config
           error: TraceDecay daemon socket '.../daemon.sock' is not
           available. No managed TraceDecay daemon service is installed.

Root causes:

* The handler always wrapped the turn-local context in
  `additional_context_json`. Since `08fe1d7f3` kept prompt steering
  turn-local, a generic workspace yields no context at all, and
  `additionalContext: ""` is not "no context" to Codex — it is an
  injected empty context block. Emit `{}` instead, exactly as the
  sibling `hook_codex_session_start` already does for guidance-free
  admission.
* Hook diagnostics reported daemon failures with `eprintln!`, which
  bypasses the hook stderr default. `stderr_tracing_default` documents
  that hooks are silent on stderr unless `RUST_LOG` says otherwise,
  because the host owns that stream and reads unexpected output as a
  hook failure. Route the projectless-ingest, counter-reset, Claude
  PostCompact, Codex Stop retention and Codex PostCompact failures
  through `tracing::warn!` so `RUST_LOG` still surfaces them.

Kiro `preToolUse` keeps its `eprintln!`: stderr is its documented block
channel, not a diagnostic.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`drop(store)` before the reopen tripped `clippy::drop_non_drop`: the
session-temporal store handle implements no `Drop`, so the call only
ended the borrow that NLL already ends at its last use.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Run 33954670866 was the first Windows archive without the parity rebuild:
the portability compile took 31 minutes and the archive was still
compiling the root crate's test targets when the 75-minute bound cancelled
it at 09:28, the fifth cancellation in a row. Because no archive has
completed, the rust-cache and kache caches never persist a warm state and
every run starts cold. Widen the bound to 120 minutes so one archive can
complete and seed the caches; the retained Cargo timings on that run will
show the warm cost, and the bound should come back down to it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment