Skip to content

refactor: remove duplicated catalogs, tests and guardrails, drop operate from VS Code - #492

Merged
emaarco merged 17 commits into
mainfrom
emaarco/refactor-simplify-the-repo
Oct 28, 2026
Merged

emaarco merged 17 commits into
mainfrom
emaarco/refactor-simplify-the-repo

Conversation

@emaarco

@emaarco emaarco commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Why

A dependency analysis found code, tests and guardrails that exist two or three times, or guard things that do not exist. This PR removes them, one commit per step, so each can be kept or dropped on its own. Net −102,600 lines, about −6,400 without the two duplicate generated catalogs.

What

CLI

  • One command catalog instead of three byte-identical ones. The bundle shrinks from 5.6 to 2.3 MB.
  • pulse-cli guide <topic> prints the bundled docs/cli page instead of a copy kept in code.
  • Each bearer rule is tested at one level. The Testcontainers suite drops from 27 to 10 cases.

MCP

  • camunda7: feeds, show tools and pipeline steps register through one helper each (registerDataFeed, registerShowTool, adapterStep).
  • Settings sections: one registerSettingsSection for shell, camunda7 and analytics.
  • Goldens: both MCP changes leave them byte-identical.

VS Code

Guardrails (each commit carries a Ratchet-Exception: trailer with its reason)

  • dependency-cruiser: drops rules that other rules already cover, and the -domain role no package has.
  • check-ratchets: drops the AST half that eslint-effective-config covers.
  • check-github-config: trimmed to the publish security core.
  • Other checks: drops the @types/vscode floor check (vsce refuses the same thing) and the pre-Pulse name rules. The --filter checks move to check-package-scripts.
  • Fixed lists and plain commands: APP_SCAN is now a fixed list, and mutation-diff is replaced by a documented stryker run.

Docs

  • Root CLAUDE.md: goes from 1,180 to 589 lines. A gated rule is one sentence plus its gate; ungated rules keep their reason.

Verification

  • Full bar: pnpm build && pnpm typecheck && pnpm test && pnpm lint && pnpm format:check passed per commit and on the combined branch.
  • Ratchets: lint:ratchets reports no loosened floor, debt list, mutate list or knip ignore.
  • VS Code test:engines: 61/61 on Operaton 2.1.5, CIB seven 2.2.0 and Camunda 7.24.
  • CLI bearer suite: 10/10 on all three. Locally under podman it needs ENVOY_UID: "0", which main needs too; that change is not part of this PR.

Breaking changes

  • VS Code proxy and CA: proxy and CA come from VS Code's settings, so NODE_EXTRA_CA_CERTS no longer applies.
  • VS Code engine errors: they read HTTP <status>: <message>.
  • pulse-cli guide <topic>: it prints the docs page.

Out of scope / Follow-ups

Refs #470

The three per-engine catalogs were byte-identical (the engine patches were
all empty), so the npm bundle and the standalone executable carried the
same 1.6 MB catalog three times. Every adapter now reads
src/generated/catalog.json; the empty spec/engines patch files, the
per-engine generator loop and the dependency rule that tied an adapter to
its own catalog file go. A diverging engine gets a catalog of its own
again when it needs one.

npm bundle dist/pulse-cli.js: 5,563,120 -> 2,311,498 bytes;
standalone build/standalone/pulse-cli.cjs: 5,665,866 -> 2,414,244 bytes.
`pulse-cli guide <topic>` printed six hand-written topic pages that
restated docs/cli (configuration, authentication, commands, output and
errors, workflow commands, recipes). The topics now are those docs pages,
bundled as text (tsdown's .md text loader for the npm bundle and the
standalone build, a small vitest transform for the tests), so the command
still works offline and the docs are the single source.

The few agent-only facts the topics carried move into the pages (agents
never run auth login, non-JSON responses and --out-file, the notable
workflow options). The topic pages' own content and size tests go; the
docs pages keep their budgets and run every command line already, and a
test pins each topic to its docs page. docs/cli now also triggers the
CLI's build scope in CI and the turbo cache of its build.
The bearer resolution rules (token sources and precedence, normalization, conflicts, expiry, their error texts) are asserted by the unit tests of src/config/ and src/auth/. The run() tests keep one case per wiring path; the Testcontainers suite keeps only what needs the gateway or Keycloak (17 of 27 cases dropped, including the TOKEN_EXPIRED wait on a 5 s token).
…s through one helper each

