Harness Intelligence Wiki
Research

CLI Verification Caching Planning Research

CLI Verification Caching Planning Research

Scope and method

Two readonly lanes inspected the current Turbo/CI trust surface and the CLI test/release/build surface. The coordinator checked the resolved Turbo 2.9.14 graph, the accepted spec, prior performance handoffs, and upstream Turborepo and GitHub Actions documentation. This report records current facts and planning constraints; it does not replace the accepted product decisions in the spec.

Facts

Current task and release graph

  • Root release:publish enters the uncached @punks/cli#release:publish Turbo task, but apps/cli/scripts/publish-release.mjs internally invokes build, release tests, and typecheck with npm run. Turbo therefore cannot cache those nested verification commands. Sources: package.json, apps/cli/package.json, turbo.json, apps/cli/scripts/publish-release.mjs.
  • The initial npm version probe already precedes deterministic verification and preserves the existing-version retry path. The new-version path does not probe npm again immediately before npm publish. Source: apps/cli/scripts/publish-release.mjs.
  • apps/cli/scripts/run-release-tests.mjs runs the four update wrappers together before the ordinary suite. Its public process contract asserts the exact two waves. Sources: apps/cli/scripts/run-release-tests.mjs and apps/cli/src/scripts/run-release-tests.test.ts.
  • Current release inventory is 84 ordinary test files containing 1,118 tests and four update wrappers containing 96 registered cases. The four wrappers contain 24 cases each and their union is exact. Source: apps/cli/src/update/run.shards.test.ts; verified with Vitest list output on 2026-08-06.

Missing cache-policy contracts

  • Ordinary files currently join release execution through a broad Vitest glob; there is no file-level cache policy. Registered update cases carry timeout selection but no cache-policy metadata. No contract can reject missing or duplicate cache assignments. Sources: apps/cli/vitest.config.ts, apps/cli/src/update/run.test-cases.test.ts, and apps/cli/src/update/run.shards.test.ts.
  • The real child-process wall-clock assertion in apps/cli/src/data/scripts/sync-subagents.test.ts uses Date.now(), a real spawned process, and an OS timeout. It is the accepted uncached exception.
  • Existing loopback tests own ephemeral local servers. Other timer-based tests still need classification evidence; a loopback address or timer alone does not prove deterministic cache eligibility.
  • The implemented CLI runtime-hardening work rejected wider file parallelism after GitHub subprocess starvation. Cache task decomposition must not revive that rejected scheduling change.

Build identity and restoration gaps

  • apps/cli/scripts/build-dist.mjs copies root CHANGELOG.md and BASELINE_CHANGELOG.md into dist, but the resolved CLI build inputs omit both root files.
  • The same build rewrites tracked apps/cli/src/data/bundled-baseline-identity.generated.ts. Its previous bytes are included by $TURBO_DEFAULT$, while the declared outputs restore only dist/**. The generated identity therefore influences its own next hash and cannot be restored as part of a cache hit.
  • The repository does not currently have an automated contract for forced execution, first miss, second hit, deleted-output restoration, each declared invalidation input, fresh/cached byte equality, or clean fresh/cached worktree paths.

Runtime and trust boundaries

  • CI pins Bun 1.3.5 and Node 24. The repository pins Bun 1.3.5 and Turbo 2.9.14, but does not pin Node or explicitly hash OS, architecture, Bun, and Node identity into CLI verification tasks. The planning host used Node 22.22.0, so the mismatch is observable rather than hypothetical.
  • The Behavior Contract workflow has a SHA-keyed local Turbo cache action. The dedicated CLI ordinary and update jobs do not restore it. No repository configuration currently supplies a remote-cache token, signature key, development/protected namespace, or same-repository fork predicate. Source: .github/workflows/behavior-contract.yml.
  • GitHub documents that fork pull-request workflows do not receive Actions secrets. It also warns that cache contents are not signed or verified and must be treated as untrusted input. Sources: https://docs.github.com/en/code-security/reference/secret-security/secret-types and https://docs.github.com/en/actions/reference/workflows-and-actions/dependency-caching.
  • Turborepo signs remote artifacts with HMAC-SHA256 when remoteCache.signature is enabled and rejects missing or invalid signatures. Its own documentation calls this integrity/authenticity defense-in-depth, not a complete security boundary. Source: https://turborepo.com/docs/core-concepts/remote-caching, cross-checked against the upstream vercel/turborepo documentation source retrieved on 2026-08-06.
  • Turborepo documents that passThroughEnv values are available at runtime but do not contribute to task hashes. Current CLI tests pass through HOME, TMPDIR, Git configuration, and XDG roots. Those values cannot affect cacheable tasks unless controlled or moved into hashed environment identity. Source: https://turborepo.com/docs/reference/configuration and turbo.json.

Inferences for planning

  • The public release seam must remain an uncached dispatcher. Static Turbo dependencies cannot preserve the accepted initial npm probe because they run before the package script. The dispatcher can route only a new version through deterministic Turbo verification and then enter an uncached mutation executor.
  • Task-level caching requires separate cacheable and uncached processes. A title filter inside one shared Vitest process is not an eligible cache boundary.
  • The test-policy module should own discovery and exact assignment. New ordinary files can resolve to uncached by default, while registered update cases must declare metadata explicitly so missing and duplicate declarations fail.
  • Development and protected use need separate hashed namespace values and separate signing authority. Forks must receive neither remote-cache nor signing credentials. A generic repository contract can name these inputs without binding delivery to one remote-cache vendor.
  • The generated bundled identity should be excluded from build inputs and added to declared outputs. Narrowing build inputs further would be risky because the current broad package input set conservatively covers bundled data, skills, package metadata, and build scripts.
  • The shared task topology in turbo.json, package scripts, release runner, workflow, and root behavior contract should have one integration owner. The policy, build-byte, and release-dispatcher modules can be implemented in parallel before that integration step.

Conflicts and uncertainty

  • No remote-cache provider credentials or protected release environment exist in repository state. Repository delivery can define and test the generic credential/namespace contract, but a real local-to-PR remote hit needs external credentials configured by the repository operator.
  • Turborepo signatures verify an artifact against a shared secret but do not by themselves prove who ran the producing task. Separate credentials, namespace hashing, protected secret access, declared inputs/outputs, and release eligibility checks must work together.
  • Existing runbooks say operators should commit the regenerated bundled identity and rerun release. That workflow contradicts the accepted derived-output and clean-worktree contract and must change during docs ingest.

Planning conclusion

Use three disjoint first-wave tracer bullets: exact test-policy inventory, reproducible build outputs, and pre-probe release dispatch. Follow with one shared Turbo/package graph integration task. After graph integration, wire same-repo PR development-cache credentials and protected release authority separately, then run one acceptance task covering force/miss/hit/restoration/invalidation, uncached execution, byte equality, and clean repository state.

On this page