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,73 @@
1
+ # skills
2
+
3
+ A folder-shape record: `modules/<module>/skills/<name>/SKILL.md`. The folder name IS the id,
4
+ and extra files (references, scripts, assets) travel with the folder.
5
+
6
+ ## create — and don't
7
+
8
+ **Create:** a technique that wasn't obvious, will recur, and applies beyond one record.
9
+ **Don't:** one-off fixes (just do them), workspace conventions (`CLAUDE.md`), or anything a
10
+ validator can enforce mechanically — automate that instead and save prose for judgment calls.
11
+
12
+ ## the description is a TRIGGER, never a summary
13
+
14
+ The description is what a session scans to decide whether to load the skill. It must state **when to
15
+ use it** — symptoms, situations, verbs — and must NOT summarize the workflow: a description that
16
+ summarizes the process becomes a shortcut the agent follows *instead of reading the skill*, silently
17
+ skipping the steps the summary dropped.
18
+
19
+ ```yaml
20
+ # ✖ workflow summary — the agent may follow this and never read the body
21
+ description: transcribes audio via whisper then fills the speaker map then commits
22
+ # ✔ trigger only
23
+ description: use when a meeting recording or pasted transcript needs to land in its meeting record
24
+ ```
25
+
26
+ Third person, lead with "use when", pack in searchable keywords (error strings, symptoms, synonyms,
27
+ tool names). Name = verb-first gerund (`writing-x`, `detecting-y`).
28
+
29
+ ## body craft
30
+
31
+ - **Lean.** Aim under ~500 words in `SKILL.md`; concrete over complete — what to read first, the
32
+ exact shape to produce, the runnable commands. Cross-reference sibling skills instead of repeating
33
+ them.
34
+ - **Progressive disclosure for anything larger.** Keep `SKILL.md` as the digest — routing plus the
35
+ rules that apply everywhere — and put per-topic detail in `references/<topic>.md` that the digest
36
+ points at by name. A session then pays for the map, not the whole territory. `building-dreamteamer`
37
+ and `using-dreamteamer` are both built this way.
38
+ - **One excellent example** beats many mediocre ones; real, adapted from a live workspace.
39
+ - **Discipline skills** (rules under pressure) must close loopholes explicitly: name the workarounds
40
+ and forbid them, add a rationalization table ("excuse → reality") and a red-flags list. A rule
41
+ without its loopholes named will be rationalized around.
42
+ - Flowcharts only for genuinely non-obvious decisions; tables for reference.
43
+ - **Write down the traps.** The most valuable line in most of these skills is a measured failure
44
+ mode — a limit, a silent-success bug, a stale-config symptom — because it transfers perfectly and
45
+ cost someone a day to find. Include the measurement; drop the identities around it.
46
+
47
+ ## no personal or account data
48
+
49
+ Skills ship with modules and are read by any operator — never bake in secrets, keys, names, emails
50
+ or account-specific paths. Per-install values go through `.env` (gitignored); the skill names the
51
+ variable it reads and what happens when it is missing.
52
+
53
+ ## heavy assets
54
+
55
+ Models, caches and venvs live in dot-prefixed subfolders (`.models/`, `.cache/`, `.venv/`) with a
56
+ **skill-local `.gitignore`**. Compile skips dot-prefixed names, and local ignore rules travel with
57
+ the folder through extraction.
58
+
59
+ ⚠ **Runnable assets must be referenced at their SOURCE path**, not under `.claude/skills/…` — that
60
+ is generated output, wiped and rebuilt every compile, and anything with local dependencies
61
+ (a `package.json`, a venv) only works where it was installed. Note the cost: a source path embeds
62
+ the module name, so renaming a module means updating every skill that names its own scripts.
63
+
64
+ ## verify before calling it done
65
+
66
+ Compile, check, and — for any skill whose misreading would cost real work — **dispatch a
67
+ fresh-context subagent with a realistic task** and watch whether it finds, loads and follows the
68
+ skill correctly. What it gets wrong is what the skill still fails to say; fix and re-run. Reading
69
+ your own skill proves nothing.
70
+
71
+ **The reference bar:** the superpowers plugin skillset. Study a few of its skills before writing
72
+ one — its structure (overview / when-to-use with symptoms / quick reference / common mistakes), its
73
+ discipline patterns and its lean prose are the standard dreamteamer skills are held to.
@@ -0,0 +1,78 @@
1
+ # ui components
2
+
3
+ **Components are prebuilt code, not records.** There is no ui-components collection, no descriptor,
4
+ no compile step for the component itself. This is an ordinary Vue coding session against a module's
5
+ `studio/` source tree — *code registers everything renderable; yaml records configure and assemble.*
6
+
7
+ ⚠ **Reach for a record first.** A `ui-view` pointing an existing `layout`/`edit`/`view` id at your
8
+ data costs no build and no code to maintain. Write a component only when nothing registered can do
9
+ the job.
10
+
11
+ ## the registries
12
+
13
+ | registry | what it holds | bound by |
14
+ |---|---|---|
15
+ | `edits` | field editors + whole-record Edit pages | a field's `edit` meta; `scope: 'record'` for a page |
16
+ | `views` | field read-renderers + whole-record View pages | a field's `view` meta |
17
+ | `lists` | collection browse arrangements (table, cards, kanban, …) | a `ui-view` record's `layout` |
18
+ | `apps`, `panels`, `operations` | routed subtrees, dashboard panels, flow operations | registration alone |
19
+
20
+ The host's own built-ins register through the exact same door a module uses — read its
21
+ `register-defaults.ts` as the reference.
22
+
23
+ ## the module contract
24
+
25
+ A module ships a browser entry (`studio/app.js`, or a built `studio/dist/app.js`) that compile stages
26
+ to `.dreamteamer/ui/<module>/app.js`; its **default export** is a register function:
27
+
28
+ ```js
29
+ export default ({ registerEdit, registerView, registerList, registerApp, registerPanel }) => {
30
+ registerList({ id: 'kanban', name: 'Kanban', component: MyKanban, fillsHeight: true });
31
+ };
32
+ ```
33
+
34
+ - **Never bundle Vue.** The host exposes its own Vue as `window.Vue` *before* importing any module
35
+ entry; two copies break reactivity silently. Use `window.Vue`, or mark `vue` external and map it to
36
+ the global.
37
+ - **Give every entry an explicit stable `id`.** That string is what a `ui-view.layout` or a field's
38
+ `edit`/`view` meta names. Changing it breaks every binding.
39
+ - **`types: []` opts an Edit/View out of kind-inference.** A non-empty `types` array makes it a *kind
40
+ default* for that wire type — which means a bare `string` or `json` field anywhere in the workspace
41
+ silently gets your bespoke widget. Most custom components should stay `types: []`.
42
+ - **`fillsHeight: true`** declares that this List owns its own scrolling; the page then stops
43
+ scrolling vertically and hands the List the leftover height. Layouts that should grow the page
44
+ (table, cards) must not set it.
45
+ - **Failures are isolated, not surfaced.** A module whose entry fails to import or throws while
46
+ registering is caught, warned and skipped — the surface still loads, your component just never
47
+ appears. **Read the browser console**; nothing else will tell you.
48
+
49
+ ⚠ **A module registering a CORE id WINS, silently.** Module bundles load after the built-ins and the
50
+ registry is a `Map.set`, so a module `kanban` shadows the core `kanban` with no warning anywhere —
51
+ which once meant an operator had been using a strictly worse board for weeks and nothing could reveal
52
+ it from the outside. Never reuse a core id unless shadowing is exactly what you mean.
53
+
54
+ ⚠ **Portalled UI cannot rely on scoped CSS.** Menus, popovers and dialogs are teleported outside the
55
+ component subtree, so a scoped rule looks correct in the source and is simply absent at runtime — the
56
+ failure mode is invisible chrome, not an error. Define those styles globally.
57
+
58
+ ## workflow
59
+
60
+ 1. Write the component in the module's `studio/` tree.
61
+ 2. Register it in that module's `app.js` with a stable `id`.
62
+ 3. Plain-JS `app.js` needs no build; a module wanting a toolchain builds to `studio/dist/app.js`
63
+ itself. Declare any Lists in `package.json` `dreamteamer.studio.layouts`, then compile to stage it.
64
+ 4. Look at it in the real surface.
65
+ 5. **Bind it** — a field's `edit`/`view` meta, or a `ui-view.layout` naming the `id`. Only this step
66
+ touches records.
67
+
68
+ ## common mistakes
69
+
70
+ | mistake | reality |
71
+ |---|---|
72
+ | looking for a ui-components collection | there isn't one; this is code |
73
+ | bundling Vue | two copies; reactivity dies silently |
74
+ | assuming a registration error is reported | swallowed by fault isolation — read the console |
75
+ | `types: ['string']` on a bespoke widget | it becomes the default for every plain string field |
76
+ | omitting `id`, or renaming it later | the id is the binding surface |
77
+ | binding before building | the id does not exist yet; the view renders nothing |
78
+ | reusing a core layout id | silently shadows the built-in with no warning |
@@ -0,0 +1,59 @@
1
+ # ui-views
2
+
3
+ `modules/<module>/ui-views/<name>.ui-view.yaml`. **A ui-view is a binding, not code** — a
4
+ route plus the id of an already-registered layout, plus how to shape the data. Nothing to build.
5
+
6
+ The surface reads compiled ui-view records at boot: `nav` becomes a sidebar entry, `path` becomes a
7
+ live route, `target: list` renders the named `layout` over the collection with `filter`/`options`
8
+ applied (`@me` resolves to the current operator). After authoring: compile, then reload the surface.
9
+
10
+ ```yaml
11
+ path: /inbox
12
+ nav: { label: Inbox, icon: inbox, order: 1 }
13
+ target: list
14
+ collection: collections/tasks
15
+ layout: table
16
+ filter: { assignee: { _eq: "@me" } } # operator objects, never a bare value
17
+ options: { columns: [name, status, due, run], sort: -due }
18
+ ```
19
+
20
+ | field | required | notes |
21
+ |---|---|---|
22
+ | `path` | yes | the route — `/inbox`, `/views/meetings/recent` |
23
+ | `target` | yes | `list`, `item`, or `page` (freestanding) |
24
+ | `layout` | yes | the id of a **registered** layout (`table`, `cards`, `kanban`, `calendar`, `map`, + module-declared) |
25
+ | `collection` | for `list`/`item` | qualified: `collections/<name>` |
26
+ | `nav` | no | `label`/`icon`/`order`; omit for direct-link-only views |
27
+ | `default` | no | `true` makes this the collection's DEFAULT view at `/content/<collection>` |
28
+
29
+ ## default views vs named views
30
+
31
+ A collection's default presentation **is an ordinary ui-view record** — `default: true`, path
32
+ `/content/<collection>`. A named view is the same record with its own path plus a `nav.label`. One
33
+ shape, one home, both agent-writable and both diffable in git.
34
+
35
+ ⚠ **A view's `options.columns` REPLACES the descriptor's `list_fields`; it does not merge.** And a
36
+ column naming a field the schema lacks is **dropped, not fallen back from** — which is how a core
37
+ inbox view asking for `title` on a collection whose field is `name` rendered every row nameless with
38
+ no error. Check the descriptor's real field names against every column you write.
39
+
40
+ ⚠ **`options.sort` must be written even when empty** (`sort: ''`), or "unsorted" cannot round-trip
41
+ and silently reverts to a fallback ordering on the next load.
42
+
43
+ ## the CLI can write these
44
+
45
+ `dt ui-views add|set|rm` — `set` takes dotted keys (`options.sort=-date`) and derives the record id
46
+ with the descriptor's own template, so a view saved from the CLI and one saved from the UI land on
47
+ the **same record**. This is the one system-stored kind with full CLI write support, because it goes
48
+ through the same compile gate.
49
+
50
+ ## common mistakes
51
+
52
+ | mistake | reality |
53
+ |---|---|
54
+ | a `layout` id you assumed exists | compile validates it only for `target: list`; otherwise the view renders nothing |
55
+ | bare `collection: tasks` | qualified refs only |
56
+ | `filter: { assignee: "@me" }` | filters are operator objects: `{ assignee: { _eq: "@me" } }` |
57
+ | a column the schema does not have | dropped silently — the row loses that value with no error |
58
+ | a ui-view that restates the built-in fallback | a record to maintain for zero gain |
59
+ | a module ui-view filtered to a named user | use `@me`; a hard-coded id breaks elsewhere |
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: using-dreamteamer
3
+ description: always load first — describes this dreamteamer workspace, its collections, conventions and your pending tasks
4
+ ---
5
+
6
+ # using dreamteamer
7
+
8
+ this is a **dreamteamer** workspace: collections, skills, agents and commands are records compiled
9
+ from sources into a runtime the harness reads.
10
+
11
+ **core principle:** read the compiled runtime, write the sources, compile — then `dt commit` to publish.
12
+
13
+ ## when to use
14
+
15
+ load this **first, every session** in this repo. reload mid-session on any of these symptoms: you're
16
+ about to guess a collection's fields; you can't tell whether the file to edit lives under
17
+ `modules/*/<kind>/` or `.dreamteamer/`; you wrote something and the harness didn't notice; you're
18
+ unsure which skill owns the job in front of you.
19
+
20
+ **this file is the MAP, not the procedure.** Detail lives in two references beside it, loaded on
21
+ demand:
22
+
23
+ | load | when |
24
+ |---|---|
25
+ | `references/records.md` | reading, creating, updating, renaming or deleting any record — the CLI verbs, hand-writing rules, the hard rules about ids and renames |
26
+ | `references/git-events.md` | "what changed while I was away" — `dt changes`, and how record events are derived from git history |
27
+
28
+ ## the contract
29
+
30
+ | concern | where | rule |
31
+ |---|---|---|
32
+ | schemas (read) | `.dreamteamer/collections/*.collection.yaml` | the single source of truth for what exists and its shape. **never edit under `.dreamteamer/`** — generated and gitignored |
33
+ | provenance | `.dreamteamer/manifest.yaml` | which module shipped which entry |
34
+ | sources (write) | `modules/<module>/` — **including the workspace's own**, the `dreamteamer.workspace-module` named in `package.json` | a source folder at the workspace ROOT is a compile ERROR. same-name collisions across modules are compile errors too. after ANY source change: `npm run compile` |
35
+ | content records | `data/<collection>/` | per each descriptor's `storage.path` |
36
+ | operational records | `state/<collection>/` | whatever a module declares there; core ships none |
37
+
38
+ - a record is a `<id>.<suffix>.<ext>` file (or a folder, for folder-shape collections). **the id is
39
+ the path** inside the collection folder minus suffix and extension — nested folders join in:
40
+ `data/meetings/2026/07/standup.meeting.md` ⇒ id `2026/07/standup`.
41
+ - **references are `<collection>/<id>`** strings — always qualified, greppable, never a bare name and
42
+ never a file path.
43
+
44
+ ## the CLI is the front door
45
+
46
+ `npm run --silent dt -- help` is the command surface — don't learn the generic verbs and flags from
47
+ prose; prose drifts. it does **not** list the purpose-built verbs some collections have
48
+ (`collections add`, `<collection> add-field`, `repos ensure`) — those live in the skill that owns
49
+ them, and a verb absent from `help` still works.
50
+
51
+ what you need to know *about* the CLI: collection verbs validate hard (invalid writes, **including
52
+ unknown fields**, are rejected before disk) **and commit for you** with the right subject. `npm run
53
+ compile` after every source change, `npm run check` after bulk edits, `npm run --silent dt -- status`
54
+ when you're not sure the runtime is fresh.
55
+
56
+ ## routing
57
+
58
+ | the request is about | load |
59
+ |---|---|
60
+ | a record — read, create, update, rename, delete | `references/records.md` |
61
+ | authoring anything under a module's source folders — a collection, field, skill, command, agent, ui-view, or component code | `building-dreamteamer` |
62
+ | "what changed while I was away" | `references/git-events.md` |
63
+ | the workspace lacks the capability entirely | `building-dreamteamer` → `references/before-you-build.md` |
64
+
65
+ Domain work — meetings, contacts, tasks, content, design — is owned by the **module** that ships those
66
+ collections, not by core. Read that module's own skills. Core knows about entity kinds, `users` and
67
+ `repos`, and deliberately nothing else.
68
+
69
+ ## conventions
70
+
71
+ - **a write puts a record on disk; `dreamteamer commit` publishes it.** committing is POLICY —
72
+ `"auto-commit"` in the workspace's `package.json`, default off — not a property of the write. so
73
+ commit when a logical change is complete, and run `dt status` if you are unsure what is pending.
74
+ subjects still read `dreamteamer: <verb> <detail>` for a single record (`dt commit` composes them
75
+ from git's own status letters), and a multi-record commit says what it swept.
76
+ - **one commit per REPO.** a module can own its records (`owns-data` in its package.json), and git
77
+ has no cross-repo commit — so a rename whose inbound refs live in another repo is TWO commits.
78
+ `dt commit` prints both. `dt commit <collection> …` scopes it; `--dry-run` shows the set first.
79
+ - **never `git add -A`, `git add .`, or `git commit -a`.** stage explicit paths. more than one agent
80
+ can be working in a tree, and a blanket add silently commits whatever another session has
81
+ uncommitted right now — under your subject, leaving `git status` clean and the damage invisible.
82
+ the CLI's own writes are pathspec-scoped for exactly this reason, which is why it is the preferred
83
+ path for record writes.
84
+ - **validate after bulk edits**: `npm run check` reports violations and never modifies files.
85
+ - workspace-level rules live in `CLAUDE.md`, and a workspace's decision log (where one exists) wins
86
+ over older documents.
87
+ - **session greeting** — surface the operator's inbox: `npm run --silent dt -- tasks list --assignee
88
+ users/<user> --status todo`. ⚠ the current user is a record in `data/users/` whose id is
89
+ `slug(git config user.name)`; when those disagree the inbox comes back **empty with no error**.
90
+
91
+ ## common mistakes
92
+
93
+ | mistake | why it bites |
94
+ |---|---|
95
+ | editing something under `.dreamteamer/` | generated + gitignored; the change vanishes on the next compile |
96
+ | changing a source and not compiling | the harness and `check` still read the stale runtime |
97
+ | hand-writing a record the CLI could add | skips validation, id generation and defaults |
98
+ | bare refs (`ada`, `data/users/x.user.md`) | refs are `<collection>/<id>`; anything else fails check |
99
+ | assuming a write was committed | it was not, unless `auto-commit` is on — `dt status` says what is pending |
100
+ | `git add -A` in a shared tree | steals another session's uncommitted work, invisibly |
@@ -0,0 +1,64 @@
1
+ # detecting data changes via git
2
+
3
+ **Core principle:** record events are never observed live — they are **derived from git history**. A
4
+ closed laptop loses nothing, and every derivation is auditable and replayable forever. History IS the
5
+ queue; there is no events file, and there never should be.
6
+
7
+ ## the verb
8
+
9
+ ```bash
10
+ dt changes # since the last commit
11
+ dt changes --since <sha> # since any commit
12
+ dt changes --json # for a program
13
+ ```
14
+
15
+ Per record it reports the collection, the id and one of `item-added` / `item-updated` /
16
+ `item-removed`, grouped by collection with counts. **Read-only by construction** — there is no cursor
17
+ to advance and nothing is stored, so running it twice is free and running it wrong costs nothing.
18
+
19
+ ## when to use
20
+
21
+ The operator asks what changed or what they missed; you are reconciling after a batch of edits or a
22
+ bulk rewrite; you want to know what a range of commits actually did to the data.
23
+
24
+ **Not for:** ordinary "show me the diff" questions — plain `git log` / `git diff` is better. Not for
25
+ reading or writing individual records (`references/records.md`).
26
+
27
+ ## the mechanism
28
+
29
+ 1. **Diff** — `git diff --name-status <sha>..HEAD -- data/ state/`.
30
+ 2. **Map** — each path maps to a collection via the compiled descriptors' `storage.path`
31
+ (longest-prefix match; the suffix + codec must also match a record file, and folder-shape
32
+ collections match on their `entry`). The id is the path inside the collection folder minus
33
+ `.<suffix>.<ext>`. **A changed path that matches no descriptor is not a record** and is skipped —
34
+ source and runtime churn are not data events.
35
+ 3. **Classify** — the git status letter becomes the event: `A` → added, `M` → updated, `D` → removed.
36
+ **A rename emits removed + added**; there is deliberately no `item-renamed` event, because the id IS
37
+ the path and a moved record is a different record as far as any consumer is concerned.
38
+
39
+ ## what this used to be, and why the rest went
40
+
41
+ Until 2026-07-31 this mechanism was the front half of `dt sync`, which also matched
42
+ `workflow-triggers` records, **created `workflow-runs`**, and advanced a per-evaluator cursor. That
43
+ whole layer was removed: measured over three days it had produced 1 workflow record and 9 runs all from
44
+ a single burst, **7 of them abandoned mid-flight**, against a cursor that had not advanced — while the
45
+ work it was meant to automate was being done by a chain of commands a person runs.
46
+
47
+ The derivation survived because it is the half that was actually used, for catch-up. Worth keeping from
48
+ that design if automation is ever rebuilt:
49
+
50
+ - **Never store an event queue.** A queue drifts from reality; history cannot.
51
+ - **Any evaluator must be idempotent over a range** — re-running must not act twice. The old design
52
+ keyed that on `trigger + item + commit`, which is the shape to reuse.
53
+ - **A migration is not a data event.** A bulk rewrite looks like N added records to git, so anything
54
+ acting on events must be scoped past it rather than run over it.
55
+
56
+ ## common mistakes
57
+
58
+ | mistake | reality |
59
+ |---|---|
60
+ | hand-rolling the diff + path mapping | `dt changes` is the mechanism; a hand-roll misses folder-shape records and the rename split |
61
+ | treating any changed path as a record | only paths matching a descriptor's `storage.path` + suffix + codec are records |
62
+ | diffing the whole tree | scope to `data/` + `state/`; source and runtime churn are not item events |
63
+ | writing an events file to "remember" what changed | history is the record; anything you write can drift from it |
64
+ | reading `dt changes` output as a to-do list | it says what changed, not what it means — the judgement is yours |
@@ -0,0 +1,102 @@
1
+ # working with structured data files
2
+
3
+ **core principle:** the CLI is the default path — it validates before disk, generates the id,
4
+ materializes defaults and commits with the right subject. direct file edits stay first-class,
5
+ but then *you* owe every rule below.
6
+
7
+ ## when to use
8
+
9
+ any time a record is read, created, changed, renamed or removed — a task, a contact, a meeting,
10
+ a doc, anything under `data/` or `state/`. load it especially when you catch yourself about to
11
+ `mv` a record file, hand-write frontmatter from memory, or set a field you haven't read in the
12
+ descriptor.
13
+
14
+ **not for:** schema changes (`building-dreamteamer` → `references/collections.md`), or system-stored records — skills, agents,
15
+ commands, ui-views, collections. those are *sources*: edit the file under the owning
16
+ module (`modules/<module>/<kind>/`) and run `npm run compile`; the CLI refuses them on
17
+ purpose.
18
+
19
+ ## the verbs
20
+
21
+ `npm run --silent dt -- help` lists the generic record verbs and their flags. read them there.
22
+ but **`help` is not the whole surface** — collections with a purpose-built verb (`collections add`,
23
+ `<collection> add-field`, `repos ensure`) don't appear in it, and a verb missing from `help`
24
+ is not a verb that doesn't exist. when a skill names a verb, use the verb.
25
+
26
+ what the help text can't tell you either way:
27
+
28
+ - **validation is hard and it includes unknown fields.** a typo'd key (`--assinee`) is rejected
29
+ with nothing written, same as a dangling ref, a bad enum value (the error echoes the value it
30
+ got) or an id that misses `id.pattern`. a rejected write leaves no partial state.
31
+ - **every write verb commits by itself**, with the right subject — never stack another commit
32
+ on top.
33
+ - `set <id> <field>=` with an empty value **removes** the field; array fields take a
34
+ comma-separated value (`--attendees contacts/a,contacts/b`).
35
+ - `--json` works on every verb — use it whenever you're going to parse the output.
36
+ - `npm run check` validates the whole workspace after the fact: report-only, never rewrites.
37
+
38
+ ## before writing anything
39
+
40
+ read the compiled descriptor: `.dreamteamer/collections/<collection>.collection.yaml`.
41
+ it defines `storage` (path/codec/shape/suffix), `id` (`generate` template + `pattern`), and
42
+ `schema` (JSON Schema; the `x-` keywords carry the domain semantics — `x-reference`, `x-body`,
43
+ `x-title-template`). It also carries `title` (what to CALL the collection) and `title_template` (how
44
+ to label one of its records) — both resolved by compile from the id unless authored.
45
+
46
+ ## writing a record by hand
47
+
48
+ **default to `dt <collection> add`** — id, defaults, validation and commit in one line. hand-write
49
+ only when the CLI can't express the value: a nested map, or a long structured body.
50
+
51
+ when you do, don't reconstruct the shape from the schema — **`dt <collection> get <existing-id>
52
+ --json` prints the exact shape a valid record has**: which fields, which ref forms, dates as
53
+ strings. copy a sibling, change what differs, and:
54
+
55
+ - put the file where the id says: the id IS its path inside `storage.path`, minus suffix and
56
+ extension (folder-shape records are a folder named `<id>` holding the descriptor's `entry`).
57
+ ids are built from **creation-time** values only, never a mutable field, and must satisfy
58
+ `id.pattern`.
59
+ - **materialize defaults explicitly** — write `status: todo` even though it's the schema default;
60
+ a file should be legible without its schema.
61
+ - `npm run check` is the only validation a hand-write gets. run it before you commit, once:
62
+ `dreamteamer: <collection> add <id>` (or `set <id>`, for an update).
63
+
64
+ example — `data/tasks/2026-07-25--fix-login-flow.task.md` (`md` codec: frontmatter holds the
65
+ fields, the body is the single `x-body: true` field):
66
+
67
+ ```markdown
68
+ ---
69
+ title: Fix login flow
70
+ status: todo
71
+ assignee: users/ada
72
+ due: '2026-07-28'
73
+ ---
74
+ Users report the login button does nothing on mobile.
75
+ ```
76
+
77
+ ## the hard rules
78
+
79
+ **never hand-rename or `mv` a record file** — the id IS the path, so a rename silently dangles
80
+ every inbound reference; `rename` moves the file and rewrites all refs in one commit. **never
81
+ delete a referenced record** (`rm` refuses for a reason — retarget first). **a changed title
82
+ never changes the id.** **one mutation, one commit.**
83
+
84
+ ## common mistakes
85
+
86
+ | mistake | reality |
87
+ |---|---|
88
+ | `mv data/tasks/old.task.md …/new.task.md` | every inbound ref now dangles. use `dt … rename`. |
89
+ | renaming a file because the title changed | the id is not a display name — edit the field. |
90
+ | reaching for `--force` to get past the refuse | it leaves the inbound refs dangling. retarget them first — unless `rm` named a *prose* mention (a skill's example, a doc), which isn't a real reference and `check` won't flag. |
91
+ | hand-writing a record "because it's quicker" | no validation, no defaults, no id check, no commit. `dt … add` is one line. |
92
+ | omitting schema defaults from a hand-written file | the file stops being legible without the schema. |
93
+ | unquoted `due: 2026-07-28` | dreamteamer parses with CORE_SCHEMA so it stays a string *here*, but any default-schema YAML reader turns it into a timestamp. quote dates when hand-writing. |
94
+ | CLI-editing a skill / agent / command / collection | system sources — edit the module file, then `npm run compile`. |
95
+ | committing again after a CLI verb | the verb already committed; you'd sweep unrelated work into it. |
96
+
97
+ ## red flags — stop
98
+
99
+ - you're about to `mv`, `cp` or `rm` a file under `data/` or `state/` directly
100
+ - you're writing a reference you haven't confirmed resolves
101
+ - you're editing anything under `.dreamteamer/` (generated, gitignored)
102
+ - you finished a bulk edit and haven't run `npm run check`