dreamteamer 0.6.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.
Files changed (51) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +16 -0
  3. package/README.md +83 -0
  4. package/agents/dreamteamer.agent.md +7 -0
  5. package/bin/dreamteamer.js +65 -0
  6. package/collection-templates/docs.collection-template.yaml +14 -0
  7. package/collection-templates/entity.collection-template.yaml +16 -0
  8. package/collections/agents.collection.yaml +33 -0
  9. package/collections/collection-templates.collection.yaml +20 -0
  10. package/collections/collections.collection.yaml +78 -0
  11. package/collections/command-bindings.collection.yaml +46 -0
  12. package/collections/commands.collection.yaml +36 -0
  13. package/collections/repos.collection.yaml +42 -0
  14. package/collections/skills.collection.yaml +22 -0
  15. package/collections/ui-views.collection.yaml +48 -0
  16. package/collections/users.collection.yaml +21 -0
  17. package/package.json +58 -0
  18. package/skills/building-dreamteamer/SKILL.md +117 -0
  19. package/skills/building-dreamteamer/references/agents.md +44 -0
  20. package/skills/building-dreamteamer/references/before-you-build.md +42 -0
  21. package/skills/building-dreamteamer/references/collections.md +120 -0
  22. package/skills/building-dreamteamer/references/commands.md +69 -0
  23. package/skills/building-dreamteamer/references/skills.md +73 -0
  24. package/skills/building-dreamteamer/references/ui-components.md +78 -0
  25. package/skills/building-dreamteamer/references/ui-views.md +59 -0
  26. package/skills/using-dreamteamer/SKILL.md +100 -0
  27. package/skills/using-dreamteamer/references/git-events.md +64 -0
  28. package/skills/using-dreamteamer/references/records.md +102 -0
  29. package/src/check.js +193 -0
  30. package/src/cli.js +250 -0
  31. package/src/collections-cli.js +389 -0
  32. package/src/commit.js +117 -0
  33. package/src/compile.js +747 -0
  34. package/src/events.js +124 -0
  35. package/src/field-values.js +69 -0
  36. package/src/filter.js +107 -0
  37. package/src/harnesses.js +233 -0
  38. package/src/history.js +64 -0
  39. package/src/init.js +307 -0
  40. package/src/presentation.js +190 -0
  41. package/src/record-commands.js +84 -0
  42. package/src/records.js +73 -0
  43. package/src/runtime.js +96 -0
  44. package/src/schema-ops.js +263 -0
  45. package/src/semver.js +32 -0
  46. package/src/server.js +291 -0
  47. package/src/store.js +450 -0
  48. package/src/template.js +98 -0
  49. package/src/temporal.js +149 -0
  50. package/src/workspace.js +51 -0
  51. package/src/yaml.js +6 -0
@@ -0,0 +1,42 @@
1
+ name: repos
2
+ # An external git repo attached to this workspace. Owns CLONE LIFECYCLE ONLY — a repo record
3
+ # never contributes schema, skills or UI (that is what a module is, declared in package.json
4
+ # `dreamteamer.git-modules`, because modules must be restorable BEFORE compile can run).
5
+ # Working trees are materialized ON DEMAND via `dreamteamer repos ensure <id>`, never at install:
6
+ # the record count only grows and any given session needs almost none of them.
7
+ storage: { path: data/repos, codec: yaml, shape: file, suffix: repo }
8
+ id:
9
+ generate: "{{ name | slug }}"
10
+ pattern: "^[a-z0-9-]+$"
11
+ schema:
12
+ type: object
13
+ required: [name, url]
14
+ properties:
15
+ name:
16
+ type: string
17
+ description: The CLONE FOLDER name, not a display title — this is what appears on disk.
18
+ description:
19
+ type: string
20
+ description: What this repo is, in one line.
21
+ url:
22
+ type: string
23
+ description: The git remote to clone from.
24
+ ref:
25
+ type: string
26
+ default: main
27
+ description: Branch or tag to check out.
28
+ identity:
29
+ type: string
30
+ description: 'Opaque path segment grouping clones by the account that owns them: <repos-path>/<identity>/<name>.'
31
+ path:
32
+ type: string
33
+ description: Workspace-relative path override. Wins over the derived <repos-path>/<identity>/<name>.
34
+ visibility:
35
+ type: string
36
+ enum: [private, public]
37
+ default: private
38
+ description: Whether the repo is public. Defaults to private, because assuming otherwise is the expensive mistake.
39
+ order: 145
40
+ list_fields: [name, identity, ref, url]
41
+ icon: source
42
+ group: system
@@ -0,0 +1,22 @@
1
+ name: skills
2
+ storage: { path: skills, codec: md, shape: folder, entry: SKILL.md }
3
+ id: { generate: "{{ name | slug }}" }
4
+ schema:
5
+ type: object
6
+ required: [name, description]
7
+ properties:
8
+ name:
9
+ type: string
10
+ description: The skill id — must equal the folder name.
11
+ description:
12
+ type: string
13
+ description: WHEN to load this skill, not what it contains. A session matches its situation against this line, so a description that describes the contents is a skill that never triggers.
14
+ instructions:
15
+ type: string
16
+ format: markdown
17
+ x-body: true
18
+ description: The skill itself. Keep it a digest with references/ beside it — a skill nobody can afford to load is not a capability.
19
+ order: 20
20
+ list_fields: [name, last-modified, description]
21
+ icon: psychology
22
+ group: system
@@ -0,0 +1,48 @@
1
+ name: ui-views
2
+ title: UI Views
3
+ storage: { path: ui-views, codec: yaml, shape: file, suffix: ui-view }
4
+ id: { generate: "{{ path | slug }}" }
5
+ schema:
6
+ type: object
7
+ required: [path, target, layout]
8
+ properties:
9
+ path:
10
+ type: string
11
+ description: The route this view renders at. The record id is derived from it, so a view saved from the CLI and one saved from the UI land on the same record.
12
+ nav:
13
+ type: object
14
+ description: How the view appears in navigation. Omit it entirely for a route with no nav entry.
15
+ properties:
16
+ label:
17
+ type: string
18
+ description: What the nav entry reads.
19
+ icon:
20
+ type: string
21
+ description: material-symbols-outlined icon name for the nav entry.
22
+ order:
23
+ type: number
24
+ description: Sort position among its siblings.
25
+ target:
26
+ type: string
27
+ enum: [list, item, page]
28
+ description: What the view renders — a collection listing, one record, or a standalone page.
29
+ collection:
30
+ type: string
31
+ x-reference: collections
32
+ description: Which collection this view is of.
33
+ default:
34
+ type: boolean
35
+ description: 'Marks the view a bare /content/<collection> renders. Directus-parity: a collection''s default presentation is an ordinary view record, not a second mechanism. At most one per collection, and never its own route — it IS the collection page.'
36
+ layout:
37
+ type: string
38
+ description: A REGISTERED layout/component id (table, cards, kanban, calendar, map…). An unregistered id degrades visibly rather than erroring.
39
+ options:
40
+ type: object
41
+ description: Layout-specific settings — columns, sort, grouping. Deliberately open, since each layout wants different things.
42
+ filter:
43
+ type: object
44
+ description: The saved filter this view applies, in the studio's operator set.
45
+ order: 50
46
+ list_fields: [path, last-modified, target, collection, layout]
47
+ icon: dashboard_customize
48
+ group: system
@@ -0,0 +1,21 @@
1
+ name: users
2
+ storage: { path: data/users, codec: yaml, shape: file, suffix: user }
3
+ id: { generate: "{{ name | slug }}" }
4
+ schema:
5
+ type: object
6
+ required: [name]
7
+ properties:
8
+ name:
9
+ type: string
10
+ description: The person's name. Its slug is the id, and `@me` resolves against it — so it must agree with `git config user.name`, or an assignee query comes back empty with no error.
11
+ description:
12
+ type: string
13
+ description: Who this person is, in one line.
14
+ email:
15
+ type: string
16
+ format: email
17
+ description: Their email address.
18
+ order: 140
19
+ list_fields: [name, last-modified, email]
20
+ icon: badge
21
+ group: system
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "dreamteamer",
3
+ "version": "0.6.0",
4
+ "description": "A workspace compiler for coding agents — schema-validated records as plain files over git, compiled into every harness",
5
+ "license": "Apache-2.0",
6
+ "author": "Gilad Khen <giladkhen@gmail.com>",
7
+ "homepage": "https://github.com/dreamteamer/dreamteamer#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/dreamteamer/dreamteamer.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/dreamteamer/dreamteamer/issues"
14
+ },
15
+ "keywords": [
16
+ "agent",
17
+ "coding-agent",
18
+ "claude-code",
19
+ "workspace",
20
+ "compiler",
21
+ "cli",
22
+ "yaml",
23
+ "markdown",
24
+ "json-schema",
25
+ "git"
26
+ ],
27
+ "type": "module",
28
+ "engines": {
29
+ "node": ">=20"
30
+ },
31
+ "bin": {
32
+ "dreamteamer": "./bin/dreamteamer.js"
33
+ },
34
+ "files": [
35
+ "NOTICE",
36
+ "bin",
37
+ "src",
38
+ "collections",
39
+ "skills",
40
+ "agents",
41
+ "commands",
42
+ "command-bindings",
43
+ "ui-views",
44
+ "collection-templates"
45
+ ],
46
+ "dependencies": {
47
+ "ajv": "^8.17.1",
48
+ "ajv-formats": "^3.0.1",
49
+ "express": "^5.2.1",
50
+ "js-yaml": "^4.1.0"
51
+ },
52
+ "dreamteamer": {},
53
+ "scripts": {
54
+ "metrics": "node scripts/metrics.mjs",
55
+ "metrics:check": "node scripts/metrics.mjs --check",
56
+ "layers": "node scripts/layers.mjs"
57
+ }
58
+ }
@@ -0,0 +1,117 @@
1
+ ---
2
+ name: building-dreamteamer
3
+ description: use when authoring or changing anything in a module's source folders (collections/, skills/, agents/, commands/, command-bindings/, ui-views/, collection-templates/) — a collection or a field, a skill, a command, an agent, a ui-view, or studio component code. Also when deciding WHICH of those a request should become, or when a compile/check error names a source file.
4
+ ---
5
+
6
+ # building dreamteamer
7
+
8
+ **core principle:** you write a **source** under `modules/<module>/<kind>/`, `compile` makes
9
+ it real, `check` reports what disagrees. Nothing you author is live until compile runs, and nothing
10
+ under `.dreamteamer/` or `.claude/` is ever the thing to edit.
11
+
12
+ This skill is the **digest**. The shape of each entity, and the mistakes specific to it, live in
13
+ `references/` — load exactly the one you need.
14
+
15
+ ## first: which entity is this?
16
+
17
+ Most authoring mistakes are a wrong choice here, not a wrong file. Pick by what the operator
18
+ actually wants to happen:
19
+
20
+ | the ask | write | reference |
21
+ |---|---|---|
22
+ | "the workspace has no home for this kind of thing" / a field is missing | a **collection** | `references/collections.md` |
23
+ | "when you're already doing X, know this" — knowledge a session should find itself | a **skill** | `references/skills.md` |
24
+ | "let me type one word and have you do this" | a **command** | `references/commands.md` |
25
+ | "do this with a fresh context and its own tools" | an **agent** | `references/agents.md` |
26
+ | "put it at this route / in the nav / show it as a board" | a **ui-view** | `references/ui-views.md` |
27
+ | a rendering or editing behaviour no registered component has | **component code** | `references/ui-components.md` |
28
+ | "which command applies to this record?" | a **command-binding** | `references/commands.md` |
29
+
30
+ Three tie-breakers worth internalising, because they are the ones that go wrong:
31
+
32
+ - **skill vs command:** a skill triggers itself when the situation arises; a command needs the
33
+ operator to remember it exists. If the answer is "and they'd have to think of running it", write
34
+ the skill.
35
+ - **agent vs skill:** an agent costs a whole context. If "just tell the current session how" works,
36
+ it is a skill.
37
+ - **a multi-step process is a CHAIN OF COMMANDS, not an entity.** There is no workflow kind: a
38
+ `workflows` collection with run records, triggers and an executor existed until 2026-07-31 and was
39
+ removed after three days of measurement showed the work being done by a command chain instead. Write
40
+ one command per step, bind each to its collection so `dt commands for <ref>` shows what applies, and
41
+ a command whose body invokes the others in order if the sequence needs a name. The record's own state
42
+ is the progress marker — which is what made the run records redundant.
43
+
44
+ ## the rules that apply to every kind
45
+
46
+ These were duplicated across seven skills; they are true for all of them.
47
+
48
+ 1. **Sources live in a module.** `modules/<module>/<kind>/`. The workspace's own go in its
49
+ **workspace module** — the `dreamteamer.workspace-module` name in `package.json`. A root
50
+ source folder at the WORKSPACE root is a **compile error**, not a fallback.
51
+ 2. **The filename is the id.** `<name>.<kind>.<ext>`, or a folder named `<id>` for folder-shape
52
+ kinds (skills). Where a record also carries a `name` in frontmatter (agents, commands),
53
+ the two **must agree** — the harness names the file from the filename, so a mismatch
54
+ makes the id lie.
55
+ 3. **The meta-descriptor IS the spec.** Every kind is itself a collection:
56
+ `.dreamteamer/collections/<kind>.collection.yaml` lists every key it may carry with its
57
+ allowed values. Read that, plus a real one (`dt <kind> get <id>`), instead of learning the shape
58
+ from prose. Prose drifts; the descriptor cannot.
59
+ 4. **`npm run compile`, then `npm run check`.** Compile materializes the runtime and the harness
60
+ adapters; check validates refs and shapes and never modifies files. Neither is optional.
61
+ 5. **A running session does not see new sources.** Compile writes files; it cannot reach into a
62
+ conversation already in progress. A new command, agent or skill is available in the **next**
63
+ session. Say so rather than letting the operator wonder.
64
+ 6. **References are qualified** — `skills/<id>`, `agents/<id>`, `commands/<id>`, `collections/<id>`,
65
+ `users/<id>`. A bare name fails `check`.
66
+ 7. **Never edit generated output.** `.dreamteamer/`, `.claude/`, `.agents/`, `.cursor/` are all
67
+ overwritten and pruned on the next compile. If you found the thing you want to change in one of
68
+ those, you are in the wrong file.
69
+ 8. **The CLI refuses system-stored records on purpose.** `dt skills set …` will not work; edit the
70
+ module source and compile. The exceptions are the meta verbs that write sources *through* a
71
+ compile gate — `collections add`, `<collection> add-field`, `ui-views add|set` — which exist so
72
+ an uncompilable source can never land in history.
73
+ 9. **Never duplicate a procedure across records.** A command body that restates a skill, an agent
74
+ body that inlines its skill's steps, a command that re-types another command's prompt — each is two
75
+ copies that drift. Reference the one that owns it.
76
+ 10. **Module-shipped entities must not name a workspace's own users, accounts or paths.** Use
77
+ `@initiator` / `@me`, and read per-install values from `.env` naming the variable. A hard-coded
78
+ `users/<someone>` does not resolve in anyone else's workspace.
79
+
80
+ ## the loop
81
+
82
+ ```bash
83
+ # 1. author the source under modules/<module>/<kind>/
84
+ npm run compile # required — nothing is live before this
85
+ npm run check # refs, shapes, id patterns
86
+ npm run --silent dt -- status # when unsure whether the runtime is fresh
87
+ ```
88
+
89
+ `compile` fails **closed**: a source that cannot compile is rejected and the previous runtime
90
+ stands. Read the error — it names the file and, for a collision or an unresolved ref, both sides.
91
+
92
+ ## is this core, or is it a recipe?
93
+
94
+ If you are adding to the **engine's own** sources, the bar is higher than "useful". Core carries
95
+ only what the engine itself reads or the compile/check/run loop needs. Anything domain-shaped —
96
+ a collection about people, meetings, tasks, products, content — belongs in a module, and a *generic*
97
+ version of it belongs in the `recipes` repo rather than here.
98
+
99
+ **The test is: does the ENGINE read it?** Core's collections are the entity kinds the compiler itself
100
+ materializes, plus `users` (because `@me` resolves against it) and `repos` (because `repos ensure`
101
+ clones them). Everything else has been ejected on exactly that test — `teams` (nothing resolved a
102
+ team), `mounts` (a one-implementation adapter enum over an `.env` key), `module-registries` (zero
103
+ readers), `workflows`/`workflow-runs`/`workflow-triggers`/`cursors` and `migrations`/`migration-runs`
104
+ (measured unused), and finally `tasks`, whose only claim to core had been the workflow gate that no
105
+ longer exists. `npm run metrics` in the engine holds the budgets that keep this honest.
106
+
107
+ ## common mistakes
108
+
109
+ | mistake | reality |
110
+ |---|---|
111
+ | authoring under `.dreamteamer/` or `.claude/` | generated; the change vanishes on the next compile |
112
+ | a source folder at the workspace root | compile error by design — it goes in the workspace module |
113
+ | forgetting compile | the CLI, `check` and every harness still see the old shape |
114
+ | filename ≠ frontmatter `name` | the id lies; dispatch and invocation miss |
115
+ | telling the operator it works now | it works in their **next** session |
116
+ | writing a bespoke entity when a registered one would do | prefer a record over code, and an existing layout/skill over a new one |
117
+ | picking the entity by what is easiest to write | pick by how it should be triggered — that is what the choice encodes |
@@ -0,0 +1,44 @@
1
+ # agents
2
+
3
+ `modules/<module>/agents/<name>.agent.md`. The filename MUST equal the frontmatter `name`.
4
+ Compile copies the file to `.claude/agents/<name>.md` with one real transform — the `skills:`
5
+ frontmatter key becomes an in-body load instruction — so the record *is* the subagent definition.
6
+
7
+ **Before writing one, check it should not be a skill.** An agent costs a whole context. It earns
8
+ that only when the job needs a dispatchable persona with its own tool allowlist and skill set — a
9
+ router, a reviewer, a critic that must not share the caller's context.
10
+
11
+ ```yaml
12
+ ---
13
+ name: dreamteamer
14
+ description: master agent — routes a request to the right collection, skill or agent; the default operator for data-facing work
15
+ tools: [Read, Write, Edit, Grep, Glob, Bash]
16
+ skills: [skills/using-dreamteamer]
17
+ ---
18
+ ```
19
+
20
+ | field | required | notes |
21
+ |---|---|---|
22
+ | `name` | yes | must equal the filename; this is the id and the subagent's identity |
23
+ | `description` | yes | **when a dispatcher should pick this agent** — concrete triggers, not a role title |
24
+ | `tools` | no | tool allowlist; give only what the job needs. Omit for the harness default |
25
+ | `model` | no | override (`sonnet`, `opus`, …); omit to inherit |
26
+ | `skills` | no | `skills/<id>` refs, verified by `check` — loaded before the agent works |
27
+
28
+ The body is the `instructions` field (`x-body: true`) and becomes the subagent's system prompt.
29
+ Direct instructions: what to read, what to decide, in what order, what it hands back. Keep it tight
30
+ — a few sentences to a short paragraph. **The procedure lives in the referenced skills**, never
31
+ duplicated here.
32
+
33
+ Reference: the core module's `agents/dreamteamer.agent.md` — the only agent core ships, and the
34
+ shape to copy: short trigger description, tight tool list, 1–2 skill refs, a one-paragraph body.
35
+
36
+ ## common mistakes
37
+
38
+ | mistake | reality |
39
+ |---|---|
40
+ | a `description` that is a role title ("the research agent") | dispatchers match on triggers; say when to pick it |
41
+ | `skills: [using-dreamteamer]` | refs are qualified: `skills/using-dreamteamer` |
42
+ | pasting the skill's procedure into the body | two copies, one drifts |
43
+ | a broad `tools` list | a Write tool on a read-only reviewer is a footgun |
44
+ | creating an agent for a one-off instruction | a skill is usually the right answer |
@@ -0,0 +1,42 @@
1
+ # before you build — look for it first
2
+
3
+ **Core principle:** when the workspace can't do something, **look before you build** — and when you do
4
+ find something, **propose concretely, never install or copy silently.** The operator decides what
5
+ enters their workspace.
6
+
7
+ This replaced a standalone `discovering-new-capabilities` skill on 2026-07-31, whose premise — "find
8
+ and propose an **installable** module" — had been reversed: domain modules are no longer packages you
9
+ install. The looking still matters; the taking changed shape.
10
+
11
+ ## where to look, in order
12
+
13
+ | # | look at | what you get |
14
+ |---|---|---|
15
+ | 1 | **this workspace's own modules** — `.dreamteamer/manifest.yaml` names them | the thing may already exist under a name you didn't guess. Check the collections list and `dt <c> --help` before anything else |
16
+ | 2 | **the `recipes` repo** — reference modules maintained to be **copied and adapted** | a working module with its reasoning attached. Read its `using-recipes` skill for the adoption procedure |
17
+ | 3 | **a sibling workspace under `projects/`** | another vault may already have solved it concretely. That is a *reference*, not a source — and it holds real personal data, so read, never lift |
18
+ | 4 | **the engine's own surface** — `dt help`, plus the purpose-built verbs `help` omits | a verb missing from `help` is not a verb that doesn't exist |
19
+
20
+ Only after all four: build it, in the module that owns the concept.
21
+
22
+ ## how to propose
23
+
24
+ Say three things: **what you found**, **what adopting it would cost**, and **what you would delete
25
+ from it**. That last one is not politeness — adoption is mostly deletion, and a proposal that skips it
26
+ is asking the operator to accept a maximal module sight unseen. Name the `.env` keys, external
27
+ accounts and binaries it needs, because a skill whose setup nobody did is a skill that fails at the
28
+ worst possible moment.
29
+
30
+ Then stop and let them choose. Copying a recipe in is a one-way door in practice: from that moment the
31
+ copy is theirs to maintain, and nothing will later tell them it drifted from the original.
32
+
33
+ ## common mistakes
34
+
35
+ | mistake | reality |
36
+ |---|---|
37
+ | building because you didn't find it in 30 seconds | there are four places to look, and the first is this workspace |
38
+ | `npm i` / `git_modules` a recipe module | recipes are copied, not installed — importing re-creates the fork the split exists to avoid |
39
+ | copying a recipe in and keeping all of it | deleting what you won't use IS the adoption step |
40
+ | lifting from another workspace under `projects/` | those hold real personal data; read for reference only |
41
+ | proposing without naming the setup cost | the `.env` keys and the accounts are the actual price |
42
+ | installing or copying, then telling the operator | they decide what enters their workspace, before it enters |
@@ -0,0 +1,120 @@
1
+ # collections
2
+
3
+ One descriptor file: `modules/<module>/collections/<name>.collection.yaml`. The descriptor
4
+ **describes reality** — you do not edit records to fit an inferred schema.
5
+
6
+ ## the meta verbs (real, absent from `dt help`)
7
+
8
+ | goal | how |
9
+ |---|---|
10
+ | new collection from a template | `dt collections add --name research-docs --template docs` |
11
+ | templateless | `dt collections add --name <n>` — emits a minimal compilable schema |
12
+ | add a field | `dt <collection> add-field --name urgent --type boolean --default-value false` |
13
+ | change / drop a field | `dt <collection> update-field …` · `remove-field --name <f>` |
14
+ | delete a collection | `dt collections rm <name>` |
15
+ | what templates exist | `.dreamteamer/collection-templates/` |
16
+
17
+ `--type` is sugar over JSON Schema: `string`/`text`, `markdown`, `boolean`, `number`, `integer`,
18
+ `date`, `datetime`, `enum` (+`--options "a,b,c"`), `tags`, `reference` (+`--target <collection>`),
19
+ or a bare collection name for a reference into it. `--required true` widens `required`.
20
+
21
+ ⚠ **The meta verbs write the WORKSPACE module only.** To change a field on a collection another
22
+ module owns, either edit that module's descriptor by hand or add an `extends:` overlay.
23
+ ⚠ **There is no `collections rename`** — it refuses system sources. A rename is `git mv` of the
24
+ descriptor + edit `name`/`storage.path`/`suffix` + re-suffix every record, all in one commit.
25
+
26
+ ## `templates:` — a live shared field set
27
+
28
+ ```yaml
29
+ name: meetings
30
+ templates: [collection-templates/provenance] # merged at compile, every time
31
+ ```
32
+
33
+ - **`templates:` is not `extends:`.** `extends: <module>/<collection>` means "this descriptor
34
+ *overlays* another module's collection of the same name". `templates:` pulls in a field set and
35
+ says nothing about module layering. A descriptor may use both.
36
+ - **Precedence is template < base < overlay** — a descriptor always wins on a key it declares, so a
37
+ collection can tighten a templated field (add an enum, change a default) without touching the
38
+ template.
39
+ - **The template is a declared SOURCE of every consumer**, so editing it marks them stale and
40
+ `dt status` names them. Without that, the edit would apply to nothing and warn about nothing.
41
+ - **Template properties insert before the `x-body` field** — property order is form order, and a
42
+ record's body belongs last.
43
+ - ⚠ **A `templates:` ref must resolve inside the module that ships the descriptor**, or that module
44
+ cannot be installed or copied on its own. This is the single most expensive mistake in the
45
+ project's history: an extracted module whose every descriptor referenced a template living in the
46
+ *consuming* workspace could not compile into a bare workspace at all, and nobody noticed for
47
+ months. A collection-template id is an *identity* entity, so two modules cannot both ship
48
+ `provenance` — scope the id per module (`crm-provenance`).
49
+ - `--template X` at creation copies the fields in once. `templates:` is the live version; prefer it
50
+ for anything you will want to change in one place later.
51
+
52
+ ## judgment the descriptor can't tell you
53
+
54
+ - **`id.generate` takes creation-time values only** — `{{ created | date }}--{{ name | slug }}`,
55
+ never a mutable field (`due`, `status`, and note `created` itself is the moment the record is
56
+ WRITTEN, so a back-dated import files under the import month; derive from the domain's own date
57
+ field instead). `id.pattern` must accept everything the template can produce — non-latin titles
58
+ slug to a deterministic short hash, so `[a-z0-9-]` still holds.
59
+ - **The `x-` keywords carry the domain semantics.** `x-reference` (a target collection, or `"*"` for
60
+ any) is what lets `check` and `rename` follow a field. `x-body` marks the single field that becomes
61
+ the md body. `x-inverse` declares a two-way link and makes `check` enforce both directions.
62
+ `x-title-template` overrides how a VALUE of that field is labelled — rarely needed, because a
63
+ reference already inherits its TARGET collection's `title_template`; author it there instead, once,
64
+ rather than on every field pointing at it.
65
+ - **Do not enum a field after the fact.** Enumerating a vocabulary the records already violate makes
66
+ `check` fail on every pre-existing value. `dt <collection> values <field>` derives the real
67
+ vocabulary from the data — a filter dropdown gets it for free without locking the set.
68
+ - **`icon` / `group`** are the studio nav's material-symbol icon and folder; ungrouped collections
69
+ list at the top. `list_fields` is the SEED a module ships, not a competing source of truth — a
70
+ ui-view's `columns` REPLACES it.
71
+
72
+ ## extending another module's collection
73
+
74
+ ```yaml
75
+ name: tasks
76
+ extends: '@dreamteamer/dreamteamer/tasks'
77
+ schema:
78
+ properties:
79
+ urgent: { type: boolean, default: false }
80
+ ```
81
+
82
+ Compile merges `schema.properties` per-property, unions `required`, and takes `storage`/`id` from
83
+ the base. Two modules extending the same base are applied in module-discovery order and the last
84
+ wins on any shared key — so keep extenders **disjoint** and never rely on the collision. Two
85
+ same-name descriptors where neither declares `extends` is a compile error; so is an `extends` value
86
+ that does not name the actual base.
87
+
88
+ ⚠ **An overlay can add fields but cannot remove an inherited one.** If the shape is wrong for the
89
+ module rather than just for this workspace, fix the base.
90
+
91
+ ## registering an existing data folder
92
+
93
+ 1. Sample the files: derive `suffix`/`codec` from the filenames (`<id>.<suffix>.<ext>`) and the id
94
+ `pattern` from the id shapes actually present.
95
+ 2. Collect frontmatter keys across files → `properties`; infer types from values. A string field
96
+ with ≤10 distinct values, repeats and ≥80% fill is probably an `enum` — but see the warning
97
+ above. Values shaped `<collection>/<id>` are `x-reference` fields. No frontmatter at all →
98
+ `required: []` with a comment saying why.
99
+ 3. **Never edit the records to fit an inferred schema.** Describe reality, compile, run `check`,
100
+ then decide which violations are worth fixing in the data.
101
+
102
+ **Evolving a schema:** widening (a new optional field, a new enum value) is always safe; narrowing
103
+ (a new required field, a removed enum value) needs the data cleaned first. A shape change across many existing
104
+ records is a **one-shot script you write, run once and commit with the records it rewrote** — there is
105
+ no `dt migrate`. A record-based migration mechanism shipped in July 2026 and was removed on 2026-07-31
106
+ having never once been used: every real schema change in this project's history went around it as a
107
+ script. If you write one, say in the commit message what it did, because that message is the only
108
+ ledger.
109
+
110
+ ## common mistakes
111
+
112
+ | mistake | reality |
113
+ |---|---|
114
+ | a mutable field in `id.generate` (`due`, `status`) | ids must never change |
115
+ | `id.generate` from `created` for imported records | `created` is when the record was written, not when the thing happened |
116
+ | tightening `required` before cleaning the data | check floods; widen, rewrite the data, then narrow |
117
+ | a second same-name descriptor without `extends` | compile error by design |
118
+ | a plain string where a ref belongs | use `x-reference` so `check` and `rename` can follow it |
119
+ | a `templates:` ref pointing at another module | that module can no longer be copied or installed alone |
120
+ | inventing a collection for a one-off extraction | a collection is for things that recur; prefer the nearest real one |
@@ -0,0 +1,69 @@
1
+ # commands and command-bindings
2
+
3
+ ## commands
4
+
5
+ `modules/<module>/commands/<name>.command.md`. A command is a **canned prompt a human
6
+ deliberately types**. Compile copies the file verbatim to `.claude/commands/<name>.md` — that file
7
+ is what makes `/<name>` work.
8
+
9
+ ```markdown
10
+ ---
11
+ name: process-inbox
12
+ description: triage every open task assigned to me, one at a time
13
+ argument-hint: "[assignee]"
14
+ ---
15
+ load this workspace's tasks skill. list my open tasks
16
+ (`npm run --silent dt -- tasks list --assignee users/<me> --status todo`), then walk them one at a
17
+ time: restate it, ask me to keep / reassign / drop, apply the decision with `tasks set`.
18
+ done when the list is empty or I say stop.
19
+ ```
20
+
21
+ - **`description`** is the only thing the operator sees in the `/` picker — say what running it does.
22
+ - **The body is the `prompt` field** (`x-body: true`) and is sent **verbatim as the turn**. Write
23
+ imperatives to yourself — which skill to load, which records to touch, what "done" means — not
24
+ documentation about the command.
25
+ - **`$ARGUMENTS` / `$1`** are substituted by the harness with whatever was typed after the command
26
+ name. Pass-through frontmatter: `argument-hint`, `allowed-tools`, `model`,
27
+ `disable-model-invocation`.
28
+ - **Convention:** a record command takes record ref(s) (`/transcribe-recording meetings/<id> …`); a
29
+ collection command takes the collection name. A multi-select invocation carries the **eligible**
30
+ refs only, space-separated, so nothing is silently dropped.
31
+
32
+ ## command-bindings — which commands apply to which records
33
+
34
+ `modules/<module>/command-bindings/<command>--<collection>.command-binding.yaml`. An m2m
35
+ record joining a command to a collection, so **every (command, record) pair has a state**.
36
+
37
+ ```yaml
38
+ command: commands/transcribe-recording
39
+ collection: collections/meeting-recordings
40
+ target: record # or `collection` for commands that need no record
41
+ can-enter: { file: { _nempty: true } }
42
+ can-exit: { transcription: { _nempty: true } }
43
+ description: audio present, no transcript yet
44
+ ```
45
+
46
+ - **`can-exit` doubles as the done-detector**, giving three states: `available` (enter ✓, exit ✗),
47
+ `done` (exit ✓ — shown completed, not disabled), `not-applicable` (enter ✗). That is what makes
48
+ the work queue **derivable from the data** rather than remembered.
49
+ - **Filters support one-hop OUTBOUND ref traversal**: `{ recording: { file: { _nempty: true } } }`
50
+ resolves the ref and evaluates the sub-condition on the target. Array refs use `_some` semantics.
51
+ A missing resolver, a dangling ref or a non-ref value **narrows**, never widens.
52
+ - ⚠ **INBOUND refs are unsupported.** "a summary referencing this meeting exists" is inexpressible,
53
+ so a command whose completion is only visible from the other side ships without a `can-exit` and
54
+ never shows done. Say so rather than faking it with a proxy field.
55
+ - **Pick the signal carefully.** "Is it transcribed?" is `transcription._nempty` (the provenance
56
+ object), NOT `transcript._nempty` — a body can be filled by hand with no provenance, which is
57
+ exactly the case worth flagging as not-yet-done.
58
+ - Read the queue with `dt commands for <collection>[/<id>] [--ids a,b] [--json]`.
59
+
60
+ ## common mistakes
61
+
62
+ | mistake | reality |
63
+ |---|---|
64
+ | filename ≠ `name` | the invocable name comes from the filename; the id then lies |
65
+ | a body that restates a whole skill | say "load `<skill>`" — one copy of the procedure |
66
+ | a body that describes the command | it is sent verbatim as a turn; write imperatives |
67
+ | a command for something a skill should auto-trigger | commands need the operator to remember them |
68
+ | a `can-exit` on a `target: collection` binding | there is no record to evaluate it against; compile warns |
69
+ | a `can-exit` over an inbound ref | unsupported — the command will never report done |