registerDataFeed (data-feeds.ts), registerShowTool (widget-tools/shared.ts) and
adapterStep (steps/shared.ts) write the registration frame once; the call sites
hand the builders their arguments by rest destructuring instead of copying them
field by field. The tools/list goldens are unchanged.
registerSettingsSection (@miragon/pulse-widget-shell/server) registers a
module's show tool, app-only feed and gated save tool, derives canSave and the
optional readOnlyReason from the one save decision, refuses a keyless save and
stamps the OAuth caller as owner. The shell, camunda7 and analytics sections use
it; one contract test replaces the per-module duplicates. Tool names, schemas,
annotations and payloads are unchanged (tools/list goldens untouched).
nothing-imports-the-cli and nothing-imports-the-extension tripped only
together with packages-never-import-the-app or apps-never-import-apps:
every planted case of theirs already carried one of the two.
extension-is-not-an-mcp-host repeated what the extension's closed-world
config (apps/vscode/.dependency-cruiser.cjs) refuses anyway: a planted
widget-shell, client, React, zod, mcp-use, toolkit or TanStack import in
the host or the webview fails there as not-in-allowed and
not-to-unresolvable. RESERVED_APP_NAMES guarded apps that do not exist
(the list was empty); the declaration check for the real apps stays.

Ratchet-Exception: removes the depcruise rules nothing-imports-the-cli, nothing-imports-the-extension (covered by packages-never-import-the-app + apps-never-import-apps) and extension-is-not-an-mcp-host (covered by the closed-world apps/vscode/.dependency-cruiser.cjs), plus the empty RESERVED_APP_NAMES list of check-package-naming (guarded no existing app)
No workspace package is a -domain package, yet three dependency rules
(connectors-own-their-domain, domains-are-leaves, domains-are-pure), the
-domain carve-out of cli-is-headless, a no-restricted-globals block in
eslint.config.mjs and a third role suffix in the naming gate guarded it,
each with its planted cases. The CLI now imports no workspace package at
all, and a connector-family package is a -connector or a -client. A
package two interfaces share is extracted on its second consumer, with
the rules it needs then.

