···11+# Used by "mix format"
22+[
33+ inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
44+ import_deps: [:typedstruct]
55+]
+26
.gitignore
···11+# The directory Mix will write compiled artifacts to.
22+/_build/
33+44+# If you run "mix test --cover", coverage assets end up here.
55+/cover/
66+77+# The directory Mix downloads your dependencies sources to.
88+/deps/
99+1010+# Where third-party dependencies like ExDoc output generated docs.
1111+/doc/
1212+1313+# If the VM crashes, it generates a dump, let's ignore it too.
1414+erl_crash.dump
1515+1616+# Also ignore archive artifacts (built via "mix archive.build").
1717+*.ez
1818+1919+# Ignore package tarball (built via "mix hex.build").
2020+mst-*.tar
2121+2222+# Temporary files, for example, from tests.
2323+/tmp/
2424+2525+.direnv
2626+.envrc
+193
AGENTS.md
···11+# AGENTS.md
22+33+Guidance for agentic coding assistants working in this repository.
44+55+## Project Overview
66+77+`elixir-mst` is an Elixir library implementing AT Protocol-flavoured Merkle Search Trees (MST).
88+99+## Build / Lint / Test Commands
1010+1111+```bash
1212+# Compile
1313+mix compile
1414+1515+# Format (run before committing)
1616+mix format
1717+1818+# Check formatting without writing
1919+mix format --check-formatted
2020+2121+# Lint
2222+mix credo
2323+2424+# Run all tests
2525+mix test
2626+2727+# Run a single test file
2828+mix test test/mst/foo_test.exs
2929+3030+# Run a single test by line number
3131+mix test test/mst/foo_test.exs:42
3232+3333+# Run doctests only
3434+mix test --only doctest
3535+3636+# Generate docs
3737+mix docs
3838+```
3939+4040+No custom Mix aliases are defined. There is no CI pipeline — validate locally.
4141+4242+## Project Structure
4343+4444+TODO
4545+4646+## Code Style
4747+4848+### Module Naming
4949+5050+- Domain acronyms are all-caps: `MST`.
5151+- Sub-modules follow `Parent.Role`.
5252+- Module file path mirrors module name exactly.
5353+5454+### Structs
5555+5656+Use `TypedStruct` with `enforce: true` for all structs. Every field must be
5757+typed. Use `default:` only where a sensible zero value exists.
5858+5959+```elixir
6060+typedstruct enforce: true do
6161+ field :version, pos_integer(), default: 1
6262+ field :roots, list(CID.t()), default: []
6363+ field :blocks, %{CID.t() => binary()}, default: %{}
6464+end
6565+```
6666+6767+### Typespecs
6868+6969+- Every public function must have `@spec`.
7070+- Every private function should have `@spec` where non-trivial.
7171+- Define named error type aliases at the top of each module, then reference them
7272+ in `@spec` annotations:
7373+7474+```elixir
7575+@type header_error() :: {:error, :header, atom()}
7676+@type block_error() :: {:error, :block, atom()}
7777+@type decode_error() :: header_error() | block_error()
7878+```
7979+8080+### Error Handling
8181+8282+Consistent tagged-tuple convention — do not deviate:
8383+8484+- Success: `{:ok, value}`
8585+- Simple error: `{:error, reason}`
8686+- Scoped error (CAR layer): `{:error, :scope, :reason}` — e.g.
8787+ `{:error, :header, :missing_roots}`, `{:error, :block, :cid_mismatch}`
8888+8989+Use `with` chains for multi-step fallible operations; use `else` to remap errors
9090+when needed. Use `Enum.reduce_while` for fallible iteration — halt on first
9191+error.
9292+9393+Bang variants (`parse_header!`, `validate_block!`) are only acceptable inside
9494+`StreamDecoder`-style modules where the documented contract is raise-on-error.
9595+Do not mix raise and tuple-return styles in the same module without explicit
9696+documentation of the contract.
9797+9898+### Pattern Matching and Guards
9999+100100+- Prefer multi-clause function heads for exhaustive dispatch over nested
101101+ conditionals.
102102+- Use bit-syntax binary pattern matching for low-level binary parsing.
103103+- Pair guards with pattern matches for validation constraints:
104104+105105+```elixir
106106+when hash_size == @hash_size and byte_size(digest) == @hash_size
107107+```
108108+109109+### Module Attributes for Constants
110110+111111+Use `@` module attributes for all magic numbers and codec identifiers. Group
112112+them at the top of the module, after `@moduledoc`.
113113+114114+```elixir
115115+@codec_raw 0x55
116116+@codec_drisl 0x71
117117+@hash_sha256 0x12
118118+@hash_size 32
119119+```
120120+121121+### Pipes
122122+123123+Use pipes where they read naturally. Do not force them. Prefer `with` over pipes
124124+for error-prone chains. The primary pipe use-case is stream pipelines:
125125+126126+```elixir
127127+chunk_stream
128128+|> StreamDecoder.decode_stream(opts)
129129+|> Stream.map(&transform/1)
130130+```
131131+132132+### Documentation
133133+134134+- Every public module must have `@moduledoc` with a prose description and, where
135135+ applicable, a `Spec: <url>` line linking to the relevant spec.
136136+- Every public function must have `@doc` with:
137137+ - A prose description. Keep it high-level — do not repeat details already
138138+ covered by the spec (e.g. byte-level encoding rules, magic constants,
139139+ algorithm steps).
140140+ - An `## Options` section if the function accepts an options keyword list.
141141+ - An `## Examples` section with `iex>` doctests for the happy path and at
142142+ least one error case.
143143+- Use dashes (`-`) for all Markdown lists in `@moduledoc` and `@doc`. Never use
144144+ asterisks (`*`).
145145+146146+### Protocol Implementations
147147+148148+Implement `String.Chars` and `Inspect` for domain structs at the **bottom** of
149149+the file, outside the main module block — see `cid.ex` for the pattern.
150150+151151+### Streaming
152152+153153+Use `Stream.transform/4` with explicit start/reduce/after arities (not the
154154+3-arity shorthand) for stateful streaming parsers.
155155+156156+### Section Separators
157157+158158+Use `# ---...---` comment separators (78 dashes) to group related functions
159159+visually, consistent with existing source files.
160160+161161+## Test Style
162162+163163+- All test modules: `use ExUnit.Case, async: true`.
164164+- Pull doctests in at the top: `doctest MST.ModuleName`.
165165+- Use `describe/test` blocks — one `describe` per public function or logical
166166+ group.
167167+- Shared fixtures: define as `@` module attributes or `defp` helpers at the top
168168+ of the test module with a brief comment on their purpose.
169169+- Assertions use pattern matching: `assert {:ok, _} = ...`, not
170170+ `{:ok, val} = ...; assert val == ...`.
171171+- For stream decoder raise tests:
172172+ `assert_raise RuntimeError, ~r/pattern/, fn -> ... end`.
173173+- Do not couple decoder tests to encoder correctness — construct raw binaries
174174+ directly in test helpers when testing a decoder in isolation.
175175+- Test file paths must mirror `lib/` paths exactly.
176176+177177+## Dependencies
178178+179179+| Dep | Purpose |
180180+| -------------- | --------------------------------- |
181181+| `:dasl` | DASL primitives (CID, DRISL, CAR) |
182182+| `:typedstruct` | Typed struct DSL |
183183+| `:ex_doc` | Doc generation (dev only) |
184184+| `:credo` | Static analysis (dev + test) |
185185+186186+No Dialyzer setup. No property-based testing. Do not add new dependencies
187187+without discussion — the dep surface is intentionally minimal.
188188+189189+## Formatter
190190+191191+`.formatter.exs` imports `:typedstruct` so `typedstruct do ... end` blocks
192192+format correctly. Default line length (98) applies. Always run `mix format`
193193+before committing.
+21
README.md
···11+# MST
22+33+**TODO: Add description**
44+55+## Installation
66+77+If [available in Hex](https://hex.pm/docs/publish), the package can be installed
88+by adding `mst` to your list of dependencies in `mix.exs`:
99+1010+```elixir
1111+def deps do
1212+ [
1313+ {:mst, "~> 0.1.0"}
1414+ ]
1515+end
1616+```
1717+1818+Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
1919+and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
2020+be found at <https://hexdocs.pm/mst>.
2121+