clearotron 0.3.2-beta.1 → 0.3.2-beta.3

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 (53) hide show
  1. package/.env.example +29 -0
  2. package/INSTALL.md +2 -0
  3. package/README.md +15 -10
  4. package/bin/onboard.mjs +4 -4
  5. package/build-info.json +2 -2
  6. package/docs/CLIENT-MCP.md +54 -54
  7. package/docs/DELIVERY.md +16 -15
  8. package/docs/E2E.md +8 -8
  9. package/docs/GLOSSARY.md +2 -2
  10. package/docs/INTAKE.md +11 -11
  11. package/docs/ONBOARDING.md +17 -16
  12. package/docs/PORTAL.md +18 -16
  13. package/docs/README.md +10 -10
  14. package/docs/SECURITY.md +20 -20
  15. package/docs/architecture/01-product-overview.md +17 -16
  16. package/docs/architecture/02-architecture.md +6 -6
  17. package/docs/architecture/03-run-lifecycle.md +7 -7
  18. package/docs/architecture/04-configuration-reference.md +15 -13
  19. package/docs/architecture/05-config-governance.md +39 -30
  20. package/docs/architecture/05-customer-profiles.md +35 -35
  21. package/docs/architecture/06-operations-runbook.md +20 -20
  22. package/docs/architecture/07-quality-and-audit.md +17 -17
  23. package/docs/architecture/08-development-guide.md +3 -3
  24. package/docs/architecture/09-security-and-data.md +27 -27
  25. package/docs/architecture/README.md +1 -1
  26. package/docs/branding.md +8 -3
  27. package/docs/configuration.md +18 -18
  28. package/docs/writing-standard.md +3 -3
  29. package/driver/CHANGELOG.md +33 -0
  30. package/driver/driver.config.mjs +19 -3
  31. package/driver/package.json +1 -1
  32. package/driver/portal-service.mjs +34 -4
  33. package/driver/suite-census.json +118 -46
  34. package/mcp-server/CHANGELOG.md +8 -0
  35. package/mcp-server/package.json +1 -1
  36. package/mcp-server/packs/client/CONNECT.md +6 -6
  37. package/package.json +1 -1
  38. package/portal-ui/dist/assets/{index-BsbasHjM.js → index-Bki5jT_N.js} +3376 -2971
  39. package/portal-ui/dist/assets/{index-DNQpLYZF.css → index-De2RFLbT.css} +972 -205
  40. package/portal-ui/dist/index.html +2 -2
  41. package/portal-ui/package.json +1 -1
  42. package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
  43. package/providers/oauth-mcp-bridge/package.json +1 -1
  44. package/providers/signa/src/core.js +29 -1
  45. package/scripts/ask-ai-render-check.mjs +287 -105
  46. package/scripts/release-note-required.mjs +16 -2
  47. package/scripts/release-pre-gate.mjs +111 -0
  48. package/scripts/settings-render-check.mjs +575 -0
  49. package/shared/brand.mjs +29 -0
  50. package/shared/connect-clients.mjs +99 -47
  51. package/shared/names-in-force.mjs +1 -0
  52. package/shared/stdio-connect.mjs +16 -2
  53. package/shared/writing-standard-classes.mjs +34 -3
