dreamteamer 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/dt-hook.sh +54 -0
- package/collections/collections.collection.yaml +9 -0
- package/collections/command-bindings.collection.yaml +1 -1
- package/collections/proofs.collection.yaml +96 -0
- package/collections/repos.collection.yaml +4 -3
- package/package.json +2 -2
- package/skills/using-dreamteamer/SKILL.md +16 -7
- package/skills/using-dreamteamer/references/before-you-build.md +16 -2
- package/skills/using-dreamteamer/references/collections.md +50 -9
- package/skills/using-dreamteamer/references/commands.md +16 -3
- package/skills/using-dreamteamer/references/data-modeling.md +2 -2
- package/skills/using-dreamteamer/references/exporting.md +53 -0
- package/skills/using-dreamteamer/references/proofs.md +435 -0
- package/skills/using-dreamteamer/references/skills.md +8 -0
- package/skills/using-dreamteamer/references/worktrees.md +235 -0
- package/src/checkout.js +666 -0
- package/src/cli.js +301 -40
- package/src/collections-cli.js +237 -63
- package/src/commit.js +1 -1
- package/src/compile.js +190 -15
- package/src/export-notebooklm.js +502 -0
- package/src/filter.js +4 -1
- package/src/harnesses.js +25 -10
- package/src/init.js +2 -0
- package/src/land.js +736 -0
- package/src/presentation.js +28 -1
- package/src/prove.js +1922 -0
- package/src/runtime.js +26 -0
- package/src/schema-ops.js +48 -25
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# worktrees — a second checkout of this workspace, and the one verb that brings its records home
|
|
2
|
+
|
|
3
|
+
A **linked worktree** is a second working directory on the same repository: its own files, its own
|
|
4
|
+
branch, one shared object store. A session can work in one without touching the tree you are looking
|
|
5
|
+
at. `dt install` makes such a checkout ready; `dt land` brings its records back.
|
|
6
|
+
|
|
7
|
+
The whole design turns on one asymmetry. Two sessions writing records in the SAME directory is
|
|
8
|
+
conflict-BLIND — the second `dt set` on a record silently wins and nobody is told. Two sessions in
|
|
9
|
+
two worktrees make that same collision a **visible landing failure**, and the tree you were not
|
|
10
|
+
looking at is left byte-identical while you decide.
|
|
11
|
+
|
|
12
|
+
| the question | read |
|
|
13
|
+
|---|---|
|
|
14
|
+
| what a worktree costs to make ready | making one ready |
|
|
15
|
+
| add, list, get, rm | the four verbs |
|
|
16
|
+
| bringing the records home | landing |
|
|
17
|
+
| it refused — why | the refusals |
|
|
18
|
+
| it stopped on a conflict | the merge policy |
|
|
19
|
+
| a harness that cuts worktrees for you | hooks |
|
|
20
|
+
| `npm` is not on the hook's PATH | the shim |
|
|
21
|
+
| the other four harnesses | per harness |
|
|
22
|
+
|
|
23
|
+
## making one ready
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
dt install # in the worktree — engine, .env, local assets, git modules, compile, post-step
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
A fresh worktree has no `node_modules`, no `.env`, no compiled runtime and no local assets. `dt
|
|
30
|
+
install` is idempotent and prints a board of what it found and what it did; run it again and every
|
|
31
|
+
line says *already*.
|
|
32
|
+
|
|
33
|
+
Three things it does that a bare `npm install` cannot:
|
|
34
|
+
|
|
35
|
+
- **`.env` is LINKED from the primary — but only when the worktree lies under the primary's root.**
|
|
36
|
+
A worktree elsewhere on the disk gets no credentials unless `--link-env` is passed for that run,
|
|
37
|
+
and the board says which capabilities are degraded without them. Credentials are not widened by
|
|
38
|
+
accident: a harness that keeps its worktrees under `~` gets an honest, credential-free checkout.
|
|
39
|
+
- **Declared local assets are linked, not copied.** `dreamteamer.local-assets` in a module's or the
|
|
40
|
+
workspace's `package.json` names gitignored heavy folders — model weights, browser profiles — and
|
|
41
|
+
the worktree gets a symlink to the primary's copy. A 1.5 GB download does not happen twice.
|
|
42
|
+
- **`dreamteamer.postinstall` runs LAST.** Deliberately not npm's `scripts.postinstall`: npm fires
|
|
43
|
+
that on every install, before the engine has compiled, and in contexts where a slow environment
|
|
44
|
+
step must not run at all.
|
|
45
|
+
|
|
46
|
+
⚠ **A worktree of a workspace running a SHADOWED engine inherits the shadow.** If
|
|
47
|
+
`node_modules/dreamteamer` or a `git_modules/<name>` entry is a symlink, `dt add worktrees` mirrors
|
|
48
|
+
that link into the new tree — so the copy compiles with the same compiler as the tree it was cut
|
|
49
|
+
from. Real clones under `git_modules/` are per-checkout working state and are restored by `install`,
|
|
50
|
+
never linked.
|
|
51
|
+
|
|
52
|
+
## the four verbs
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
dt add worktrees --name <n> [--path <dir>] [--base <ref>] [--temp]
|
|
56
|
+
dt list worktrees [--json]
|
|
57
|
+
dt get worktrees/<name|path> [--json]
|
|
58
|
+
dt rm worktrees/<name|path> [--force]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
A worktree is **observed, never stored** — every row comes from `git worktree list` plus two cheap
|
|
62
|
+
reads. There is no record to drift from the truth.
|
|
63
|
+
|
|
64
|
+
`add` cuts the tree on branch `worktree-<n>`, runs `install` in it, and prints its absolute path
|
|
65
|
+
**last**, which is what a creation hook echoes. `--temp` is the sandbox form: detached, under
|
|
66
|
+
`.worktrees/.tmp-<rand>/<n>` inside the primary root, for a caller that will remove it.
|
|
67
|
+
|
|
68
|
+
⚠ **`rm` REFUSES by default, and names why.** A worktree holds two things the primary cannot see:
|
|
69
|
+
records written but not committed, and commits not landed. `git worktree remove` knows about
|
|
70
|
+
neither. A detached worktree's commits are reachable from nothing but the HEAD about to be deleted,
|
|
71
|
+
so the guard measures orphanhood rather than branch-ahead-ness — and if that measurement cannot be
|
|
72
|
+
taken, it refuses rather than assuming zero.
|
|
73
|
+
|
|
74
|
+
## landing
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
dt land worktrees/<name|path> [--keep] [--dry-run] [--branch <n>] [--json]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Run it **from the primary**. It rebases a COPY of the worktree's branch onto the primary branch,
|
|
81
|
+
fast-forwards, recompiles, and retires the worktree. `--dry-run` prints the plan and mutates
|
|
82
|
+
nothing. `--keep` leaves the worktree standing on the landed tip.
|
|
83
|
+
|
|
84
|
+
What it prints when it works:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
✔ landed worktrees/w onto main: 1 commit(s)
|
|
88
|
+
notes: 1 record(s)
|
|
89
|
+
managed block regenerated: CLAUDE.md
|
|
90
|
+
primary recompiled
|
|
91
|
+
worktree and branch removed
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The order matters and each step exists for a measured reason:
|
|
95
|
+
|
|
96
|
+
1. **One lock**, owned by the engine, at `<common-dir>/dreamteamer-land.lock`. A lock whose process
|
|
97
|
+
is gone is reclaimed out loud; a live one is refused with its pid and age.
|
|
98
|
+
2. **A copy.** `land/<name>` is created from the worktree's branch and rebased in a throwaway
|
|
99
|
+
detached worktree. The branch you are standing on is never touched until the fast-forward
|
|
100
|
+
succeeds — which is what makes a failed landing leave every tree byte-identical.
|
|
101
|
+
3. **Recompile on the copy's tip**, then commit the regenerated harness blocks pathspec-scoped.
|
|
102
|
+
`.dreamteamer/` is gitignored, so a fresh checkout has no runtime and `check` would otherwise
|
|
103
|
+
answer "no compiled runtime" for every landing.
|
|
104
|
+
4. **`dt check` on the rebased tree.** A failure stops here: `land/<name>` is KEPT for inspection
|
|
105
|
+
and the message says the branch is rebased and NOT landed. (A compile *error* deletes the copy;
|
|
106
|
+
a check *failure* keeps it. Both are safe — the difference is whether there is anything to look
|
|
107
|
+
at.)
|
|
108
|
+
5. **Fast-forward**, asked of `git merge-base --is-ancestor` rather than of git's English. If the
|
|
109
|
+
primary moved underneath, the copy is re-rebased and retried, three times, then refused.
|
|
110
|
+
6. **`npm ci` in the primary** if the range changed `package-lock.json`, then **compile** there.
|
|
111
|
+
7. **Retire** — but only after re-measuring. See the warning below.
|
|
112
|
+
|
|
113
|
+
⚠ **A worktree that CHANGED during the landing is kept, not destroyed.** Cleanliness is measured
|
|
114
|
+
when the landing starts; a session inside that worktree can write a record while the rebase runs.
|
|
115
|
+
So the tree is measured again immediately before the destructive step, and if it changed it survives
|
|
116
|
+
with its own line: `worktree kept at <path> — it changed during the landing (<n> path(s)); land
|
|
117
|
+
again to pick them up`. Exit 0 — the landing itself succeeded. The window is milliseconds, not zero:
|
|
118
|
+
**a harness hook must never delete a dirty worktree on its own.**
|
|
119
|
+
|
|
120
|
+
`--json` carries `kept`, read from the disk after the work rather than inferred from the flags that
|
|
121
|
+
went in, so a script can tell a retired worktree from one that survived.
|
|
122
|
+
|
|
123
|
+
⚠ **`--keep` recompiles the kept worktree, and a compile is a write.** The tree is reset to what
|
|
124
|
+
landed and then compiled, so if that compile produces a block even one byte different from the one
|
|
125
|
+
just committed, the worktree is left with a modified tracked `CLAUDE.md` — and the NEXT `dt land` of
|
|
126
|
+
it refuses with `1 uncommitted change(s)`. That is an ordinary system write, not a fault: commit it
|
|
127
|
+
in the worktree, or land without `--keep`.
|
|
128
|
+
|
|
129
|
+
## the refusals
|
|
130
|
+
|
|
131
|
+
Each is printed under `✖ cannot land worktrees/<name>:` and each names its own fix:
|
|
132
|
+
|
|
133
|
+
| it says | it means |
|
|
134
|
+
|---|---|
|
|
135
|
+
| `it is the primary checkout` | you named the tree you are standing in |
|
|
136
|
+
| `it is DETACHED (no branch)` | give it one — the message prints both spellings, including `--branch <name>` |
|
|
137
|
+
| `<n> dirty record(s) — dt commit them first` | the worktree has records written but not published |
|
|
138
|
+
| `<n> uncommitted change(s)` | ordinary git dirt in the worktree |
|
|
139
|
+
| `branch <b> has an upstream (<u>)` | a pushed branch is not rebased; land it by hand |
|
|
140
|
+
| `the primary has <n> pending record write(s)` | ⚠ read from the primary's STORE, not from `git status` — a landing would sweep another session's unpublished records into it |
|
|
141
|
+
| `the primary has uncommitted changes to <n> file(s) this branch also touches` | resolve them first |
|
|
142
|
+
| `another land holds the lock (pid <p>, <age>)` | wait, or remove the lock if that process is gone |
|
|
143
|
+
| `nothing to land` | the branch is already on the primary branch |
|
|
144
|
+
|
|
145
|
+
`--branch <name>` must name the worktree it is applied to, and it never mutates under `--dry-run`
|
|
146
|
+
(the plan prints `would switch …` instead).
|
|
147
|
+
|
|
148
|
+
## the merge policy
|
|
149
|
+
|
|
150
|
+
On a conflict, every conflicted path is classified and the classes are treated differently:
|
|
151
|
+
|
|
152
|
+
| class | how it is known | what happens |
|
|
153
|
+
|---|---|---|
|
|
154
|
+
| **the generated instruction block** | the path is exactly `CLAUDE.md`, `AGENTS.md` or `GEMINI.md`, AND every conflict hunk lies entirely between the block's begin/end markers | the primary's block wins, the rebase continues, and the block is regenerated once at the end |
|
|
155
|
+
| **anything else** | — | `rebase --abort`; the paths are listed grouped by collection; exit 1; **both trees exactly as they were** |
|
|
156
|
+
|
|
157
|
+
⚠ **Matched by exact ROOT path, never by basename.** A nested `docs/CLAUDE.md` is a hand-written
|
|
158
|
+
file the compiler never regenerates; taking the primary's side of it would silently discard someone's
|
|
159
|
+
prose. And a hunk that touches the markers themselves, or lies outside the block, is an ordinary
|
|
160
|
+
conflict — the classifier errs toward surfacing.
|
|
161
|
+
|
|
162
|
+
There is no union merge. An append-only file is not append-only in practice: rows get corrected in
|
|
163
|
+
place, and two branches each appending would keep both. A log conflict is an ordinary conflict,
|
|
164
|
+
resolved by hand.
|
|
165
|
+
|
|
166
|
+
The Cursor rules file is a **whole-file generated output** with no block in it, so there is nothing
|
|
167
|
+
to take a side of; `init` gitignores `.cursor/` anyway. A workspace that un-ignores it gets an
|
|
168
|
+
ordinary conflict there and regenerates the file.
|
|
169
|
+
|
|
170
|
+
## hooks
|
|
171
|
+
|
|
172
|
+
A harness can cut a worktree, or start a session in one, and call the engine at that moment. Print
|
|
173
|
+
the configuration for this workspace:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
dt install --print-adapters
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Three entry points, each reading the harness's JSON payload from **stdin**:
|
|
180
|
+
|
|
181
|
+
- `dt install --hook` — a session started. Makes this checkout ready and, in a linked worktree,
|
|
182
|
+
ends the board with the line that tells the session how to finish: *this is worktree `<name>` of
|
|
183
|
+
`<primary>`; before you finish, `dt commit` your records and tell the operator to run `dt land
|
|
184
|
+
worktrees/<name>`.*
|
|
185
|
+
- `dt add worktrees --hook` — a worktree is being created. Takes the name from the payload, places
|
|
186
|
+
the tree under `.worktrees/<name>`, and prints the path last. It is a FORM: `--path`, `--base` and
|
|
187
|
+
`--temp` are refused with it, rather than accepted and ignored.
|
|
188
|
+
- `dt land --hook --dry-run` — a worktree is being removed. Reports whether it would land, and never
|
|
189
|
+
removes anything. `worktree_path` is required in the payload; a path that is not a registered
|
|
190
|
+
worktree is a quiet exit 0, since the harness may have unregistered it already.
|
|
191
|
+
|
|
192
|
+
⚠ **`--hook` refuses a terminal.** Reading stdin from a TTY blocks forever with no output, which in
|
|
193
|
+
a hook is indistinguishable from a hang; so it says `--hook reads the harness's JSON on stdin —
|
|
194
|
+
nothing is piped` and exits.
|
|
195
|
+
|
|
196
|
+
## the shim
|
|
197
|
+
|
|
198
|
+
⚠ **A hook's shell reads no startup file.** A hook runs as `sh -c` / `bash -c`, and neither reads a
|
|
199
|
+
profile — only an interactive or login shell does. So a hook line beginning with `npm` or `npx` can
|
|
200
|
+
fail with *command not found* in an environment where the same command works perfectly in the
|
|
201
|
+
operator's terminal, and a hook's stderr is nobody's problem. No profile edit can fix this; a profile
|
|
202
|
+
is per-machine, per-shell and outside the workspace, which is the opposite of what a committed
|
|
203
|
+
adapter needs to be.
|
|
204
|
+
|
|
205
|
+
The engine therefore ships `bin/dt-hook.sh`, a POSIX shell shim that resolves node absolutely:
|
|
206
|
+
`$DREAMTEAMER_NODE`, then `command -v node`, then the highest `~/.nvm/versions/node/*/bin/node`,
|
|
207
|
+
then `/opt/homebrew/bin`, then `/usr/local/bin` — and when none resolves it fails LOUDLY onto
|
|
208
|
+
stdout, which the harness adds to the session's context, rather than dying silently.
|
|
209
|
+
|
|
210
|
+
⚠ **Resolving `npm` beside node is not enough.** npm's own shebang is `#!/usr/bin/env node`, so an
|
|
211
|
+
absolutely-resolved npm still dies with `env: node: No such file or directory` at exit 127 when the
|
|
212
|
+
child's PATH has no node on it. The engine puts node's own directory at the front of the PATH it
|
|
213
|
+
hands to any child that needs npm. Measured on a real worktree, not reasoned.
|
|
214
|
+
|
|
215
|
+
## per harness
|
|
216
|
+
|
|
217
|
+
Only Claude Code's adapter ships today. The rest are documented as they are measured — the
|
|
218
|
+
`SessionStart` contract is the same shim line for all of them, and the two things that stay
|
|
219
|
+
per-harness are the **per-directory trust prompt** and **removal**, which only some harnesses hook
|
|
220
|
+
at all.
|
|
221
|
+
|
|
222
|
+
| harness | creation | session start | removal |
|
|
223
|
+
|---|---|---|---|
|
|
224
|
+
| Claude Code | `WorktreeCreate` — replaces git's own logic; your hook cuts the tree and prints its path | `SessionStart`, no matcher, `cwd` in the payload | `WorktreeRemove` — dry-run only; never delete a dirty worktree from a hook |
|
|
225
|
+
| Codex · Gemini CLI · Cursor · pi | not yet shipped | not yet shipped | not hooked, or not documented |
|
|
226
|
+
|
|
227
|
+
## common mistakes
|
|
228
|
+
|
|
229
|
+
| mistake | what happens |
|
|
230
|
+
|---|---|
|
|
231
|
+
| running `dt land` from inside the worktree being landed | works, but the primary is where the verb belongs — it is the tree being changed |
|
|
232
|
+
| deleting a worktree with `git worktree remove` | git checks a dirty tree and stops there; it knows nothing about unpublished records or orphaned commits. `dt rm worktrees/<n>` does |
|
|
233
|
+
| expecting a sandbox to see uncommitted work | a worktree is cut from HEAD. What is not committed is not there — the engine-ships-first rule in miniature |
|
|
234
|
+
| putting `npm` at the front of a hook line | works in a terminal, fails in the hook. Use `dt install --print-adapters` and paste what it gives you |
|
|
235
|
+
| landing a pushed branch | refused. Rebasing a branch someone else may have is not the engine's call |
|