anti-slop-engineering 0.4.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.skills.json +1 -1
- package/README.md +29 -6
- package/SKILL.md +4 -1
- package/bin/cli.js +76 -20
- package/marketplace.json +1 -1
- package/package.json +13 -6
- package/references/anti-pattern-catalog.md +2 -0
- package/references/clean-room-recon.md +7 -7
- package/references/engineering-judgment.md +3 -0
- package/scripts/decision_guard.py +199 -0
- package/scripts/sre_guard.py +1 -1
- package/services/anti-slop-rust/Cargo.lock +757 -0
- package/services/anti-slop-rust/Cargo.toml +12 -0
- package/services/anti-slop-rust/src/ladder_auditor.rs +112 -0
- package/services/anti-slop-rust/src/lib.rs +66 -0
- package/services/anti-slop-rust/src/main.rs +197 -0
- package/services/anti-slop-rust/src/model.rs +76 -0
- package/services/anti-slop-rust/src/path_utils.rs +18 -0
- package/services/anti-slop-rust/src/server.rs +100 -0
- package/services/anti-slop-rust/src/shit_interceptor.rs +247 -0
- package/services/toil-guard-go/cmd/toil-guard/main.go +271 -0
- package/services/toil-guard-go/cmd/toil-guard/main_test.go +36 -0
- package/services/toil-guard-go/go.mod +3 -0
- package/services/toil-guard-go/pkg/model/types.go +61 -0
- package/services/toil-guard-go/pkg/server/server.go +142 -0
- package/services/toil-guard-go/pkg/server/server_test.go +77 -0
- package/services/toil-guard-go/pkg/sre/audit.go +27 -0
- package/services/toil-guard-go/pkg/sre/complexity.go +158 -0
- package/services/toil-guard-go/pkg/sre/complexity_test.go +81 -0
- package/services/toil-guard-go/pkg/sre/dry.go +103 -0
- package/services/toil-guard-go/pkg/sre/dry_test.go +84 -0
- package/services/toil-guard-go/pkg/sre/sli.go +77 -0
- package/services/toil-guard-go/pkg/sre/sli_test.go +55 -0
- package/services/toil-guard-go/pkg/sre/toil.go +98 -0
- package/services/toil-guard-go/pkg/sre/toil_test.go +53 -0
- package/skills/decision-guard/SKILL.md +103 -0
- package/skills/decision-guard/evals/scenarios.json +78 -0
- package/skills/decision-guard/examples/architecture-record.json +35 -0
- package/skills/decision-guard/references/decision-method.md +46 -0
- package/skills/decision-guard/references/decision-record.md +23 -0
- package/skills/decision-guard/usecases/architect.md +20 -0
- package/skills/decision-guard/usecases/investigate.md +19 -0
package/.skills.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://skills.sh/schema.json",
|
|
3
3
|
"name": "anti-slop-engineering",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"description": "An Engineering Discipline Playbook & Guard System for AI Coding Agents. Enforces Google SRE, Clean-Room Senior Dev Ladders, and Rust/Go Microservices.",
|
|
6
6
|
"repository": "https://github.com/imMamdouhaboammar/anti-slop-engineering",
|
|
7
7
|
"author": "Mamdouh Aboammar",
|
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
[](https://skills.sh)
|
|
13
13
|
[](https://claude.ai)
|
|
14
14
|
|
|
15
|
-
[Install](#install) · [What it catches](#what-it-catches) · [Engines](#engines) · [Agent skill](#agent-skill) · [Anti-pattern catalog](#anti-pattern-catalog-
|
|
15
|
+
[Install](#install) · [What it catches](#what-it-catches) · [Engines](#engines) · [Decision Guard](#decision-guard) · [Agent skill](#agent-skill) · [Anti-pattern catalog](#anti-pattern-catalog-215-patterns)
|
|
16
16
|
|
|
17
17
|
</div>
|
|
18
18
|
|
|
@@ -139,7 +139,7 @@ bunx anti-slop-engineering sre --max-nesting 4 --min-dup-lines 4 --max-function-
|
|
|
139
139
|
|
|
140
140
|
### Rust SHIT Interceptor (`services/anti-slop-rust`)
|
|
141
141
|
|
|
142
|
-
A high-performance Axum HTTP microservice that audits diffs and source for
|
|
142
|
+
A high-performance Axum HTTP microservice that audits diffs and source for Scope creep (`S`), Hypothetical hardening (`H`), and Intent violation (`I`) in diffs, plus regex-based Senior Dev Ladder checks for hand-rolled alternatives to common platform primitives. Task-thrashing detection is not implemented.
|
|
143
143
|
|
|
144
144
|
```bash
|
|
145
145
|
# Triage git changes through Rust engine:
|
|
@@ -158,9 +158,9 @@ A concurrent Go microservice for high-throughput SRE metric verification. Runs K
|
|
|
158
158
|
|
|
159
159
|
```bash
|
|
160
160
|
# SRE audit across repository:
|
|
161
|
-
bunx anti-slop-engineering toil audit
|
|
161
|
+
bunx anti-slop-engineering toil audit .
|
|
162
162
|
|
|
163
|
-
# Live git
|
|
163
|
+
# Live git diff statistics (not a live error-budget calculation):
|
|
164
164
|
bunx anti-slop-engineering toil stream
|
|
165
165
|
|
|
166
166
|
# Start Go microservice daemon:
|
|
@@ -201,6 +201,29 @@ flowchart TD
|
|
|
201
201
|
|
|
202
202
|
---
|
|
203
203
|
|
|
204
|
+
## Decision Guard
|
|
205
|
+
|
|
206
|
+
The [Decision Guard module](skills/decision-guard/SKILL.md) addresses two decisions the code diff cannot catch: deriving an answer that already exists directly, and choosing a familiar technology stack without testing the product's actual requirements and future operating costs.
|
|
207
|
+
|
|
208
|
+
| Situation | Agent behavior |
|
|
209
|
+
| --- | --- |
|
|
210
|
+
| Seven metrics infer a value present in a source field | Verify the field's semantics and propose using the direct answer |
|
|
211
|
+
| A logged-in app triggers an automatic Next.js/Vercel/database bundle | Separate rendering, jobs, hosting, persistence and operations choices |
|
|
212
|
+
| VPS appears cheaper than managed infrastructure | Compare recovery, maintenance, on-call, migration and actual workload |
|
|
213
|
+
| New evidence invalidates days of prior work | Interrupt, explain the change and offer the smallest corrected path |
|
|
214
|
+
|
|
215
|
+
The module is loaded selectively during Gate 0/1 for consequential decisions. It includes a portable skill, two focused workflows, decision references, nine evaluation scenarios and a standard-library JSON record checker.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
node bin/cli.js decision audit skills/decision-guard/examples/architecture-record.json --json
|
|
219
|
+
# Or, using the package CLI:
|
|
220
|
+
npx anti-slop-engineering decision audit path/to/decision.json --json
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Exit `0` means the record is structurally complete, `2` means required decision evidence is missing, and `1` means unreadable or malformed input. These are **not** claims that the chosen architecture is correct or that sources were verified. A complete record can intentionally recommend `pause` or `stop`.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
204
227
|
## Agent skill
|
|
205
228
|
|
|
206
229
|
The agent skill wires Anti-Slop Engineering into your AI coding agent's context. Once installed, every code change the agent generates is governed by the Seven Gates, the 213-pattern anti-pattern catalog, and the Google SRE enforcement rules.
|
|
@@ -223,9 +246,9 @@ When installed via `npm install anti-slop-engineering` or `bun add anti-slop-eng
|
|
|
223
246
|
|
|
224
247
|
---
|
|
225
248
|
|
|
226
|
-
## Anti-pattern catalog (
|
|
249
|
+
## Anti-pattern catalog (215 patterns)
|
|
227
250
|
|
|
228
|
-
The catalog in [`references/anti-pattern-catalog.md`](references/anti-pattern-catalog.md) documents
|
|
251
|
+
The catalog in [`references/anti-pattern-catalog.md`](references/anti-pattern-catalog.md) documents 215 diagnostic patterns across 14 engineering domains, each with a canonical ID, symptom, root cause, and remediation.
|
|
229
252
|
|
|
230
253
|
**Domains covered:**
|
|
231
254
|
|
package/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: Prevent, detect, and repair AI coding slop and dirty engineering in
|
|
|
4
4
|
author: Mamdouh Aboammar <mamdouhfces1997@gmail.com>
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
|
-
version: "
|
|
7
|
+
version: "1.1.0"
|
|
8
8
|
focus: "engineering-discipline"
|
|
9
9
|
repository: "https://github.com/imMamdouhaboammar/anti-slop-engineering"
|
|
10
10
|
---
|
|
@@ -37,6 +37,7 @@ metadata:
|
|
|
37
37
|
- Climb the Senior Dev Ladder before authoring code: (1) Does this need to exist? (2) Does it already exist in the codebase? (3) Does the stdlib/platform do it? (4) Does native runtime cover it? (5) Does an existing dependency solve it? (6) Can it be a direct expression? (7) Write only minimal, necessary, tested code.
|
|
38
38
|
- **Name the Built-in:** Before writing custom parsing, validation, or UUID/cloning utilities, name the platform built-in (`crypto.randomUUID()`, `URLSearchParams`, `structuredClone()`). Hand-rolling standard primitives is a defect.
|
|
39
39
|
- **Practical DRY 3-Point Barrier:** Shared utilities or abstractions are permitted only when appearing in >=2 real existing call-sites, when all call-sites require identical modifications upon change, and when the abstraction can be named without coordinating conjunctions (`and`, `or`).
|
|
40
|
+
15. **Decision Guard before consequential commitments:** When the task proposes derived metrics, an architectural stack, infrastructure, persistence, or a difficult-to-reverse engineering decision, read [Decision Guard](skills/decision-guard/SKILL.md). Check direct evidence and independent alternatives before treating the proposed method as necessary. Never substitute the decision record's structural check for source verification.
|
|
40
41
|
|
|
41
42
|
## Activation / modes
|
|
42
43
|
|
|
@@ -47,6 +48,7 @@ Select the relevant mode and follow the same gates:
|
|
|
47
48
|
- **REVIEW:** read-only diff and repository-context audit; report findings with severity, evidence, and counterfactual.
|
|
48
49
|
- **CLEANUP:** establish an observable behavior baseline, isolate pure refactors from feature changes, prove no drift.
|
|
49
50
|
- **NOTED_SLOP:** codify newly observed AI slop into a diagnostic catalog pattern, agent directive, and automated diff guard heuristic (see [CONTRIBUTING.md](CONTRIBUTING.md)).
|
|
51
|
+
- **DECIDE:** when premises, direct answers or long-term architecture choices are materially uncertain, follow [Decision Guard](skills/decision-guard/SKILL.md) before BUILD. Return to ordinary modes after a decision; skip the extra protocol for routine repairs.
|
|
50
52
|
|
|
51
53
|
For extensive audits, load [anti-pattern catalog](references/anti-pattern-catalog.md). For Google SRE discipline and distributed resilience patterns, load [google-sre-discipline](references/google-sre-discipline.md). For any test changes, load [test integrity policy](references/test-integrity.md). For a PR or merge request, load [PR review protocol](references/pr-review.md). For elevated permissions, sensitive code, network access, or deployment, load [security and execution boundaries](references/security-and-operations.md). For project-specific exceptions, load [judgment and exceptions](references/engineering-judgment.md). For community contributions, follow [contributor guidelines](CONTRIBUTING.md).
|
|
52
54
|
|
|
@@ -59,6 +61,7 @@ For extensive audits, load [anti-pattern catalog](references/anti-pattern-catalo
|
|
|
59
61
|
- Read relevant open issues/PRs when available to avoid duplicates or fighting in-flight changes. Do not require network access to proceed when unavailable.
|
|
60
62
|
- Record working tree status; do not overwrite user changes, reset, stash, or modify unrelated files without authorization.
|
|
61
63
|
- Run an appropriate baseline build/test if feasible; label any existing failure as baseline, not caused by this change.
|
|
64
|
+
- For a derived-answer task, inspect existing direct sources before constructing proxies. For an architectural commitment, inspect the product's actual rendering, workload, operations and persistence needs instead of defaulting to a familiar provider bundle. Load `skills/decision-guard/SKILL.md` only when this uncertainty is material.
|
|
62
65
|
- For new projects, services, components, or SDK integrations, identify the official installer, CLI generator, and canonical starter boilerplate before writing code. Do not manually assemble configurations or directory trees from memory. Never generate unprompted documentation files, submission dossiers, or speculative third-party platform guides.
|
|
63
66
|
|
|
64
67
|
### Gate 1: Establish a change contract
|
package/bin/cli.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// Copyright (c) 2026 Mamdouh Aboammar <mamdouhfces1997@gmail.com> and contributors
|
|
4
4
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
|
-
import { existsSync } from "node:fs";
|
|
7
|
-
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
7
|
+
import { basename, dirname, resolve } from "node:path";
|
|
8
8
|
import process from "node:process";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
|
|
@@ -13,18 +13,26 @@ const __dirname = dirname(__filename);
|
|
|
13
13
|
const rootDir = resolve(__dirname, "..");
|
|
14
14
|
const diffGuardScript = resolve(rootDir, "scripts", "diff_guard.py");
|
|
15
15
|
const sreGuardScript = resolve(rootDir, "scripts", "sre_guard.py");
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
16
|
+
const decisionGuardScript = resolve(rootDir, "scripts", "decision_guard.py");
|
|
17
|
+
const nativeSuffix = process.platform === "win32" ? ".exe" : "";
|
|
18
|
+
const rustExecutable = "anti-slop-rust" + nativeSuffix;
|
|
19
|
+
const goExecutable = "toil-guard" + nativeSuffix;
|
|
20
|
+
const disableNative = process.env.ANTI_SLOP_DISABLE_NATIVE === "1" || process.env.ANTI_SLOP_FORCE_FALLBACK === "1";
|
|
21
|
+
const rustBinPath = !disableNative && existsSync(resolve(rootDir, "dist", "bin", rustExecutable))
|
|
22
|
+
? resolve(rootDir, "dist", "bin", rustExecutable)
|
|
23
|
+
: (!disableNative ? resolve(rootDir, "services", "anti-slop-rust", "target", "release", rustExecutable) : "");
|
|
24
|
+
const goBinPath = !disableNative && existsSync(resolve(rootDir, "dist", "bin", goExecutable))
|
|
25
|
+
? resolve(rootDir, "dist", "bin", goExecutable)
|
|
26
|
+
: (!disableNative ? resolve(rootDir, "services", "toil-guard-go", "bin", goExecutable) : "");
|
|
27
|
+
|
|
28
|
+
const invokedAs = basename(process.argv[1] ?? "").replace(/\.(?:js|cmd|exe)$/i, "");
|
|
29
|
+
const executableAliases = { "sre-guard": "sre", "rust-guard": "rust", "toil-guard": "toil" };
|
|
23
30
|
const args = process.argv.slice(2);
|
|
31
|
+
if (executableAliases[invokedAs]) args.unshift(executableAliases[invokedAs]);
|
|
24
32
|
|
|
25
33
|
function printBanner() {
|
|
26
34
|
console.log(`
|
|
27
|
-
🛡️ Anti-Slop Engineering CLI (
|
|
35
|
+
🛡️ Anti-Slop Engineering CLI (v1.1.0)
|
|
28
36
|
Clean-Room AI Coding Guard & High-Performance Polyglot Architecture
|
|
29
37
|
Engines: Standard Library Python Guard | Rust Microservice | Go Toil Guard
|
|
30
38
|
Author: Mamdouh Aboammar <mamdouhfces1997@gmail.com>
|
|
@@ -35,12 +43,13 @@ Usage:
|
|
|
35
43
|
bunx anti-slop-engineering [command] [options]
|
|
36
44
|
|
|
37
45
|
Commands:
|
|
38
|
-
rust, sts Run native Rust Engine (Scope Creep, Hypothetical Hardening, Intent,
|
|
46
|
+
rust, sts Run native Rust Engine (Scope Creep, Hypothetical Hardening, Intent, Senior Dev Ladder)
|
|
39
47
|
toil, go-guard Run Go SRE Engine (KISS nesting, DRY token window, Toil & Error Budget)
|
|
40
48
|
serve-rust Start background Rust HTTP microservice daemon (default: :9091)
|
|
41
49
|
serve-go Start background Go HTTP SRE microservice daemon (default: :9092)
|
|
42
50
|
build Compile both Rust and Go microservices
|
|
43
51
|
sre, sre-guard Run Python Google SRE Guard (zero-dependency stdlib fallback)
|
|
52
|
+
decision audit <file> Check a Decision Guard JSON record for missing evidence and trade-offs
|
|
44
53
|
test Run unit test suite
|
|
45
54
|
contribute Print guidelines on reporting noted slop or opening PRs
|
|
46
55
|
|
|
@@ -64,7 +73,7 @@ function spawnProcess(execPath, execArgs, label) {
|
|
|
64
73
|
process.exit(1);
|
|
65
74
|
});
|
|
66
75
|
child.on("close", (code) => {
|
|
67
|
-
process.exit(code ??
|
|
76
|
+
process.exit(code ?? 1); // Signal termination must never be reported as success.
|
|
68
77
|
});
|
|
69
78
|
}
|
|
70
79
|
|
|
@@ -88,6 +97,54 @@ function runPythonScript(scriptPath, scriptArgs, scriptLabel) {
|
|
|
88
97
|
spawnProcess("python3", [scriptPath, ...scriptArgs], scriptLabel);
|
|
89
98
|
}
|
|
90
99
|
|
|
100
|
+
function unsupportedFallback(engine, request) {
|
|
101
|
+
console.error(`Error: ${engine} native binary is required for ${request}. Run 'anti-slop-engineering build'.`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function extractCommonFlags(flags, engine, onUnrecognized) {
|
|
106
|
+
const mapped = [];
|
|
107
|
+
for (let i = 0; i < flags.length; i++) {
|
|
108
|
+
const flag = flags[i];
|
|
109
|
+
if (flag === "--base" && flags[i + 1]) {
|
|
110
|
+
mapped.push("--base", flags[++i]);
|
|
111
|
+
} else if (flag === "--format" && flags[i + 1] === "json") {
|
|
112
|
+
mapped.push("--json");
|
|
113
|
+
i++;
|
|
114
|
+
} else if (flag === "--format" && flags[i + 1] === "text") {
|
|
115
|
+
i++;
|
|
116
|
+
} else if (onUnrecognized) {
|
|
117
|
+
onUnrecognized(flag);
|
|
118
|
+
} else {
|
|
119
|
+
unsupportedFallback(engine, `option ${flag}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return mapped;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function runRustFallback(request) {
|
|
126
|
+
const [command, ...flags] = request;
|
|
127
|
+
if (command !== "triage") unsupportedFallback("Rust", command);
|
|
128
|
+
const mapped = ["--fail-on-blocker", ...extractCommonFlags(flags, "Rust")];
|
|
129
|
+
runPythonScript(diffGuardScript, mapped, "diff_guard");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function runGoFallback(request) {
|
|
133
|
+
const [command, ...flags] = request;
|
|
134
|
+
if (command !== "audit") unsupportedFallback("Go", command);
|
|
135
|
+
const paths = [];
|
|
136
|
+
const mapped = extractCommonFlags(flags, "Go", (flag) => {
|
|
137
|
+
if (flag.startsWith("-")) unsupportedFallback("Go", `audit option ${flag}`);
|
|
138
|
+
else paths.push(flag);
|
|
139
|
+
});
|
|
140
|
+
if (paths.length > 0) {
|
|
141
|
+
if (mapped.includes("--base")) unsupportedFallback("Go", "audit with both --base and paths");
|
|
142
|
+
mapped.push("--files", ...paths);
|
|
143
|
+
} else if (!mapped.includes("--base")) mapped.push("--base", "HEAD");
|
|
144
|
+
mapped.push("--strict");
|
|
145
|
+
runPythonScript(sreGuardScript, mapped, "sre_guard");
|
|
146
|
+
}
|
|
147
|
+
|
|
91
148
|
function runBuild() {
|
|
92
149
|
console.log("Compiling Rust microservice...");
|
|
93
150
|
const cargo = spawn("cargo", ["build", "--release"], {
|
|
@@ -102,7 +159,8 @@ function runBuild() {
|
|
|
102
159
|
}
|
|
103
160
|
|
|
104
161
|
console.log("Compiling Go microservice...");
|
|
105
|
-
|
|
162
|
+
mkdirSync(resolve(rootDir, "services", "toil-guard-go", "bin"), { recursive: true });
|
|
163
|
+
const goBuild = spawn("go", ["build", "-o", "bin/" + goExecutable, "./cmd/toil-guard"], {
|
|
106
164
|
cwd: resolve(rootDir, "services", "toil-guard-go"),
|
|
107
165
|
stdio: "inherit",
|
|
108
166
|
});
|
|
@@ -125,21 +183,19 @@ if (cmd === "build") {
|
|
|
125
183
|
} else if (cmd === "rust" || cmd === "rust-guard" || cmd === "sts") {
|
|
126
184
|
const subArgs = args.slice(1);
|
|
127
185
|
const forwardArgs = subArgs.length > 0 ? subArgs : ["triage"];
|
|
128
|
-
runBinary(rustBinPath, forwardArgs, "anti-slop-rust", () =>
|
|
129
|
-
runPythonScript(diffGuardScript, ["--base", "HEAD"], "diff_guard");
|
|
130
|
-
});
|
|
186
|
+
runBinary(rustBinPath, forwardArgs, "anti-slop-rust", () => runRustFallback(forwardArgs));
|
|
131
187
|
} else if (cmd === "toil" || cmd === "toil-guard" || cmd === "go-guard") {
|
|
132
188
|
const subArgs = args.slice(1);
|
|
133
189
|
const forwardArgs = subArgs.length > 0 ? subArgs : ["audit"];
|
|
134
|
-
runBinary(goBinPath, forwardArgs, "toil-guard-go", () =>
|
|
135
|
-
runPythonScript(sreGuardScript, ["--base", "HEAD"], "sre_guard");
|
|
136
|
-
});
|
|
190
|
+
runBinary(goBinPath, forwardArgs, "toil-guard-go", () => runGoFallback(forwardArgs));
|
|
137
191
|
} else if (cmd === "serve-rust") {
|
|
138
192
|
runBinary(rustBinPath, ["serve", ...args.slice(1)], "anti-slop-rust daemon", null);
|
|
139
193
|
} else if (cmd === "serve-go") {
|
|
140
194
|
runBinary(goBinPath, ["serve", ...args.slice(1)], "toil-guard-go daemon", null);
|
|
141
195
|
} else if (cmd === "sre" || cmd === "sre-guard") {
|
|
142
196
|
runPythonScript(sreGuardScript, args.slice(1), "sre_guard");
|
|
197
|
+
} else if (cmd === "decision") {
|
|
198
|
+
runPythonScript(decisionGuardScript, args.slice(1), "decision_guard");
|
|
143
199
|
} else if (args.includes("--help") || args.includes("-h")) {
|
|
144
200
|
printBanner();
|
|
145
201
|
process.exit(0);
|
|
@@ -164,7 +220,7 @@ Path B: Submit a PR (Problem + Solution Integrated)
|
|
|
164
220
|
cwd: rootDir,
|
|
165
221
|
stdio: "inherit",
|
|
166
222
|
});
|
|
167
|
-
pyTest.on("close", (code) => process.exit(code ??
|
|
223
|
+
pyTest.on("close", (code) => process.exit(code ?? 1));
|
|
168
224
|
} else if (cmd === "test-all") {
|
|
169
225
|
spawnProcess("make", ["test-all"], "test-all");
|
|
170
226
|
} else if (cmd === "check-all") {
|
package/marketplace.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-plugin-manifest.json",
|
|
3
3
|
"name": "anti-slop-engineering",
|
|
4
4
|
"displayName": "Anti-Slop Engineering",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "1.1.0",
|
|
6
6
|
"description": "An Engineering Discipline Playbook, Agent Skill & Triage Guard for AI Coding Agents. Enforces behavioral contracts, focused diffs, test integrity, Google SRE principles, and native Rust/Go clean-room microservices.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Mamdouh Aboammar",
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "anti-slop-engineering",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "An Engineering Discipline Playbook, Agent Skill & Triage Guard for AI Coding Agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/cli.js",
|
|
7
7
|
"bin": {
|
|
8
|
-
"anti-slop-engineering": "
|
|
9
|
-
"diff-guard": "
|
|
10
|
-
"sre-guard": "
|
|
11
|
-
"rust-guard": "
|
|
12
|
-
"toil-guard": "
|
|
8
|
+
"anti-slop-engineering": "bin/cli.js",
|
|
9
|
+
"diff-guard": "bin/cli.js",
|
|
10
|
+
"sre-guard": "bin/cli.js",
|
|
11
|
+
"rust-guard": "bin/cli.js",
|
|
12
|
+
"toil-guard": "bin/cli.js"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -22,6 +22,13 @@
|
|
|
22
22
|
"files": [
|
|
23
23
|
"bin",
|
|
24
24
|
"scripts",
|
|
25
|
+
"skills",
|
|
26
|
+
"services/anti-slop-rust/Cargo.toml",
|
|
27
|
+
"services/anti-slop-rust/Cargo.lock",
|
|
28
|
+
"services/anti-slop-rust/src",
|
|
29
|
+
"services/toil-guard-go/go.mod",
|
|
30
|
+
"services/toil-guard-go/cmd",
|
|
31
|
+
"services/toil-guard-go/pkg",
|
|
25
32
|
"references",
|
|
26
33
|
"assets",
|
|
27
34
|
"SKILL.md",
|
|
@@ -18,6 +18,7 @@ Format: **ID | pattern | how to verify / safer alternative**. Numbers are stable
|
|
|
18
18
|
- **REQ-10 | Skipped acceptance criteria.** PR satisfies prose loosely but not contract; maintain acceptance evidence mapping.
|
|
19
19
|
- **REQ-11 | Incorrect partial-success semantics.** Batch reports complete when only half succeeded; model outcomes and retryable failures.
|
|
20
20
|
- **REQ-12 | Placeholder leakage.** Temporary behavior, fake data, TODO 'later' shipped as final; enumerate and resolve release blockers.
|
|
21
|
+
- **REQ-13 | Indirect-answer tunnel.** Agent constructs proxy metrics or inference chains while the answer already exists in an accessible source; inspect direct fields and their semantics before calculating.
|
|
21
22
|
|
|
22
23
|
## B. Architecture, boundaries, and repository consistency
|
|
23
24
|
|
|
@@ -36,6 +37,7 @@ Format: **ID | pattern | how to verify / safer alternative**. Numbers are stable
|
|
|
36
37
|
- **ARC-13 | Inappropriate inheritance.** A shared base class couples unrelated behavior; prefer composition only if it clarifies.
|
|
37
38
|
- **ARC-14 | Leaky plugin architecture.** Dynamic extension point without real callers; reduce indirection.
|
|
38
39
|
- **ARC-15 | Manual scaffolding improvisation.** Hand-crafting directory structures, build configs, and boilerplate files (`tsconfig.json`, `package.json`, `pyproject.toml`, Dockerfiles) from memory instead of using the official framework/language CLI installer, starter template, or generator; introduces missing build flags, non-standard layouts, and broken tooling.
|
|
40
|
+
- **ARC-16 | Familiar-stack default and short-horizon choice.** Agent bundles framework, host and database by popularity without testing actual workload, operations, recovery, exit paths or credible future states; compare independent axes, constraints and reversibility. Managed services and VPS are each legitimate where evidence supports them.
|
|
39
41
|
|
|
40
42
|
## C. Simplicity, duplication, and code readability
|
|
41
43
|
|
|
@@ -59,7 +59,7 @@ No code from any reference repository is copied or decompiled. Only functional m
|
|
|
59
59
|
| - Scope Creep Detector | | • Concurrency & Toil Guard |
|
|
60
60
|
| - Hypothetical Hardening | | • Sliding-Window DRY Scanner |
|
|
61
61
|
| - Intent Violation Gate | | • SLI/SLO Error Budget Engine|
|
|
62
|
-
| -
|
|
62
|
+
| - Rule-based diff checks | | • Prometheus /metrics Server |
|
|
63
63
|
| • Senior Dev Ladder Auditor | | • HTTP REST Service (/v1/..) |
|
|
64
64
|
| • Axum REST Server (/v1/..) | +------------------------------+
|
|
65
65
|
+------------------------------+ |
|
|
@@ -75,19 +75,19 @@ No code from any reference repository is copied or decompiled. Only functional m
|
|
|
75
75
|
```
|
|
76
76
|
|
|
77
77
|
### 2.1 Rust Microservice (`services/anti-slop-rust`)
|
|
78
|
-
- **Focus:**
|
|
78
|
+
- **Focus:** Regex-based diff triage for scope, speculative hardening, and read-only intent, plus simple built-in replacement checks.
|
|
79
79
|
- **Key Modules:**
|
|
80
80
|
- `src/shit_interceptor.rs`: Scope creep detection, unread checksum/hash generation checks, speculative try-catch error swallowing, and file boundary guards.
|
|
81
|
-
- `src/ladder_auditor.rs`:
|
|
82
|
-
- `src/server.rs`: Lightweight HTTP microservice (`axum` + `tokio`) exposing `/health`, `/v1/triage`, `/v1/shit-check`, and `/v1/ladder`.
|
|
81
|
+
- `src/ladder_auditor.rs`: Regex-based built-in replacement suggestions and conjunction naming hints.
|
|
82
|
+
- `src/server.rs`: Lightweight HTTP microservice (`axum` + `tokio`) exposing `/health`, `/v1/triage`, `/v1/shit-check`, and `/v1/ladder-audit`.
|
|
83
83
|
- `src/main.rs`: High-performance CLI interface powered by `clap`.
|
|
84
84
|
|
|
85
85
|
### 2.2 Go Microservice (`services/toil-guard-go`)
|
|
86
86
|
- **Focus:** Concurrent repository-wide stream analysis, Google SRE metric verification (KISS, DRY, YAGNI, Scale), automated toil detection, and SLI/SLO budget verification.
|
|
87
87
|
- **Key Modules:**
|
|
88
88
|
- `pkg/sre/complexity.go`: Control flow nesting level (<=4) and function length (<=80 lines) verification.
|
|
89
|
-
- `pkg/sre/
|
|
90
|
-
- `pkg/sre/toil.go`: SRE anti-pattern heuristics (
|
|
89
|
+
- `pkg/sre/dry.go`: Sliding-window token hash scanner across files.
|
|
90
|
+
- `pkg/sre/toil.go`: SRE anti-pattern heuristics (blind sleep synchronization, fixed retry loops, and silent error swallowing).
|
|
91
91
|
- `pkg/sre/sli.go`: Error budget calculation and slop index scoring.
|
|
92
|
-
- `pkg/server/server.go`: Production Go HTTP microservice with graceful shutdown, `/healthz`, `/metrics`, and `/v1/
|
|
92
|
+
- `pkg/server/server.go`: Production Go HTTP microservice with graceful shutdown, `/healthz`, `/metrics`, and `/v1/audit`.
|
|
93
93
|
- `cmd/toil-guard/main.go`: Multi-subcommand CLI runner (`audit`, `stream`, `serve`).
|
|
@@ -16,6 +16,9 @@ A huge mandatory checklist can itself cause excessive code, bureaucracy and agen
|
|
|
16
16
|
- Changing existing tests is valid when the approved contract changes; changing tests to fit a regression is not.
|
|
17
17
|
- Unit vs integration vs E2E should reflect system risk and cost; neither all-unit nor all-E2E is a universal rule.
|
|
18
18
|
- Rule thresholds (file length, diff size, number of files) are prompts for scrutiny, not blockers without measured harm.
|
|
19
|
+
- A directly named metric is not automatically the right observation: validate event semantics, units, cohort, freshness and provenance before replacing inference with a source field.
|
|
20
|
+
- A familiar managed stack and a self-hosted VPS stack are both legitimate options. Separate rendering, compute, hosting, data and operational ownership; do not equate lower infrastructure spend with lower total risk.
|
|
21
|
+
- A multi-year architecture scenario is a conditional test, not an exact forecast. Reopen decisions on material evidence changes, not at every routine edit.
|
|
19
22
|
- In existing codebases, preserve established version constraints and lockfile stability without uncoordinated major-version drift. However, when introducing **new** libraries, packages, or SDK integrations, always strictly resolve and pin the **latest stable version** from the official registry rather than guessing outdated versions from model training memory.
|
|
20
23
|
- For greenfield projects, new modules, or SDK integrations, always use the **official boilerplate, CLI installer, or generator** (e.g., `uv init`, `cargo init`, `bun create`, official SDK starters). Hand-rolling ad-hoc scaffolding from memory introduces missing build flags, broken toolchains, and dirty slop.
|
|
21
24
|
- UI microcopy should be succinct, high-signal, and respectful of the user's attention. Tooltips and helper text should clarify non-obvious interactions, not narrate obvious UI controls.
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Check decision-record completeness; never certify the truth of its evidence."""
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def filled(value):
|
|
11
|
+
"""Check whether value is a non-empty string after stripping whitespace."""
|
|
12
|
+
return isinstance(value, str) and bool(value.strip())
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def one_of(value, allowed):
|
|
16
|
+
"""Check whether value is a string belonging to the allowed collection."""
|
|
17
|
+
return isinstance(value, str) and value in allowed
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _audit_architecture(packet, require):
|
|
21
|
+
"""Validate architecture-specific decision axes, alternatives, and horizon stress scenarios."""
|
|
22
|
+
axes = packet.get("decision_axes")
|
|
23
|
+
require(isinstance(axes, list) and len(axes) > 0, "MISSING_DECISION_AXES", "Compare independent decision axes instead of assuming a bundled stack")
|
|
24
|
+
if isinstance(axes, list):
|
|
25
|
+
for index, axis in enumerate(axes):
|
|
26
|
+
if not isinstance(axis, dict):
|
|
27
|
+
require(False, "INVALID_DECISION_AXIS", f"decision_axes[{index}] must be an object")
|
|
28
|
+
continue
|
|
29
|
+
name = axis.get("name")
|
|
30
|
+
considered = axis.get("considered")
|
|
31
|
+
selected = axis.get("selected")
|
|
32
|
+
eliminated = axis.get("eliminated") if isinstance(axis.get("eliminated"), list) else []
|
|
33
|
+
eliminated_set = set(map(str, eliminated))
|
|
34
|
+
active_considered = [c for c in considered if str(c) not in eliminated_set] if isinstance(considered, list) else []
|
|
35
|
+
constraint_rationale = axis.get("constraint_rationale")
|
|
36
|
+
has_single_flag = axis.get("single_candidate") is True
|
|
37
|
+
has_rationale = filled(constraint_rationale)
|
|
38
|
+
is_single_candidate = has_single_flag or has_rationale
|
|
39
|
+
|
|
40
|
+
if is_single_candidate:
|
|
41
|
+
valid = (filled(name) and isinstance(considered, list)
|
|
42
|
+
and len(active_considered) == 1
|
|
43
|
+
and filled(active_considered[0])
|
|
44
|
+
and filled(selected) and selected == active_considered[0]
|
|
45
|
+
and has_rationale)
|
|
46
|
+
require(valid, "INVALID_DECISION_AXIS", f"decision_axes[{index}] single candidate requires exactly one active candidate with constraint_rationale")
|
|
47
|
+
else:
|
|
48
|
+
valid = (filled(name) and isinstance(considered, list)
|
|
49
|
+
and len(set(map(str, active_considered))) >= 2
|
|
50
|
+
and all(filled(v) for v in active_considered)
|
|
51
|
+
and filled(selected) and selected in active_considered)
|
|
52
|
+
require(valid, "INVALID_DECISION_AXIS", f"decision_axes[{index}] must compare >=2 distinct candidates and select one")
|
|
53
|
+
|
|
54
|
+
alternatives = packet.get("alternatives")
|
|
55
|
+
has_alts_list = isinstance(alternatives, list)
|
|
56
|
+
alt_names = [item.get("name").strip() for item in alternatives if isinstance(item, dict) and filled(item.get("name"))] if has_alts_list else []
|
|
57
|
+
distinct_alt_names = set(alt_names)
|
|
58
|
+
packet_rationale = packet.get("constraint_rationale")
|
|
59
|
+
if not packet_rationale and isinstance(packet.get("decision"), dict):
|
|
60
|
+
packet_rationale = packet.get("decision", {}).get("constraint_rationale")
|
|
61
|
+
is_single_alt = packet.get("single_candidate") is True or filled(packet_rationale)
|
|
62
|
+
|
|
63
|
+
if is_single_alt:
|
|
64
|
+
require(
|
|
65
|
+
has_alts_list and len(alternatives) == 1 and len(distinct_alt_names) == 1 and len(alt_names) == 1,
|
|
66
|
+
"MISSING_ALTERNATIVES",
|
|
67
|
+
"Single candidate architecture record must contain exactly one alternative"
|
|
68
|
+
)
|
|
69
|
+
require(filled(packet_rationale), "MISSING_CONSTRAINT_RATIONALE", "Single candidate requires constraint_rationale explaining eliminated alternatives")
|
|
70
|
+
else:
|
|
71
|
+
require(
|
|
72
|
+
has_alts_list and len(alternatives) >= 2 and len(distinct_alt_names) >= 2 and len(alt_names) == len(alternatives),
|
|
73
|
+
"MISSING_ALTERNATIVES",
|
|
74
|
+
"Compare at least two distinct architectural alternatives"
|
|
75
|
+
)
|
|
76
|
+
if has_alts_list:
|
|
77
|
+
for index, item in enumerate(alternatives):
|
|
78
|
+
valid = isinstance(item, dict) and all(filled(item.get(key)) for key in ("name", "benefit", "risk", "reversal"))
|
|
79
|
+
require(valid, "INVALID_ALTERNATIVE", f"alternatives[{index}] needs benefit, risk and reversal path")
|
|
80
|
+
|
|
81
|
+
horizons = packet.get("horizons")
|
|
82
|
+
valid_horizons = isinstance(horizons, list) and all(
|
|
83
|
+
isinstance(item, dict) and one_of(item.get("stage"), {"now", "later"})
|
|
84
|
+
and filled(item.get("scenario")) and filled(item.get("consequence"))
|
|
85
|
+
for item in horizons
|
|
86
|
+
)
|
|
87
|
+
require(valid_horizons, "INVALID_HORIZONS", "horizons requires stage (now/later), scenario and consequence")
|
|
88
|
+
if isinstance(horizons, list):
|
|
89
|
+
require(any(isinstance(item, dict) and item.get("stage") == "later" for item in horizons),
|
|
90
|
+
"MISSING_FUTURE_SCENARIO", "Test at least one credible future growth, failure or migration scenario")
|
|
91
|
+
require(any(isinstance(item, dict) and item.get("stage") == "now" for item in horizons),
|
|
92
|
+
"MISSING_CURRENT_SCENARIO", "Evaluate the near-term operational reality")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _audit_proceed(packet, direct, valid_direct, decision, valid_decision, evidence, require):
|
|
96
|
+
"""Validate conditions required when a decision concludes with a proceed action."""
|
|
97
|
+
if not (valid_direct and valid_decision and decision.get("action") == "proceed"):
|
|
98
|
+
return
|
|
99
|
+
grounded = isinstance(evidence, list) and any(
|
|
100
|
+
isinstance(item, dict) and one_of(item.get("level"), {"observed", "derived"})
|
|
101
|
+
for item in evidence
|
|
102
|
+
)
|
|
103
|
+
require(grounded, "NO_GROUNDED_EVIDENCE", "Proceed requires at least one observed or derived source; inference alone is insufficient")
|
|
104
|
+
if direct.get("status") == "unknown":
|
|
105
|
+
require(False, "DIRECT_PATH_UNRESOLVED", "Investigate direct path or pause before committing")
|
|
106
|
+
if direct.get("status") == "found":
|
|
107
|
+
handling = decision.get("direct_path_handling")
|
|
108
|
+
require(one_of(handling, {"use", "bypass"}), "DIRECT_PATH_OVERRIDE_REQUIRED", "Explicitly use the direct path or justify bypassing it")
|
|
109
|
+
if handling == "bypass":
|
|
110
|
+
require(filled(decision.get("direct_override_reason")), "DIRECT_PATH_OVERRIDE_REQUIRED", "Explain why an indirect approach beats the observed direct answer")
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def audit(packet):
|
|
114
|
+
"""Audit a decision packet against structural completeness requirements."""
|
|
115
|
+
findings = []
|
|
116
|
+
|
|
117
|
+
def require(condition, code, message):
|
|
118
|
+
if not condition:
|
|
119
|
+
findings.append({"code": code, "message": message})
|
|
120
|
+
|
|
121
|
+
kind = packet.get("kind")
|
|
122
|
+
require(one_of(kind, {"investigation", "architecture"}), "INVALID_KIND", "kind must be investigation or architecture")
|
|
123
|
+
for key in ("goal", "proposal"):
|
|
124
|
+
require(filled(packet.get(key)), "MISSING_" + key.upper(), key + " must be nonempty")
|
|
125
|
+
|
|
126
|
+
evidence = packet.get("evidence")
|
|
127
|
+
require(isinstance(evidence, list) and len(evidence) > 0, "MISSING_EVIDENCE", "List at least one traceable source and finding")
|
|
128
|
+
if isinstance(evidence, list):
|
|
129
|
+
for index, item in enumerate(evidence):
|
|
130
|
+
valid = (isinstance(item, dict) and filled(item.get("source"))
|
|
131
|
+
and filled(item.get("finding"))
|
|
132
|
+
and one_of(item.get("level"), {"observed", "derived", "inferred"}))
|
|
133
|
+
require(valid, "INVALID_EVIDENCE", f"evidence[{index}] requires source, finding and valid level")
|
|
134
|
+
|
|
135
|
+
direct = packet.get("direct_check")
|
|
136
|
+
valid_direct = (isinstance(direct, dict)
|
|
137
|
+
and one_of(direct.get("status"), {"found", "not_found", "unknown", "not_applicable"})
|
|
138
|
+
and filled(direct.get("source")) and filled(direct.get("finding")))
|
|
139
|
+
require(valid_direct, "MISSING_DIRECT_CHECK", "Record direct-answer/source search, including unknown or unavailable evidence")
|
|
140
|
+
if valid_direct and kind == "investigation":
|
|
141
|
+
require(direct["status"] != "not_applicable", "DIRECT_CHECK_SKIPPED", "Investigations must inspect or explicitly mark the direct source unknown")
|
|
142
|
+
|
|
143
|
+
assumptions = packet.get("assumptions", [])
|
|
144
|
+
require(isinstance(assumptions, list), "INVALID_ASSUMPTIONS", "assumptions must be a list")
|
|
145
|
+
if isinstance(assumptions, list):
|
|
146
|
+
for index, item in enumerate(assumptions):
|
|
147
|
+
valid = (isinstance(item, dict) and filled(item.get("claim"))
|
|
148
|
+
and one_of(item.get("state"), {"verified", "unverified"})
|
|
149
|
+
and filled(item.get("basis")))
|
|
150
|
+
require(valid, "INVALID_ASSUMPTION", f"assumptions[{index}] must distinguish verified from unverified")
|
|
151
|
+
|
|
152
|
+
decision = packet.get("decision")
|
|
153
|
+
valid_decision = isinstance(decision, dict) and one_of(decision.get("action"), {"proceed", "pause", "stop"})
|
|
154
|
+
require(valid_decision, "MISSING_DECISION", "decision.action must be proceed, pause or stop")
|
|
155
|
+
if isinstance(decision, dict):
|
|
156
|
+
for key in ("choice", "reason", "invalidated_by"):
|
|
157
|
+
require(filled(decision.get(key)), "MISSING_DECISION_FIELD", f"decision.{key} is required")
|
|
158
|
+
|
|
159
|
+
if kind == "architecture":
|
|
160
|
+
_audit_architecture(packet, require)
|
|
161
|
+
|
|
162
|
+
_audit_proceed(packet, direct, valid_direct, decision, valid_decision, evidence, require)
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
"record_status": "complete" if not findings else "incomplete",
|
|
166
|
+
"decision_action": decision.get("action") if isinstance(decision, dict) and isinstance(decision.get("action"), str) else None,
|
|
167
|
+
"machine_check_only": True,
|
|
168
|
+
"findings": findings,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def main():
|
|
173
|
+
"""Parse command-line arguments and run the decision-record audit."""
|
|
174
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
175
|
+
parser.add_argument("command", choices=["audit"])
|
|
176
|
+
parser.add_argument("record", type=Path)
|
|
177
|
+
parser.add_argument("--json", action="store_true")
|
|
178
|
+
args = parser.parse_args()
|
|
179
|
+
try:
|
|
180
|
+
packet = json.loads(args.record.read_text(encoding="utf-8"))
|
|
181
|
+
except (OSError, UnicodeError, ValueError) as error:
|
|
182
|
+
print(f"Invalid JSON or unreadable decision record: {error}", file=sys.stderr)
|
|
183
|
+
return 1
|
|
184
|
+
if not isinstance(packet, dict):
|
|
185
|
+
print("Invalid JSON: decision record must be an object", file=sys.stderr)
|
|
186
|
+
return 1
|
|
187
|
+
result = audit(packet)
|
|
188
|
+
if args.json:
|
|
189
|
+
print(json.dumps(result, indent=2))
|
|
190
|
+
else:
|
|
191
|
+
print(f"Decision record: {result['record_status']} ({result['decision_action']})")
|
|
192
|
+
for finding in result["findings"]:
|
|
193
|
+
print(f" {finding['code']}: {finding['message']}")
|
|
194
|
+
print("Structural check only; sources and reasoning require independent verification.")
|
|
195
|
+
return 2 if result["findings"] else 0
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
sys.exit(main())
|
package/scripts/sre_guard.py
CHANGED
|
@@ -130,7 +130,7 @@ def normalize_line(line):
|
|
|
130
130
|
stripped = line.strip()
|
|
131
131
|
if not stripped or stripped.startswith(('#', '//', '/*', '*', 'import ', 'from ', 'package ', 'use ')):
|
|
132
132
|
return ''
|
|
133
|
-
if
|
|
133
|
+
if re.fullmatch(r'''(?:"[^"]*"|'[^']*'),?''', stripped):
|
|
134
134
|
return ''
|
|
135
135
|
if stripped in ('{', '}', '(', ')', '};', '];', ');', 'return', ']', '['):
|
|
136
136
|
return ''
|