baldart 5.8.0 → 5.10.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.
@@ -0,0 +1,233 @@
1
+ # Security Review Protocol — the AppSec verification engine
2
+
3
+ **Purpose**: the security-specific passes that turn a generic code review into
4
+ an adversarial AppSec audit — repository-scoped threat modeling, the modern
5
+ high-miss discovery lens, class-specific proof tuples, structured attack-path →
6
+ mechanical severity, adversarial self-refutation (attacker vs victim), and a
7
+ coverage ledger so *"not observed" never reads as "not reviewed"*. Distilled
8
+ from the Codex `codex-security` workbench + the Claude Code `security-guidance`
9
+ agentic reviewer. This module is the **single SSOT** of that methodology.
10
+
11
+ **Relationship to `review-protocol.md`**: that module owns the GENERIC verifier
12
+ passes (Challenge, Actionability, Simulation, Chain-of-Verification, risk
13
+ scoring). This module **extends**, never duplicates them: `adversarial-refute`
14
+ is the security-specialized layer that runs *inside* the generic Challenge pass;
15
+ `proof-tuples` is the security grounding for the generic CoVe pass. On the
16
+ generic passes, `review-protocol.md` wins; on AppSec specifics, this module
17
+ wins.
18
+
19
+ **Consumer**: `security-reviewer` (body citations). Portable as-is across Claude
20
+ Code and Codex (read at runtime by both).
21
+
22
+ ## Contract
23
+
24
+ - **Dispatch**: cite `agents/security-review-protocol.md SECTION=<threat-model|discovery-lens|proof-tuples|attack-path|adversarial-refute|coverage-ledger|boundary-gate>`.
25
+ Grep for `### SECTION: <name>` and Read ONLY that section.
26
+ - **Order of passes** (normative): `boundary-gate` (once, at kickoff) →
27
+ `threat-model` (adopt or generate) → discovery under the `discovery-lens` →
28
+ per candidate: `proof-tuples` (validation) → `attack-path` (structured facts)
29
+ → `adversarial-refute` → mechanical severity (`review-protocol.md
30
+ SECTION=risk-scoring`) → `coverage-ledger` close. The generic Challenge + CoVe
31
+ passes still run; the security passes feed them.
32
+ - **Degrade-safe**: skipping a pass means less rigor, never a malformed report —
33
+ the report/YAML shape stays defined in the agent body.
34
+
35
+ ---
36
+
37
+ ### SECTION: boundary-gate
38
+
39
+ Run ONCE before confirming any finding. A vulnerability only exists when an
40
+ attacker crosses a trust boundary of a **shipped/deployed** surface — so
41
+ classify the surface first, or you will flag same-privilege local code as a
42
+ "vuln".
43
+
44
+ 1. **Read the repo's own policy FIRST**: `SECURITY.md`, `AGENTS.md` deployment
45
+ notes, package metadata. They define which surfaces are in-scope and what the
46
+ supported threat model is. A finding that contradicts a documented,
47
+ intentional design is a policy question, not a confirmed vuln.
48
+ 2. **Classify the surface under review**:
49
+ - `product_surface`: hosted service | library API | CLI | local dev UI |
50
+ MCP/tooling | example/demo | test | docs | generated | vendored.
51
+ - `source_trust` of the input: untrusted (remote/attacker) |
52
+ trusted_operator | trusted_developer_config | local_only |
53
+ intentional_code_execution.
54
+ - `boundary_crossed`: does attacker-controlled input reach the sink across a
55
+ real privilege/trust boundary? true | false | unknown.
56
+ 3. **Gate**: no boundary crossed (attacker == victim, same privilege, local dev
57
+ tool with no request handler) → NOT a finding. Record the disposition in the
58
+ coverage ledger, do not emit it as LOW.
59
+
60
+ ### SECTION: threat-model
61
+
62
+ Establish (or adopt) a **repository-scoped** threat model — deliberately NOT
63
+ biased by the current diff, so it stays valid for any change in the same repo.
64
+
65
+ - **Adopt** an existing model from the user, `SECURITY.md`, or `AGENTS.md` when
66
+ present. Only **generate** a fallback when none exists.
67
+ - Fallback generation, repository-level (not diff-scoped):
68
+ 1. Real-world purpose + primary product/runtime surfaces actually exposed
69
+ (separate them from test/demo/tooling/docs paths).
70
+ 2. Trust boundaries + the actors on each side; separate **attacker-controlled**
71
+ vs **operator-controlled** vs **developer-controlled** inputs explicitly.
72
+ 3. Sensitive assets: user data, auth artifacts, authorization state,
73
+ secrets/keys, config, models/weights, audit logs, availability-critical
74
+ resources, tenant-isolation state.
75
+ 4. Vulnerability CLASSES relevant to this repo's context (not the diff's
76
+ findings) + existing mitigations grounded in real files.
77
+ 5. When realistic vs out-of-scope: state attacker (non-)capabilities so
78
+ severity is not inflated.
79
+ - Use it to pick **focus paths** for the review; do not let the diff recenter it
80
+ unless the user asks for a diff-scoped model.
81
+
82
+ ### SECTION: discovery-lens
83
+
84
+ Beyond the OWASP core checklist (in the agent body), sweep for the **modern
85
+ high-miss patterns** — the ones a generic checklist walks past. For each changed
86
+ region, ask:
87
+
88
+ - **FAIL-OPEN state drift**: a default that *allows* on the unknown/error path —
89
+ catch-all `default: allow`, `unwrap_or({})`, a removed `finally` that left the
90
+ deny, an early-return before the check. Security must fail *closed*.
91
+ - **ALLOWLIST semantic escape**: a matcher weakened — a `||`/disjunction added, a
92
+ regex left unanchored (`^…$` missing), an allowlist checked by `startsWith`/
93
+ substring, a URL trusted by substring instead of parsed host.
94
+ - **CONTROL regression**: a deny-by-default replaced by a single positive
95
+ condition; an authz/confirm/audit control loosened or removed in the diff.
96
+ - **GATE / ACTION field mismatch**: the gate checks resource A (parent, id X) but
97
+ the action writes/reads resource B (child, id Y) — the check and the effect
98
+ disagree.
99
+ - **UNDER-VALIDATED sink arg**: user data flows into sink arg *X* while a sibling
100
+ path validates arg *Y* — asymmetry between siblings is the tell.
101
+ - **PARSER/VALIDATOR differential**: the validator and the consumer parse the
102
+ input differently (two URL parsers, JSON vs form, unicode/normalization) — the
103
+ gap is the exploit.
104
+ - **SENSITIVE-to-observability**: secrets/tokens/PII reaching logs, traces,
105
+ error responses, analytics, crash dumps.
106
+ - **CI/CD trust**: `pull_request_target` / `workflow_dispatch` / injectable
107
+ `${{ github.event.*.body }}` combined with secrets or repo-write; a workflow
108
+ running untrusted code with elevated tokens.
109
+ - **IaC omitted arg**: a Terraform/CDK/K8s resource missing a security flag
110
+ (public bucket, open SG, no encryption, over-broad IAM `*`).
111
+ - **OVER-BROAD grant**: an admin/`*` role/scope where a narrower one exists;
112
+ new IAM/RLS/role wider than the action needs.
113
+ - **STALE identity mapping**: a window between unregister/rotate and the new
114
+ binding where the old identity still resolves.
115
+ - **SECURITY-REGISTRY fanout**: a new entity type (model, scope, route, event)
116
+ added but missing from the sanitizer/allowlist/authz registries that must
117
+ enumerate it.
118
+ - **RESOURCE-bound placement**: a cap/limit on the wrong accumulator; an
119
+ under/overflow; a size check after the allocation.
120
+
121
+ ### SECTION: proof-tuples
122
+
123
+ A security finding is a *hypothesis* until its **class-specific proof tuple** is
124
+ grounded against real code (this is the AppSec form of the CoVe pass). Confirm
125
+ every element by grep/read before emitting; drop the finding if any element is
126
+ missing.
127
+
128
+ - **authz / IDOR**: attacker reachability path + the missing/bypassed guard + the
129
+ protected object or state it exposes.
130
+ - **injection** (SQL/NoSQL/OS/LDAP/template): attacker-controlled bytes + the
131
+ sanitizer/parameterization result (or its absence) + the dangerous sink.
132
+ - **deserialization**: attacker serialized input + the unsafe loader + the
133
+ execution/instantiation effect.
134
+ - **SSRF / open redirect**: attacker-controlled destination + the control bypass
135
+ + the network reach / side effect.
136
+ - **auth / token**: attacker-supplied value + the validator's exact semantics +
137
+ the mismatch with the trusted value.
138
+ - **XSS**: attacker string + the encoding/escaping context at output + the sink
139
+ (`innerHTML`/`dangerouslySetInnerHTML`/template) with no encode.
140
+
141
+ **Confidence anchored to validation METHOD, never to how scary the bug class
142
+ sounds**:
143
+ - reproduced PoC / failing test that triggers the sink → confidence 90–100.
144
+ - debugger/trace or a proven interface reproduction → 75–90.
145
+ - static source→sink trace fully grounded in read code → 60–80.
146
+ - code-understanding argument with a defensible but not fully-traced path →
147
+ 30–55. Below that, it is a hypothesis — do not ship as HIGH.
148
+
149
+ **Instance-preserving**: do NOT collapse independently exploitable sibling
150
+ instances (`execute` / `executemany` / `executescript`; two routes with the same
151
+ flaw) into one finding. Each independently triggerable sink gets its own
152
+ finding/closure row.
153
+
154
+ ### SECTION: attack-path
155
+
156
+ BEFORE assigning severity, write the attack path as **structured facts**, then
157
+ let severity fall out of them mechanically (`review-protocol.md
158
+ SECTION=risk-scoring`) — this stops severity from being argued emotionally.
159
+
160
+ - `attacker`: who, at what trust level, from where.
161
+ - `entrypoint`: the exact reachable entry (route/RPC/CLI/webhook/MCP tool).
162
+ - `preconditions`: conditions that must hold for the exploit (feature flag on,
163
+ authenticated user, specific config). A **precondition** narrows likelihood but
164
+ does NOT refute — keep it distinct from counterevidence (which kills the
165
+ finding, see `adversarial-refute`).
166
+ - `dataflow`: source → sink in one line.
167
+ - `outcome`: the concrete impact (data read/written, code executed, tenant
168
+ crossed).
169
+ - `impact` (1–5) and `likelihood` (1–5), each with one line of repo evidence.
170
+
171
+ Severity is then `review-protocol.md SECTION=risk-scoring` applied to
172
+ `impact × likelihood` — do not re-argue it afterward.
173
+
174
+ ### SECTION: adversarial-refute
175
+
176
+ The security-specialized layer of the generic Challenge pass. Stance: **a finding
177
+ SURVIVES unless you produce concrete refuting evidence** — but the single most
178
+ common false positive is a **missing privilege boundary**, so test that first.
179
+
180
+ **Privilege-boundary test (BINDING)**: name the attacker (who controls the input)
181
+ and the victim (who is harmed). If attacker == victim operating on their own
182
+ machine / same-privilege process (a CLI arg in a same-trust process, a dev
183
+ script, a local tool with no remote entrypoint) → **REFUTE**. Keep it only when
184
+ the attacker is a lower-trust actor and the impact reaches other users, tenants,
185
+ or infrastructure.
186
+
187
+ **Refute the finding when**:
188
+ - it is **pre-existing** code untouched by the diff (in diff-scoped review, flag
189
+ only newly introduced or newly reachable surface; an off-diff finding must name
190
+ the specific `+`/`-` line that enables the sink).
191
+ - a sanitizer / validator / parameterization / authz check on the real path
192
+ already prevents the exploit.
193
+ - the sink is not actually dangerous here (typed-schema decoder, hardcoded
194
+ `https` URL, ORM/parameterized query, non-HTML context for an "XSS").
195
+ - a frontend-only gate is independently enforced by the backend (or vice-versa).
196
+ - validation is delegated to a caller/framework that provably runs it.
197
+ - it is throwaway/dev-only code (`scripts/`, `dev/`, `__main__`, fixtures, tests)
198
+ with no shipped surface.
199
+ - a dependency bump moved the control into the library (documented).
200
+
201
+ **Precondition vs counterevidence (keep precise)**: "deployment is internal-only"
202
+ is a *precondition* on the finding (still exploitable if it holds) → keep, lower
203
+ likelihood. "the code is test-only" is *counterevidence* → refute. Do not confuse
204
+ the two.
205
+
206
+ **Never-refute guard**: an item the agent's never-demote list marks (auth bypass,
207
+ RCE, secret exposure, cross-tenant breach with a grounded proof tuple) is never a
208
+ false positive — do not refute it however convincing the argument sounds.
209
+
210
+ Record refuted candidates (title + one-line refutation) so the audit trail shows
211
+ they were considered, not missed.
212
+
213
+ ### SECTION: coverage-ledger
214
+
215
+ Make the review's *coverage* explicit so a clean verdict is honest and a gap is
216
+ never silent. For each security-relevant surface in scope, record a disposition:
217
+
218
+ - `reported` — a finding was emitted.
219
+ - `no_issue_found` — reviewed, no credible issue survived the passes.
220
+ - `not_applicable` — the class does not apply to this surface (with the reason).
221
+ - `needs_follow_up` — a real concern out of this review's scope/ownership
222
+ (surfaces as residual).
223
+ - `deferred` — could not complete (tool/access/time), with the reason + paths.
224
+
225
+ Rules:
226
+ - A **complete** review has zero `deferred` / `needs_follow_up` rows; otherwise
227
+ it is **partial** — say so. Zero findings on a fully-covered surface is a
228
+ legitimate, reportable outcome.
229
+ - A seeded obligation (a CVE/advisory/user pointer at a specific file/function)
230
+ stays OPEN until its exact row is closed as reported / not_applicable /
231
+ deferred — a neighboring same-family finding does not close it.
232
+ - In the pooled YAML mode, carry this as a compact `coverage:` block in the
233
+ `# Security Review Summary` header, not as fake LOW findings.
@@ -55,72 +55,76 @@ Document security requirements, threats, and mitigation strategies.
55
55
  - [Sensitive data masking in logs]
