just build--- builds debug and release Go binaries togo/build/debug/andgo/build/release/- Must run before integration tests if binaries are stale
- Codegen files must always be committed alongside the source changes that
triggered them. Files matching
*_tommy.go,*_string.go, or*_stringer.goare generated byjust build-go-generate. Usejust commit-codegen(fromgo/) to regenerate and commit in one step.
- All tests:
just test(unit + integration). Exit code 0 means all tests passed --- do not grep output for "not ok" or "fail" to find failures, as those substrings appear in passing test names (e.g.clean_fails_outside_workspace). - Unit tests only:
just test-go - Integration tests only:
just test-bats(builds first, generates fixtures, runs BATS) - Specific test files:
just test-bats-targets show.bats - Filter by tag:
just test-bats-tags migration
Always use just recipes from the repo root. Never run bats directly. The
root recipes set BATS_BIN_DIR, DODDER_VERSION, inject the binary via
--bin-dir, and ensure fixtures exist. Running from zz-tests_bats/ uses the
system dodder binary instead of the freshly built one.
Capture test output to a file so you don't need to re-run the suite just to
read failures: just test 2>&1 > /tmp/dodder-test.txt. Then use grep to find
failures and sed/head/tail to read details.
When a test fails: run ONLY the failing test file with
just test-bats-targets <file>.bats. Do NOT re-run the entire suite.
zz-tests_bats/lib/common.bash-- shared helpers, library loads, fixture value getterszz-tests_bats/current_version/*.bats-- current version's integration testszz-tests_bats/previous_versions/v14/-- frozen snapshot: fixtures + .fixtures.env + testszz-tests_bats/previous_versions/main.bats-- migration conformance test
Fixtures in zz-tests_bats/previous_versions/v*/ are committed test data.
Regenerate ONLY when: - Store version bumps (VCurrent changed in store_version/main.go) - Store format changes that alter persisted data - Test seed data changes (cat_yin, cat_yang, create_test_zettels)
Do NOT regenerate when: - CLI output format changes (update assertions, not fixtures) - Adding new tests - Refactoring helpers
just test-bats-update-fixtures- Review:
git diff -- zz-tests_bats/previous_versions/ - Commit fixtures + .fixtures.env together
just test-bats-snapshot-version(freeze current suite into previous_versions/vN/)- Bump VCurrent in
go/internal/alfa/store_version/main.go just test-bats-update-fixtures(generate new fixtures + .fixtures.env)- Update test assertions for new behavior
just test(verify everything passes)
- Always use the tightest possible assertion. Full exact
assert_outputmatching the complete output is the default.assert_output_unsortedfor non-deterministic line order.assert_output --regexpONLY when a value is truly dynamic (sandbox paths, timestamps). Never useassert_output --partial--- it hides regressions by ignoring unexpected extra output. If you find yourself reaching for--partial, use the two-pass strategy below to capture the full output and write an exact assertion. - Two-pass assertion strategy: When writing new assertions, first use
assert_output "WRONG"to intentionally fail and capture the actual output from the test runner. Then replace with the real assertion matching the captured output. This avoids guessing at output format. - Fixture-specific values (signatures, config SHA, type SHA) live in
previous_versions/$VERSION/.fixtures.env, auto-generated during fixture creation. Access via helpers:$(get_konfig_sha),$(get_type_blob_sha),$(get_fixture_type_sig). - Content-addressed blob hashes (blake2b256-...) ARE deterministic and can be hardcoded in assertions.
- Signatures (ed25519_sig-...) are NOT deterministic -- ALWAYS use
$(get_fixture_type_sig). - Fresh-store tests (
run_dodder_init_disable_age) generate a new key each run -- useassert_output --regexp -with! type@.*for signatures.
-
ALWAYS use
just test*recipes --- never runbats,go test, or fixture generation directly. The just recipes set BATS_BIN_DIR, DODDER_VERSION, inject the binary, and ensure fixtures exist. -
BATS fixture tests use
$(get_fixture_type_sig)for signatures (not deterministic). Fresh-store tests (run_dodder_init_disable_age) useassert_output --regexpwith! type@.*patterns instead. -
NEVER call
errors.Iswhen err might be EOF --- useerrors.IsEOF()guard first. The standarderrors.Isdoes not handle the custom EOF wrapping. -
When bumping store version, do NOT remove the old version's codec/gob support. Old versions must remain decodable for migration.
-
"Lock" has two meanings --- content locks (metadata on objects, managed by the lock command) vs filesystem mutexes (
LockSmithinenv_repo). Don't confuse them. -
Trailing whitespace matters in dodder output assertions. Use
xxdorcat -Ato debug invisible mismatches in BATS tests. -
Do not recreate existing formatters. When dodder already has a formatter (e.g.,
box_format.BoxTransacted,sku_fmtprinters), use it via dependency injection or direct import --- never hand-build the same output withfmt.Fprintf/strings.Builder. -
Adding or changing metadata fields requires binary codec updates (#38). Any field added to
objects.metadata,containedObject, orblobReferenceEntryis NOT automatically serialized. Without encoder/decoder support inindia/stream_index, the field will be populated during commit but silently lost on the next read from the store. Seego/internal/india/stream_index/CLAUDE.mdfor the 4-file checklist. -
Hyphence format requires a blank line between closing
---and blob body (#41). Without it, the parser silently drops the blob content, resulting in objects committed without a blob digest. This applies to.typefiles,.zettelfiles, and heredocs in BATS tests.
Query syntax: <predicate><sigil><genre>. Sigils from ids.Sigil: :
(latest), + (history), . (external/checked-out), ? (hidden/dormant).
Sigils can be combined (e.g. :. for latest + external).
- Default genre is zettels.
show :lists zettels.show '!md'finds zettels whose type is!md, NOT the!mdtype object itself. - Genre suffixes filter by genre:
:z(zettels),:t(types),:e(tags). The genre is part of the query term, not a separate argument. - To query a specific type object:
show '!img:t'--- the:tgenre suffix goes on the predicate. NOTshow :t '!img'(that's two separate terms). - To list all type objects:
show :t.
- Markdown files are formatted by pandoc on save (via lux filetype config). Pandoc converts GFM pipe tables to column-aligned plain text and fenced code blocks to indented blocks. Write markdown accordingly --- don't expect GFM table syntax to survive.
- "dodder: command not found" --- run
just buildfirst, or ensure you're in the nix devshell - BATS tests fail with stale fixtures --- run
just test-bats-update-fixtures, review diff, commit
Integration How to verify
pivy-agent ECDH Round-trip: encrypt blob, decrypt with real token ECDSA P256 signing Sign + verify with real key, not just one direction SSH agent protocol Connect to real agent, list + sign + verify age encryption Encrypt + decrypt round-trip WASM guest filters Build guest, load in host, execute filter