@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,136 @@
1
+ ---
2
+ title: Premature Interface (One Implementation, No Plan for a Second)
3
+ impact: HIGH
4
+ impactDescription: "Adds indirection nobody asked for; doubles the surface for every change"
5
+ tags: over-engineering, interfaces, abstractions, ai-fingerprint
6
+ ---
7
+
8
+ ## Premature Interface (One Implementation, No Plan for a Second)
9
+
10
+ **Impact: HIGH (Adds indirection nobody asked for; doubles the surface for every change)**
11
+
12
+ AI defaults to "enterprise-grade" because its training distribution is enterprise-grade. The result: every service gets an interface and one implementation, with no second implementation in the foreseeable future. The interface is pure ceremony — every change updates two files, every test mocks the interface, every reader chases two clicks to find the actual code.
13
+
14
+ **Interfaces earn their place when:**
15
+
16
+ - There IS a second implementation (e.g., `StripeGateway` and `AdyenGateway`)
17
+ - There IS a planned second implementation (in this quarter's roadmap)
18
+ - The implementation is being swapped in tests with a fake (and even then, dependency injection of the concrete type is often fine)
19
+
20
+ **Otherwise: write the concrete type. Add the interface when a second implementation actually shows up.** YAGNI is real.
21
+
22
+ ## Incorrect
23
+
24
+ ```php
25
+ // ❌ Interface with exactly one implementation, no plan for a second
26
+
27
+ interface UserRepositoryInterface
28
+ {
29
+ public function find(int $id): ?User;
30
+ public function save(User $user): void;
31
+ public function delete(int $id): void;
32
+ }
33
+
34
+ final class UserRepository implements UserRepositoryInterface
35
+ {
36
+ public function find(int $id): ?User { /* ... */ }
37
+ public function save(User $user): void { /* ... */ }
38
+ public function delete(int $id): void { /* ... */ }
39
+ }
40
+
41
+ // Binding in a service provider, mostly to satisfy the interface
42
+ $this->app->bind(UserRepositoryInterface::class, UserRepository::class);
43
+ ```
44
+
45
+ ```typescript
46
+ // ❌ Same TS pattern
47
+ interface IOrderService {
48
+ placeOrder(payload: OrderPayload): Promise<Order>;
49
+ cancelOrder(orderId: string): Promise<void>;
50
+ }
51
+
52
+ class OrderService implements IOrderService {
53
+ async placeOrder(payload: OrderPayload): Promise<Order> {
54
+ /* ... */
55
+ }
56
+ async cancelOrder(orderId: string): Promise<void> {
57
+ /* ... */
58
+ }
59
+ }
60
+ ```
61
+
62
+ **Why it's slop:**
63
+
64
+ - Every method declared in two files
65
+ - `find` / `save` / `delete` change — both interface AND implementation must be updated; nothing prevents drift
66
+ - Reader hits `UserRepositoryInterface` in a controller, has to navigate through to find the actual code
67
+ - The `I*` prefix or `*Interface` suffix is itself a tell — clean naming uses domain words without the marker
68
+
69
+ ## Correct
70
+
71
+ ```php
72
+ // ✅ Concrete class until you need a second implementation
73
+
74
+ final class UserRepository
75
+ {
76
+ public function find(int $id): ?User { /* ... */ }
77
+ public function save(User $user): void { /* ... */ }
78
+ public function delete(int $id): void { /* ... */ }
79
+ }
80
+
81
+ // Controller depends on the concrete class — Laravel auto-resolves it
82
+ public function show(int $id, UserRepository $users): UserResource
83
+ {
84
+ $user = $users->find($id) ?? throw new NotFoundException;
85
+ return new UserResource($user);
86
+ }
87
+ ```
88
+
89
+ ```typescript
90
+ // ✅ Concrete class
91
+ class OrderService {
92
+ async placeOrder(payload: OrderPayload): Promise<Order> {
93
+ /* ... */
94
+ }
95
+ async cancelOrder(orderId: string): Promise<void> {
96
+ /* ... */
97
+ }
98
+ }
99
+ ```
100
+
101
+ **When a second implementation arrives** — e.g., you need a `CachedUserRepository` — _then_ extract the interface. By that point you'll know what shape it should have because you have two concrete examples.
102
+
103
+ **Why it reads human:**
104
+
105
+ - Concrete class is reachable in one click
106
+ - No double-maintenance
107
+ - Adding a second implementation is a deliberate refactor, not a default scaffold
108
+
109
+ ## Tests: don't create an interface just to mock
110
+
111
+ If you need to swap the dependency in tests, prefer:
112
+
113
+ 1. **Inject the concrete class; instantiate with fakes** (e.g., `UserRepository` with an in-memory `Database` connection)
114
+ 2. **Use Laravel's container-bound fake** (`$this->mock(UserRepository::class, ...)`) — no interface needed
115
+ 3. **Method-level mocks** via PHPUnit / Vitest
116
+
117
+ An interface created _only_ to mock the dependency in tests is also slop. Real interfaces serve production code paths.
118
+
119
+ ## Detection
120
+
121
+ ```bash
122
+ # Interfaces in the codebase
123
+ grep -rn '^interface\s\|^export interface' --include='*.php' --include='*.ts' app/ src/ \
124
+ | wc -l
125
+
126
+ # For each interface, count implementations — flag interfaces with exactly 1
127
+ # (manual review with `phpstorm` or VSCode Go to Implementation)
128
+
129
+ # PHP heuristic: interfaces with 'Interface' suffix or 'I*' prefix
130
+ grep -rEn '^interface\s+(I[A-Z]|[A-Z][a-zA-Z]+Interface)\b' --include='*.php' app/
131
+
132
+ # TS heuristic
133
+ grep -rEn '^export interface I[A-Z]|^interface I[A-Z]' --include='*.ts' --include='*.tsx' src/
134
+ ```
135
+
136
+ Reference: [Martin Fowler — YAGNI](https://martinfowler.com/bliki/Yagni.html) · [Sandi Metz — The Wrong Abstraction](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction) · Internal: [`over-eng-useless-wrapper`](over-eng-useless-wrapper.md), [`naming-suffix-abuse`](naming-suffix-abuse.md)
@@ -0,0 +1,161 @@
1
+ ---
2
+ title: Single-Method Class That Should Be a Function
3
+ impact: HIGH
4
+ impactDescription: "Wraps a free function in class ceremony for no reason"
5
+ tags: over-engineering, classes, ai-fingerprint
6
+ ---
7
+
8
+ ## Single-Method Class That Should Be a Function
9
+
10
+ **Impact: HIGH (Wraps a free function in class ceremony for no reason)**
11
+
12
+ A class with exactly one public method, no state, and no dependencies should usually be a function. AI generates these because its training data is enterprise Java/C# where classes are mandatory for everything. PHP and TypeScript both support free functions / static methods / single-purpose action classes — the wrapping ceremony is pure slop.
13
+
14
+ The exception: **invokable action classes** (Laravel convention) using `__invoke()` are legitimate when they have constructor-injected dependencies and represent a named domain action. The slop variant is the class with no constructor, no state, one static method, and no clear domain identity.
15
+
16
+ ## Incorrect
17
+
18
+ ```php
19
+ // ❌ Class wrapping a single pure function
20
+
21
+ class StringFormatter
22
+ {
23
+ public static function slugify(string $input): string
24
+ {
25
+ return Str::slug($input);
26
+ }
27
+ }
28
+
29
+ class EmailValidator
30
+ {
31
+ public function isValid(string $email): bool
32
+ {
33
+ return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
34
+ }
35
+ }
36
+
37
+ class TimestampHelper
38
+ {
39
+ public static function toIso(DateTime $dt): string
40
+ {
41
+ return $dt->format(DateTimeInterface::ATOM);
42
+ }
43
+ }
44
+ ```
45
+
46
+ ```typescript
47
+ // ❌ Same in TS
48
+ class StringUtil {
49
+ static capitalize(s: string): string {
50
+ return s.charAt(0).toUpperCase() + s.slice(1);
51
+ }
52
+ }
53
+
54
+ class ResponseFormatter {
55
+ static format(data: unknown): ApiResponse {
56
+ return { data, timestamp: Date.now() };
57
+ }
58
+ }
59
+ ```
60
+
61
+ **Why it's slop:**
62
+
63
+ - Callers write `StringFormatter::slugify(...)` instead of `slugify(...)`
64
+ - `new EmailValidator()->isValid($email)` is six tokens for what should be one
65
+ - The class adds no encapsulation (no state to encapsulate)
66
+ - Importing/autoloading the class wastes bytes for zero benefit
67
+ - Pattern is recognisably "Java port" — PHP and TS have first-class functions
68
+
69
+ ## Correct
70
+
71
+ ```php
72
+ // ✅ Free function or method on the existing domain type
73
+
74
+ // helpers.php (or composer autoload "files")
75
+ function slugify(string $input): string
76
+ {
77
+ return Str::slug($input);
78
+ }
79
+
80
+ function isValidEmail(string $email): bool
81
+ {
82
+ return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
83
+ }
84
+
85
+ // Or extension methods on Carbon
86
+ class CustomCarbon extends Carbon
87
+ {
88
+ public function toIso(): string { return $this->format(DateTimeInterface::ATOM); }
89
+ }
90
+ ```
91
+
92
+ ```typescript
93
+ // ✅ Free exported functions
94
+ export function capitalize(s: string): string {
95
+ return s.charAt(0).toUpperCase() + s.slice(1);
96
+ }
97
+
98
+ export function formatResponse<T>(data: T): ApiResponse<T> {
99
+ return { data, timestamp: Date.now() };
100
+ }
101
+ ```
102
+
103
+ **Why it reads human:**
104
+
105
+ - Caller writes `slugify(input)` — one token, no ceremony
106
+ - No autoloader hit, no class instantiation, no dependency to mock in tests
107
+ - Functions are testable directly with no setup
108
+
109
+ ## Single-method class is OK when…
110
+
111
+ These are legitimate, NOT slop:
112
+
113
+ ```php
114
+ // ✅ Invokable action with injected dependencies — Laravel idiom
115
+ final class PlaceOrder
116
+ {
117
+ public function __construct(
118
+ private PaymentGateway $payments,
119
+ private InventoryService $inventory,
120
+ ) {}
121
+
122
+ public function __invoke(OrderRequest $request): Order { /* ... */ }
123
+ }
124
+
125
+ // ✅ Job / command — meant to be queued
126
+ final class SendWeeklyDigest implements ShouldQueue
127
+ {
128
+ public function handle(MailerService $mailer): void { /* ... */ }
129
+ }
130
+
131
+ // ✅ Form Request — Laravel pattern
132
+ final class StoreUserRequest extends FormRequest { /* ... */ }
133
+ ```
134
+
135
+ The test: **does it have state, dependencies, or a domain identity beyond "I wrap a function"?** If yes, it's a class. If no, it should be a function.
136
+
137
+ ## Detection
138
+
139
+ ```bash
140
+ # PHP — classes with exactly one public method and no constructor injection
141
+ # (rough heuristic: file has 'class X' + exactly one 'public function')
142
+ for f in $(find app/ -name '*.php'); do
143
+ PUBLIC=$(grep -cE '^\s+public function ' "$f")
144
+ CTOR=$(grep -cE '^\s+public function __construct' "$f")
145
+ PROPS=$(grep -cE '^\s+(private|protected) (readonly )?[a-zA-Z]+ \$' "$f")
146
+ if [ "$PUBLIC" = "1" ] && [ "$CTOR" = "0" ] && [ "$PROPS" = "0" ]; then
147
+ echo "SUSPECT (single-method, no state): $f"
148
+ fi
149
+ done
150
+
151
+ # TS — classes with one method and no constructor or fields
152
+ grep -rln 'class\s\+[A-Z]' --include='*.ts' src/ | while read f; do
153
+ METHODS=$(grep -cE '^\s+(public\s+|private\s+|protected\s+)?[a-zA-Z]+\s*\(' "$f")
154
+ CTOR=$(grep -c 'constructor' "$f")
155
+ if [ "$METHODS" = "1" ] && [ "$CTOR" = "0" ]; then
156
+ echo "SUSPECT: $f"
157
+ fi
158
+ done
159
+ ```
160
+
161
+ Reference: Internal: [`naming-suffix-abuse`](naming-suffix-abuse.md), [`over-eng-useless-wrapper`](over-eng-useless-wrapper.md)
@@ -0,0 +1,140 @@
1
+ ---
2
+ title: Useless Wrapper Functions
3
+ impact: HIGH
4
+ impactDescription: "Adds indirection layer for no behavioral gain; callers chase one extra hop to find real code"
5
+ tags: over-engineering, wrappers, indirection, ai-fingerprint
6
+ ---
7
+
8
+ ## Useless Wrapper Functions
9
+
10
+ **Impact: HIGH (Adds indirection layer for no behavioral gain; callers chase one extra hop to find real code)**
11
+
12
+ A function that exists only to delegate to another function, with no added validation, transformation, or context. AI generates these because it pattern-matches on "wrap external dependencies for testability" but applies it indiscriminately.
13
+
14
+ A wrapper earns its place when it adds **something** the underlying call doesn't: validation, default arguments, error normalisation, logging, retry logic, type narrowing. A wrapper that just forwards arguments is pure indirection.
15
+
16
+ ## Incorrect
17
+
18
+ ```php
19
+ // ❌ Wrappers that just delegate
20
+
21
+ class UserService
22
+ {
23
+ public function __construct(private UserRepository $repo) {}
24
+
25
+ public function findUser(int $id): ?User
26
+ {
27
+ return $this->repo->find($id);
28
+ }
29
+
30
+ public function deleteUser(int $id): void
31
+ {
32
+ $this->repo->delete($id);
33
+ }
34
+
35
+ public function createUser(array $data): User
36
+ {
37
+ return $this->repo->create($data);
38
+ }
39
+ }
40
+ ```
41
+
42
+ ```typescript
43
+ // ❌ Same TS pattern
44
+ class StripeWrapper {
45
+ private stripe: Stripe;
46
+ constructor(stripe: Stripe) {
47
+ this.stripe = stripe;
48
+ }
49
+
50
+ charge(amount: number, token: string) {
51
+ return this.stripe.charges.create({ amount, source: token });
52
+ }
53
+
54
+ refund(chargeId: string) {
55
+ return this.stripe.refunds.create({ charge: chargeId });
56
+ }
57
+ }
58
+ ```
59
+
60
+ **Why it's slop:**
61
+
62
+ - `UserService::findUser($id)` does exactly what `$repo->find($id)` does
63
+ - Every caller now goes through TWO layers (`Service → Repo`) to find the actual logic
64
+ - Adding a parameter requires touching both files
65
+ - Tests of `UserService` mostly verify "did we call the repo correctly?" — they don't verify business Behavior
66
+
67
+ ## Correct
68
+
69
+ ```php
70
+ // ✅ Drop the wrapper; use the underlying type directly
71
+
72
+ // Controller depends on the Repository or Model directly
73
+ public function show(int $id, UserRepository $users): UserResource
74
+ {
75
+ return new UserResource($users->findOrFail($id));
76
+ }
77
+ ```
78
+
79
+ ## When a wrapper IS worth keeping
80
+
81
+ A wrapper earns its place when it adds something real:
82
+
83
+ ```php
84
+ // ✅ Wrapper that normalises errors + adds retry — real value
85
+ final class ResilientStripeGateway
86
+ {
87
+ public function __construct(private Stripe $stripe) {}
88
+
89
+ public function charge(Money $amount, string $token): Charge
90
+ {
91
+ return retry(times: 3, sleepMs: 200, callback: function () use ($amount, $token) {
92
+ try {
93
+ return $this->stripe->charges->create([
94
+ 'amount' => $amount->cents(),
95
+ 'currency' => $amount->currency()->code(),
96
+ 'source' => $token,
97
+ ]);
98
+ } catch (CardException $e) {
99
+ throw new PaymentDeclined($e->getMessage(), $e); // domain exception
100
+ }
101
+ });
102
+ }
103
+ }
104
+ ```
105
+
106
+ This wrapper adds:
107
+
108
+ - Retry on transient failures
109
+ - Domain-specific exception (`PaymentDeclined`, not `CardException`)
110
+ - Type-safe `Money` input rather than raw cents + currency strings
111
+
112
+ That's worth the file.
113
+
114
+ **Why it reads human:**
115
+
116
+ - Wrappers exist when they add value; non-wrappers don't pad the layer count
117
+ - Caller can see "this charge call is resilient" because the type is `ResilientStripeGateway`
118
+ - No mystery hops to chase
119
+
120
+ ## Detection
121
+
122
+ ```bash
123
+ # Heuristic: methods that are one-liners and just call $this->dependency->method($args)
124
+ # PHP — find public methods whose body is exactly one delegate call
125
+ grep -rEn -A1 '^\s+public function [a-zA-Z]+\([^)]*\)' --include='*.php' app/ | \
126
+ awk '/public function/{name=$0; next} /^\s+return \$this->[a-zA-Z]+->[a-zA-Z]+\(/{print name; print $0; print "---"}'
127
+
128
+ # TS — same heuristic
129
+ grep -rEn -A1 '^\s+(public |async )?[a-zA-Z]+\([^)]*\):' --include='*.ts' src/ | \
130
+ awk '/[a-zA-Z]+\(/{name=$0; next} /return this\.[a-zA-Z]+\./{print name; print $0; print "---"}'
131
+ ```
132
+
133
+ The cleanest signal: **call-sites**. If a wrapper is called from exactly one place, it's almost certainly slop. Inline it.
134
+
135
+ ```bash
136
+ # For each public method in Service.php, count call sites
137
+ # (rough — use phpstorm 'Find Usages' for accurate results)
138
+ ```
139
+
140
+ Reference: [Sandi Metz — The Wrong Abstraction](https://sandimetz.com/blog/2016/1/20/the-wrong-abstraction) · [Martin Fowler — Inline Function](https://refactoring.com/catalog/inlineFunction.html) · Internal: [`over-eng-single-method-class`](over-eng-single-method-class.md), [`over-eng-premature-interface`](over-eng-premature-interface.md)
@@ -0,0 +1,127 @@
1
+ ---
2
+ title: as any / @ts-ignore Escape Hatches
3
+ impact: HIGH
4
+ impactDescription: "AI sprinkles these wherever inference is hard — defeats the type system in exactly the places it earns its value"
5
+ tags: style, typescript, type-safety, ai-fingerprint
6
+ ---
7
+
8
+ ## as any / @ts-ignore Escape Hatches
9
+
10
+ **Impact: HIGH (AI sprinkles these wherever inference is hard — defeats the type system in exactly the places it earns its value)**
11
+
12
+ AI's common move when TypeScript types don't line up cleanly: cast to `any`, add `@ts-ignore`, or annotate with `: any`. The escape hatch silences the compiler — and silences the protection it was about to provide. Worst case: a real bug (wrong shape, missing field, wrong arg order) survives review because the compiler stopped warning.
13
+
14
+ A reviewer should treat every `as any` / `@ts-ignore` / `@ts-expect-error` as **a request for justification**. Sometimes legitimate (third-party library with bad types, complex generics, runtime-validated unknowns). Often not.
15
+
16
+ ## Incorrect
17
+
18
+ ```typescript
19
+ // ❌ as any sprinkled to silence the compiler
20
+
21
+ async function processWebhook(payload: unknown): Promise<void> {
22
+ const event = (payload as any).event; // unknown → any without validation
23
+ const orderId = (payload as any).data.order.id; // chain of unsafe access
24
+ await db.orders.update({ where: { id: orderId }, data: { status: "paid" } });
25
+ }
26
+
27
+ // ❌ @ts-ignore over a real type mismatch
28
+ function calculateTotal(items: Item[]): number {
29
+ // @ts-ignore — items.map sometimes returns undefined? not sure
30
+ return items.map((i) => i.price * i.quantity).reduce((a, b) => a + b, 0);
31
+ }
32
+
33
+ // ❌ : any in function signature
34
+ function processData(data: any) {
35
+ // gives up on the input shape
36
+ return data.value;
37
+ }
38
+
39
+ // ❌ Mixing ts-ignore with no comment
40
+ function send(): void {
41
+ // @ts-ignore
42
+ emailService.send(undefined); // why is undefined OK here?
43
+ }
44
+ ```
45
+
46
+ **Why it's slop:**
47
+
48
+ - `as any` propagates — every subsequent access is also unchecked
49
+ - A real bug (e.g., the payload's actual shape is `payload.event.data.order_id`, not `payload.data.order.id`) doesn't fail until production
50
+ - `@ts-ignore` with no explanation is one of the most-cited AI tells in 2025 reviews
51
+ - `: any` parameters defeat every downstream type check
52
+
53
+ ## Correct
54
+
55
+ ```typescript
56
+ // ✅ Validate at the boundary; types flow through downstream
57
+
58
+ import { z } from "zod";
59
+
60
+ const WebhookPayload = z.object({
61
+ event: z.string(),
62
+ data: z.object({
63
+ order: z.object({ id: z.string() }),
64
+ }),
65
+ });
66
+
67
+ async function processWebhook(payload: unknown): Promise<void> {
68
+ const { event, data } = WebhookPayload.parse(payload); // typed from here on
69
+ await db.orders.update({ where: { id: data.order.id }, data: { status: "paid" } });
70
+ }
71
+
72
+ // ✅ Fix the real type issue rather than ignore it
73
+ function calculateTotal(items: Item[]): number {
74
+ return items.reduce((sum, i) => sum + i.price * i.quantity, 0);
75
+ }
76
+
77
+ // ✅ Type the input
78
+ function processData<T extends { value: string }>(data: T): string {
79
+ return data.value;
80
+ }
81
+ ```
82
+
83
+ ## When the escape hatch IS warranted
84
+
85
+ Legitimate use is narrow:
86
+
87
+ - **Bad third-party types** — the lib's `.d.ts` is wrong; you've filed an upstream issue. Use `@ts-expect-error` (not `@ts-ignore`) with a comment naming the issue:
88
+
89
+ ```typescript
90
+ // @ts-expect-error: upstream types incorrectly require `id` — see https://github.com/lib/issues/420
91
+ ```
92
+
93
+ - **Migration phase** — moving a JS file to TS gradually; mark debt and track removal
94
+ - **Complex generic constraints** where the compiler can't prove correctness but humans can — annotate with a comment explaining why it's safe
95
+
96
+ Every escape hatch in production code should have an explanatory comment. No bare `// @ts-ignore`.
97
+
98
+ ## Detection
99
+
100
+ ```bash
101
+ # Count escape hatches in the diff / repo
102
+ grep -rEn '\bas any\b' --include='*.ts' --include='*.tsx' src/ | wc -l
103
+ grep -rEn '@ts-ignore|@ts-nocheck' --include='*.ts' --include='*.tsx' src/ | wc -l
104
+ grep -rEn ': any\b' --include='*.ts' --include='*.tsx' src/ | wc -l
105
+
106
+ # Bare @ts-ignore / @ts-expect-error without an explanatory comment
107
+ grep -rEnA1 '@ts-(ignore|expect-error)\s*$' --include='*.ts' --include='*.tsx' src/
108
+ ```
109
+
110
+ Add to the Oxlint configuration (`.oxlintrc.json`):
111
+
112
+ ```json
113
+ {
114
+ "plugins": ["typescript"],
115
+ "rules": {
116
+ "typescript/no-explicit-any": "error",
117
+ "typescript/ban-ts-comment": [
118
+ "error",
119
+ { "ts-ignore": "allow-with-description", "ts-expect-error": "allow-with-description" }
120
+ ]
121
+ }
122
+ }
123
+ ```
124
+
125
+ **Bar:** zero `as any` / bare `@ts-ignore` in new code. Existing ones grandfathered but tracked.
126
+
127
+ Reference: [TypeScript handbook — strict mode](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html) · [Oxlint typescript/no-explicit-any](https://oxc.rs/docs/guide/usage/linter/rules/typescript/no-explicit-any.html) · [Oxlint typescript/ban-ts-comment](https://oxc.rs/docs/guide/usage/linter/rules/typescript/ban-ts-comment.html) · [Zod](https://zod.dev/) · Internal: [`defensive-impossible-null`](defensive-impossible-null.md)