dreamteamer 0.8.0 → 0.9.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/src/schema-ops.js +54 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dreamteamer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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>",
|
package/src/schema-ops.js
CHANGED
|
@@ -64,6 +64,37 @@ export function workspaceSystemDir(ws, kind) {
|
|
|
64
64
|
return kindDir(wm ? path.join(ws.root, 'modules', wm) : ws.root, kind);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* WHERE A COLLECTION'S DESCRIPTOR ACTUALLY LIVES — asked of the manifest, not assumed.
|
|
69
|
+
*
|
|
70
|
+
* `renameCollection` used to derive this from `workspaceSystemDir`, which silently meant "only the
|
|
71
|
+
* workspace module's own collections can be renamed". That is the wrong line. The guard exists to
|
|
72
|
+
* stop a write that will be ERASED, and the thing that erases writes is `npm install` — so the test
|
|
73
|
+
* is `node_modules/`, not "which module". A module whose sources are inline in the workspace repo is
|
|
74
|
+
* under the same git history as everything else and is perfectly safe to rewrite; refusing it made
|
|
75
|
+
* `collections rename` unusable for exactly the migration it was built for, because a workspace's
|
|
76
|
+
* domain collections almost always live in a module.
|
|
77
|
+
*
|
|
78
|
+
* Returns `{ dir, sources }` — the kind dir to write into (the SAME module the descriptor came from,
|
|
79
|
+
* so a rename never teleports a collection into the workspace module), and every descriptor source
|
|
80
|
+
* that contributed, so the caller can refuse the cases this cannot honestly do.
|
|
81
|
+
*/
|
|
82
|
+
function descriptorSourceDir(ws, name) {
|
|
83
|
+
const entry = readManifest(ws.root)?.entries?.[`collections/${name}.collection.yaml`];
|
|
84
|
+
// `sources` mixes the descriptor with any collection-templates it merged, so match on the shape
|
|
85
|
+
// of a descriptor path for THIS collection. A namespaced name is nested, hence the full suffix.
|
|
86
|
+
const suffix = `collections/${name}.collection.yaml`;
|
|
87
|
+
const sources = (entry?.sources ?? [])
|
|
88
|
+
.map((s) => s.path)
|
|
89
|
+
.filter((p) => p.endsWith(suffix));
|
|
90
|
+
if (!sources.length) return { dir: null, sources };
|
|
91
|
+
// The BASE descriptor is the one to move. With an overlay present there are two, and the overlay's
|
|
92
|
+
// `extends` names the base by its old qualified id — rewriting that is a second, different
|
|
93
|
+
// migration, so the caller refuses rather than half-doing it.
|
|
94
|
+
const moduleRoot = path.join(ws.root, sources[0].slice(0, sources[0].length - suffix.length));
|
|
95
|
+
return { dir: kindDir(moduleRoot, 'collections'), sources };
|
|
96
|
+
}
|
|
97
|
+
|
|
67
98
|
// ---- ops ------------------------------------------------------------------------
|
|
68
99
|
|
|
69
100
|
export function createCollection(ws, store, { name, template, namespace }) {
|
|
@@ -143,6 +174,15 @@ export function removeCollection(ws, store, name, { force = false } = {}) {
|
|
|
143
174
|
* already scopes prose to `[[wikilinks]]` (decision 7) — a fresh `oldName/` pattern would have to
|
|
144
175
|
* relearn both, and would corrupt `data/tasks/` in a path or a URL on its first outing. N passes over
|
|
145
176
|
* the record files is the price, and at human scale it is worth paying for reusing the correct code.
|
|
177
|
+
*
|
|
178
|
+
* ⚠ MEASURED 2026-08-17, so the cost is a number rather than a hope: a 2,291-record collection in a
|
|
179
|
+
* 3,391-file workspace — gk-brain's `finance-transactions` — takes **3 minutes**, of which 142s is
|
|
180
|
+
* system time. That is 7.7M file reads to rewrite ZERO references, because the pass runs per id
|
|
181
|
+
* whether or not anything points at the collection. Tolerable for a one-time migration and left
|
|
182
|
+
* alone on that basis; it is O(records x files), so a workspace 3x larger pays 27 minutes. The fix
|
|
183
|
+
* when it is needed is a batch entry point on the store that reads each file ONCE and loops the ref
|
|
184
|
+
* set in memory, with `text.includes(oldName + '/')` as a cheap NEGATIVE filter only — never as the
|
|
185
|
+
* matcher, for the reason above.
|
|
146
186
|
*/
|
|
147
187
|
export function renameCollection(ws, store, oldName, newName) {
|
|
148
188
|
const d = store.descriptor(oldName); // throws with the known-collection list if absent
|
|
@@ -156,10 +196,21 @@ export function renameCollection(ws, store, oldName, newName) {
|
|
|
156
196
|
if (store.descriptors.has(newName)) throw new Error(`collection "${newName}" already exists`);
|
|
157
197
|
if (d.storage.base === 'runtime') throw new Error(`"${oldName}" is a compiled source, not a data collection — it cannot be renamed`);
|
|
158
198
|
|
|
159
|
-
|
|
160
|
-
|
|
199
|
+
// The descriptor is renamed IN THE MODULE THAT SHIPS IT — see `descriptorSourceDir`. Two cases
|
|
200
|
+
// this refuses, both because doing them halfway is worse than not doing them:
|
|
201
|
+
const { dir: sourceDir, sources } = descriptorSourceDir(ws, oldName);
|
|
202
|
+
if (sources.length > 1) {
|
|
203
|
+
throw new Error(`"${oldName}" is overlaid — ${sources.length} modules contribute a descriptor (${sources.join(', ')}).\n the overlay's \`extends\` names the base by its current id, so renaming the base alone would break it. merge or remove the overlay first.`);
|
|
204
|
+
}
|
|
205
|
+
if (sources.some((p) => p.split(path.sep).includes('node_modules'))) {
|
|
206
|
+
throw new Error(`"${oldName}" ships from node_modules (${sources[0]}) — a write there is erased by the next \`npm install\`. rename it in its own repo and release, or overlay it with \`extends\`.`);
|
|
207
|
+
}
|
|
208
|
+
const src = sourceDir
|
|
209
|
+
? path.join(sourceDir, `${oldName}.collection.yaml`)
|
|
210
|
+
: path.join(workspaceSystemDir(ws, 'collections'), `${oldName}.collection.yaml`);
|
|
211
|
+
const dest = path.join(sourceDir ?? workspaceSystemDir(ws, 'collections'), `${newName}.collection.yaml`);
|
|
161
212
|
if (!fs.existsSync(src)) {
|
|
162
|
-
throw new Error(`"${oldName}"
|
|
213
|
+
throw new Error(`"${oldName}" has no writable descriptor source — the manifest names none under a module in this workspace. it may be contributed by the engine itself; overlay it with \`extends\` instead.`);
|
|
163
214
|
}
|
|
164
215
|
|
|
165
216
|
const doc = load(fs.readFileSync(src, 'utf8'));
|