dreamteamer 0.13.0 → 0.13.2

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 (2) hide show
  1. package/package.json +2 -2
  2. package/src/compile.js +29 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dreamteamer",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "A workspace compiler for coding agents — 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>",
@@ -29,7 +29,7 @@
29
29
  "node": ">=20"
30
30
  },
31
31
  "bin": {
32
- "dreamteamer": "./bin/dreamteamer.js"
32
+ "dreamteamer": "bin/dreamteamer.js"
33
33
  },
34
34
  "files": [
35
35
  "NOTICE",
package/src/compile.js CHANGED
@@ -12,6 +12,7 @@ import { walk, patternRe } from './records.js';
12
12
  import { unknownOperators } from './filter.js';
13
13
  import {
14
14
  normalizeNamespaces, namespaceProblems, unqualifiedProblems, defaultStoragePath, storageOverlaps,
15
+ baseNameOf,
15
16
  } from './namespace.js';
16
17
  // circular on paper in earlier versions — safe: both sides only
17
18
  // call at run time, same pattern as store.js ↔ compile.js.
@@ -717,7 +718,14 @@ export function compile({ root, pkg }) {
717
718
  // browse page, the CLI and the extension then read ONE field instead of each carrying its
718
719
  // own title-caser. Authored values always win — `??=` never overwrites. After the ajv gate
719
720
  // on purpose: a malformed property must fail as a bad schema, not as a TypeError here.
720
- merged.title ??= titleCase(name);
721
+ // ⚠ From the BARE name, not the qualified one. A namespace is the FOLDER a collection sits in,
722
+ // not part of what it is called — every surface that draws the namespace as a folder was
723
+ // otherwise saying it twice on one screen ("R&D > Rnd Prototypes", "Family > Health >
724
+ // Health Documents"). Workspaces had already worked around it by hand-authoring a title on
725
+ // every namespaced collection, which is the tell: a derivation nobody can use is not a
726
+ // default. `baseNameOf` resolves against the DECLARED list, so an undeclared prefix — which
727
+ // is not a namespace — keeps its whole name in the label.
728
+ merged.title ??= titleCase(baseNameOf(name, namespaces));
721
729
  const labelProps = merged.schema?.properties ?? {};
722
730
  // how a RECORD of this collection is labelled — the probe presentation.js has always used
723
731
  // for `meta.title_field`, promoted to an authorable field. Reference fields pointing here
@@ -841,6 +849,9 @@ export function compile({ root, pkg }) {
841
849
  // The descriptor already documented the correct behaviour — ui-views.collection.yaml: "An
842
850
  // unregistered id degrades visibly rather than erroring" — and the surface already implements
843
851
  // it (presets.ts#resolveRendererEntry falls back to table). Decision 195.
852
+ // ui-views' own field names, read from the descriptor that was just merged rather than hardcoded —
853
+ // a list in here would silently stop covering a field the moment ui-views grew one.
854
+ const ownFields = load(entries.get(path.join('collections', 'ui-views.collection.yaml'))?.bytes?.toString('utf8') ?? '')?.schema?.properties ?? {};
844
855
  for (const [rt, e] of entries) {
845
856
  if (!rt.startsWith('ui-views/')) continue;
846
857
  const view = load(e.bytes.toString('utf8'));
@@ -854,6 +865,23 @@ export function compile({ root, pkg }) {
854
865
  if (view?.filter && JSON.stringify(view.filter).includes('"@me"')) {
855
866
  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" } }).`);
856
867
  }
868
+ // ⚠ `options` is a deliberately OPEN object — every key it does not own rides through untouched
869
+ // to whichever surface renders the layout. That openness is right, and it has one sharp edge: a
870
+ // field that belongs ONE LEVEL UP, written inside it, is accepted, saved, round-tripped and read
871
+ // by nobody. `options.filter` cost a real afternoon — the view drew all 429 rows of a collection
872
+ // it was supposed to narrow to 90, and neither compile nor check nor the surface said a word.
873
+ //
874
+ // This is the same rule the block above states, not an exception to it: the engine DOES interpret
875
+ // `filter`, so a `filter` it will never be handed is a value it can catch. A warning rather than a
876
+ // failure because `options` is open by contract and a surface may legitimately want a key that
877
+ // collides — but the operator has to be told, because the symptom is a view that looks like it
878
+ // works.
879
+ const shadowed = view?.options && typeof view.options === 'object' && !Array.isArray(view.options)
880
+ ? Object.keys(view.options).filter((k) => k in ownFields)
881
+ : [];
882
+ for (const k of shadowed) {
883
+ console.warn(`⚠ ${rt}: options.${k} is read by nothing — \`${k}\` is a field of ui-views itself, one level up. move it out of \`options\`.`);
884
+ }
857
885
  }
858
886
 
859
887
  // ---- command-binding validation --------------------------------------------------