@starci/hfs 4.7.27 → 4.7.28

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 (44) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +2 -3
  3. package/package.json +7 -1
  4. package/release/candidate-ci.mjs +3 -21
  5. package/runtime/engine/yaml.mjs +1 -1
  6. package/runtime/knowledge/hfs/canon-pins.yaml +4 -4
  7. package/runtime/knowledge/hfs/rules.yaml +913 -637
  8. package/runtime/knowledge/hfs/slots.yaml +21 -14
  9. package/runtime/modules/kernel/failure-codes.yaml +10 -0
  10. package/runtime/packages/cli/src/catalog.generated.mjs +130 -54
  11. package/runtime/scripts/api/git/check-ignore.mjs +5 -0
  12. package/runtime/scripts/gates/runtime-host.mjs +83 -0
  13. package/runtime/scripts/gates/sonar-gate.mjs +251 -0
  14. package/runtime/scripts/gates/starcistacks-ci.mjs +55 -0
  15. package/runtime/scripts/gates/starcistacks-form.mjs +26 -0
  16. package/runtime/scripts/gates/starcistacks-runtime.mjs +2 -0
  17. package/runtime/scripts/gates/starcistacks-value-file.mjs +17 -0
  18. package/runtime/scripts/gates/starcistacks.mjs +499 -0
  19. package/runtime/scripts/hfs/app-wiring.mjs +2 -2
  20. package/runtime/scripts/hfs/architecture/supabase.mjs +1 -1
  21. package/runtime/scripts/hfs/check.mjs +4 -1
  22. package/runtime/scripts/hfs/coverage-from.mjs +1 -1
  23. package/runtime/scripts/hfs/manifest-shape.mjs +1 -3
  24. package/runtime/scripts/hfs/rule-catalog.mjs +7 -10
  25. package/runtime/scripts/hfs/rules/a15-findings.mjs +1 -1
  26. package/runtime/scripts/hfs/rules/assets.mjs +1 -1
  27. package/runtime/scripts/hfs/rules/bypass-markers.mjs +24 -0
  28. package/runtime/scripts/hfs/rules/frontend-tree.mjs +2 -2
  29. package/runtime/scripts/hfs/rules/supabase-secrets.mjs +1 -1
  30. package/runtime/scripts/lib/bypass-markers.mjs +116 -0
  31. package/runtime/scripts/lib/check-code-loosening.mjs +128 -0
  32. package/runtime/scripts/lib/example-refs.mjs +205 -0
  33. package/runtime/scripts/lib/failure-code-reads.mjs +100 -0
  34. package/runtime/scripts/lib/gate-loosening.mjs +52 -8
  35. package/runtime/scripts/lib/json-formats.mjs +34 -0
  36. package/runtime/scripts/lib/package-at.mjs +1 -1
  37. package/runtime/scripts/lib/properties.mjs +16 -0
  38. package/runtime/scripts/lib/provision-control-failure.mjs +32 -0
  39. package/runtime/scripts/lib/sonar-new-code.mjs +58 -0
  40. package/runtime/scripts/lib/starciwork-boundary.mjs +164 -0
  41. package/runtime/scripts/lib/work-edition.mjs +40 -0
  42. package/sync/format.mjs +1 -1
  43. package/sync/hygiene.mjs +16 -4
  44. package/templates/app/hooks/husky/pre-commit +8 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.7.28 - 2026-10-23