56
56
  - [Secrets management]
57
57
 
58
- ## Common Vulnerabilities (OWASP Top 10)
58
+ ## Common Vulnerabilities (OWASP Top 10 — 2021)
59
59
 
60
- ### Injection Attacks
60
+ > Modernized to the OWASP Top 10:2021 categories (the 2017 list — with XXE and
61
+ > "Sensitive Data Exposure" as standalone entries — is superseded). XXE now folds
62
+ > into Security Misconfiguration; insecure deserialization into Software & Data
63
+ > Integrity Failures; XSS into Injection. A 2025 revision is in draft — revisit
64
+ > when it is finalized (expected consolidation of supply-chain risks).
61
65
 
62
- - Use parameterized queries
63
- - Validate and sanitize inputs
64
- - Implement least privilege database access
66
+ ### A01 Broken Access Control
65
67
 
66
- ### Broken Authentication
68
+ - Enforce authorization on every request, server-side; deny by default
69
+ - Prevent IDOR — never trust a user-supplied record/object identifier
70
+ - Enforce ownership + tenant isolation on every sensitive action
71
+ - Principle of least privilege; no privilege escalation via business logic
67
72
 
68
- - Implement secure session management
69
- - Use strong password policies
70
- - Implement account lockout
71
- - Protect against brute force
73
+ ### A02 Cryptographic Failures
72
74
 
73
- ### Sensitive Data Exposure
75
+ - Encrypt sensitive data at rest and in transit (HTTPS/TLS everywhere)
76
+ - Use strong, modern algorithms (no MD5/SHA1 for security, no AES-ECB, no static IV)
77
+ - Proper key management + rotation; never log secrets or PII
78
+ - Strong password hashing (argon2/bcrypt/scrypt) with per-user salt
74
79
 
75
- - Encrypt sensitive data
76
- - Use HTTPS everywhere
77
- - Don't log sensitive information
78
- - Implement secure key management
80
+ ### A03 Injection (incl. XSS)
79
81
 
80
- ### XML External Entities (XXE)
82
+ - Parameterized queries / ORM; never build queries by string concatenation
83
+ - Validate + sanitize inputs; allowlists over denylists
84
+ - Least-privilege database access
85
+ - Output encoding at the right context + Content Security Policy for XSS
81
86
 
82
- - Disable XML external entity processing
83
- - Use safe XML parsers
84
- - Validate XML inputs
87
+ ### A04 Insecure Design
85
88
 
86
- ### Broken Access Control
89
+ - Threat-model the feature before building; secure-by-default patterns
90
+ - Establish trust boundaries and enforce them; fail closed
91
+ - Rate limits, resource bounds, and abuse cases considered up front
87
92
 
88
- - Implement proper authorization
89
- - Validate permissions on every request
90
- - Use principle of least privilege
93
+ ### A05 Security Misconfiguration (incl. XXE)
91
94
 
92
- ### Security Misconfiguration
95
+ - Harden defaults; remove unnecessary features/debug endpoints in prod
96
+ - Set security headers; keep software updated
97
+ - Disable XML external-entity processing; use safe parsers/validators
93
98
 
94
- - Harden default configurations
95
- - Keep software updated
96
- - Remove unnecessary features
97
- - Implement security headers
99
+ ### A06 Vulnerable & Outdated Components
98
100
 
99
- ### Cross-Site Scripting (XSS)
101
+ - Keep dependencies patched; monitor security advisories (CVE/GHSA)
102
+ - Run dependency + vulnerability scanning in CI (e.g. audit / govulncheck)
103
+ - Remove unused dependencies and features
100
104
 
101
- - Escape output
102
- - Use Content Security Policy
103
- - Validate and sanitize inputs
104
- - Use framework protections
105
+ ### A07 Identification & Authentication Failures
105
106
 
106
- ### Insecure Deserialization
107
+ - Secure session management (rotation, expiry, secure cookies)
108
+ - Strong password policy, account lockout / brute-force protection, MFA where applicable
109
+ - No session fixation, no token leakage in URLs/logs
107
110
 
108
- - Validate serialized data
109
- - Use safe deserialization libraries
110
- - Implement integrity checks
111
+ ### A08 Software & Data Integrity Failures (incl. insecure deserialization)
111
112
 
112
- ### Using Components with Known Vulnerabilities
113
+ - Never deserialize untrusted data with unsafe loaders; validate + integrity-check
114
+ - Verify integrity of updates/CI artifacts (signatures, SRI, pinned digests)
115
+ - Guard the CI/CD supply chain — no untrusted code with elevated tokens
113
116
 
114
- - Keep dependencies updated
115
- - Monitor security advisories
116
- - Use dependency scanning tools
117
+ ### A09 Security Logging & Monitoring Failures
117
118
 
118
- ### Insufficient Logging & Monitoring
119
+ - Log security events; monitor for suspicious activity; alert
120
+ - Protect log integrity; never log secrets/PII
121
+ - Ensure exploitation attempts are detectable and auditable
119
122
 