Ratchet-Exception: removes the depcruise rules connectors-own-their-domain, domains-are-leaves and domains-are-pure, the no-restricted-globals block for packages/connectors/*/*-domain and the -domain naming role; no -domain package exists, so they guarded nothing
…rom source

check-ratchets read eslint.config.mjs twice: an AST pass over the debt
maps, every complexity/max-lines/no-restricted-syntax setting and every
shape it could not see into, and the effective config ESLint itself
computes per file (eslint-effective-config.mjs), which already fails a
raised or dropped budget, a gate rule switched off or to warn and a file
newly ignored. The AST pass now keeps only what the effective check
cannot see: an ignores entry in a block of any other rule, and a local or
dynamic import (the base config is evaluated next to today's files). The
debt maps complexityRatchet and maxLinesRatchet were empty; they go with
their spreads and the fitness report's debt line.

Ratchet-Exception: drops the AST budget/debt-map/opaque-shape half of the eslint.config.mjs ratchet and the empty complexityRatchet/maxLinesRatchet maps; scripts/eslint-effective-config.mjs still fails every loosened complexity, max-lines or no-restricted-syntax setting and every newly ignored file, and the AST keeps ignores and local imports
lint:github keeps what protects the release credentials and the
published set: the npm publish job's id-token and action pins, the
publish matrix against the workspace with each package's repository,
the release-please extra-files and plugin versions, the release gate,
the CLI publish workflows' token shape, the VS Code publish path (one
job in vscode-marketplace, the store tokens by name, never secrets:
inherit, an install with --ignore-scripts and no build next to them)
and the CI scope wiring, which keeps a failed changes job from passing
as a row of skipped checks. Its test still runs the npm publish
workflow's bash steps.

Gone: the Dependabot group pins, the mirror of the ruleset's required
checks, the Playwright pin sync, the report-artifact, nightly-mutation
and Stryker incremental shape pins, the plain-release/dry-run/version
and --skip-duplicate pins of the extension workflow, and the bash runs
of the CLI release, tap update, extension Resolve and nightly steps.

Ratchet-Exception: drops the lint:github checks checkDependabot, checkRequiredChecks, checkPlaywrightPins, checkArtifactGlobs, checkNightlyMutation, checkStrykerIncremental and the non-credential VS Code release pins; they pinned workflow shape, not credentials, and a broken shape shows up as a failed or missing run
check-package-naming parsed every @types/vscode range against
engines.vscode to refuse a types floor above the oldest VS Code the
extension admits. vsce refuses exactly that package ("@types/vscode ...
greater than engines.vscode"), and CI's "VS Code package (vsce dry run)"
job runs vsce package on every pull request in the extension's scope.
The naming gate keeps the extension's name and a non-empty
engines.vscode.

Ratchet-Exception: drops the @types/vscode floor check (versionFloor, compareVersions, checkVsCodeTypes) from lint:naming; vsce package, run by the CI "VS Code package (vsce dry run)" job, refuses the same package
APP_SCAN in source-scan.ts already named the three apps and their kind;
on every scan it also walked apps/ to fail on an unclassified app, an
entry naming no app, an app without src/ and an extension classified as
an MCP server, each with its fixture test. The scan now reads the apps
APP_SCAN lists; a new app adds its entry in the PR that brings it.

Ratchet-Exception: drops the APP_SCAN classification checks (unclassified app, stale entry, app without src/, extension as mcp-host) and their self-tests; three apps exist and are listed, a new one adds its entry
The rename to Pulse is long done: old-names.test.ts no longer reads for
the old npm scope, repo, docs URL, image, starter, slug, product name and
Fly app names, nor for a capital in a GHCR path, and their allowances
go. It keeps what still fails silently: the retired operate CLI's
technical names, the unscoped npm package pulse-cli and an image off
GHCR (the server's and the CLI's, now one rule).

The pnpm filter checks (a --filter naming no workspace package, a pnpm
filter without --fail-if-no-match on a gate path) move to
scripts/check-package-scripts.mjs with their cases, so lint:package-scripts
owns everything about how the workspace's packages are addressed. The
Lint + Format job runs it next to docs-refs and old-names, so a docs-only
pull request is still checked.

Ratchet-Exception: drops the pre-Pulse name rules of old-names.test.ts (old scope, repo, docs URL, image, starter, slug, product name, Fly app names, image not lowercase); the rename shipped in 0.21.0 and nothing reintroduces those names; the filter checks moved unchanged to lint:package-scripts
pnpm test:mutation (scripts/mutation-diff.mjs) mapped a branch's changed
files onto each package's mutate list, capped the run at 25 files behind
a Mutation-Cap-Exception trailer and called each package's test:mutation
script, which lint:package-scripts held to the plain `stryker run`. It
is a local, on-demand helper no gate runs. The developer docs now give
the command per package instead, `pnpm --fail-if-no-match --filter
<package> exec stryker run` (`--mutate <file>` narrows it), and the
nightly workflow calls `exec stryker run` the same way. The
stryker.config.json files and the nightly run stay as they are.

Ratchet-Exception: drops the root and package test:mutation scripts (gate commands pinned by check-ratchets) and scripts/mutation-diff.mjs with its tests; no CI job ran the diff runner, the nightly run now calls stryker run directly, and lint:package-scripts still holds every stryker.config.json
The C7 adapter no longer bundles and spawns the retired operate CLI. A
small REST client inside the adapter (apps/vscode/src/adapters/c7/engine-rest)
sends every call with fetch: no redirects, a 60 s deadline, at most
64 MiB of an answer, basic auth from the target, lossless JSON, every
page of a list, and a read connection that refuses writes. The instance
inspection operate's `inspect` did (called instances, wait states, root
incidents with their stack traces, the history of activities, scope
variables) and its incident retry are ported into the adapter; the error
classification and redaction read HTTP statuses instead of exit codes.

Gone: the engine-cli wrapper, its build step and dist/engine-cli.js, the
@miragon/operate dependency, the engine-cli tests and fixtures, the
engine-cli-wrapper-is-self-contained rule and the operate allowances of
old-names. The conformance kit passes all 61 laws on operaton 2.1.5,
cibseven run-2.2.0 and camunda run-7.24.0.

Refs #470
Each of the 23 mapper classes had one implementation and one user. Their
translations are now private methods of the reader that uses them; the
Mapper interface and the wiring in C7ProcessRuntime.create are gone.
What several readers share (definition refs, instance states,
timestamps, root incidents) stays a static utility in mapping/.
Rule 4 of code-structure.md no longer asks for mapper classes: a
translation with one user is a private method of that class.
Every rule an agent follows stays. A rule a gate enforces is now one sentence plus the gate's name; the mechanics the gate already carries (and its failure message explains) go. Rules no gate enforces keep their reason. Invariant numbering, the section headings and the German Design-System section with its owner decisions stay.

Fixes drifts: SANCTIONED_TOKEN_MANIFESTS lists the docs kit and the VS Code webview theme, not only the kit; the extension's npm name is pulse-cockpit; the CLI has one command catalog; the releases section points at the trimmed lint:github instead of restating its removed pins.
@netlify

netlify Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for spectacular-cobbler-f6c85b ready!

Name Link
🔨 Latest commit d9a430f
🔍 Latest deploy log https://app.netlify.com/projects/spectacular-cobbler-f6c85b/deploys/6ae1a0cc2ef1f1c9a40609d9
😎 Deploy Preview https://deploy-preview-492--spectacular-cobbler-f6c85b.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@emaarco emaarco changed the title refactor: simplify the repo in nine steps refactor: remove duplicated catalogs, tests and guardrails, drop operate from VS Code Oct 28, 2026
@emaarco
emaarco marked this pull request as ready for review October 28, 2026 07:12
@emaarco
emaarco enabled auto-merge (squash) October 28, 2026 07:12
@emaarco
emaarco merged commit d2f423b into main Oct 28, 2026
33 checks passed
@emaarco
emaarco deleted the emaarco/refactor-simplify-the-repo branch October 28, 2026 07:20
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.

1 participant