ci(integrations): parity gate — every integration must declare a tab AND a widget#211
ci(integrations): parity gate — every integration must declare a tab AND a widget#211rubenvdlinde wants to merge 3 commits into
Conversation
…AND a widget (umbrella PR 10a)
Adds the AD-11/AD-13 parity gate as a CI + pre-commit check. The
registry already throws at `register()` time when `tab` or `widget`
is missing; this gate catches the same condition statically before
merge for the descriptors shipped in this repo's `src/integrations/`.
- scripts/check-integration-parity.js (new) — imports
`builtinIntegrations` and asserts each descriptor has a string
`id` + `label` and truthy `tab` + `widget`; exits non-zero with a
clear message listing every offender. Falls back to a source scan
of `src/integrations/builtin/*.js` if the module can't be required.
- package.json — `npm run check:integration-parity`
- .github/workflows/code-quality.yml — new "Integration parity gate"
step (after the JSDoc ratchet)
- scripts/precommit-regenerate-partials.sh — runs the gate on
commit when any `src/integrations/` file is staged (placed before
the Cn* SFC early-exit so integrations-only commits aren't skipped)
Implements tasks 12.1, 12.2, 12.4 of the umbrella (12.3 — the hydra
quality-gate hook — and 13.x — the leaf scaffold script — and the
ADR-019 authoring are tracked as separate PRs per the task list).
Verification: `npm run check:integration-parity` → exit 0,
"every registered integration has both a tab and a widget".
Pre-commit smoke-tested. Lint clean (7 pre-existing errors in
useObjectLock.js unchanged). 25 integration tests green.
| * Print the result to stdout/stderr. | ||
| * | ||
| * @param {string[]} list Failure messages (empty when all good). | ||
| * |
There was a problem hiding this comment.
[CONCERN] Regex fallback matches tab: in comments or string literals
The regex /\btab\s*:/ in the source-scan fallback will match the key anywhere in the file — including inside comments (// tab: unused), string literals, or nested objects that are not the top-level descriptor key. A descriptor file that exports { tab: null } would still pass even though null is explicitly rejected by the primary path. The fallback should be treated as a best-effort heuristic and its comment should clearly say so, or the regex should be tightened (e.g. require tab: to be a top-level property by checking for a leading newline + optional whitespace: /^\s+tab\s*:/m). The same applies to the widget: regex.
There was a problem hiding this comment.
[CONCERN] Falsy-but-defined values (false, 0, '') pass the tab/widget check
The check d.tab === undefined || d.tab === null only rejects undefined and null. A descriptor that accidentally sets tab: false, tab: 0, or tab: '' will pass the gate even though none of those are valid Vue component references. The runtime registry check may catch this later, but the static gate should be equally strict. Prefer: if (!d.tab) { failures.push(...) } — or better, typeof d.tab !== 'object' && typeof d.tab !== 'function' if components are always objects/constructors.
|
|
||
| report(failures) | ||
| process.exit(failures.length === 0 ? 0 : 1) | ||
|
|
There was a problem hiding this comment.
[CONCERN] Fallback source scan silently skips subdirectories inside builtin/
fs.readdirSync(dir) returns only direct children. If a future integration is organised as builtin/github/index.js (a subdirectory), the fallback scan will skip it entirely — producing a false negative (gate passes when it should fail). The primary require() path is unaffected, but the fallback should use a recursive walk or at least warn when it encounters a directory entry.
WilcoLouwerse
left a comment
There was a problem hiding this comment.
Review
🟡 Concerns (3)
- Regex fallback matches
tab:in comments or string literals —scripts/check-integration-parity.js:84 - Falsy-but-defined values (false, 0, '') pass the tab/widget check —
scripts/check-integration-parity.js:104 - Fallback source scan silently skips subdirectories inside builtin/ —
scripts/check-integration-parity.js:79
🟢 Minor (3)
- builtinIntegrations may be undefined if export name changes — silent false negative (
scripts/check-integration-parity.js:67)
The destructuringrequire(...).builtinIntegrationssilently yieldsundefinedif the export is later renamed. The guardbuiltinIntegrations || []on line 96 means the gate would then exit 0 with no failures — a silent false negative. Add an explicit check after thetryblock:if (!builtinIntegrations) { console.error('Could not read builtinIntegrations export'); process.exit(1) }. - Pre-commit gate runs on rename — old path disappears, cached require may pass incorrectly (
scripts/precommit-regenerate-partials.sh:156)
--diff-filter=ACMRincludes R (renamed). If someone renames only the descriptor file without updating index.js, the gate could pass because the module is loaded from the old cached require. This is a pre-commit concern only (CI always resolves fresh), but worth a comment noting the rename edge case. - Hard-coded
../src/relative path — implicit depth assumption (scripts/check-integration-parity.js:70)
The script lives inscripts/so../src/is correct today, but the path is implicit. A one-line comment or a repo-root constant would make this easier to maintain if the script is ever moved.
Reviewed by WilcoLouwerse via automated batch review.
Part of the openregister pluggable-integration-registry umbrella. Cross-repo PR 10a/N. Stacked on #210 → #209 → #204 → #202 →
beta; auto-retargets as upstream merges.Summary
The AD-11/AD-13 parity gate, as a CI + pre-commit check. The registry already throws at
register()time whentaborwidgetis missing — this gate catches the same condition statically before merge for the descriptors shipped in this repo'ssrc/integrations/.scripts/check-integration-parity.js(new) — importsbuiltinIntegrations, asserts each descriptor has a stringid+labeland truthytab+widget; exits non-zero listing every offender. Falls back to a source scan ofsrc/integrations/builtin/*.jsif the module can't berequired in the runner.package.json—npm run check:integration-parity.github/workflows/code-quality.yml— new "Integration parity gate" step (after the JSDoc ratchet)scripts/precommit-regenerate-partials.sh— runs the gate on commit when anysrc/integrations/file is staged (placed before the Cn* SFC early-exit so integrations-only commits aren't skipped)Implements tasks 12.1, 12.2, 12.4 of the umbrella. (12.3 — the hydra quality-gate hook — and 13.x — the leaf scaffold script — and ADR-019 authoring are separate PRs per the task list, two of which land in the hydra/openregister repos.)
Test plan
npm run check:integration-parity→ exit 0, "every registered integration has both a tab and a widget"bash scripts/precommit-regenerate-partials.sh→ exit 0)npm run lint— clean (7 pre-existing errors inuseObjectLock.jsunchanged)