Skip to content

Bundler migration, type packaging, scan perf, and community contribution infra - #3

Merged
paladini merged 10 commits into
mainfrom
feat/distribution-perf-community
Jul 14, 2026
Merged

Bundler migration, type packaging, scan perf, and community contribution infra#3
paladini merged 10 commits into
mainfrom
feat/distribution-perf-community

Conversation

@paladini

@paladini paladini commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Summary

Four independent improvements to harness-score, each verified not to
regress the existing public behavior:

1. Distribution — migrate build to tsup

  • packages/cli/dist/ goes from 36 unbundled files (~82KB) to 5 files
    (~54KB) via tsup with splitting + minification.
  • package.json gains an explicit "types" field and a "types"
    condition in "exports".
  • RELEASING.md documents that JSR deliberately keeps publishing raw
    src/**/*.ts (pre-existing, not something this PR needed to reconcile).

2. Type completeness for library consumers

  • test/types/smoke.ts: imports every public symbol from the built
    dist/ output (not src/), type-checked via a new typecheck:consumer
    script — catches packaging regressions vitest's runtime tests (which
    import from src/) never see.
  • check:types runs attw --pack . --profile esm-only in CI (the
    package was already ESM-only before this change).

3. Scan performance

  • ctx.matching(re) is now memoized per regex source+flags inside
    createScanContext — several checks already re-query the same pattern
    independently (e.g. every CTX-03..06 rule check calls
    matching(RULE_RE)). No behavior change (verified byte-identical
    output). ~7% faster on a 20k-file synthetic benchmark
    (npm run bench); negligible on this repo itself (too small to show
    the win).
  • Deliberately did not attempt async/parallel file reads —
    Check.run()/ScanContext.read() are public API and synchronous by
    contract; making them async would be a breaking (major-version) change,
    not a drive-by perf tweak.

