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.
Files changed (42) hide show
  1. package/.skills.json +1 -1
  2. package/README.md +29 -6
  3. package/SKILL.md +4 -1
  4. package/bin/cli.js +76 -20
  5. package/marketplace.json +1 -1
  6. package/package.json +13 -6
  7. package/references/anti-pattern-catalog.md +2 -0
  8. package/references/clean-room-recon.md +7 -7
  9. package/references/engineering-judgment.md +3 -0
  10. package/scripts/decision_guard.py +199 -0
  11. package/scripts/sre_guard.py +1 -1
  12. package/services/anti-slop-rust/Cargo.lock +757 -0
  13. package/services/anti-slop-rust/Cargo.toml +12 -0
  14. package/services/anti-slop-rust/src/ladder_auditor.rs +112 -0
  15. package/services/anti-slop-rust/src/lib.rs +66 -0
  16. package/services/anti-slop-rust/src/main.rs +197 -0
  17. package/services/anti-slop-rust/src/model.rs +76 -0
  18. package/services/anti-slop-rust/src/path_utils.rs +18 -0
  19. package/services/anti-slop-rust/src/server.rs +100 -0
  20. package/services/anti-slop-rust/src/shit_interceptor.rs +247 -0
  21. package/services/toil-guard-go/cmd/toil-guard/main.go +271 -0
  22. package/services/toil-guard-go/cmd/toil-guard/main_test.go +36 -0
  23. package/services/toil-guard-go/go.mod +3 -0
  24. package/services/toil-guard-go/pkg/model/types.go +61 -0
  25. package/services/toil-guard-go/pkg/server/server.go +142 -0
  26. package/services/toil-guard-go/pkg/server/server_test.go +77 -0
  27. package/services/toil-guard-go/pkg/sre/audit.go +27 -0
  28. package/services/toil-guard-go/pkg/sre/complexity.go +158 -0
  29. package/services/toil-guard-go/pkg/sre/complexity_test.go +81 -0
  30. package/services/toil-guard-go/pkg/sre/dry.go +103 -0
  31. package/services/toil-guard-go/pkg/sre/dry_test.go +84 -0
  32. package/services/toil-guard-go/pkg/sre/sli.go +77 -0
  33. package/services/toil-guard-go/pkg/sre/sli_test.go +55 -0
  34. package/services/toil-guard-go/pkg/sre/toil.go +98 -0
  35. package/services/toil-guard-go/pkg/sre/toil_test.go +53 -0
  36. package/skills/decision-guard/SKILL.md +103 -0
  37. package/skills/decision-guard/evals/scenarios.json +78 -0
  38. package/skills/decision-guard/examples/architecture-record.json +35 -0
  39. package/skills/decision-guard/references/decision-method.md +46 -0
  40. package/skills/decision-guard/references/decision-record.md +23 -0
  41. package/skills/decision-guard/usecases/architect.md +20 -0
  42. 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": "0.4.0",
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
  [![Skills.sh](https://img.shields.io/badge/Skills.sh-Compatible-000000?style=flat-square&logo=vercel&logoColor=white)](https://skills.sh)
13
13
  [![Claude Code](https://img.shields.io/badge/Claude%20Code-Certified%20Skill-D97706?style=flat-square&logo=anthropic&logoColor=white)](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-213-patterns)
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 four categories of agent failure — Scope creep (`S`), Hypothetical hardening (`H`), Intent violation (`I`), and Task thrashing (`T`) — plus the Senior Dev Ladder pattern: detecting custom implementations where platform primitives already exist (`crypto.randomUUID()`, `URLSearchParams`, `structuredClone()`).
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 telemetry with error budget tracking:
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 (213 patterns)
249
+ ## Anti-pattern catalog (215 patterns)
227
250
 
228
- The catalog in [`references/anti-pattern-catalog.md`](references/anti-pattern-catalog.md) documents 213 diagnostic patterns across 14 engineering domains, each with a canonical ID, symptom, root cause, and remediation.
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: "0.4.0"
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 rustBinPath = existsSync(resolve(rootDir, "dist", "bin", "anti-slop-rust"))
17
- ? resolve(rootDir, "dist", "bin", "anti-slop-rust")
18
- : resolve(rootDir, "services", "anti-slop-rust", "target", "release", "anti-slop-rust");
19
- const goBinPath = existsSync(resolve(rootDir, "dist", "bin", "toil-guard"))
20
- ? resolve(rootDir, "dist", "bin", "toil-guard")
21
- : resolve(rootDir, "services", "toil-guard-go", "bin", "toil-guard");
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 (v0.4.0)
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, Task Thrashing, Senior Dev Ladder)
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 ?? 0);
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
- const goBuild = spawn("go", ["build", "-o", "bin/toil-guard", "./cmd/toil-guard"], {
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 ?? 0));
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": "0.4.0",
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": "0.4.0",
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": "./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"
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
- | - Task Thrashing Watcher | | • Prometheus /metrics Server |
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:** Ultra-fast AST pattern triage, SHIT boundary violation interception, token waste analysis, and deterministic circuit-breaker protection.
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`: Code reduction ratio auditing, stdlib built-in identification, and 3-point abstraction gate checks.
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/dry_window.go`: Sliding-window token hash scanner across files.
90
- - `pkg/sre/toil.go`: SRE anti-pattern heuristics (unbounded concurrency, blind sleep synchronization, missing exponential backoff + jitter).
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/toil-audit`.
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())
@@ -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 (stripped.startswith('"') and stripped.endswith('"')) or (stripped.startswith("'") and stripped.endswith("'")):
133
+ if re.fullmatch(r'''(?:"[^"]*"|'[^']*'),?''', stripped):
134
134
  return ''
135
135
  if stripped in ('{', '}', '(', ')', '};', '];', ');', 'return', ']', '['):
136
136
  return ''