Development

Changing a dependency breaks three environments

This is the single most common way to break a working checkout, and nothing detects it automatically — the failure surfaces as a precompilation error the next time you run tryit:

ERROR: Package TryIt does not have TOML in its dependencies

Three environments each carry their own Manifest.toml, and none of them is updated by pulling a commit that changes dependencies:

EnvironmentWhat breaks when it goes stale
.Pkg.test()
docsdocs/make.jl
testRunning the test env directly. Pkg.test() resolves a fresh sandbox, so this hides until you use --project=test
@TryIttryit itself — the shell function runs against it

Moving the repository breaks all four at once. A develop install records an absolute path, so relocating the checkout leaves every manifest pointing at a directory that no longer exists. The same script fixes it — it re-runs Pkg.develop against the current location before resolving.

@TryIt is the one that bites, because it breaks the tool rather than the build, and it is the one a contributor is least likely to think of.

After adding, removing or bumping any dependency:

./bin/resolve-envs.sh

That resolves all three and reports which failed. Doing it by hand means three separate Pkg.resolve() invocations, and forgetting the third leaves tryit broken while every test passes.

Gates

Everything below runs in CI; run it locally before pushing.

julia --startup-file=no --project=.    -e 'using Pkg; Pkg.test()'
julia --startup-file=no --project=docs docs/make.jl
julia --startup-file=no -e 'using Pkg; Pkg.activate(temp=true);
    Pkg.add("JuliaFormatter"); using JuliaFormatter; format(".")'

Check the exit status, not the output. A pipeline like … | tail -5 or a trailing && echo ok will report success for a command that failed — that has already produced two false "passing" readings in this project's history, once masking a build error and once masking a docs failure.

Guards worth knowing about

Three tests exist to catch drift rather than defects, and each has caught something real:

  • test/spec/test_traceability.jl — every requirement ID in spec.md must appear in src/ or test/, or carry a written exemption. It found twelve capabilities that had been built with no written requirement.
  • test/spec/test_docs_sync.jl — every documented Ctrl+X must be a key the selector handles and must appear in the ? overlay; the documented default tries root must match the code.
  • test/docs/test_embedded_docs.jl — the manual embedded for the in-app browser must match docs/src. It caught the embedding going stale because Julia does not invalidate a precompiled image for a file that is merely read; include_dependency fixed it.

When one of these fails, the fix is usually the documentation or the spec, not the test.

Two globals that leak between tests

Both are correct in production and wrong under a test runner, which shares one process across test items:

  • _APP_MODE — a Ref that _app_main sets and never clears. An app process only ever runs as an app, so nothing resets it; but one test item leaving it true makes emit_shell_init emit the compiled-app form in a later item, pointing at a binary that does not exist. Pin and restore it around anything that renders the shell snippet.
  • ENVwithenv mutates the process-global environment. With test items running concurrently, one item's cleanup can clear another's override. Anything that writes (config, files) must take its path as an argument rather than reading it from ENV.

Spawning julia from a test

Pkg.test sets JULIA_LOAD_PATH for its sandbox, and any julia a test spawns inherits it — leaving the child unable to resolve the package's own dependencies, with an error that looks like a missing dependency rather than an inherited one. Clear it:

withenv("JULIA_LOAD_PATH" => nothing, "JULIA_PROJECT" => nothing) do
    run(...)
end

Adding a key binding

The selector owns its whole key map (default_bindings=false), so a new binding needs four things, and the guards enforce the last two:

  1. A branch in Tachikoma.update! in src/selector.jl.
  2. A mapping in press_keys! in test/tachikoma_helpers.jl — without it the keystroke silently lands in the filter and the test passes for the wrong reason.
  3. A row in HELP_KEYS, which is the ? overlay.
  4. A row in the key-binding table in docs/src/interface.md.