Skip to content

Commit 7a9ba9a

Browse files
committed
Merge subtree logistic.
2 parents 3911291 + 129876e commit 7a9ba9a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+532
-249
lines changed

.logistic/ci/Dockerfile.ci.ubuntu

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ RUN apt-get update && apt-get install --no-install-recommends --yes \
3030
apt-get clean
3131

3232
# Install Nix
33-
RUN curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install linux \
33+
RUN curl --proto '=https' --tlsv1.2 -sSf -L https://artifacts.nixos.org/experimental-installer | sh -s -- install linux \
3434
--extra-conf "sandbox = false" \
3535
--init none \
3636
--no-confirm

.logistic/ci/builders-scale

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ apt-get install --yes anacron
7373
cat > /etc/cron.hourly/docker-image-purge <<EOF
7474
#!/bin/sh
7575
76-
docker image ls -q --filter reference='registry.gitlab.routine.co/routine/*/ci' --filter until=36h | xargs -r docker rmi
77-
docker image ls -q --filter reference='registry.gitlab.routine.co/routine/*' --filter until=168h | xargs -r docker rmi
76+
docker image ls -q --filter reference='registry.gitlab.routine.co/routine/*/ci' --filter until=36h | xargs -r docker rmi -f
77+
docker image ls -q --filter reference='registry.gitlab.routine.co/routine/*' --filter until=168h | xargs -r docker rmi -f
7878
docker image prune -f
7979
docker system prune -f
8080
EOF

.logistic/ci/dune/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
(executable
22
(name dune_sak)
3-
(libraries cmdliner fmt opam-file-format sexplib stdio unix))
3+
(libraries base cmdliner fmt opam-file-format sexplib stdio unix))

.logistic/ci/dune/dune-project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
(package
66
(name dune-sak)
77
(allow_empty)
8-
(depends cmdliner fmt ocaml ocaml-index opam-file-format sexplib stdio))
8+
(depends base cmdliner fmt ocaml ocaml-index opam-file-format sexplib stdio))

.logistic/ci/dune/dune_sak.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ let parse path =
1111

1212
let pp_file = OpamPrinter.FullPos.format_opamfile
1313

14-
let path =
14+
let path_ =
1515
let realpath path =
1616
let open Stdlib.Filename in
1717
if is_relative path then concat (Stdlib.Sys.getcwd ()) path else path
1818
in
1919
Cmdliner.Arg.conv (Fn.compose Result.return realpath, Fmt.string)
2020

21-
let input = Cmdliner.Arg.(opt (some path) None & info [ "i"; "input" ])
21+
let input = Cmdliner.Arg.(opt (some path_) None & info [ "i"; "input" ])
2222

