unoverse 0.1.165 → 0.1.166

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/lib/publish.mjs CHANGED
@@ -155,9 +155,19 @@ export async function publish(args) {
155
155
  // Deploy is a SYNC: what left the workspace leaves the universe, said before it happens.
156
156
  for (const r of plan.remove ?? []) console.log(` ${c.red}-${c.off} ${label(r)} ${c.dim}(no longer in this workspace — will be removed)${c.off}`);
157
157
 
158
+ if (plan.syncUnavailable) {
159
+ console.log(` ${c.yellow}⚠${c.off} this universe cannot answer the sync check yet ${c.dim}(its server predates it)${c.off}`);
160
+ console.log(` ${c.dim}items you deleted from the workspace were NOT reconciled. Update the universe`);
161
+ console.log(` ${c.dim}(unoverse update on its box), then deploy again to remove them.${c.off}`);
162
+ }
163
+
158
164
  const toSend = plan.create.length + plan.update.length + (plan.remove?.length ?? 0);
159
165
  if (!toSend) {
160
- console.log(`\n ${c.green}✓${c.off} nothing to do, ${project} is up to date\n`);
166
+ if (plan.syncUnavailable) {
167
+ console.log(`\n ${c.green}✓${c.off} nothing new to send ${c.dim}(deletions unchecked, see above)${c.off}\n`);
168
+ } else {
169
+ console.log(`\n ${c.green}✓${c.off} nothing to do, ${project} is up to date\n`);
170
+ }
161
171
  process.exit(plan.refused.length ? 1 : 0);
162
172
  }
163
173
  if (flag("dry-run")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unoverse",
3
- "version": "0.1.165",
3
+ "version": "0.1.166",
4
4
  "description": "The Unoverse front door — create a Studio project, a universe, or a client app, and launch Studio.",
5
5
  "license": "SEE LICENSE IN README.md",
6
6
  "type": "module",
@@ -24,7 +24,17 @@ import { collectProject } from "./collect.js";
24
24
  export async function lintForPublish(designRoot, project) {
25
25
  const { lintDefinitions } = await import("../lint/design/index.mjs");
26
26
  const result = lintDefinitions(designRoot);
27
- const mine = result.problems.filter((p) => !p.file || p.file.includes(`/${project}/`) || !p.file.includes("/design/"));
27
+ /**
28
+ * SCOPED TO THE PROJECT BEING DEPLOYED, on paths that may be relative. The old
29
+ * check looked for "/design/" with a leading slash, which a relative path never
30
+ * has, so every other project's findings leaked through and a bpp comment blocked
31
+ * a lyceum deploy (observed live 2026-08-21). Boundary regexes hold for absolute
32
+ * and relative paths alike; findings outside the design tree (the tree itself,
33
+ * nodes, prompts) still always count.
34
+ */
35
+ const inDesign = (f) => /(^|\/)design\//.test(f);
36
+ const inProject = (f) => new RegExp(`(^|/)design/${project}(/|$)`).test(f);
37
+ const mine = result.problems.filter((p) => !p.file || !inDesign(p.file) || inProject(p.file));
28
38
  return { problems: mine, errors: mine.filter((p) => p.level === "error") };
29
39
  }
30
40
  /**
@@ -65,10 +75,18 @@ project = "", fetchImpl = fetch) {
65
75
  plan.remove.push(r);
66
76
  }
67
77
  }
68
- // An older universe answers this op with an error: no removals proposed, the
69
- // additive behavior every deploy had before. Never a failure.
78
+ else {
79
+ // An older universe answers this op with an error: no removals proposed, the
80
+ // additive behavior every deploy had before — but SAID, never silent. A deploy
81
+ // that could not check deletions and prints "up to date" is lying by omission
82
+ // (observed live 2026-08-21: an hour of retests against a server that could
83
+ // not answer, each one claiming there was nothing to do).
84
+ plan.syncUnavailable = true;
85
+ }
86
+ }
87
+ catch {
88
+ plan.syncUnavailable = true; /* unreachable universe also fails later, on the real sends */
70
89
  }
71
- catch { /* unreachable universe fails later, on the real sends */ }
72
90
  }
73
91
  for (const item of items) {
74
92
  // ASK THE GATE, not the item store. `/api/items` is deliberately shut (§9.10), so a
@@ -54,8 +54,27 @@ function lintFile(file) {
54
54
  // never a style the SDK resolves — token law governs the inside, not the envelope.
55
55
  if (!isFixture(file) && !isHook(file) && !isManifest(file))
56
56
  src.split("\n").forEach((line, i) => {
57
- if (RAW_VALUE.test(line) && !/^\s*"?appWidth"?\s*:/.test(line))
58
- report("error", file, `raw value. Token names only; add/scale a token in the org styles instead (LAW 1, docs/design/06): ${line.trim()}`, i + 1);
57
+ /**
58
+ * THE RULE JUDGES CODE, NEVER COMMENTARY. A comment explaining a contrast
59
+ * decision legitimately names the hex it rejected ("#E9F1FF dropped it to
60
+ * 1.14:1"), and scanning the raw line flagged exactly the best-documented
61
+ * files (observed live 2026-08-21: two comment hexes blocked a deploy).
62
+ * YAML comments start at an unquoted #, at line start or after whitespace;
63
+ * everything from there is prose. A quoted "#fff" VALUE stays visible to the
64
+ * rule, which is the whole point of the law.
65
+ */
66
+ let code = line, inS = false, inD = false;
67
+ for (let j = 0; j < line.length; j++) {
68
+ const ch = line[j];
69
+ if (ch === "'" && !inD) inS = !inS;
70
+ else if (ch === '"' && !inS) inD = !inD;
71
+ else if (ch === "#" && !inS && !inD && (j === 0 || /\s/.test(line[j - 1]))) {
72
+ code = line.slice(0, j);
73
+ break;
74
+ }
75
+ }
76
+ if (RAW_VALUE.test(code) && !/^\s*"?appWidth"?\s*:/.test(code))
77
+ report("error", file, `raw value. Token names only; add/scale a token in the org styles instead (LAW 1, docs/design/06): ${code.trim()}`, i + 1);
59
78
  });
60
79
 
61
80
  let json;