120
- - Log security events
121
- - Monitor for suspicious activity
122
- - Implement alerting
123
- - Protect log integrity
123
+ ### A10 Server-Side Request Forgery (SSRF)
124
+
125
+ - Validate + allowlist outbound destinations; parse (don't substring) URLs
126
+ - Block internal metadata endpoints and private ranges
127
+ - Enforce egress controls; treat any user-supplied URL/host as hostile
124
128
 
125
129
  ## Rate Limiting
126
130
 
@@ -0,0 +1,280 @@
1
+ # Simplify Protocol — the reuse & simplicity verification engine
2
+
3
+ **Purpose**: the simplification-specific methodology that turns a generic code
4
+ pass into a ruthless anti-duplication / reuse-first / right-altitude audit — an
5
+ agentic-defect taxonomy grounded in empirical research, a canonical refactoring
6
+ vocabulary so findings are actionable, the clone-type vocabulary, a deterministic
7
+ clone-floor contract, an internal-documentation awareness contract for reuse
8
+ retrieval, and the judge-bias guards a *simplify* reviewer specifically needs
9
+ (a simplify judge that rewards verbosity is inverted). This module is the
10
+ **single SSOT** of that methodology.
11
+
12
+ **Relationship to `review-protocol.md`**: that module owns the GENERIC verifier
13
+ passes (Challenge, Actionability, Simulation, Chain-of-Verification, risk
14
+ scoring). This module **extends**, never duplicates them — it supplies the
15
+ simplify-specialized taxonomy, vocabulary and evidence rules that FEED those
16
+ generic passes. On the generic passes, `review-protocol.md` wins; on
17
+ simplification specifics, this module wins.
18
+
19
+ **Relationship to the `simplify` SKILL**: the skill owns the OPERATIONAL
20
+ workflow (identify diff → three-lens fan-out → aggregate → fix → verify). This
21
+ module owns the taxonomy/vocabulary/bias/doc-awareness the lenses apply. The
22
+ skill and the `code-simplifier` agent CITE this module; they do not restate it.
23
+
24
+ **Consumers**: the `code-simplifier` agent (body citations), the `simplify`
25
+ skill (Step 2), the `coder` author-time mirror (§ Author-Time Simplicity
26
+ Discipline). Portable as-is across Claude Code and Codex (read at runtime by
27
+ both).
28
+
29
+ **Empirical anchors** (why this exists): LLM-authored code carries a
30
+ duplication/inefficiency tax that generic linters miss — Abbassi et al.,
31
+ *A Taxonomy of Inefficiencies in LLM-Generated Python Code* (arXiv:2503.06327,
32
+ ~90% of samples ≥1 inefficiency); Sonar, *The Coding Personalities of Leading
33
+ LLMs* (>90% of introduced issues are code smells). Self-review is unreliable —
34
+ Huang et al., *LLMs Cannot Self-Correct Reasoning Yet* (ICLR 2024,
35
+ arXiv:2310.01798); Xu et al., *Pride and Prejudice: LLM Amplifies Self-Bias in
36
+ Self-Refinement* (ACL 2024, arXiv:2402.11436). Hence: an INDEPENDENT reviewer,
37
+ grounded in EXTERNAL evidence (clone match / zero-caller proof / resolved
38
+ symbol), never the author's own opinion.
39
+
40
+ ## Contract
41
+
42
+ - **Dispatch**: cite `agents/simplify-protocol.md SECTION=<taxonomy|canonical-vocab|clone-vocab|rubric|deterministic-tier|doc-awareness|bias-guards>`.
43
+ Grep for `### SECTION: <name>` and Read ONLY that section.
44
+ - **Order** (normative): `deterministic-tier` (anchored clone candidates, once,
45
+ before eyeballing) → retrieval under `doc-awareness` → per candidate the
46
+ `rubric` lens applies its `taxonomy` class + `canonical-vocab` name →
47
+ `clone-vocab` classifies duplicates → generic Challenge + CoVe
48
+ (`review-protocol.md`) → `bias-guards` neutralize the verdict.
49
+ - **Evidence rule (BINDING)**: every finding cites executed/retrieved evidence —
50
+ a clone match (`path:line` × 2), a zero-inbound-caller proof, a resolved-or-not
51
+ symbol, an existing-utility `path:line`. A finding whose only support is the
52
+ reviewer's unaided judgment is a HYPOTHESIS, not a finding — drop it or ground
53
+ it. This is the simplify twin of the anti-assertion-fitting rule: retrieve/run
54
+ first, judge second.
55
+ - **Degrade-safe**: skipping a pass means less rigor, never a malformed report —
56
+ the report/YAML shape stays defined in the agent/skill body.
57
+
58
+ ---
59
+
60
+ ### SECTION: taxonomy
61
+
62
+ The agentic-defect taxonomy. Each class carries the DETECTABLE SIGNAL a reviewer
63
+ or the deterministic tier keys on — never flag on vibes. Classes A1/A2/A4/A5 are
64
+ the highest-yield agentic-specific findings (generic linters miss them; the
65
+ research shows they are pervasive).
66
+
67
+ - **A1 — Reuse-miss** (does an equivalent already exist in the repo?). Signal:
68
+ a new function/class/hook whose name, signature, or body is ≥Type-3 similar to
69
+ an existing exported symbol elsewhere; a local helper shadowing a public util;
70
+ no import added from the module that already owns the capability. **Highest
71
+ agentic-specific value.** Retrieval-driven (§ doc-awareness).
72
+ - **A2 — Duplication / near-duplicate block**. Signal: identical token/line
73
+ sequence ≥ threshold in 2+ places (in the diff OR against the repo). The
74
+ deterministic tier (§ deterministic-tier) anchors these; classify per
75
+ § clone-vocab.
76
+ - **A3 — Reinventing stdlib/native/framework capability**. Signal: hand-rolled
77
+ logic that is a one-liner in the language stdlib or an already-imported lib
78
+ (manual dedup vs `Set`, manual deep-clone vs `structuredClone`, manual date
79
+ math vs the date lib, manual debounce/groupBy/chunk). A new third-party
80
+ dependency for a stdlib-covered capability is A3 + an ADR trigger.
81
+ - **A4 — Over-engineering / premature generalization / wrong altitude**. Signal:
82
+ an abstraction (interface/factory/generic/config flag) with exactly ONE
83
+ caller/one implementation; parameters never varied; speculative extension
84
+ points with no consumer; the Rule-of-Three violated (abstraction minted for the
85
+ first or second occurrence). See § canonical-vocab (Speculative Generality,
86
+ AHA, YAGNI).
87
+ - **A5 — Dead / unused code left behind**. Signal: unused export/param/var/import;
88
+ unreachable branch; a function with zero inbound call-graph edges. **Advisory
89
+ by default** — dynamic dispatch / reflection / DI / side-effect imports produce
90
+ false positives; verify before deleting.
91
+ - **A6 — Pattern inconsistency vs the surrounding codebase** (cargo-cult
92
+ boilerplate). Signal: the new code uses a different idiom / error-handling /
93
+ naming / state pattern than the 2–3 nearest sibling files; boilerplate copied
94
+ but partially wired.
95
+ - **A7 — Unresolved/hallucinated API or package**. Signal: an import/call that
96
+ resolves to no real symbol/package (LSP "unresolved"); a method not on the
97
+ type's real surface. Cheap, high-value, security-adjacent (slopsquatting).
98
+ - **A8 — Missed optimization**. Signal: quadratic where linear exists; repeated
99
+ recompute instead of hoist/memo; N+1; independent ops run sequentially; new
100
+ blocking work on a startup/per-request/per-render hot path.
101
+ - **A9 — Complexity spike** (the umbrella smell). Signal: cyclomatic/cognitive
102
+ complexity, function length, nesting depth, or parameter count materially above
103
+ the surrounding code — see § canonical-vocab for the calibrated thresholds.
104
+
105
+ ### SECTION: canonical-vocab
106
+
107
+ Map every finding to a NAMED refactoring concept so the fix is actionable and
108
+ the severity is calibrated, not invented. Cite the name in the finding.
109
+
110
+ **Fowler / refactoring.guru code smells** (`refactoring.com/catalog`):
111
+ - *Bloaters* → Long Method, Large Class, Long Parameter List, Data Clumps,
112
+ Primitive Obsession. (A9 / A4)
113
+ - *Dispensables* → **Duplicate Code** (A2), **Dead Code** (A5), **Speculative
114
+ Generality** (A4), Lazy Class, Comments-as-deodorant.
115
+ - *Couplers* → Feature Envy, Message Chains, Middle Man, Inappropriate Intimacy
116
+ (leaky boundaries — A4/A6).
117
+
118
+ **Four Rules of Simple Design** (Beck, `martinfowler.com/bliki/BeckDesignRules.html`),
119
+ priority order — a finding that violates a higher rule outranks a lower one:
120
+ 1. Passes the tests. 2. Reveals intention. 3. **No duplication** (the simplify
121
+ core — A1/A2/A3). 4. Fewest elements (A4 — no speculative parts).
122
+
123
+ **Governing principles** (cite when scoping an abstraction finding):
124
+ - **Rule of Three** — do NOT abstract until the third occurrence; abstracting on
125
+ the first/second is A4. The mirror caveat: `code-reviewer` will not *demand*
126
+ abstraction for ≤3 repetitions either — the two agents agree at the boundary.
127
+ - **AHA** (*Avoid Hasty Abstractions*, Kent C. Dodds) / **"duplication is far
128
+ cheaper than the wrong abstraction"** (Sandi Metz) — prefer a little
129
+ duplication over a wrong/leaky abstraction. So an A2 duplicate is only worth
130
+ unifying when the shared shape is genuine, not coincidental.
131
+ - **YAGNI / KISS** — no extension point, config flag, or parameter without a
132
+ present consumer (A4).
133
+
134
+ **Calibrated complexity thresholds** (A9 — cite the number, never invent one;
135
+ these are surrounding-code-relative signals, not absolute mandates):
136
+ - Cyclomatic complexity per function **>10** warn / **>15** act (McCabe 1976;
137
+ NIST SP 500-235).
138
+ - Cognitive complexity per function **>15** (SonarSource S3776 default;
139
+ Campbell white paper — penalizes nesting/flow-breaks over raw branch count).
140
+ - Function length **>~50 LOC**, nesting depth **>4**, parameters **>3–4**
141
+ (ESLint `max-lines-per-function` / `max-depth` / `max-params` defaults).
142
+ Treat as *relative to the file's neighbours*; a spike vs siblings matters more
143
+ than the absolute number.
144
+
145
+ ### SECTION: clone-vocab
146
+
147
+ Classify every A2 duplicate. The vocabulary sets the fix and who catches it.
148
+
149
+ - **Type-1** — identical modulo whitespace/comments. Deterministic tier catches
150
+ it. Fix: delete + reuse, or extract.
151
+ - **Type-2** — identical modulo renamed identifiers/literals/types. Deterministic
152
+ tier catches it only with identifier normalization (which the built-in floor
153
+ does NOT do — the reviewer catches these). Fix: extract parametrized shared.
154
+ - **Type-3** — copied-then-modified (added/removed/changed statements — the gap
155
+ case). Partially caught by the floor's line-window overlap; the reviewer
156
+ confirms. Fix: extract with parameters for the delta.
157
+ - **Type-4** — semantically equivalent, syntactically different. Structurally
158
+ invisible to any deterministic tier — **this is the reviewer's job**, backed by
159
+ retrieval (§ doc-awareness) and behavioural reasoning, never a hash.
160
+
161
+ Fix-classification (reused by the skill's aggregation): Exact duplicate → delete
162
+ new, use existing · Near duplicate → extract shared parametrized · Inline
163
+ reimplementation → replace with the existing utility · Dependency-reimplements-
164
+ native → native primitive + remove dep.
165
+
166
+ ### SECTION: rubric
167
+
168
+ The lens checklists the operational pass applies. The `simplify` skill fans these
169
+ out as its three parallel lenses; the `code-simplifier` agent runs them as one
170
+ persona covering all lenses. Each lens tags findings with its `taxonomy` class.
171
+
172
+ - **Reuse lens** (A1/A2/A3) — the load-bearing, cross-codebase job the author
173
+ could not see from inside the task. Consume the deterministic tier's anchored
174
+ clone candidates FIRST (§ deterministic-tier), then retrieve reuse candidates
175
+ (§ doc-awareness), then judge Type-2/3/4 the tier missed. Classify per
176
+ § clone-vocab.
177
+ - **Quality lens** — redundant state (derivable values, effects where a direct
178
+ call works), parameter sprawl, copy-paste-with-variation, leaky abstractions,
179
+ stringly-typed code, unnecessary nesting/wrappers, unnecessary comments
180
+ (WHAT/change-narration — keep only non-obvious WHY).
181
+ - **Design-altitude lens** (A4/A6 — the *design-defect* coverage) — single-caller
182
+ abstraction / Rule-of-Three violation / speculative generality / config flag
183
+ with no consumer (A4); pattern inconsistency vs the 2–3 nearest sibling files
184
+ (A6); leaky boundaries. **Advisory by default** — "wrong altitude" is the most
185
+ judgment-heavy class; a *simplify* pass that fabricates abstraction complaints
186
+ causes the churn it exists to prevent. Fold into the Quality lens (skill) /
187
+ cover as one persona (agent); do NOT spin a fourth parallel spawn.
188
+ - **Efficiency lens** (A8) — unnecessary work, missed concurrency, hot-path
189
+ bloat, recurring no-op updates (add change-detection guards), unnecessary
190
+ existence checks (TOCTOU), memory leaks, overly broad reads.
191
+
192
+ ### SECTION: deterministic-tier
193
+
194
+ The anchored clone floor. Grounds the reviewer with an external signal it lacks
195
+ (the research: an LLM eyeballing a diff misses clones exactly as it misses its
196
+ own smells). Run BEFORE eyeballing; feed anchored candidates to the Reuse lens.
197
+
198
+ - **Tool**: `framework/.claude/skills/simplify/scripts/simplify-scan.mjs` — a
199
+ zero-dependency Node scanner (identical on Claude Code and Codex; the twin of
200
+ `ui-design/scripts/craft-check.mjs`). Rolling-hash near-duplicate detection
201
+ over `diff × repo`.
202
+ - **Contract** (what it MUST do to be signal, not noise): whitespace + comment
203
+ normalization; NO identifier normalization by default (that explodes
204
+ boilerplate false positives — Type-2 is the reviewer's job); minimum window
205
+ (~6 lines / ~50 tokens) below which everything matches; an ignore-list that
206
+ reuses the project's `.gitignore` + generated/vendored/lockfile/min/snapshot/
207
+ migration conventions; import-block and trivial-statement suppression. It emits
208
+ anchored candidates (`path:line` × the matching loci + clone type). It NEVER
209
+ edits and NEVER decides — it PROPOSES; the reviewer DISPOSES (verifies each
210
+ candidate, discards false positives, promotes real ones).
211
+ - **No external-tool tier in the floor** (deliberate, since v5.10.0): the floor
212
+ is the always-on, stack-agnostic capability. Richer external detectors (jscpd,
213
+ lizard, ast-grep, knip, vulture) are NOT wired here — they are 3-ecosystem
214
+ binaries whose silent presence would be non-deterministic capability drift, and
215
+ they break Codex portability. Should they ever be added, it is behind a
216
+ DEDICATED flag with full schema-change propagation + a documented Codex
217
+ fallback + a measured redundancy analysis vs this floor — never ambient
218
+ detection and never gated on `has_toolchain` (a proven false coupling: that
219
+ flag installs Biome/Vitest/tsc/Lefthook, none of these).
220
+
221
+ ### SECTION: doc-awareness
222
+
223
+ Reuse-miss (A1) is a retrieval problem, and the highest-signal source in a
224
+ BALDART consumer is the internal documentation that catalogs what already exists.
225
+ The reviewer MUST consult it — but under a strict posture, because docs drift.
226
+
227
+ **Retrieve-then-verify (BINDING)**: internal docs are a HIGH-RECALL INDEX for
228
+ FINDING reuse candidates; **the code is the SSOT** and CONFIRMS the match. Never
229
+ decide a reuse-miss on the doc alone (a stale registry entry may point at dead
230
+ code); never feed a textual doc proxy where the ground-truth code is readable
231
+ (the anti-assertion-fitting rule). Consult, in order of signal:
232
+
233
+ 1. **Component / utility registry** — `${paths.references_dir}/component-registry.md`
234
+ (the authoritative inventory of primitives, shared components, hooks, utility
235
+ modules). The #1 reuse map; scan it before grepping.
236
+ 2. **Design system** — `${paths.design_system}/INDEX.md` + `components/<Name>.md`
237
+ (UI reuse + closed-set members) when `features.has_design_system: true`.
238
+ 3. **Code graph** — Graphify `GRAPH_REPORT.md` (communities / god-node clusters)
239
+ + `graphify query`/`affected` when `features.has_code_graph: true`.
240
+ 4. **Symbol layer** — LSP `workspace/symbol` + `find-references` when
241
+ `features.has_lsp_layer: true`.
242
+ 5. **API / data-model / schemas** — `${paths.references_dir}/api/`,
243
+ `data-model.md` — to catch a reinvented endpoint/type/validator.
244
+ 6. **Wiki syntheses** — `${paths.wiki_dir}/` ("we already have a pattern X").
245
+ 7. **ADRs** — `${paths.adrs_dir}/` — a dependency/abstraction with a deliberate
246
+ ADR is NOT a removal candidate; respect the recorded intent.
247
+
248
+ **Do NOT duplicate the retrieval protocols** — cite them: the search hierarchy is
249
+ `agents/code-search-protocol.md`, the graph queries `agents/code-graph-protocol.md`,
250
+ the registry-first cascade `agents/design-system-protocol.md`, path resolution
251
+ `agents/project-context.md`. Universal fallback is grep (silent, never abort).
252
+
253
+ **Doc-drift is a handoff, not a fix** (strict specialization): if the reviewer
254
+ finds the registry stale w.r.t. a symbol (a new util duplicating an existing one
255
+ that is not in the registry, or an entry pointing at dead code), it emits an
256
+ **advisory** finding and hands the doc reconciliation to `doc-reviewer` — it does
257
+ NOT rewrite the registry itself.
258
+
259
+ ### SECTION: bias-guards
260
+
261
+ An LLM judge carries biases that are specifically corrosive to a *simplify* pass.
262
+ Neutralize them mechanically.
263
+
264
+ - **No verbosity reward (INVERTED for simplify)** — general LLM-judge verbosity
265
+ bias (MT-Bench; *Justice or Prejudice?* arXiv:2410.02736) rewards longer
266
+ answers. A simplify reviewer wants the SHORTER, fewer-elements solution; longer
267
+ code is a *negative* signal, never a positive. Never prefer a change because it
268
+ adds structure.
269
+ - **Independence (no self-grading)** — the reviewer is INDEPENDENT of the author:
270
+ fresh-context minimum, cross-model ideal. On a same-model run it is
271
+ fresh-context adversarial, NOT cross-model — state that honestly, never claim
272
+ cross-model diversity the run does not have. The author (`coder`) applies an
273
+ author-time subset (its § Author-Time Simplicity Discipline) but never grades
274
+ the final diff for reuse — that is this pass's exclusive, load-bearing job.
275
+ - **Evidence over opinion** — see the Contract's evidence rule; a survivor of the
276
+ generic Actionability pass that needs no change is a cleared concern, not a
277
+ finding.
278
+ - **Severity is absolute, not a quota** — zero findings on a clean diff is a
279
+ legitimate, expected outcome. Do NOT invent problems (the skill says it
280
+ verbatim). A false abstraction complaint causes churn — the anti-goal.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "5.8.0",
3
+ "version": "5.10.0",
4
4
  "description": "Claude Agent Framework - Reusable framework for coordinating AI agents and humans in software projects",
5
5
  "bin": {
6
6
  "baldart": "./bin/baldart.js"