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
package/bin/dt-hook.sh
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# dt-hook.sh — run the engine from a harness hook, where there is no PATH to speak of.
|
|
3
|
+
#
|
|
4
|
+
# ⚠ THIS FILE EXISTS BECAUSE OF A MEASUREMENT, not a worry. A harness hook is `sh -c`, and `sh`
|
|
5
|
+
# reads NO startup file — not .zshenv, not .bash_profile, not .bashrc. On the machine where the
|
|
6
|
+
# first live worktree spawn was watched:
|
|
7
|
+
#
|
|
8
|
+
# env -i zsh -c 'command -v node' -> ~/.nvm/versions/node/<v>/bin/node
|
|
9
|
+
# env -i sh -c 'command -v node' -> NOT FOUND
|
|
10
|
+
#
|
|
11
|
+
# So a hook line beginning with a bare `npm`, `npx` or `node` fails, and fails SILENTLY: a fresh
|
|
12
|
+
# worktree has no node_modules either, so there is nothing to fall back to, and a hook's stderr goes
|
|
13
|
+
# to a log the session never reads. A shell-profile fix cannot reach this (`bash -c` and `sh -c`
|
|
14
|
+
# were both measured failing after one was applied) and would not travel with the workspace anyway.
|
|
15
|
+
# Hence an absolute resolution, here, in the one language a hook is guaranteed to have.
|
|
16
|
+
#
|
|
17
|
+
# sh /path/to/dt-hook.sh <verb> [args...]
|
|
18
|
+
#
|
|
19
|
+
# Resolution order: $DREAMTEAMER_NODE · node on PATH · the highest ~/.nvm install · /opt/homebrew ·
|
|
20
|
+
# /usr/local. The last two are where the two common non-nvm installers put it.
|
|
21
|
+
set -eu
|
|
22
|
+
|
|
23
|
+
node=""
|
|
24
|
+
if [ -n "${DREAMTEAMER_NODE:-}" ] && [ -x "${DREAMTEAMER_NODE}" ]; then
|
|
25
|
+
node="${DREAMTEAMER_NODE}"
|
|
26
|
+
elif command -v node >/dev/null 2>&1; then
|
|
27
|
+
node="$(command -v node)"
|
|
28
|
+
else
|
|
29
|
+
# `sort -V`, never a plain sort: v9.9.9 sorts AFTER v10.10.0 lexicographically, which would hand
|
|
30
|
+
# every hook the oldest install on the disk. A glob that matches nothing expands to itself, so
|
|
31
|
+
# the whole pipeline is allowed to come back empty rather than being trusted — and EVERY stage
|
|
32
|
+
# of it is silenced, because `-V` is not in POSIX and a sort that lacks it would otherwise write
|
|
33
|
+
# a usage error into the one stream a hook must leave clean.
|
|
34
|
+
nvm=""
|
|
35
|
+
if [ -n "${HOME:-}" ]; then
|
|
36
|
+
nvm="$(ls -d "${HOME}"/.nvm/versions/node/*/bin/node 2>/dev/null | sort -V 2>/dev/null | tail -1 2>/dev/null)" || nvm=""
|
|
37
|
+
fi
|
|
38
|
+
for candidate in "${nvm}" /opt/homebrew/bin/node /usr/local/bin/node; do
|
|
39
|
+
if [ -n "${candidate}" ] && [ -x "${candidate}" ]; then
|
|
40
|
+
node="${candidate}"
|
|
41
|
+
break
|
|
42
|
+
fi
|
|
43
|
+
done
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# ⚠ STDOUT, NOT STDERR, and that is the whole point of failing loudly here: a hook's stdout is added
|
|
47
|
+
# to the session's context, so this line is read by the agent that is about to work in a checkout
|
|
48
|
+
# nothing has made ready. Its stderr is not.
|
|
49
|
+
if [ -z "${node}" ]; then
|
|
50
|
+
echo "✖ dreamteamer hook: node not found — set DREAMTEAMER_NODE=/path/to/node in the harness environment, or install node under ~/.nvm, /opt/homebrew or /usr/local"
|
|
51
|
+
exit 1
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
exec "${node}" "$(dirname "$0")/dreamteamer.js" "$@"
|
|
@@ -120,6 +120,15 @@ schema:
|
|
|
120
120
|
opened on its own. It is stated here as DATA precisely so the record layer never has to
|
|
121
121
|
learn what a module is; a bare string list rather than `x-reference: collections`, because
|
|
122
122
|
the whole point is that the target is absent.
|
|
123
|
+
sensitive:
|
|
124
|
+
type: boolean
|
|
125
|
+
description: >-
|
|
126
|
+
This collection's records must not leave the workspace through an export — `dt export
|
|
127
|
+
<target>` writes none of them and names the omission in what it does write (the schema
|
|
128
|
+
source, the persona), so a reader knows the gap is deliberate. Set with `dt set
|
|
129
|
+
collections/<c> sensitive=true`. For ONE field rather than the collection, mark the field:
|
|
130
|
+
`x-sensitive: true` on its property (`dt add-field … --sensitive`). Nothing is inferred
|
|
131
|
+
from a name — the mark is the decision.
|
|
123
132
|
module:
|
|
124
133
|
type: string
|
|
125
134
|
description: >-
|
|
@@ -19,7 +19,7 @@ schema:
|
|
|
19
19
|
collection:
|
|
20
20
|
type: string
|
|
21
21
|
x-reference: collections
|
|
22
|
-
description: The collection the command applies TO — which is what makes `dt
|
|
22
|
+
description: The collection the command applies TO — which is what makes `dt next <ref>` able to answer "what can I do with this record".
|
|
23
23
|
target:
|
|
24
24
|
type: string
|
|
25
25
|
enum:
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
name: proofs
|
|
2
|
+
storage:
|
|
3
|
+
path: proofs
|
|
4
|
+
codec: yaml
|
|
5
|
+
shape: file
|
|
6
|
+
suffix: proof
|
|
7
|
+
id:
|
|
8
|
+
generate: '{{ name | slug }}'
|
|
9
|
+
schema:
|
|
10
|
+
type: object
|
|
11
|
+
required:
|
|
12
|
+
- name
|
|
13
|
+
- about
|
|
14
|
+
- kind
|
|
15
|
+
properties:
|
|
16
|
+
name:
|
|
17
|
+
type: string
|
|
18
|
+
description: The proof id — must equal the filename.
|
|
19
|
+
about:
|
|
20
|
+
type: array
|
|
21
|
+
items:
|
|
22
|
+
type: string
|
|
23
|
+
description: What this proof is ABOUT — one or more artifact refs it proves something about. An artifact ref is `skills/<id>` · `commands/<id>` · `command-bindings/<id>` · `<module-id>/bin/<file>`.
|
|
24
|
+
kind:
|
|
25
|
+
type: string
|
|
26
|
+
enum:
|
|
27
|
+
- gate
|
|
28
|
+
- live
|
|
29
|
+
description: '`gate` — a static check with no fixture record; `live` — runs against a real record and is judged by `expect`.'
|
|
30
|
+
mode:
|
|
31
|
+
type: string
|
|
32
|
+
enum:
|
|
33
|
+
- readonly
|
|
34
|
+
- writes
|
|
35
|
+
description: Whether a `live` proof only reads the workspace or actually writes to it. Required when kind is live, forbidden on a gate.
|
|
36
|
+
description:
|
|
37
|
+
type: string
|
|
38
|
+
description: What this proof asserts, in one line.
|
|
39
|
+
external:
|
|
40
|
+
type: boolean
|
|
41
|
+
description: Excludes this proof from a bare `--all` run — it needs a network call, a credential or a mount that is not always present. `--external` still runs it.
|
|
42
|
+
requires:
|
|
43
|
+
type: object
|
|
44
|
+
description: What must already be present for this proof to be runnable at all — missing ones make it UNAVAILABLE rather than FAIL.
|
|
45
|
+
properties:
|
|
46
|
+
env:
|
|
47
|
+
type: array
|
|
48
|
+
items:
|
|
49
|
+
type: string
|
|
50
|
+
description: '.env keys (names only, never values) that must be set.'
|
|
51
|
+
bin:
|
|
52
|
+
type: array
|
|
53
|
+
items:
|
|
54
|
+
type: string
|
|
55
|
+
description: Binaries that must be on PATH.
|
|
56
|
+
given:
|
|
57
|
+
type: object
|
|
58
|
+
description: The record a `live` proof runs against — either a live filter over a collection or a checked-in fixture. Exactly one of `where`/`fixture`.
|
|
59
|
+
properties:
|
|
60
|
+
collection:
|
|
61
|
+
type: string
|
|
62
|
+
description: A collection NAME (not a reference) the record is picked from — e.g. `notes`, never `collections/notes`.
|
|
63
|
+
where:
|
|
64
|
+
type: object
|
|
65
|
+
description: 'A filter narrowing which record is picked — the ordinary filter grammar, one reference hop deep. ⚠ It may NOT use `{record}`: the given is what PICKS the record, so nothing substitutes here and compile refuses the literal.'
|
|
66
|
+
pick:
|
|
67
|
+
type: string
|
|
68
|
+
description: '`latest` (the collection''s own `sort_field`, descending — refused when the collection declares none) or an explicit record id. `any` is refused. A `fixture` given REQUIRES a `pick: <id>` naming one of its records.'
|
|
69
|
+
fixture:
|
|
70
|
+
type: boolean
|
|
71
|
+
description: Read the record from this proof's own fixtures folder instead of the live workspace.
|
|
72
|
+
steps:
|
|
73
|
+
type: array
|
|
74
|
+
items:
|
|
75
|
+
type: object
|
|
76
|
+
description: What actually runs, in order — a shell command (`run`) or a bound command (`perform`). Required; a gate takes one or more `run` steps, a live proof one or more of either.
|
|
77
|
+
expect:
|
|
78
|
+
type: array
|
|
79
|
+
items:
|
|
80
|
+
type: object
|
|
81
|
+
description: 'What must hold for a `live` proof to PASS. Live only, and every row is exactly ONE of four forms — `collection`+`where`+`count` · `record`+`where` · `step` (`exit`/`stdout`/`stdout_json`) · `path`+`exists`. A row whose keys span two forms is a compile error, because compile and the judge would otherwise read it as different shapes. `count` takes its own closed operator set (`_eq _neq _gt _gte _lt _lte _delta`) and every operand must be an INTEGER; a `record:` row REQUIRES a non-empty `where` and takes only the literal `{record}` as its target; a `step:` index past the last step is refused; `stdout` and `stdout_json` are FILTER MAPS (`{ _contains: ''…'' }` · `{ ''a.b'': { _gte: 1 } }`) and a scalar, a list or a bare key is refused, because the judge reads only a map and a row it skips asserts nothing. A `where`''s string literals substitute `{record}` and `{record.<field>}` against the picked record before the filter runs — in an expectation only, never in `given.where`.'
|
|
82
|
+
timeout:
|
|
83
|
+
type: integer
|
|
84
|
+
description: Seconds allowed per `run` step before it is killed.
|
|
85
|
+
order: 46
|
|
86
|
+
# ⚠ NO `last-modified` HERE (M12). Every other list_fields carries it, and on a proof it renders as
|
|
87
|
+
# a bare `-` in the MIDDLE of the row: `dt list proofs` appends two computed columns (availability
|
|
88
|
+
# and the ledger tail) after the declared ones, so the empty cell sits between the about list and
|
|
89
|
+
# the answer the reader came for. A proof's meaningful date is its LAST RUN, which is the `last`
|
|
90
|
+
# column, not the mtime of a file git already tracks.
|
|
91
|
+
list_fields:
|
|
92
|
+
- name
|
|
93
|
+
- kind
|
|
94
|
+
- about
|
|
95
|
+
icon: verified
|
|
96
|
+
group: system
|
|
@@ -6,12 +6,13 @@ use_when: >-
|
|
|
6
6
|
a repository this workspace depends on is named — its own engine or extension clone, a client's
|
|
7
7
|
repo — read the record here for WHERE it lives (`path`, a `${env:…}` template `dt resolve` renders
|
|
8
8
|
per machine) before assuming a path or concluding a clone is unreachable; `dt status` reports
|
|
9
|
-
each one and `dt
|
|
9
|
+
each one and `dt install repos/<id>` materializes it
|
|
10
10
|
# An external git repo attached to this workspace. Owns CLONE LIFECYCLE ONLY — a repo record
|
|
11
11
|
# never contributes schema, skills or UI (that is what a module is, declared in package.json
|
|
12
12
|
# `dreamteamer.git-modules`, because modules must be restorable BEFORE compile can run).
|
|
13
|
-
# Working trees are materialized ON DEMAND via `dreamteamer repos
|
|
14
|
-
# the record count only grows and any given session needs almost none
|
|
13
|
+
# Working trees are materialized ON DEMAND via `dreamteamer install repos/<id>`, and never as part
|
|
14
|
+
# of making a checkout ready: the record count only grows and any given session needs almost none
|
|
15
|
+
# of them.
|
|
15
16
|
storage: { path: data/repos, codec: yaml, shape: file, suffix: repo }
|
|
16
17
|
id:
|
|
17
18
|
generate: "{{ name | slug }}"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dreamteamer",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "A workspace compiler for coding agents
|
|
3
|
+
"version": "0.22.0",
|
|
4
|
+
"description": "A workspace compiler for coding agents \u2014 schema-validated records as plain files over git, compiled into every harness",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Gilad Khen <giladkhen@gmail.com>",
|
|
7
7
|
"homepage": "https://github.com/dreamteamer/dreamteamer#readme",
|
|
@@ -59,10 +59,12 @@ their flags, on one page (there is no per-verb `--help`).
|
|
|
59
59
|
the verb names, as a map (semantics and flags live in `help`; a test holds this list to the
|
|
60
60
|
dispatch, so it cannot drift):
|
|
61
61
|
|
|
62
|
-
- read & measure — `list` `get` `values` `history` `diff` `
|
|
63
|
-
- write & publish — `add` `set` `rm` `rename` `move` `revert` `commit`
|
|
64
|
-
- fields (sources, through the compile gate) — `add-field` `
|
|
65
|
-
- workspace — `init` `install` `update` `compile` `check` `status` `start` `changes` `
|
|
62
|
+
- read & measure — `list` `get` `values` `history` `diff` `next` `relations` `resolve`
|
|
63
|
+
- write & publish — `add` `set` `rm` `rename` `move` `revert` `commit`
|
|
64
|
+
- fields (sources, through the compile gate) — `add-field` `set-field` `rm-field` `rename-field` (system entities — modules, collections, skills, ui-views… — take the RECORD verbs above)
|
|
65
|
+
- workspace — `init` `install` `land` `update` `compile` `check` `prove` `status` `start` `changes` `export` `help`
|
|
66
|
+
|
|
67
|
+
don't learn syntax from prose, this skill included: prose drifts, and `help` ships in
|
|
66
68
|
the same file as the dispatch it documents. run it once before your first write of a session.
|
|
67
69
|
what prose adds is judgment — *when* a verb is the right move, and the guarantees you can lean
|
|
68
70
|
on: **validation is hard** (unknown fields included; an invalid write is rejected before disk
|
|
@@ -80,14 +82,17 @@ Load by the map; nothing here is loaded "just in case".
|
|
|
80
82
|
| a brand-new or empty workspace, dreamteamer over an existing pile of files, "help me set this up" | `references/getting-started.md` |
|
|
81
83
|
| read, create, update, rename, delete, commit — or UNDO — a record | `references/records.md` |
|
|
82
84
|
| "what changed while I was away" | `references/changes.md` |
|
|
85
|
+
| the workspace has to reach a reader that is not a coding agent — a NotebookLM notebook; "which fields are sensitive" | `references/exporting.md` |
|
|
83
86
|
| the workspace seems unable to do something — a new kind of thing, a missing capability, "don't we already have this?" | `references/before-you-build.md` (look first); a new model then continues `references/data-modeling.md` (decide) → `references/collections.md` (write it) |
|
|
84
87
|
| a collection or field, mechanically — the descriptor, the system and field verbs, `templates:`/`extends:`, a compile or check message | `references/collections.md` |
|
|
85
88
|
| knowledge a session should find on its own | `references/skills.md` |
|
|
86
89
|
| "let me type one word and have this done" | `references/commands.md` |
|
|
87
90
|
| "which command applies to this record?" — a binding, a gate | `references/commands.md` |
|
|
91
|
+
| "how would anyone know this still works?" — a proof of a skill, a command or a script, and the exit code `dt prove` answers with | `references/proofs.md` |
|
|
88
92
|
| a job needing a fresh context and its own tools | `references/agents.md` |
|
|
89
93
|
| a route, a nav entry, a board / calendar / map over records | `references/ui-views.md` |
|
|
90
94
|
| a rendering or editing behaviour nothing registered has | `references/ui-components.md` |
|
|
95
|
+
| a second checkout — making one ready, landing its records, a harness that cuts them for you | `references/worktrees.md` |
|
|
91
96
|
|
|
92
97
|
three act-two tie-breakers, because they are the ones that go wrong:
|
|
93
98
|
|
|
@@ -107,8 +112,8 @@ workspace's decision log (where one exists) wins over older documents.
|
|
|
107
112
|
|
|
108
113
|
## system entities take the RECORD verbs
|
|
109
114
|
|
|
110
|
-
Modules, collections, skills, agents, commands, command-bindings, ui-views
|
|
111
|
-
are collections in the runtime, and since 0.19.0 the ordinary verbs write them:
|
|
115
|
+
Modules, collections, skills, agents, commands, command-bindings, ui-views, collection-templates
|
|
116
|
+
and proofs are collections in the runtime, and since 0.19.0 the ordinary verbs write them:
|
|
112
117
|
|
|
113
118
|
```
|
|
114
119
|
dt add modules --name core --description "The shared nouns."
|
|
@@ -120,6 +125,10 @@ dt set modules/hr namespaces=hr dependencies=modules/core
|
|
|
120
125
|
dt rm modules/hr --force # --dry-run first; it prints its plan
|
|
121
126
|
```
|
|
122
127
|
|
|
128
|
+
⚠ **`proofs` is the one exception, and only to `add`:** a proof is hand-authored like a skill or a
|
|
129
|
+
command, so `dt add proofs` is refused, naming the file to write
|
|
130
|
+
(`modules/<module>/proofs/<id>.proof.yaml`, `references/proofs.md`). Every other verb works on it.
|
|
131
|
+
|
|
123
132
|
`dt schema <op>` is **gone** since 0.19.0 and fails with the translation printed. `UPDATING.md` has
|
|
124
133
|
the complete mapping table.
|
|
125
134
|
|
|
@@ -130,7 +139,7 @@ holds the source**, so a write into a `git_modules/` module commits there and sa
|
|
|
130
139
|
`ahead 1 — push when ready`. A record write lands on disk and `dt commit` publishes it.
|
|
131
140
|
|
|
132
141
|
⚠ **Every verb that moves records or clears values takes `--dry-run` and prints its plan first:**
|
|
133
|
-
`rename collections/…`, `rename-field`, `
|
|
142
|
+
`rename collections/…`, `rename-field`, `rm-field`, `set collections/… module=`,
|
|
134
143
|
`rm modules/… --force`. The plan line is one shape — `records N · refs M · descriptors K · values
|
|
135
144
|
cleared V` — so two dry runs are comparable, and a term that reads 0 means zero rather than
|
|
136
145
|
unmeasured (where a number genuinely cannot be known before the run, the plan says so in words).
|
|
@@ -40,13 +40,27 @@ install. The looking still matters; the taking changed shape.
|
|
|
40
40
|
|
|
41
41
|
| # | look at | how, concretely | what you get |
|
|
42
42
|
|---|---|---|---|
|
|
43
|
-
| 1 | **this workspace's own modules** | the orientation block's MODULES AND THEIR COLLECTIONS list is already in your context — find the module whose sentence owns the concept, then reread its collections with the concept's *synonyms* in mind, `use when` clauses included; then read the candidate's DESCRIPTOR, not its records; `dt help` shows the verbs and `dt
|
|
43
|
+
| 1 | **this workspace's own modules** | the orientation block's MODULES AND THEIR COLLECTIONS list is already in your context — find the module whose sentence owns the concept, then reread its collections with the concept's *synonyms* in mind, `use when` clauses included; then read the candidate's DESCRIPTOR, not its records; `dt help` shows the verbs and `dt next <collection>` the bound ones; the module's skills line names the techniques | the thing may already exist under a name you didn't guess. This is where misses actually happen, because it is the place you assume you already know |
|
|
44
44
|
| 2 | **a recipes repo — where this workspace's own CLAUDE.md names one** (reference modules maintained to be **copied and adapted**) | read that repo's own instructions first; they ARE the adoption procedure. no recipes repo declared → skip this row | a working module with its reasoning attached — descriptors, skills, and the trap notes that each cost someone a day |
|
|
45
45
|
| 3 | **a sibling workspace — where the operator has named one** | read-only; grep its `modules/` for the concept. none named → skip | another vault may have solved it concretely. That is a *reference*, not a source — it holds real personal data, so read the shape, never lift the content |
|
|
46
|
-
| 4 | **the engine's own surface** | `dt help` — the complete verb surface, system and field verbs included; read it rather than recalling it | the capability may already be a verb (`relations rebuild`, `resolve`, `
|
|
46
|
+
| 4 | **the engine's own surface** | `dt help` — the complete verb surface, system and field verbs included; read it rather than recalling it | the capability may already be a verb (`relations rebuild`, `resolve`, `install repos/<id>`) rather than a missing module |
|
|
47
47
|
|
|
48
48
|
Only after all four: build it, in the module that owns the concept.
|
|
49
49
|
|
|
50
|
+
## name the proof before you build it
|
|
51
|
+
|
|
52
|
+
**Before writing the thing, say how anyone would know it works** — one sentence, in the shape a
|
|
53
|
+
`proofs` record takes: *this record, in this state, after this step, must look like this*
|
|
54
|
+
(`proofs.md`). It costs a minute and it is the cheapest design review there is.
|
|
55
|
+
|
|
56
|
+
⚠ **When you cannot name one, the artifact has no observable post-state, and THAT is the first
|
|
57
|
+
thing to change** — not something to note and carry on past. A skill nothing can check is a skill
|
|
58
|
+
whose only evidence is that a session read it; a command whose completion leaves no trace in any
|
|
59
|
+
record is a command nobody can tell ran. Give it a post-state — a field it sets, a record it
|
|
60
|
+
writes, a file it leaves, an exit code it returns — and the proof follows for free. Do it after the
|
|
61
|
+
proposal is accepted and before the build, so the assertion is what you build toward rather than
|
|
62
|
+
what you retrofit.
|
|
63
|
+
|
|
50
64
|
## what "found" actually means
|
|
51
65
|
|
|
52
66
|
Rarely the exact thing. Four outcomes, each with its own move:
|
|
@@ -63,26 +63,26 @@ unpublished schema is not a state the workspace should sit in. The verbs and eve
|
|
|
63
63
|
`dt help` under "system verbs" and "field verbs" — read that, not prose. What help cannot tell you:
|
|
64
64
|
|
|
65
65
|
- **The field verbs write the WORKSPACE module.** On a collection another module owns, `add-field`
|
|
66
|
-
and `
|
|
66
|
+
and `set-field` author an `extends:` overlay in the workspace module — which compiles only
|
|
67
67
|
if the workspace module declares the owning module in `dreamteamer.dependencies` (the extends
|
|
68
|
-
gate exempts nobody). `
|
|
68
|
+
gate exempts nobody). `rm-field` has no overlay form at all — an overlay cannot remove an
|
|
69
69
|
inherited field, so it refuses a module-shipped field by name. So on a module-owned collection:
|
|
70
|
-
declare the dependency and let add
|
|
71
|
-
or **edit the owning module's descriptor by hand** and compile (the change
|
|
72
|
-
module) — the only exit for a removal. Pick by who should own the field —
|
|
73
|
-
Part III.
|
|
70
|
+
declare the dependency and let `add-field`/`set-field` write the overlay (the change stays
|
|
71
|
+
workspace-local), or **edit the owning module's descriptor by hand** and compile (the change
|
|
72
|
+
ships with the module) — the only exit for a removal. Pick by who should own the field —
|
|
73
|
+
`data-modeling.md` Part III.
|
|
74
74
|
- **`--type <collection>` beats the type sugar, always.** A type that names a collection in the
|
|
75
75
|
runtime is a reference to it, whatever `string`/`enum`/`date`/`tags`/… would otherwise mean — so
|
|
76
76
|
in a workspace that ships a `tags` collection, `--type tags` points at it and the relation flags
|
|
77
77
|
work on it. Only a stated `--type` resolves this way; omitting it still means a plain string.
|
|
78
|
-
- `
|
|
78
|
+
- `rm-field` on a populated field **clears the values in the same write and reports the
|
|
79
79
|
count** — a leftover key would make every later write to those records fail as unknown. It also
|
|
80
80
|
prunes the field out of **the same descriptor's `list_fields` and `sort_field`** (that is the
|
|
81
81
|
field's own presentation, and a dangling `sort_field` is a compile error), and **warns, by id,**
|
|
82
82
|
about any ui-view whose `options.columns` still names it — a different source, so it is named
|
|
83
83
|
rather than edited.
|
|
84
84
|
- **`add-field` inserts before the `x-body` field**, on the same rule as a `templates:` merge
|
|
85
|
-
below: property order is form order, and a record's body belongs last. `
|
|
85
|
+
below: property order is form order, and a record's body belongs last. `set-field` never
|
|
86
86
|
reorders — an existing field keeps the place its author gave it.
|
|
87
87
|
- **`dt rename collections/<old> <new>`** moves the descriptor **in the module that ships it**
|
|
88
88
|
(its guard is against writes an `npm install` would erase, not against modules), plus the
|
|
@@ -227,6 +227,45 @@ Two gates around it:
|
|
|
227
227
|
- ⚠ **An overlay can add fields but cannot remove an inherited one.** If the shape is wrong for
|
|
228
228
|
the module rather than just for this workspace, fix the base.
|
|
229
229
|
|
|
230
|
+
## `x-choices` — what an enum VALUE looks like
|
|
231
|
+
|
|
232
|
+
An enum value carries a label and nothing else by default: a surface gets `{ text, value }` and
|
|
233
|
+
draws the value. `x-choices` is an OPTIONAL sparse map, keyed by the value, that gives a surface
|
|
234
|
+
more to draw with — a board grouping by the field, a dropdown in a form, anything reading
|
|
235
|
+
`edit_options.choices`.
|
|
236
|
+
|
|
237
|
+
```yaml
|
|
238
|
+
lane:
|
|
239
|
+
type: string
|
|
240
|
+
enum: [alpha, bravo, charlie]
|
|
241
|
+
x-choices:
|
|
242
|
+
alpha:
|
|
243
|
+
label: Alpha team # what a surface shows; the stored VALUE is still `alpha`
|
|
244
|
+
description: the one that ships
|
|
245
|
+
icon: rocket # a codicon name …
|
|
246
|
+
color: charts.blue # a theme colour id — the accent
|
|
247
|
+
background: charts.blue # … and the fill
|
|
248
|
+
bravo:
|
|
249
|
+
icon: assets/icons/lucide/anchor # … OR a reference to a `codec: file` record
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- **Sparse and additive.** Decorate one value, or none. A value with no entry projects exactly as it
|
|
253
|
+
did before this keyword existed, so adding it changes nothing that already works.
|
|
254
|
+
- **`enum` still owns the value set AND its order.** A map key cannot add, remove or reorder a
|
|
255
|
+
value — which matters, because a grouped view takes its band order from the enum.
|
|
256
|
+
- **Five keys, and only five** — `label` · `description` · `icon` · `color` · `background`, each an
|
|
257
|
+
optional string. Anything else in an entry is dropped: the projection copies by name, so a
|
|
258
|
+
descriptor cannot inject keys into a contract every surface reads.
|
|
259
|
+
- **`label` becomes `text`.** So a workspace can relabel a value without touching the value, and no
|
|
260
|
+
stored record moves.
|
|
261
|
+
- **`icon` is a codicon name or a reference to a record of a `codec: file` collection.** A codicon
|
|
262
|
+
name never contains a slash and a record reference always does, so the surface decides which
|
|
263
|
+
without a second keyword.
|
|
264
|
+
- **Colours are theme colour ids, not hex.** A hex is authored against one theme and wrong in the
|
|
265
|
+
other.
|
|
266
|
+
- **Both mistakes warn rather than fail** — a key that is not one of the enum's values, and the
|
|
267
|
+
keyword on a field with no enum. See the message catalog below.
|
|
268
|
+
|
|
230
269
|
## the reference contract — `x-reference` across the module graph
|
|
231
270
|
|
|
232
271
|
Every `x-reference` target must be one of: a **core** collection (the entity kinds plus `repos`)
|
|
@@ -291,6 +330,8 @@ collection author actually meets. (⚠ = warning: it compiled, and you should st
|
|
|
291
330
|
| `cyclic module dependencies: a → b → a` | concept-level links declared as module deps | the collection belongs in `peerDependencies` |
|
|
292
331
|
| relation refusals (`stamps a mirror onto…`, `declared on both sides…`) | the relation rules | `data-modeling.md` Part VI |
|
|
293
332
|
| ⚠ `x-unique on "f" is inert` | a relation keyword with no relation — nothing enforces it | declare the inverse, or drop it |
|
|
333
|
+
| ⚠ `x-choices on "f" has an entry for "k"` | it decorates enum VALUES and `k` is not one — a typo, or a value since removed | fix the spelling, or drop the entry |
|
|
334
|
+
| ⚠ `x-choices on "f" is inert` | the keyword on a field that declares no enum — nothing reads it | give the field an enum, or drop the keyword |
|
|
294
335
|
| ⚠ `collection … has no description` | it renders as a bare name in the orientation block every session loads | write the sentence (`data-modeling.md` §18) |
|
|
295
336
|
| ⚠ `module "…" contributed no recognised sources` | its folders match no kind and it ships no UI bundle | usually a layout or naming mistake |
|
|
296
337
|
| ⚠ `module X: <channel> copy shadows <channel> copy` | the same module delivered twice — the more local wins (npm-link semantics) | intended for dev; otherwise remove one |
|
|
@@ -316,7 +357,7 @@ silently not discovered — see declaring a module.)*
|
|
|
316
357
|
| a second same-name descriptor without `extends` | compile error by design |
|
|
317
358
|
| a plain string where a ref belongs | use `x-reference` so `check` and `rename` can follow it |
|
|
318
359
|
| a `templates:` ref pointing at another module | that module can no longer be copied or installed alone |
|
|
319
|
-
| a field verb aimed at a module-owned collection, retried verbatim | add/
|
|
360
|
+
| a field verb aimed at a module-owned collection, retried verbatim | add-field/set-field write a workspace overlay behind a dependency gate — declare the dependency or edit the owning module; rm-field refuses outright (edit the module) |
|
|
320
361
|
| authoring `storage.path` under an entity-kind name | it compiles as a runtime collection and becomes unwritable |
|
|
321
362
|
| hand-editing schema when a schema verb could express it | the verbs are compile-gated and commit their write; a hand edit can land uncompilable and sit unpublished |
|
|
322
363
|
| ignoring a ⚠ because compile said ✔ | every warning above is a defect with a deferred bill |
|
|
@@ -8,7 +8,7 @@ queue without any run records.
|
|
|
8
8
|
What you write is consumed by three readers with different needs: the **operator** scanning the
|
|
9
9
|
`/` picker, who sees the `description` and `argument-hint` and nothing else; the **session** that
|
|
10
10
|
receives the body verbatim as its turn, and needs instructions, not documentation; and the
|
|
11
|
-
**surfaces** that render bindings — `dt
|
|
11
|
+
**surfaces** that render bindings — `dt next <ref>`, the studio's Commands tab, and the
|
|
12
12
|
orientation block, which prints every binding's gates so each new session knows the queue exists
|
|
13
13
|
before any skill is loaded.
|
|
14
14
|
|
|
@@ -107,7 +107,7 @@ can-exit: { transcript: { _nempty: true } }
|
|
|
107
107
|
description: audio present, not yet transcribed
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
What a binding buys: `dt
|
|
110
|
+
What a binding buys: `dt next <collection>[/<id>] [--ids a,b] [--json]` answers "what can I do
|
|
111
111
|
with this record right now"; the studio draws the same answer as buttons; and the orientation block
|
|
112
112
|
renders every binding with its gates **literally** (`/transcribe-visit (enter: recording_file set ·
|
|
113
113
|
exit: transcript set)`) — so the gate you write is also documentation every session reads without
|
|
@@ -151,8 +151,21 @@ they read stay honest:
|
|
|
151
151
|
`{ summary: { _nempty: true } }` works the moment `summary` is a mirror — or ship the binding
|
|
152
152
|
without a `can-exit` and accept that it never shows done. What is not honest is a proxy field a
|
|
153
153
|
human must remember to set.
|
|
154
|
+
- **A `can-exit` and a proof's `count` answer different questions — put each expectation on its own
|
|
155
|
+
side.** A gate is a filter over ONE record, evaluated on every render of `dt next` and every board
|
|
156
|
+
the studio draws, so it can only ever read that record's own fields (plus one outbound hop) — which
|
|
157
|
+
is exactly the gap the bullet above names: "a summary referencing this record exists" is
|
|
158
|
+
inexpressible there. A **proof** (`proofs.md`) is evaluated on demand and is collection-scoped, so
|
|
159
|
+
it says the thing a gate cannot: `{ collection: summaries, where: { about: { _eq: '{record}' } },
|
|
160
|
+
count: { _delta: 1 } }` — *running this command left one more summary behind*. (`{record}` inside a
|
|
161
|
+
proof's `where` is SUBSTITUTED with the picked record's reference before the filter runs, which is
|
|
162
|
+
what makes that line count the summaries about THIS record rather than all of them.) The rule of thumb:
|
|
163
|
+
**the record's own post-state is the binding's** (it has to be, or the queue cannot advance);
|
|
164
|
+
**what the command left elsewhere is the proof's**. Writing the second one as a gate needs a mirror
|
|
165
|
+
field or a proxy a human must remember to set; writing the first one only as a proof leaves every
|
|
166
|
+
record reading `available` forever.
|
|
154
167
|
- **The binding's `description` is the state pair in words** ("audio present, not yet
|
|
155
|
-
transcribed") — it renders beside the button and in `dt
|
|
168
|
+
transcribed") — it renders beside the button and in `dt next` output (the orientation block
|
|
156
169
|
carries the gates themselves), so write it as the answer to "why is this available".
|
|
157
170
|
|
|
158
171
|
## the chain — multi-step processes
|
|
@@ -93,7 +93,7 @@ The useful half of object orientation maps cleanly onto a workspace; the inherit
|
|
|
93
93
|
| OO idea | its shape here | its non-shape |
|
|
94
94
|
|---|---|---|
|
|
95
95
|
| an object owns its state | a record owns its fields; the collection is the class | fields about X scattered on Y "for convenience" |
|
|
96
|
-
| methods | **command-bindings**: verbs bound to a collection, gated on field state (`can-enter`/`can-exit`) — `dt
|
|
96
|
+
| methods | **command-bindings**: verbs bound to a collection, gated on field state (`can-enter`/`can-exit`) — `dt next <ref>` lists what applies to a record right now | a workflow engine; procedures copied into descriptions |
|
|
97
97
|
| interfaces / mixins | **`templates:`** — a shared field set stamped onto consumers, merged at compile | copy-pasting the same four fields into six descriptors |
|
|
98
98
|
| encapsulation | **module ownership** — a concept's fields live with the module that owns the concept | the module that happened to need the field first |
|
|
99
99
|
| polymorphism | a **union reference** (`x-reference: [meetings, visits]`) or the open-world `'*'` for evidence/source fields | a `type` field plus fields that only apply to some rows |
|
|
@@ -845,7 +845,7 @@ useless until the flood is drained.
|
|
|
845
845
|
command-binding's `can-enter`/`can-exit`. A field is referenced BY NAME rather than as a
|
|
846
846
|
`<collection>/<id>` reference, so `store.rewriteRefs` can see none of those — which is exactly
|
|
847
847
|
why this used to be "no rename verb, deliberately", with the honest sequence being add-new,
|
|
848
|
-
script the values, `
|
|
848
|
+
script the values, `rm-field` the old. That sequence still works and is what you want when
|
|
849
849
|
the values themselves have to CHANGE shape; when only the name changes, the verb is one command
|
|
850
850
|
and takes `--dry-run`.
|
|
851
851
|
- **Values** (an id, a reference target): `dt rename <collection>/<old> <new>` rewrites inbound
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# exporting — the workspace for a reader that is not a coding agent
|
|
2
|
+
|
|
3
|
+
`compile` renders the runtime for harnesses that read files. `dt export <target>` renders it for a
|
|
4
|
+
consumer that does not: today **NotebookLM**, which holds a bounded number of *sources* per notebook
|
|
5
|
+
and takes its standing instructions as a 10,000-character *persona*. `dt help` has the flags; this
|
|
6
|
+
page has the judgment.
|
|
7
|
+
|
|
8
|
+
## what travels, and what never does
|
|
9
|
+
|
|
10
|
+
Sensitivity is SCHEMA, decided once where the field is declared — never a flag on the export and
|
|
11
|
+
never inferred from a field's name (`email` travels unless somebody marked it):
|
|
12
|
+
|
|
13
|
+
- **a whole collection**: `dt set collections/<c> sensitive=true`. No record of it is written; the
|
|
14
|
+
schema source and the persona NAME the omission so the reader knows the gap is deliberate.
|
|
15
|
+
- **one field**: `dt add-field <c> --name <f> … --sensitive`, or `dt set-field <c> --name <f>
|
|
16
|
+
--sensitive` (`--sensitive false` clears). The field is projected out of every record.
|
|
17
|
+
|
|
18
|
+
Also never exported as records: the system collections (they ARE the schema source) and `codec: file`
|
|
19
|
+
collections (bytes a view draws). **Before the first export of a workspace, read the report** — it
|
|
20
|
+
prints every field that WAS exported per collection; that list is the review.
|
|
21
|
+
|
|
22
|
+
## what a notebook gets
|
|
23
|
+
|
|
24
|
+
- **`dt · schema`** — one source: workspace → module → collection → field, one heading level per
|
|
25
|
+
step, with each collection's `use when`, id shape and every field's type, enum values and reference
|
|
26
|
+
target. The lexicon the persona cannot afford in full.
|
|
27
|
+
- **`dt · <collection>`** — one source per collection, `## <collection>/<id>` per record, references
|
|
28
|
+
rendered as `companies/acme (Acme Ltd)`. Over `--max-words` it shards into `dt · <c> [n/m]`.
|
|
29
|
+
- **the persona** — a Markdown template with `{{workspace}}` `{{schema_brief}}` `{{sources}}`
|
|
30
|
+
`{{omitted}}` `{{exported_at}}` `{{engine_version}}` `{{schema_title}}` `{{collections}}`; the
|
|
31
|
+
engine ships a default, `--instructions <file.md>` replaces it, an unknown placeholder is an
|
|
32
|
+
error. `--response-length` and `--mode` ride along to `notebooklm configure`.
|
|
33
|
+
|
|
34
|
+
**The budget is the plan's source cap** (`--plan standard|plus|pro|ultra|<n>`, default `standard` =
|
|
35
|
+
50). Too many sources is a refusal that names the count and the remedies — nothing is dropped
|
|
36
|
+
quietly. A workspace with more collections than the plan has sources narrows with `--collections`.
|
|
37
|
+
|
|
38
|
+
## syncing
|
|
39
|
+
|
|
40
|
+
Without `--notebook`/`--create` the export is a pure render into `--out` (default
|
|
41
|
+
`.cache/dreamteamer/notebooklm/`, gitignored) and touches no network. With one, it makes the notebook
|
|
42
|
+
match **by title**: adds new sources, replaces those whose content hash changed, removes its own stale
|
|
43
|
+
ones (the `dt · ` prefix marks ownership — a source added by hand is never touched), then applies the
|
|
44
|
+
persona. `notebook.json` in the out dir keeps the notebook id and per-source hashes, so the second run
|
|
45
|
+
is incremental and `--create` is needed once. Every vendor call names the notebook with `-n`;
|
|
46
|
+
`notebooklm use` is never run. **Preflight is `notebooklm auth check --test`** — the bare check
|
|
47
|
+
reports a stale session as valid — and an expired login is the operator's to renew (`notebooklm
|
|
48
|
+
login` opens a browser).
|
|
49
|
+
|
|
50
|
+
## asking
|
|
51
|
+
|
|
52
|
+
Not a verb here. Ask with `notebooklm ask -n <id> --json`, and treat an answer as real only when it
|
|
53
|
+
has non-blank text AND at least one reference — exit 0 with an empty answer is a known shape.
|