Skip to content

ci(integrations): parity gate — every integration must declare a tab AND a widget#211

Open
rubenvdlinde wants to merge 3 commits into
feature/integration-builtinsfrom
feature/integration-ci
Open

ci(integrations): parity gate — every integration must declare a tab AND a widget#211
rubenvdlinde wants to merge 3 commits into
feature/integration-builtinsfrom
feature/integration-ci

Conversation

@rubenvdlinde
Copy link
Copy Markdown
Contributor

Part of the openregister pluggable-integration-registry umbrella. Cross-repo PR 10a/N. Stacked on #210#209#204#202beta; 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 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, asserts each descriptor has a string id + label and truthy tab + widget; exits non-zero listing every offender. Falls back to a source scan of src/integrations/builtin/*.js if the module can't be required in the runner.
  • package.jsonnpm 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 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"
  • Pre-commit hook smoke-tested (bash scripts/precommit-regenerate-partials.sh → exit 0)
  • npm run lint — clean (7 pre-existing errors in useObjectLock.js unchanged)
  • 25 integration tests green

…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).
*
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copy link
Copy Markdown

@WilcoLouwerse WilcoLouwerse left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

🟡 Concerns (3)

🟢 Minor (3)

  • builtinIntegrations may be undefined if export name changes — silent false negative (scripts/check-integration-parity.js:67)
    The destructuring require(...).builtinIntegrations silently yields undefined if the export is later renamed. The guard builtinIntegrations || [] on line 96 means the gate would then exit 0 with no failures — a silent false negative. Add an explicit check after the try block: 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=ACMR includes 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 in scripts/ 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants