Skip to content

Cache misses when Go testdata/embed, Node .mjs/.css/config, or other read files change - #337

Open
calvin-archastro wants to merge 3 commits into
mainfrom
factory/tsk_3ZcmMUQxEMT4HknMopt8Eo-f85a43d3-b633-480d-84fb-
Open

calvin-archastro wants to merge 3 commits into
mainfrom
factory/tsk_3ZcmMUQxEMT4HknMopt8Eo-f85a43d3-b633-480d-84fb-

Conversation

@calvin-archastro

@calvin-archastro calvin-archastro commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Review on ArchCode

Epic: Aster bug bash fixes
Task: Cache misses when Go testdata/embed, Node .mjs/.css/config, or other read files change

Problem and author intent

Inferred Go, Node.js, Elixir, Rust, and Python targets hashed only narrow source globs (**/*.go, src/**/*.{ts,tsx,js,jsx} plus lib/**/*.{ts,js}, lib/**/*.ex / lib/**/*.exs with test/**/*.exs only for test, src/**/*.rs plus build.rs and test/bench globs, and *.py). The hasher walked those globs, honored .gitignore, and skipped some dependency directories, but a file the toolchain actually reads that did not match the glob never entered the key.

Before this change, editing Go testdata/ or a //go:embed file, a Node .mjs / .css / .vue module or tool config such as vite.config.js, Elixir priv/ or test/support, a Rust include_str! asset or rustfmt.toml / clippy.toml, or a Python test-data file left the cache key unchanged. The next aster build, test, or lint could hit while the real tool would fail or emit different output. cache.include could paper over a single path, but defaults were expected to cover what those targets read.

The intent is to use a project-wide **/* input for every non-deps target of those five plugins, keep deps lockfile- and toolchain-file-only so a source edit does not reinstall dependencies, let user exclude win, and keep a build from invalidating itself by hashing its own outputs or tool caches.

What changed

Non-deps cache_inputs for Go, Node.js, Elixir, Rust, and Python now use PROJECT_FILES_GLOB (**/*) instead of language-specific source lists. Target-specific extra globs (Node test files, Elixir test/**/*.exs, Rust tests/ and benches/, Python test modules) are no longer appended, because those paths are already project files. deps still has an empty source_globs list; Python deps can still name .venv/pyvenv.cfg as a config file.

The walker still skips gitignored directories, does not follow symlinks, and prunes nested project roots. It now also prunes tool caches even when they are not gitignored: .git, .aster, node_modules, __pycache__, .venv, .pytest_cache, and *.egg-info at any depth, and target, _build, deps, dist, and build only as children of the project root. src/build and internal/dist stay inputs. A catch-all **/* does not count as naming a pruned directory. A config-file pattern or user include that names a file inside one of those directories still walks that directory, and the matcher keeps only the named file. Declared [targets.*.cache] outputs are dropped by the matcher, including a bare bin/app. User exclude is applied first and wins over a named include.

Watch uses the same matcher. A project file now owns every non-deps target, so a non-source edit fires both build and test unless it is excluded. Default watch ignores gain **/.pytest_cache/**, **/__pycache__/**, and **/*.egg-info/**. Tooling paths such as node_modules and root dist/ are ignored before cache inputs are consulted. Declared outputs do not own a watch target; a nested src/build file still does.

A second revision on the same base adjusts fixtures that became self-invalidating once every project file is an input. A command that writes an undeclared marker (runs.txt, gen_ran) changes the post-run key, so the result is never stored. Those fixtures now exclude the marker. A lint target that shares a lookup with build excludes out/**, because restoring build's output would otherwise change lint's key after the shared lookup. Remote-cache assertions also surface stderr when a lookup count fails.

README documents the new default, the root-only output-dir prune, exclude winning, deps staying narrow, and include being able to name a file inside a pruned directory.

Scope

Two frozen revisions share base 0c4b2857395fc0db905afdc1069771f4787d16ba. Neither patch listing omitted hunks or paths. Diagnostics are truncated at the source/check-log layer, so this summary is from the supplied excerpts, not from a claim of complete CI logs.

  • a64dd59559af772ebf34199441e397129840cdb4: 15 paths, 55 hunks, +703 / −90. Cache hasher and matcher, five language plugins, plugin registry constant, watch ignore/plan/event loop, workspace ignore comment, README, tests/integration.rs, tests/watch_tests.rs.
  • 00c6d328d56f6c78af7f2335fab24a57a2a0eafd: 19 paths, 64 hunks, +729 / −99. Same tree plus src/executor/runner.rs, tests/cache_login_e2e.rs, tests/cache_remote.rs, and tests/cache_remote_e2e.rs for the marker/out/** excludes.

Out of scope, and not changed here: files-read tracing or sandboxing, deps input policy, and Go dependency-edge behavior.

Risk assessment

High. Every cache key for the five plugins' non-deps targets changes, so the first run after upgrade is a miss and shared or remote caches must repopulate. Wider inputs lower hit rate when generated files are not gitignored, not in the prune list, and not declared outputs. Hash cost grows with project size. Root-only pruning of dist, build, target, _build, and deps is intentional so nested source trees stay inputs, but a nested output directory that is not gitignored can still self-invalidate. A file the command writes that is not a declared output changes the post-run key and prevents store; that is why the fixture excludes were required.

User impact

A change to testdata, embeds, styles, Vue/mjs modules, tool configs, Elixir priv / test/support, or similar project files misses build, test, lint, format, and check for these plugins. deps still does not rerun on a source edit. User exclude still removes paths; include can pull one file out of a pruned directory or a declared output. Watch reruns on those newly covered files unless they are excluded or ignored. A no-op rerun after declared outputs exist should still hit. Users who relied on non-source files not busting the cache, or on test-only paths not firing build, will see more misses and more watch cycles.

Testing

The patches add or retarget tests. Presence in the diff is not proof they passed.

Hasher unit tests: non_source_files_change_the_key, user_exclude_removes_a_project_file_from_the_key, declared_outputs_do_not_change_the_key, tool_caches_do_not_change_the_key_even_when_not_gitignored, include_can_name_a_file_inside_a_root_output_dir, deps_still_hashes_venv_config_when_not_gitignored. Matcher: tool_cache_and_outputs_are_not_inputs_unless_named, user_exclude_wins_over_a_named_tool_cache_file. Plugin registry: inferred_languages_hash_every_project_file_except_deps. Rust plugin: test_cache_inputs_cover_tests_and_benches. Watch: user_exclude_file_is_noop, project_file_fires_build_and_test, declared_output_does_not_own_a_watch_target, plus ignore assertions for __pycache__ and .pytest_cache. Integration: tests/integration.rs::default_cache_keys_cover_files_targets_read (Go- and Node-shaped fixtures, no compiler; second run hit, excluded notes.txt stays hit, each named edit misses only that project's build, deps stays cached, no-op after outputs exist hits). tests/watch_tests.rs::watch_ignores_non_input_file_changes now excludes README.md instead of assuming it is outside the default globs.

Advisory pipeline outcomes, all reported succeeded with exit 0: format, lint, test, fix, reformat, verify-lint, verify-test, commit (No remediation files to commit). The fix excerpt names cargo fmt --all --check, cargo clippy --locked --workspace --all-targets --all-features -- -D warnings, and cargo test --locked --workspace --all-targets --all-features as succeeded, and says no files were edited. Check excerpts are truncated and do not name the new hasher, matcher, plugin, watch, integration, or remote-cache tests, so those logs do not independently confirm those functions ran. Author notes claim the canonical integration test and tests/cache_remote.rs passed after the fixture excludes; that is author-reported, not visible in the truncated check excerpts.

Follow-ups and known issues

  • Nested dist/, build/, target/, _build/, and deps/ are inputs on purpose. Projects that emit outputs there without declaring them, or without gitignoring them, can still miss on a no-op rerun.
  • pattern_names_cache_dir is a prefix check after stripping a leading **/, not a full glob match. Unusual include patterns may not force a walk of a pruned directory.
  • Hash cost and hit rate on large trees are not measured here. Generated files outside the prune list still need exclude, a declared output, or gitignore.
  • No runtime tracing of files a command actually reads; coverage is still "every non-ignored project file," which is broader than the toolchain's real read set.
  • Other language plugins are unchanged. Go dependency-edge behavior is unchanged.
  • Pipeline evidence is truncated, so a reviewer should re-run default_cache_keys_cover_files_targets_read and the hasher/matcher/plugin/watch tests if those names must be confirmed in CI.

