-
Notifications
You must be signed in to change notification settings - Fork 0
368 lines (318 loc) · 19.5 KB
/
Copy pathdocs.yml
File metadata and controls
368 lines (318 loc) · 19.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
name: Documentation Validation
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
pull_request:
push:
branches: [main, develop]
permissions:
contents: read
pages: write
id-token: write
jobs:
# GT-476: the semantic tracking guard runs on every PR and on push to
# main/develop so the gap board can't silently drift. Kept as a focused,
# dependency-free job (git history only) so it stays fast and does not drag
# the full doc-publish pipeline below into every pull request.
tracking-guard:
name: Validate semantic tracking (guard)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: "20"
- name: Validate semantic tracking
run: node .harness/scripts/ci/08-validate-tracking.mjs
validate:
name: Validate documentation
# GT-563: this job used to carry `if: github.event_name == 'workflow_dispatch'`,
# so on every push and pull_request it was SKIPPED -- and a skipped job reports
# neutral, which GitHub rolls up as a green "Documentation Validation" check. The
# workflow therefore reported SUCCESS on commits whose docs did not validate. A
# check that cannot fail is not a check; it is a false signal, and it is worse than
# a red one because nobody goes looking for it.
#
# The gate is removed: validation now runs on push and pull_request, and it BLOCKS.
# It is deliberately not marked continue-on-error -- a non-blocking gate would
# reproduce the exact defect this fixes, just with more convincing logs.
#
# The old comment justified the gate as keeping "the full documentation-publish
# pipeline" out of PRs. That rationale did not hold: this workflow has no publish
# step. The job is npm ci plus a handful of node validators.
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: "20"
cache: 'npm'
cache-dependency-path: package-lock.json
- name: Install Dependencies
run: npm ci
- name: Validate docs
run: node .harness/scripts/ci/01-validate-docs.mjs
# Here rather than beside their subjects, and the reason is the defect itself.
# `17-validate-knowledge-intake.mjs` runs in knowledge-intake.yml, but that
# workflow is `paths:`-filtered to `KI-*`/`SRC-*` files and the intake schema —
# so a pull request that changes the GUARD does not trigger it, and a self-test
# placed next to the guard would be unreachable in exactly the case it is for.
# The three `knowledge-okf-*` guards run in no workflow at all. All four are
# hermetic, so the required job is where they produce a signal.
- name: Self-tests for the knowledge intake and OKF guards
run: |
node --test .harness/scripts/ci/17-validate-knowledge-intake.test.mjs
node --test .harness/scripts/knowledge-okf-project.test.mjs
node --test .harness/scripts/knowledge-okf-standard-watch.test.mjs
node --test .harness/scripts/knowledge-okf-precommit-guard.test.mjs
- name: Check bilingual parity
run: node .harness/scripts/ci/04-check-bilingual-parity.mjs
- name: Check Field Label Coverage
run: node .harness/scripts/ci/71-validate-field-label-coverage.mjs
# GT-620's negative fixtures for the language heuristic the step above depends
# on — including the two cases where it must DECLINE to judge. They ran in no
# workflow, so the heuristic that closed GT-620 was itself unguarded.
- name: Self-test for the bilingual language heuristic
run: node --test .harness/scripts/ci/suites/bilingual-suite.test.mjs
# GT-702. The step above asks whether the ES file EXISTS, matches heading counts
# and is Spanish — all properties of the files as they stand, none of which can
# see that the two DISAGREE. On 2026-08-16 the EN catalog was corrected to 266
# violation literals while its ES twin kept asserting 251, and parity stayed
# green over the contradiction for the whole day.
#
# Runs against this branch's own range, so historical one-sided commits do not
# fail somebody else's PR. Depends on the `fetch-depth: 0` checkout above: with a
# shallow clone there is no range to read, and the guard's `assertScanned` call
# makes that a failure rather than a silent pass.
- name: Check bilingual pairs moved together
run: node .harness/scripts/ci/66-validate-bilingual-sync.mjs --since origin/${{ github.base_ref || 'main' }}
- name: Self-test for the bilingual sync guard
run: node .harness/scripts/ci/66-validate-bilingual-sync.mjs --self-test
- name: Validate semantic tracking
run: node .harness/scripts/ci/08-validate-tracking.mjs
# Two files, one subject: both import `08-validate-tracking.mjs` directly. The
# 17 cases in the first are what GT-629 added when the guard learned to refuse an
# open row with no criteria at all; neither file was referenced anywhere.
- name: Self-tests for the semantic tracking guard
run: |
node --test .harness/scripts/ci/08-validate-tracking.test.mjs
node --test .harness/scripts/validate-tracking.test.mjs
- name: Reconcile maturity evidence
run: node .harness/scripts/ci/09-reconcile-maturity.mjs --check
# The inventory summaries are the source every doc quotes when it says how many
# ADRs, rulesets and schemas the corpus carries -- and until now nothing checked
# them. `07-generate-inventories.mjs` appears in exactly one other place in the
# repository: its own unit test in ci-cd.yml. The generator ran in no workflow,
# and in the local hook it hung off `infraChanged` (compose files, helm, the
# workflow directory) -- never off `src/rulesets/` or the ADR directory, which
# are the only two things that can change the numbers.
#
# The result was a published tally that had been stale since 2026-08-16: 141/181
# against a measured 142/182, quoted onward by the README as "412 rules". This
# step runs the generator in --check mode, so a ruleset or ADR added without
# regenerating the summaries fails here instead of aging into the front page.
- name: Check inventory summaries match the corpus
run: node .harness/scripts/ci/07-generate-inventories.mjs --check
- name: Self-test for the inventory generator
run: node --test .harness/scripts/ci/07-generate-inventories.test.mjs
# 21 cases over `09-reconcile-maturity.mjs`, including the GT-576 rule that a
# capability may not be marked Validated on an ADR citation alone and the GT-596
# ISO/IEC 33020 threshold rule. Both were closed with "ships with a negative
# self-test" as the evidence; the self-test ran nowhere.
- name: Self-test for the maturity reconciler
run: node --test .harness/scripts/reconcile-maturity.test.mjs
# GT-627: the generated ADR-conformance corpus fell SEVEN rulesets behind
# its own generator, and six of the seven were security standards
# (ADR-0119..0124: SSRF prevention, input validation, shell-execution
# safety, timing-safe comparison, credential management, API hardening) —
# accepted decisions with no conformance ruleset at all. Nothing detected
# it for as long as it lasted, because the generator was only ever run by
# hand. It has always supported `--check`; nobody ran it.
#
# This lives in a REQUIRED check on purpose. The same drift in the skill
# registry (GT-424) went unnoticed for weeks behind a guard that existed
# and was not enforced.
- name: ADR ruleset corpus matches its generator
run: node .harness/scripts/generate-adr-rulesets.mjs --check
- name: Self-tests for the ADR ruleset generator
run: node --test .harness/scripts/generate-adr-rulesets.test.mjs
# GT-598: `native-evaluability-snapshot.json` says of itself that it is a
# capture, and until now nothing captured it — it was written by hand, and
# it drifted. It still declared `documentation-only: 129` after Core moved
# to 136 (the same seven ADR rulesets the step above exists for), and its
# guard could not see that, because it compared the snapshot against six
# numbers hardcoded in the test: the same six the snapshot contained.
#
# Drift here is not contained. `build-iso-5055-mapping.mjs` stamps
# nativeEvaluability onto all 388 rows of the mapping FROM this file, so a
# stale class is laundered into the larger artifact and overstates the
# handler backlog. The order of these two steps is the order of the chain.
- name: Native evaluability snapshot is a faithful capture of Core's triage
run: node src/rulesets/standards/capture-native-evaluability-snapshot.mjs --check
- name: ISO/IEC 5055 mapping guard
run: node --test src/rulesets/standards/iso-5055-mapping.test.mjs
# GT-583: the `capability operation schemas` link in the chain below boots
# the mcp-server DI graph through ts-node to ask ToolRegistryService to
# describe itself. mcp-server resolves `@beyondnet/evolith-core-domain`
# through the workspace symlink, whose `exports` map points at `dist/` —
# so with only `npm ci` behind it the guard dies on MODULE_NOT_FOUND for
# `dist/capabilities/capability-operations.js` and reports the link STALE.
# It passed in `test-mcp-server` and on every developer machine because
# both had a built `dist/` (gitignored) sitting there already. `npm run
# build` further down this job is too late — the guard runs here.
- name: Build workspace dependencies for the runtime-derived guards
run: |
npm run build --workspace @beyondnet/evolith-contracts
npm run build --workspace @beyondnet/evolith-core-domain
npm run build --workspace @beyondnet/evolith-agent-runtime
npm run build --workspace @beyondnet/evolith-sdk
npm run build --workspace @beyondnet/evolith-core
npm run build --workspace @beyondnet/evolith-infra-providers
# GT-630: the derived artifacts have a dependency ORDER — the executive
# summary is built FROM the maturity reconciliation, which is built from
# the board. Generate the summary first and it captures a value the
# reconciler then moves; each artifact's own --check still passes at that
# moment, so the suite goes green locally and this job goes red. That cost
# three red required checks on 2026-07-28 and the order was written down
# nowhere. It is now data, and this step walks it.
- name: Derived artifacts are current, in dependency order
run: node .harness/scripts/ci/46-validate-derived-artifact-order.mjs --verbose
- name: Self-test for the derived-artifact order guard
run: node --test .harness/scripts/ci/46-validate-derived-artifact-order.test.mjs
# GT-632: `40-validate-path-literals` scans string LITERALS, so it is blind
# to a path that is BUILT — `path.join(ctx.corePath, 'sdk', 'cli', …)`.
# Twelve of those survived the `src/` move: one resolved the compiled ABAC
# policy at the pre-refactor path, and because the evaluator denies
# fail-closed when it cannot load, EVERY MCP tool was refused in production
# on a clean checkout; three more probed files that had moved and reported
# false NEGATIVES, which nothing notices because a false negative looks
# exactly like a clean run. The specs agreed with the code and neither
# agreed with the repository, so no existing suite could catch it.
- name: Built (not written) paths resolve to something that exists
run: node .harness/scripts/ci/47-validate-joined-paths.mjs --verbose
- name: Self-test for the joined-path guard
run: node --test .harness/scripts/ci/47-validate-joined-paths.test.mjs
# GT-587. The OpenTelemetry GenAI conventions are Development-status, and the
# Core emits their attribute names as plain LITERALS (it must not import an
# observability SDK — rule HXA-05). Literals do not move when upstream moves, so
# a rename there turns Evolith's "standard" telemetry back into a private
# vocabulary with nothing going red. This is the check that goes red.
- name: Validate the pinned OpenTelemetry semconv revision
run: node .harness/scripts/ci/51-validate-semconv-pin.mjs --verbose
- name: Self-test for the semconv pin guard
run: node --test .harness/scripts/ci/51-validate-semconv-pin.test.mjs
# GT-597. The gate itself runs inside `openssf-scorecard.yml`, where the
# Scorecard JSON exists; only its logic is tested here. That logic is the
# difference between a weekly score that is published and a weekly score
# that is CHECKED, so it is tested on its failure surface: a dropped check,
# a vanished check, an inconclusive -1, an unseeded baseline, and a SARIF
# file handed to it by mistake must each be red.
- name: Self-test for the Scorecard regression gate
run: node --test .harness/scripts/ci/52-validate-scorecard-regression.test.mjs
- name: Validate machine contracts
run: node .harness/scripts/ci/10-validate-contract-conformance.mjs
- name: Validate product documentation synchronization
run: node .harness/scripts/ci/11-validate-product-docs.mjs
- name: Check surface parity matrix
run: node .harness/scripts/ci/24-check-surface-parity.mjs
- name: Validate topology rule coverage
run: node .harness/scripts/ci/26-validate-topology-rule-coverage.mjs
# The guard above has run in this required check since it was written; its own
# self-test ran in no job at all, and had been red on both `develop` and `main`
# since `98a20dca` without anyone seeing it. That commit moved `rulesets/` under
# `src/` and updated the path this test's fixture WRITES TO, but not the relative
# path written INSIDE it — so the fixture's satellite reference stopped resolving
# and the coverage assertion it exists to make was never reached.
#
# A validator whose own tests nobody runs is the defect this board keeps
# recording, so it goes next to its guard, in the same required check, exactly
# like every other self-test in this job.
- name: Self-test for the topology rule coverage guard
run: node --test .harness/scripts/generate-rule-coverage.test.mjs
- name: Validate harness path literals
run: node .harness/scripts/ci/39-validate-harness-paths.mjs
# Re-armed: disabled in 42cf18a8's mass "disable until fixed" sweep and never
# restored. Its matrix path was dead (ENOENT); both it and the matrix's own
# stale module paths are fixed, so it now validates 3 surfaces for real.
- name: Validate surface compatibility matrix
run: node .harness/scripts/ci/20-validate-surface-compatibility.mjs
# All six cases were red — and had never once run. `setup()` wrote the fixture
# matrix to `reference/core/control-center/`, one directory above the `audits/`
# path the guard reads, into a directory it also never created: every case died
# in setup with ENOENT. Four of the six are negative cases asserting the guard's
# specific failure text, so re-arming the step above (see the note on it) was
# done with its self-test in this state.
- name: Self-test for the surface compatibility guard
run: node --test .harness/scripts/ci/20-validate-surface-compatibility.test.mjs
# The governance suite (16 validators) previously existed ONLY behind
# `ci-runner.mjs`, whose sole caller was `.husky/pre-commit` — and that hook
# exits before reaching it whenever there is no TTY (every agent, script and
# CI invocation). So 16 validators were inert: they could fail for weeks and
# tell nobody. Running them here makes them independent of anyone's local
# git config, TTY or shell.
- name: Run governance validator suite
run: node .harness/scripts/ci-runner.mjs governance
# Suite exploratoria cross-superficie: ejerce CLI, MCP y REST de VERDAD
# (48 operaciones, 66 invocaciones) y compara sus respuestas.
#
# Es hermetica: su cwd es el satelite temporal que ella construye, asi que
# no scaffoldea en la raiz del repo ni depende de lo que dejaran corridas
# anteriores. Y los how-to derivados ya no embeben el VEREDICTO de la
# evaluacion, que variaba entre maquinas y hacia infalseable el chequeo
# anti-drift: primero se resto campo a campo (`rulesChecked`, `issues`, las
# listas de violaciones, `confidence`) -- una denylist que solo podia crecer,
# porque nombraba la volatilidad por la que ya nos habian mordido -- y ahora
# el generador proyecta el sobre a su FORMA e imprime literal solo lo que
# esta en su allowlist. Un campo nuevo ya no puede romper este paso.
- name: Build workspace packages
run: npm run build
- name: Populate CLI bundled ruleset corpus
run: npm run copy-rulesets --workspace @beyondnet/evolith-cli
- name: Cross-surface exploration suite
run: npm run test:exploration
# GT-563: the steps below REPORT, they do not validate. This job runs no tests and
# produces no coverage data, and COVERAGE_REPORT.md is untracked, so on a PR the
# dashboard --check can only ever report "COVERAGE_REPORT.md is missing" and the
# upload can only ever attach nothing. They stay on workflow_dispatch, where a
# coverage run can precede them and they measure something real.
#
# This is a deliberately narrow gate: every VALIDATOR above runs on push and
# pull_request and blocks. Only report generation is manual.
- name: Check coverage dashboard is up to date
if: github.event_name == 'workflow_dispatch'
run: node .harness/scripts/coverage-dashboard.mjs --check
# Deliberately NOT carrying the `workflow_dispatch` condition above it. That gate
# exists because generating the report needs a coverage run; these two need
# nothing but a tmp directory, and inheriting the condition would wire them into
# a job they almost never run in — which is the defect being closed, not a fix
# for it.
#
# Five of the seven coverage-dashboard cases were red. `coverage-dashboard.mjs`
# wrote its report with no `mkdirSync` for the parent, so off the real tree it
# printed a correct 100% report and then died on an unhandled ENOENT. Only the
# positive cases failed; the negative ones exit before the write and passed,
# which is what made it look like a scoring bug rather than a crash.
- name: Self-tests for the coverage dashboard
run: |
node --test .harness/scripts/coverage-dashboard.test.mjs
node --test .harness/scripts/lib/generated-doc-exclusions.test.mjs
- name: Upload coverage report
uses: actions/upload-artifact@v7
if: github.event_name == 'workflow_dispatch'
with:
name: coverage-report
path: COVERAGE_REPORT.md
retention-days: 30
- name: Report bilingual coverage
if: github.event_name == 'workflow_dispatch'
run: node .harness/scripts/bilingual-coverage.mjs