- This repo exists to give agents durable memory while working with *Unison/UCM.
- Unison code is not primarily stored as files; Unison sketches are created in ephemeral .u files which can be typechecked, and once they typecheck, can be added to the "unison codebase" aka "codebase" which is managed by UCM
- you Interact with UCM via the unison MCP
- therefore, do not treat
.ufiles as durable source-of-truth. - Use beads when possible so workflow context is injected about the specific task being worked on
- Try to keep tasks in beads small
- Assume that any library a project depends on is also in UCM. You should aggressively suggest improvements to other projects by creating issues in their beads database.
- For any project we work with in UCM we will on demand create a
projects/<slug>/' directory using./bin/add-project `. This will create the directory structure, initialize a beads database, and create the initial PROJECT.md file.
- You must operate inside exactly one project at a time:
projects/<slug>/ - The authoritative slug is the contents of
projects/<slug>/PROJECT.md. - Before calling any UCM/MCP command (typecheck/run/etc), explicitly state:
- Active project slug
- Working directory you are using
- Each project directory is its own Beads namespace, initialized as:
bd init --prefix <slug> - Always run
bdcommands from insideprojects/<slug>/.
Location: /home/stew/devel/unison-src/ (repo root)
Prefix: global
Purpose: Cross-cutting issues that affect multiple projects or the tooling itself
When to use global:
- Bugs/issues with MCP tools (e.g.,
global-c4z: mcp__unison__update-definitions parsing error) - Infrastructure problems (build scripts, repo structure, CI/CD)
- Tooling bugs (ucm, bd, Claude Code integration issues)
- Documentation improvements that span projects
- Cross-project dependencies or blockers
Referencing global tasks from projects: When a project task is blocked by a global issue, reference it:
bd update <project-task-id> --notes "Blocked by: global-<hash>"Always run bd from the correct directory:
- Project tasks:
cd projects/<slug>first - Global tasks:
cd /home/stew/devel/unison-src(repo root)
Common operations:
bd ready # Find unblocked tasks
bd show <id> # View task details
bd close <id> --reason <reason> # Mark task done
bd update <id> --notes "short note here"Creating tasks with complex notes: Write to a temp file first, then:
bd create --title "Title" --type task --priority P2 --labels "a,b,c" --notes "$(cat /tmp/notes.md)"End of session workflow:
git add <changed-files> # Stage beads + docs (NOT scratch/ or WORK/)
git commit -m "message"
git pull --rebase
bd sync
git push
git status # MUST show "up to date with origin"What NOT to commit: scratch/, WORK/, *.sock* files
cd projects/<slug>- Use Beads for work discovery:
bd readyto find unblocked tasksbd show <id>to inspect
- When you pick a task, immediately "claim" it
bd update <id> --status in_progress, - Record your workspace path in the task notes.
- Never edit scratch in a shared location.
- Each task gets a workspace under:
projects/<slug>/WORK/<issue-id>/agent-<agent>/Create it with:./bin/mkws <slug> <issue-id> <agent-name> - Anything in
WORK/is volatile and gitignored. - If a workspace has a
LOCKand it's not yours, do not edit files there.
If you discover a blocking issue:
If the blocker is about:
- MCP tools, UCM, bd, or Claude Code integration
- Build scripts, repo structure, or CI/CD
- Documentation that spans multiple projects
cd /home/stew/devel/unison-src(repo root)bd create --title "..." --type bug --priority P1 ...- Update your project task:
bd update <id> --notes "Blocked by: global-<hash>"
If the blocker is in a different Unison project:
- Leave your current workspace unchanged.
- Switch to the other project directory and create a new Beads task there.
- Update your original task to reference the blocker by ID (
blocked-by: otherproj-...). - Do not move tasks between projects; link them by ID.
When you make progress that should persist across sessions:
- Add a short entry to
LOG.md - If it's a design choice, add an ADR in
DECISIONS/ - If it's a reproducible UCM action, write a small transcript in
TRANSCRIPTS/(include commands/run steps; keep it short and replayable)
Do not commit WORK/ or top-level scratch/.
In this repo, we only code in Unison.
Unison has no typeclasses, no lazy evaluation by default, and fundamentally different syntax from Haskell/Elm
- No pattern matching on left of
=(usecasesormatch ... with) - No
letorwherekeywords (these don't exist in Unison) - No building lists in reverse then calling
List.reverse(use:+to append directly) - No multi-argument lambdas as
x -> y -> ...(usex y -> ...) - No record field access as
record.field(useRecord.field record) - No
'for thunks in function bodies (use'in types,doin bodies) - No non-tail-recursive list functions (always use accumulator pattern)
- No
None/Somewithout qualification (it'sOptional.None/Optional.Some) - Record style types are constructed with a constructor function naed after the type, not with
{ }. sorecord = RecordType.RecordType field1 field1notrecord = { field1 = field1 }
Read the relevant guide based on your task:
-
Before writing any unison code -> Read
unison-language-guide.md- Core syntax, lists, ADTs, pattern matching, blocks, testing
-
Writing concurrent/distributed code? -> Read
unison-concurrency-guide.md- Remote ability, forkAt, promises, refs, race, timeout, structured concurrency
-
Writing documentation blocks? -> Read
unison-documentation-guide.md- {{ }} doc syntax, examples, special formatting
-
Writing ability handlers? -> Read
unison-abilities-guide.md- Custom abilities, handler patterns, effect management
-
Working with Unison Cloud? -> Read
unison-cloud-guide.md- Deploying services, daemons, databases, storage, blobs, environments, logging
-
Need authoritative verification? -> Use MCP via
unison-context.md- Query @unison/website docs for edge cases
- Pattern matching:
cases [] -> ... | x +: xs -> ...ORmatch expr with. When pattern matching with a constructor, use the type name and the constructor name together, e.g.,MyType.ConstructorName x y -> ...not justConstructorName x y -> ... - Looping: Tail recursion with accumulator parameter
- Multi-arg lambdas:
(x y -> x + y)NOT(x -> y -> x + y) - Thunks: Type:
'{IO} (), Body:do printLine "hello" - Records: Generated accessor functions, NOT dot syntax
- List append: Use
:+to append to end (O(1)),+:to prepend (O(1)) - Tuple access:
Tuple.fst tup,Tuple.snd tupor pattern match withcases (x, y) -> ...
- When adding new functions, add a doc block if it is a public entrypoint (see
unison-documentation-guide.md) - The Unison MCP server is the source of truth for code
.ufiles in this directory are sketches only, not authoritative- Prefer reading adjacent Unison code for patterns instead of guessing
Use mcp__unison__update-definitions to add or update code in the codebase. It will typecheck and update in one step.
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, or other documented Audits
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase bd sync git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session, and suggest a prompt referencing a specific issue ID for the next session.
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds