diff --git a/.kit-baseline.json b/.kit-baseline.json index bb0b6471..a626c363 100644 --- a/.kit-baseline.json +++ b/.kit-baseline.json @@ -94,8 +94,851 @@ "src/utils/promptSelect.ts", "src/validation/dynamic-schema.ts" ] + }, + "standards": { + "complexity": [ + "packages/kit-plugin-aisle/src/scan.test.ts:(anonymous)", + "packages/kit-plugin-aisle/src/scan.ts:(anonymous)", + "packages/kit-plugin-aisle/src/scan.ts:normalizeSeverity", + "packages/kit-plugin-railway/src/railway-deploy.ts:provision", + "scripts/gen-plugin-registry.mjs:collect", + "skills/triage/scripts/triage.py:_resolve_npm_spec", + "skills/triage/scripts/triage.py:_resolve_npm_spec.satisfies", + "skills/triage/scripts/triage.py:_resolve_pip_spec", + "skills/triage/scripts/triage.py:triage_npm", + "skills/triage/scripts/triage.py:triage_pip", + "skills/triage/scripts/triage.py:triage_repo", + "src/adapters-flyio.test.ts:(anonymous)", + "src/adapters-railway.test.ts:(anonymous)", + "src/adapters/expo-eas.ts:provision", + "src/adapters/ingest.ts:parseSarif", + "src/adapters/searxng-instance.ts:provision", + "src/adapters/vercel-hosting.ts:provision", + "src/adr-derive.ts:deriveLayerCandidates", + "src/adr.ts:scalar", + "src/advisory-baseline.ts:visit", + "src/agent-audit.test.ts:(anonymous)", + "src/agent-audit.ts:auditMcpServers", + "src/agent-config.test.ts:(anonymous)", + "src/agent-config.test.ts:(anonymous)", + "src/agent-config.test.ts:(anonymous)", + "src/agent-config.ts:(anonymous)", + "src/agent-config.ts:codexGateCommands", + "src/agent-config.ts:managedWrapperProblems", + "src/analyze.ts:detectTestRunners", + "src/analyze.ts:gitCommitPrefixes", + "src/analyze.ts:renderClaudeMd", + "src/approval.test.ts:(anonymous)", + "src/approval.test.ts:(anonymous)", + "src/approval.ts:requestApproval", + "src/audit-anchor-all.test.ts:(anonymous)", + "src/audit-anchor-all.test.ts:(anonymous)", + "src/audit-anchor.test.ts:(anonymous)", + "src/audit-anchor.test.ts:(anonymous)", + "src/audit-anchor.test.ts:(anonymous)", + "src/audit-anchor.test.ts:(anonymous)", + "src/audit.test.ts:(anonymous)", + "src/audit.test.ts:(anonymous)", + "src/audit.test.ts:(anonymous)", + "src/audit.ts:logAuditEvent", + "src/browser.test.ts:(anonymous)", + "src/browser.ts:diagnoseBrowser", + "src/budget.test.ts:(anonymous)", + "src/check-attestation.test.ts:(anonymous)", + "src/check-attestation.ts:verifyAttestation", + "src/check-decision-ledger.test.ts:(anonymous)", + "src/check-decision-ledger.ts:checkDecisionLedger", + "src/check-deploy.test.ts:(anonymous)", + "src/check-deploy.ts:checkVercelDeploy", + "src/check-deploy.ts:parseVercelEnvNames", + "src/check-design.ts:stripComments", + "src/check-disk-encryption.ts:checkDiskEncryption", + "src/check-gitignore.test.ts:(anonymous)", + "src/check-hooks.test.ts:(anonymous)", + "src/check-lock.test.ts:(anonymous)", + "src/check-lock.ts:checkLockFiles", + "src/check-policy-ops.test.ts:(anonymous)", + "src/check-run.ts:runCheckGate", + "src/check-security-cwd.test.ts:(anonymous)", + "src/check-security.test.ts:(anonymous)", + "src/check-security.test.ts:(anonymous)", + "src/check-security.test.ts:(anonymous)", + "src/check-security.ts:checkAllowScripts", + "src/check-security.ts:checkGuardDog", + "src/check-security.ts:checkMavenAudit", + "src/check-security.ts:checkPinnedVersions", + "src/check-security.ts:checkSecretsInCode", + "src/check-security.ts:checkServiceExposure", + "src/check-security.ts:checkTrivyConfig", + "src/check-standards.test.ts:(anonymous)", + "src/check-standards.ts:checkStandards", + "src/check-web-search.ts:checkWebSearch", + "src/cli.test.ts:(anonymous)", + "src/cli.test.ts:(anonymous)", + "src/cli.test.ts:(anonymous)", + "src/cli.test.ts:(anonymous)", + "src/cli.ts:cmdHelp", + "src/cli.ts:main", + "src/clone.test.ts:(anonymous)", + "src/commands/adr-derive.ts:adrDerive", + "src/commands/adr.ts:tryFile", + "src/commands/broker.test.ts:(anonymous)", + "src/commands/broker.test.ts:(anonymous)", + "src/commands/browser.ts:cmdBrowser", + "src/commands/check.ts:cmdCheck", + "src/commands/ci.ts:cmdCi", + "src/commands/config.test.ts:(anonymous)", + "src/commands/config.ts:migrateConfigFile", + "src/commands/context.ts:cmdContext", + "src/commands/context.ts:cmdContextCheck", + "src/commands/coverage.ts:cmdCoverage", + "src/commands/coverage.ts:cmdSelfAudit", + "src/commands/env.ts:cmdEnv", + "src/commands/hooks.test.ts:(anonymous)", + "src/commands/hooks.ts:cmdHooks", + "src/commands/hooks.ts:cmdHooksAdd", + "src/commands/insight.ts:insightUnused", + "src/commands/mcp.ts:cmdMcp", + "src/commands/memory.ts:memMerge", + "src/commands/memory.ts:memPal", + "src/commands/memory.ts:memStats", + "src/commands/memory.ts:memVerify", + "src/commands/panic.ts:cmdPanic", + "src/commands/profile.test.ts:(anonymous)", + "src/commands/project.ts:cmdClone", + "src/commands/repomap.ts:cmdMap", + "src/commands/review.test.ts:(anonymous)", + "src/commands/scan.ts:cmdScan", + "src/commands/secrets.ts:(anonymous)", + "src/commands/secrets.ts:cmdSecrets", + "src/commands/secrets.ts:cmdSecretsMigrate", + "src/commands/secrets.ts:cmdSecretsOneCli", + "src/commands/secrets.ts:cmdSecretsPropagateStandalone", + "src/commands/secrets.ts:cmdSecretsPurgeHistory", + "src/commands/security.ts:cmdSecurity", + "src/commands/security.ts:cmdSecurityPrescan", + "src/commands/security.ts:cmdSecurityVerifyPull", + "src/commands/sentinel.ts:cmdSentinel", + "src/commands/setup.ts:runConfiguredCommand", + "src/commands/skill.ts:cmdSkill", + "src/commands/tools.ts:cmdTools", + "src/commands/triage.ts:cmdTriage", + "src/completions.ts:generateBashCompletion", + "src/config.test.ts:(anonymous)", + "src/config.test.ts:(anonymous)", + "src/context-lock.test.ts:(anonymous)", + "src/context-lock.ts:emit", + "src/context-lock.ts:suggestContextToml", + "src/context.test.ts:(anonymous)", + "src/cost-monitor.test.ts:(anonymous)", + "src/cost-monitor.test.ts:(anonymous)", + "src/cost-monitor.ts:detectCostAnomalies", + "src/create-plugin.test.ts:(anonymous)", + "src/database.test.ts:(anonymous)", + "src/decision-ledger.test.ts:(anonymous)", + "src/decision-ledger.ts:(anonymous)", + "src/doctor.test.ts:(anonymous)", + "src/doctor.ts:checkIdentityKeystore", + "src/doctor.ts:checkNodeVersion", + "src/elevation-scopes.test.ts:(anonymous)", + "src/elevation.test.ts:(anonymous)", + "src/env-diff.test.ts:(anonymous)", + "src/environment.test.ts:(anonymous)", + "src/escalate.test.ts:(anonymous)", + "src/exec-broker/broker.test.ts:(anonymous)", + "src/exec-broker/broker.test.ts:(anonymous)", + "src/exec-broker/broker.ts:collectDenials", + "src/exec-broker/policy-cwd.test.ts:(anonymous)", + "src/exec-broker/policy.test.ts:(anonymous)", + "src/exec-broker/policy.ts:validateBrokerPolicy", + "src/exec-broker/profile-policy.test.ts:(anonymous)", + "src/external-findings.ts:parseExternalFindings", + "src/fix-cwd.test.ts:(anonymous)", + "src/fix.ts:(anonymous)", + "src/fix.ts:fixDeploy", + "src/flag-surface.test.ts:(anonymous)", + "src/flag-surface.test.ts:stringLiterals", + "src/governance-middleware.test.ts:(anonymous)", + "src/governance-middleware.test.ts:(anonymous)", + "src/governance-middleware.test.ts:(anonymous)", + "src/governance-middleware.test.ts:(anonymous)", + "src/governance.test.ts:(anonymous)", + "src/guard.test.ts:(anonymous)", + "src/health-sensors/bitbucket-pipelines.test.ts:deps", + "src/health-sensors/github-actions.test.ts:(anonymous)", + "src/hints.test.ts:(anonymous)", + "src/hitl.test.ts:(anonymous)", + "src/hitl.ts:stripInfoCommand", + "src/hook-floor.test.ts:(anonymous)", + "src/hook-floor.test.ts:(anonymous)", + "src/hooks.test.ts:(anonymous)", + "src/hooks.test.ts:(anonymous)", + "src/hooks.test.ts:(anonymous)", + "src/hooks.test.ts:(anonymous)", + "src/identity.test.ts:(anonymous)", + "src/insight/unused.ts:computeUnused", + "src/install-gate.test.ts:(anonymous)", + "src/install-gate.test.ts:(anonymous)", + "src/install-gate.test.ts:(anonymous)", + "src/install-gate.ts:argStart", + "src/install-gate.ts:pipInstallerArgStart", + "src/install.test.ts:(anonymous)", + "src/install.test.ts:(anonymous)", + "src/keyless/http-sig.test.ts:(anonymous)", + "src/keyless/sign-request.test.ts:(anonymous)", + "src/keyless/sign-request.test.ts:(anonymous)", + "src/keystore/active.test.ts:(anonymous)", + "src/keystore/command-store.test.ts:(anonymous)", + "src/keystore/mandate.test.ts:(anonymous)", + "src/keystore/resolve.test.ts:(anonymous)", + "src/keystore/revoked-guard.test.ts:counting", + "src/keystore/trust-store.test.ts:fakeStore", + "src/kit-wrapper-entry.test.ts:(anonymous)", + "src/kit-wrapper.test.ts:(anonymous)", + "src/kit-wrapper.ts:ensureKitWrapper", + "src/lock.test.ts:(anonymous)", + "src/lock.test.ts:(anonymous)", + "src/login.test.ts:(anonymous)", + "src/login.ts:loginServices", + "src/mcp-dependency-surface.test.ts:(anonymous)", + "src/mcp-orchestrator.test.ts:(anonymous)", + "src/mcp-server.test.ts:(anonymous)", + "src/mcp-server.test.ts:(anonymous)", + "src/mcp-server.test.ts:(anonymous)", + "src/mcp-server.test.ts:(anonymous)", + "src/mcp-server.test.ts:(anonymous)", + "src/mcp-server.test.ts:(anonymous)", + "src/mcp-server.ts:(anonymous)", + "src/mcp-server.ts:(anonymous)", + "src/mcp-server.ts:register_kit_fix", + "src/mcp-server.ts:register_kit_init", + "src/mcp-triage.ts:stableStringify", + "src/memory/amazonq.test.ts:(anonymous)", + "src/memory/codex.ts:indexFile", + "src/memory/db.test.ts:(anonymous)", + "src/memory/db.test.ts:(anonymous)", + "src/memory/db.ts:insertMessage", + "src/memory/db.ts:searchMessages", + "src/memory/db.ts:toFtsMatchQuery", + "src/memory/droid.test.ts:(anonymous)", + "src/memory/droid.ts:indexFile", + "src/memory/hook.test.ts:(anonymous)", + "src/memory/hook.ts:sessionStartRecovery", + "src/memory/injection.test.ts:(anonymous)", + "src/memory/kiro.test.ts:(anonymous)", + "src/memory/learn.test.ts:(anonymous)", + "src/memory/merge.test.ts:(anonymous)", + "src/memory/merge.ts:projectKeyFor", + "src/memory/obsidian.test.ts:mk", + "src/memory/opencode.ts:indexOpenCodeDb", + "src/memory/pal.test.ts:(anonymous)", + "src/memory/pal.ts:palSyncFindings", + "src/memory/parser.test.ts:(anonymous)", + "src/memory/parser.ts:indexFile", + "src/memory/remote-sync.test.ts:(anonymous)", + "src/memory/remote-sync.test.ts:(anonymous)", + "src/memory/remote-sync.test.ts:(anonymous)", + "src/memory/scan.ts:(anonymous)", + "src/memory/shared.test.ts:(anonymous)", + "src/memory/sync.test.ts:(anonymous)", + "src/memory/threads.test.ts:(anonymous)", + "src/memory/write-gate.test.ts:(anonymous)", + "src/memory/write-gate.ts:evaluateWriteGate", + "src/open.test.ts:(anonymous)", + "src/opencli.test.ts:(anonymous)", + "src/output.ts:printSecurityTable", + "src/pkg.test.ts:(anonymous)", + "src/plugin-loader.test.ts:(anonymous)", + "src/plugin-write-gates.test.ts:(anonymous)", + "src/plugins.test.ts:(anonymous)", + "src/policy-check.test.ts:(anonymous)", + "src/policy-check.ts:evaluatePolicy", + "src/policy-doc.ts:validatePolicy", + "src/policy-doc.ts:verifyPolicy", + "src/policy-gate.test.ts:(anonymous)", + "src/policy-gate.test.ts:(anonymous)", + "src/policy-gate.test.ts:(anonymous)", + "src/policy-gate.test.ts:(anonymous)", + "src/policy-trust.test.ts:(anonymous)", + "src/post-pull-audit.test.ts:(anonymous)", + "src/profile/portable.ts:importBundle", + "src/profile/reconcile.test.ts:(anonymous)", + "src/rbac/policy-schema.test.ts:(anonymous)", + "src/rbac/rbac.test.ts:(anonymous)", + "src/rbac/rbac.test.ts:(anonymous)", + "src/rbac/resolve.test.ts:(anonymous)", + "src/read-only-surface.test.ts:(anonymous)", + "src/recommended.ts:recommendPosture", + "src/repomap/ownership.ts:globMatch", + "src/resend-email.test.ts:(anonymous)", + "src/review-cwd.test.ts:(anonymous)", + "src/run.test.ts:(anonymous)", + "src/run.ts:(anonymous)", + "src/run.ts:executeCommand", + "src/scan-diff.ts:classifyChange", + "src/scan-staged.test.ts:(anonymous)", + "src/scanners.ts:isLocalSemgrepConfig", + "src/scanners.ts:verifyAirGapScanners", + "src/scope-needs-adoption.test.ts:extractContextObject", + "src/scope-needs-adoption.test.ts:stripNoise", + "src/secret-backends.test.ts:(anonymous)", + "src/secret-expiration.test.ts:(anonymous)", + "src/secrets-propagate.test.ts:(anonymous)", + "src/secrets-rotate-cli.ts:buildPlaybook", + "src/secrets-rotate-cli.ts:cmdSecretsRotate", + "src/secrets-rotate-cli.ts:cmdSecretsRotateSupabaseMgmt", + "src/secrets-rotate-cli.ts:pickBackendOpts", + "src/secrets-vault-migrate.ts:readSecretFromBackend", + "src/secrets-vault-migrate.ts:vaultMigrate", + "src/secrets.test.ts:(anonymous)", + "src/secrets.ts:generateSecrets", + "src/security-prescan.test.ts:(anonymous)", + "src/security-prescan.test.ts:(anonymous)", + "src/security-prescan.test.ts:(anonymous)", + "src/security-prescan.ts:runPrescan", + "src/self-audit-ci.test.ts:(anonymous)", + "src/self-audit-ci.ts:runCiScriptAudit", + "src/self-audit-docs.test.ts:(anonymous)", + "src/self-audit-docs.test.ts:(anonymous)", + "src/self-audit-docs.test.ts:(anonymous)", + "src/self-audit-docs.ts:extractDocCommandRefs", + "src/self-audit-wiring.test.ts:(anonymous)", + "src/self-audit.test.ts:(anonymous)", + "src/self-audit.ts:run", + "src/self-audit.ts:stripStringsAndComments", + "src/sentinel.ts:parseSuppressions", + "src/service-adapter.test.ts:(anonymous)", + "src/service-adapter.test.ts:(anonymous)", + "src/service-adapter.test.ts:(anonymous)", + "src/service-registry.test.ts:(anonymous)", + "src/skill-run.test.ts:(anonymous)", + "src/skill/attribute.test.ts:(anonymous)", + "src/skill/test.test.ts:(anonymous)", + "src/skill/test.test.ts:(anonymous)", + "src/source-walk.test.ts:(anonymous)", + "src/stack-detector.test.ts:(anonymous)", + "src/stack-detector.ts:resolveNodeVersion", + "src/stack-detector.ts:resolvePythonVersion", + "src/standards-plugins-exec.test.ts:(anonymous)", + "src/standards-plugins.test.ts:(anonymous)", + "src/standards-plugins.ts:checkStandardsPlugins", + "src/standards-plugins.ts:evaluatePlugin", + "src/standards-plugins.ts:loadStandardPlugins", + "src/status.test.ts:(anonymous)", + "src/supply-chain.ts:runSupplyChain", + "src/toml-generator.test.ts:(anonymous)", + "src/toml-generator.test.ts:(anonymous)", + "src/toml-generator.ts:generateToml", + "src/toml-generator.ts:parseEnvTemplateKeys", + "src/toml-generator.ts:secretsSection", + "src/toml-generator.ts:setupSection", + "src/tool-inventory.ts:readToolVersion", + "src/tool-latest.test.ts:deps", + "src/tool-provenance.ts:classifyToolPath", + "src/triage-ecosystem-parity.test.ts:(anonymous)", + "src/triage.test.ts:(anonymous)", + "src/triage.test.ts:(anonymous)", + "src/triage.ts:verdictPassed", + "src/update-check.test.ts:(anonymous)", + "src/usage-prove.ts:controlSecretScanBlocksCommit", + "src/usage-report.test.ts:(anonymous)", + "src/usage-report.ts:coverageFromRuns", + "src/usage-report.ts:keysFromConfigAndRun", + "src/utils/promptMultiSelect.test.ts:(anonymous)", + "src/utils/promptSelect.ts:promptSelect", + "src/utils/shellSplit.ts:shellSplit" + ], + "duplication": [ + ".github/workflows/security.yml|.github/workflows/security.yml", + ".kit.toml|README.md:toml", + "AGENTS.md:markdown|CLAUDE.md:markdown", + "AGENTS.md:markdown|README.md:markdown", + "README.md:markdown|README.md:markdown", + "README.md:markdown|docs/COMMANDS.md:markdown", + "README.md:markdown|docs/adr/0003-core-coverage-isolation.md:markdown", + "README.md:toml|docs/COMMANDS.md:toml", + "contracts/kit.opencli.json|contracts/kit.opencli.json", + "docs/ADAPTER_GUIDE.md:typescript|docs/CUSTOM_ADAPTERS.md:typescript", + "docs/AIR_GAP.md:markdown|docs/AIR_GAP.md:markdown", + "docs/AUDIT_ATTESTATION.md:markdown|docs/CLI_STABILITY.md:markdown", + "docs/AUDIT_ATTESTATION.md:markdown|docs/COMMANDS.md:markdown", + "docs/AUDIT_ATTESTATION.md:markdown|docs/PLATFORM_SUPPORT.md:markdown", + "docs/COMMANDS.md:markdown|docs/COMMANDS.md:markdown", + "docs/CUSTOM_ADAPTERS.md:typescript|docs/PLUGIN_DEVELOPMENT.md:typescript", + "docs/plans/2026-06-21-kit-health-v1a.md:typescript|src/cli-health.test.ts", + "docs/plans/2026-06-21-kit-health-v1a.md:typescript|src/commands/info.ts", + "docs/plans/2026-06-21-kit-health-v1a.md:typescript|src/findings-track.ts", + "docs/plans/2026-06-21-kit-health-v1a.md:typescript|src/health-sensors/github-actions.test.ts", + "docs/plans/2026-06-21-kit-health-v1a.md:typescript|src/health-sensors/github-actions.ts", + "docs/plans/2026-06-21-kit-health-v1a.md:typescript|src/health-track.test.ts", + "docs/plans/2026-06-21-kit-health-v1a.md:typescript|src/health-track.ts", + "docs/plans/2026-06-21-kit-health-v1a.md:typescript|src/health.test.ts", + "docs/plans/2026-06-21-kit-health-v1a.md:typescript|src/health.ts", + "packages/adapter-sdk/CHANGELOG.md:markdown|packages/adapter-sdk/README.md:markdown", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-aisle/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-cloudflare/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-fly/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-github/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-railway/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-sentrux/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-sentry/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-snyk/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-stripe/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-supabase/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-vercel/tsconfig.json", + "packages/adapter-sdk/tsconfig.json|packages/kit-plugin-wiz/tsconfig.json", + "packages/kit-plugin-aisle/package.json|packages/kit-plugin-cloudflare/package.json", + "packages/kit-plugin-aisle/package.json|packages/kit-plugin-fly/package.json", + "packages/kit-plugin-aisle/package.json|packages/kit-plugin-sentrux/package.json", + "packages/kit-plugin-aisle/package.json|packages/kit-plugin-sentry/package.json", + "packages/kit-plugin-aisle/package.json|packages/kit-plugin-stripe/package.json", + "packages/kit-plugin-cloudflare/src/mgmt-api.ts|packages/kit-plugin-fly/src/mgmt-api.ts", + "packages/kit-plugin-cloudflare/src/mgmt-api.ts|packages/kit-plugin-github/src/mgmt-api.ts", + "packages/kit-plugin-cloudflare/src/mgmt-api.ts|packages/kit-plugin-sentry/src/mgmt-api.ts", + "packages/kit-plugin-cloudflare/src/mgmt-api.ts|packages/kit-plugin-stripe/src/mgmt-api.ts", + "packages/kit-plugin-cloudflare/src/mgmt-api.ts|packages/kit-plugin-supabase/src/mgmt-api.ts", + "packages/kit-plugin-cloudflare/src/mgmt-api.ts|packages/kit-plugin-vercel/src/mgmt-api.ts", + "packages/kit-plugin-github/package.json|packages/kit-plugin-railway/package.json", + "packages/kit-plugin-github/package.json|packages/kit-plugin-supabase/package.json", + "packages/kit-plugin-github/package.json|packages/kit-plugin-vercel/package.json", + "packages/kit-plugin-github/src/mgmt-api.ts|packages/kit-plugin-vercel/src/mgmt-api.ts", + "packages/kit-plugin-railway/src/railway-deploy.ts|src/adapters/flyio-hosting.ts", + "packages/kit-plugin-sentrux/package.json|packages/kit-plugin-snyk/package.json", + "packages/kit-plugin-sentrux/package.json|packages/kit-plugin-wiz/package.json", + "packages/kit-plugin-sentrux/src/scan.ts|packages/kit-plugin-snyk/src/scan.ts", + "scripts/gen-opencli.mjs|scripts/gen-public-surface.mjs", + "src/adapters-flyio.test.ts|src/adapters-railway.test.ts", + "src/adapters/flyio-hosting.ts|src/adapters/railway-hosting.ts", + "src/agent-audit.test.ts|src/agent-audit.test.ts", + "src/agent-audit.test.ts|src/ci-audit.test.ts", + "src/agent-audit.ts|src/agent-audit.ts", + "src/agent-config.test.ts|src/agent-config.test.ts", + "src/agent-config.ts|src/agent-config.ts", + "src/analyze.ts|src/analyze.ts", + "src/approval.test.ts|src/approval.test.ts", + "src/audit-anchor-all.test.ts|src/audit-anchor-all.test.ts", + "src/audit-anchor-all.test.ts|src/cli.test.ts", + "src/audit-anchor-all.test.ts|src/flag-surface.test.ts", + "src/audit-anchor-all.test.ts|src/hook-floor.test.ts", + "src/audit-anchor.test.ts|src/audit-anchor.test.ts", + "src/audit-anchor.ts|src/elevation.ts", + "src/audit.test.ts|src/audit.test.ts", + "src/broker/gates.test.ts|src/exec-broker/profile-policy.test.ts", + "src/budget.test.ts|src/budget.test.ts", + "src/budget.test.ts|src/check-hooks.test.ts", + "src/budget.test.ts|src/check-lock.test.ts", + "src/budget.test.ts|src/governance-middleware.test.ts", + "src/check-attestation.test.ts|src/check-attestation.test.ts", + "src/check-client-exposure.test.ts|src/check-nested-projects.test.ts", + "src/check-deploy.test.ts|src/check-deploy.test.ts", + "src/check-design.ts|src/check-design.ts", + "src/check-gitignore.test.ts|src/check-gitignore.test.ts", + "src/check-hooks.test.ts|src/check-hooks.test.ts", + "src/check-hooks.test.ts|src/hooks.test.ts", + "src/check-lock.test.ts|src/check-lock.test.ts", + "src/check-run.ts|src/commands/ci.ts", + "src/check-secrets.ts|src/secret-backends.ts", + "src/check-security-cwd.test.ts|src/review-cwd.test.ts", + "src/check-services.ts|src/login.ts", + "src/check-standards-platform.ts|src/check-standards-specific.ts", + "src/check-standards-specific.ts|src/check-standards-specific.ts", + "src/check-web-search.test.ts|src/check-web-search.test.ts", + "src/check-web-search.ts|src/check-web-search.ts", + "src/ci-audit.test.ts|src/self-audit-ci.test.ts", + "src/cli.test.ts|src/cli.test.ts", + "src/cli.test.ts|src/commands/hooks.test.ts", + "src/commands/agent.ts|src/commands/setup.ts", + "src/commands/agent.ts|src/plugins-cli.ts", + "src/commands/broker.test.ts|src/commands/broker.test.ts", + "src/commands/broker.test.ts|src/commands/browser.test.ts", + "src/commands/broker.test.ts|src/commands/coverage.test.ts", + "src/commands/broker.test.ts|src/commands/profile.test.ts", + "src/commands/broker.test.ts|src/commands/triage.test.ts", + "src/commands/check.ts|src/commands/ci.ts", + "src/commands/check.ts|src/commands/design.ts", + "src/commands/check.ts|src/commands/hooks.ts", + "src/commands/check.ts|src/commands/review.ts", + "src/commands/config.ts|src/commands/config.ts", + "src/commands/design.ts|src/commands/standards.ts", + "src/commands/flag-value-forms.test.ts|src/flag-form-parity.test.ts", + "src/commands/gate.ts|src/commands/gate.ts", + "src/commands/gha-audit.ts|src/commands/info.ts", + "src/commands/hooks.test.ts|src/commands/hooks.test.ts", + "src/commands/info.ts|src/commands/info.ts", + "src/commands/memory.ts|src/commands/memory.ts", + "src/commands/policy.ts|src/commands/profile.ts", + "src/commands/profile.test.ts|src/profile/reconcile.test.ts", + "src/commands/profile.ts|src/commands/profile.ts", + "src/commands/project.ts|src/plugins-cli.ts", + "src/commands/project.ts|src/run.ts", + "src/commands/secrets.ts|src/commands/secrets.ts", + "src/commands/secrets.ts|src/secrets-rotate-cli.ts", + "src/commands/security.ts|src/commands/security.ts", + "src/commands/security.ts|src/commands/setup.ts", + "src/commands/setup.ts|src/commands/upgrade.ts", + "src/commands/setup.ts|src/fix.ts", + "src/commands/tools.test.ts|src/gate-bash-refusal.test.ts", + "src/commands/upgrade.ts|src/mcp-server.ts", + "src/config-migrate-text.test.ts|src/usage-prove.ts", + "src/config-surface.test.ts|src/flag-surface.test.ts", + "src/config-surface.test.ts|src/plugin-registry.generated.test.ts", + "src/cost-monitor.test.ts|src/cost-monitor.test.ts", + "src/database.test.ts|src/database.test.ts", + "src/docs-verify-commands.test.ts|src/publish-workflow.test.ts", + "src/doctor.test.ts|src/doctor.test.ts", + "src/doctor.test.ts|src/policy-pull-rbac.test.ts", + "src/doctor.test.ts|src/policy-pull.test.ts", + "src/doctor.test.ts|src/profile/portable.test.ts", + "src/env-diff.ts|src/env-inspect.ts", + "src/env-diff.ts|src/secrets-migrate.ts", + "src/env-diff.ts|src/secrets-validate.ts", + "src/environment.test.ts|src/environment.test.ts", + "src/exec-broker/broker.test.ts|src/exec-broker/broker.test.ts", + "src/exec-broker/policy-cwd.test.ts|src/exec-broker/policy-cwd.test.ts", + "src/exec-broker/profile-policy.test.ts|src/keyless/sign-request.test.ts", + "src/exec-broker/profile-policy.test.ts|src/profile/sign.test.ts", + "src/exec-broker/scope-needs.test.ts|src/governance-middleware.test.ts", + "src/fix-cwd.test.ts|src/review-cwd.test.ts", + "src/flag-form-parity.test.ts|src/read-only-surface.test.ts", + "src/flag-surface.test.ts|src/hook-floor.test.ts", + "src/flag-surface.ts|src/flag-surface.ts", + "src/governance-middleware.test.ts|src/governance-middleware.test.ts", + "src/governance-middleware.ts|src/governance-middleware.ts", + "src/health-sensors/bitbucket-pipelines.test.ts|src/health-sensors/gitlab-ci.test.ts", + "src/health-sensors/bitbucket-pipelines.test.ts|src/health-sensors/vercel.test.ts", + "src/health-sensors/bitbucket-pipelines.ts|src/health-sensors/gitlab-ci.ts", + "src/health-sensors/github-actions.test.ts|src/health-sensors/github-actions.test.ts", + "src/health-sensors/gitlab-ci.test.ts|src/health-sensors/sentry.test.ts", + "src/health-sensors/resend.test.ts|src/health-sensors/sentry.test.ts", + "src/health-sensors/resend.test.ts|src/health-sensors/supabase-advisor.test.ts", + "src/health-sensors/resend.test.ts|src/health-sensors/vercel.test.ts", + "src/health-sensors/tls-cert.test.ts|src/health.test.ts", + "src/hook-floor.test.ts|src/hook-floor.test.ts", + "src/hooks.test.ts|src/hooks.test.ts", + "src/identity.test.ts|src/keystore/file-store.test.ts", + "src/identity.test.ts|src/policy-doc.test.ts", + "src/install-gate.test.ts|src/install-gate.test.ts", + "src/install-gate.ts|src/install-gate.ts", + "src/install.test.ts|src/install.test.ts", + "src/keyless/sign-request.test.ts|src/keyless/sign-request.test.ts", + "src/keystore/command-store.test.ts|src/keystore/trust-store.test.ts", + "src/keystore/mandate.test.ts|src/keystore/mandate.test.ts", + "src/keystore/revoked-guard.test.ts|src/keystore/trust-store.test.ts", + "src/keystore/secure-enclave-store.ts|src/keystore/tpm-store.ts", + "src/keystore/stubs.test.ts|src/keystore/stubs.test.ts", + "src/lock.test.ts|src/lock.test.ts", + "src/mcp-server.test.ts|src/mcp-server.test.ts", + "src/mcp-server.ts|src/mcp-server.ts", + "src/memory-share-cli.test.ts|src/memory-share-cli.test.ts", + "src/memory/amazonq.test.ts|src/memory/cursor.test.ts", + "src/memory/amazonq.test.ts|src/memory/kiro.test.ts", + "src/memory/amazonq.ts|src/memory/antigravity.ts", + "src/memory/amazonq.ts|src/memory/cursor.ts", + "src/memory/amazonq.ts|src/memory/kiro.ts", + "src/memory/antigravity.test.ts|src/memory/cline.test.ts", + "src/memory/antigravity.test.ts|src/memory/codex.test.ts", + "src/memory/antigravity.test.ts|src/memory/continue.test.ts", + "src/memory/antigravity.test.ts|src/memory/droid.test.ts", + "src/memory/antigravity.test.ts|src/memory/gemini.test.ts", + "src/memory/antigravity.test.ts|src/memory/parser.test.ts", + "src/memory/antigravity.ts|src/memory/cline.ts", + "src/memory/antigravity.ts|src/memory/codex.ts", + "src/memory/backup.test.ts|src/memory/backup.test.ts", + "src/memory/backup.ts|src/memory/backup.ts", + "src/memory/cline.ts|src/memory/continue.ts", + "src/memory/cline.ts|src/memory/gemini.ts", + "src/memory/codex.ts|src/memory/continue.ts", + "src/memory/codex.ts|src/memory/droid.ts", + "src/memory/codex.ts|src/memory/gemini.ts", + "src/memory/codex.ts|src/memory/opencode.ts", + "src/memory/continue.ts|src/memory/gemini.ts", + "src/memory/hook.test.ts|src/memory/hook.test.ts", + "src/memory/hook.test.ts|src/memory/shared.test.ts", + "src/memory/hook.test.ts|src/memory/suggest.test.ts", + "src/memory/install.test.ts|src/memory/install.test.ts", + "src/memory/merge.test.ts|src/memory/merge.test.ts", + "src/memory/opencode.test.ts|src/memory/opencode.test.ts", + "src/memory/pal.test.ts|src/memory/pal.test.ts", + "src/memory/remote-sync.test.ts|src/memory/remote-sync.test.ts", + "src/memory/shared.test.ts|src/memory/shared.test.ts", + "src/memory/sync.test.ts|src/memory/sync.test.ts", + "src/multi-env.test.ts|src/multi-env.test.ts", + "src/opencli.ts|src/public-surface.ts", + "src/pkg.test.ts|src/pkg.test.ts", + "src/plugin-loader.test.ts|src/plugin-loader.test.ts", + "src/plugins-cli.ts|src/plugins-cli.ts", + "src/policy-check.test.ts|src/policy-trust.test.ts", + "src/policy-check.test.ts|src/rbac/rbac.test.ts", + "src/policy-doc.ts|src/profile/sign.ts", + "src/policy-pull-rbac.test.ts|src/policy-pull.test.ts", + "src/policy-pull-rbac.test.ts|src/revocation-pull.test.ts", + "src/profile/reconcile.test.ts|src/profile/reconcile.test.ts", + "src/rbac/providers-cloud.test.ts|src/rbac/providers-cloud.test.ts", + "src/rbac/providers-cloud.test.ts|src/rbac/rbac.test.ts", + "src/rbac/providers-cloud.ts|src/rbac/providers-cloud.ts", + "src/repomap/graph.ts|src/repomap/graph.ts", + "src/scan-build.test.ts|src/scan-build.test.ts", + "src/scan-build.test.ts|src/scan-staged.test.ts", + "src/scan-build.ts|src/scan-transcripts.ts", + "src/scan-staged.test.ts|src/scan-transcripts.test.ts", + "src/scanners.test.ts|src/scanners.test.ts", + "src/secrets-purge-history.test.ts|src/secrets-purge-history.test.ts", + "src/secrets-sync.ts|src/secrets-sync.ts", + "src/security-prescan.test.ts|src/security-prescan.test.ts", + "src/self-audit-ci.test.ts|src/self-audit-ci.test.ts", + "src/self-audit-ci.ts|src/self-audit.ts", + "src/self-audit-docs.test.ts|src/self-audit-docs.test.ts", + "src/self-audit-docs.test.ts|src/self-audit-wiring.test.ts", + "src/self-audit-docs.ts|src/self-audit-docs.ts", + "src/self-audit-docs.ts|src/self-audit-wiring.ts", + "src/self-audit-docs.ts|src/self-audit.ts", + "src/self-audit.test.ts|src/self-audit.test.ts", + "src/self-audit.ts|src/self-audit.ts", + "src/service-adapter.test.ts|src/service-adapter.test.ts", + "src/skill-run.test.ts|src/skill-run.test.ts", + "src/skill/adherence.test.ts|src/skill/adherence.test.ts", + "src/skipped-commits.test.ts|src/skipped-commits.test.ts", + "src/stack-detector.test.ts|src/stack-detector.test.ts", + "src/stack-detector.ts|src/stack-detector.ts", + "src/standards-plugins-exec.test.ts|src/standards-plugins.test.ts", + "src/triage-ecosystem-parity.test.ts|src/triage-repo-host.test.ts", + "src/triage-repo-host.test.ts|src/triage-repo-host.test.ts", + "src/update-check.test.ts|src/update-check.test.ts", + "src/usage-report.test.ts|src/usage-report.test.ts", + "src/usage-report.ts|src/usage-report.ts", + "src/utils/promptMultiSelect.test.ts|src/utils/promptSelect.test.ts", + "templates/bitbucket/bitbucket-pipelines.yml|templates/bitbucket/bitbucket-pipelines.yml", + "templates/github/kit-security.yml|templates/github/kit-security.yml" + ], + "size": [ + "CHANGELOG.md", + "README.md", + "SECURITY-SCANNING.md", + "SECURITY.md", + "contracts/kit.opencli.json", + "docs/ADAPTER_PATTERNS.md", + "docs/PLUGIN_DOCUMENTATION_STANDARDS.md", + "docs/plans/2026-06-21-kit-health-v1a.md", + "skills/triage/scripts/triage.py", + "src/agent-config.test.ts", + "src/agent-config.ts", + "src/audit-anchor.test.ts", + "src/audit-anchor.ts", + "src/audit.test.ts", + "src/audit.ts", + "src/check-security.test.ts", + "src/check-security.ts", + "src/check-standards-specific.ts", + "src/cli.test.ts", + "src/cli.ts", + "src/commands/memory.ts", + "src/commands/secrets.ts", + "src/commands/security.ts", + "src/commands/setup.ts", + "src/commands/triage.ts", + "src/config.ts", + "src/context-lock.ts", + "src/doctor.test.ts", + "src/doctor.ts", + "src/exec-broker/broker.test.ts", + "src/flag-surface.ts", + "src/governance-middleware.test.ts", + "src/hooks.test.ts", + "src/install-gate.test.ts", + "src/install-gate.ts", + "src/mcp-server.test.ts", + "src/mcp-server.ts", + "src/memory/db.test.ts", + "src/memory/db.ts", + "src/memory/shared.test.ts", + "src/policy-gate.test.ts", + "src/secret-backends.ts", + "src/security-prescan.ts", + "src/self-audit-docs.test.ts", + "src/self-audit-docs.ts", + "src/self-audit.test.ts", + "src/self-audit.ts", + "src/stack-detector.test.ts", + "src/stack-detector.ts" + ], + "specific/typescript": [ + "typescript/eslint:examples/cdn-cache-headers.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:examples/cdn-cache-headers.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:examples/cdn-cache-headers.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:examples/cdn-cache-headers.ts#@typescript-eslint/no-require-imports", + "typescript/eslint:examples/mcp-tools-usage.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:examples/mcp-tools-usage.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:examples/mcp-tools-usage.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:examples/mcp-tools-usage.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:examples/team-management-workflows.ts#@typescript-eslint/no-unused-vars", + "typescript/eslint:examples/team-management-workflows.ts#@typescript-eslint/no-unused-vars", + "typescript/eslint:packages/kit-plugin-sentrux/src/scan.ts#preserve-caught-error", + "typescript/eslint:packages/kit-plugin-snyk/src/scan.ts#preserve-caught-error", + "typescript/eslint:packages/kit-plugin-supabase/src/rotate.ts#@typescript-eslint/no-unused-vars", + "typescript/eslint:src/adapters/ingest.ts#complexity", + "typescript/eslint:src/adapters/searxng-instance.ts#max-depth", + "typescript/eslint:src/adapters/searxng-instance.ts#max-depth", + "typescript/eslint:src/adapters/searxng-instance.ts#max-depth", + "typescript/eslint:src/adapters/searxng-instance.ts#max-depth", + "typescript/eslint:src/adapters/searxng-instance.ts#max-depth", + "typescript/eslint:src/adapters/searxng-instance.ts#max-lines-per-function", + "typescript/eslint:src/adapters/vercel-hosting.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/adr.ts#complexity", + "typescript/eslint:src/adr.ts#max-depth", + "typescript/eslint:src/adr.ts#max-depth", + "typescript/eslint:src/adr.ts#max-params", + "typescript/eslint:src/advisory-baseline.ts#complexity", + "typescript/eslint:src/agent-audit.ts#max-params", + "typescript/eslint:src/agent-config.ts#max-lines", + "typescript/eslint:src/audit-anchor-all.ts#complexity", + "typescript/eslint:src/audit-anchor.ts#complexity", + "typescript/eslint:src/audit-anchor.ts#complexity", + "typescript/eslint:src/audit.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/audit.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/browser.ts#max-lines-per-function", + "typescript/eslint:src/check-attestation.ts#max-lines-per-function", + "typescript/eslint:src/check-design.ts#complexity", + "typescript/eslint:src/check-design.ts#max-depth", + "typescript/eslint:src/check-lock.ts#complexity", + "typescript/eslint:src/check-run.ts#complexity", + "typescript/eslint:src/check-secrets.ts#complexity", + "typescript/eslint:src/check-security.ts#complexity", + "typescript/eslint:src/check-security.ts#complexity", + "typescript/eslint:src/check-security.ts#max-depth", + "typescript/eslint:src/check-security.ts#max-lines", + "typescript/eslint:src/check-services.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/check-services.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/check-web-search.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/check-web-search.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/check-web-search.ts#complexity", + "typescript/eslint:src/check-web-search.ts#max-lines-per-function", + "typescript/eslint:src/cli.ts#complexity", + "typescript/eslint:src/cli.ts#max-depth", + "typescript/eslint:src/cli.ts#max-depth", + "typescript/eslint:src/cli.ts#max-lines", + "typescript/eslint:src/cli.ts#max-lines-per-function", + "typescript/eslint:src/cli.ts#max-lines-per-function", + "typescript/eslint:src/commands/agent.ts#complexity", + "typescript/eslint:src/commands/audit.ts#complexity", + "typescript/eslint:src/commands/audit.ts#max-depth", + "typescript/eslint:src/commands/browser.ts#complexity", + "typescript/eslint:src/commands/check.ts#complexity", + "typescript/eslint:src/commands/check.ts#max-lines-per-function", + "typescript/eslint:src/commands/check.ts#max-lines-per-function", + "typescript/eslint:src/commands/ci.ts#max-lines-per-function", + "typescript/eslint:src/commands/ci.ts#max-lines-per-function", + "typescript/eslint:src/commands/config.ts#max-params", + "typescript/eslint:src/commands/context.ts#complexity", + "typescript/eslint:src/commands/coverage.ts#complexity", + "typescript/eslint:src/commands/coverage.ts#complexity", + "typescript/eslint:src/commands/coverage.ts#complexity", + "typescript/eslint:src/commands/hooks.ts#complexity", + "typescript/eslint:src/commands/info.ts#complexity", + "typescript/eslint:src/commands/mcp.ts#complexity", + "typescript/eslint:src/commands/memory.ts#complexity", + "typescript/eslint:src/commands/memory.ts#complexity", + "typescript/eslint:src/commands/memory.ts#max-lines", + "typescript/eslint:src/commands/memory.ts#max-lines-per-function", + "typescript/eslint:src/commands/profile.ts#complexity", + "typescript/eslint:src/commands/scan.ts#complexity", + "typescript/eslint:src/commands/scan.ts#max-lines-per-function", + "typescript/eslint:src/commands/secrets.ts#complexity", + "typescript/eslint:src/commands/secrets.ts#complexity", + "typescript/eslint:src/commands/secrets.ts#complexity", + "typescript/eslint:src/commands/secrets.ts#complexity", + "typescript/eslint:src/commands/secrets.ts#max-lines", + "typescript/eslint:src/commands/secrets.ts#max-lines-per-function", + "typescript/eslint:src/commands/secrets.ts#max-lines-per-function", + "typescript/eslint:src/commands/security.ts#complexity", + "typescript/eslint:src/commands/security.ts#complexity", + "typescript/eslint:src/commands/security.ts#complexity", + "typescript/eslint:src/commands/security.ts#max-lines", + "typescript/eslint:src/commands/security.ts#max-lines-per-function", + "typescript/eslint:src/commands/security.ts#max-lines-per-function", + "typescript/eslint:src/commands/setup.ts#complexity", + "typescript/eslint:src/commands/setup.ts#complexity", + "typescript/eslint:src/commands/setup.ts#max-lines", + "typescript/eslint:src/commands/setup.ts#max-lines-per-function", + "typescript/eslint:src/commands/setup.ts#max-lines-per-function", + "typescript/eslint:src/commands/tools.ts#complexity", + "typescript/eslint:src/commands/triage.ts#complexity", + "typescript/eslint:src/commands/triage.ts#complexity", + "typescript/eslint:src/commands/triage.ts#complexity", + "typescript/eslint:src/commands/triage.ts#max-lines-per-function", + "typescript/eslint:src/commands/usage.test.ts:46", + "typescript/eslint:src/commands/usage.ts:41", + "typescript/eslint:src/config.ts#max-lines", + "typescript/eslint:src/context-lock.ts#complexity", + "typescript/eslint:src/database.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/environment.ts#complexity", + "typescript/eslint:src/fix.ts#complexity", + "typescript/eslint:src/fix.ts#complexity", + "typescript/eslint:src/fix.ts#max-lines-per-function", + "typescript/eslint:src/fix.ts#max-lines-per-function", + "typescript/eslint:src/fix.ts#max-lines-per-function", + "typescript/eslint:src/gha-audit.ts#no-useless-escape", + "typescript/eslint:src/governance-middleware.ts#complexity", + "typescript/eslint:src/governance-middleware.ts#complexity", + "typescript/eslint:src/governance-middleware.ts#max-lines-per-function", + "typescript/eslint:src/heal.ts#max-depth", + "typescript/eslint:src/install-gate.ts#complexity", + "typescript/eslint:src/install-gate.ts#max-lines", + "typescript/eslint:src/mcp-server.ts#max-lines", + "typescript/eslint:src/memory/hook.ts#complexity", + "typescript/eslint:src/memory/merge.ts#complexity", + "typescript/eslint:src/memory/merge.ts#max-lines-per-function", + "typescript/eslint:src/memory/opencode.ts#max-params", + "typescript/eslint:src/memory/parser.ts#complexity", + "typescript/eslint:src/memory/scan.ts#max-params", + "typescript/eslint:src/output.ts#complexity", + "typescript/eslint:src/output.ts#complexity", + "typescript/eslint:src/plugins-cli.ts#complexity", + "typescript/eslint:src/plugins-cli.ts#max-lines-per-function", + "typescript/eslint:src/policy-check.ts#complexity", + "typescript/eslint:src/provision.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/scan-diff.ts#complexity", + "typescript/eslint:src/secrets-rotate-cli.ts#complexity", + "typescript/eslint:src/secrets-rotate-cli.ts#complexity", + "typescript/eslint:src/secrets-rotate-cli.ts#complexity", + "typescript/eslint:src/secrets-rotate-cli.ts#max-lines-per-function", + "typescript/eslint:src/secrets-rotate-cli.ts#max-lines-per-function", + "typescript/eslint:src/secrets-sync.ts#max-params", + "typescript/eslint:src/secrets-validate.ts#complexity", + "typescript/eslint:src/secrets-vault-migrate.ts#complexity", + "typescript/eslint:src/secrets.test.ts#@typescript-eslint/no-explicit-any", + "typescript/eslint:src/self-audit.ts#max-depth", + "typescript/eslint:src/self-audit.ts#max-depth", + "typescript/eslint:src/self-audit.ts#max-depth", + "typescript/eslint:src/self-audit.ts#max-depth", + "typescript/eslint:src/self-audit.ts#max-lines", + "typescript/eslint:src/service-registry.ts#complexity", + "typescript/eslint:src/standards-plugins-exec.ts#max-depth", + "typescript/eslint:src/standards-plugins.ts#complexity", + "typescript/eslint:src/standards-plugins.ts#complexity", + "typescript/eslint:src/standards-run.ts#complexity", + "typescript/eslint:src/status.ts#complexity", + "typescript/eslint:src/toml-generator.ts#complexity", + "typescript/eslint:src/tool-provenance.ts#complexity", + "typescript/eslint:src/usage-report.ts#complexity", + "typescript/eslint:src/utils/shellSplit.ts#complexity" + ] } }, - "generated": "2026-07-31T22:34:41.913Z", + "generated": "2026-08-30T09:57:43.077Z", "version": 1 } diff --git a/.kit/shared/memory.jsonl b/.kit/shared/memory.jsonl index a5bfd262..53660ac8 100644 --- a/.kit/shared/memory.jsonl +++ b/.kit/shared/memory.jsonl @@ -46,3 +46,4 @@ {"id":"f47c5a","area":"cli","kind":"convention","title":"A gate that exists but is never invoked is the default failure, not the exception","body":"kit adopted its own ADR gate in #403 and no workflow, hook or agent instruction ever called it — armed and unfired for a month, while the rules provably caught violations. A gate nobody runs emits nothing, and nothing reads exactly like a clean run. self-audit-ci already proves every script a workflow points AT exists; the inverse (a gate that exists is pointed at by something) had no rule. When adding a gate to this repo, wire the invocation AND pin it with a test that strips comments and forbids continue-on-error / || true — a gate named in a comment is not a gate, and one that cannot fail the build is a report. General case tracked in #533.","refs":[],"author":"Peter Sandström ","ts":"2026-08-25T12:06:22.827Z","source_ref":"a49e85c","kid":"kid_31cd7bffcece85256671f0fced3b42df","sig":"CNhZy+Of23FnzPSF6O95FvxAriwbLWBumewW/9QtgPN606vE4IiidXydMmKQj7JyfBJPOWRBCZM1c7QoSIr4Dw=="} {"id":"d1814d","area":"cli","kind":"decision","title":"pre-commit excludes full npm test until suite timeouts are fixed","body":"kit-public uses externally managed .githooks. [hooks].pre-commit should require staged security scan + build, not full npm test: a real pre-commit run on 2026-08-27 hit Node test file timeouts in dist/policy-gate.test.js and dist/secrets-propagate.test.js. Re-add full npm test only after those suite timeouts are fixed or the suite is split for hook use.","refs":[],"author":"Peter Sandström ","ts":"2026-08-27T09:22:36.795Z","source_ref":"3cfa838","kid":"kid_31cd7bffcece85256671f0fced3b42df","sig":"bbdKUzm6w6dIFEacsfFMdkuLhhwBjZ732zvs08X0ZPlv6NQ7J3CgDJ2GpZhf+M8k7Yff8gvlpPej4b+sYHWOBA=="} {"id":"f57f88","area":"ops","kind":"convention","title":"post-merge health loop","body":"After merge, release, deploy, or tag push, do not rely on email or GitHub check conclusions alone. Run the connected health inbox command, treat red and unknown as not green, inspect each connected source, and keep acting or waiting until every connected sensor is green before telling the user the arc is closed. For this repo before a release is published, run the local built CLI via node dist/cli.js health.","refs":[],"author":"Peter Sandström ","ts":"2026-08-28T14:47:56.828Z","source_ref":"cc8e4a0","kid":"kid_31cd7bffcece85256671f0fced3b42df","sig":"f2AEPcCAs2OVwuS6fbIm2JZG7UMoUZN4+54DI4V2b9Fmc6pDCfLkCkqnmH1/51XQd8431xlVfhrkw5mQkuuCDw=="} +{"id":"0ed015","area":"cli","kind":"decision","title":"A linter that lints skills must pass its own lint, and something must run it","body":"kit shipped a skill linter with a working --gate (exit 1) that nothing ever invoked -- not CI, not kit review, not verify-suite.sh -- while kit's only shipped SKILL.md failed that linter's scope check for as long as the linter had existed (no allowed-tools, so the skill implicitly claimed every tool). Fixed in d64c58e: the skill declares allowed-tools: Bash with its surface pinned in a snapshot, src/skill-run.ts gates every SKILL.md under skills/ and .claude/skills/, kit review gained a fifth stage, and ci.yml runs it as a hard failure with ci-adr-gate.test.ts pinning the invocation. Written as derived, not operator: an agent derived this from measurement, no human stated it. Second instance of the repo's own convention that a gate which exists but is never invoked is the default failure -- the first was the ADR gate in #403, unfired for a month.","refs":["d64c58e"],"author":"Claude ","ts":"2026-08-30T06:59:28.396Z","source_ref":"81fabd7","provenance":"derived","confidence":"high"} diff --git a/AGENTS.md b/AGENTS.md index 34caf471..24fb572b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,12 +17,21 @@ This repo is managed by [kit](https://github.com/sandstream/kit) (env, secrets, ## Architecture decisions are a gate, and `kit check` does not run it -`docs/adr` holds five ADRs. The three accepted ones carry a `kit-enforce` block, which -makes them four deterministic rules — not prose: +`docs/adr` holds seven ADRs. The four accepted-and-enforced ones carry a `kit-enforce` +block, which makes them five deterministic rules — not prose: - **ADR-0001** no model-client import anywhere in `src/**` (the zero-LLM core). - **ADR-0002** no new runtime dependency from the forbidden list — stdlib otherwise. + The *floor itself* (exactly four runtime deps, all pinned) is **not** expressible in the + `kit-enforce` grammar and is enforced by `src/dependency-floor.test.ts`; the ADR says why. - **ADR-0003** the check path imports no coverage-framework mappings. +- **ADR-0006** `src/utils/**` imports nothing from the repo — derived by `kit adr derive` + from an asymmetry the code had obeyed for months, then accepted. + +Three more are accepted but **documented, not enforced** — ADR-0004 (workflow skills live +above kit), ADR-0005 (browser substrate), and **ADR-0007** (kit may measure model + kit +residual risk, but owns only the rig — the frozen input, ingest schema, deterministic +adjudication and receipts — never the judgement). `node dist/cli.js adr check` runs them and **fails CI hard** on a violation. `kit check` does **not** include the ADR stage — only `kit review` (check + design + standards + adr + skill) @@ -31,3 +40,11 @@ does. So before opening a PR that adds a dependency, moves an import, or touches Adding one of those imports is an ADR-level decision, not a code change: amend or supersede the ADR in the same PR, or the gate will refuse the code and cite the ADR that refused it. + +**When a decision does not fit the `kit-enforce` grammar, say where it lives instead of +dropping it.** Three limits are measured and will bite again: `paths` has no negation, +`forbid_pattern`/`require_pattern` match line by line, and the import extractor is +text-level (a fixture string that looks like an import counts as one). An ADR in that +position declares `enforced_by: [src/x.test.ts]` in its frontmatter — `adr list` prints it +and `adr check` fails if the file is missing. Silently declaring more than you enforce is +the failure this exists to prevent; ADR-0002 sat that way for months. diff --git a/CLAUDE.md b/CLAUDE.md index c26b8c72..3ebe0e94 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -30,12 +30,21 @@ This repo is managed by [kit](https://github.com/sandstream/kit) (env, secrets, ## Architecture decisions are a gate, and `kit check` does not run it -`docs/adr` holds five ADRs. The three accepted ones carry a `kit-enforce` block, which -makes them four deterministic rules — not prose: +`docs/adr` holds seven ADRs. The four accepted-and-enforced ones carry a `kit-enforce` +block, which makes them five deterministic rules — not prose: - **ADR-0001** no model-client import anywhere in `src/**` (the zero-LLM core). - **ADR-0002** no new runtime dependency from the forbidden list — stdlib otherwise. + The *floor itself* (exactly four runtime deps, all pinned) is **not** expressible in the + `kit-enforce` grammar and is enforced by `src/dependency-floor.test.ts`; the ADR says why. - **ADR-0003** the check path imports no coverage-framework mappings. +- **ADR-0006** `src/utils/**` imports nothing from the repo — derived by `kit adr derive` + from an asymmetry the code had obeyed for months, then accepted. + +Three more are accepted but **documented, not enforced** — ADR-0004 (workflow skills live +above kit), ADR-0005 (browser substrate), and **ADR-0007** (kit may measure model + kit +residual risk, but owns only the rig — the frozen input, ingest schema, deterministic +adjudication and receipts — never the judgement). `node dist/cli.js adr check` runs them and **fails CI hard** on a violation. `kit check` does **not** include the ADR stage — only `kit review` (check + design + standards + adr + skill) @@ -44,3 +53,11 @@ does. So before opening a PR that adds a dependency, moves an import, or touches Adding one of those imports is an ADR-level decision, not a code change: amend or supersede the ADR in the same PR, or the gate will refuse the code and cite the ADR that refused it. + +**When a decision does not fit the `kit-enforce` grammar, say where it lives instead of +dropping it.** Three limits are measured and will bite again: `paths` has no negation, +`forbid_pattern`/`require_pattern` match line by line, and the import extractor is +text-level (a fixture string that looks like an import counts as one). An ADR in that +position declares `enforced_by: [src/x.test.ts]` in its frontmatter — `adr list` prints it +and `adr check` fails if the file is missing. Silently declaring more than you enforce is +the failure this exists to prevent; ADR-0002 sat that way for months. diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index 460dba1f..0d190486 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -101,7 +101,7 @@ port = 3107 | `kit standards [--category general\|specific\|plugins\|platform\|] [--enforce]` | Dev-standards gate: general metrics (complexity/duplication/size via lizard/jscpd/scc) + per-language linters (11 langs) + user plugins (`.kit/standards.d/`) + container (hadolint). Warn by default; `--enforce` fails net-new findings AND setup gaps. | | `kit standards freeze` | Snapshot only the standards dimensions into `.kit-baseline.json`. | | `kit review` | Meta-runner — `check + design + standards + adr + skill` gate for PR. The `skill` stage runs module discipline over every shipped `SKILL.md` (contract, trigger collision, bounded tool scope, snapshot drift); a repo with no skills skips honestly. | -| `kit adr [check\|list\|freeze\|derive]` | ADR → gate: enforce accepted ADRs' `kit-enforce` rules (`forbid_pattern` / `require_pattern` / `forbid_import`, incl. transitive and cross-package via `follow_packages`), cited to the ADR. `list` shows status; `freeze` baselines existing findings; `derive` proposes ADRs the code already obeys (absent import edges with a populated reverse), each re-run through the real evaluator before it is shown and emitted as `status: proposed` so it gates nothing until a human accepts it. Zero-LLM (prose is never interpreted). | +| `kit adr [check\|list\|freeze\|derive]` | ADR → gate: enforce accepted ADRs' `kit-enforce` rules (`forbid_pattern` / `require_pattern` / `forbid_import`, incl. transitive and cross-package via `follow_packages`), cited to the ADR. `list` shows status plus any `enforced_by:` frontmatter pointing at enforcement the grammar cannot express (verified to exist by `check`, so it cannot rot); `freeze` baselines existing findings; `derive` proposes ADRs the code already obeys (absent import edges with a populated reverse), each re-run through the real evaluator before it is shown and emitted as `status: proposed` so it gates nothing until a human accepts it. Zero-LLM (prose is never interpreted). | | `kit baseline [freeze]` | Snapshot current acceptable warnings (incl. standards + ADR) to `.kit-baseline.json`. | | `kit analyze [--write]` | Mine git history + framework markers → draft `CLAUDE.md` / `RULES.md`. | diff --git a/docs/adr/0002-dependency-floor.md b/docs/adr/0002-dependency-floor.md index ea278d7d..bf9dd025 100644 --- a/docs/adr/0002-dependency-floor.md +++ b/docs/adr/0002-dependency-floor.md @@ -2,6 +2,7 @@ id: ADR-0002 title: Dependency floor — four runtime deps, stdlib otherwise status: accepted +enforced_by: [src/dependency-floor.test.ts] --- # ADR-0002: Dependency floor @@ -20,6 +21,41 @@ Adding a runtime dependency is an ADR-level decision, not a convenience call. The rule below blocks the common utility imports outright; anything else new must argue its case in a PR that updates this ADR. +## Where the floor is actually enforced — and why not here + +**Amended 2026-08-29.** This ADR declared more than its `kit-enforce` block enforced, and +the gap was found the only way such gaps are found: by breaking the rule and watching the +gate stay green. `kit pkg npm:jscpd` added a fifth runtime dependency to `package.json`, +and `kit adr check` reported `✓ 4 enforced ADR(s) — no new violations`. + +The block below is a **deny-list of twelve named packages, matched at the import level in +`src/**`**. It cannot see a dependency that is not on the list, and it cannot see one that +has been added to the manifest but not yet imported. The title says "four runtime deps"; +the rule says "not these twelve". + +The floor is **not expressible in the `kit-enforce` grammar**, for two structural reasons: + +1. `package.json` is not in the file set the ADR gate walks — `CODE_EXTS` in + `src/commands/adr.ts` lists source extensions only, so no rule here can ever apply to a + manifest. +2. `forbid_pattern` and `require_pattern` are matched **line by line** + (`firstMatchingLine` splits on newlines). An entry in `dependencies` is textually + identical to one in `devDependencies`, so a line-based regex cannot tell them apart, and + a multi-line pattern pinning the whole block cannot match at all. + +Widening the walk and adding block-aware matching to serve one rule is a larger change than +the rule is worth. So the floor is enforced by **`src/dependency-floor.test.ts`**, which +asserts the exact runtime dependency set and that every version is pinned rather than a +range. It runs in the same CI job as everything else and fails the moment either claim +stops being true. + +The `kit-enforce` block stays: it catches the utility-import case earlier and more cheaply, +in review rather than in the suite. It is a first line, not the line. + +**A dev tool kit shells out to — a scanner, a linter — is not a runtime dependency.** It is +installed as a tool and must never be added to `dependencies`; that mistake is what exposed +this gap. + ```toml kit-enforce [[forbid_import]] import = "^(lodash|lodash-es|lodash\\.|underscore|ramda|axios|node-fetch|request|moment|dayjs|bluebird|jquery)" diff --git a/docs/adr/0006-utils-is-a-leaf.md b/docs/adr/0006-utils-is-a-leaf.md new file mode 100644 index 00000000..08a4327c --- /dev/null +++ b/docs/adr/0006-utils-is-a-leaf.md @@ -0,0 +1,77 @@ +--- +id: ADR-0006 +title: src/utils is a leaf — it depends on nothing in the repo +status: accepted +--- + +# ADR-0006: src/utils is a leaf + +## Decision + +No file under `src/utils/**` imports anything else in this repository. It may use Node +builtins and declared dependencies; it may not reach into `src/` — not a sibling +subsystem, not the root, not the command layer. + +## Rationale + +This was not decided in a meeting. It was **derived from the code by `kit adr derive`** +and then confirmed by measurement, which is the point: a constraint the whole repo has +obeyed is a decision whether or not anyone wrote it down. + +Measured on the import graph at the time of writing: + +| direction | edges | +|---|---| +| `commands` → `utils` | 113 distinct file pairs | +| `(root)` → `utils` | 86 | +| `utils` → **anything in `src/`** | **0** | + +`kit adr derive` proposed three separate candidates from that asymmetry (`utils → +commands` at 113, `utils → adapters` at 8, `utils → memory` at 5). They are one rule: +`utils` imports **nothing**. Enforcing the general form is simpler than enforcing three +special cases, and it covers a subsystem that does not exist yet. + +Why it matters beyond tidiness: `utils` is the most fanned-in directory in the repo. +Anything it imports is imported, transitively, by almost everything. A single upward +import from `utils` into a subsystem creates a cycle through the majority of the tree and +makes the affected modules untestable in isolation. Keeping the bottom of the stack at +the bottom is what makes the layers above it movable. + +## Consequences + +- A helper in `utils` that needs a subsystem's type or function does not belong in + `utils`. Move it to the subsystem, or push the dependency the other way (pass the value + in rather than reaching for it). +- The rule is stated as "no parent-relative import", which is broader than "no sibling + subsystem". That is deliberate: `../` from `src/utils/**` can only ever leave `utils`, + so the broad form has no false positives and needs no maintenance when a directory is + added. +- Superseding this is an ADR-level act. If `utils` genuinely needs to depend on something, + amend or supersede this file in the same PR — the gate will otherwise refuse the code + and cite this ADR. + +## Scope of the evidence, stated honestly + +This records that the repository behaves as if the decision were made, and that we have +now made it. It is a snapshot of the import graph, not a reconstruction of anyone's +intent — see `kit adr derive`'s own limits (TS/JS + Python relative imports, one source +root, top-level buckets). + +Two related candidates were deliberately **not** accepted: + +- **"No subsystem imports the command layer"** — true of all 16 subsystems, not just the + six above the evidence floor, but the general form (`paths = "src/*/**"`) fires on + `src/commands/adr-derive.test.ts`, where a test fixture *string* containing + `import "../commands/x.js"` is read as a real import by the text-level extractor. The + glob grammar has no negation, so "every subsystem except `src/commands/**`" cannot be + expressed today. Left underived rather than encoded wrong. +- **`profile` does not import `exec-broker`** (support 8) — a lone pair with no wider + pattern behind it. Insufficient evidence that it is a decision rather than an ordering + accident. + +```toml kit-enforce +[[forbid_import]] +import = "^\\.\\./" +paths = "src/utils/**" +message = "src/utils is a leaf (ADR-0006) — it imports nothing from the rest of the repo; move the helper to the subsystem, or pass the value in" +``` diff --git a/docs/adr/0007-kit-owns-the-rig-never-the-judgement.md b/docs/adr/0007-kit-owns-the-rig-never-the-judgement.md new file mode 100644 index 00000000..5b2d32e0 --- /dev/null +++ b/docs/adr/0007-kit-owns-the-rig-never-the-judgement.md @@ -0,0 +1,114 @@ +--- +id: ADR-0007 +title: kit owns the rig, never the judgement — measuring model + kit residual risk +status: accepted +--- + +# ADR-0007: kit owns the rig, never the judgement + +## Decision + +kit may measure how well a model performs **together with kit's gates**, and may publish +that measurement. Specifically, kit owns: + +- the **frozen input** — a pinned commit plus a pinned prompt/rubric, content-hashed, so + two runs are answering the same question; +- the **ingest schema** — a model's findings arrive as structured rows (file, line, claim, + severity) attributed to model, version and date; +- the **deterministic adjudication** — whether a claimed defect is real is decided by the + repository (test suite, mutation harness, broker denials), never by a model; +- the **arithmetic** — overlap, divergence, caught/missed counts, drift across runs; +- the **receipts** — the same signed, replayable evidence trail every other kit verdict + carries. + +kit does **not** produce a review, rank models by preference, score a judgement's quality, +or call a model at any point. The models run outside kit and hand their output in. + +## Rationale — this is about kit's own residual risk, not benchmarking + +The earlier framing was that model-vs-model comparison is mush because the judge is +another model. True, but it made this look like a nice-to-have. The real motive is +narrower and more uncomfortable. + +**kit's green is a claim about kit's floor, and readers take it as a claim about the +work.** Measured on this repo: `kit check` runs 42 checks, of which **41 inspect the code +and 1 executes it**. kit lives almost entirely in the static tier. + +Multi-tier verification research ([arXiv:2607.00107](https://arxiv.org/abs/2607.00107) — +8,918 C++ programs across 851 tasks, four tiers, three models plus human-authored code) +found: + +> *"AI-generated code is roughly twice as likely as human code to trigger a confirmed +> runtime violation, even after controlling for code length and test pass-rate."* + +and, critically, that under **static analysis the two appear equally safe** — a similarity +the authors call misleading. The tiers detect largely different classes of violation; no +single tier suffices. + +So on a weak model, `kit check` passing says approximately what it would have said about +human-written code. That is not a false verdict, it is an **uninformative** one presented +in the same colour as an informative one. Without a residual-risk number we cannot say +what our own green is worth — and a governance tool that cannot say that is asserting +trust it has not earned. + +## Why this does not break ADR-0001 + +The zero-LLM contract forbids a model call **in the gate loop**, because that would make +the verdict probabilistic, add egress to the trust boundary, and put a prompt-injection +surface inside the security tool. None of that applies here: + +| step | actor | deterministic? | +|---|---|---| +| produce the work | the model, outside kit | no — and it is not a verdict | +| run the gates | kit | yes | +| decide whether a finding is real | the **repository** | yes | +| count, difference, report | kit | yes | + +No SDK import in `src/**` — which ADR-0001's `forbid_import` rule already enforces, and +which is the mechanical half of this decision. No model judgement decides a pass or fail. +The output is reproducible from the frozen input. + +**The precedent is already shipped.** `kit triage` scores a third-party package +deterministically, publishes a claim about code kit does not own, and gates an install on +it — failing closed when it cannot verify rather than passing on thin evidence. This is +`kit triage` pointed at the agent instead of the package. + +## Relationship to ADR-0004 + +ADR-0004 rules that model-shaped work — grilling, specs, review framing — lives above +kit, and that a feature needing a model judgement to decide pass/fail stays out. This ADR +does not weaken that. It draws the line one notch more precisely: + +> **Producing or grading a judgement is model-shaped and stays out. Framing the question, +> receiving the answer, and adjudicating it against the repository is substrate, and is +> kit's.** + +If those two ever conflict in practice, ADR-0004 wins and the rig is wrong. + +## Consequences — three limits that ship with the number, not with its documentation + +Any residual-risk figure kit emits must carry these in its own output. Without them the +measurement becomes the false comfort it was built to remove. + +1. **Lower bound only.** You can measure only what you can seed. Defects nobody thought to + inject are invisible. The number is a floor on risk, never a ceiling, and must never be + phrased as a safety score. +2. **Goodhart.** The moment a defect corpus exists it becomes the thing that gets + optimised against. It has to rotate, and it must never be published as a benchmark. +3. **Transfer is unproven.** The 2× above is C++ competitive programming. The direction + transfers — static analysis cannot see what execution can — the constant does not, and + quoting it as kit's own figure would be borrowed precision. + +Also: **`kit check` now states its scope next to its verdict** (`tierNotice`, +`src/cli-checks-shared.ts`), counted from the checks that ran rather than asserted, so the +narrowness above is visible without needing this rig at all. That was the cheap half of +the fix and it shipped first. + +## Status of enforcement + +This ADR carries **no `kit-enforce` block**, and that is deliberate rather than an +oversight: its mechanical half is already ADR-0001's `forbid_import` over `src/**`, and +duplicating it here would give two rules that can drift apart. What this ADR adds is a +boundary that a human applies in review — which is why it is listed as *documented, not +enforced*, and why building the rig before this file existed would have been the erosion +it guards against. diff --git a/examples/cdn-cache-headers.ts b/examples/cdn-cache-headers.ts index 85c64d44..9835a252 100644 --- a/examples/cdn-cache-headers.ts +++ b/examples/cdn-cache-headers.ts @@ -5,6 +5,7 @@ * to optimize CloudFront caching behavior. */ +import { createHash } from "node:crypto"; import { NextRequest, NextResponse } from "next/server"; // ─── Public API Endpoints (Cacheable) ─────────────────────────────────── @@ -232,7 +233,7 @@ export async function refreshToken(req: NextRequest) { // ─── Utility Functions ──────────────────────────────────────────────── function generateETag(data: any): string { - const hash = require("crypto").createHash("md5").update(JSON.stringify(data)).digest("hex"); + const hash = createHash("md5").update(JSON.stringify(data)).digest("hex"); return `"${hash}"`; } diff --git a/examples/team-management-workflows.ts b/examples/team-management-workflows.ts index 75d21ea0..f8ca610d 100644 --- a/examples/team-management-workflows.ts +++ b/examples/team-management-workflows.ts @@ -10,7 +10,6 @@ import { addTeamMember, removeTeamMember, updateMemberRole, - getTeam, listTeamMembers, grantPermission, revokePermission, @@ -43,7 +42,7 @@ async function createTeamWithMembers() { // Add members const members = ["user-003", "user-004", "user-005"]; members.forEach((userId) => { - const result = addTeamMember(team.team.id, userId, "member"); + addTeamMember(team.team.id, userId, "member"); console.log(`Member added: ${userId}`); }); diff --git a/packages/kit-plugin-sentrux/src/scan.ts b/packages/kit-plugin-sentrux/src/scan.ts index 2a346cd8..3c46a63e 100644 --- a/packages/kit-plugin-sentrux/src/scan.ts +++ b/packages/kit-plugin-sentrux/src/scan.ts @@ -61,7 +61,11 @@ export function parseSentruxJson(text: string): SentruxResult { try { parsed = JSON.parse(text); } catch (err) { - throw new Error(`Invalid Sentrux JSON: ${err instanceof Error ? err.message : err}`); + // `cause` keeps the parser's own diagnostic (offset, token) attached: a scanner that + // loses WHY it could not read its input turns "cannot verify" into an opaque failure. + throw new Error(`Invalid Sentrux JSON: ${err instanceof Error ? err.message : err}`, { + cause: err, + }); } if (!parsed || typeof parsed !== "object") { return { gatePassed: true, metrics: {}, violations: [] }; diff --git a/packages/kit-plugin-snyk/src/scan.ts b/packages/kit-plugin-snyk/src/scan.ts index a9f6df71..4ac2e9ae 100644 --- a/packages/kit-plugin-snyk/src/scan.ts +++ b/packages/kit-plugin-snyk/src/scan.ts @@ -55,7 +55,11 @@ export function parseSnykJson(text: string): SnykResult[] { try { parsed = JSON.parse(text); } catch (err) { - throw new Error(`Invalid Snyk JSON: ${err instanceof Error ? err.message : err}`); + // See sentrux/scan.ts: the underlying parse error is the only thing that says WHERE + // the output went wrong, and a scanner must not discard it. + throw new Error(`Invalid Snyk JSON: ${err instanceof Error ? err.message : err}`, { + cause: err, + }); } // multi-project: Snyk returns an array of result objects. if (Array.isArray(parsed)) { diff --git a/packages/kit-plugin-supabase/src/rotate.ts b/packages/kit-plugin-supabase/src/rotate.ts index fd22cae6..b43d4977 100644 --- a/packages/kit-plugin-supabase/src/rotate.ts +++ b/packages/kit-plugin-supabase/src/rotate.ts @@ -12,7 +12,6 @@ import { makeClient, rollJwtSecret, mintScopedKey, - listApiKeys, detectKeyMode, type MgmtClient, type RotateMode, diff --git a/src/adr.test.ts b/src/adr.test.ts index 592c777b..12442cdf 100644 --- a/src/adr.test.ts +++ b/src/adr.test.ts @@ -463,3 +463,32 @@ describe("resolveRelative", () => { assert.equal(resolveRelative("src/web/h.ts", "./nope.js", set), null); }); }); + +describe("parseAdr — enforced_by", () => { + const withFrontmatter = (extra: string): string => + `---\nid: ADR-0099\ntitle: T\nstatus: accepted\n${extra}---\n\n# body\n`; + + it("parses an inline list", () => { + const adr = parseAdr(withFrontmatter("enforced_by: [src/a.test.ts, src/b.test.ts]\n")); + assert.deepEqual(adr?.enforcedBy, ["src/a.test.ts", "src/b.test.ts"]); + }); + + it("parses a block list", () => { + const adr = parseAdr(withFrontmatter("enforced_by:\n - src/a.test.ts\n - src/b.test.ts\n")); + assert.deepEqual(adr?.enforcedBy, ["src/a.test.ts", "src/b.test.ts"]); + }); + + it("strips quotes", () => { + const adr = parseAdr(withFrontmatter(`enforced_by: ["src/a.test.ts"]\n`)); + assert.deepEqual(adr?.enforcedBy, ["src/a.test.ts"]); + }); + + it("is an empty list when absent — the field is optional, never undefined", () => { + // Callers iterate it directly; an undefined here would be a crash at the call site. + assert.deepEqual(parseAdr(withFrontmatter(""))?.enforcedBy, []); + }); + + it("yields nothing rather than a guess for a shape it does not understand", () => { + assert.deepEqual(parseAdr(withFrontmatter("enforced_by: src/a.test.ts\n"))?.enforcedBy, []); + }); +}); diff --git a/src/adr.ts b/src/adr.ts index 8172e32d..000d97a3 100644 --- a/src/adr.ts +++ b/src/adr.ts @@ -66,6 +66,21 @@ export interface Adr { rules: AdrRule[]; /** True when a ```toml kit-enforce block was present (even if it parsed to zero rules). */ hasEnforceBlock: boolean; + /** + * Repo-relative paths that enforce this ADR's claim OUTSIDE the `kit-enforce` block. + * + * Some decisions are not expressible in the block's grammar — measured cases: `paths` + * has no negation, `forbid_pattern`/`require_pattern` match line by line so a JSON + * block cannot be pinned, and the manifest is not in the walked file set at all. Before + * this field the only options were to encode the rule wrong or to leave the ADR + * declaring more than it enforced, silently. ADR-0002 sat in the second state for + * months: titled "four runtime deps", enforcing "not these twelve imports". + * + * Naming the real enforcement point turns that silence into a claim, and a claim can be + * checked — `adr check` fails when a path listed here does not exist, so the pointer + * cannot rot into a lie. + */ + enforcedBy: string[]; } export interface AdrViolation { @@ -91,6 +106,40 @@ function scalar(frontmatter: string, key: string): string | undefined { return m ? m[1].trim().replace(/^["']|["']$/g, "") : undefined; } +/** + * A frontmatter list, in either shape: + * + * enforced_by: [src/a.test.ts, src/b.test.ts] + * enforced_by: + * - src/a.test.ts + * - src/b.test.ts + * + * Deliberately tiny: this is not a YAML parser, it is two shapes an ADR author writes by + * hand. Anything else yields an empty list rather than a guess. + */ +function list(frontmatter: string, key: string): string[] { + const inline = frontmatter.match(new RegExp(`^${key}:[ \\t]*\\[(.*)\\][ \\t]*$`, "mi")); + if (inline) { + return inline[1] + .split(",") + .map((v) => v.trim().replace(/^["']|["']$/g, "")) + .filter(Boolean); + } + const blocked = frontmatter.match( + new RegExp(`^${key}:[ \\t]*\\n((?:[ \\t]*-[^\\n]*\\n?)+)`, "mi"), + ); + if (!blocked) return []; + return blocked[1] + .split("\n") + .map((line) => + line + .replace(/^[ \t]*-[ \t]*/, "") + .trim() + .replace(/^["']|["']$/g, ""), + ) + .filter(Boolean); +} + function str(o: Record, k: string): string | undefined { return typeof o[k] === "string" ? (o[k] as string) : undefined; } @@ -112,6 +161,7 @@ export function parseAdr(raw: string): Adr | null { const status: AdrStatus = (STATUSES as string[]).includes(rawStatus) ? (rawStatus as AdrStatus) : "unknown"; + const enforcedBy = list(frontmatter, "enforced_by"); // A fenced ```toml kit-enforce block anywhere in the body. const block = body.match(/```toml\s+kit-enforce\s*\n([\s\S]*?)\n```/); @@ -154,7 +204,7 @@ export function parseAdr(raw: string): Adr | null { rules = []; // malformed TOML → zero rules, but hasEnforceBlock stays true (surfaced) } } - return { id, title, status, rules, hasEnforceBlock }; + return { id, title, status, rules, hasEnforceBlock, enforcedBy }; } function arr(v: unknown): Record[] { diff --git a/src/check-standards.test.ts b/src/check-standards.test.ts index 50cfa388..927630b0 100644 --- a/src/check-standards.test.ts +++ b/src/check-standards.test.ts @@ -191,9 +191,20 @@ describe("check-standards — checkStandards gating", () => { describe("check-standards — collectStandardsKeys", () => { it("returns empty slices when the general tools are absent (nothing to freeze)", async () => { - // lizard/jscpd/scc are not installed in this environment → didNotRun → empty. - const keys = await collectStandardsKeys(process.cwd()); - assert.deepEqual(keys, { complexity: [], duplication: [], size: [] }); + // The tools are FORCED absent rather than assumed absent. This test previously read + // "lizard/jscpd/scc are not installed in this environment", which made its green depend + // on the machine: provisioning lizard and jscpd turned it red without any code changing. + // A test whose pass condition is "the box happens to be missing something" proves nothing + // on a box that has it. Emptying PATH makes the scan's spawns fail to resolve on every + // machine, which is the condition the assertion is actually about. + const realPath = process.env.PATH; + process.env.PATH = ""; + try { + const keys = await collectStandardsKeys(process.cwd()); + assert.deepEqual(keys, { complexity: [], duplication: [], size: [] }); + } finally { + process.env.PATH = realPath; + } }); }); diff --git a/src/cli-checks-shared.ts b/src/cli-checks-shared.ts index b50e29f7..2bea326e 100644 --- a/src/cli-checks-shared.ts +++ b/src/cli-checks-shared.ts @@ -17,6 +17,43 @@ export const KIT_VERSION = ( JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8")) as { version: string } ).version; +/** + * Check categories whose verdict comes from EXECUTING the code under test rather than + * reading it. Everything else — secret scans, dependency audits, import rules, manifest + * checks, registry queries — is static: it inspects text and metadata. + * + * The distinction is not pedantry. Multi-tier verification research (arXiv:2607.00107, + * 8,918 programs across four tiers) found AI-generated code roughly twice as likely as + * human code to trigger a confirmed runtime violation — while under STATIC analysis the + * two appear equally safe, a similarity the authors call misleading. The tiers catch + * largely different classes of defect. + * + * kit's check surface is almost entirely the tier that cannot tell those apart. That does + * not make its green wrong; it makes it NARROWER than a reader assumes, and a gate whose + * scope is assumed rather than stated is the failure mode this repo keeps finding. So the + * scope gets printed next to the verdict, counted rather than claimed, so it cannot go + * stale as categories are added. + */ +export const EXECUTING_CATEGORIES: readonly string[] = ["tests"]; + +/** + * One line naming what a green verdict covers. Returns "" for an empty run — there is no + * scope to state when nothing ran, and the caller already says so. + */ +export function tierNotice(checks: readonly JsonCheck[]): string { + if (checks.length === 0) return ""; + const executing = checks.filter((k) => EXECUTING_CATEGORIES.includes(k.category)).length; + const stat = checks.length - executing; + const runtime = + executing === 0 + ? "none execute the code" + : `${executing} execute${executing === 1 ? "s" : ""} it`; + return ( + `scope: ${checks.length} check(s) — ${stat} inspect the code, ${runtime}. ` + + `A pass here does not cover runtime behaviour; \`kit broker\` is that tier.` + ); +} + /** One check row in the machine-readable `kit check --json` / `kit ci --json` output. */ export interface JsonCheck { name: string; diff --git a/src/commands/adr.test.ts b/src/commands/adr.test.ts index 36464ac8..00ae742d 100644 --- a/src/commands/adr.test.ts +++ b/src/commands/adr.test.ts @@ -84,3 +84,44 @@ describe("collectAdrFindings + freezeAdrBaseline (temp repo)", () => { assert.equal(live.length, 0, "the frozen violation is suppressed on re-check"); }); }); + +describe("collectAdrFindings — an enforced_by pointer must point at something real", () => { + let dir = ""; + + const seed = (frontmatterExtra: string, status = "accepted"): string => { + const root = mkdtempSync(join(tmpdir(), "kit-enforcedby-")); + mkdirSync(join(root, "docs", "adr"), { recursive: true }); + writeFileSync( + join(root, "docs", "adr", "0001-x.md"), + `---\nid: ADR-0001\ntitle: X\nstatus: ${status}\n${frontmatterExtra}---\n\n# X\n`, + ); + return root; + }; + + after(() => rmSync(dir, { recursive: true, force: true })); + + it("fails when the named file does not exist — a claim of coverage that is not coverage", () => { + dir = seed("enforced_by: [src/gone.test.ts]\n"); + const f = collectAdrFindings(dir); + assert.equal(f.violations.length, 1); + assert.equal(f.violations[0].detail, "src/gone.test.ts"); + assert.match(f.violations[0].message, /does not exist/); + }); + + it("passes when the file is there", () => { + dir = seed("enforced_by: [src/here.test.ts]\n"); + mkdirSync(join(dir, "src"), { recursive: true }); + writeFileSync(join(dir, "src", "here.test.ts"), "// enforcement lives here\n"); + assert.equal(collectAdrFindings(dir).violations.length, 0); + }); + + it("ignores a non-accepted ADR — a proposal's pointer is not yet a claim", () => { + dir = seed("enforced_by: [src/gone.test.ts]\n", "proposed"); + assert.equal(collectAdrFindings(dir).violations.length, 0); + }); + + it("is silent when the field is absent", () => { + dir = seed(""); + assert.equal(collectAdrFindings(dir).violations.length, 0); + }); +}); diff --git a/src/commands/adr.ts b/src/commands/adr.ts index a45b0d4c..d315004f 100644 --- a/src/commands/adr.ts +++ b/src/commands/adr.ts @@ -10,7 +10,13 @@ * * kit never interprets ADR prose (off-charter); it enforces only the explicit * toml block. Only `accepted` ADRs gate; an accepted ADR with no rules is surfaced - * as "documented, not enforced" — never silently green. A transitive forbid_import + * as "documented, not enforced" — never silently green. + * + * Some decisions are not expressible in the block's grammar (no `paths` negation; + * patterns match line by line; the manifest is not in the walked file set). Those name + * their real enforcement point in frontmatter — `enforced_by: [src/x.test.ts]` — which + * `adr list` prints and `adr check` verifies exists, so the pointer cannot rot into a + * claim of coverage that is not coverage. A transitive forbid_import * that hits an unresolvable relative import is a `gap` (can't prove), not a pass. * * This file also owns the impure `node_modules` resolver injected into the pure evaluator @@ -215,7 +221,26 @@ export function collectAdrFindings(cwd: string): AdrFindings { // One resolver per run: its caches are what make a cross-package walk affordable. Rules // without `follow_packages` never call it, so this costs nothing when nobody opted in. const packages = createNodeModulesResolver(cwd); - for (const { adr } of adrs) { + for (const { adr, file } of adrs) { + // An `enforced_by` pointer is a CLAIM that the decision is enforced somewhere the + // kit-enforce grammar cannot reach. A pointer at a file that does not exist is worse + // than no pointer: it reads as coverage. Checked for every accepted ADR, enforced or + // not — a documented-only ADR may still name an external enforcement point. + if (adr.status === "accepted") { + for (const target of adr.enforcedBy) { + if (!exists(pathJoin(cwd, target))) { + violations.push({ + adrId: adr.id, + file, + line: 1, + rule: "require-pattern", + detail: target, + message: `${adr.id} declares enforced_by: ${target}, which does not exist — an enforcement claim must point at something real`, + kind: "violation", + }); + } + } + } if (!adrIsEnforced(adr)) continue; enforcedCount++; for (const v of evaluateAdr(adr, files, { packages })) { @@ -398,6 +423,11 @@ export async function cmdAdr(): Promise { ? `${c.yellow}documented, not enforced${c.reset}` : `${c.dim}${adr.status}${c.reset}`; console.log(` ${adr.id} ${adr.title} [${state}] ${c.dim}${file}${c.reset}`); + // Where the claim is held when the grammar cannot hold it. Printed under the ADR so + // "documented, not enforced" is never confused with "not enforced anywhere". + for (const target of adr.enforcedBy) { + console.log(` ${c.dim}└ also enforced by ${target}${c.reset}`); + } } return true; } diff --git a/src/commands/check.ts b/src/commands/check.ts index dbd922d8..e1ed367b 100644 --- a/src/commands/check.ts +++ b/src/commands/check.ts @@ -36,6 +36,7 @@ import { syncSecurityFindings } from "../findings-track.js"; import { collectHints } from "../hints.js"; import { KIT_VERSION, + tierNotice, type JsonCheck, type JsonCheckOutput, autoInstallScanners, @@ -271,6 +272,17 @@ export async function cmdCheck(): Promise { }), ); + // What the verdict COVERS, counted from the checks that ran. Sibling to the + // partial-run line below and for the same reason: a scope the reader has to assume + // is a scope that gets assumed wrong. kit's surface is almost all static analysis, + // which is the tier that provably cannot distinguish AI-written code from + // human-written code (see EXECUTING_CATEGORIES) — so it says so rather than letting + // a green stand in for more than it checked. + { + const notice = tierNotice(checkRunToJsonChecks(run)); + if (notice) console.log(`${c.dim}${notice}${c.reset}`); + } + // A narrowed run must say so next to its verdict. Without this line a // `--category security` pass is visually identical to a full green. if (run.scope) { diff --git a/src/commands/usage.test.ts b/src/commands/usage.test.ts index 341e730c..3294a3ff 100644 --- a/src/commands/usage.test.ts +++ b/src/commands/usage.test.ts @@ -43,7 +43,7 @@ describe("renderTab", () => { const widths = new Set( rendered .split("\n") - // eslint-disable-next-line no-control-regex + .map((l) => l.replace(/\x1b\[[0-9;]*m/g, "").length), ); assert.equal( diff --git a/src/commands/usage.ts b/src/commands/usage.ts index 00fc2d32..c9e7f526 100644 --- a/src/commands/usage.ts +++ b/src/commands/usage.ts @@ -38,7 +38,6 @@ const WIDTH = 66; /** Visible length: padding maths has to ignore the colour escapes, or the box comes out ragged. */ function plain(s: string): string { - // eslint-disable-next-line no-control-regex return s.replace(/\x1b\[[0-9;]*m/g, ""); } diff --git a/src/dependency-floor.test.ts b/src/dependency-floor.test.ts new file mode 100644 index 00000000..eb787244 --- /dev/null +++ b/src/dependency-floor.test.ts @@ -0,0 +1,76 @@ +/** + * ADR-0002's claim, enforced. + * + * WHY THIS FILE EXISTS. ADR-0002 is titled "Dependency floor — four runtime deps, stdlib + * otherwise", and CLAUDE.md lists it among kit's deterministic rules. Its `kit-enforce` + * block enforces something narrower: a `forbid_import` deny-list of twelve named packages + * (lodash, axios, moment, …) checked in `src/**`. Measured — by accidentally adding a + * fifth runtime dependency and watching the gate stay green — a new dependency that is not + * on that list passes silently. The ADR declared more than it enforced. + * + * WHY NOT FIX IT IN THE ADR GATE. Two structural reasons, both measured rather than + * assumed: + * + * 1. `package.json` is not in the file set the ADR gate walks. `CODE_EXTS` in + * `commands/adr.ts` is source extensions only, so no `kit-enforce` rule can ever apply + * to a manifest. + * 2. `forbid_pattern` / `require_pattern` are matched LINE BY LINE (`firstMatchingLine` + * splits on newlines). A dependency entry in `dependencies` is textually identical to + * one in `devDependencies`, so a line-based regex cannot tell them apart, and a + * multi-line pattern pinning the whole block cannot match at all. + * + * Widening the walk and adding block-aware matching to serve one rule is a larger change + * than the rule is worth. A test is the smaller, honest mechanism: it is deterministic, it + * runs in CI already, and it fails the moment the claim stops being true. + * + * ADDING A DEPENDENCY IS AN ADR-LEVEL ACT. If a fifth is genuinely needed, amend or + * supersede ADR-0002 in the same PR and update the list below. Editing this list alone to + * make the suite green is the failure this test exists to catch. + */ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), ".."); + +/** The floor ADR-0002 declares. Changing this set requires amending that ADR. */ +const RUNTIME_DEPENDENCIES = [ + "@modelcontextprotocol/sdk", + "@upstash/redis", + "smol-toml", + "zod", +] as const; + +function manifest(): { dependencies?: Record } { + return JSON.parse(readFileSync(join(REPO_ROOT, "package.json"), "utf-8")) as { + dependencies?: Record; + }; +} + +describe("ADR-0002 dependency floor", () => { + it("ships exactly the declared runtime dependencies — no more, no fewer", () => { + const declared = Object.keys(manifest().dependencies ?? {}).sort(); + assert.deepEqual( + declared, + [...RUNTIME_DEPENDENCIES].sort(), + "package.json's runtime dependencies drifted from ADR-0002's floor. A new runtime " + + "dependency is an architecture decision: amend or supersede docs/adr/0002-dependency-floor.md " + + "in the same PR. Note that a dev TOOL kit shells out to (a scanner, a linter) is not a " + + "runtime dependency and must not be added here — install it as a tool.", + ); + }); + + it("pins every runtime dependency to an exact version", () => { + // A range lets the floor move without anyone editing package.json, which would make + // the assertion above true and the claim behind it false. + for (const [name, range] of Object.entries(manifest().dependencies ?? {})) { + assert.match( + range, + /^\d+\.\d+\.\d+$/, + `${name} is pinned as "${range}" — ADR-0002's floor is only meaningful if the versions cannot float`, + ); + } + }); +}); diff --git a/src/tier-notice.test.ts b/src/tier-notice.test.ts new file mode 100644 index 00000000..aa00a6a4 --- /dev/null +++ b/src/tier-notice.test.ts @@ -0,0 +1,47 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { tierNotice, EXECUTING_CATEGORIES, type JsonCheck } from "./cli-checks-shared.js"; + +const check = (category: string): JsonCheck => ({ + name: `${category} check`, + status: "pass", + detail: "", + category, +}); + +describe("tierNotice", () => { + it("counts the tiers rather than asserting them, so it cannot go stale", () => { + const notice = tierNotice([ + check("security/secrets"), + check("security/supply-chain"), + check("tests"), + ]); + assert.match(notice, /3 check\(s\)/); + assert.match(notice, /2 inspect the code/); + assert.match(notice, /1 executes it/); + }); + + it("says none execute the code when no executing category ran", () => { + // The case that matters: a run made entirely of static checks must not leave the + // reader thinking anything was actually run. + const notice = tierNotice([check("security/secrets"), check("deploy")]); + assert.match(notice, /2 inspect the code, none execute the code/); + }); + + it("names the runtime tier, so the limitation comes with its remedy", () => { + assert.match(tierNotice([check("tests")]), /kit broker/); + }); + + it("returns nothing for an empty run — there is no scope to state", () => { + assert.equal(tierNotice([]), ""); + }); + + it("derives the split from EXECUTING_CATEGORIES, not from a hardcoded count", () => { + // Proves the notice tracks the constant: every category listed there counts as + // executing, and a category absent from it counts as static. + for (const cat of EXECUTING_CATEGORIES) { + assert.match(tierNotice([check(cat)]), /0 inspect the code, 1 executes it/); + } + assert.match(tierNotice([check("not-a-real-category")]), /1 inspect the code/); + }); +});