@use-the-fork/spindle 0.0.0-stage → 0.0.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.
Files changed (245) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -2
  3. package/build/Assets/Blueprints/Catalog.js +110 -0
  4. package/build/Assets/Blueprints/agents/general.agent.md +67 -0
  5. package/build/Assets/Blueprints/agents/sdd-code-reviewer.agent.md +62 -0
  6. package/build/Assets/Blueprints/agents/sdd-code-slop-reviewer.agent.md +59 -0
  7. package/build/Assets/Blueprints/agents/sdd-implementer.agent.md +77 -0
  8. package/build/Assets/Blueprints/agents/sdd-orchestrator.agent.md +42 -0
  9. package/build/Assets/Blueprints/agents/sdd-re-review.agent.md +57 -0
  10. package/build/Assets/Blueprints/agents/sdd-reviewer.agent.md +89 -0
  11. package/build/Assets/Blueprints/agents/sdd-task-reviewer.agent.md +54 -0
  12. package/build/Assets/Blueprints/agents/shared-codebase-analyzer.agent.md +151 -0
  13. package/build/Assets/Blueprints/agents/shared-codebase-locator.agent.md +130 -0
  14. package/build/Assets/Blueprints/agents/shared-codebase-pattern-finder.agent.md +247 -0
  15. package/build/Assets/Blueprints/agents/shared-explore.agent.md +53 -0
  16. package/build/Assets/Blueprints/agents/shared-r.agent.md +17 -0
  17. package/build/Assets/Blueprints/agents/shared-rw.agent.md +17 -0
  18. package/build/Assets/Blueprints/agents/shared-rwx.agent.md +17 -0
  19. package/build/Assets/Blueprints/agents/shared-web-search-researcher.agent.md +122 -0
  20. package/build/Assets/Blueprints/instructions/code-commenting.instructions.md +39 -0
  21. package/build/Assets/Blueprints/instructions/source-architecture.instructions.md +15 -0
  22. package/build/Assets/Blueprints/skills/asd-ste100/CREDITS.MD +1 -0
  23. package/build/Assets/Blueprints/skills/asd-ste100/SKILL.md +83 -0
  24. package/build/Assets/Blueprints/skills/asd-ste100/examples/before-after.md +107 -0
  25. package/build/Assets/Blueprints/skills/asd-ste100/references/writing-rules.md +65 -0
  26. package/build/Assets/Blueprints/skills/author-agent/SKILL.md +26 -0
  27. package/build/Assets/Blueprints/skills/author-design/SKILL.md +38 -0
  28. package/build/Assets/Blueprints/skills/author-instruction/SKILL.md +28 -0
  29. package/build/Assets/Blueprints/skills/author-skill/SKILL.md +26 -0
  30. package/build/Assets/Blueprints/skills/grill/SKILL.md +33 -0
  31. package/build/Assets/Blueprints/skills/grill-design/SKILL.md +41 -0
  32. package/build/Assets/Blueprints/skills/grill-with-docs/SKILL.md +14 -0
  33. package/build/Assets/Blueprints/skills/implement/SKILL.md +79 -0
  34. package/build/Assets/Blueprints/skills/implement-spec/SKILL.md +34 -0
  35. package/build/Assets/Blueprints/skills/improve-codebase-architecture/HTML-REPORT.md +129 -0
  36. package/build/Assets/Blueprints/skills/improve-codebase-architecture/SKILL.md +74 -0
  37. package/build/Assets/Blueprints/skills/int-code-review/SKILL.md +76 -0
  38. package/build/Assets/Blueprints/skills/int-code-slop-review/SKILL.md +238 -0
  39. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/_sections.md +36 -0
  40. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/_template.md +42 -0
  41. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-closing-brace-labels.md +112 -0
  42. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-empty-docblocks.md +132 -0
  43. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-narration.md +118 -0
  44. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/comments-placeholder.md +130 -0
  45. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-generic-catch.md +153 -0
  46. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-impossible-null.md +157 -0
  47. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/defensive-missing-real.md +157 -0
  48. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-generic-placeholders.md +120 -0
  49. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-over-descriptive.md +108 -0
  50. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-suffix-abuse.md +167 -0
  51. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/naming-type-in-name.md +100 -0
  52. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-dependency-creep.md +114 -0
  53. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-premature-interface.md +136 -0
  54. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-single-method-class.md +161 -0
  55. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/over-eng-useless-wrapper.md +140 -0
  56. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-as-any-escape.md +127 -0
  57. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-debug-artifacts.md +144 -0
  58. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-hyper-consistent.md +84 -0
  59. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-no-hack-scars.md +113 -0
  60. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/style-trivial-boilerplate.md +191 -0
  61. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-doesnt-throw.md +132 -0
  62. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-mirror-implementation.md +125 -0
  63. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-mock-everything.md +148 -0
  64. package/build/Assets/Blueprints/skills/int-code-slop-review/rules/test-snapshot-abuse.md +131 -0
  65. package/build/Assets/Blueprints/skills/int-codebase-design/SKILL.md +116 -0
  66. package/build/Assets/Blueprints/skills/int-codebase-design/references/DEEPENING.md +37 -0
  67. package/build/Assets/Blueprints/skills/int-codebase-design/references/DESIGN-IT-TWICE.md +44 -0
  68. package/build/Assets/Blueprints/skills/int-domain-modeling/SKILL.md +78 -0
  69. package/build/Assets/Blueprints/skills/int-domain-modeling/references/ADR-FORMAT.md +47 -0
  70. package/build/Assets/Blueprints/skills/int-domain-modeling/references/GLOSSARY-FORMAT.md +60 -0
  71. package/build/Assets/Blueprints/skills/int-prototype/LOGIC.md +67 -0
  72. package/build/Assets/Blueprints/skills/int-prototype/SKILL.md +30 -0
  73. package/build/Assets/Blueprints/skills/int-prototype/UI.md +112 -0
  74. package/build/Assets/Blueprints/skills/int-retrospective/SKILL.md +88 -0
  75. package/build/Assets/Blueprints/skills/int-retrospective/assets/retrospective-prompt.md +38 -0
  76. package/build/Assets/Blueprints/skills/int-retrospective/references/assessment.md +56 -0
  77. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/SKILL.md +64 -0
  78. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/assets/implementer-prompt.md +40 -0
  79. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/assets/reviewer-prompt.md +43 -0
  80. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/aggregate-review.md +7 -0
  81. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/corrective-rounds.md +72 -0
  82. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/example-workflow.md +25 -0
  83. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/full-spec.md +72 -0
  84. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/implementer-corrections.md +9 -0
  85. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/review-inputs.md +26 -0
  86. package/build/Assets/Blueprints/skills/int-subagent-driven-dev/references/verification-evidence.md +7 -0
  87. package/build/Assets/Blueprints/skills/int-test-driven-development/SKILL.md +51 -0
  88. package/build/Assets/Blueprints/skills/int-test-driven-development/references/mocking.md +31 -0
  89. package/build/Assets/Blueprints/skills/int-test-driven-development/references/tests.md +68 -0
  90. package/build/Assets/Blueprints/skills/int-testing-agent-guidance/SKILL.md +53 -0
  91. package/build/Assets/Blueprints/skills/int-verification-before-completion/SKILL.md +25 -0
  92. package/build/Assets/Blueprints/skills/int-writing-agents/SKILL.md +66 -0
  93. package/build/Assets/Blueprints/skills/int-writing-for-agents/SKILL.md +86 -0
  94. package/build/Assets/Blueprints/skills/int-writing-instruction/SKILL.md +57 -0
  95. package/build/Assets/Blueprints/skills/int-writing-skills/SKILL.md +129 -0
  96. package/build/Assets/Blueprints/skills/int-writing-skills/assets/interactive.md +83 -0
  97. package/build/Assets/Blueprints/skills/int-writing-skills/assets/reference.md +32 -0
  98. package/build/Assets/Blueprints/skills/int-writing-skills/references/arguments.md +50 -0
  99. package/build/Assets/Blueprints/skills/int-writing-skills/references/interactive-skills.md +53 -0
  100. package/build/Assets/Blueprints/skills/int-writing-skills/references/persuasion-principles.md +220 -0
  101. package/build/Assets/Blueprints/skills/int-writing-skills/references/reference-skills.md +20 -0
  102. package/build/Assets/Blueprints/skills/int-writing-skills/references/testing-skills.md +48 -0
  103. package/build/Assets/Blueprints/skills/retrospective/SKILL.md +16 -0
  104. package/build/Assets/Blueprints/skills/setup-project/SKILL.md +66 -0
  105. package/build/Assets/Blueprints/skills/to-spec/SKILL.md +53 -0
  106. package/build/Assets/Blueprints/skills/to-tickets/SKILL.md +49 -0
  107. package/build/Assets/Blueprints/skills/update-tooling/SKILL.md +86 -0
  108. package/build/Assets/Templates/Catalog.js +12 -0
  109. package/build/Assets/Templates/Catalog.ts +17 -0
  110. package/build/Assets/Templates/project/copilot-instructions.md +17 -0
  111. package/build/Assets/Templates/project/domain.md +51 -0
  112. package/build/Assets/Templates/project/ticket-tracker.md +33 -0
  113. package/build/Assets/Templates/sdd/design.md +25 -0
  114. package/build/Assets/Templates/sdd/spec.md +29 -0
  115. package/build/Assets/Templates/sdd/ticket.md +15 -0
  116. package/build/BlueprintManagement/BlueprintManagementServiceProvider.js +15 -0
  117. package/build/BlueprintManagement/Catalog/BlueprintCatalog.js +238 -0
  118. package/build/BlueprintManagement/Commands/BlueprintCommandOptions.js +9 -0
  119. package/build/BlueprintManagement/Commands/ConflictPolicy.js +6 -0
  120. package/build/BlueprintManagement/Commands/InitCommand.js +117 -0
  121. package/build/BlueprintManagement/Commands/Prompts.js +66 -0
  122. package/build/BlueprintManagement/Commands/UpdateCommand.js +86 -0
  123. package/build/BlueprintManagement/Handlers/Agents/AgentHandler.js +21 -0
  124. package/build/BlueprintManagement/Handlers/BlueprintHandlers.js +4 -0
  125. package/build/BlueprintManagement/Handlers/Instructions/InstructionHandler.js +21 -0
  126. package/build/BlueprintManagement/Handlers/Skills/SkillHandler.js +10 -0
  127. package/build/BlueprintManagement/Reconciliation/BlueprintCatalogPlanner.js +87 -0
  128. package/build/BlueprintManagement/Reconciliation/BlueprintRemovalCoordinator.js +78 -0
  129. package/build/BlueprintManagement/Services/BlueprintReconciliationService.js +391 -0
  130. package/build/BlueprintManagement/Services/InstallationService.js +54 -0
  131. package/build/BlueprintManagement/Support/BlueprintFiles.js +113 -0
  132. package/build/BlueprintManagement/Types/Blueprint.js +1 -0
  133. package/build/BlueprintManagement/Types/Installation.js +1 -0
  134. package/build/BlueprintManagement/Types/Reconciliation.js +1 -0
  135. package/build/BlueprintManagement/Versioning/BlueprintVersion.js +70 -0
  136. package/build/BlueprintManagement/Versioning/BlueprintVersioning.js +55 -0
  137. package/build/Commands/Command.js +3 -0
  138. package/build/Commands/CommandOptions.js +12 -0
  139. package/build/Config/Config.js +3 -0
  140. package/build/Foundation/Application.js +112 -0
  141. package/build/Foundation/ApplicationBuilder.js +23 -0
  142. package/build/Foundation/Bootstrap/Bootstrapper.js +2 -0
  143. package/build/Foundation/Bootstrap/DiscoverGitRoot.js +8 -0
  144. package/build/Foundation/Bootstrap/LoadConsoleArgs.js +47 -0
  145. package/build/Foundation/Bootstrap/LoadProjectConfiguration.js +144 -0
  146. package/build/Foundation/Bootstrap/RegisterProviders.js +12 -0
  147. package/build/Foundation/Bootstrap/RequireProjectConfiguration.js +14 -0
  148. package/build/Foundation/ConsoleInvocation.js +1 -0
  149. package/build/Foundation/Container.js +185 -0
  150. package/build/Foundation/FoundationServiceProvider.js +5 -0
  151. package/build/Foundation/Kernel.js +30 -0
  152. package/build/Foundation/Service.js +23 -0
  153. package/build/Foundation/ServiceProvider.js +12 -0
  154. package/build/Git/GitServiceProvider.js +8 -0
  155. package/build/Git/Services/GitService.js +26 -0
  156. package/build/Project/ProjectServiceProvider.js +8 -0
  157. package/build/Project/Services/ProjectService.js +124 -0
  158. package/build/SubagentDrivenDevelopment/Commands/ChecksRecordCommand.js +35 -0
  159. package/build/SubagentDrivenDevelopment/Commands/ChecksRequireCommand.js +33 -0
  160. package/build/SubagentDrivenDevelopment/Commands/ClaimCommand.js +23 -0
  161. package/build/SubagentDrivenDevelopment/Commands/CorrectCommand.js +37 -0
  162. package/build/SubagentDrivenDevelopment/Commands/CreateCommand.js +26 -0
  163. package/build/SubagentDrivenDevelopment/Commands/FindingCreateCommand.js +39 -0
  164. package/build/SubagentDrivenDevelopment/Commands/FindingUpdateCommand.js +36 -0
  165. package/build/SubagentDrivenDevelopment/Commands/FinishCommand.js +23 -0
  166. package/build/SubagentDrivenDevelopment/Commands/ImplementCommand.js +23 -0
  167. package/build/SubagentDrivenDevelopment/Commands/NextCommand.js +21 -0
  168. package/build/SubagentDrivenDevelopment/Commands/ReadyCommand.js +21 -0
  169. package/build/SubagentDrivenDevelopment/Commands/RequirementsDecideCommand.js +33 -0
  170. package/build/SubagentDrivenDevelopment/Commands/ResumeCommand.js +31 -0
  171. package/build/SubagentDrivenDevelopment/Commands/ReviewCommand.js +55 -0
  172. package/build/SubagentDrivenDevelopment/Commands/ReviewRequireCommand.js +12 -0
  173. package/build/SubagentDrivenDevelopment/Commands/StatusCommand.js +21 -0
  174. package/build/SubagentDrivenDevelopment/Commands/SubmitCommand.js +59 -0
  175. package/build/SubagentDrivenDevelopment/Commands/TicketCommentCommand.js +24 -0
  176. package/build/SubagentDrivenDevelopment/Commands/TicketCreateCommand.js +25 -0
  177. package/build/SubagentDrivenDevelopment/Commands/TicketUpdateCommand.js +31 -0
  178. package/build/SubagentDrivenDevelopment/Commands/groups.js +36 -0
  179. package/build/SubagentDrivenDevelopment/Evidence/GitEvidence.js +449 -0
  180. package/build/SubagentDrivenDevelopment/Evidence/ReportEvidence.js +80 -0
  181. package/build/SubagentDrivenDevelopment/Models/Assignments.js +1 -0
  182. package/build/SubagentDrivenDevelopment/Models/Evidence.js +1 -0
  183. package/build/SubagentDrivenDevelopment/Models/Findings.js +1 -0
  184. package/build/SubagentDrivenDevelopment/Models/Options.js +1 -0
  185. package/build/SubagentDrivenDevelopment/Models/Workflow.js +16 -0
  186. package/build/SubagentDrivenDevelopment/Persistence/AssignmentSchema.js +155 -0
  187. package/build/SubagentDrivenDevelopment/Persistence/CheckSchema.js +71 -0
  188. package/build/SubagentDrivenDevelopment/Persistence/FeatureWorkspace.js +76 -0
  189. package/build/SubagentDrivenDevelopment/Persistence/FileSystem.js +28 -0
  190. package/build/SubagentDrivenDevelopment/Persistence/FindingSchema.js +81 -0
  191. package/build/SubagentDrivenDevelopment/Persistence/LedgerRepository.js +322 -0
  192. package/build/SubagentDrivenDevelopment/Persistence/PathSafety.js +62 -0
  193. package/build/SubagentDrivenDevelopment/Persistence/ProgressSchema.js +95 -0
  194. package/build/SubagentDrivenDevelopment/Persistence/ReadinessReader.js +40 -0
  195. package/build/SubagentDrivenDevelopment/Persistence/ReviewSchema.js +68 -0
  196. package/build/SubagentDrivenDevelopment/Persistence/TicketRepository.js +147 -0
  197. package/build/SubagentDrivenDevelopment/Persistence/WorkspaceTransaction.js +282 -0
  198. package/build/SubagentDrivenDevelopment/Policies/AcceptancePolicy.js +100 -0
  199. package/build/SubagentDrivenDevelopment/Policies/AssignmentPolicy.js +36 -0
  200. package/build/SubagentDrivenDevelopment/Policies/CheckPolicy.js +93 -0
  201. package/build/SubagentDrivenDevelopment/Policies/FindingPolicy.js +53 -0
  202. package/build/SubagentDrivenDevelopment/Policies/InputValidation.js +36 -0
  203. package/build/SubagentDrivenDevelopment/Policies/ReadinessPolicy.js +26 -0
  204. package/build/SubagentDrivenDevelopment/Policies/RequiredReviewers.js +12 -0
  205. package/build/SubagentDrivenDevelopment/Policies/ReviewPolicy.js +87 -0
  206. package/build/SubagentDrivenDevelopment/Policies/WorkflowPolicy.js +85 -0
  207. package/build/SubagentDrivenDevelopment/Services/AssignmentService.js +269 -0
  208. package/build/SubagentDrivenDevelopment/Services/ChecksService.js +319 -0
  209. package/build/SubagentDrivenDevelopment/Services/CorrectionService.js +273 -0
  210. package/build/SubagentDrivenDevelopment/Services/DeliveryCorrectionService.js +253 -0
  211. package/build/SubagentDrivenDevelopment/Services/DeliveryFindingService.js +302 -0
  212. package/build/SubagentDrivenDevelopment/Services/DeliveryService.js +160 -0
  213. package/build/SubagentDrivenDevelopment/Services/DeliverySubmissionService.js +273 -0
  214. package/build/SubagentDrivenDevelopment/Services/FeatureCreationService.js +138 -0
  215. package/build/SubagentDrivenDevelopment/Services/FeatureInspectionService.js +399 -0
  216. package/build/SubagentDrivenDevelopment/Services/FindingService.js +392 -0
  217. package/build/SubagentDrivenDevelopment/Services/IntegrationService.js +258 -0
  218. package/build/SubagentDrivenDevelopment/Services/ReadinessService.js +21 -0
  219. package/build/SubagentDrivenDevelopment/Services/RequirementsService.js +216 -0
  220. package/build/SubagentDrivenDevelopment/Services/ReviewService.js +471 -0
  221. package/build/SubagentDrivenDevelopment/Services/SubmissionService.js +299 -0
  222. package/build/SubagentDrivenDevelopment/Services/TicketService.js +186 -0
  223. package/build/SubagentDrivenDevelopment/SubagentDrivenDevelopmentServiceProvider.js +113 -0
  224. package/build/Support/Records.js +5 -0
  225. package/build/Support/Repository.js +139 -0
  226. package/build/Support/Stacks.js +10 -0
  227. package/build/TemplateRendering/Computed/ComputedContextService.js +13 -0
  228. package/build/TemplateRendering/Computed/ComputedVariableProvider.js +6 -0
  229. package/build/TemplateRendering/Computed/ComputedVariableProviders.js +6 -0
  230. package/build/TemplateRendering/Computed/Providers/InternalSkillDescriptionProvider.js +6 -0
  231. package/build/TemplateRendering/Computed/Providers/SpindlePackageManagerProvider.js +11 -0
  232. package/build/TemplateRendering/Computed/ResolveRenderContext.js +18 -0
  233. package/build/TemplateRendering/Rendering/RenderText.js +43 -0
  234. package/build/TemplateRendering/TemplateRenderingServiceProvider.js +8 -0
  235. package/build/Tooling/Commands/ToolingAddCommand.js +29 -0
  236. package/build/Tooling/Commands/ToolingListCommand.js +19 -0
  237. package/build/Tooling/Commands/ToolingRemoveCommand.js +19 -0
  238. package/build/Tooling/Commands/ToolingUpdateCommand.js +30 -0
  239. package/build/Tooling/Commands/groups.js +4 -0
  240. package/build/Tooling/Models/ToolingEntry.js +1 -0
  241. package/build/Tooling/Services/ToolingService.js +278 -0
  242. package/build/Tooling/ToolingServiceProvider.js +15 -0
  243. package/build/cli.js +36 -0
  244. package/build/index.js +31 -0
  245. package/package.json +54 -3