4
+ - Added: `scripts/lib/sonar-new-code.mjs` joins the bundled runtime copy (the New Code definition `starci sonar project ensure` sets), proven by `src/sonar-new-code.spec.mjs`.
5
+ - Fixed: the bundled gate-loosening detector no longer reads a quoted operand of a printed message as a ban-list row, nor the upkeep of an exception list the check stops reading as enforcement lost (`src/bypass-detector.spec.mjs`).
6
+ - Added: `scripts/lib/provision-control-failure.mjs` joins the bundled runtime copy, proven by `src/provision-control-failure.spec.mjs`.
7
+ - Changed: the bundled canon pins select @starci/cli 1.7.28, @starci/eslint-canon-be 3.4.36 and @starci/eslint-canon-fe 8.4.35.
8
+ - Changed: no check keeps a list of exceptions. The JSON a tool requires is declared once by format (`scripts/lib/json-formats.mjs`, bundled), the failure-code scanner no longer counts a literal the code only reads (`scripts/lib/failure-code-reads.mjs`, bundled), and a rule is proven by a package spec unless every enforcer it has runs only inside the runtime (`RUNTIME_ONLY_ENFORCERS`); the catalog keys `specNa`, `exampleNa` and the unreachable witness are gone.
9
+ - Added: the specs of the deleted runtime tests that prove bundled checkers, restored as package specs (`src/*.spec.mjs`, helpers in `src/helpers`, fixtures in `src/fixtures`), and fresh specs for R296-R301, the tree codes of R01/R03, R112 and R216.
10
+ - Changed: `scripts/gates/sonar-gate.mjs` and `scripts/gates/starcistacks.mjs` joined the bundled runtime copy, and a few exports the specs read are exported again.
11
+ - Changed: a red candidate CI never passes: `packages/hfs/release/candidate-ci.mjs` has no ci-override record any more (owner ruling 2026-10-22, a loosening is always red).
12
+ - Added: R303 `HFS_BYPASS_MARKER` in `starci app check` and the bypass guard of `starci app hygiene` (staged files read from the index): an inline disable of any tool, a type-check escape, a Sonar or coverage ignore, a skipped or focused test and a failure swallowed in a hook or workflow are refused in a product repository, the front end included.
13
+ - Changed: the managed `.husky/pre-commit` runs `starci app sync --check` and ends the hook when any step fails; the stylelint step can no longer be skipped by a failing run.
14
+
3
15
  ## 4.7.27 - 2026-10-23
4
16
  - Fixed: the bundled `scripts/lib/managed-json.mjs` passes each slot path to `braceVariants` through an arrow, so `flatMap` never hands it an index (Sonar S7727).
5
17
  - Changed: the bundled canon pins select @starci/cli 1.7.27, @starci/eslint-canon-be 3.4.34 and @starci/eslint-canon-fe 8.4.33.
package/README.md CHANGED
@@ -187,10 +187,9 @@ derived from the machine's rule id lists). After changing any of those files, `k
187
187
  `knowledge/hfs/canon-pins.yaml`, `knowledge/patterns/fe/folder.yaml` or the catalog entries of those codes, run `starci release sync-runtime`;
188
188
  `starci release sync-runtime --check` fails on a stale copy. Bump `version` here and in the pin when the behaviour changes.
189
189
 
190
- The examples gate `starci runtime check --only example-architecture` runs `starci app lint` of this CLI at the root of every `examples/*`
190
+ The examples workflow (`.github/workflows/examples.yml`) runs `starci app lint` of this CLI at the root of every `examples/*`
191
191
  app with an `hfs.json` (each `examples/<app>` root) and fails on any finding or any tool that could not run;
192
- `starci app check` alone would miss the machine's source rules, which the canons judge. It is heavy: run it once, by hand, after `npm ci`
193
- in each app.
192
+ `starci app check` alone would miss the machine's source rules, which the canons judge.
194
193
 
195
194
  ## Serving knowledge to other packages
196
195
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starci/hfs",
3
- "version": "4.7.27",
3
+ "version": "4.7.28",
4
4
  "description": "The StarCi app command implementation for scaffolding, linting, checking and synchronizing one product repository.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -15,6 +15,12 @@
15
15
  "smol-toml": "1.9.0"
16
16
  },
17
17
  "devDependencies": {
18
+ "@nestjs/common": "11.2.5",
19
+ "@nestjs/core": "11.2.5",
20
+ "@nestjs/testing": "11.2.5",
21
+ "prettier": "3.9.6",
22
+ "reflect-metadata": "0.2.2",
23
+ "rxjs": "7.8.2",
18
24
  "typescript": "5.9.3"
19
25
  },
20
26
  "files": [
@@ -5,11 +5,8 @@
5
5
  // red RELEASE_CANDIDATE_CI_RED (a run or a job that concluded skipped is red, never a pass: owner ruling 2026-10-22, a skip is a loosening) with the run URL and the failing jobs and steps; nothing reaches the default branch. The candidate ref stays for diagnosis
6
6
  // timeout RELEASE_CANDIDATE_CI_TIMEOUT: no run listed within the grace (a queued run is listed), or it did not end within the wait
7
7
  // unknown RELEASE_CI_UNKNOWN: GitHub could not be read to the end; a release never proceeds blind
8
- // Emergency path (documented, owner-only): the owner's `starci release ci-override --sha <sha>` record for this exact sha
9
- // (<git common dir>/starci-release/<sha>.ci-override.json, schema starci/ci-override@1) lets a red candidate through, and the push says so.
8
+ // There is no override: a red candidate never passes (owner ruling 2026-10-22: a loosening is always red).
10
9
  // Every process goes through ctx.spawn (git, gh); the wait through ctx.sleep and ctx.now.
11
- import fs from 'node:fs';
12
- import path from 'node:path';
13
10
 
14
11
  const CANDIDATE_PREFIX = 'refs/heads/release/candidate-';
15
12
  /** The wait of the candidate run: poll interval, the grace for a run to appear, the wait for it to end (an app's ci.yml jobs time out within 45 minutes). */
@@ -67,19 +64,6 @@ export function failingJobs(ctx, runRow) {
67
64
  });