2323
let toolchain =
2424
Cmdliner.Arg.(

.logistic/claude/CLAUDE.md

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Team-Level Claude Code Instructions
2+
3+
This file contains development guidelines that apply across all projects in this directory.
4+
5+
## OCaml Type-Driven Development Workflow
6+
7+
**CRITICAL**: When working with OCaml modules that have signatures (`.mli` files), follow this workflow:
8+
9+
### 1. Signature First, Implementation Second
10+
11+
When adding new functions to a module with a `.mli`, ALWAYS add the signature to the `.mli` file FIRST:
12+
13+
- Define the function signature in the `.mli` file
14+
- Use stub implementations (`failwith "TODO: implement function_name"`) in the `.ml` file to make everything compile
15+
- Only implement the real functionality after the signature is correct everywhere
16+
- **Never** write implementations in `.ml` without first defining the signature in `.mli`
17+
18+
**Why:** The signature is the contract. Define the contract first, then fulfill it. This prevents type inference from guessing the wrong thing and makes refactoring much easier.
19+
20+
### 2. Getting Type Information
21+
22+
**FIRST: Use the ocaml-mcp tools to query type information**
23+
24+
When you need to understand what type something has:
25+
26+
- **DO:** Use `mcp__ocaml-mcp__ocaml_type_at_pos` to get the type at a specific location
27+
- This works after running `dune build` to ensure artifacts are up-to-date
28+
- Use `mcp__ocaml-mcp__ocaml_find_definition` to jump to where symbols are defined
29+
- Use `mcp__ocaml-mcp__dune_build_status` to check for compilation errors
30+
31+
**ONLY WHEN NEEDED: Add type annotations to localize errors**
32+
33+
When you encounter type errors you don't understand or need to narrow down where the issue is:
34+
35+
- **DO:** Add type annotations at the call site reflecting what you EXPECT the types to be
36+
- Use abstract types from module signatures (not their expansions)
37+
- Let the compiler show you exactly where your expectations differ from reality
38+
- Read the error message carefully - it tells you precisely what's wrong
39+
40+
- **DON'T:** Try to manually reason through complex type unification
41+
- Don't guess at fixes without understanding the actual type mismatch
42+
- Don't remove type annotations when they cause errors (they're revealing the problem!)
43+
44+
**Why:** The ocaml-mcp server gives you immediate access to type information from compiled artifacts. Type annotations are still useful as "checkpoints" to localize where type mismatches occur, but you should query the MCP server first to understand what types you're actually working with.
45+
46+
### 3. Iterate on Types with Stubs
47+
48+
The recommended workflow when designing new functions:
49+
50+
```ocaml
51+
(* Step 1: Add to .mli with your desired signature *)
52+
val my_function : input -> output
53+
54+
(* Step 2: Add stub to .ml *)
55+
let my_function input = failwith "TODO: implement my_function"
56+
57+
(* Step 3: Use it at call sites with type annotations *)
58+
let result : expected_type = my_function my_input
59+
60+
(* Step 4: If you get type errors, revise the signature in .mli *)
61+
val my_function : better_input -> better_output
62+
63+
(* Step 5: Once types are right everywhere, implement for real *)
64+
let my_function input = (* actual implementation *)
65+
```
66+
67+
**Why:** This workflow separates concerns - first get the API right, then implement it. It's much easier to change a `failwith` stub than to refactor a full implementation.
68+
69+
### 4. Query Types First, Annotate When Localizing Errors
70+
71+
**Preferred approach - Query the MCP server:**
72+
```ocaml
73+
let process_deltas deltas =
74+
(* First, use mcp__ocaml-mcp__ocaml_type_at_pos to understand what types you have *)
75+
let pattern = Map.Pattern.add __ ignore in
76+
match_list pattern deltas ()
77+
```
78+
79+
**When debugging type errors - Add annotations to localize:**
80+
```ocaml
81+
let process_deltas deltas =
82+
(* Add annotation to narrow down where the type error occurs *)
83+
let pattern : (Delta.t, unit, result) Pattern.t =
84+
Map.Pattern.add __ ignore
85+
in
86+
(* If this causes an error, you know the problem is in this binding *)
87+
match_list pattern deltas ()
88+
```
89+
90+
**Why:** Use the MCP server to understand types interactively without cluttering code. Add annotations strategically when you need to pinpoint exactly where a type mismatch occurs.
91+
92+
## OCaml Code Style Guidelines
93+
94+
### Sequencing Expressions
95+
- **Always use** `let () = a in b` instead of `a ; b` for sequencing
96+
- Makes intent explicit and avoids type inference issues
97+
98+
### Custom Let Operators
99+
- Prefer custom let operators for applicative/monadic patterns
100+
- Examples:
101+
- `let*` for monads
102+
- `let+` for functors/applicatives
103+
- `let*!` for `('a, 'err) Result.t Monad.t` (e.g., `Result.t Lwt.t`)
104+
- Makes control flow clear and reduces nesting
105+
106+
### Module Documentation
107+
- **Always add `.mli` files** for modules you create
108+
- Include docstrings for all public functions using `(** *)` style comments
109+
- Document parameters, return values, and any side effects
110+
111+
### Data Structure Design
112+
- **If a tuple has 3+ elements, use a record type instead**
113+
- This applies to both standalone values and variant constructors
114+
- Use inline record syntax for variants:
115+
```ocaml
116+
(* Bad - positional arguments are error-prone *)
117+
| Constructor of string * int * bool * float
118+
119+
(* Good - named fields are self-documenting *)
120+
| Constructor of {
121+
name : string;
122+
count : int;
123+
enabled : bool;
124+
threshold : float;
125+
}
126+
```
127+
128+
**Why:** Named fields make code self-documenting, easier to refactor (can add fields without breaking all call sites), and prevent subtle bugs from swapping arguments.
129+
130+
### Error Handling: Never Use `_exn` Functions
131+
132+
**DO NOT use `_exn` functions** (like `List.hd_exn`, `Map.of_sequence_exn`, `Option.value_exn`).
133+
134+
- Use `Result.t` or `Option.t` to propagate errors explicitly
135+
- Use `_reduce` variants with error logging when handling duplicates:
136+
```ocaml
137+
Map.of_sequence_reduce (module String) ~f:(fun first second ->
138+
Logs.err (fun m -> m "duplicate: %s" key);
139+
first)
140+
```
141+
- **Only exception**: When the code structure guarantees the precondition holds (rare)
142+
143+
**Why:** `_exn` functions crash at runtime. Explicit error handling makes failures visible in types.
144+
145+
## General Principles
146+
147+
### Work with Module Signatures
148+
149+
- When working with modules, always refer to the `.mli` file first
150+
- The abstract types defined in the interface are what you should use in annotations
151+
- Don't try to work with concrete implementation types unless absolutely necessary
152+
153+
### Let the Type System Help You
154+
155+
- OCaml's type system is one of its greatest strengths
156+
- Type errors are your friend - they prevent runtime bugs
157+
- The more precise your types, the more the compiler can help you
158+
- When refactoring, let type errors guide you to all the places that need changes
159+
160+
## Build Process
161+
162+
### Using Dune
163+
164+
- **ALWAYS** use `dune build` to build the project
165+
- **NEVER** run `dune clean` - rely on the incremental build process
166+
- **NEVER** try to remove `_build/.lock` manually
167+
- **NEVER** run `dune runtest` - use `dune build @runtest` instead since a build daemon is running and holds the lock
168+
- The build system has a running watch process that handles incremental compilation
169+
- Just call `dune build` and let it handle everything
170+
- After editing code, run `dune build @fmt || dune promote` to format it correctly and pass CI
171+
172+
**Why:** The project uses a persistent build watcher that maintains incremental build state. Cleaning or removing locks disrupts this process and wastes time.
173+
174+
## Summary
175+
176+
**The golden rule: Signature → Stub → Query Types (via MCP) → Implement**
177+
178+
This approach will save time, reduce bugs, and make code easier to maintain. Use `mcp__ocaml-mcp__ocaml_type_at_pos` to understand types interactively without cluttering code. Add type annotations strategically only when you need to localize where type errors occur.
179+
180+
**For setting up ocaml-mcp**, see `~/.claude/OCAML_MCP_SETUP.md` for detailed installation and configuration instructions.

0 commit comments

Comments
 (0)