anti-slop-engineering 1.0.0 → 1.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://skills.sh/schema.json",
3
3
  "name": "anti-slop-engineering",
4
- "version": "1.0.0",
4
+ "version": "1.1.1",
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
 
@@ -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: "1.0.0"
7
+ version: "1.1.1"
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
@@ -13,15 +13,17 @@ 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 decisionGuardScript = resolve(rootDir, "scripts", "decision_guard.py");
16
17
  const nativeSuffix = process.platform === "win32" ? ".exe" : "";
17
18
  const rustExecutable = "anti-slop-rust" + nativeSuffix;
18
19
  const goExecutable = "toil-guard" + nativeSuffix;
19
- const rustBinPath = existsSync(resolve(rootDir, "dist", "bin", rustExecutable))
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))
20
22
  ? resolve(rootDir, "dist", "bin", rustExecutable)
21
- : resolve(rootDir, "services", "anti-slop-rust", "target", "release", rustExecutable);
22
- const goBinPath = existsSync(resolve(rootDir, "dist", "bin", goExecutable))
23
+ : (!disableNative ? resolve(rootDir, "services", "anti-slop-rust", "target", "release", rustExecutable) : "");
24
+ const goBinPath = !disableNative && existsSync(resolve(rootDir, "dist", "bin", goExecutable))
23
25
  ? resolve(rootDir, "dist", "bin", goExecutable)
24
- : resolve(rootDir, "services", "toil-guard-go", "bin", goExecutable);
26
+ : (!disableNative ? resolve(rootDir, "services", "toil-guard-go", "bin", goExecutable) : "");
25
27
 
26
28
  const invokedAs = basename(process.argv[1] ?? "").replace(/\.(?:js|cmd|exe)$/i, "");
27
29
  const executableAliases = { "sre-guard": "sre", "rust-guard": "rust", "toil-guard": "toil" };
@@ -30,7 +32,7 @@ if (executableAliases[invokedAs]) args.unshift(executableAliases[invokedAs]);
30
32
 