@@ -0,0 +1,144 @@
1
+ ---
2
+ title: Debug Artifacts Left in Production Code
3
+ impact: HIGH
4
+ impactDescription: "console.log, dd(), dump(), var_dump — AI's exploratory leftovers ship to production"
5
+ tags: style, debug, ai-fingerprint, cleanup
6
+ ---
7
+
8
+ ## Debug Artifacts Left in Production Code
9
+
10
+ **Impact: HIGH (console.log, dd(), dump(), var_dump — AI's exploratory leftovers ship to production)**
11
+
12
+ `console.log("here")`, `console.log("got user", user)`, `dd($order)`, `dump($result)`, `var_dump($payload)`, `print_r($data)`, `echo $error` — these are the breadcrumbs left from when the developer (or AI) was debugging. They ship to production and:
13
+
14
+ - Leak sensitive data into stdout / log aggregators (PII, tokens)
15
+ - Bloat production logs to the point you can't grep for real signal
16
+ - `dd()` literally halts execution — if it reaches prod, your endpoint returns "1" + var_dump output instead of JSON
17
+
18
+ AI is particularly bad about this because the model tends to add `console.log("got result", x)` "to help with debugging" and rarely removes it before "finalising" the function.
19
+
20
+ ## Incorrect
21
+
22
+ ```typescript
23
+ // ❌ Debug artifacts left in
24
+
25
+ async function processPayment(order: Order, token: string): Promise<Charge> {
26
+ console.log("processPayment start", order.id); // shipped
27
+ console.log("token", token); // SHIPS THE TOKEN
28
+ const charge = await stripe.charges.create({/* ... */});
29
+ console.log("got charge", charge); // shipped
30
+ return charge;
31
+ }
32
+ ```
33
+
34
+ ```php
35
+ // ❌ Same in PHP
36
+ public function processWebhook(Request $request): JsonResponse
37
+ {
38
+ $payload = $request->json()->all();
39
+ dd($payload); // halts execution; returns a debug page
40
+ // …rest never runs
41
+ }
42
+
43
+ public function calculateTax(Order $order): Money
44
+ {
45
+ dump($order); // prints to stdout in production
46
+ print_r($order->items);
47
+ $taxRate = 0.06;
48
+ var_dump($taxRate);
49
+ return $order->subtotal->multiplied($taxRate);
50
+ }
51
+ ```
52
+
53
+ **Why it's slop:**
54
+
55
+ - `dd()` in a controller is an outage — the request never completes
56
+ - `console.log('token', token)` is a credentials leak; on serverless logs, every Stripe call ships the token to CloudWatch
57
+ - `dump()` / `var_dump()` show up in HTTP responses if not in a CLI context (especially during `artisan tinker` or test failures)
58
+ - A repo with 50+ stray `console.log` in production paths signals nobody is reading their own code before merging
59
+
60
+ ## Correct
61
+
62
+ ```typescript
63
+ // ✅ No debug; if logging matters, use the proper logger with context
64
+ import { logger } from "@/lib/logger";
65
+
66
+ async function processPayment(order: Order, token: string): Promise<Charge> {
67
+ // Real logger — structured, redacts secrets, levels enforced
68
+ const log = logger.child({ orderId: order.id });
69
+ log.info("payment.start");
70
+
71
+ const charge = await stripe.charges.create({/* ... */});
72
+
73
+ log.info("payment.success", { chargeId: charge.id, amountCents: charge.amount });
74
+ return charge;
75
+ }
76
+ ```
77
+
78
+ ```php
79
+ // ✅ Structured logging at the boundary; no debug() calls
80
+
81
+ public function processWebhook(Request $request): JsonResponse
82
+ {
83
+ Log::withContext([
84
+ 'webhook_id' => $request->header('Stripe-Webhook-Id'),
85
+ 'event_type' => $request->json('type'),
86
+ ])->info('webhook.received');
87
+
88
+ // … actual handling …
89
+
90
+ return response()->json(['ok' => true]);
91
+ }
92
+ ```
93
+
94
+ **Why it reads human:**
95
+
96
+ - A logger with structured fields and levels (info/warn/error) — not stdout spam
97
+ - Tokens / secrets get redacted by the logger (or not logged at all)
98
+ - The log lines are intentional, useful for production debugging, and won't break the response
99
+
100
+ ## When debug calls in production code ARE warranted
101
+
102
+ Rare. Usually zero. Specific cases:
103
+
104
+ - **Logs in well-defined CLI scripts** that are _meant_ to be verbose: a one-off data migration script can use `echo` / `console.log` freely
105
+ - **Test-only files** (`*.test.ts`, `*Test.php`) — fine to keep debug there during development
106
+ - **Explicit `if (DEBUG_MODE) console.log(...)`** wrapped behind a feature flag — fine, but rare in practice
107
+
108
+ Production controllers, services, jobs, listeners, middleware: **zero raw debug calls**.
109
+
110
+ ## Detection
111
+
112
+ ```bash
113
+ # JavaScript / TypeScript — console.log in production code
114
+ grep -rEn '\bconsole\.(log|debug|info|warn)\(' --include='*.ts' --include='*.tsx' --include='*.js' --include='*.jsx' \
115
+ src/ resources/js/ 2>/dev/null \
116
+ | grep -v -E '\.test\.|\.spec\.|/__tests__/|/scripts/'
117
+
118
+ # PHP — debug helpers
119
+ grep -rEn '\b(dd|dump|var_dump|print_r|var_export)\s*\(' --include='*.php' \
120
+ app/ 2>/dev/null
121
+
122
+ # CI gate — block PRs that introduce debug artifacts
123
+ NEW_DEBUG=$(git diff --diff-filter=ACM origin/main...HEAD -- 'app/**/*.php' 'src/**/*.ts' \
124
+ | grep -E '^\+.*\b(console\.log|dd\(|dump\(|var_dump\(|print_r\()')
125
+ test -z "$NEW_DEBUG" || { echo "Debug artifacts in PR:"; echo "$NEW_DEBUG"; exit 1; }
126
+ ```
127
+
128
+ Oxlint configuration (`.oxlintrc.json`):
129
+
130
+ ```json
131
+ {
132
+ "rules": {
133
+ "no-console": ["error", { "allow": ["warn", "error"] }]
134
+ }
135
+ }
136
+ ```
137
+
138
+ Mago's `no-debug-symbols` flags PHP debug calls such as `var_dump`, `print_r`, and `dd`:
139
+
140
+ ```bash
141
+ mago lint --only no-debug-symbols
142
+ ```
143
+
144
+ Reference: [Laravel Logging docs](https://laravel.com/docs/logging) · [Oxlint no-console](https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-console.html) · [Mago no-debug-symbols](https://mago.carthage.software/1.59.0/en/tools/linter/rules/#no-debug-symbols) · [Pino structured logging](https://github.com/pinojs/pino)
@@ -0,0 +1,84 @@
1
+ ---
2
+ title: Hyper-Consistent Formatting
3
+ impact: MEDIUM
4
+ impactDescription: "A codebase where every file looks linter-perfect is suspiciously not-human"
5
+ tags: style, formatting, ai-fingerprint
6
+ ---
7
+
8
+ ## Hyper-Consistent Formatting
9
+
10
+ **Impact: MEDIUM (A codebase where every file looks linter-perfect is suspiciously not-human)**
11
+
12
+ Real codebases have geology — older files use older conventions, busy modules have rushed sections, 2am hotfix code has different spacing than carefully-reviewed weeks. AI-generated code is **uniformly polished**: every function the same length, every blank line in the same place, every import alphabetized, no styling drift anywhere. This isn't a hallmark of quality — it's a hallmark of generation.
13
+
14
+ The fingerprint is **uniformity at scale**. One file looking pristine is fine. A 2000-line PR where every file is identically pristine, across files that should have different velocity histories, is a strong AI signal.
15
+
16
+ ## What it looks like
17
+
18
+ The PR diff shows:
19
+
20
+ - Every PHP file has exactly 4-space indent, exactly one blank line between methods, exactly two blank lines between class members, every parameter aligned identically
21
+ - Every TS file has the same import-grouping pattern, named imports always alphabetised, every arrow function body wrapped identically
22
+ - No `// HACK:` / `// XXX:` / late-night comments anywhere
23
+ - No commented-out code (clean — but also no signs of someone exploring)
24
+ - Identical commit-message structure across 50 commits
25
+ - All files use the same paradigm (e.g., every function is an arrow function; every class uses constructor property promotion) even in places where the existing codebase mixed styles
26
+
27
+ ## Why this matters
28
+
29
+ Code geology is a tool for the next developer:
30
+
31
+ - **Different ages tell different stories** — "this section is from 2022, before we switched to..."
32
+ - **`// HACK:` markers signal known fragility** that hasn't been worth refactoring
33
+ - **Commented-out code reveals exploration** — "we tried X, didn't work, kept the option in case"
34
+ - **Velocity differences** show which paths were rushed and might harbour bugs
35
+
36
+ A repo with no geology forces every reader to start fresh — no inherited context, no "the team didn't bother fixing this, it works fine" signal. AI-generated codebases are flat: every line treated as equally important.
37
+
38
+ ## The "all green field" tell
39
+
40
+ Hyper-consistency is most suspicious in **brownfield additions** — i.e., when AI adds code to a codebase that has its own conventions. Real engineers either:
41
+
42
+ - **Match the existing style** (consistent with the surrounding 50 files)
43
+ - **Refactor the surrounding files** alongside their change (drift announces itself in the diff)
44
+ - **Introduce a deliberate change** with an explanation ("new files use the new pattern; old files updated as touched")
45
+
46
+ AI typically does the first poorly — it picks "the most recent convention" or "the most popular convention from training data" rather than matching the actual codebase. Result: the new files are linter-perfect by some standard, but inconsistent with the surrounding codebase.
47
+
48
+ ## What "human formatting" looks like
49
+
50
+ You won't catch this with a linter. It's a feel:
51
+
52
+ - Some files have slightly off blank-line spacing where someone hit enter twice
53
+ - Imports sometimes group "stuff I added recently" at the top
54
+ - One file has 6-space indent inside a heredoc because the dev didn't fight prettier on it
55
+ - A function has an extra blank line before a tricky if-branch where the author paused to think
56
+ - A 2-line comment is in mid-sentence-case because someone typed it angry
57
+
58
+ These tiny artifacts are how humans write code. Their absence — especially across many files at once — is the slop.
59
+
60
+ ## Detection
61
+
62
+ There's no automatic detector. The signal is human-judgment:
63
+
64
+ 1. **Run the diff through `git diff --stat`** — many files changed at once is normal; many files all with the same line-count profile is suspicious
65
+ 2. **`git log --author` distribution** — if the diff is from one author but covers 30 files in one commit, raise an eyebrow
66
+ 3. **Compare new files to surrounding files** — does the indentation, import order, brace style match the existing convention?
67
+ 4. **Search for `// HACK:` / `// XXX:` markers** in the new files — their _absence_ is the signal:
68
+
69
+ ```bash
70
+ git diff origin/main...HEAD -- '*.ts' '*.php' | grep -E '// HACK:|// XXX:|// FIXME' || echo "NO HACK/XXX MARKERS"
71
+ ```
72
+
73
+ 5. **Mixed-paradigm check** — in a TS PR, count `function` declarations vs arrow functions; in a PHP PR, count `final class` vs `class`. A sudden swing is a tell.
74
+
75
+ ## What to do
76
+
77
+ If a PR looks hyper-consistent:
78
+
79
+ - Ask the author: "Was this AI-assisted? Walk me through the section in `OrderService` line 80." Their ability to explain the _intent_, not just the code, separates "AI-written, human-reviewed" from "AI-written, accepted-as-is".
80
+ - Sample a few functions and ask: "What's the trade-off you chose here?" If they shrug, the code wasn't really written by them.
81
+
82
+ The goal isn't to ban AI assistance — it's to make sure the author owns the code they ship.
83
+
84
+ Reference: Internal: [`style-no-hack-scars`](style-no-hack-scars.md)
@@ -0,0 +1,113 @@
1
+ ---
2
+ title: No HACK Scars — Suspiciously Pristine Code
3
+ impact: MEDIUM
4
+ impactDescription: "Real codebases have geology; absence of any '// HACK:' / '// XXX:' markers is itself a tell"
5
+ tags: style, geology, ai-fingerprint
6
+ ---
7
+
8
+ ## No HACK Scars — Suspiciously Pristine Code
9
+
10
+ **Impact: MEDIUM (Real codebases have geology; absence of any '// HACK:' / '// XXX:' markers is itself a tell)**
11
+
12
+ A real codebase has _scars_. `// HACK:` comments where someone went around the type system to ship. `// XXX:` markers where the engineer noticed something fishy but had to ship. Files with a "we tried it the right way; this is the workaround until Stripe v2" note. These markers signal:
13
+
14
+ - A human engineered the code, hit a real constraint, and acknowledged the trade-off
15
+ - The team agreed to ship with the trade-off rather than over-engineer
16
+ - A future engineer is being warned
17
+
18
+ AI-generated codebases are flat: no `// HACK:`, no `// XXX:`, no "we know this is wrong but" markers. Every line presented as equally polished. The _absence_ of scars is the slop signal — real systems don't look like this.
19
+
20
+ ## The pattern
21
+
22
+ You can audit a repo's "humanity" by looking for these markers:
23
+
24
+ ```bash
25
+ git grep -E '// (HACK|XXX|NOTE|GOTCHA|HMMMM|WTF)' | wc -l
26
+ ```
27
+
28
+ A repo with 50 of these is alive. A repo with zero across a 50K-line codebase is either:
29
+
30
+ - A library that was rigorously reviewed across many years (rare)
31
+ - An AI-generated codebase passing as human (more common)
32
+
33
+ You're not looking for high counts — you're looking for **some**. Two or three `// HACK:` markers in 5K LoC is healthy.
34
+
35
+ ## What healthy scars look like
36
+
37
+ ```php
38
+ // ✅ Real human marker — explains the constraint
39
+ public function chargeCustomer(int $cents, string $token): string
40
+ {
41
+ // HACK: Stripe v1 SDK doesn't expose retry headers; manually parse from Response
42
+ // until we migrate to v2 (tracked in #1842).
43
+ try {
44
+ return $this->stripe->charges->create([...])->id;
45
+ } catch (RateLimitException $e) {
46
+ $retryAfter = (int) ($e->getResponse()->headers['Retry-After'] ?? 30);
47
+ sleep($retryAfter);
48
+ return $this->stripe->charges->create([...])->id;
49
+ }
50
+ }
51
+ ```
52
+
53
+ ```typescript
54
+ // ✅ XXX marker that warns the next reader
55
+ function parseEnvNumber(key: string): number {
56
+ const raw = process.env[key];
57
+ // XXX: parseInt('1.5') === 1, parseFloat('1.5e10') === 15000000000 — fine for us
58
+ // because we only use this for ports and integer caps. Audit before reusing.
59
+ return parseInt(raw ?? "0", 10);
60
+ }
61
+ ```
62
+
63
+ ```php
64
+ // ✅ NOTE marker capturing tribal knowledge
65
+ class OrderObserver
66
+ {
67
+ public function creating(Order $order): void
68
+ {
69
+ // NOTE: tax_amount must be set BEFORE Stripe charge; the webhook handler
70
+ // re-reads this row and expects it to be non-zero.
71
+ $order->tax_amount = TaxCalculator::for($order);
72
+ }
73
+ }
74
+ ```
75
+
76
+ These tell the next engineer: _"someone thought about this; the workaround is intentional; if you change it, here's the context."_
77
+
78
+ ## What absence looks like
79
+
80
+ Five files, 600 lines, all in a PR:
81
+
82
+ - Every method has the same length and shape
83
+ - Every error path is the same `try { ... } catch (e) { console.error(e); }`
84
+ - No comments about edge cases, browser quirks, library bugs, race conditions
85
+ - No `// HACK:` / `// XXX:` / `// NOTE:` anywhere
86
+ - Every variable has a "perfect" name
87
+
88
+ That's not human. Real production code dealing with real third parties has scars.
89
+
90
+ ## What to do when you find pristine-looking code
91
+
92
+ This rule is harder to action than to detect. Recommended response:
93
+
94
+ 1. **Pair-review with the author** — ask them to walk you through one of the "perfect" sections. If they can articulate the _why_ (and the trade-offs they considered), it's fine. If they can't, treat it as AI-assisted code that needs deeper review.
95
+ 2. **Run mutation testing on the new code** — Stryker (TS) / Infection (PHP). Mirror-tests die fast.
96
+ 3. **Sample edge cases** — try inputs the AI wouldn't have thought of (empty string, very long string, Unicode, negative numbers, future dates, leap years). If the code falls over on basics, the polish was cosmetic.
97
+
98
+ ## Detection
99
+
100
+ ```bash
101
+ # Count scars in the existing repo (the baseline)
102
+ git grep -cE '// (HACK|XXX|NOTE|GOTCHA|WTF|FIXME)' -- '*.ts' '*.tsx' '*.php' '*.js' '*.jsx' 2>/dev/null | head
103
+
104
+ # Same, but for the PR diff only — if a 1000-line PR adds zero scars, suspicious
105
+ git diff origin/main...HEAD -- '*.ts' '*.tsx' '*.php' | \
106
+ grep -cE '^\+.*// (HACK|XXX|NOTE|GOTCHA|WTF)'
107
+ ```
108
+
109
+ A PR adding > 500 LoC with zero `HACK:` / `XXX:` / `NOTE:` is unusual. Real engineering on real systems leaves these.
110
+
111
+ **Note:** this rule cuts both ways. Don't _add_ a `// HACK:` just to look human. Add them when you actually have a hack to mark. The signal is genuine, not performative.
112
+
113
+ Reference: Internal: [`style-hyper-consistent`](style-hyper-consistent.md)
@@ -0,0 +1,191 @@
1
+ ---
2
+ title: Trivial Boilerplate
3
+ impact: MEDIUM
4
+ impactDescription: "Pattern-matched boilerplate that hides intent and inflates line count"
5
+ tags: style, boilerplate, ai-fingerprint, readability
6
+ ---
7
+
8
+ ## Trivial Boilerplate
9
+
10
+ **Impact: MEDIUM (Pattern-matched boilerplate that hides intent and inflates line count)**
11
+
12
+ The class of "code that says less than the underlying expression". Common AI variants:
13
+
14
+ - `if (x) return true; else return false;` — when `return x` does it
15
+ - `return x === true ? true : false;` — ditto
16
+ - `const isPaid: boolean = order.status === 'paid';` — TS infers `boolean` already; the annotation is noise
17
+ - `const name: string = 'asyraf';` — TS infers `string`
18
+ - `await Promise.resolve(value)` — when `value` is already non-Promise
19
+ - `try { await fn(); } catch (e) { throw e; }` — pass-through catch
20
+ - `[...array]` to "make a copy" when the next operation doesn't mutate
21
+
22
+ Each is a small AI fingerprint. A few are fine. A repo with many of them across files reads as model-generated.
23
+
24
+ ## Incorrect
25
+
26
+ ```typescript
27
+ // ❌ if-true-false
28
+ function isPaid(order: Order): boolean {
29
+ if (order.status === "paid") {
30
+ return true;
31
+ } else {
32
+ return false;
33
+ }
34
+ }
35
+
36
+ // ❌ Ternary returning the booleans it was given
37
+ function isCompleted(order: Order): boolean {
38
+ return order.status === "completed" ? true : false;
39
+ }
40
+
41
+ // ❌ Redundant type annotations on obvious literals
42
+ const name: string = "asyraf";
43
+ const age: number = 30;
44
+ const isActive: boolean = true;
45
+ const orders: Order[] = await db.orders.find(); // db.orders.find() return type IS Order[]
46
+
47
+ // ❌ Pass-through catch
48
+ async function processPayment(order: Order): Promise<Charge> {
49
+ try {
50
+ return await stripe.charges.create({/* ... */});
51
+ } catch (e) {
52
+ throw e; // catch literally does nothing
53
+ }
54
+ }
55
+
56
+ // ❌ Unnecessary await + Promise.resolve
57
+ async function formatName(user: User): Promise<string> {
58
+ return await Promise.resolve(`${user.first} ${user.last}`);
59
+ }
60
+
61
+ // ❌ Spread to "copy" — but nothing mutates
62
+ function totalsFor(items: Item[]): number {
63
+ const copied = [...items]; // unused: the next op doesn't mutate
64
+ return copied.reduce((s, i) => s + i.price, 0);
65
+ }
66
+ ```
67
+
68
+ ```php
69
+ // ❌ PHP equivalents
70
+ public function isPaid(Order $order): bool
71
+ {
72
+ if ($order->status === 'paid') {
73
+ return true;
74
+ } else {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ public function processWebhook(Request $request): JsonResponse
80
+ {
81
+ try {
82
+ return $this->handle($request);
83
+ } catch (\Exception $e) {
84
+ throw $e; // pass-through
85
+ }
86
+ }
87
+ ```
88
+
89
+ **Why it's slop:**
90
+
91
+ - Each line carries no information beyond the simpler form
92
+ - The `try { ... } catch (e) { throw e; }` is the most embarrassing — six tokens to do nothing
93
+ - Type annotations on inferred literals fight the type system instead of using it
94
+ - A reader's eyes have to traverse "what does this expand to?" rather than read the expression directly
95
+
96
+ ## Correct
97
+
98
+ ```typescript
99
+ // ✅ Direct expressions
100
+ function isPaid(order: Order): boolean {
101
+ return order.status === "paid";
102
+ }
103
+
104
+ function isCompleted(order: Order): boolean {
105
+ return order.status === "completed";
106
+ }
107
+
108
+ // ✅ Let TS infer
109
+ const name = "asyraf";
110
+ const age = 30;
111
+ const isActive = true;
112
+ const orders = await db.orders.find();
113
+
114
+ // ✅ No catch when not handling
115
+ async function processPayment(order: Order): Promise<Charge> {
116
+ return stripe.charges.create({/* ... */});
117
+ }
118
+
119
+ // ✅ No need to await a non-Promise
120
+ function formatName(user: User): string {
121
+ return `${user.first} ${user.last}`;
122
+ }
123
+
124
+ // ✅ No spread when nothing mutates
125
+ function totalsFor(items: Item[]): number {
126
+ return items.reduce((s, i) => s + i.price, 0);
127
+ }
128
+ ```
129
+
130
+ ```php
131
+ // ✅ PHP equivalents
132
+ public function isPaid(Order $order): bool
133
+ {
134
+ return $order->status === 'paid';
135
+ }
136
+
137
+ public function processWebhook(Request $request): JsonResponse
138
+ {
139
+ return $this->handle($request);
140
+ }
141
+ ```
142
+
143
+ **Why it reads human:**
144
+
145
+ - Each expression does exactly the work; nothing extra
146
+ - Type inference does its job; the type annotations are added where they earn their place (function signatures, public APIs)
147
+ - No pass-through catch; if an error needs handling, it's handled
148
+
149
+ ## When annotations / spreads ARE worth it
150
+
151
+ A few cases:
152
+
153
+ - **Public-API parameters / returns** — explicit types document the contract: `function add(a: number, b: number): number`
154
+ - **Complex inferred types** — annotate when the inferred type is hard to read
155
+ - **Boundary code** — `as const`, `satisfies`, or explicit annotations on configuration objects help downstream type narrowing
156
+ - **Spread when you actually want a copy** — before sorting, before mutating, before passing to a function that mutates
157
+
158
+ The trivial-boilerplate test: **does removing the construct change the program's Behavior or the reader's understanding?** If no, remove.
159
+
160
+ ## Detection
161
+
162
+ ```bash
163
+ # if-true-false
164
+ grep -rEnB1 -A2 'if\s*\([^)]+\)\s*\{?\s*$' --include='*.ts' --include='*.tsx' --include='*.js' --include='*.php' \
165
+ src/ app/ 2>/dev/null | grep -B2 -A1 'return true\b' | grep -A2 'else' | head
166
+
167
+ # Pass-through catch
168
+ grep -rEnB0 -A1 'catch\s*\([^)]*\)\s*\{' --include='*.ts' --include='*.tsx' --include='*.php' \
169
+ src/ app/ 2>/dev/null | grep -B1 '^\s*throw\b'
170
+
171
+ # Ternary returning booleans
172
+ grep -rEn '\?\s*true\s*:\s*false\b' --include='*.ts' --include='*.tsx' --include='*.js' --include='*.php' \
173
+ src/ app/
174
+
175
+ # Redundant type annotations on string/number/boolean literals (TS)
176
+ grep -rEn ':\s*(string|number|boolean)\s*=\s*("[^"]*"|[0-9]+|true|false)' --include='*.ts' --include='*.tsx' src/
177
+ ```
178
+
179
+ Oxlint configuration (`.oxlintrc.json`):
180
+
181
+ ```json
182
+ {
183
+ "rules": {
184
+ "no-useless-return": "error",
185
+ "no-useless-catch": "error",
186
+ "no-unneeded-ternary": "error"
187
+ }
188
+ }
189
+ ```
190
+
191
+ Reference: [Oxlint no-useless-catch](https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-useless-catch.html) · [Oxlint no-unneeded-ternary](https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-unneeded-ternary.html) · [Oxlint no-useless-return](https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-useless-return.html) · [PHP-CS-Fixer rules](https://cs.symfony.com/) · Internal: [`defensive-generic-catch`](defensive-generic-catch.md)
@@ -0,0 +1,132 @@
1
+ ---
2
+ title: "Doesn't Throw" Tests
3
+ impact: HIGH
4
+ impactDescription: "Tests that assert nothing meaningful; pass even when the function does the wrong thing"
5
+ tags: testing, weak-assertions, ai-fingerprint
6
+ ---
7
+
8
+ ## "Doesn't Throw" Tests
9
+
10
+ **Impact: HIGH (Tests that assert nothing meaningful; pass even when the function does the wrong thing)**
11
+
12
+ A test that calls the function and then asserts the function didn't throw is barely a test. It verifies one of the weakest possible properties — "the program didn't crash" — and gives false confidence in coverage. AI often defaults to this pattern because it's the easiest way to make a test pass.
13
+
14
+ A real test verifies a specific outcome. "Didn't throw" is an outcome only in the narrowest cases (you specifically want to verify a thrown error from a previous bug is now absent).
15
+
16
+ ## Incorrect
17
+
18
+ ```typescript
19
+ // ❌ Tests that just verify "the call completes"
20
+
21
+ describe("OrderService", () => {
22
+ it("places an order", async () => {
23
+ const service = new OrderService();
24
+ await expect(service.place(makeValidOrder())).resolves.not.toThrow();
25
+ });
26
+
27
+ it("exports users", async () => {
28
+ const service = new UserExportService();
29
+ const result = await service.export();
30
+ expect(result).toBeDefined(); // weak: undefined is the only failure mode caught
31
+ });
32
+
33
+ it("handles empty input", () => {
34
+ const result = sum([]);
35
+ expect(result).not.toBeNull(); // passes for 0, NaN, '', false, anything except null/undefined
36
+ });
37
+ });
38
+ ```
39
+
40
+ ```php
41
+ // ❌ Same pattern in PHPUnit / Pest
42
+ test('it places an order', function () {
43
+ $service = new OrderService();
44
+
45
+ expect(fn () => $service->place(makeValidOrder()))->not->toThrow();
46
+ });
47
+
48
+ test('export returns something', function () {
49
+ $result = (new UserExportService())->export();
50
+ expect($result)->not->toBeNull();
51
+ });
52
+ ```
53
+
54
+ **Why it's slop:**
55
+
56
+ - "Doesn't throw" tells you nothing about whether the right thing happened
57
+ - `place(order)` could return without throwing AND not actually place the order
58
+ - `export()` could return `undefined` (the test fails) OR `null` (test fails) OR an empty array, a misleading "true", etc. — the test catches only the narrowest failure
59
+ - `not.toBeNull()` passes for `0`, `''`, `NaN`, `false` — all of which are usually bugs
60
+
61
+ ## Correct — assert what should happen
62
+
63
+ ```typescript
64
+ // ✅ Specific outcomes
65
+
66
+ describe("OrderService", () => {
67
+ it("place(order) persists the order and returns the saved row with status=pending", async () => {
68
+ const service = new OrderService(db);
69
+ const order = makeValidOrder({ total: 100_00 });
70
+
71
+ const saved = await service.place(order);
72
+
73
+ expect(saved.id).toBeDefined();
74
+ expect(saved.status).toBe("pending");
75
+ expect(saved.total).toBe(100_00);
76
+ expect(await db.orders.findById(saved.id)).toMatchObject({ status: "pending" });
77
+ });
78
+
79
+ it("exports all active users to CSV", async () => {
80
+ await db.users.insertMany([
81
+ { email: "a@b.com", active: true },
82
+ { email: "c@d.com", active: false },
83
+ { email: "e@f.com", active: true },
84
+ ]);
85
+
86
+ const csv = await new UserExportService(db).export();
87
+
88
+ expect(csv).toContain("a@b.com");
89
+ expect(csv).toContain("e@f.com");
90
+ expect(csv).not.toContain("c@d.com"); // inactive should be excluded
91
+ });
92
+
93
+ it("sum([]) returns 0", () => {
94
+ expect(sum([])).toBe(0);
95
+ });
96
+ });
97
+ ```
98
+
99
+ **Why it reads human:**
100
+
101
+ - Each test verifies a specific, named outcome
102
+ - Bugs are detected: changing `status: 'pending'` to `'paid'` would fail the test; returning all users (not just active) would fail; `sum([])` returning `NaN` would fail
103
+ - Test names describe the _Behavior_, not the _function called_
104
+
105
+ ## When "doesn't throw" IS legitimate
106
+
107
+ Rarely:
108
+
109
+ - **Smoke tests** for a never-throws contract on a public API (one or two tests; not the bulk of the suite)
110
+ - **Regression tests** for a specific previously-thrown error: "after fix #123, calling X with Y no longer throws"
111
+
112
+ In these cases, name the test clearly: `it('does not throw on empty input — regression for #123')`.
113
+
114
+ ## Detection
115
+
116
+ ```bash
117
+ # Tests whose only assertion is "not.toThrow" or "not.toBeNull"
118
+ grep -rEn '\.not\.toThrow\(|\.not\.toBeNull\(|\.toBeDefined\(' \
119
+ --include='*.test.ts' --include='*.spec.ts' --include='*.test.tsx' src/ tests/ \
120
+ | head -30
121
+
122
+ # PHPUnit / Pest equivalents
123
+ grep -rEn '\bexpect\(.*\)->not->toThrow\(\)|->assertNotNull\(\$result\)' \
124
+ --include='*Test.php' --include='*.test.php' tests/
125
+
126
+ # Heuristic: test files where the ratio of weak assertions to strong assertions is high
127
+ # (manual review of files with > 5 weak assertions)
128
+ ```
129
+
130
+ A few weak assertions are fine. A test suite dominated by them is a slop fingerprint.
131
+
132
+ Reference: [Martin Fowler — Test Pyramid](https://martinfowler.com/articles/practical-test-pyramid.html) · Internal: [`test-mock-everything`](test-mock-everything.md), [`test-mirror-implementation`](test-mirror-implementation.md)