dreamteamer 0.7.0 → 0.8.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/package.json +1 -1
- package/skills/building-dreamteamer/SKILL.md +10 -7
- package/skills/building-dreamteamer/references/commands.md +1 -1
- package/skills/building-dreamteamer/references/ui-views.md +10 -5
- package/skills/using-dreamteamer/SKILL.md +7 -6
- package/skills/using-dreamteamer/references/records.md +1 -1
- package/src/compile.js +13 -4
- package/src/init.js +20 -19
- package/src/server.js +4 -14
- package/collections/users.collection.yaml +0 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dreamteamer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
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>",
|
|
@@ -62,7 +62,7 @@ These were duplicated across seven skills; they are true for all of them.
|
|
|
62
62
|
conversation already in progress. A new command, agent or skill is available in the **next**
|
|
63
63
|
session. Say so rather than letting the operator wonder.
|
|
64
64
|
6. **References are qualified** — `skills/<id>`, `agents/<id>`, `commands/<id>`, `collections/<id>`,
|
|
65
|
-
|
|
65
|
+
and `<collection>/<id>` for any record. A bare name fails `check`.
|
|
66
66
|
7. **Never edit generated output.** `.dreamteamer/`, `.claude/`, `.agents/`, `.cursor/` are all
|
|
67
67
|
overwritten and pruned on the next compile. If you found the thing you want to change in one of
|
|
68
68
|
those, you are in the wrong file.
|
|
@@ -73,9 +73,11 @@ These were duplicated across seven skills; they are true for all of them.
|
|
|
73
73
|
9. **Never duplicate a procedure across records.** A command body that restates a skill, an agent
|
|
74
74
|
body that inlines its skill's steps, a command that re-types another command's prompt — each is two
|
|
75
75
|
copies that drift. Reference the one that owns it.
|
|
76
|
-
10. **Module-shipped entities must not name a workspace's own
|
|
77
|
-
|
|
78
|
-
`
|
|
76
|
+
10. **Module-shipped entities must not name a workspace's own people, accounts or paths.** Read
|
|
77
|
+
per-install values from `.env` naming the variable, and leave who-did-what to a collection the
|
|
78
|
+
workspace owns. A hard-coded `contacts/<someone>` does not resolve in anyone else's workspace.
|
|
79
|
+
⚠ **There is no `@me` since 0.8.0** — it expanded to `users/<slug>`, and `users` is gone. A
|
|
80
|
+
ui-view filter still using it is a compile error, not a view that quietly shows nothing.
|
|
79
81
|
|
|
80
82
|
## the loop
|
|
81
83
|
|
|
@@ -97,11 +99,12 @@ a collection about people, meetings, tasks, products, content — belongs in a m
|
|
|
97
99
|
version of it belongs in the `recipes` repo rather than here.
|
|
98
100
|
|
|
99
101
|
**The test is: does the ENGINE read it?** Core's collections are the entity kinds the compiler itself
|
|
100
|
-
materializes, plus `
|
|
101
|
-
|
|
102
|
+
materializes, plus `repos` (because `repos ensure` clones them). Everything else has been ejected on
|
|
103
|
+
exactly that test — `teams` (nothing resolved a
|
|
102
104
|
team), `mounts` (a one-implementation adapter enum over an `.env` key), `module-registries` (zero
|
|
103
105
|
readers), `workflows`/`workflow-runs`/`workflow-triggers`/`cursors` and `migrations`/`migration-runs`
|
|
104
|
-
(measured unused),
|
|
106
|
+
(measured unused), `users` (0.8.0 — its justification was circular: core because `@me` resolved
|
|
107
|
+
against it, and `@me` existed because it was core), and finally `tasks`, whose only claim to core had been the workflow gate that no
|
|
105
108
|
longer exists. `npm run metrics` in the engine holds the budgets that keep this honest.
|
|
106
109
|
|
|
107
110
|
## common mistakes
|
|
@@ -13,7 +13,7 @@ description: triage every open task assigned to me, one at a time
|
|
|
13
13
|
argument-hint: "[assignee]"
|
|
14
14
|
---
|
|
15
15
|
load this workspace's tasks skill. list my open tasks
|
|
16
|
-
(`npm run --silent dt -- tasks list --
|
|
16
|
+
(`npm run --silent dt -- tasks list --status todo`), then walk them one at a
|
|
17
17
|
time: restate it, ask me to keep / reassign / drop, apply the decision with `tasks set`.
|
|
18
18
|
done when the list is empty or I say stop.
|
|
19
19
|
```
|
|
@@ -5,7 +5,7 @@ route plus the id of an already-registered layout, plus how to shape the data. N
|
|
|
5
5
|
|
|
6
6
|
The surface reads compiled ui-view records at boot: `nav` becomes a sidebar entry, `path` becomes a
|
|
7
7
|
live route, `target: list` renders the named `layout` over the collection with `filter`/`options`
|
|
8
|
-
applied
|
|
8
|
+
applied. After authoring: compile, then reload the surface.
|
|
9
9
|
|
|
10
10
|
```yaml
|
|
11
11
|
path: /inbox
|
|
@@ -13,10 +13,14 @@ nav: { label: Inbox, icon: inbox, order: 1 }
|
|
|
13
13
|
target: list
|
|
14
14
|
collection: collections/tasks
|
|
15
15
|
layout: table
|
|
16
|
-
filter: {
|
|
17
|
-
options: { columns: [name, status, due
|
|
16
|
+
filter: { status: { _eq: todo } } # operator objects, never a bare value
|
|
17
|
+
options: { columns: [name, status, due], sort: -due }
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
⚠ **`@me` no longer exists** — it was removed with the `users` collection in 0.8.0, and a filter using
|
|
21
|
+
it is a compile error rather than a view that quietly shows nothing. Filter on a field this workspace
|
|
22
|
+
owns; where a person is genuinely the axis, the workspace ships its own collection of people.
|
|
23
|
+
|
|
20
24
|
| field | required | notes |
|
|
21
25
|
|---|---|---|
|
|
22
26
|
| `path` | yes | the route — `/inbox`, `/views/meetings/recent` |
|
|
@@ -53,7 +57,8 @@ through the same compile gate.
|
|
|
53
57
|
|---|---|
|
|
54
58
|
| a `layout` id you assumed exists | compile validates it only for `target: list`; otherwise the view renders nothing |
|
|
55
59
|
| bare `collection: tasks` | qualified refs only |
|
|
56
|
-
| `filter: {
|
|
60
|
+
| `filter: { status: "todo" }` | filters are operator objects: `{ status: { _eq: todo } }` |
|
|
61
|
+
| a filter using `@me` | gone in 0.8.0 with `users` — compile refuses it by name |
|
|
57
62
|
| a column the schema does not have | dropped silently — the row loses that value with no error |
|
|
58
63
|
| a ui-view that restates the built-in fallback | a record to maintain for zero gain |
|
|
59
|
-
| a module ui-view
|
|
64
|
+
| a module ui-view naming one person | a hard-coded id resolves in no other workspace |
|
|
@@ -63,8 +63,8 @@ when you're not sure the runtime is fresh.
|
|
|
63
63
|
| the workspace lacks the capability entirely | `building-dreamteamer` → `references/before-you-build.md` |
|
|
64
64
|
|
|
65
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
|
|
67
|
-
|
|
66
|
+
collections, not by core. Read that module's own skills. Core knows about entity kinds and `repos`,
|
|
67
|
+
and deliberately nothing else — including nothing about people. There is no `users` collection.
|
|
68
68
|
|
|
69
69
|
## conventions
|
|
70
70
|
|
|
@@ -84,9 +84,10 @@ collections, not by core. Read that module's own skills. Core knows about entity
|
|
|
84
84
|
- **validate after bulk edits**: `npm run check` reports violations and never modifies files.
|
|
85
85
|
- workspace-level rules live in `CLAUDE.md`, and a workspace's decision log (where one exists) wins
|
|
86
86
|
over older documents.
|
|
87
|
-
- **session greeting** — surface the operator's inbox
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
- **session greeting** — surface the operator's inbox from whatever collection this workspace uses for
|
|
88
|
+
work, e.g. `npm run --silent dt -- tasks list --status todo`. ⚠ **there is no `users` collection and
|
|
89
|
+
no `@me`** (both removed in 0.8.0); read the operator from `git config user.name` at the point you
|
|
90
|
+
need one, and never filter on a person unless this workspace owns a collection of them.
|
|
90
91
|
|
|
91
92
|
## common mistakes
|
|
92
93
|
|
|
@@ -95,6 +96,6 @@ collections, not by core. Read that module's own skills. Core knows about entity
|
|
|
95
96
|
| editing something under `.dreamteamer/` | generated + gitignored; the change vanishes on the next compile |
|
|
96
97
|
| changing a source and not compiling | the harness and `check` still read the stale runtime |
|
|
97
98
|
| hand-writing a record the CLI could add | skips validation, id generation and defaults |
|
|
98
|
-
| bare refs (`ada`, `data/
|
|
99
|
+
| bare refs (`ada`, `data/contacts/x.contact.md`) | refs are `<collection>/<id>`; anything else fails check |
|
|
99
100
|
| assuming a write was committed | it was not, unless `auto-commit` is on — `dt status` says what is pending |
|
|
100
101
|
| `git add -A` in a shared tree | steals another session's uncommitted work, invisibly |
|
package/src/compile.js
CHANGED
|
@@ -520,10 +520,13 @@ export function compile({ root, pkg }) {
|
|
|
520
520
|
const base = group.find((g) => !g.doc.extends);
|
|
521
521
|
if (base) collOwner.set(name, base.moduleName);
|
|
522
522
|
}
|
|
523
|
-
// The engine's own nine collections are an implicit dependency of every module:
|
|
524
|
-
//
|
|
525
|
-
// on the host it cannot run without would be ceremony, not
|
|
526
|
-
|
|
523
|
+
// The engine's own nine collections are an implicit dependency of every module: the entity kinds
|
|
524
|
+
// the compiler materializes, plus `repos` (because `repos ensure` clones them). Requiring every
|
|
525
|
+
// module to declare a dependency on the host it cannot run without would be ceremony, not
|
|
526
|
+
// verification. ⚠ `users` was in this set until 0.8.0 — a module still declaring
|
|
527
|
+
// `x-reference: users` now FAILS here, which is the intended loud outcome rather than a ref
|
|
528
|
+
// pointing at a collection nothing provides.
|
|
529
|
+
const CORE_COLLECTIONS = new Set([...KINDS, ...DERIVED_KINDS, 'repos']);
|
|
527
530
|
const wsDir = config['workspace-module'];
|
|
528
531
|
const wsModuleName = wsDir
|
|
529
532
|
? sources.find((s) => rel(s.root) === path.join('modules', wsDir))?.name
|
|
@@ -802,6 +805,12 @@ export function compile({ root, pkg }) {
|
|
|
802
805
|
// fail at compile, not silently at render (review finding 5)
|
|
803
806
|
const badOps = view?.filter ? [...unknownOperators(view.filter)] : [];
|
|
804
807
|
if (badOps.length) fail(`${rt}: unknown filter operator(s) ${badOps.join(', ')}`);
|
|
808
|
+
// `@me` died with the `users` collection in 0.8.0. It expanded to `users/<slug>`, so on this
|
|
809
|
+
// engine it can only ever match nothing — and a filter that narrows to zero rows is the exact
|
|
810
|
+
// silent failure this block exists to prevent. Refuse it by name, with the fix.
|
|
811
|
+
if (view?.filter && JSON.stringify(view.filter).includes('"@me"')) {
|
|
812
|
+
fail(`${rt}: filter uses "@me", which was removed with the \`users\` collection in 0.8.0 — it would now match nothing.\n filter on a field this workspace owns instead (e.g. { status: { _eq: "todo" } }).`);
|
|
813
|
+
}
|
|
805
814
|
}
|
|
806
815
|
|
|
807
816
|
// ---- command-binding validation --------------------------------------------------
|
package/src/init.js
CHANGED
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
import { execFileSync, spawnSync } from 'node:child_process';
|
|
5
5
|
import fs from 'node:fs';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
-
import { dump } from './yaml.js';
|
|
8
|
-
import { slugOrHash } from './template.js';
|
|
9
7
|
import { discoverModules, KINDS } from './compile.js';
|
|
10
8
|
import { Store } from './store.js';
|
|
11
9
|
|
|
@@ -76,7 +74,17 @@ export function init({ flags = {} } = {}) {
|
|
|
76
74
|
'data-path': dataPath,
|
|
77
75
|
harnesses,
|
|
78
76
|
'gitignore-runtime-folder': true,
|
|
79
|
-
|
|
77
|
+
// The workspace's own sources live in `modules/default/`, and the folder is named for its ROLE,
|
|
78
|
+
// not for the vault. It used to be named after the workspace, and that name went stale twice in
|
|
79
|
+
// one repo (`hq3` → `gk`, decision 213 reversed by 224) — each rename rewriting every path that
|
|
80
|
+
// RESOLVES while the historical documents deliberately kept the old spelling, so a stale-looking
|
|
81
|
+
// `modules/hq3` was correct in prose and a bug in a path. A role name cannot go stale.
|
|
82
|
+
//
|
|
83
|
+
// `default` is deliberately the same word `RESERVED_NAMESPACES` holds (namespace.js): this module
|
|
84
|
+
// owns the DEFAULT-namespace collections, and the default namespace is the empty prefix. The one
|
|
85
|
+
// misreading it invites — `default/tasks` — is a compile error whose message states the rule.
|
|
86
|
+
// Override with `--workspace-module <name>` if a workspace wants its own spelling.
|
|
87
|
+
'workspace-module': flags['workspace-module'] ?? 'default',
|
|
80
88
|
'git-modules': {},
|
|
81
89
|
disable: [],
|
|
82
90
|
...pkg.dreamteamer,
|
|
@@ -102,23 +110,16 @@ export function init({ flags = {} } = {}) {
|
|
|
102
110
|
fs.mkdirSync(path.join(root, dataPath), { recursive: true });
|
|
103
111
|
fs.mkdirSync(path.join(root, 'state'), { recursive: true });
|
|
104
112
|
|
|
105
|
-
//
|
|
113
|
+
// NO user record is seeded, and there is no `users` collection — both removed from core in 0.8.0.
|
|
114
|
+
// It failed the "does the ENGINE read it?" test on a circular justification: `users` was core
|
|
115
|
+
// because `@me` resolved against it, and `@me` existed because `users` was core. Nothing else in
|
|
116
|
+
// the compiler, the store or `check` ever read a user record. One record per workspace, whose only
|
|
117
|
+
// job was to restate `git config user.name` in a file that then had to AGREE with it — and when it
|
|
118
|
+
// disagreed the symptom was an empty inbox with no error (decision 99b), a trap that now cannot
|
|
119
|
+
// happen because there is nothing to disagree with.
|
|
106
120
|
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
// /inbox view work by construction. A workspace where a user record is authored by hand under a
|
|
110
|
-
// different id gets an EMPTY inbox with no error (decision 99b) — hence the setup-script step in
|
|
111
|
-
// any workspace with a second person.
|
|
112
|
-
//
|
|
113
|
-
// No `everyone` team is seeded: `teams` was removed from core 2026-07-31. It was a one-record
|
|
114
|
-
// abstraction with no reader — nothing in the engine, in `check`, or in any view resolved a team.
|
|
115
|
-
const gitName = tryGit(root, ['config', 'user.name']) ?? 'operator';
|
|
116
|
-
const gitEmail = tryGit(root, ['config', 'user.email']);
|
|
117
|
-
const userId = slugOrHash(gitName);
|
|
118
|
-
const usersDir = path.join(root, dataPath, 'users');
|
|
119
|
-
fs.mkdirSync(usersDir, { recursive: true });
|
|
120
|
-
const userFile = path.join(usersDir, `${userId}.user.yaml`);
|
|
121
|
-
if (!fs.existsSync(userFile)) fs.writeFileSync(userFile, dump({ name: gitName, ...(gitEmail ? { email: gitEmail } : {}) }));
|
|
121
|
+
// A workspace that needs people as records ships its own collection (a module's `contacts` already
|
|
122
|
+
// does), and reads the operator from git where it needs one. `teams` went the same way 2026-07-31.
|
|
122
123
|
|
|
123
124
|
// .gitignore + .env.example (append-if-missing, never clobber)
|
|
124
125
|
appendMissing(path.join(root, '.gitignore'), GITIGNORE);
|
package/src/server.js
CHANGED
|
@@ -14,12 +14,6 @@ import { matchesFilter } from './filter.js';
|
|
|
14
14
|
import { sortRows } from './temporal.js';
|
|
15
15
|
import { commandsFor, recordResolver } from './record-commands.js';
|
|
16
16
|
import { distinctValues } from './field-values.js';
|
|
17
|
-
import { slugOrHash } from './template.js';
|
|
18
|
-
|
|
19
|
-
// git calls whose failure we CATCH must not print git's own error: execFileSync forwards the
|
|
20
|
-
// child's stderr to ours unless told otherwise, so a handled "not a git repository" still
|
|
21
|
-
// reached the user's terminal. stdout stays piped because we read it.
|
|
22
|
-
const QUIET = ['ignore', 'pipe', 'ignore'];
|
|
23
17
|
|
|
24
18
|
|
|
25
19
|
export function startServer(ws, { port = 8080, host = '127.0.0.1' } = {}) {
|
|
@@ -44,13 +38,10 @@ export function startServer(ws, { port = 8080, host = '127.0.0.1' } = {}) {
|
|
|
44
38
|
const api = express.Router();
|
|
45
39
|
api.use((req, res, next) => { freshStore(); next(); });
|
|
46
40
|
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
operatorId = slugOrHash(execFileSync('git', ['config', 'user.name'], { cwd: ws.root, stdio: QUIET }).toString().trim());
|
|
52
|
-
} catch { /* no git identity — @me filters simply won't narrow */ }
|
|
53
|
-
|
|
41
|
+
// ⚠ NO `user` on /info since 0.8.0, and no `@me` — both went with the `users` collection. The
|
|
42
|
+
// token expanded to the literal string `users/<slug of git user.name>`, so with no such
|
|
43
|
+
// collection it could only ever produce a dangling reference. A workspace that wants
|
|
44
|
+
// operator-scoped views filters on a field it owns.
|
|
54
45
|
api.get('/info', (req, res) => {
|
|
55
46
|
const manifest = readManifest(ws.root);
|
|
56
47
|
const stale = staleness(ws.root);
|
|
@@ -60,7 +51,6 @@ export function startServer(ws, { port = 8080, host = '127.0.0.1' } = {}) {
|
|
|
60
51
|
compiled: manifest?.compiled,
|
|
61
52
|
modules: manifest?.modules ?? [],
|
|
62
53
|
ui: manifest?.ui ?? [], // module UI bundles staged at /ui/<name>/app.js
|
|
63
|
-
user: operatorId,
|
|
64
54
|
stale: stale.stale?.length ?? 0,
|
|
65
55
|
collections: [...store.descriptors.keys()],
|
|
66
56
|
});
|
|
@@ -1,21 +0,0 @@
|
|
|
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
|