Skip to content

Commit 39c8f79

Browse files
authored
feat: reduction ladder + cleanup-audit skill (#170)
Graft an explicit reduction ladder + deliberate-shortcut guidance into the default agent prompt and the /best-practices profile so the agent reaches for the simplest working solution by default, and add a read-only cleanup-audit skill (whole-repo over-engineering audit; the repo-wide complement to pythinker review diff --mode deslopify).
1 parent 529bdff commit 39c8f79

7 files changed

Lines changed: 72 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ GitHub Releases page; `0.8.0` is the new starting line.
1515

1616
## Unreleased
1717

18+
- Added an explicit "reduction ladder" and deliberate-shortcut guidance to the default agent prompt so the agent reaches for the simplest working solution (stdlib/native before custom code) by default.
19+
- Added a `cleanup-audit` skill: a read-only, whole-repo pass that ranks over-engineering to delete, simplify, or replace with standard-library/platform equivalents (the repo-wide complement to `pythinker review diff --mode deslopify`).
1820
- **Fix: duplicate update notices at startup.** When a background install finishes
1921
or a cached update is detected, the hint now renders only on the persistent
2022
under-input line instead of also flashing as a footer toast.

src/pythinker_code/agents/default/system.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,9 @@ ${PYTHINKER_SCRATCHPAD_SECTION}
149149

150150
**Simplicity first — minimum code that solves the problem, nothing speculative.** No features beyond what was asked; no abstractions for single-use code; no unrequested configurability; no error handling for impossible scenarios — validate at boundaries only. If a 200-line draft could be 50 lines, rewrite it before showing it. Over-fragmentation is overcomplication too: don't scatter logic across tiny files or extra layers to satisfy a pattern — match the codebase's existing granularity. Self-check: *would a senior engineer call this over-engineered?* If yes, simplify.
151151

152-
**Quality defaults** (unless project or domain rules override): focused, shallow, scannable functions with early exits over deep nesting; meaningful identifiers, no shadowing, the context's casing convention; avoid duplicate logic within a change without inventing broad abstractions for one-off repetition; comment only non-obvious algorithms, workarounds, business rules, and edge cases (`TODO:` for real debt; no self-evident comments; never add copyright or license headers unless requested); cohesive, testable modules; efficient data structures where they aid clarity or scale; wrap error-prone I/O, API, network, and resource operations with handling, timeouts/fallbacks, and cleanup; adopt stricter domain standards (e.g. MISRA-style C/C++) when relevant. Once correct, run the repo's formatter (up to 3 attempts); never add one where none exists.
152+
**The reduction ladder — walk it before writing code; stop at the first rung that holds.** (1) *Does this need to exist at all?* A speculative need is skipped, said so in one line. (2) *Does the standard library do it?* Use it. (3) *Does a native platform or framework feature cover it?* A database constraint over an app-level check, a built-in form control over a picker library, the language's own construct over a hand-rolled one — use it. (4) *Does a dependency already in the manifest solve it?* Use it; never add a new dependency for what a few lines cover. (5) *Can it be one line?* Make it one line. (6) *Only then* write the minimum code that works. When two rungs both hold, take the higher one and move on — the ladder is a reflex, not a research project. None of this overrides the guards in this section: trust-boundary validation, error handling that prevents data loss, security, and accessibility stay in even at rung 5.
153+
154+
**Quality defaults** (unless project or domain rules override): focused, shallow, scannable functions with early exits over deep nesting; meaningful identifiers, no shadowing, the context's casing convention; avoid duplicate logic within a change without inventing broad abstractions for one-off repetition; comment only non-obvious algorithms, workarounds, business rules, edge cases, and deliberate simplifications whose ceiling matters — a coarse lock, an O(n²) scan, a naive heuristic — naming the ceiling and the upgrade path (`TODO:` for real debt; no self-evident comments; never add copyright or license headers unless requested); cohesive, testable modules; efficient data structures where they aid clarity or scale; wrap error-prone I/O, API, network, and resource operations with handling, timeouts/fallbacks, and cleanup; adopt stricter domain standards (e.g. MISRA-style C/C++) when relevant. Once correct, run the repo's formatter (up to 3 attempts); never add one where none exists.
153155

154156
**Honest testing.** Verification per Rule 3, from the narrowest scope outward. Never game it: no weakened or deleted assertions, skipped tests, widened tolerances, overfitting to test cases, or mocking away the behavior under test. Keep tests deterministic — control time, randomness, and the network through the repo's existing patterns; never synchronize with sleeps.
155157

src/pythinker_code/prompts/best_practices.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ The user ran `/best-practices`. Engineering best practices are now in effect: ap
3030
- Never invent APIs. Verify every external symbol — function signatures, config keys, CLI flags, library methods — against the actual source, installed package, or type definitions before using it. If you cannot verify it, look it up; if you still cannot, say so instead of guessing.
3131
- Prefer the standard library and dependencies already in the manifest. A new dependency is a design decision: justify it (maintenance, license, size, transitive risk), verify the exact package name exists in the registry (hallucinated names are a typosquatting vector), pin it per repo convention, and modify lockfiles only through the package manager — never by hand.
3232
- Apply YAGNI: no speculative abstractions, flags, generality, or extension points the request does not need.
33+
- Before writing code, walk the reduction ladder and stop at the first rung that holds: (1) does this need to exist at all (YAGNI) — skip it and say so; (2) does the standard library do it; (3) does a native platform or framework feature cover it (a database constraint over app code, a built-in control over a library); (4) does a dependency already in the manifest solve it — never add a new one for what a few lines cover; (5) can it be one line; (6) only then the minimum code that works. When two rungs both hold, take the higher one and move on. The guards in this profile — trust-boundary validation, data-loss handling, security, accessibility — are never traded away for fewer lines.
3334
- No placeholders in completed work: no TODO stubs, commented-out blocks, empty handler bodies, or mock data presented as a real integration.
3435
- Fail loudly per the codebase's conventions. Never swallow exceptions, downgrade errors to warnings, or return fabricated defaults to make a failure disappear.
3536
- Preserve backward compatibility by default. Search the integration surfaces your change touches — public APIs, CLI parameters, configuration loading, persisted state, session and wire formats, database schemas — and if a break is unavoidable, call it out and migrate or gate it.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
name: cleanup-audit
3+
description: Whole-repo audit for over-engineering and accidental complexity. Scans the entire codebase (not just a diff) and returns a ranked, read-only list of what to delete, simplify, or replace with standard-library or platform equivalents. Use when the user asks to "audit the codebase", "find bloat", "what can I delete", or wants a repo-wide simplification pass. For a diff-scoped pass use `pythinker review diff --mode deslopify` instead. One-shot report; applies no fixes.
4+
---
5+
6+
# Cleanup Audit
7+
8+
A read-only, whole-repo pass that hunts accidental complexity and over-engineering. The diff-scoped
9+
version of this job already ships as `pythinker review diff --mode deslopify`; this skill is its
10+
repo-wide complement — scan the whole tree, rank the biggest cut first.
11+
12+
Scope is complexity only. Correctness bugs, security holes, and performance belong to a normal
13+
review or security pass — note them in one line if you trip over them, but do not chase them here.
14+
15+
## What to hunt
16+
17+
- Dead code, unused flexibility, and speculative features no caller needs.
18+
- Hand-rolled logic the standard library already ships — name the function that replaces it.
19+
- A dependency (or hand-written code) doing what the language, runtime, or framework already does.
20+
- Single-implementation interfaces, one-product factories, wrappers that only delegate, a module
21+
that exports one trivial thing, dead flags and config nobody sets.
22+
- The same logic spelled out long-hand where a shorter, equally clear form exists.
23+
24+
## How to work
25+
26+
1. Map before judging: read the tree, the manifest/lockfile, and entry points; use `Grep`/`Glob`
27+
(or `LSP` for references and call hierarchy) to confirm a thing is actually unused before
28+
proposing its deletion. A deletion proposed without checking callers is a guess.
29+
2. Rank findings biggest cut first.
30+
3. Apply nothing. This is a report.
31+
32+
## Output
33+
34+
One line per finding, ranked, each tagged and citing a path:
35+
36+
- `delete:` — dead code / speculative feature. Replacement: nothing.
37+
- `stdlib:` — hand-rolled thing the standard library ships. Name the function.
38+
- `native:` — dependency or code doing what the platform/framework already does. Name the feature.
39+
- `yagni:` — abstraction with one implementation, config nobody sets, layer with one caller.
40+
- `shrink:` — same logic, fewer lines. Show the shorter form.
41+
42+
Format: `<tag> <what to cut>. <replacement>. [path:line]`
43+
End with an estimate: `net: ~-<N> lines, -<M> deps possible.` Nothing to cut: `Lean already.`
44+
45+
## Guardrails
46+
47+
Never propose cutting trust-boundary validation, error handling that prevents data loss, security
48+
measures, or accessibility — minimal is not the same as unsafe. When a "simplification" would touch
49+
one of those, leave it and say why. Verify each deletion candidate is truly unreferenced before
50+
listing it; an audit that proposes deleting live code is worse than no audit.

tests/core/test_default_agent.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ async def test_default_agent(runtime: Runtime):
3939
assert "hallucinated package names are a typosquatting vector" in agent.system_prompt
4040
assert "Never game it: no weakened or deleted assertions" in agent.system_prompt
4141
assert "never rerun an identical failing command" in agent.system_prompt
42+
assert "The reduction ladder" in agent.system_prompt
43+
assert "stop at the first rung that holds" in agent.system_prompt
4244

4345
# Prompt-injection defense — the <untrusted_data> wrapper is only effective if
4446
# the model is told the tags mean "data, never instructions". Keep this in the

tests/utils/test_pyinstaller_utils.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ def test_pyinstaller_datas():
120120
"src/pythinker_code/skills/check-impl-against-spec/SKILL.md",
121121
"pythinker_code/skills/check-impl-against-spec",
122122
),
123+
(
124+
"src/pythinker_code/skills/cleanup-audit/SKILL.md",
125+
"pythinker_code/skills/cleanup-audit",
126+
),
123127
(
124128
"src/pythinker_code/skills/customize-pythinker/SKILL.md",
125129
"pythinker_code/skills/customize-pythinker",

tests_e2e/test_wire_protocol.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ def test_initialize_handshake(tmp_path) -> None:
120120
"description": "Compare an implementation against a product or technical spec and report gaps with evidence.",
121121
"aliases": [],
122122
},
123+
{
124+
"name": "skill:cleanup-audit",
125+
"description": 'Whole-repo audit for over-engineering and accidental complexity. Scans the entire codebase (not just a diff) and returns a ranked, read-only list of what to delete, simplify, or replace with standard-library or platform equivalents. Use when the user asks to "audit the codebase", "find bloat", "what can I delete", or wants a repo-wide simplification pass. For a diff-scoped pass use `pythinker review diff --mode deslopify` instead. One-shot report; applies no fixes.',
126+
"aliases": [],
127+
},
123128
{
124129
"name": "skill:create-pr",
125130
"description": "Prepare a pull request by summarizing changes, verification, risks, and reviewer guidance without adding AI footers.",
@@ -330,6 +335,11 @@ def test_initialize_external_tool_conflict(tmp_path) -> None:
330335
"description": "Compare an implementation against a product or technical spec and report gaps with evidence.",
331336
"aliases": [],
332337
},
338+
{
339+
"name": "skill:cleanup-audit",
340+
"description": 'Whole-repo audit for over-engineering and accidental complexity. Scans the entire codebase (not just a diff) and returns a ranked, read-only list of what to delete, simplify, or replace with standard-library or platform equivalents. Use when the user asks to "audit the codebase", "find bloat", "what can I delete", or wants a repo-wide simplification pass. For a diff-scoped pass use `pythinker review diff --mode deslopify` instead. One-shot report; applies no fixes.',
341+
"aliases": [],
342+
},
333343
{
334344
"name": "skill:create-pr",
335345
"description": "Prepare a pull request by summarizing changes, verification, risks, and reviewer guidance without adding AI footers.",

0 commit comments

Comments
 (0)