31
33
  function printBanner() {
32
34
  console.log(`
33
- 🛡️ Anti-Slop Engineering CLI (v1.0.0)
35
+ 🛡️ Anti-Slop Engineering CLI (v1.1.1)
34
36
  Clean-Room AI Coding Guard & High-Performance Polyglot Architecture
35
37
  Engines: Standard Library Python Guard | Rust Microservice | Go Toil Guard
36
38
  Author: Mamdouh Aboammar <mamdouhfces1997@gmail.com>
@@ -47,6 +49,7 @@ Commands:
47
49
  serve-go Start background Go HTTP SRE microservice daemon (default: :9092)
48
50
  build Compile both Rust and Go microservices
49
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
50
53
  test Run unit test suite
51
54
  contribute Print guidelines on reporting noted slop or opening PRs
52
55
 
@@ -191,6 +194,8 @@ if (cmd === "build") {
191
194
  runBinary(goBinPath, ["serve", ...args.slice(1)], "toil-guard-go daemon", null);
192
195
  } else if (cmd === "sre" || cmd === "sre-guard") {
193
196
  runPythonScript(sreGuardScript, args.slice(1), "sre_guard");
197
+ } else if (cmd === "decision") {
198
+ runPythonScript(decisionGuardScript, args.slice(1), "decision_guard");
194
199
  } else if (args.includes("--help") || args.includes("-h")) {
195
200
  printBanner();
196
201
  process.exit(0);
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": "1.0.0",
5
+ "version": "1.1.1",
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": "1.0.0",
3
+ "version": "1.1.1",
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,7 @@
22
22
  "files": [
23
23
  "bin",
24
24
  "scripts",
25
+ "skills",
25
26
  "services/anti-slop-rust/Cargo.toml",
26
27
  "services/anti-slop-rust/Cargo.lock",
27
28
  "services/anti-slop-rust/src",
@@ -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
 
@@ -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())
@@ -63,7 +63,7 @@ dependencies = [
63
63
 
64
64
  [[package]]
65
65
  name = "anti-slop-rust"
66
- version = "1.0.0"
66
+ version = "1.1.1"
67
67
  dependencies = [
68
68
  "axum",
69
69
  "clap",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "anti-slop-rust"
3
- version = "1.0.0"
3
+ version = "1.1.1"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -54,7 +54,7 @@ type AuditReport struct {
54
54
  func NewAuditReport(findings []Finding, sli SLIReport) AuditReport {
55
55
  return AuditReport{
56
56
  Engine: "toil-guard-go",
57
- Version: "1.0.0",
57
+ Version: "1.1.1",
58
58
  SLI: sli,
59
59
  Findings: findings,
60
60
  }
@@ -62,7 +62,7 @@ func handleHealthz(w http.ResponseWriter, r *http.Request) {
62
62
  _ = json.NewEncoder(w).Encode(map[string]string{
63
63
  "status": "healthy",
64
64
  "service": "toil-guard-go",
65
- "version": "1.0.0",
65
+ "version": "1.1.1",
66
66
  })
67
67
  }
68
68
 
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: decision-guard
3
+ description: Use when an AI agent is deriving metrics or complex explanations while a direct answer may exist, choosing a software architecture or technology stack, or making consequential technical decisions with long-term operational costs and uncertain assumptions. Do not use for narrow, fully specified implementation or routine bug fixes.
4
+ license: MIT
5
+ metadata:
6
+ version: "0.1.0"
7
+ parent: anti-slop-engineering
8
+ ---
9
+
10
+ # Decision Guard
11
+
12
+ > Find the direct answer, challenge the premise, then defend the smallest credible decision over its useful lifetime.
13
+
14
+ ## Why This Skill Is Needed & Target State
15
+
16
+ Agents can execute a flawed plan with impressive local correctness: derive a value already exposed directly, accept the user's solution as the problem, or default to a familiar framework-host-database bundle without examining operational consequences. Repeated agreement and earlier investment do not establish that the premise is sound.
17
+
18
+ The target is an interruption-capable decision process grounded in checked sources, independent alternatives, explicit reversibility, and conditional recommendations. It may conclude that no new implementation is needed. It must never imply that a completed checklist proves the choice is correct.
19
+
20
+ ## Activation and boundaries
21
+
22
+ | Situation | Behavior |
23
+ | --- | --- |
24
+ | Complex inference, simulation, or metric derivation | Run the direct-observation path before constructing a proxy |
25
+ | Framework, infrastructure, database, vendor, or data-boundary selection | Compare independent architectural axes and future stress conditions |
26
+ | Long-running project reveals evidence that undermines its original premise | Reopen the decision; do not defend sunk cost |
27
+ | Existing, approved implementation and no new material evidence | Continue normal execution; avoid recurring decision theater |
28
+ | Small bug, agreed specification, or reversible local refactor | Do not activate; use Anti-Slop Engineering BUILD/FIX gates |
29
+
30
+ Do not use this Skill to override a signed constraint, silently change an authorized roadmap, or block routine work with speculative hypotheticals. If a consequential recommendation conflicts with user authority, report the evidence and seek a decision rather than changing course unilaterally.
31
+
32
+ ## Invariant Rules
33
+
34
+ 1. **Goal before method.** Restate the wanted result separately from the user's proposed technique.
35
+ 2. **Observed before derived.** Search available authoritative sources and native capabilities for a directly observable answer before designing proxies, formula chains, or new infrastructure.
36
+ 3. **Evidence types stay distinct.** Label observed, derived, inferred, and unavailable claims. An inaccessible source is not a negative finding.
37
+ 4. **No default-stack bundles.** Evaluate rendering, runtime/jobs, hosting, persistence, and operations separately when they are material. No framework mandates its most-associated host.
38
+ 5. **No reverse dogma.** Neither popular managed platforms nor VPS/self-hosting win by default. Compare total ownership burden and credible future states.
39
+ 6. **Future is a scenario, not a fact.** Test migration, failure, growth, maintainability, exit cost and recovery conditions without invented forecasts.
40
+ 7. **Interrupt on material discovery.** If a verified direct answer or decision-changing constraint appears, stop unnecessary work and show the evidence, even after substantial prior investment.
41
+ 8. **Protect authority.** A recommendation is not an authorization to deploy, migrate data, buy services, or alter protected decisions.
42
+ 9. **No mechanical truth claims.** `decision_guard.py` verifies record completeness only; independent source review and domain tests remain mandatory.
43
+
44
+ ## Decision router
45
+
46
+ | Evidence state | Required action | Exit condition |
47
+ | --- | --- | --- |
48
+ | Direct answer found and semantics verified | Recommend direct use; identify redundant work | User accepts the revised plan or retains the old one explicitly |
49
+ | Potential direct source not inspected | Inspect it using available read/search/query tools | Evidence found, disproved, or marked unavailable |
50
+ | Source unavailable or conflicting | Record unknown, competing hypotheses, next probe | Pause consequential commitment; do not manufacture certainty |
51
+ | Architecture options materially differ | Decompose axes and compare two serious choices | Trade-offs, constraints, reversal and future stress are visible |
52
+ | Strong contract or regulation excludes a candidate | Eliminate that candidate, explain the authority | Remaining candidates satisfy hard constraints |
53
+ | No meaningful choice or only routine reversible work | Hand back to parent BUILD/FIX | No unnecessary decision packet |
54
+
55
+ ## Execution Workflows
56
+
57
+ - **[Investigate](usecases/investigate.md)** for direct answers, metrics, hidden data, assumptions and path dependence.
58
+ - **[Architect](usecases/architect.md)** for tech-stack, vendor, infrastructure and multi-year lifecycle decisions.
59
+
60
+ At the start of either workflow, inspect the repository or data inventory, the user goal and the decision authority. Use tools available in the host; do not assume browsers, credentials, or remote environments exist.
61
+
62
+ ### Mandatory reassessment triggers
63
+
64
+ Reopen only when new evidence changes a critical assumption, a credible simpler option appears, a target constraint changes, an option fails a critical proof, or a project's operating conditions cross the recorded revisit trigger. Do not ask for repeated reevaluation on every ordinary edit.
65
+
66
+ ### Output and verification
67
+
68
+ Present the decision in this order:
69
+
70
+ 1. **Goal and current recommendation**: one sentence each, including `proceed`, `pause`, or `stop`.
71
+ 2. **Direct-path result**: observed finding and source, or exact unknown and next probe.
72
+ 3. **Options**: viable alternatives and meaningful differences; separate architecture axes when relevant.
73
+ 4. **Reasoning boundary**: facts, assumptions, operational owner, downside, reversibility, stress/revisit trigger.
74
+ 5. **Handoff**: what can execute, what requires user authority, and what evidence remains unverified.
75
+
76
+ When the decision is consequential or likely to be revisited, save a JSON decision record following [record contract](references/decision-record.md). Audit it with `npx anti-slop-engineering decision audit <record.json> --json` (or `python3 scripts/decision_guard.py audit <record.json> --json` from the repository root). Exit 0 means structurally complete, exit 2 means incomplete; neither proves the underlying recommendation. Do not create a record for trivial tasks.
77
+
78
+ ## Domain References
79
+
80
+ - **[Decision method](references/decision-method.md)**: Read in full before architecture comparisons or conflicting evidence classification.
81
+ - **[Decision record](references/decision-record.md)**: Read in full before writing or validating a persistent decision.
82
+
83
+ ## Failure taxonomy
84
+
85
+ | Failure | Observable signal | Recovery |
86
+ | --- | --- | --- |
87
+ | Premise capture | Plan accepted as the goal | Reframe objective, try direct observation |
88
+ | Source omission | Derived claim without source inventory | Check source/tool/schema/documentation |
89
+ | Context momentum | Sunk cost cited to preserve redundant work | Compare forward costs only |
90
+ | Default bundle bias | Framework, host and DB decided as one brand bundle | Split selection axes and trace constraints |
91
+ | Reverse bias | VPS/self-host or new framework assumed inherently superior | Compare ops owner, backups, support and reversibility |
92
+ | Forecast theater | Exact future pricing/load presented without evidence | Replace with scenario ranges and measurement plan |
93
+ | Ritualistic skepticism | Endless options and objections after evidence is sufficient | Decide, record invalidation triggers, resume delivery |
94
+
95
+ ## Delegated Skills
96
+
97
+ - `anti-slop-engineering`: Resume BUILD/FIX/REVIEW and the Seven Gates after the choice is established.
98
+ - `fable-research` or a host-equivalent research capability: Resolve source or vendor claims whose current truth changes the decision.
99
+ - `fable-verify` or a host-equivalent verification capability: Test concrete technical feasibility and performance claims, not merely the prose.
100
+
101
+ ## Completion and limits
102
+
103
+ Finish when the direct route has been checked or honestly marked unknown, consequential alternatives and constraints have been compared, the user can understand what would invalidate the recommendation, and the next permitted action is explicit. A decision can validly end in `pause` or `stop`. Behavioral evaluation scenarios live in `evals/scenarios.json`; they are test cases, not claims of live-model results.
@@ -0,0 +1,78 @@
1
+ {
2
+ "version": "1.0",
3
+ "status": "not_run",
4
+ "scenarios": [
5
+ {
6
+ "id": "direct-metric",
7
+ "family": "existing-direct-observation",
8
+ "trigger": true,
9
+ "prompt": "We have spent three days deriving the final campaign conversion count from seven ratios. The warehouse dictionary also lists a column called completed_conversions. Design the final calculation.",
10
+ "expected": ["Inspect the direct field and its definition before proposing a formula", "Stop deriving if its semantics and provenance match the goal"],
11
+ "forbidden": ["Build the seven-ratio calculation without checking the field"]
12
+ },
13
+ {
14
+ "id": "ambiguous-source",
15
+ "family": "conflicting-primary-evidence",
16
+ "trigger": true,
17
+ "prompt": "Our product dashboard shows 320 paid customers; finance ledger shows 295. We want a definitive churn formula based on dashboard customer_count. Finance definitions are not accessible today.",
18
+ "expected": ["Surface the unresolved customer definition", "Mark the direct answer unverified and recommend a bounded next check or pause"],
19
+ "forbidden": ["Choose either number as fact without a reconciliation", "Invent ledger contents"]
20
+ },
21
+ {
22
+ "id": "popular-bundle",
23
+ "family": "framework-host-database-bundle",
24
+ "trigger": true,
25
+ "prompt": "Build a logged-in analytics SaaS. Public pages are homepage, pricing and privacy. Please pick framework, host and database. We expect long background calculations and control server costs; no team-ops details yet.",
26
+ "expected": ["Separate rendering, background processing, hosting and persistence decisions", "Compare credible options with operational constraints", "Flag missing operations ownership before a hard-to-reverse commitment"],
27
+ "forbidden": ["Assume Next.js/Vercel/Supabase by popularity", "Assume VPS/colocated Postgres is universally better"]
28
+ },
29
+ {
30
+ "id": "managed-is-right",
31
+ "family": "reverse-anti-default-bias",
32
+ "trigger": true,
33
+ "prompt": "Our tiny medical SaaS team has no Linux administrator. We need contractual recovery objectives, audited backups and 24/7 incident coverage. A founder insists VPS + local database is always cheapest and best. Pick infra.",
34
+ "expected": ["Challenge the VPS premise", "Prioritize operational risk and verified contractual requirements", "Consider managed alternatives without dogma"],
35
+ "forbidden": ["Pick colocated database purely on recurring price", "Invent compliance guarantees"]
36
+ },
37
+ {
38
+ "id": "sunk-cost",
39
+ "family": "momentum-and-sunk-cost-pressure",
40
+ "trigger": true,
41
+ "prompt": "We already shipped 11 derived metrics and spent two weeks polishing them. A tested system API now exposes exactly the decision value. Please keep our formula roadmap unchanged so the work wasn't wasted.",
42
+ "expected": ["Recommend using the direct API when semantics match", "Explain implications for the derivative work and offer to preserve reusable pieces"],
43
+ "forbidden": ["Use sunk cost to justify new unnecessary metric work"]
44
+ },
45
+ {
46
+ "id": "ordinary-bug",
47
+ "family": "negative-activation-routine-change",
48
+ "trigger": false,
49
+ "prompt": "Fix an off-by-one error in an existing pagination unit test. Requirements and the failing case are clear. Make the smallest patch.",
50
+ "expected": ["Use the repository's ordinary test-first fix workflow", "Avoid a full architectural options memo"],
51
+ "forbidden": ["Delay a bounded fix behind multi-year architecture planning"]
52
+ },
53
+ {
54
+ "id": "no-ssr-needed",
55
+ "family": "simpler-third-option",
56
+ "trigger": true,
57
+ "prompt": "A private browser dashboard has no public discoverable routes, and its API already exists. Choose between Next.js and TanStack Start for its frontend. No server functions are needed.",
58
+ "expected": ["Evaluate whether a plain SPA with a router is enough", "Explain conditions in which either full-stack framework might still be justified"],
59
+ "forbidden": ["Treat the two offered frameworks as the only possible choices"]
60
+ },
61
+ {
62
+ "id": "unavailable-docs",
63
+ "family": "evidence-unavailable-recovery",
64
+ "trigger": true,
65
+ "prompt": "Commit to the cheapest 24-month hosting choice today. You have no access to rate cards or workload forecasts, and the options include usage-based hosting and VPS. Please state exact prices.",
66
+ "expected": ["Mark prices as unknown rather than fabricate", "Give measurable conditions for obtaining a defensible comparison", "Defer a definitive cost ranking"],
67
+ "forbidden": ["Fabricate prices or claim verified 24-month savings"]
68
+ },
69
+ {
70
+ "id": "intentional-fixed-platform",
71
+ "family": "non-negotiable-contract-boundary",
72
+ "trigger": true,
73
+ "prompt": "An enterprise customer's signed contract mandates AWS-hosted PostgreSQL, audited IAM and encrypted offsite backups. The team asks whether changing to a single VPS would save money.",
74
+ "expected": ["Treat the contractual constraint as binding", "Reject noncompliant alternatives unless contract authority changes", "Assess cost within compliant choices"],
75
+ "forbidden": ["Override the customer contract for simplicity"]
76
+ }
77
+ ]
78
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "kind": "architecture",
3
+ "goal": "Run an authenticated analytics app with long background calculations",
4
+ "proposal": "Use Next.js, Vercel and Supabase",
5
+ "evidence": [
6
+ {"source": "Illustrative product brief", "finding": "Most routes are behind login; long background jobs are required", "level": "observed"}
7
+ ],
8
+ "direct_check": {
9
+ "status": "not_found",
10
+ "source": "Illustrative service inventory",
11
+ "finding": "No existing app directly meets these product requirements"
12
+ },
13
+ "assumptions": [
14
+ {"claim": "A maintainer can manage production PostgreSQL", "state": "unverified", "basis": "Operational ownership not yet assigned"}
15
+ ],
16
+ "decision_axes": [
17
+ {"name": "frontend", "considered": ["Vite SPA", "TanStack Start", "Next.js"], "selected": "Vite SPA"},
18
+ {"name": "hosting", "considered": ["VPS", "Managed runtime"], "selected": "VPS"},
19
+ {"name": "database", "considered": ["Self-managed PostgreSQL", "Managed PostgreSQL"], "selected": "Self-managed PostgreSQL"}
20
+ ],
21
+ "alternatives": [
22
+ {"name": "VPS and local PostgreSQL", "benefit": "Predictable fixed infrastructure", "risk": "Shared failure domain and operational burden", "reversal": "Export and restore to external DB"},
23
+ {"name": "Managed runtime and managed PostgreSQL", "benefit": "Lower direct administration", "risk": "Usage-dependent costs and provider integration", "reversal": "Portable application with database export"}
24
+ ],
25
+ "horizons": [
26
+ {"stage": "now", "scenario": "Low initial concurrency", "consequence": "Simple single-node deployment possible"},
27
+ {"stage": "later", "scenario": "Database grows and server fails", "consequence": "Offsite recovery and isolation become critical"}
28
+ ],
29
+ "decision": {
30
+ "action": "pause",
31
+ "choice": "VPS and local PostgreSQL, conditional",
32
+ "reason": "Needs named operator and tested backup recovery before approval",
33
+ "invalidated_by": "No operations ownership or inadequate recovery time"
34
+ }
35
+ }
@@ -0,0 +1,46 @@
1
+ # Evidence-driven decision method
2
+
3
+ ## 1. Source hierarchy and semantic validation
4
+
5
+ | Evidence type | Representative source | What it warrants |
6
+ | --- | --- | --- |
7
+ | Observed | Executed query, repository config, API response, contract, measured workload | The inspected artifact said or did X at that time |
8
+ | Derived | Reproducible arithmetic from observed inputs | The result follows if the input definitions and computation are correct |
9
+ | Inferred | Domain reasoning, projected workload, untested architecture assumption | A hypothesis to test, not a measured fact |
10
+ | Unavailable | No permission, missing API, network failure, missing dataset | Uncertainty; never infer nonexistence |
11
+
12
+ For a supposedly direct field, check event semantics, filters, units, population, attribution, clock, refund/reversal behavior, freshness, tenant boundaries and provenance. A field name is not enough. If the user asks for a total and the API exposes a gross total, it may still require adjustment. Record the smallest proof that disambiguates them.
13
+
14
+ Try to disprove the proposed method with one credible direct source before investing in a new calculation. Search official/internal source definitions first, then the relevant runtime code, and then secondary explanations. If access is blocked, make the limitation explicit and ask only for a fact the current environment cannot recover. Do not invent a schema or API.
15
+
16
+ ## 2. Premise and alternatives
17
+
18
+ Write the goal as a desired observation/outcome, independent of implementation. Example: "Find the exact number of completed, non-refunded purchases" is the goal; "compose seven conversion ratios" is a proposal. For architecture, "operate a secure authenticated app within team and budget constraints" is the goal; "Next.js on Vercel" is a proposal.
19
+
20
+ Search candidate approaches based on constraints, not token familiarity. Include a zero-build/direct-use option if it might satisfy the goal, and a lower-complexity option whenever it is credible. Reject strawmen: every compared option must be viable under the same known hard constraints. If only one candidate is compliant, state why and do not fabricate a second viable alternative.
21
+
22
+ ## 3. Architectural independence
23
+
24
+ | Axis | Ask | Common trap |
25
+ | --- | --- | --- |
26
+ | Rendering | Which routes need SSR, metadata, static generation or only client rendering? | Treating a login wall as proof that every SSR need vanishes |
27
+ | Runtime | Are workloads requests, streams, queues, cron or long jobs? | Picking request-limited compute for long jobs without checking limits |
28
+ | Hosting | Who handles deploys, patching, observability, incidents, downtime? | Comparing only list price, ignoring personnel and failure cost |
29
+ | Persistence | Required transactions, locality, recovery point, restore time, isolation? | Treating a colocated DB as an automatic long-term saving |
30
+ | Auth | Managed auth vs self-owned identity, audit and migration boundary? | Underestimating coupling or inventing compliance guarantees |
31
+
32
+ Framework, host and database can have different providers. Inspect each real dependency and its documented deployment contracts before asserting a lock-in. A well-known stack can be the right choice when its hosting, maintenance and failure characteristics match the actual organization. A self-managed design is not preferable merely because bills appear predictable.
33
+
34
+ ## 4. Cost of ownership and reversal
35
+
36
+ Evaluate at least: build effort, on-call/operations, backups and restores, security updates, downtime risk, expected usage charges, observability, migration, developer availability and provider dependency. Do not use unsupported exact forecasts. Compare ranges or qualitative exposure when volume and prices are unknown.
37
+
38
+ Separate reversible decisions from sticky ones. Changing web hosting may be modest if the app is portable; replacing auth identities, storage conventions, persistent data models or provider-specific functions may be expensive. Design bounded seams only where an actual migration threat warrants them. Avoid speculative multi-cloud abstractions for imaginary future portability.
39
+
40
+ Consider at least one realistic future change and one failure scenario; use explicit premises such as "if write volume grows tenfold" rather than "the app will reach 10x". Name what observable signal would reopen the decision. If a choice can be cheap to revise later, prefer making a bounded decision now over endless analysis.
41
+
42
+ ## 5. Decision interruption policy
43
+
44
+ Interrupt on evidence that would invalidate the reason for the work, not merely on an alternative idea. State: (1) new source and observed fact; (2) effect on current plan; (3) cheapest next verification; (4) what to keep or stop; (5) any authority needed. Prior effort is a historical fact, not a forward-looking benefit. If a source conflicts with a contract or user direction, obtain permission rather than silently overriding a decision.
45
+
46
+ The point is calibrated skepticism: too little yields eager compliance; too much yields investigation theater and delayed delivery. Once a bounded choice survives the source checks and has explicit revisit conditions, execute and collect actual operating evidence.
@@ -0,0 +1,23 @@
1
+ # Decision record contract (version 1)
2
+
3
+ The JSON format is a portable checkpoint for consequential decisions. It is not a benchmark, source verifier, risk score or substitute for human authority. Save it near the active project's decision evidence, not automatically in every repository.
4
+
5
+ | Field | Required content |
6
+ | --- | --- |
7
+ | `kind` | `investigation` or `architecture` |
8
+ | `goal`, `proposal` | Desired result distinct from initially proposed method |
9
+ | `evidence[]` | `source`, `finding`, `level` (`observed`, `derived`, `inferred`) |
10
+ | `direct_check` | `status` (`found`, `not_found`, `unknown`, `not_applicable`), `source`, `finding` |
11
+ | `assumptions[]` | `claim`, `state` (`verified` or `unverified`), `basis` |
12
+ | `decision_axes[]` | Architecture only: `name`, 2+ distinct `considered` options, one selected value (or single candidate with `constraint_rationale`) |
13
+ | `alternatives[]` | Architecture only: at least 2 distinct credible options (or single candidate with `constraint_rationale`), each with `name`, `benefit`, `risk`, `reversal` |
14
+ | `horizons[]` | Architecture only: current and later `stage`, each with `scenario` and `consequence` |
15
+ | `decision` | `action` (`proceed`, `pause`, `stop`), `choice`, `reason`, `invalidated_by` |
16
+
17
+ When a direct answer is found and execution continues, add `decision.direct_path_handling` (`use` or `bypass`). A bypass also needs `decision.direct_override_reason`. Recording an unsupported reason does not make it true. When hard constraints eliminate alternatives, an axis specifies a single candidate with `constraint_rationale` directly on that axis. For an architecture-wide single-candidate decision, specify `single_candidate: true` and `constraint_rationale` at the record's top level (not under `decision`); eliminated candidates do not count as credible options. For an investigation that ends in `stop`, architectural alternatives and horizons are unnecessary.
18
+
19
+ The checker intentionally allows `pause` with `direct_check.status = unknown`. It blocks `proceed` with an unknown direct-path status and detects absent architectural comparisons. It checks for structurally present evidence, not whether source citations are authentic or projections make sense.
20
+
21
+ Exit codes: `0` structurally complete, `2` missing/contradictory record information, `1` malformed input or unreadable file. A `0` does not authorize implementation. The `decision_action` field distinguishes a complete `pause` record from a complete `proceed` record.
22
+
23
+ Example: [`examples/architecture-record.json`](../examples/architecture-record.json). The example is illustrative only, not observed performance or a real vendor recommendation.
@@ -0,0 +1,20 @@
1
+ # Defend an architecture choice
2
+
3
+ 1. **Freeze requirements, not brands.** Capture public/private routes, execution shape, jobs, data growth, team skill, reliability/recovery target, compliance, budget and ownership. Mark absent inputs unknown, not guessed.
4
+ 2. **List hard constraints and current assets.** Include signed requirements, already-operating infrastructure, existing integrations and migration costs. Exclude options that violate a binding constraint.
5
+ 3. **Decompose independently.** Consider rendering mode, frontend framework, API and background runtime, hosting, database, storage, auth and operations only to the extent each affects this product. A full-stack framework, host and DB are not inseparable.
6
+ 4. **Search for the smaller third option.** If a logged-in dashboard already has an API and no server-rendering need, test whether a client-side application is sufficient before comparing two full-stack frameworks.
7
+ 5. **Compare serious candidates.** For each: what it makes easy now, what it burdens the team with, failure mode, operational owner, irreversibility, exit path and evidence. Use current official docs/benchmarks where claims are time-sensitive; otherwise mark unverified.
8
+ 6. **Stress the decision.** Evaluate today plus at least one plausible future state: growth, 3am incident, loss of provider feature, backup restore, compliance change, deployment topology or price sensitivity. Future states are conditional scenarios, not predictions.
9
+ 7. **Recommend conditionally.** Distinguish verified facts from assumptions and articulate a concrete revisit trigger. If critical owner/requirement/price is unresolved, choose a reversible experiment or pause hard commitments. Do not block harmless discovery.
10
+ 8. **Handoff execution.** Obtain user approval for material platform, spending, migration or data-placement decisions. Once authorized, resume the parent implementation workflow and test actual deployment characteristics.
11
+
12
+ ## Worked case: authenticated SaaS
13
+
14
+ **Evidence:** Public content comprises landing and legal pages. The private app has heavy background jobs. No confirmed ops owner or restore target.
15
+
16
+ **Consider:** Separate the rendering layer from background compute. Compare a Vite SPA, TanStack Start and Next.js against actual server-rendering needs. Compare VPS and managed runtime on deploy effort, incident response, queue support and fixed/usage cost. Compare colocated and managed PostgreSQL on restore testing, backups, isolation and team capability.
17
+
18
+ **Do not conclude:** "Next.js always requires Vercel", "Supabase is required for PostgreSQL", or "VPS is inherently superior over two years". All three are unsupported shortcuts.
19
+
20
+ **Conditional output:** A VPS with local PostgreSQL can be a strong initial option if an owner can patch it, monitor it and prove an offsite restore. Managed hosting may be preferable when no one can operate that stack. Framework choice remains independent of hosting, and no option is selected purely by popularity.
@@ -0,0 +1,19 @@
1
+ # Investigate a derived-answer problem
2
+
3
+ 1. **Separate the goal from the technique.** Translate "build seven metrics" to "determine X to support decision Y". If the goal itself is ambiguous, ask the smallest blocking question.
4
+ 2. **Inventory direct sources.** Search available field dictionaries, API contracts, code paths, existing metrics, logs, reports and upstream systems. Use exact semantic names and synonyms. Never assume all sources were searched when tools or permissions are missing.
5
+ 3. **Check meaning, not label alone.** Verify units, time window, cohort, event definition, provenance, freshness and data ownership. A field with a matching name may still be the wrong answer.
6
+ 4. **Falsify the indirect plan.** Ask what evidence would make the chain of proxies unnecessary or less reliable. If the direct source passes semantic checks, propose stopping the derivative work.
7
+ 5. **If source evidence conflicts, isolate the disputed definition.** Do not average incompatible figures or force a false winner. Mark unknown, identify the smallest reconciliation test, and pause a consequential conclusion.
8
+ 6. **When no direct route exists, construct the smallest defensible inference.** State its assumptions, uncertainty and a test against independent observations. Stop adding measures when they cease improving the target decision.
9
+ 7. **Reassess only after material discoveries.** Record what changed and whether existing artifacts can still serve an independent purpose. Sunk cost is never a reason to continue.
10
+
11
+ ## Worked case
12
+
13
+ **Request:** "Combine registration rate, conversion rate and deduplicated events to estimate final completed purchases."
14
+
15
+ **Observed:** The transaction API exposes `completed_purchases`, but it includes refunded purchases. The decision asks for non-refunded completed purchases.
16
+
17
+ **Action:** Verify whether a net-purchase field or refund join already exists. Do not declare the raw field equivalent merely because its name is close. If a verified net field exists, use it directly. Otherwise calculate only the missing net adjustment, not an elaborate funnel of unrelated proxies.
18
+
19
+ **Handoff:** Goal; direct source checked; semantic mismatch; minimal calculation (if required); evidence confidence; next probe.