@thedecipherist/claude-packs 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +680 -0
- package/LICENSE +21 -0
- package/README.md +316 -2
- package/bin/claude-packs.mjs +204 -0
- package/configurations/base/config.json +85 -0
- package/configurations/cpp/config.json +92 -0
- package/configurations/full-stack/config.json +152 -0
- package/configurations/go/config.json +96 -0
- package/configurations/node/config.json +100 -0
- package/configurations/python/config.json +97 -0
- package/configurations/synth-creator/config.json +148 -0
- package/global/hooks/gnu-shell.mjs +131 -0
- package/global/hooks/lib/program-read.mjs +196 -0
- package/global/hooks/lib/program-writes.mjs +177 -0
- package/global/hooks/lib/shell-writes.mjs +293 -0
- package/global/hooks/shell-traps.mjs +79 -0
- package/global/hooks/version-pin-guard.mjs +280 -0
- package/global/skills/latest-versions/SKILL.md +49 -0
- package/lib/backup.mjs +34 -0
- package/lib/claude-md.mjs +58 -0
- package/lib/config-cli.mjs +36 -0
- package/lib/configs.mjs +145 -0
- package/lib/coverage.mjs +86 -0
- package/lib/custom-packs.mjs +93 -0
- package/lib/doctor.mjs +115 -0
- package/lib/fix-plan.mjs +71 -0
- package/lib/git-tokens.mjs +64 -0
- package/lib/global.mjs +125 -0
- package/lib/hook-proof.mjs +28 -0
- package/lib/hook-timing.mjs +42 -0
- package/lib/hover-gate.mjs +29 -0
- package/lib/kit-lock.mjs +149 -0
- package/lib/layout.mjs +75 -0
- package/lib/logs.mjs +69 -0
- package/lib/manifest.mjs +24 -0
- package/lib/new.mjs +49 -0
- package/lib/once-only-report.mjs +54 -0
- package/lib/pack-manifest.mjs +20 -0
- package/lib/packs.mjs +107 -0
- package/lib/planner.mjs +59 -0
- package/lib/repeats.mjs +65 -0
- package/lib/repo-weight.mjs +28 -0
- package/lib/rules.mjs +40 -0
- package/lib/scan-detect.mjs +94 -0
- package/lib/scan.mjs +168 -0
- package/lib/selftest-cache.mjs +39 -0
- package/lib/settings.mjs +138 -0
- package/lib/shape-cli.mjs +76 -0
- package/lib/shape.mjs +4 -0
- package/lib/suggest.mjs +18 -0
- package/lib/switches.mjs +44 -0
- package/lib/symmetry-doctor.mjs +26 -0
- package/lib/sync.mjs +130 -0
- package/package.json +34 -4
- package/packs/ai/checks/boundary-computes-its-own-hash.json +30 -0
- package/packs/ai/checks/model-call-needs-boundary.json +29 -0
- package/packs/ai/checks/model-id-from-config.json +28 -0
- package/packs/ai/checks/model-output-validated.json +26 -0
- package/packs/ai/pack.json +38 -0
- package/packs/ai/rules/ai-input-sha-boundary.md +31 -0
- package/packs/ai/rules/llm-pipeline-security.md +26 -0
- package/packs/audio-core/checks/denormals-not-in-prepare.json +12 -0
- package/packs/audio-core/checks/dsp-without-apvts.json +12 -0
- package/packs/audio-core/checks/float-phase-or-rate.json +12 -0
- package/packs/audio-core/checks/juce-snap-to-zero-off.json +12 -0
- package/packs/audio-core/checks/keep-werror.json +12 -0
- package/packs/audio-core/checks/no-fast-math.json +14 -0
- package/packs/audio-core/checks/no-locale-change.json +12 -0
- package/packs/audio-core/checks/no-march-native.json +14 -0
- package/packs/audio-core/clang-tidy +28 -0
- package/packs/audio-core/hooks/lib/realtime-checks.mjs +56 -0
- package/packs/audio-core/hooks/param-id-lock.mjs +129 -0
- package/packs/audio-core/hooks/realtime-guard.mjs +67 -0
- package/packs/audio-core/pack.json +187 -0
- package/packs/audio-core/rules/audio-thread-realtime-safe.md +27 -0
- package/packs/audio-core/rules/audio-ui-thread-handoff.md +21 -0
- package/packs/audio-core/rules/boundary-tests-cross-the-boundary.md +16 -0
- package/packs/audio-core/rules/build-flags-safe.md +20 -0
- package/packs/audio-core/rules/cached-parameter-pointers.md +28 -0
- package/packs/audio-core/rules/clang-tidy-core-guidelines.md +21 -0
- package/packs/audio-core/rules/control-reaches-every-surface.md +23 -0
- package/packs/audio-core/rules/cpu-worst-case-per-block.md +26 -0
- package/packs/audio-core/rules/dc-block-after-asymmetry.md +22 -0
- package/packs/audio-core/rules/discrete-params-snap.md +19 -0
- package/packs/audio-core/rules/double-where-precision-matters.md +20 -0
- package/packs/audio-core/rules/filters-stable-under-modulation.md +29 -0
- package/packs/audio-core/rules/host-parameters-at-scale.md +24 -0
- package/packs/audio-core/rules/house-control-behaviours.md +34 -0
- package/packs/audio-core/rules/math-domain-guards.md +19 -0
- package/packs/audio-core/rules/modulated-delay-interpolation.md +22 -0
- package/packs/audio-core/rules/multiband-phase-coherent.md +22 -0
- package/packs/audio-core/rules/nonlinear-stages-antialiased.md +28 -0
- package/packs/audio-core/rules/note-ledger-and-pedals.md +22 -0
- package/packs/audio-core/rules/one-formula-per-value.md +20 -0
- package/packs/audio-core/rules/optimisations-are-null-tested.md +23 -0
- package/packs/audio-core/rules/oscillators-bandlimited.md +23 -0
- package/packs/audio-core/rules/output-limiter.md +23 -0
- package/packs/audio-core/rules/parameter-ids-never-change.md +20 -0
- package/packs/audio-core/rules/params-and-state-thread-safe.md +19 -0
- package/packs/audio-core/rules/params-snapshot-per-block.md +21 -0
- package/packs/audio-core/rules/patch-excludes-performance-controls.md +18 -0
- package/packs/audio-core/rules/per-sample-code-inlinable.md +22 -0
- package/packs/audio-core/rules/prepare-is-repeatable.md +23 -0
- package/packs/audio-core/rules/recursive-state-nan-guard.md +20 -0
- package/packs/audio-core/rules/reverb-loop-gain-bounded.md +24 -0
- package/packs/audio-core/rules/sample-accurate-midi.md +22 -0
- package/packs/audio-core/rules/sample-rate-and-block-size-from-host.md +23 -0
- package/packs/audio-core/rules/scoped-no-denormals.md +26 -0
- package/packs/audio-core/rules/smooth-continuous-parameters.md +22 -0
- package/packs/audio-core/rules/state-load-versioned-and-safe.md +22 -0
- package/packs/audio-core/rules/straight-on-and-aligned.md +21 -0
- package/packs/audio-core/rules/ui-matches-reference-look.md +21 -0
- package/packs/audio-core/rules/undo-one-gesture-one-step.md +21 -0
- package/packs/audio-core/rules/voice-steal-fades.md +21 -0
- package/packs/audio-core/rules/voice-sum-headroom.md +20 -0
- package/packs/audio-core/skills/analyse-reference-look/SKILL.md +21 -0
- package/packs/audio-core/skills/copy-and-prove/SKILL.md +98 -0
- package/packs/audio-core/skills/juce-realtime-audit/SKILL.md +22 -0
- package/packs/audio-core/skills/measure-tone/SKILL.md +20 -0
- package/packs/audio-core/skills/new-panel-control/SKILL.md +15 -0
- package/packs/audio-core/skills/prove-control/SKILL.md +23 -0
- package/packs/audio-core/skills/trace-parameter/SKILL.md +29 -0
- package/packs/audio-core/tools/lib/spectrum.mjs +112 -0
- package/packs/audio-core/tools/lib/wav.mjs +41 -0
- package/packs/audio-core/tools/tone-report.mjs +47 -0
- package/packs/base/checks/field-map-over-if-runs.json +25 -0
- package/packs/base/checks/lookup-over-if-chains.json +25 -0
- package/packs/base/checks/lookup-over-ternary-chains.json +20 -0
- package/packs/base/checks/no-code-after-midline-comment.json +12 -0
- package/packs/base/checks/one-walk-per-template.json +23 -0
- package/packs/base/checks/stub-reports-success.json +31 -0
- package/packs/base/checks/test-skip-on-failed-setup.json +17 -0
- package/packs/base/hooks/brief-guard.mjs +96 -0
- package/packs/base/hooks/code-shape.mjs +95 -0
- package/packs/base/hooks/dispatch-once.mjs +87 -0
- package/packs/base/hooks/file-size-guard.mjs +61 -0
- package/packs/base/hooks/folder-law.mjs +74 -0
- package/packs/base/hooks/git-guard.mjs +289 -0
- package/packs/base/hooks/lesson-checks.mjs +174 -0
- package/packs/base/hooks/lib/apply-edit.mjs +20 -0
- package/packs/base/hooks/lib/code-shapes.mjs +137 -0
- package/packs/base/hooks/lib/content.mjs +14 -0
- package/packs/base/hooks/lib/cpp-scan.mjs +94 -0
- package/packs/base/hooks/lib/dup-index.mjs +199 -0
- package/packs/base/hooks/lib/dup.mjs +127 -0
- package/packs/base/hooks/lib/elements.mjs +87 -0
- package/packs/base/hooks/lib/event-log.mjs +67 -0
- package/packs/base/hooks/lib/function-shapes.mjs +90 -0
- package/packs/base/hooks/lib/git-adds.mjs +20 -0
- package/packs/base/hooks/lib/git-guard-selftest.mjs +99 -0
- package/packs/base/hooks/lib/glob.mjs +33 -0
- package/packs/base/hooks/lib/hook-log.mjs +100 -0
- package/packs/base/hooks/lib/house.py +77 -0
- package/packs/base/hooks/lib/languages.mjs +66 -0
- package/packs/base/hooks/lib/layout.mjs +54 -0
- package/packs/base/hooks/lib/making-check-selftest.mjs +109 -0
- package/packs/base/hooks/lib/near-copies.mjs +76 -0
- package/packs/base/hooks/lib/program-read.mjs +196 -0
- package/packs/base/hooks/lib/program-writes.mjs +177 -0
- package/packs/base/hooks/lib/project.mjs +81 -0
- package/packs/base/hooks/lib/prose.mjs +11 -0
- package/packs/base/hooks/lib/pushed.mjs +201 -0
- package/packs/base/hooks/lib/repo-index.mjs +52 -0
- package/packs/base/hooks/lib/secret-patterns.mjs +18 -0
- package/packs/base/hooks/lib/selftest.mjs +45 -0
- package/packs/base/hooks/lib/shape-code.mjs +126 -0
- package/packs/base/hooks/lib/shape.mjs +294 -0
- package/packs/base/hooks/lib/shell-writes-selftest.mjs +65 -0
- package/packs/base/hooks/lib/shell-writes.mjs +293 -0
- package/packs/base/hooks/lib/turn.mjs +81 -0
- package/packs/base/hooks/lib/writes.mjs +71 -0
- package/packs/base/hooks/making-check.mjs +243 -0
- package/packs/base/hooks/making.mjs +106 -0
- package/packs/base/hooks/once-only.mjs +150 -0
- package/packs/base/hooks/placement-law.mjs +84 -0
- package/packs/base/hooks/repo-litter.mjs +75 -0
- package/packs/base/hooks/reuse-hint.mjs +75 -0
- package/packs/base/hooks/run-hooks.mjs +132 -0
- package/packs/base/hooks/secrets-guard.mjs +99 -0
- package/packs/base/hooks/shape-law.mjs +133 -0
- package/packs/base/hooks/suite-once.mjs +80 -0
- package/packs/base/hooks/template-law.mjs +148 -0
- package/packs/base/hooks/untrusted-boundary.mjs +53 -0
- package/packs/base/pack.json +214 -0
- package/packs/base/rules/a-control-is-seen-to-fire.md +27 -0
- package/packs/base/rules/behaviour-lives-with-its-kind.md +51 -0
- package/packs/base/rules/bug-fix-starts-red.md +25 -0
- package/packs/base/rules/field-map-over-if-runs.md +47 -0
- package/packs/base/rules/finished-work-lands.md +17 -0
- package/packs/base/rules/git-right-first-time.md +19 -0
- package/packs/base/rules/instances-by-index-identities-by-id.md +54 -0
- package/packs/base/rules/lookup-over-if-chains.md +49 -0
- package/packs/base/rules/making-line-first.md +14 -0
- package/packs/base/rules/open-items-tagged.md +17 -0
- package/packs/base/rules/plan-and-logs.md +22 -0
- package/packs/base/rules/references-use-anchors.md +19 -0
- package/packs/base/rules/refusals-are-judged.md +18 -0
- package/packs/base/rules/reuse-before-writing.md +15 -0
- package/packs/base/rules/secrets-never-committed.md +16 -0
- package/packs/base/rules/skill-writing.md +23 -0
- package/packs/base/rules/split-before-the-limit.md +14 -0
- package/packs/base/rules/subagent-dispatch.md +26 -0
- package/packs/base/rules/test-only-what-changed.md +17 -0
- package/packs/base/rules/test-the-template-once.md +22 -0
- package/packs/base/rules/untrusted-content-boundary.md +15 -0
- package/packs/base/skills/add-lesson-check/SKILL.md +26 -0
- package/packs/base/skills/fix-from-scan/SKILL.md +25 -0
- package/packs/base/skills/land-branch/SKILL.md +24 -0
- package/packs/base/skills/make-it-a-template/SKILL.md +23 -0
- package/packs/base/skills/pdf-to-md/SKILL.md +25 -0
- package/packs/base/skills/pick-tests/SKILL.md +20 -0
- package/packs/base/skills/pressure-test-skill/SKILL.md +23 -0
- package/packs/base/skills/prove-instances-identical/SKILL.md +37 -0
- package/packs/base/tools/shape-symmetry-selftest.mjs +66 -0
- package/packs/base/tools/shape-symmetry.mjs +178 -0
- package/packs/base/tools/test-verdict.mjs +75 -0
- package/packs/ci/checks/ci-permissions-stated.json +18 -0
- package/packs/ci/checks/ci-tools-pinned.json +14 -0
- package/packs/ci/pack.json +21 -0
- package/packs/ci/rules/ci-covers-what-ships.md +20 -0
- package/packs/ci/rules/generated-files-have-no-diff.md +15 -0
- package/packs/content-site/checks/captcha-is-not-constant.json +27 -0
- package/packs/content-site/checks/img-has-size.json +20 -0
- package/packs/content-site/checks/page-head-complete.json +26 -0
- package/packs/content-site/pack.json +22 -0
- package/packs/content-site/rules/content-has-one-source.md +23 -0
- package/packs/content-site/rules/media-is-sized.md +24 -0
- package/packs/content-site/rules/page-head-is-complete.md +26 -0
- package/packs/content-site/rules/public-forms-are-defended.md +27 -0
- package/packs/content-site/skills/publish-a-page/SKILL.md +21 -0
- package/packs/cpp/checks/cpp-clamp-before-cast.json +16 -0
- package/packs/cpp/checks/cpp-find-with-the-library.json +12 -0
- package/packs/cpp/checks/cpp-hardened-test-build.json +18 -0
- package/packs/cpp/checks/cpp-m-pi-not-standard.json +23 -0
- package/packs/cpp/checks/cpp-no-atoi.json +16 -0
- package/packs/cpp/checks/cpp-no-const-cast.json +16 -0
- package/packs/cpp/checks/cpp-no-float-loop-counter.json +16 -0
- package/packs/cpp/checks/cpp-no-getenv-in-product.json +22 -0
- package/packs/cpp/checks/cpp-no-mutable-global.json +20 -0
- package/packs/cpp/checks/cpp-no-mutable-local-static.json +23 -0
- package/packs/cpp/checks/cpp-no-unchecked-fwrite.json +17 -0
- package/packs/cpp/checks/cpp-no-using-namespace-in-header.json +19 -0
- package/packs/cpp/checks/cpp-no-zero-size-array.json +23 -0
- package/packs/cpp/checks/cpp-numbers-pi.json +16 -0
- package/packs/cpp/pack.json +22 -0
- package/packs/cpp/rules/cpp-absent-and-failed-are-types.md +24 -0
- package/packs/cpp/rules/cpp-builds-everywhere.md +26 -0
- package/packs/cpp/rules/cpp-faults-only-in-the-test-build.md +21 -0
- package/packs/cpp/rules/cpp-find-with-the-library.md +19 -0
- package/packs/cpp/rules/cpp-gates-share-one-reader.md +22 -0
- package/packs/cpp/rules/cpp-headers-declare.md +22 -0
- package/packs/cpp/rules/cpp-numbers-and-casts.md +22 -0
- package/packs/cpp/rules/cpp-repeats-become-templates.md +33 -0
- package/packs/cpp/rules/cpp-static-analysis-runs.md +22 -0
- package/packs/cpp/rules/cpp-thread-contracts-are-code.md +23 -0
- package/packs/cpp/skills/cpp-extract-template/SKILL.md +38 -0
- package/packs/docker/checks/alpine-with-native-build.json +13 -0
- package/packs/docker/checks/compose-db-needs-auth.json +14 -0
- package/packs/docker/checks/compose-image-pinned.json +14 -0
- package/packs/docker/checks/compose-no-hardcoded-ip.json +12 -0
- package/packs/docker/checks/compose-no-host-bind-for-data.json +18 -0
- package/packs/docker/checks/compose-no-plaintext-secret.json +13 -0
- package/packs/docker/checks/compose-no-swarm-ignored-keys.json +13 -0
- package/packs/docker/checks/compose-overlay-not-bridge.json +13 -0
- package/packs/docker/checks/compose-service-needs-healthcheck.json +11 -0
- package/packs/docker/checks/compose-service-needs-init.json +11 -0
- package/packs/docker/checks/copy-needs-chown-with-user.json +17 -0
- package/packs/docker/checks/copy-source-before-deps.json +13 -0
- package/packs/docker/checks/docker-exec-form.json +16 -0
- package/packs/docker/checks/dockerfile-needs-healthcheck.json +17 -0
- package/packs/docker/checks/dockerfile-needs-user.json +21 -0
- package/packs/docker/checks/dockerignore-has-the-basics.json +21 -0
- package/packs/docker/checks/gitattributes-forces-lf.json +17 -0
- package/packs/docker/checks/health-checks-nothing.json +25 -0
- package/packs/docker/checks/no-crlf-in-scripts.json +15 -0
- package/packs/docker/checks/no-latest-base.json +13 -0
- package/packs/docker/checks/no-secret-in-image.json +13 -0
- package/packs/docker/checks/no-sensitive-file-copy.json +13 -0
- package/packs/docker/checks/node-env-before-install.json +13 -0
- package/packs/docker/checks/npm-ci-not-install.json +13 -0
- package/packs/docker/pack.json +46 -0
- package/packs/docker/rules/compose-pinned-and-closed.md +21 -0
- package/packs/docker/rules/compose-swarm-safe.md +26 -0
- package/packs/docker/rules/docker-exec-form.md +19 -0
- package/packs/docker/rules/dockerfile-multistage.md +13 -0
- package/packs/docker/rules/dockerfile-production.md +25 -0
- package/packs/docker/skills/docker-diagnose/SKILL.md +23 -0
- package/packs/dynamic-data/checks/draft-on-the-server.json +27 -0
- package/packs/dynamic-data/checks/guard-does-its-job.json +28 -0
- package/packs/dynamic-data/checks/key-is-not-a-display-name.json +28 -0
- package/packs/dynamic-data/checks/no-code-from-data.json +29 -0
- package/packs/dynamic-data/checks/no-collection-from-request.json +27 -0
- package/packs/dynamic-data/checks/no-pipeline-from-stored-text.json +27 -0
- package/packs/dynamic-data/checks/no-regexp-from-stored-config.json +27 -0
- package/packs/dynamic-data/checks/no-template-from-stored-text.json +27 -0
- package/packs/dynamic-data/checks/save-takes-named-paths.json +27 -0
- package/packs/dynamic-data/pack.json +23 -0
- package/packs/dynamic-data/rules/a-list-is-a-saved-query.md +23 -0
- package/packs/dynamic-data/rules/definitions-are-validated-data.md +24 -0
- package/packs/dynamic-data/rules/every-dynamic-surface-has-a-boundary.md +29 -0
- package/packs/dynamic-data/rules/fields-have-owners.md +22 -0
- package/packs/dynamic-data/rules/imports-drop-nothing.md +26 -0
- package/packs/dynamic-data/rules/one-definition-many-surfaces.md +24 -0
- package/packs/dynamic-data/rules/one-fact-one-place.md +28 -0
- package/packs/dynamic-data/rules/rules-live-in-the-engine.md +29 -0
- package/packs/dynamic-data/rules/saves-are-patches.md +28 -0
- package/packs/dynamic-data/rules/tenant-costs-one-document.md +22 -0
- package/packs/dynamic-data/skills/design-a-definition/SKILL.md +24 -0
- package/packs/fastify/checks/api-routes-versioned.json +28 -0
- package/packs/fastify/checks/cookie-host-prefix.json +24 -0
- package/packs/fastify/checks/hijack-needs-try.json +28 -0
- package/packs/fastify/checks/no-cast-at-route-boundary.json +24 -0
- package/packs/fastify/checks/no-db-in-route-file.json +28 -0
- package/packs/fastify/checks/no-err-message-to-client.json +24 -0
- package/packs/fastify/checks/no-regexp-from-input.json +24 -0
- package/packs/fastify/checks/no-trust-proxy-true.json +25 -0
- package/packs/fastify/checks/one-error-body.json +26 -0
- package/packs/fastify/checks/origin-from-config-not-host.json +31 -0
- package/packs/fastify/checks/password-needs-slow-hash.json +30 -0
- package/packs/fastify/checks/sec-headers-present.json +28 -0
- package/packs/fastify/checks/secrets-compared-constant-time.json +24 -0
- package/packs/fastify/checks/sse-needs-error-listener.json +28 -0
- package/packs/fastify/checks/user-bytes-need-disposition.json +29 -0
- package/packs/fastify/pack.json +41 -0
- package/packs/fastify/rules/credentials-and-input.md +23 -0
- package/packs/fastify/rules/security-one-switch.md +28 -0
- package/packs/fastify/rules/streams-and-hijacks.md +22 -0
- package/packs/fastify/rules/tests-use-inject.md +13 -0
- package/packs/fastify/rules/three-layers.md +20 -0
- package/packs/fastify/rules/trust-config-not-headers.md +24 -0
- package/packs/full-stack/checks/no-express-no-vite.json +23 -0
- package/packs/full-stack/hooks/stack-guard.mjs +90 -0
- package/packs/full-stack/pack.json +76 -0
- package/packs/full-stack/rules/stack-is-fastify-and-nextjs.md +20 -0
- package/packs/go/checks/go-exec-result-ignored.json +15 -0
- package/packs/go/checks/go-unmarshal-error-ignored.json +15 -0
- package/packs/go/pack.json +21 -0
- package/packs/go/rules/go-errors-are-handled.md +22 -0
- package/packs/gpu-painter/checks/dawn-backends-and-validation.json +9 -0
- package/packs/gpu-painter/checks/dawn-no-spontaneous.json +10 -0
- package/packs/gpu-painter/checks/dawn-present-on-render-thread.json +17 -0
- package/packs/gpu-painter/checks/gpu-no-frame-stalls.json +12 -0
- package/packs/gpu-painter/checks/no-opengl-link.json +11 -0
- package/packs/gpu-painter/checks/no-opengl.json +13 -0
- package/packs/gpu-painter/hooks/browser-guard.py +196 -0
- package/packs/gpu-painter/pack.json +91 -0
- package/packs/gpu-painter/rules/dawn-build-and-shaders.md +27 -0
- package/packs/gpu-painter/rules/dawn-device-and-render-thread.md +26 -0
- package/packs/gpu-painter/rules/gpu-antialiasing.md +20 -0
- package/packs/gpu-painter/rules/gpu-blend-order.md +22 -0
- package/packs/gpu-painter/rules/gpu-bloom.md +20 -0
- package/packs/gpu-painter/rules/gpu-frame-budget.md +25 -0
- package/packs/gpu-painter/rules/gpu-frame-pacing-and-power.md +27 -0
- package/packs/gpu-painter/rules/gpu-frame-work.md +30 -0
- package/packs/gpu-painter/rules/gpu-golden-images.md +21 -0
- package/packs/gpu-painter/rules/gpu-hdr-and-tonemap.md +23 -0
- package/packs/gpu-painter/rules/gpu-picking-on-cpu.md +19 -0
- package/packs/gpu-painter/rules/gpu-render-lifecycle.md +25 -0
- package/packs/gpu-painter/rules/gpu-text-atlas.md +24 -0
- package/packs/gpu-painter/rules/gpu-travellers-from-snapshot.md +24 -0
- package/packs/gpu-painter/rules/hover-every-pressable.md +25 -0
- package/packs/gpu-painter/skills/gpu-capture/SKILL.md +15 -0
- package/packs/gpu-painter/skills/gpu-frame-bench/SKILL.md +14 -0
- package/packs/gpu-painter/skills/gpu-golden-check/SKILL.md +13 -0
- package/packs/gpu-painter/skills/gpu-golden-update/SKILL.md +16 -0
- package/packs/gpu-painter/skills/gpu-lifecycle-stress/SKILL.md +15 -0
- package/packs/gpu-painter/skills/hover-state/SKILL.md +33 -0
- package/packs/hook-proof/checks/hook-catch-must-not-allow.json +16 -0
- package/packs/hook-proof/checks/hook-command-no-pipe.json +16 -0
- package/packs/hook-proof/checks/hook-exit-1-does-not-block.json +17 -0
- package/packs/hook-proof/checks/hook-no-or-true.json +18 -0
- package/packs/hook-proof/checks/hook-refusal-on-stderr.json +17 -0
- package/packs/hook-proof/pack.json +38 -0
- package/packs/hook-proof/rules/a-refusal-is-exit-2.md +28 -0
- package/packs/hook-proof/skills/prove-a-hook/SKILL.md +21 -0
- package/packs/host-agent/checks/file-path-from-param.json +27 -0
- package/packs/host-agent/checks/integrity-check-optional.json +27 -0
- package/packs/host-agent/checks/quoted-value-into-config.json +24 -0
- package/packs/host-agent/checks/secret-on-argv.json +28 -0
- package/packs/host-agent/checks/ssh-verifies-host-key.json +27 -0
- package/packs/host-agent/checks/system-file-written-in-place.json +26 -0
- package/packs/host-agent/checks/tmp-path-predictable.json +27 -0
- package/packs/host-agent/pack.json +22 -0
- package/packs/host-agent/rules/root-on-other-machines.md +27 -0
- package/packs/html-ui/checks/email-html-escapes.json +25 -0
- package/packs/html-ui/checks/external-link-rel.json +21 -0
- package/packs/html-ui/checks/no-duplicate-escape.json +26 -0
- package/packs/html-ui/checks/no-inline-client-script.json +21 -0
- package/packs/html-ui/checks/no-inline-style.json +23 -0
- package/packs/html-ui/pack.json +33 -0
- package/packs/html-ui/rules/email-html-escapes.md +19 -0
- package/packs/html-ui/rules/server-html-escapes-everything.md +22 -0
- package/packs/http-api/checks/api-path-versioned.json +31 -0
- package/packs/http-api/checks/authz-from-request-body.json +26 -0
- package/packs/http-api/checks/client-calls-a-version.json +28 -0
- package/packs/http-api/checks/client-ip-not-leftmost-xff.json +28 -0
- package/packs/http-api/checks/cors-needs-origin.json +26 -0
- package/packs/http-api/checks/csp-not-switched-off.json +28 -0
- package/packs/http-api/checks/debug-route-not-shipped.json +26 -0
- package/packs/http-api/checks/encrypted-name-plaintext-value.json +26 -0
- package/packs/http-api/checks/err-text-stays-server-side.json +27 -0
- package/packs/http-api/checks/failure-is-not-empty.json +28 -0
- package/packs/http-api/checks/gate-is-not-a-todo.json +27 -0
- package/packs/http-api/checks/guard-fails-open.json +28 -0
- package/packs/http-api/checks/hmac-over-reserialised-body.json +26 -0
- package/packs/http-api/checks/jwt-verify-pins-algorithm.json +26 -0
- package/packs/http-api/checks/middleware-skips-without-user.json +26 -0
- package/packs/http-api/checks/next-api-route-versioned.json +47 -0
- package/packs/http-api/checks/no-security-off-switch.json +28 -0
- package/packs/http-api/checks/oauth-state-signed.json +26 -0
- package/packs/http-api/checks/one-time-code-compared-raw.json +26 -0
- package/packs/http-api/checks/query-cast-is-not-validation.json +28 -0
- package/packs/http-api/checks/queue-action-from-client.json +26 -0
- package/packs/http-api/checks/rate-limit-one-module.json +28 -0
- package/packs/http-api/checks/role-checked-in-one-guard.json +27 -0
- package/packs/http-api/checks/route-body-needs-schema.json +31 -0
- package/packs/http-api/checks/route-scopes-to-the-caller.json +31 -0
- package/packs/http-api/checks/secret-compared-constant-time-any.json +28 -0
- package/packs/http-api/checks/secret-in-job-record.json +27 -0
- package/packs/http-api/checks/server-fetches-tenant-url.json +30 -0
- package/packs/http-api/checks/short-code-needs-limit.json +26 -0
- package/packs/http-api/checks/user-url-through-safe-fetch.json +28 -0
- package/packs/http-api/checks/webhook-dedupes-event-id.json +30 -0
- package/packs/http-api/pack.json +45 -0
- package/packs/http-api/rules/adapters-one-db-gateway.md +18 -0
- package/packs/http-api/rules/apis-are-versioned.md +27 -0
- package/packs/http-api/rules/credentials-handled-safely.md +22 -0
- package/packs/http-api/rules/handlers-own-the-work.md +17 -0
- package/packs/http-api/rules/health-and-correlation.md +17 -0
- package/packs/http-api/rules/one-error-contract.md +17 -0
- package/packs/http-api/rules/routes-are-thin.md +17 -0
- package/packs/http-api/rules/routes-scope-to-the-caller.md +26 -0
- package/packs/http-api/rules/routes-validate-and-answer.md +25 -0
- package/packs/http-api/skills/change-an-api/SKILL.md +20 -0
- package/packs/http-api/skills/secure-a-route/SKILL.md +21 -0
- package/packs/instrument-shell/checks/no-raw-struct-blob.json +23 -0
- package/packs/instrument-shell/pack.json +22 -0
- package/packs/instrument-shell/rules/fold-keeps-every-route.md +24 -0
- package/packs/instrument-shell/rules/quiet-announces-itself.md +22 -0
- package/packs/instrument-shell/rules/render-the-extremes.md +25 -0
- package/packs/instrument-shell/rules/seams-are-declared.md +27 -0
- package/packs/instrument-shell/rules/session-blob-append-only.md +25 -0
- package/packs/instrument-shell/skills/gates-from-commit-one/SKILL.md +20 -0
- package/packs/instrument-shell/skills/hit-the-painted-control/SKILL.md +18 -0
- package/packs/js-ts/checks/async-callback-unguarded.json +26 -0
- package/packs/js-ts/checks/background-loop-needs-stop.json +28 -0
- package/packs/js-ts/checks/child-process-error-handler.json +28 -0
- package/packs/js-ts/checks/entry-registers-shutdown.json +31 -0
- package/packs/js-ts/checks/fetch-needs-timeout.json +30 -0
- package/packs/js-ts/checks/job-only-in-memory.json +28 -0
- package/packs/js-ts/checks/json-only-through-helper.json +29 -0
- package/packs/js-ts/checks/line-under-200-chars.json +34 -0
- package/packs/js-ts/checks/localhost-default-in-server.json +29 -0
- package/packs/js-ts/checks/no-any.json +27 -0
- package/packs/js-ts/checks/no-cast-escape-hatches.json +24 -0
- package/packs/js-ts/checks/no-console-in-app.json +31 -0
- package/packs/js-ts/checks/no-dynamic-import-node-builtin.json +25 -0
- package/packs/js-ts/checks/no-inline-binary.json +20 -0
- package/packs/js-ts/checks/no-inline-zod-outside-schemas.json +27 -0
- package/packs/js-ts/checks/no-local-sleep.json +25 -0
- package/packs/js-ts/checks/no-nested-ternary.json +20 -0
- package/packs/js-ts/checks/no-secret-in-log.json +24 -0
- package/packs/js-ts/checks/no-shell-string-interpolation.json +24 -0
- package/packs/js-ts/checks/no-swallowed-errors.json +25 -0
- package/packs/js-ts/checks/no-ts-enum.json +20 -0
- package/packs/js-ts/checks/process-env-only-in-config.json +31 -0
- package/packs/js-ts/checks/race-leaves-work-running.json +23 -0
- package/packs/js-ts/checks/reject-as-catch-all-exists.json +24 -0
- package/packs/js-ts/checks/secret-has-no-fallback.json +24 -0
- package/packs/js-ts/checks/secret-needs-crypto-random.json +24 -0
- package/packs/js-ts/checks/server-close-awaited.json +27 -0
- package/packs/js-ts/checks/session-user-typed-once.json +24 -0
- package/packs/js-ts/checks/switch-is-a-table.json +24 -0
- package/packs/js-ts/checks/test-imports-the-source.json +19 -0
- package/packs/js-ts/checks/test-name-says-behaviour.json +16 -0
- package/packs/js-ts/checks/tsconfig-flags.json +21 -0
- package/packs/js-ts/checks/void-needs-catch.json +24 -0
- package/packs/js-ts/checks/zod-string-needs-max.json +25 -0
- package/packs/js-ts/checks/zod-v4-formats.json +23 -0
- package/packs/js-ts/pack.json +84 -0
- package/packs/js-ts/rules/await-handled-at-a-boundary.md +66 -0
- package/packs/js-ts/rules/env-through-config.md +36 -0
- package/packs/js-ts/rules/errors-never-swallowed.md +33 -0
- package/packs/js-ts/rules/files-under-300-lines.md +18 -0
- package/packs/js-ts/rules/guard-clauses-over-nesting.md +34 -0
- package/packs/js-ts/rules/js-repeats-become-templates.md +26 -0
- package/packs/js-ts/rules/json-only-through-one-helper.md +62 -0
- package/packs/js-ts/rules/named-constants-not-magic-numbers.md +34 -0
- package/packs/js-ts/rules/no-any.md +34 -0
- package/packs/js-ts/rules/no-console.md +30 -0
- package/packs/js-ts/rules/no-floating-promises.md +31 -0
- package/packs/js-ts/rules/no-param-mutation.md +28 -0
- package/packs/js-ts/rules/no-unsafe-assertions.md +30 -0
- package/packs/js-ts/rules/one-logger.md +24 -0
- package/packs/js-ts/rules/outbound-calls-have-a-limit.md +20 -0
- package/packs/js-ts/rules/parallel-independent-awaits.md +29 -0
- package/packs/js-ts/rules/process-lifecycle.md +47 -0
- package/packs/js-ts/rules/schema-single-source-of-truth.md +36 -0
- package/packs/js-ts/rules/secrets-stay-secret.md +22 -0
- package/packs/js-ts/rules/small-functions.md +20 -0
- package/packs/js-ts/rules/test-names-describe-behavior.md +28 -0
- package/packs/js-ts/rules/tests-can-fail.md +19 -0
- package/packs/js-ts/rules/tsconfig-strict.md +32 -0
- package/packs/js-ts/rules/typed-exports.md +27 -0
- package/packs/js-ts/rules/types-once-at-the-edge.md +18 -0
- package/packs/js-ts/rules/unions-over-enums.md +29 -0
- package/packs/js-ts/rules/zod-at-route-boundaries.md +40 -0
- package/packs/midi/checks/active-sensing-no-pulse.json +24 -0
- package/packs/midi/checks/clock-not-at-sample-zero.json +23 -0
- package/packs/midi/checks/midi-realtime-first.json +27 -0
- package/packs/midi/checks/no-tempo-writeback.json +23 -0
- package/packs/midi/checks/position-not-integrated.json +25 -0
- package/packs/midi/checks/stub-playhead-advances.json +27 -0
- package/packs/midi/checks/sysex-length-literal.json +23 -0
- package/packs/midi/checks/sysex-not-on-audio.json +24 -0
- package/packs/midi/checks/sysex-survives-realtime.json +27 -0
- package/packs/midi/pack.json +22 -0
- package/packs/midi/rules/clock-is-a-pulse-stream.md +30 -0
- package/packs/midi/rules/imports-count-every-loss.md +25 -0
- package/packs/midi/rules/measure-the-clock-in-audio.md +30 -0
- package/packs/midi/rules/sync-is-a-mode.md +29 -0
- package/packs/midi/rules/sysex-is-a-record.md +29 -0
- package/packs/midi/skills/clock-probe/SKILL.md +23 -0
- package/packs/midi/skills/clock-probe/clock-law.md +417 -0
- package/packs/midi/skills/does-it-sync/SKILL.md +19 -0
- package/packs/mongo/checks/bulk-over-many.json +28 -0
- package/packs/mongo/checks/created-at-immutable.json +25 -0
- package/packs/mongo/checks/driver-outside-gateway.json +24 -0
- package/packs/mongo/checks/key-lookup-is-hashed.json +24 -0
- package/packs/mongo/checks/mongoclient-cached-and-named.json +34 -0
- package/packs/mongo/checks/mongoclient-outside-gateway.json +24 -0
- package/packs/mongo/checks/no-count-plus-one-ids.json +25 -0
- package/packs/mongo/checks/no-db-write-in-loop.json +25 -0
- package/packs/mongo/checks/no-drop-in-code.json +27 -0
- package/packs/mongo/checks/no-empty-deletemany.json +26 -0
- package/packs/mongo/checks/no-find-reads.json +26 -0
- package/packs/mongo/checks/no-input-spread-into-write.json +24 -0
- package/packs/mongo/checks/no-mongoose.json +23 -0
- package/packs/mongo/checks/no-negation-delete.json +26 -0
- package/packs/mongo/checks/no-non-null-after-findone.json +25 -0
- package/packs/mongo/checks/objectid-equals.json +26 -0
- package/packs/mongo/checks/update-then-reread.json +24 -0
- package/packs/mongo/checks/watch-not-poll.json +25 -0
- package/packs/mongo/pack.json +52 -0
- package/packs/mongo/rules/deletes-name-what-dies.md +21 -0
- package/packs/mongo/rules/driver-only-in-gateway.md +26 -0
- package/packs/mongo/rules/keys-hashed-input-picked.md +21 -0
- package/packs/mongo/rules/one-client-one-pool.md +32 -0
- package/packs/mongo/rules/reads-aggregate-writes-bulk.md +25 -0
- package/packs/mongo/rules/writes-are-atomic.md +27 -0
- package/packs/multi-tenant/checks/role-sees-all-tenants.json +26 -0
- package/packs/multi-tenant/checks/tenant-filter-from-url.json +25 -0
- package/packs/multi-tenant/pack.json +22 -0
- package/packs/multi-tenant/rules/no-role-crosses-a-tenant.md +21 -0
- package/packs/multi-tenant/skills/prove-tenant-isolation/SKILL.md +19 -0
- package/packs/nginx/checks/modsec-no-blanket-off.json +16 -0
- package/packs/nginx/checks/nginx-csp-no-unsafe.json +15 -0
- package/packs/nginx/checks/nginx-headers-always.json +15 -0
- package/packs/nginx/checks/nginx-keepalive-http11.json +20 -0
- package/packs/nginx/checks/nginx-logs-to-stdout.json +15 -0
- package/packs/nginx/checks/nginx-no-crlf.json +17 -0
- package/packs/nginx/checks/nginx-resolver-ttl.json +19 -0
- package/packs/nginx/pack.json +43 -0
- package/packs/nginx/rules/modsecurity-tuning.md +22 -0
- package/packs/nginx/rules/nginx-production.md +26 -0
- package/packs/nginx/skills/waf-rollout/SKILL.md +20 -0
- package/packs/python/checks/py-no-bare-except.json +18 -0
- package/packs/python/checks/py-no-eval.json +18 -0
- package/packs/python/checks/py-no-mutable-default.json +18 -0
- package/packs/python/checks/py-no-shell-true.json +18 -0
- package/packs/python/checks/py-no-star-import.json +18 -0
- package/packs/python/checks/py-no-swallowed-exception.json +18 -0
- package/packs/python/checks/py-none-by-is.json +18 -0
- package/packs/python/checks/py-open-with-with.json +18 -0
- package/packs/python/checks/py-requests-timeout.json +18 -0
- package/packs/python/checks/py-subprocess-output-checked.json +18 -0
- package/packs/python/checks/py-yaml-safe-load.json +18 -0
- package/packs/python/pack.json +26 -0
- package/packs/python/rules/python-errors-and-resources.md +26 -0
- package/packs/python/rules/python-repeats-become-templates.md +27 -0
- package/packs/python/skills/python-extract-template/SKILL.md +35 -0
- package/packs/react/checks/dialog-needs-role.json +28 -0
- package/packs/react/checks/effect-not-for-derived-state.json +24 -0
- package/packs/react/checks/icon-button-needs-name.json +24 -0
- package/packs/react/checks/label-needs-a-field.json +24 -0
- package/packs/react/checks/ui-failed-fetch-says-so.json +23 -0
- package/packs/react/checks/ui-ok-branch-has-an-else.json +23 -0
- package/packs/react/pack.json +20 -0
- package/packs/react/rules/react-accessible-controls.md +20 -0
- package/packs/react/rules/react-state-and-effects.md +23 -0
- package/packs/reverse-engineering/checks/ear-constant-tagged.json +23 -0
- package/packs/reverse-engineering/checks/no-default-on-distrust.json +24 -0
- package/packs/reverse-engineering/checks/no-rom-table-literal.json +29 -0
- package/packs/reverse-engineering/checks/prose-never-decides.json +22 -0
- package/packs/reverse-engineering/hooks/firmware-out-of-git.mjs +49 -0
- package/packs/reverse-engineering/pack.json +35 -0
- package/packs/reverse-engineering/rules/a-negative-names-what-was-scanned.md +19 -0
- package/packs/reverse-engineering/rules/account-every-term.md +20 -0
- package/packs/reverse-engineering/rules/addresses-name-their-space.md +21 -0
- package/packs/reverse-engineering/rules/every-law-has-a-source.md +18 -0
- package/packs/reverse-engineering/rules/findings-are-withdrawn-in-place.md +17 -0
- package/packs/reverse-engineering/rules/firmware-stays-out.md +18 -0
- package/packs/reverse-engineering/rules/names-before-code.md +19 -0
- package/packs/reverse-engineering/rules/reproduction-or-original.md +18 -0
- package/packs/reverse-engineering/rules/two-sources-before-naming.md +21 -0
- package/packs/reverse-engineering/skills/bank-census/SKILL.md +18 -0
- package/packs/reverse-engineering/skills/gather-sources/SKILL.md +21 -0
- package/packs/reverse-engineering/skills/menu-map/SKILL.md +28 -0
- package/packs/reverse-engineering/skills/names-first/SKILL.md +41 -0
- package/packs/reverse-engineering/skills/re-deliverables/SKILL.md +21 -0
- package/packs/reverse-engineering/skills/tripwire-the-law/SKILL.md +19 -0
- package/packs/reverse-engineering/tools/lib/unwrap.mjs +115 -0
- package/packs/reverse-engineering/tools/names-sweep.mjs +264 -0
- package/packs/shell/checks/sh-mktemp.json +18 -0
- package/packs/shell/checks/sh-no-backticks.json +18 -0
- package/packs/shell/checks/sh-no-eval.json +18 -0
- package/packs/shell/checks/sh-no-parse-ls.json +18 -0
- package/packs/shell/checks/sh-no-pipe-to-shell.json +18 -0
- package/packs/shell/checks/sh-no-program-in-heredoc.json +18 -0
- package/packs/shell/checks/sh-quote-arguments.json +18 -0
- package/packs/shell/checks/sh-rm-guarded.json +18 -0
- package/packs/shell/checks/sh-strict-mode.json +20 -0
- package/packs/shell/pack.json +28 -0
- package/packs/shell/rules/shell-repeats-become-templates.md +23 -0
- package/packs/shell/rules/shell-strict-and-quoted.md +27 -0
- package/packs/shell/skills/shell-extract-template/SKILL.md +32 -0
- package/packs/shell-hooks/checks/gnu-only-in-shell.json +18 -0
- package/packs/shell-hooks/checks/hook-command-anchored.json +12 -0
- package/packs/shell-hooks/checks/hook-json-by-tool.json +18 -0
- package/packs/shell-hooks/checks/no-bash-c-with-variable.json +20 -0
- package/packs/shell-hooks/pack.json +36 -0
- package/packs/shell-hooks/rules/hooks-portable-anchored-quoted.md +26 -0
- package/packs/sound-design/checks/audio-seed-fixed.json +23 -0
- package/packs/sound-design/checks/mix-is-a-crossfade.json +30 -0
- package/packs/sound-design/checks/no-hash-in-the-signal.json +30 -0
- package/packs/sound-design/checks/no-literal-key-anchor.json +30 -0
- package/packs/sound-design/checks/test-compares-a-function-with-itself.json +22 -0
- package/packs/sound-design/pack.json +22 -0
- package/packs/sound-design/rules/algorithms-are-data.md +26 -0
- package/packs/sound-design/rules/meter-before-engine.md +27 -0
- package/packs/sound-design/rules/neutral-is-exact.md +26 -0
- package/packs/sound-design/rules/test-off-the-easy-point.md +28 -0
- package/packs/sound-design/rules/unreachable-is-said.md +25 -0
- package/packs/sound-design/rules/waveforms-and-octaves.md +24 -0
- package/packs/sound-design/skills/choose-observable/SKILL.md +18 -0
- package/packs/sound-design/skills/measure-a-law/SKILL.md +18 -0
- package/packs/sound-design/skills/measure-reverb/SKILL.md +19 -0
- package/packs/sound-design/skills/spread-probe/SKILL.md +18 -0
- package/packs/stellix/checks/no-keyboard-focus.json +12 -0
- package/packs/stellix/checks/one-seeded-random.json +16 -0
- package/packs/stellix/checks/world-never-includes-painter.json +12 -0
- package/packs/stellix/checks/world-no-libm.json +15 -0
- package/packs/stellix/checks/world-no-unordered.json +10 -0
- package/packs/stellix/hooks/no-shipped-images.mjs +88 -0
- package/packs/stellix/pack.json +61 -0
- package/packs/stellix/rules/stellix-deterministic-world.md +29 -0
- package/packs/stellix/rules/stellix-reset-generated.md +24 -0
- package/packs/stellix/rules/stellix-world-snapshot.md +27 -0
- package/packs/stellix/rules/world-continuous-collision.md +26 -0
- package/packs/stellix/rules/world-fp-determinism.md +28 -0
- package/packs/stored-text/checks/no-raw-html-from-data.json +31 -0
- package/packs/stored-text/checks/stored-text-libs-only-in-renderer.json +31 -0
- package/packs/stored-text/checks/stored-text-renderer-complete.json +24 -0
- package/packs/stored-text/checks/stored-text-through-renderer.json +25 -0
- package/packs/stored-text/pack.json +41 -0
- package/packs/stored-text/rules/stored-text-renders-once.md +26 -0
- package/packs/stored-text/skills/render-stored-text/SKILL.md +24 -0
- package/packs/synth-creator/checks/timing-from-synthcore.json +24 -0
- package/packs/synth-creator/checks/ui-drawn-in-code.json +23 -0
- package/packs/synth-creator/hooks/build-guard.mjs +232 -0
- package/packs/synth-creator/hooks/build-sweep.mjs +166 -0
- package/packs/synth-creator/hooks/frozen-files.mjs +63 -0
- package/packs/synth-creator/hooks/idle-wake.mjs +90 -0
- package/packs/synth-creator/hooks/protect-vendor.mjs +174 -0
- package/packs/synth-creator/hooks/spec-lock.mjs +117 -0
- package/packs/synth-creator/hooks/stage-guard.mjs +50 -0
- package/packs/synth-creator/hooks/time-guard.mjs +292 -0
- package/packs/synth-creator/hooks/user-asks.mjs +165 -0
- package/packs/synth-creator/pack.json +136 -0
- package/packs/synth-creator/rules/copy-trees-read-only.md +25 -0
- package/packs/synth-creator/rules/debug-by-measurement.md +20 -0
- package/packs/synth-creator/rules/done-means-fresh-evidence.md +18 -0
- package/packs/synth-creator/rules/hooks-and-gates.md +21 -0
- package/packs/synth-creator/rules/long-work-in-the-background.md +16 -0
- package/packs/synth-creator/rules/timing-from-synthcore.md +16 -0
- package/packs/synth-creator/skills/build-step/SKILL.md +125 -0
- package/packs/synth-creator/skills/build-through-the-queue/SKILL.md +19 -0
- package/packs/synth-creator/tools/freeze.mjs +36 -0
- package/packs/tui/checks/tui-entry-restores-terminal.json +27 -0
- package/packs/tui/checks/tui-gates-on-tty.json +25 -0
- package/packs/tui/checks/tui-no-ris-reset.json +22 -0
- package/packs/tui/checks/tui-resize-listener-removed.json +26 -0
- package/packs/tui/checks/tui-width-is-state.json +33 -0
- package/packs/tui/pack.json +44 -0
- package/packs/tui/rules/tui-resize-and-screen.md +25 -0
- package/packs/tui/skills/build-tui/SKILL.md +21 -0
- package/packs/ui-craft/checks/ui-input-needs-autocomplete.json +23 -0
- package/packs/ui-craft/checks/ui-intl-for-dates.json +21 -0
- package/packs/ui-craft/checks/ui-no-ai-palette.json +22 -0
- package/packs/ui-craft/checks/ui-no-animate-layout.json +22 -0
- package/packs/ui-craft/checks/ui-no-assistant-palette.json +23 -0
- package/packs/ui-craft/checks/ui-no-bounce-easing.json +22 -0
- package/packs/ui-craft/checks/ui-no-custom-cursor.json +22 -0
- package/packs/ui-craft/checks/ui-no-emoji-as-ui.json +22 -0
- package/packs/ui-craft/checks/ui-no-empty-image.json +21 -0
- package/packs/ui-craft/checks/ui-no-eyebrow.json +21 -0
- package/packs/ui-craft/checks/ui-no-furniture-text.json +22 -0
- package/packs/ui-craft/checks/ui-no-gradient-text.json +22 -0
- package/packs/ui-craft/checks/ui-no-index-numbering.json +21 -0
- package/packs/ui-craft/checks/ui-no-lorem.json +22 -0
- package/packs/ui-craft/checks/ui-no-marketing-buzzword.json +22 -0
- package/packs/ui-craft/checks/ui-no-paste-block.json +21 -0
- package/packs/ui-craft/checks/ui-no-placeholder-names.json +21 -0
- package/packs/ui-craft/checks/ui-no-scroll-hijack.json +21 -0
- package/packs/ui-craft/checks/ui-no-side-tab.json +21 -0
- package/packs/ui-craft/checks/ui-no-stock-labels.json +22 -0
- package/packs/ui-craft/checks/ui-no-transition-all.json +22 -0
- package/packs/ui-craft/checks/ui-no-uppercase-table-header.json +21 -0
- package/packs/ui-craft/checks/ui-no-zebra-rows.json +22 -0
- package/packs/ui-craft/checks/ui-no-zoom-lock.json +21 -0
- package/packs/ui-craft/pack.json +43 -0
- package/packs/ui-craft/rules/apps-are-not-pages.md +26 -0
- package/packs/ui-craft/rules/data-tells-the-truth.md +26 -0
- package/packs/ui-craft/rules/dates-and-numbers.md +18 -0
- package/packs/ui-craft/rules/forms-never-fight.md +22 -0
- package/packs/ui-craft/rules/interface-copy.md +26 -0
- package/packs/ui-craft/rules/motion-is-rare.md +31 -0
- package/packs/ui-craft/rules/no-generated-look.md +37 -0
- package/packs/ui-craft/rules/page-holds-its-frame.md +28 -0
- package/packs/ui-craft/rules/page-proved-rendered.md +19 -0
- package/packs/ui-craft/rules/pictures-do-a-job.md +25 -0
- package/packs/ui-craft/rules/pointer-feedback.md +28 -0
- package/packs/ui-craft/rules/screen-answers-one-question.md +24 -0
- package/packs/ui-craft/rules/survives-real-data.md +23 -0
- package/packs/ui-craft/rules/touch-and-small-screens.md +19 -0
- package/packs/ui-craft/rules/type-space-colour.md +26 -0
- package/packs/ui-craft/rules/url-holds-the-state.md +19 -0
- package/packs/ui-craft/skills/app-screen/SKILL.md +33 -0
- package/packs/ui-craft/skills/build-from-reference/SKILL.md +28 -0
- package/packs/ui-craft/skills/chart-choice/SKILL.md +34 -0
- package/packs/ui-craft/skills/hover-state/SKILL.md +28 -0
- package/packs/ui-craft/skills/page-critique/SKILL.md +33 -0
- package/packs/ui-craft/skills/prove-page/SKILL.md +23 -0
- package/packs/ui-craft/skills/redesign-page/SKILL.md +19 -0
- package/packs/ui-craft/skills/ui-code-review/SKILL.md +22 -0
- package/packs/ui-craft/skills/write-the-page/SKILL.md +21 -0
- package/packs/ui-craft/tools/page-measure.mjs +41 -0
- package/packs/web/checks/a11y-img-needs-alt.json +26 -0
- package/packs/web/checks/a11y-no-bare-outline-none.json +21 -0
- package/packs/web/checks/a11y-no-clickable-div.json +26 -0
- package/packs/web/checks/a11y-no-positive-tabindex.json +26 -0
- package/packs/web/checks/client-no-api-key.json +29 -0
- package/packs/web/checks/client-no-browser-global-at-module-scope.json +28 -0
- package/packs/web/checks/client-no-server-import.json +31 -0
- package/packs/web/checks/client-no-window-location-nav.json +28 -0
- package/packs/web/checks/css-no-inline-style.json +30 -0
- package/packs/web/checks/ds-pages-compose-primitives.json +28 -0
- package/packs/web/checks/key-from-index.json +23 -0
- package/packs/web/checks/prodbuild-no-cdn-runtime-deps.json +21 -0
- package/packs/web/checks/prodbuild-no-dev-flags.json +21 -0
- package/packs/web/checks/prodbuild-no-remote-fonts.json +22 -0
- package/packs/web/checks/rcss-button-cursor.json +30 -0
- package/packs/web/checks/rcss-no-100vw.json +21 -0
- package/packs/web/checks/rcss-no-fixed-wide-width.json +21 -0
- package/packs/web/checks/rcss-viewport-meta.json +28 -0
- package/packs/web/checks/secret-not-in-web-storage.json +25 -0
- package/packs/web/checks/tw-no-raw-hex-in-markup.json +34 -0
- package/packs/web/checks/wp-hero-not-lazy.json +27 -0
- package/packs/web/checks/wp-img-needs-dimensions.json +26 -0
- package/packs/web/pack.json +73 -0
- package/packs/web/rules/accessibility.md +26 -0
- package/packs/web/rules/client-boundaries.md +23 -0
- package/packs/web/rules/production-build.md +22 -0
- package/packs/web/rules/secrets-not-in-the-browser.md +18 -0
- package/packs/web/rules/styles-live-in-stylesheets.md +23 -0
- package/packs/web/rules/tailwind-v4.md +23 -0
- package/packs/web/rules/ui-primitives-one-module.md +30 -0
- package/packs/web/rules/web-performance.md +21 -0
- package/packs/web/skills/design-direction/SKILL.md +30 -0
- package/packs/web/skills/design-review/SKILL.md +28 -0
- package/packs/web/skills/pick-rendering-mode/SKILL.md +21 -0
- package/packs/web/skills/preflight-build/SKILL.md +23 -0
- package/packs/web/skills/seo-audit/SKILL.md +22 -0
- package/packs/websocket/checks/no-token-in-query.json +26 -0
- package/packs/websocket/checks/stale-socket-deletes-new-entry.json +25 -0
- package/packs/websocket/checks/ws-identity-from-message.json +26 -0
- package/packs/websocket/pack.json +21 -0
- package/packs/websocket/rules/identity-comes-from-the-connection.md +24 -0
- package/packs/writing/checks/ascii-no-em-dash.json +17 -0
- package/packs/writing/checks/no-copula-dodging.json +18 -0
- package/packs/writing/checks/no-dramatic-colon.json +18 -0
- package/packs/writing/checks/no-not-x-but-y.json +18 -0
- package/packs/writing/checks/no-participial-opener.json +18 -0
- package/packs/writing/checks/no-pseudo-wisdom.json +18 -0
- package/packs/writing/checks/no-slop-words.json +18 -0
- package/packs/writing/checks/no-stock-connectors.json +18 -0
- package/packs/writing/checks/no-time-or-date.json +27 -0
- package/packs/writing/checks/no-trailing-ing.json +18 -0
- package/packs/writing/checks/no-vague-attribution.json +18 -0
- package/packs/writing/pack.json +43 -0
- package/packs/writing/rules/ascii-no-em-dash.md +21 -0
- package/packs/writing/rules/cut-the-padding.md +20 -0
- package/packs/writing/rules/direct-voice.md +22 -0
- package/packs/writing/rules/no-cargo-cult.md +16 -0
- package/packs/writing/rules/no-scaffolding.md +19 -0
- package/packs/writing/rules/no-stock-language.md +27 -0
- package/packs/writing/rules/no-time-or-date.md +18 -0
- package/packs/writing/rules/rhythm-like-talk.md +20 -0
- package/packs/writing/rules/write-from-your-own-corner.md +20 -0
- package/packs/writing/skills/argument-post/SKILL.md +28 -0
- package/packs/writing/skills/humanize/SKILL.md +28 -0
- package/packs/writing/skills/launch-post/SKILL.md +38 -0
- package/packs/writing/skills/pick-subreddit/SKILL.md +69 -0
- package/packs/writing/skills/post-voice/SKILL.md +22 -0
- package/packs/writing/skills/reddit-post/SKILL.md +63 -0
- package/packs/writing/skills/reddit-title/SKILL.md +23 -0
- package/packs/writing/skills/reply-moves/SKILL.md +40 -0
- package/packs/writing/skills/reply-stance/SKILL.md +23 -0
- package/packs/writing/skills/should-i-reply/SKILL.md +21 -0
- package/packs/zod/hooks/schemas-only.mjs +53 -0
- package/packs/zod/pack.json +44 -0
- package/packs/zod/rules/env-shapes-only.md +14 -0
- package/packs/zod/rules/zod-4-schemas.md +14 -0
- package/profiles/stellix.layout.json +315 -0
- package/profiles/sub37.layout.json +247 -0
- package/prompts/scan-review.md +40 -0
- package/prompts/survey.md +82 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse (Write|Edit|MultiEdit|NotebookEdit|Bash): anything "component" used more than once is a template (0.23.0: html, c++ and apps;
|
|
3
|
+
// a change, an update or a test is then made once, never per copy). A write that adds an element built by hand, in the shape of one that already
|
|
4
|
+
// exists in this file or another of this checkout, is refused (lib/elements.mjs: names and text stripped). A line marked `one-off:` with its
|
|
5
|
+
// reason is left alone, and so are tests, `protected` globs, generated and built code. The other files' shapes are kept between calls, keyed by
|
|
6
|
+
// time and size, so a write reads only what changed. `--selftest` proves it.
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { extname, join } from 'node:path';
|
|
9
|
+
import { parametersOf } from './lib/code-shapes.mjs';
|
|
10
|
+
import { isContent } from './lib/content.mjs';
|
|
11
|
+
import { languageOf } from './lib/languages.mjs';
|
|
12
|
+
import { nearCopy } from './lib/near-copies.mjs';
|
|
13
|
+
import { EXTS, readText, repoIndex } from './lib/repo-index.mjs';
|
|
14
|
+
import { TEMPLATE, TESTS, copiesNeeded, skippedBy } from './lib/elements.mjs';
|
|
15
|
+
import { asHook, isMain } from './lib/hook-log.mjs';
|
|
16
|
+
import { judgeEvent } from './lib/writes.mjs';
|
|
17
|
+
import { checkoutRoot, claudeRoot } from './lib/project.mjs';
|
|
18
|
+
import { readLayout } from './lib/layout.mjs';
|
|
19
|
+
import { matchesAny } from './lib/glob.mjs';
|
|
20
|
+
import { bashOf, editOf, runHook, scratchProject, verdicts, writeOf } from './lib/selftest.mjs';
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// { id: [{ file, line, what }] } over every other file.
|
|
24
|
+
function copiesElsewhere(entries, rel) {
|
|
25
|
+
const by = new Map();
|
|
26
|
+
for (const [file, e] of Object.entries(entries)) {
|
|
27
|
+
if (file === rel) continue;
|
|
28
|
+
for (const [id, line, what] of e.shapes) { if (!by.has(id)) by.set(id, []); by.get(id).push({ file, line, what }); }
|
|
29
|
+
}
|
|
30
|
+
return by;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const AGAIN = { markup: (s) => `${s.what} built by hand again, the same element with other names and text`, setup: (s) => `${s.what} set up by hand again, the same set-up with other names`,
|
|
34
|
+
run: () => 'code the repo already has, the same statements with other names', rows: (s) => `${s.nodes} calls of ${s.what} in a row, a table written as code` };
|
|
35
|
+
|
|
36
|
+
function refusal(s, w, rel, root, elsewhere, after) {
|
|
37
|
+
const where = [...after.filter((x) => x.id === s.id).map((x) => `${rel}:${x.line}`), ...elsewhere.map((x) => `${x.file}:${x.line}`)];
|
|
38
|
+
const differs = s.kind === 'run' && elsewhere.length ? parametersOf(s.id, w.after, readFileSync(join(root, elsewhere[0].file), 'utf8'), extname(rel)) : [];
|
|
39
|
+
const params = s.kind !== 'run' || !elsewhere.length ? '' : differs.length ? `It differs only in: ${differs.slice(0, 6).join(', ')}. Those are the parameters.\n` : 'It is the same, every name: move it to one place and call it.\n';
|
|
40
|
+
return `this is ${AGAIN[s.kind](s)}:\n ${where.slice(0, 6).join('\n ')}\n${params}` +
|
|
41
|
+
`Anything used more than once is one template: make it ${TEMPLATE[s.kind]}, so a change or a test is made once (skill: make-it-a-template).\n` +
|
|
42
|
+
'Take the copies out first, then write the template: it is refused while they stand. If this one truly differs, say so above it: `one-off: <reason>`.\n';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const sameLanguage = (rel, file) => languageOf(extname(rel)) === languageOf(extname(file));
|
|
46
|
+
const COMPOUND = /[a-z0-9][A-Z]|[a-z0-9]_[a-z0-9]/; // a plain word (`count`, `read`) is in many files by chance
|
|
47
|
+
const CONVENTION = 3; // a name in this many other files is a convention: `selftest`, `handler`
|
|
48
|
+
|
|
49
|
+
// What the write adds that was not there: the statements, in order, and the new definitions.
|
|
50
|
+
function addedTo(before, after) {
|
|
51
|
+
const left = before.st.reduce((m, [h]) => m.set(h, (m.get(h) ?? 0) + 1), new Map());
|
|
52
|
+
const statements = after.st.filter(([h]) => { const n = left.get(h) ?? 0; left.set(h, n - 1); return n <= 0; });
|
|
53
|
+
const had = new Set(before.defs.map(([name]) => name));
|
|
54
|
+
return { statements, defs: after.defs.filter(([name]) => !had.has(name) && COMPOUND.test(name)) };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function nearRefusal(hit) {
|
|
58
|
+
return `this is close to code the repo already has: ${hit.matched} of ${hit.of} statements from line ${hit.line} match ${hit.file}:${hit.from}-${hit.to}, in order, with other names.\n` +
|
|
59
|
+
`Anything used more than once is one template: make it ${TEMPLATE.run}, so a change or a test is made once (skill: make-it-a-template).\n` +
|
|
60
|
+
'Take the copies out first, then write the template. If this one truly differs, say so above it: `one-off: <reason>`.\n';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function remadeRefusal(name, line, where) {
|
|
64
|
+
return `${name} (line ${line}) already exists: ${where.map((x) => `${x.file}:${x.line}`).join(', ')}.\nCall it, and move it somewhere both can reach if it cannot be reached. ` +
|
|
65
|
+
'If it does a different job, give it a name that says so. If it truly must exist twice, say so above it: `one-off: <reason>`.\n';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function remade(defs, entries, rel) {
|
|
69
|
+
for (const [name, line] of defs) {
|
|
70
|
+
const where = Object.entries(entries).filter(([f]) => f !== rel && sameLanguage(rel, f)).flatMap(([file, e]) => (e.defs ?? []).filter(([n]) => n === name).map(([, l]) => ({ file, line: l })));
|
|
71
|
+
if (where.length && where.length < CONVENTION) return remadeRefusal(name, line, where);
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function judgeWrite(w, rel) {
|
|
77
|
+
if (w.op === 'delete' || w.after === null || w.after === undefined || !EXTS.has(extname(w.path)) || matchesAny(rel, TESTS)) return null;
|
|
78
|
+
const before = readText(w.before ?? '', rel);
|
|
79
|
+
const after = readText(w.after, rel);
|
|
80
|
+
const count = (list) => list.reduce((m, s) => m.set(s.id, (m.get(s.id) ?? 0) + 1), new Map());
|
|
81
|
+
const was = count(before.shapes);
|
|
82
|
+
const here = count(after.shapes);
|
|
83
|
+
const fresh = after.shapes.filter((s, i) => after.shapes.findIndex((x) => x.id === s.id) === i && here.get(s.id) > (was.get(s.id) ?? 0));
|
|
84
|
+
const { statements, defs } = addedTo(before, after);
|
|
85
|
+
if (!fresh.length && !statements.length && !defs.length) return null;
|
|
86
|
+
const layout = readLayout(claudeRoot(w.path));
|
|
87
|
+
const skip = skippedBy(layout);
|
|
88
|
+
if (matchesAny(rel, skip) || isContent(rel, layout)) return null;
|
|
89
|
+
const root = checkoutRoot(w.path);
|
|
90
|
+
const entries = repoIndex(root, [...TESTS, ...skip], layout);
|
|
91
|
+
const others = copiesElsewhere(entries, rel);
|
|
92
|
+
for (const s of fresh) {
|
|
93
|
+
const elsewhere = others.get(s.id) ?? [];
|
|
94
|
+
const oneName = new Set([s.what, ...elsewhere.map((x) => x.what)]).size === 1;
|
|
95
|
+
if (here.get(s.id) + elsewhere.length >= copiesNeeded(s, oneName)) return refusal(s, w, rel, root, elsewhere, after.shapes);
|
|
96
|
+
}
|
|
97
|
+
const near = nearCopy(statements, Object.entries(entries).filter(([f]) => f !== rel && sameLanguage(rel, f)).map(([file, e]) => ({ file, st: e.st ?? [] })));
|
|
98
|
+
return near ? nearRefusal(near) : remade(defs, entries, rel);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const main = (text) => judgeEvent(text, { name: 'template-law', write: judgeWrite });
|
|
102
|
+
|
|
103
|
+
function selftest() {
|
|
104
|
+
const hook = new URL(import.meta.url).pathname;
|
|
105
|
+
const card = (t) => `<div class="card"><h3 class="t">${t}</h3><p>${t} text</p><button type="button">Go</button></div>\n`;
|
|
106
|
+
const knob = (n) => ` ${n}.setRange (0.0, 1.0);\n ${n}.setValue (0.5);\n ${n}.setSliderStyle (Rotary);\n addAndMakeVisible (${n});\n`;
|
|
107
|
+
// Stellix's way: many statements on one line, and a helper copied from file to file with other names.
|
|
108
|
+
const oneLine = (n) => ` ${n}.setRange (0.0, 1.0); ${n}.setValue (0.5); ${n}.setSliderStyle (Rotary); addAndMakeVisible (${n});\n`;
|
|
109
|
+
const reader = (f, id) => `auto ${f} = [this](const std::string& ${id}) {\n const auto& L = shown(); float best = 0.0f;\n for (const auto& c : L.controls) if (c.id == ${id}) best = c.box.getCentreX() * float(getWidth());\n if (best > 10.0f) log(best, "${id}");\n const auto p = at(best, 2.5f); pressAt(p); releaseAt(p); return best;\n};\n`;
|
|
110
|
+
const rows = (s) => `void seed(Entry& e) {\n${[...'abcdef'].map((k) => ` set(e, "${s}.${k}", 0.0f);\n`).join('')}}\n`;
|
|
111
|
+
const table = (t) => `const Row ${t}[] = {\n { "a", 1, 2, 3 },\n { "b", 4, 5, 6 },\n { "c", 7, 8, 9 },\n { "d", 1, 2, 3 },\n { "e", 4, 5, 6 },\n { "f", 7, 8, 9 },\n};\n`;
|
|
112
|
+
// A near copy: the same steps with a line changed every few lines, so no stretch of it is exact.
|
|
113
|
+
const steps = (n, odd) => `def ${n}(rows, limit):\n${[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => ` value_${i} = compute_step(rows[${i}], limit, scale=${i}, name="k${i}") + other(rows, ${i})\n${i % 3 === 2 && odd ? ` note_${i}(limit)\n` : ''}`).join('')} return rows\n`;
|
|
114
|
+
const dir = scratchProject({ 'src/steps.py': steps('load_all', false), 'src/dates.ts': 'export function formatDateRange(a, b) {\n return a + b;\n}\n', 'src/a.html': card('One'), 'src/ctl.cpp': `void build() {\n${knob('cutoff')}}\n`, 'tests/t.cpp': `void t() {\n${knob('probe')}}\n`, 'src/gate_a.h': reader('pressOf', 'id'), 'src/rows.cpp': table('kOne'), 'vendored/gate.h': reader('tapOf', 'key'), 'kept/gate.h': reader('holdOf', 'key'),
|
|
115
|
+
'.claude/layout.json': JSON.stringify({ folders: { vendored: { path: 'vendored', kind: 'copy' } }, guards: { templates: { exclude: ['kept/**'] } } }) });
|
|
116
|
+
const w = (file, content) => runHook(hook, dir, writeOf(join(dir, file), content));
|
|
117
|
+
return verdicts('template-law', [
|
|
118
|
+
['a second card, in another file', w('src/b.html', card('Two')), 2],
|
|
119
|
+
['two cards in one new file', w('src/c.tsx', card('A') + card('B')), 2],
|
|
120
|
+
['a second hand-built control, other names', w('src/more.cpp', `void more() {\n${knob('resonance')}}\n`), 2],
|
|
121
|
+
['the same control twice in one file', w('src/two.cpp', `void two() {\n${knob('a1')}\n int x;\n${knob('b1')}}\n`), 2],
|
|
122
|
+
['the same control, many statements on a line', w('src/line.cpp', `void line() {\n${oneLine('drive')}}\n`), 2],
|
|
123
|
+
['a helper copied with other names', w('src/gate_b.h', reader('tapOf', 'key')), 2],
|
|
124
|
+
['a table written as calls', w('src/seed.cpp', rows('seed')), 2],
|
|
125
|
+
['a table as data rows', w('src/rows2.cpp', table('kTwo')), 0],
|
|
126
|
+
['a copy tree', w('vendored/more.h', reader('dropOf', 'name')), 0],
|
|
127
|
+
['a tree the layout excludes', w('kept/more.h', reader('dropOf', 'name')), 0],
|
|
128
|
+
['a tutorial sample in a content folder', w('articles/guide/gate.h', reader('tapOf', 'key')), 0],
|
|
129
|
+
['a near copy, with a line added every few lines', w('src/steps2.py', steps('load_more', true)), 2],
|
|
130
|
+
['a function the repo already has, written again', w('src/other.ts', 'export function formatDateRange(x, y) {\n return `${x} to ${y}`;\n}\n'), 2],
|
|
131
|
+
['a plain-word name is not judged', w('src/plain.ts', 'export function render(x) {\n return x;\n}\n'), 0],
|
|
132
|
+
['the same name in another language', w('src/dates.py', 'def formatDateRange(a, b):\n return a\n'), 0],
|
|
133
|
+
['the component used three times', w('src/use.tsx', '<Card t="a" />\n<Card t="b" />\n<Card t="c" />\n'), 0],
|
|
134
|
+
['a list from one .map()', w('src/list.tsx', 'rows.map((r) => (<tr key={r.id} class="row"><td class="t">{r.t}</td><td>{r.x}</td></tr>))\n'), 0],
|
|
135
|
+
['one element is no template', w('src/one.html', '<section class="hero"><h1 class="big">Hi</h1><p>x</p><a href="/">y</a></section>\n'), 0],
|
|
136
|
+
['a bare list', w('src/ul.html', '<ul><li>a</li><li>b</li><li>c</li></ul>\n'), 0],
|
|
137
|
+
['data rows are not elements', w('src/d.ts', "const R = [\n { id: 'a', label: 'b' },\n { id: 'c', label: 'd' },\n];\n"), 0],
|
|
138
|
+
['calls on one object are one set-up', w('src/ctx.ts', 'ctx.save();\nctx.fill();\nctx.restore();\nf();\nctx.save();\nctx.fill();\nctx.restore();\n'), 0],
|
|
139
|
+
['a marked one-off', w('src/odd.html', `<!-- one-off: the print view -->\n${card('Odd')}`), 0],
|
|
140
|
+
['a test file', w('tests/x_test.cpp', `void x() {\n${knob('q')}}\n`), 0],
|
|
141
|
+
['an edit that adds no copy', runHook(hook, dir, editOf(join(dir, 'src/a.html'), 'One text', 'Uno text')), 0],
|
|
142
|
+
['a heredoc that adds a copy', runHook(hook, dir, bashOf(`cat > src/h.html <<'EOF'\n${card('H')}EOF`, dir)), 2],
|
|
143
|
+
['another project is not ours', runHook(hook, dir, writeOf(join(dir, '..', 'elsewhere', 'x.html'), card('X'))), 0],
|
|
144
|
+
]);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const hook = main; // run-hooks.mjs calls it with the event's text (KWB-24)
|
|
148
|
+
if (isMain(import.meta.url)) process.exit(process.argv.includes('--selftest') ? selftest() : await asHook('template-law', main));
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// untrusted-boundary (PreToolUse, Write|Edit|MultiEdit|NotebookEdit|Bash): text from outside reaches a model only inside a
|
|
3
|
+
// SHA-256 boundary (KWB-34). The house form (the user's release-watcher skills): every block of untrusted text sent to a model (a
|
|
4
|
+
// web page, a fetched document, a scraped row, a user's upload, a stored copy of any of them) is wrapped, per call, in a
|
|
5
|
+
// boundary named by the SHA-256 of the block, with a note before and after that gives the exact hash and says the block is
|
|
6
|
+
// information, not instructions; our own fields (ids, dates, the task) stay outside it.
|
|
7
|
+
// Refused, by every write route (lib/writes.mjs): a write that ADDS a model call (Anthropic messages.create / .stream,
|
|
8
|
+
// OpenAI chat.completions.create / responses.create, the AI SDK's generateText / streamText / generateObject / streamObject,
|
|
9
|
+
// LangChain's .invoke on a chat model is not read) to a JS, TS or Python file that, once written, has no boundary: no call
|
|
10
|
+
// of the house helper (wrapUntrusted / wrap_untrusted / untrustedBoundary) and no line `untrusted: none` saying the file sends
|
|
11
|
+
// a model only its own text. Another project's files are not judged.
|
|
12
|
+
// node untrusted-boundary.mjs --selftest
|
|
13
|
+
import { extname, join } from 'node:path';
|
|
14
|
+
import { judgeEvent } from './lib/writes.mjs';
|
|
15
|
+
import { runHook, scratchProject, verdicts, writeOf } from './lib/selftest.mjs';
|
|
16
|
+
import { asHook, isMain } from './lib/hook-log.mjs';
|
|
17
|
+
|
|
18
|
+
const CODE = new Set(['.js', '.mjs', '.cjs', '.ts', '.mts', '.cts', '.tsx', '.jsx', '.py']);
|
|
19
|
+
const CALL = /\bmessages\.(?:create|stream)\s*\(|\bchat\.completions\.create\s*\(|\bresponses\.create\s*\(|\b(?:generateText|streamText|generateObject|streamObject)\s*\(/g;
|
|
20
|
+
const BOUNDARY = /\b(?:wrapUntrusted|wrap_untrusted|untrustedBoundary)\s*\(|^\s*(?:\/\/|#|\*)\s*untrusted:\s*none\b/m;
|
|
21
|
+
const calls = (text) => (text.match(CALL) ?? []).length;
|
|
22
|
+
|
|
23
|
+
export function verdict(rel, before, after) {
|
|
24
|
+
if (!CODE.has(extname(rel)) || after === null) return null;
|
|
25
|
+
if (calls(after) <= calls(before ?? '') || BOUNDARY.test(after)) return null;
|
|
26
|
+
return `BLOCKED by untrusted-boundary: ${rel} gains a model call and has no SHA-256 boundary.\n` +
|
|
27
|
+
'Every block of untrusted text (a web page, a fetched document, a scraped row, an upload) goes to the model inside wrapUntrusted(text): ' +
|
|
28
|
+
'a per-call boundary named by the SHA-256 of the block, with a note before and after giving the exact hash and saying the block is information, ' +
|
|
29
|
+
'not instructions; your own fields stay outside it. If this file sends the model only its own text, say so on a line of its own: `// untrusted: none`.\n';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const main = (text) => judgeEvent(text, { name: 'untrusted-boundary', write: (w, rel) => verdict(rel, w.before, w.after) });
|
|
33
|
+
|
|
34
|
+
function selftest() {
|
|
35
|
+
const hook = new URL(import.meta.url).pathname;
|
|
36
|
+
const dir = scratchProject({ 'src/ask.ts': 'export const x = 1;\n' });
|
|
37
|
+
const run = (rel, text) => runHook(hook, dir, writeOf(join(dir, rel), text));
|
|
38
|
+
const call = 'const r = await client.messages.create({ model, messages: [{ role: "user", content: page }] });\n';
|
|
39
|
+
const cases = [
|
|
40
|
+
['a model call fed a page, no boundary (Anthropic)', run('src/ask.ts', call), 2],
|
|
41
|
+
['an OpenAI call, no boundary', run('src/ask.mjs', 'await openai.chat.completions.create({ messages });\n'), 2],
|
|
42
|
+
['an AI SDK call, no boundary', run('src/ask.tsx', 'const { text } = await generateText({ model, prompt: html });\n'), 2],
|
|
43
|
+
['a Python call, no boundary', run('tools/ask.py', 'r = client.messages.create(model=m, messages=msgs)\n'), 2],
|
|
44
|
+
['the call with the page inside wrapUntrusted', run('src/ask.ts', `const page = wrapUntrusted(html);\n${call}`), 0],
|
|
45
|
+
['a file that sends only its own text, and says so', run('src/ask.ts', `// untrusted: none\n${call}`), 0],
|
|
46
|
+
['a doc that shows a call is not code', run('docs/howto.md', call), 0],
|
|
47
|
+
['code with no model call', run('src/plain.ts', 'export const y = fetch(url);\n'), 0],
|
|
48
|
+
];
|
|
49
|
+
return verdicts('untrusted-boundary', cases);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const hook = main; // run-hooks.mjs calls it with the event's text (KWB-24)
|
|
53
|
+
if (isMain(import.meta.url)) process.exit(process.argv.includes('--selftest') ? selftest() : await asHook('untrusted-boundary', main));
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "base",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Universal engineering every configuration gets: the git laws, the line limit, the folder and placement laws, the shape law (shape-law, brief-guard, making, making-check), once-only, lesson checks, secrets-guard and the untrusted-content boundary.",
|
|
5
|
+
"type": "stock",
|
|
6
|
+
"author": "claude-packs",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/TheDecipherist/claude-packs",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/TheDecipherist/claude-packs.git",
|
|
12
|
+
"directory": "packs/base"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"universal",
|
|
16
|
+
"git",
|
|
17
|
+
"hooks"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"claude-packs": ">=0.19.0"
|
|
21
|
+
},
|
|
22
|
+
"concepts": {
|
|
23
|
+
"cpp": [
|
|
24
|
+
"src/**/*.cpp",
|
|
25
|
+
"src/**/*.h",
|
|
26
|
+
"src/**/*.hpp",
|
|
27
|
+
"src/**/*.mm",
|
|
28
|
+
"tests/**/*.cpp",
|
|
29
|
+
"tests/**/*.h"
|
|
30
|
+
],
|
|
31
|
+
"tests": [
|
|
32
|
+
"tests"
|
|
33
|
+
],
|
|
34
|
+
"build": [
|
|
35
|
+
"**/CMakeLists.txt",
|
|
36
|
+
"**/*.cmake",
|
|
37
|
+
"CMakePresets.json"
|
|
38
|
+
],
|
|
39
|
+
"claude": [
|
|
40
|
+
".claude/skills/**",
|
|
41
|
+
"**/.claude/skills/**",
|
|
42
|
+
".claude/agents/**",
|
|
43
|
+
".claude/rules/**"
|
|
44
|
+
],
|
|
45
|
+
"notes": [
|
|
46
|
+
"bugs.md",
|
|
47
|
+
"workflow-bugs.md",
|
|
48
|
+
"**/known-issues*.md",
|
|
49
|
+
"docs/**/*.md"
|
|
50
|
+
],
|
|
51
|
+
"docs-folder": [
|
|
52
|
+
"docs"
|
|
53
|
+
],
|
|
54
|
+
"tooling": [
|
|
55
|
+
"tools/**",
|
|
56
|
+
".claude/hooks/**",
|
|
57
|
+
".claude/settings.json"
|
|
58
|
+
],
|
|
59
|
+
"plan": [
|
|
60
|
+
"build.md",
|
|
61
|
+
"**/build.md"
|
|
62
|
+
],
|
|
63
|
+
"copy-trees": [
|
|
64
|
+
"kind:copy",
|
|
65
|
+
".claude/layout.json"
|
|
66
|
+
],
|
|
67
|
+
"js": [
|
|
68
|
+
"src/**/*.{js,mjs,cjs,jsx,ts,tsx}",
|
|
69
|
+
"tests/**/*.{js,mjs,cjs,ts,tsx}",
|
|
70
|
+
"test/**/*.{js,mjs,cjs,ts,tsx}"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"skillsIn": null,
|
|
74
|
+
"hooks": {
|
|
75
|
+
"PreToolUse": [
|
|
76
|
+
{
|
|
77
|
+
"matcher": "Write|Edit|MultiEdit|NotebookEdit|Bash",
|
|
78
|
+
"hooks": [
|
|
79
|
+
{
|
|
80
|
+
"type": "command",
|
|
81
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/lesson-checks.mjs\"",
|
|
82
|
+
"timeout": 10
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"type": "command",
|
|
86
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/file-size-guard.mjs\"",
|
|
87
|
+
"timeout": 10
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"type": "command",
|
|
91
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/placement-law.mjs\"",
|
|
92
|
+
"timeout": 10
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"type": "command",
|
|
96
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/folder-law.mjs\"",
|
|
97
|
+
"timeout": 10
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"type": "command",
|
|
101
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/shape-law.mjs\"",
|
|
102
|
+
"timeout": 15
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
"type": "command",
|
|
106
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/brief-guard.mjs\"",
|
|
107
|
+
"timeout": 10
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"type": "command",
|
|
111
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/once-only.mjs\"",
|
|
112
|
+
"timeout": 20
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"type": "command",
|
|
116
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/secrets-guard.mjs\"",
|
|
117
|
+
"timeout": 10
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"type": "command",
|
|
121
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/untrusted-boundary.mjs\"",
|
|
122
|
+
"timeout": 10
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"type": "command",
|
|
126
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/dispatch-once.mjs\"",
|
|
127
|
+
"timeout": 15
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"type": "command",
|
|
131
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/template-law.mjs\"",
|
|
132
|
+
"timeout": 15
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"type": "command",
|
|
136
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/code-shape.mjs\"",
|
|
137
|
+
"timeout": 15
|
|
138
|
+
}
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"matcher": "Agent|Task",
|
|
143
|
+
"hooks": [
|
|
144
|
+
{
|
|
145
|
+
"type": "command",
|
|
146
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/brief-guard.mjs\"",
|
|
147
|
+
"timeout": 10
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"matcher": "Bash",
|
|
153
|
+
"hooks": [
|
|
154
|
+
{
|
|
155
|
+
"type": "command",
|
|
156
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/git-guard.mjs\"",
|
|
157
|
+
"timeout": 15
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"type": "command",
|
|
161
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/suite-once.mjs\"",
|
|
162
|
+
"timeout": 10
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"type": "command",
|
|
166
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/repo-litter.mjs\"",
|
|
167
|
+
"timeout": 10
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
],
|
|
172
|
+
"Stop": [
|
|
173
|
+
{
|
|
174
|
+
"hooks": [
|
|
175
|
+
{
|
|
176
|
+
"type": "command",
|
|
177
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/making-check.mjs\"",
|
|
178
|
+
"timeout": 60
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
"type": "command",
|
|
182
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/git-guard.mjs\"",
|
|
183
|
+
"timeout": 30
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
}
|
|
187
|
+
],
|
|
188
|
+
"UserPromptSubmit": [
|
|
189
|
+
{
|
|
190
|
+
"hooks": [
|
|
191
|
+
{
|
|
192
|
+
"type": "command",
|
|
193
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/making.mjs\"",
|
|
194
|
+
"timeout": 10
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"type": "command",
|
|
198
|
+
"command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/reuse-hint.mjs\"",
|
|
199
|
+
"timeout": 10
|
|
200
|
+
}
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
]
|
|
204
|
+
},
|
|
205
|
+
"onByDefault": [
|
|
206
|
+
"hooks/shape-law.mjs",
|
|
207
|
+
"hooks/brief-guard.mjs",
|
|
208
|
+
"hooks/once-only.mjs",
|
|
209
|
+
"hooks/making.mjs",
|
|
210
|
+
"hooks/making-check.mjs",
|
|
211
|
+
"hooks/run-hooks.mjs"
|
|
212
|
+
],
|
|
213
|
+
"optIn": []
|
|
214
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
# concepts: cpp, js, tests
|
|
3
|
+
paths:
|
|
4
|
+
- "placeholder"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# A control is seen to fire, and a test can fail for the reason that matters
|
|
8
|
+
|
|
9
|
+
<constraints>
|
|
10
|
+
Always do:
|
|
11
|
+
- Prove a control (a guard, an audit log, a limiter) through the real path, and see it refuse.
|
|
12
|
+
- Make a mock return what the real function returns.
|
|
13
|
+
- Keep one test per data layer against the real store.
|
|
14
|
+
- Fail a test when its setup failed.
|
|
15
|
+
- Break every check on purpose, and write to the state you inject: an injection that passes means dead code or a missing check.
|
|
16
|
+
- Establish which artefact you are looking at before diagnosing: check the binary's time and the server's age.
|
|
17
|
+
|
|
18
|
+
Never do:
|
|
19
|
+
- Return success from a stub, or leave `// for now` in front of `success: true`.
|
|
20
|
+
- Declare a security dependency and never use it.
|
|
21
|
+
- Skip a test because an earlier step made nothing.
|
|
22
|
+
- Let a check recompute its own subject: it holds by construction.
|
|
23
|
+
- Leave a local workaround with no test that fails the day upstream is fixed.
|
|
24
|
+
</constraints>
|
|
25
|
+
|
|
26
|
+
## Check
|
|
27
|
+
`stub-reports-success`, `test-skip-on-failed-setup`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
# concepts: cpp, js
|
|
3
|
+
paths:
|
|
4
|
+
- "placeholder"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Behaviour lives with its kind: objects and tables, not branches
|
|
8
|
+
|
|
9
|
+
<constraints>
|
|
10
|
+
Always do:
|
|
11
|
+
- When behaviour varies by a **kind** (mode, type, action, tool, message, format), each kind owns its behaviour in **one place**:
|
|
12
|
+
- **JS/TS:** a table of handler objects `{ kind: { label, build(), validate() } }` typed `satisfies Record<Kind, Handler>`. Or classes implementing one interface, chosen once from a registry.
|
|
13
|
+
- **C++:** an interface with one class per kind, created once from a registry or factory. Or `std::variant` + `std::visit`, where the compiler checks every kind is handled. Or a `constexpr` table of plain data per kind.
|
|
14
|
+
- Choose the object **once**, when the kind is set (a setting changes, a request arrives, a file is opened), then call it, so no code asks "which kind?" again.
|
|
15
|
+
- A new kind is **one new entry** (one class or one table row), with nothing else edited. If adding a kind needs edits in several files, the design is wrong; fix the design.
|
|
16
|
+
|
|
17
|
+
Never do:
|
|
18
|
+
- Branch on the same kind in more than one place (`switch (mode)` in the renderer, the label code and the config loader). The `dispatch-once` hook refuses the second one.
|
|
19
|
+
- Grow a `switch` or an if-chain past three cases where a table or an object belongs (`lookup-over-if-chains`, `field-map-over-if-runs`).
|
|
20
|
+
|
|
21
|
+
The one exception is **a hot inner loop** (per element, per pixel, per sample: an audio callback is one; audio-core's `per-sample-code-inlinable` says why). There a virtual call or a `std::function` can't be inlined and costs on every iteration. The kind's object is still chosen once, but its inner loop may be:
|
|
22
|
+
- a non-virtual member function chosen when the kind is set;
|
|
23
|
+
- a `constexpr` table;
|
|
24
|
+
- or one exhaustive `switch` on an `enum class` **inside that kind's owner**, never repeated elsewhere.
|
|
25
|
+
</constraints>
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// NO: the same question asked in three places, and a new kind means three edits
|
|
29
|
+
switch (tool.kind) { case 'search': … case 'fetch': … } // run
|
|
30
|
+
switch (tool.kind) { case 'search': … case 'fetch': … } // label
|
|
31
|
+
switch (tool.kind) { case 'search': … case 'fetch': … } // validate
|
|
32
|
+
|
|
33
|
+
// YES: each kind answers for itself
|
|
34
|
+
const TOOLS = {
|
|
35
|
+
search: { label: 'Search', schema: SearchArgs, run: runSearch },
|
|
36
|
+
fetch: { label: 'Fetch', schema: FetchArgs, run: runFetch },
|
|
37
|
+
} satisfies Record<ToolKind, ToolHandler>;
|
|
38
|
+
const tool = TOOLS[kind]; // chosen once
|
|
39
|
+
tool.schema.parse(args); return tool.run(args);
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```cpp
|
|
43
|
+
// YES (outside a hot inner loop): one class per kind, chosen once
|
|
44
|
+
struct Shape { virtual ~Shape() = default; virtual std::string label() const = 0; virtual void prepare (const Settings& s) = 0; };
|
|
45
|
+
std::unique_ptr<Shape> makeShape (Mode m); // the one factory: the only place that names every kind
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Check
|
|
49
|
+
- `dispatch-once` (hook) refuses an edit that adds a `switch` on a kind already switched on in another file.
|
|
50
|
+
- `lookup-over-if-chains` and `field-map-over-if-runs` (lesson checks) refuse new if-chains and field runs.
|
|
51
|
+
- If branching on a kind ever slips through in a new shape (a ternary ladder, a `match`, an `if` on `typeid`), add that shape to a check.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
# concepts: tests, cpp
|
|
3
|
+
paths:
|
|
4
|
+
- "placeholder"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# A bug fix starts with a test that fails for that bug
|
|
8
|
+
|
|
9
|
+
<constraints>
|
|
10
|
+
Always do:
|
|
11
|
+
- Before touching the code, write the smallest test or probe that reproduces the bug: a render, a parameter sequence, a MIDI file, a state load.
|
|
12
|
+
- Run it and see it fail for the reported reason: `node .claude/tools/test-verdict.mjs --red <test-name>` must say `RED_CONFIRMED`.
|
|
13
|
+
- Freeze what defines "fixed" before the fix, where the project has `freeze.mjs`: `node .claude/tools/freeze.mjs on "bug <id>" <test file> <golden files>`.
|
|
14
|
+
- Fix the code until the same test passes, then run the full suite once, then unfreeze.
|
|
15
|
+
- Keep the test in the suite forever. That is what stops the bug coming back.
|
|
16
|
+
|
|
17
|
+
Never do:
|
|
18
|
+
- Fix first and write the test after. A test written against the fix proves nothing.
|
|
19
|
+
- Edit the test, the golden render or the fingerprint to make the fix pass. If the target is wrong, stop and ask.
|
|
20
|
+
- Call a bug fixed because the symptom went away once by ear.
|
|
21
|
+
</constraints>
|
|
22
|
+
|
|
23
|
+
## Check
|
|
24
|
+
- The `frozen-files` hook refuses edits to frozen files.
|
|
25
|
+
- `test-verdict.mjs --red` exits 1 if a test that must fail passes.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
---
|
|
2
|
+
# concepts: cpp, js
|
|
3
|
+
paths:
|
|
4
|
+
- "placeholder"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Three ifs in a row on one object's fields: a field table and one loop
|
|
8
|
+
|
|
9
|
+
<constraints>
|
|
10
|
+
Always do:
|
|
11
|
+
- When **3 or more `if`s in a row** test fields of the same object (`args.filter?.flag`, `args.filter?.kind`, `args.filter?.path`…) to copy, map or build something, write a **field table** and **one loop**:
|
|
12
|
+
- **JS/TS:** a module-scope object from field name to what to do with it, typed against the input (`satisfies Record<keyof Filter, …>`), and one `for … of Object.entries(TABLE)` that applies it. A new field is one new row.
|
|
13
|
+
- **C++:** a `constexpr std::array` of `{ name, member pointer / setter }`, and one loop.
|
|
14
|
+
- **Validation of required fields** is the same law: a list of required keys, and one loop that names the first one missing.
|
|
15
|
+
- Use the schema as the table when one exists (Zod `pick`/`partial`, a Mongo filter builder), rather than a hand-written list.
|
|
16
|
+
|
|
17
|
+
Never do:
|
|
18
|
+
- Write `if (src.a !== undefined) dst.a = …; if (src.b !== undefined) dst.b = …; if (src.c …` one field per line. It grows by one line per field until it's fifteen.
|
|
19
|
+
- Hide the run by putting an unrelated line between the ifs.
|
|
20
|
+
|
|
21
|
+
`if` is still right for:
|
|
22
|
+
- two fields;
|
|
23
|
+
- fields of different objects;
|
|
24
|
+
- conditions on different things.
|
|
25
|
+
</constraints>
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
// NO
|
|
29
|
+
if (args.filter?.flag !== undefined) filter.flags = args.filter.flag;
|
|
30
|
+
if (args.filter?.kind !== undefined) filter.kind = args.filter.kind;
|
|
31
|
+
if (args.filter?.path !== undefined) filter.path = { $regex: escapeRe(args.filter.path) };
|
|
32
|
+
|
|
33
|
+
// YES: one row per field, one loop
|
|
34
|
+
const FILTER_FIELDS = {
|
|
35
|
+
flag: (v) => ({ flags: v }),
|
|
36
|
+
kind: (v) => ({ kind: v }),
|
|
37
|
+
path: (v) => ({ path: { $regex: escapeRe(v) } }),
|
|
38
|
+
} satisfies Record<keyof NonNullable<Args['filter']>, (v: never) => Partial<Filter>>;
|
|
39
|
+
|
|
40
|
+
for (const [key, put] of Object.entries(FILTER_FIELDS)) {
|
|
41
|
+
const value = args.filter?.[key];
|
|
42
|
+
if (value !== undefined) Object.assign(filter, put(value));
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Check
|
|
47
|
+
`.claude/checks/field-map-over-if-runs.json` refuses an edit that **adds** three ifs on fields of the same object within a few lines of each other. It covers `!== undefined`, `!= null`, truthy tests, `?.`, `.` and `->`. Its companion is `lookup-over-if-chains`, for branches on one value. If a run ever slips through in a new shape, tighten the check rather than fixing the one case.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
# hook: git-guard.mjs
|
|
3
|
+
---
|
|
4
|
+
# Finished work lands: a finished branch is merged into the trunk and pushed
|
|
5
|
+
|
|
6
|
+
<constraints>
|
|
7
|
+
Always do:
|
|
8
|
+
- When a work branch's "done when" is measured and the whole suite is green, land it in the same turn with the `land-branch` skill. The trunk is always up to date.
|
|
9
|
+
- Land stacked branches in order, or land the top one when it holds them all.
|
|
10
|
+
- Say what landed, and the commit the remote now has.
|
|
11
|
+
|
|
12
|
+
Never do:
|
|
13
|
+
- Leave finished work on a branch waiting for someone to ask.
|
|
14
|
+
- Land work that is unfinished, or has a red test. A branch that cannot land yet says why.
|
|
15
|
+
- Ask before pushing a work branch: a work branch always pushes, from its first commit, with no yes needed.
|
|
16
|
+
- Land on the trunk over a project's own law: only the trunk can need a yes. Where its CLAUDE.md says so, ask once, with the branch pushed and ready.
|
|
17
|
+
</constraints>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
# hook: git-guard.mjs
|
|
3
|
+
---
|
|
4
|
+
# Git in the form the guard takes, the first time
|
|
5
|
+
|
|
6
|
+
<constraints>
|
|
7
|
+
Always do:
|
|
8
|
+
- Stage by name: `git add <paths>`, then `git commit -m "..." -- <paths>`.
|
|
9
|
+
- Push every commit before the next one, and every branch from its first commit: a later commit and the session's end both wait for it. A work branch never needs a yes to push; only the trunk can.
|
|
10
|
+
- Push to the repo by name: a remote whose URL is the owner's SSH URL, or the token form with the token as `$VARIABLE`, never pasted.
|
|
11
|
+
- Name the token on every `gh` call, when the project's guards ask for it.
|
|
12
|
+
- Commit before undoing anything: a work-in-progress commit on a branch, then undo.
|
|
13
|
+
- Start work on a branch from the fetched remote trunk.
|
|
14
|
+
|
|
15
|
+
Never do:
|
|
16
|
+
- `git add -A`, `git add .` or `commit -a`.
|
|
17
|
+
- Rebase, amend, force a push or use `--no-verify`: make a new commit.
|
|
18
|
+
- Put a command that a guard judges in the same call as the step it depends on: a guard reads the whole command before any of it runs.
|
|
19
|
+
</constraints>
|