68
65
  }
69
66
 
70
- /** Only a complete starci/ci-override@1 record for this exact commit can excuse a red candidate. */
71
- export function overrideOf(ctx, sha) {
72
- if (!SHA.test(String(sha))) return null;
73
- const common = run(ctx, 'git', ['rev-parse', '--git-common-dir']);
74
- if (common.error || common.status !== 0 || !String(common.stdout ?? '').trim()) return null;
75
- const file = path.join(path.resolve(ctx.cwd, String(common.stdout).trim()), 'starci-release', `${sha}.ci-override.json`);
76
- try {
77
- const record = JSON.parse(fs.readFileSync(file, 'utf8'));
78
- if (record.schema !== 'starci/ci-override@1' || record.sha !== sha || typeof record.reason !== 'string' || !record.reason.trim() || typeof record.by !== 'string' || !record.by.trim() || typeof record.at !== 'string' || !Number.isFinite(Date.parse(record.at))) return null;
79
- return record;
80
- } catch { return null; }
81
- }
82
-
83
67
  /** Poll the candidate run of `sha` until green or red, or the grace (no run) or the wait passes: {state: green|red|timeout|unstarted|unknown, run}. */
84
68
  export async function waitForCandidate(ctx, sha, wait = CANDIDATE_WAIT) {
85
69
  const started = ctx.now();
@@ -96,8 +80,8 @@ export async function waitForCandidate(ctx, sha, wait = CANDIDATE_WAIT) {
96
80
  }
97
81
 
98
82
  /**
99
- * The candidate step of the push: push HEAD to the candidate ref (through the pre-push hook), wait for its CI. {ok, lines, data: {ref, state, run, failing, override}}.
100
- * ok only on green, or on red under the owner's override record for this exact sha.
83
+ * The candidate step of the push: push HEAD to the candidate ref (through the pre-push hook), wait for its CI. {ok, lines, data: {ref, state, run, failing}}.
84
+ * ok only on green.
101
85
  */
102
86
  export async function candidateStep(ctx, { head, tag, prefix, remote, wait = CANDIDATE_WAIT }) {
103
87
  const ref = candidateRef(tag, prefix);
@@ -110,9 +94,7 @@ export async function candidateStep(ctx, { head, tag, prefix, remote, wait = CAN
110
94
  if (seen.state === 'green') return { ok: true, lines: [...lines, `candidate CI green${url}`], data: { ref, state: 'green', run: seen.run } };
111
95
  if (seen.state === 'red') {
112
96
  const failing = failingJobs(ctx, seen.run);
113
- const record = overrideOf(ctx, head);
114
97
  const detail = failing.length ? failing.join('; ') : 'the failing jobs could not be read';
115
- if (record) return { ok: true, lines: [...lines, `candidate CI RED${url}; failing: ${detail}`, `RELEASE_CANDIDATE_CI_RED overridden by the owner (${record.by}): ${record.reason}`], data: { ref, state: 'red', run: seen.run, failing, override: { by: record.by, reason: record.reason, at: record.at } } };
116
98
  return { ok: false, lines: [...lines, `RELEASE_CANDIDATE_CI_RED: the ci.yml run of ${head.slice(0, 10)} on ${ref} is red${url}; failing: ${detail}. Nothing reached the default branch; the candidate ref stays for diagnosis`], data: { ref, state: 'red', run: seen.run, failing } };
117
99
  }
118
100
  if (seen.state === 'unstarted') return { ok: false, lines: [...lines, `RELEASE_CANDIDATE_CI_TIMEOUT: no ci.yml run started for ${head.slice(0, 10)} on ${ref} within ${wait.graceMinutes} minutes (does .github/workflows/ci.yml trigger on release/candidate-*? run starci app sync)`], data: { ref, state: 'unstarted' } };
@@ -4,7 +4,7 @@
4
4
  // rather than importing node_modules; yaml is a devDependency and a direct import breaks that
5
5
  // install. tests/engine/yaml-vendored-parity.spec.mjs proves the vendored parseYaml/stringifyYaml behave
6
6
  // exactly like yaml@2.9.0 with these options for every tracked *.yaml/*.yml file (parsed values
7
- // deep-equal, stringify output byte-identical). License: engine/yaml-license.json.
7
+ // deep-equal, stringify output byte-identical). License: engine/yaml-license.yaml.
8
8
  // Rebuild only when deliberately upgrading yaml. The entry source is:
9
9
  // import * as YAML from 'yaml';
10
10
  // export function parseYaml(source) {
@@ -29,14 +29,14 @@ pins:
29
29
  source: packages/grammar/package.json
30
30
  why: 'the grammar front ends read: the brand layer sets `--font-sans` and `--font-mono` and the grammar reads them; it carries the Input tel kind, the copyable and read-only Input, the IconButton disclosure props and count badge, ChatBubble, FillViewport, Textarea mentions and Enter-to-send, the leading PrimaryRailLayout rail, the plate-less Image, ListRow, the soft Alert, Progress steps, the floating primary IconButton, FlowGraph, the STATE-8 loading views and the bordered selectable TagGroup chips. The runtime source version wins for a @starci package.'
31
31
  '@starci/eslint-canon-be':
32
- version: 3.4.34
32
+ version: 3.4.36
33
33
  group: starci
34
34
  install: registry
35
35
  side: be
36
36
  source: packages/eslint/be/package.json
37
37
  why: 'the back-end ESLint canon: `loadHfs(import.meta.url)` of be/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the be side, and the project graph is built per side; its bundled runtime copies (slots, canon-pins, failure codes, the architecture machine) put .starcistacks and .sops.yaml at the app root; `starciBeConfig({ hfs: loadHfs(import.meta.url) })` is the typed factory and the BE-CONVENTION laws are its rules.'
38
38
  '@starci/eslint-canon-fe':
39
- version: 8.4.33
39
+ version: 8.4.35
40
40
  group: starci
41
41
  install: registry
42
42
  side: fe
@@ -76,14 +76,14 @@ pins:
76
76
  source: packages/test-world/package.json
77
77
  why: 'the shared e2e library of every back end (R47, R48), a devDependency of every back end: the warm stack behind toxiproxy, the network-edge fakes, the Nest boot, the typed useTestWorld handle, useSandbox for contract specs and the outage lock. Kafka is real own infra (the one digest-pinned apache/kafka KRaft image, a broker listener and proxy per slot, slot-prefixed topics, consumer groups and client ids) and Postgres is schema-per-context (a schema and a login role per context in a shared database). Each worker has its own data slot (<ns>_w<k>) with its own databases, Keycloak realm, leased Redis DB, bucket/collection/topic prefixes, fakes host, toxiproxy proxies and outage lock; a run provisions min(jest workers, workers cap, default 2) slots, and state file protocol 2 pairs exactly with @starci/jest-preset (TEST_WORLD_PAIR_MISMATCH otherwise). A modules world boots real peer apps beside its modules, world.apps.<name>.during(fn) is the outage of a peer app, w.keycloak.clientSecret(client) answers the run-generated secret of a confidential realm client, world.keycloak.events/sessions read the realm user events and live sessions, world.infra.postgresql.connection(name) takes one database connection down while the others serve, and world.resolve and scope.resolve take any Nest token. Every path a declaration names (`stack`, seeds, the realm, Dockerfiles) resolves from the app root (the directory of hfs.json); the globalSetup registers the path aliases as TypeScript resolves them. Its CLI main is exported at ./cli for starci app stack.'
78
78
  '@starci/cli':
79
- version: 1.7.27
79
+ version: 1.7.28
80
80
  group: starci
81
81
  install: registry
82
82
  side: both
83
83
  source: packages/cli/package.json
84
84
  why: 'the only StarCi command package; product repositories install it for starci app, while @starci/hfs remains its pinned transitive implementation.'
85
85
  '@starci/hfs':
86
- version: 4.7.27
86
+ version: 4.7.28
87
87
  group: starci
88
88
  install: registry
89
89
  side: both