@@ -0,0 +1,111 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
3
+ //
4
+ // release-pre-gate.mjs — let a stable cut reach the version script when every note has already been
5
+ // consumed by a pre-release.
6
+ //
7
+ // THE PROBLEM THIS EXISTS FOR, MEASURED. `changesets/action` decides whether to run `version-script` by
8
+ // counting the release notes it can see, and it does that ONCE, at the top of the run, before it touches
9
+ // the working tree. Its reader is `readChangesetState`:
10
+ //
11
+ // let preState = await readPreState(cwd);
12
+ // let changesets = await readChangesets(cwd);
13
+ // if (preState !== undefined && preState.mode === "pre") {
14
+ // return { preState, changesets: changesets.filter((c) => !c.id.startsWith("pre/")) };
15
+ // }
16
+ // return { preState: undefined, changesets };
17
+ //
18
+ // `readChangesets` DOES enumerate `.changeset/pre/`, giving each note an id beginning `pre/`. The filter
19
+ // that hides them applies only while the tree says `mode: "pre"`.
20
+ //
21
+ // A pre-release consumes a note by MOVING it into `.changeset/pre/`, where it waits so the eventual
22
+ // stable can list every change since the last stable. So the ordinary state after cutting a beta is: no
23
+ // notes at the top level, every note under `pre/`, and the flag still saying `pre`. In that state the
24
+ // action counts zero, skips the version script, emits no pull request number, and the stable cut refuses
25
+ // with "Nothing to cut" — a refusal about the wrong thing, because the tree holds a whole release.
26
+ //
27
+ // WHAT THIS DOES, AND WHY IT IS NOT A TRICK. It writes the channel the dispatch asked for into the file
28
+ // the action reads, before the action reads it. A stable cut IS a tree leaving pre-release mode, and the
29
+ // notes under `pre/` ARE the notes that stable will carry; saying so is a true statement about this cut,
30
+ // not a device to get past a check. `release-version.mjs` already states the same principle for the
31
+ // other reader: "the mode a cut needs is a fact about the channel asked for, not about what the last cut
32
+ // happened to do." There are two readers of that fact and they are reached separately.
33
+ //
34
+ // THE WRITE IS DISCARDED, ON PURPOSE, AND THAT IS THE POINT. The action's own first act inside
35
+ // `runVersion` is `git checkout changeset-release/main` followed by `git reset --hard <the run's
36
+ // commit>`, which puts `pre.json` back to `pre`. The version script then runs in that rebuilt tree, finds
37
+ // the tree still in pre mode, and performs the real transition itself with `changeset pre exit` — which
38
+ // is where the transition has to happen, and the reason it was moved there. So this changes what the
39
+ // action COUNTS and nothing about what the cut DOES. Nothing here versions anything, and a tree this
40
+ // touched still produces the same version it would have produced had the gate never been in the way.
41
+ //
42
+ // WHAT IT REFUSES TO DO. It is silent on a beta, on a tree that is not in pre mode, on a tree whose notes
43
+ // are at the top level where the action can already see them, and on a tree with no notes anywhere. That
44
+ // last one matters most: a cut with genuinely nothing to cut must still refuse, and it still does,
45
+ // because this writes nothing when `.changeset/pre/` is empty.
46
+ import { readFileSync, writeFileSync, existsSync, readdirSync } from "node:fs";
47
+ import { join, dirname } from "node:path";
48
+ import { fileURLToPath } from "node:url";
49
+ import { isEntrypoint } from "../shared/is-entrypoint.mjs";
50
+
51
+ const ROOT = join(dirname(dirname(fileURLToPath(import.meta.url))));
52
+
53
+ /** A note is a `.md` file that is not the directory's own README. */
54
+ export function countNotes(dir) {
55
+ if (!existsSync(dir)) return 0;
56
+ return readdirSync(dir).filter((f) => f.endsWith(".md") && f.toLowerCase() !== "readme.md").length;
57
+ }
58
+
59
+ /**
60
+ * PURE. Whether this cut has to restate its channel for the action's counter, and nothing else.
61
+ *
62
+ * `null` means leave the tree alone, and every `null` here is a case the action already reads correctly
63
+ * — including the two that must keep refusing. Only the one state the action cannot see returns a write.
64
+ *
65
+ * Kept separate from the file it writes so the decision can be driven against every member of the class
66
+ * rather than only against the tree that happens to be checked out.
67
+ */
68
+ export function preGateDecision({ cut, mode, topLevelNotes, preNotes }) {
69
+ // A beta never restates anything. A beta with no pending notes MUST still refuse, and that refusal is
70
+ // this function returning null.
71
+ if (cut !== "stable") return null;
72
+ // Not in pre mode: the action's filter is not engaged, so whatever is there is already counted.
73
+ if (mode !== "pre") return null;
74
+ // The action can see these. Touching the flag here would change the pull request's title for a cut
75
+ // that never needed help.
76
+ if (topLevelNotes > 0) return null;
77
+ // Nothing anywhere. "Nothing to cut" is then the truth, and the refusal downstream is correct.
78
+ if (preNotes === 0) return null;
79
+ return { mode: "exit" };
80
+ }
81
+
82
+ export function main(root = ROOT, argv = process.argv.slice(2)) {
83
+ const cut = (argv.find((a) => a.startsWith("--cut=")) ?? "").slice("--cut=".length);
84
+ // `--root` exists so this can be driven against a built tree rather than only against the checkout it
85
+ // happens to live in. The workflow never passes it: there the script sits in the repository being cut,
86
+ // which is what ROOT already means.
87
+ const rootFlag = argv.find((a) => a.startsWith("--root="));
88
+ if (rootFlag) root = rootFlag.slice("--root=".length);
89
+ const preJson = join(root, ".changeset", "pre.json");
90
+ const flag = existsSync(preJson) ? JSON.parse(readFileSync(preJson, "utf8")) : null;
91
+ const mode = flag?.mode ?? "none";
92
+ const topLevelNotes = countNotes(join(root, ".changeset"));
93
+ const preNotes = countNotes(join(root, ".changeset", "pre"));
94
+
95
+ const decision = preGateDecision({ cut, mode, topLevelNotes, preNotes });
96
+ const state = `cut=${cut || "none"}, mode=${mode}, ${topLevelNotes} note(s) pending, ${preNotes} already consumed by the pre-release`;
97
+ if (!decision) {
98
+ // SAYS SO EITHER WAY. A step that is silent when it does nothing cannot be told from a step that did
99
+ // not run, and this one is skipped far more often than it acts.
100
+ console.log(`release-pre-gate: ${state} — leaving the flag alone.`);
101
+ return 0;
102
+ }
103
+ writeFileSync(preJson, `${JSON.stringify({ ...flag, ...decision }, null, 2)}\n`);
104
+ console.log(`release-pre-gate: ${state} — the notes this stable carries are all under .changeset/pre/,`
105
+ + " where the action does not count them while the flag says pre. Writing mode=exit so it counts them."
106
+ + " The version script performs the real transition after the action rebuilds the tree; this write is"
107
+ + " discarded by that rebuild and changes no version.");
108
+ return 0;
109
+ }
110
+
111
+ if (isEntrypoint(import.meta.url)) process.exit(main());