@calvin-archastro
calvin-archastro requested a review from a team November 1, 2026 10:47
@calvin-archastro

calvin-archastro commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor Author

ArchDev is watching this pull request. State: watching (cycle 6 of 6 for head 129ea25).

when trigger decision result
2026-11-01T11:00:09.273Z failed ci enqueue job_f6dd266080f2e084aed38fd7 published b4a0225
2026-11-01T11:17:50.859Z verify head enqueue job_9a9d4bde63c73a1c8200d580 no change
2026-11-01T12:35:42.316Z merge conflict enqueue job_4025a802f921d433b9c9eb2b published abffbeb
2026-11-01T12:56:20.582Z verify head enqueue job_121d71a4865b9f58b2d051f6 no change
2026-11-01T16:42:51.066Z merge conflict enqueue job_256343f240ea900b063e4dc9 failed Pipeline step finalize-conflict failed: command exited with code 1
2026-11-01T17:06:22.911Z verify head enqueue job_c932a877afc7946852230348 no change

Paused by the no-autofix label or a draft; never resolves reviewer threads. Jobs: job_f6dd266080f2e084aed38fd7, job_9a9d4bde63c73a1c8200d580, job_4025a802f921d433b9c9eb2b, job_121d71a4865b9f58b2d051f6, job_256343f240ea900b063e4dc9, job_c932a877afc7946852230348.

@calvin-archastro
calvin-archastro force-pushed the factory/tsk_3ZcmMUQxEMT4HknMopt8Eo-f85a43d3-b633-480d-84fb- branch from b4a0225 to abffbeb Compare November 1, 2026 12:44
calvin-archastro and others added 3 commits November 1, 2026 09:43
- check Action: action/test/fixture/app/aster.toml excludes runs.txt,
which the command writes and the new **/* key hashed; action tests 15/15
pass (fixed)

Archdev-Job: job_f6dd266080f2e084aed38fd7
Co-Authored-By: ArchDev <archdev@archastro.ai>
Archdev-Model: anthropic/claude-opus-5.5
@calvin-archastro
calvin-archastro force-pushed the factory/tsk_3ZcmMUQxEMT4HknMopt8Eo-f85a43d3-b633-480d-84fb- branch from abffbeb to 129ea25 Compare November 1, 2026 16:55

This branch has not been deployed

No deployments
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.

1 participant