Skip to content

perf(gate): spawn secretlint/madge once per gate — ~47% faster pre-commit (closes #211)#212

Open
yuyu04 wants to merge 1 commit into
qwerfunch:developfrom
yuyu04:feature/drift-filewalk-cache
Open

perf(gate): spawn secretlint/madge once per gate — ~47% faster pre-commit (closes #211)#212
yuyu04 wants to merge 1 commit into
qwerfunch:developfrom
yuyu04:feature/drift-filewalk-cache

Conversation

@yuyu04

@yuyu04 yuyu04 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

A gate-scoped memo makes the two shell-out detectors — HARDCODED_SECRET (secretlint) and ARCHITECTURE_VIOLATION (madge) — spawn their external tool once per gate instead of twice, the dedup secret.ts already documented but never enforced. Closes #211.

Why (measured, not assumed)

Per-detector profiling on cladding's own repo:

HARDCODED_SECRET       4425 ms   (secretlint)   ┐ together ~97% of the
ARCHITECTURE_VIOLATION 1406 ms   (madge)        ┘ drift stage
every other detector    < 60 ms

They run in the Drift stage (which sweeps all detectors) AND again as the dedicated Secret/Arch stage → 2× spawn = ~5.8s duplicated per gate.

An earlier hypothesis (caching the detectors' file-walk) was dropped after profiling — the file-walking detectors total <30 ms, so that idea was negligible. Following the measurement led here instead.

What's in the box

  • src/stages/scanner-cache.ts (new)primeScannerCache(on) + memoizeScan(key, compute), mirroring the run-scoped spec cache (F-cd0415). A fresh Map per primed gate; pass-through when unprimed.
  • hardcoded-secret.ts / architecture-violation.ts — route their execaSync through memoizeScan, keyed by (cwd, cmd, args).
  • clad.ts — primes the memo around the stage loop, clears in a finally (the long-lived MCP server must not carry a scan across runs).
  • secret.ts — comment updated: the "avoids spawning twice" promise is now actually enforced.
  • Lives in src/stages/ (NOT detectors/) so the 38-detector count is unchanged.

A/B (isolated worktrees, 3 runs each)

clad check --tier=pre-commit time
develop (spawn ×2) 11.7 / 11.9 / 12.3 s
this PR (spawn ×1) 6.3 / 6.3 / 6.4 s
delta ~−47%

Correctness: the HARDCODED_SECRET / ARCHITECTURE_VIOLATION findings are identical — the memo returns the same result, just once (proven by the unit test returning the same reference; the only OLD↔branch JSON diffs are unrelated branch state — a new feature shard + pre-attestation, not scanner findings).

Scope / safety

  • Pass-through when unprimed → standalone detector calls and the MCP read path are byte-for-byte unchanged.
  • Cleared in finally → each gate run gets a fresh cache; no stale cross-run scans in the MCP server.

Feature cycle

spec/features/gate-scanner-memo-5a49899e.yaml (F-5a49899e, 4 ACs) → implement → blind tests (tests/stages/scanner-cache.test.ts, 6) → clad done GREEN. Single clean commit on develop.

🤖 Generated with Claude Code

…anner memo (F-5a49899e)

HARDCODED_SECRET (secretlint, ~4.4s) and ARCHITECTURE_VIOLATION (madge, ~1.4s)
shell out to an external tool — together ~97% of the drift stage. They ran
TWICE per gate: once inside the Drift stage (stage_1.3 sweeps every detector)
and again as their dedicated Secret (stage_1.6) / Arch (stage_1.5) stage.
secret.ts even documented that the layering "avoids spawning the scanner twice"
— but nothing enforced it, so a full gate paid ~5.8s of duplicate subprocess time.

- src/stages/scanner-cache.ts (new) — gate-scoped memo (mirrors the run-scoped
  spec cache F-cd0415): primeScannerCache(on) + memoizeScan(key, compute).
- hardcoded-secret.ts / architecture-violation.ts — route execaSync through
  memoizeScan keyed by (cwd, cmd, args). Pass-through when no cache is primed
  → standalone/MCP behavior byte-for-byte unchanged.
- clad check primes the memo around the stage loop, clears in finally (the
  long-lived MCP server must not carry a scan across runs).
- secret.ts comment updated: the dedup it promised is now actually enforced.

Measured (isolated worktree A/B, cladding's own repo):
  clad check --tier=pre-commit  ~11.9s → ~6.3s (-47%), IDENTICAL findings.

Blind-authored tests (tests/stages/scanner-cache.test.ts, 6). scanner-cache.ts
lives in src/stages/ (NOT detectors/) so the 38-detector count is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@yuyu04 yuyu04 force-pushed the feature/drift-filewalk-cache branch from 4c68d1b to bd3d525 Compare July 2, 2026 01:31
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