4. Community contribution infrastructure

  • .github/ISSUE_TEMPLATE/rubric_change.yml: dedicated add/edit/remove-a-check
    proposal form (dimension, points, fixture impact, sync checklist).
  • .github/CODEOWNERS: routes rubric/release-surface PRs for review.
  • .github/dependabot.yml: weekly devDependency + Actions updates
    (packages/cli still has zero runtime dependencies).
  • Changesets (@changesets/cli) + scripts/sync-version.mjs: contributors
    describe user-facing changes via npm run changeset; npm run version-packages bumps package.json, generates
    packages/cli/CHANGELOG.md, and mirrors the version into
    TOOL_VERSION/jsr.json (which changesets doesn't know about natively).

Not done here (needs the maintainer's own action, not a code change):
enabling GitHub Discussions, installing an all-contributors bot, adding
FUNDING.yml.

Non-breaking verification (see plan's "Garantias de não-quebra")

  • packages/cli/test/golden-output.test.ts: new snapshot baseline of the
    Report JSON for every fixture + this repo's own self-scan — passed
    unchanged through the build/perf changes.
  • Real external-consumer smoke test: npm pack, install the tarball in a
    throwaway project, run the bin directly and via npx, import the
    library at runtime, and tsc --noEmit against a consumer script using
    the published types.
  • npm ci clean install + full CI-equivalent gate (lint, test:coverage,
    self-scan, docs:build) all green on the final state.
  • Also closed a pre-existing gap while in the area: maturity-model.md's
    dimension-points table and level thresholds were only mirrored by hand
    (a comment saying "change both together"); test/rubric-sync.test.ts
    now enforces it in CI, same as docs.test.ts already does for
    measure-and-improve.md.

Test plan

  • npm ci (clean install) + npm run lint + npm run test:coverage -w harness-score + self-audit (node packages/cli/dist/cli.js . --min-level 4) + npm run docs:build — all green
  • packages/cli — 171/171 tests pass, including the new golden-output snapshot and rubric-sync tests
  • Repo self-scan stays L4 · 108/108 (100%)
  • npm pack + install in an external throwaway project: bin execution, npx, runtime import, and tsc --noEmit against consumer types all pass
  • attw --pack . --profile esm-only clean
  • npm run bench shows ~7% improvement on a 20k-file synthetic repo, no regression on this repo
  • Maintainer: enable GitHub Discussions / add FUNDING.yml / install all-contributors bot, if desired (out of scope for this PR)

paladini and others added 5 commits July 14, 2026 15:15
…seline

maturity-model.md's dimension-points table and LEVEL_REQUIREMENTS thresholds
were only mirrored by hand ("change both together" in a comment), unlike
measure-and-improve.md which docs.test.ts already enforces. A new
rubric-sync.test.ts closes that gap. levels.test.ts now derives the total
from ALL_CHECKS instead of a bare 108 literal, with an explicit snapshot
test forcing a conscious bump when the rubric total changes.

golden-output.test.ts snapshots the Report JSON for every fixture and the
self-scan, giving upcoming build/perf changes a concrete regression baseline.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Replace plain tsc emit (36 unbundled files, ~82KB) with a tsup build:
splitting+minify collapse dist/ to 5 files at ~54KB (index.js, cli.js, a
shared chunk, and rolled-up .d.ts per entry). package.json gains an explicit
"types" field and a "types" condition in "exports" so TS consumers resolve
declarations without relying on default sibling-file lookup; tsc now runs
as a separate --noEmit typecheck step instead of doing the emit.

Verified non-breaking: golden-output.test.ts (byte-identical Report JSON,
modulo tool.version), self-scan stays L4/108/108, and a real external
consumer smoke test — npm pack, install the tarball in a throwaway project,
run the bin directly and via npx, import the library at runtime, and
tsc --noEmit against a consumer.ts using the published types. attw --pack
shows no new problems (the package was already ESM-only before this change).

RELEASING.md documents that JSR keeps publishing raw src/**/*.ts rather than
the dist/ bundle — a deliberate, pre-existing divergence (JSR type-checks
TS natively), not something this change needed to reconcile.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The public symbol surface was already complete (src/index.ts re-exports
everything), but nothing verified the *published* declarations resolve
correctly for an external consumer. Two additions close that gap:

- test/types/smoke.ts imports every exported symbol from dist/index.js
  (not src/) and is type-checked in isolation via a new
  `typecheck:consumer` script + tsconfig — this is what would have caught
  a wrong "types" path or a missing re-export, which vitest's runtime
  tests (importing from src/) never exercise.
- `check:types` runs `attw --pack .` with the `esm-only` profile (the
  package was already ESM-only before this change; the profile just stops
  attw from flagging that pre-existing, intentional characteristic as an
  error).

Both wired into ci.yml. golden-output snapshot updated: adding files under
packages/cli/test/ shifts the self-scan's own SNS-05 "test file" count
(that check's regex matches anything under a test/ directory, not just
*.test.* files) — a pre-existing check quirk, not a regression from this
change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
files is fixed once the walk completes, so matching(re) is a pure function
of re's source+flags. Several checks already re-query the exact same
pattern independently (every CTX-03..06 rule check calls matching(RULE_RE),
HYG-04/HYG-08 both call matching(MCP_RE)) — this turns those repeats into
one filter() per distinct pattern instead of one per call site. No behavior
change: verified against golden-output.test.ts (byte-identical Report JSON)
and the full fixture suite.

Deliberately NOT attempted here: parallelizing file reads. Check.run()
and ScanContext.read() are part of the public API and synchronous by
contract — making reads async would require an API break (a major version),
not something to slip in as a perf tweak. Left for a future breaking release
per the "Garantias de não-quebra" in the distribution/perf plan.

Added bench/scan.bench.mjs (npm run bench) — builds a synthetic repo and
times score() against it, since there was no way to measure scan-time
changes before this. Measured: ~7% faster on a 20k-file synthetic repo
(165.8ms → 154.1ms avg over 8 runs); negligible on this repo itself
(too few files with repeated-pattern checks for the win to show), which is
expected — this specifically targets large monorepos, not small ones.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…hange template, changesets

Formalizes the contribution process CONTRIBUTING.md already describes in
prose, and adds low-risk repo-maintenance automation, without inventing a
new schema/plugin layer for checks (kept to hand-written TS checks, per
scope decision):

- .github/ISSUE_TEMPLATE/rubric_change.yml: a dedicated form for
  add/edit/remove-a-check proposals (dimension, points, detection logic,
  fixture impact, and a checklist of the artifacts that must move
  together) — distinct from the general feature_request.yml, and covers
  edit/removal which that template doesn't.
- .github/CODEOWNERS: routes PRs touching checks/score.ts/maturity docs/
  fixtures/release workflow to @paladini automatically.
- .github/dependabot.yml: weekly updates for devDependencies (grouped,
  minor/patch only) and GitHub Actions. packages/cli has zero runtime
  dependencies by design, so this never touches the supply-chain surface
  that actually ships to consumers.
- Changesets (@changesets/cli): contributors run `npm run changeset` to
  describe a user-facing change; `npm run version-packages` (changeset
  version + scripts/sync-version.mjs) bumps packages/cli/package.json,
  generates packages/cli/CHANGELOG.md, and mirrors the new version into
  TOOL_VERSION/jsr.json — closing the "bump three files by hand" gap
  changesets doesn't know about on its own. Verified sync-version.mjs
  against a scratch copy (not the real files) before relying on it.
  RELEASING.md documents this as the preferred path; the old manual
  triple-bump still works when no changesets were added.
- CONTRIBUTING.md links the new template from "Adding or changing a
  check" and mentions adding a changeset before opening a PR.

Deliberately not done here (need the maintainer's own action or a
decision this PR shouldn't make unilaterally): enabling GitHub
Discussions, installing an all-contributors bot, or adding FUNDING.yml.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@paladini paladini self-assigned this Jul 14, 2026
paladini and others added 5 commits July 14, 2026 17:18
report.root is path.resolve(rootInput) — an absolute filesystem path that
differs between machines (D:\...\fixtures\level-3 locally vs
/home/runner/work/... on the GitHub Actions runner). The snapshot baseline
captured it verbatim, so every snapshot test failed in CI despite passing
locally. Excluded alongside tool.version, which was already normalized out
for the same class of reason (environment-dependent, not a real regression).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Updates the harness, guide, and agent-facing docs to match this PR's
actual state, plus folds in a few small pre-existing (uncommitted) fixes
that were sitting in the working tree from an earlier session:

- ROADMAP.md: new "Shipped in v0.4.0" section (bundler migration, verified
  type packaging, scan-perf memoization, community infra) following the
  existing v0.2.0/v0.3.0 pattern; "Proposing something new" now points at
  the new Rubric change issue template specifically.
- docs/guide/measure-and-improve.md: new "Using it as a library" section
  (score(), and the lower-level createScanContext/buildReport/computeDiff/
  renderMarkdown building blocks) — this capability existed before but was
  only documented in the npm README, not the canonical guide.
- packages/cli/README.md, README.md: mention the bundled build + verified
  type packaging; link the new Rubric change template from the
  Contributing section; add `npm run bench` to the dev commands.
- AGENTS.md, .cursor/rules/{core,checks}.mdc, .cursor/skills/release/SKILL.md:
  bring the harness's own guidance current — `npm test` now also
  typechecks and runs the packaging smoke test, `rubric-sync.test.ts`
  closes the maturity-model.md enforcement gap, the sync/read-parallelism
  API-stability rule, and the changesets-based release flow. Also fixes a
  real stale fact in checks.mdc ("the overall total must stay 100" — it's
  been 108 since v0.2.0, just never corrected).
- .github/ISSUE_TEMPLATE/feature_request.yml: narrowed to non-rubric
  features now that rubric_change.yml exists, so the two templates don't
  overlap/compete in the issue picker.
- Small pre-existing fixes folded in (predate this session, never
  committed): stale v0.1.2/61/100 example output in packages/cli/README.md
  corrected to v0.3.0/66/108; HomeLanding.vue's Cursor plugin link pointed
  at the Marketplace before it's actually live there — now points at the
  in-repo plugin/ directory; PLUGINS-ROADMAP.md (the multi-harness plugin
  plan ROADMAP.md already linked to) added since it didn't exist in git
  yet.

Verified: npm test (171/171), biome check (0 errors), self-scan stays
L4/108/108/100% (CTX-02's evidence line legitimately changed — AGENTS.md
grew from 36 to 49 non-empty lines — golden-output snapshot updated to
match), npm run docs:build clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ting that isn't live

Verified against the actual Cursor Marketplace (a search for "harness-score"
returns nothing; the only "Harness" result is an unrelated harness.io
plugin) — five places still said "Install from the Cursor Marketplace" as
if the listing were live: docs/index.md's hero action, the "Cursor plugin"
product card in HomeLanding.vue (a second, separate spot from the "installs"
list that a prior session already corrected), measure-and-improve.md,
packages/cli/README.md, and cursor-harness-surface.md. All now point at the
plugin's directory in this repo instead, with a note that the Marketplace
listing is submitted and pending review — accurate today, and each spot
already links to a real, working destination instead of a search that
finds nothing (or the wrong plugin entirely).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tually pinned

/harness-audit always runs npx harness-score, which resolves to whatever's
latest on npm — not a version frozen at plugin-release time. The "33 checks"
figure in the 0.1.0 entry wasn't a harmless historical fact (like a fixed
bug), it was actively describing current behavior incorrectly (the rubric
has had 36 checks since v0.2.0), so treating it as an untouchable changelog
entry didn't hold up on reflection — removed the number rather than
re-pinning a new one that would just go stale again next rubric change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Today's live research, written into the repo instead of left only in
chat: a new "Landscape research (2026-07-14)" section covering two facts
that change the plan's calculus (AGENTS.md is now a Linux Foundation-
governed standard read by 8 of the top 10 tools researched; MCP is
near-universal across the same list), a verified top-10 comparison table
of AI-first dev tools and their own artifact conventions, and new
deep-dive sections for OpenCode, Cline, Continue.dev, Zed, and JetBrains
AI Assistant/Junie (previously a one-line "not researched" stub for three
of these). Repo layout, release-pipeline notes, and sequencing updated to
include all of them; the three open decisions needing a maintainer call
are unchanged in substance, just updated with the new scope (nine targets,
not four).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@paladini
paladini merged commit 6c7a64b into main Jul 14, 2026
1 check passed
@paladini
paladini deleted the feat/distribution-perf-community branch July 14, 2026 23:01
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