@polymorphism-tech/morph-spec 4.22.0 → 5.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.
- package/CLAUDE.md +57 -71
- package/README.md +2 -2
- package/bin/morph-spec.js +224 -491
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +55 -38
- package/framework/MORPH.md +244 -0
- package/framework/agents.json +127 -1198
- package/framework/commands/morph-apply.md +56 -240
- package/framework/commands/morph-archive.md +48 -85
- package/framework/commands/morph-preflight.md +7 -7
- package/framework/commands/morph-proposal.md +35 -191
- package/framework/commands/morph-status.md +19 -50
- package/framework/commands/morph-troubleshoot.md +2 -2
- package/framework/evals/rubrics/README.md +44 -0
- package/framework/evals/rubrics/architecture.md +46 -0
- package/framework/evals/rubrics/code-quality.md +50 -0
- package/framework/evals/rubrics/contracts.md +47 -0
- package/framework/evals/rubrics/test-coverage.md +50 -0
- package/framework/evals/scenarios/dotnet-crud.yaml +42 -0
- package/framework/evals/scenarios/nextjs-auth.yaml +38 -0
- package/framework/evals/suites/standard.yaml +5 -0
- package/framework/evals/trace-schema.md +67 -0
- package/framework/hooks/claude-code/core/README.md +3 -0
- package/framework/hooks/claude-code/core/denylist.json +14 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/enforce-phase-writes.js +5 -5
- package/framework/hooks/claude-code/core/post-edit-typecheck.js +69 -0
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +29 -0
- package/framework/hooks/claude-code/{pre-tool-use → core}/protect-spec-files.js +4 -6
- package/framework/hooks/claude-code/{stop → core}/validate-completion.js +13 -82
- package/framework/hooks/shared/phase-utils.js +20 -31
- package/framework/hooks/shared/state-reader.js +4 -5
- package/framework/{skills/level-0-meta/mcp-registry.json → mcp/registry.json} +254 -243
- package/framework/phases.json +41 -115
- package/framework/rubrics/eval-rubric.json +66 -0
- package/framework/skills/README.md +39 -64
- package/framework/skills/core/README.md +3 -0
- package/framework/skills/core/morph-brainstorming/SKILL.md +102 -0
- package/framework/skills/core/morph-eval/SKILL.md +57 -0
- package/framework/skills/core/morph-implement/SKILL.md +83 -0
- package/framework/skills/core/morph-plan/SKILL.md +64 -0
- package/framework/skills/core/morph-review/SKILL.md +62 -0
- package/framework/skills/core/morph-stack-scan/SKILL.md +109 -0
- package/framework/skills/core/morph-standards/SKILL.md +49 -0
- package/framework/templates/CLAUDE.md.core.guide.md +29 -0
- package/framework/templates/CLAUDE.md.core.template +22 -0
- package/framework/templates/REGISTRY.json +4 -4
- package/framework/workflows/configs/fast-track.json +5 -10
- package/framework/workflows/configs/full.json +4 -8
- package/framework/workflows/configs/quick.json +32 -0
- package/framework/workflows/configs/standard.json +57 -64
- package/package.json +7 -6
- package/src/commands/mcp/mcp-setup.js +58 -31
- package/src/commands/project/doctor.js +69 -70
- package/src/commands/project/init.js +26 -5
- package/src/commands/project/status.js +18 -19
- package/src/commands/state/approve.js +1 -1
- package/src/commands/validation/validate-feature.js +18 -112
- package/src/core/paths/output-schema.js +133 -152
- package/src/core/state/phase-state-machine.js +10 -24
- package/src/core/state/state-manager.js +17 -17
- package/src/core/workflows/workflow-detector.js +214 -31
- package/src/core/workflows/workflow-router.js +204 -0
- package/src/dashboard/app/pages/AgentMap.jsx +32 -33
- package/src/dashboard/app/pages/LiveState.jsx +1 -1
- package/src/dashboard/app/pages/PhasePipeline.jsx +2 -4
- package/src/dashboard/app/pages/SkillsLibrary.jsx +2 -3
- package/src/dashboard/app/pages/Workflows.jsx +2 -2
- package/src/dashboard/scanner/agents-scanner.js +30 -35
- package/src/lib/agents/metrics-aggregator.js +1 -1
- package/src/lib/agents/output-collector.js +7 -9
- package/src/lib/context/context-builder.js +289 -0
- package/src/lib/detectors/claude-config-detector.js +19 -3
- package/src/lib/generators/context-generator.js +20 -42
- package/src/lib/generators/index.js +1 -0
- package/src/lib/generators/mandate-generator.js +512 -0
- package/src/lib/generators/metadata-extractor.js +1 -1
- package/src/lib/generators/recap-generator.js +3 -4
- package/src/lib/installers/mcp-installer.js +300 -92
- package/src/lib/phase-chain/phase-validator.js +36 -40
- package/src/lib/stack/stack-profile.js +74 -91
- package/src/lib/tasks/task-parser.js +1 -1
- package/src/lib/tasks/wave-resolver.js +353 -0
- package/src/lib/validators/checkpoint-persistence.js +2 -2
- package/src/lib/validators/content/cross-phase-validator.js +281 -0
- package/src/lib/validators/content/index.js +1 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +6 -56
- package/src/scripts/global-install.js +2 -27
- package/src/utils/agents-installer.js +224 -447
- package/src/utils/claude-settings-manager.js +57 -15
- package/src/utils/file-copier.js +1 -0
- package/src/utils/hooks-installer.js +15 -117
- package/src/utils/skills-installer.js +12 -18
- package/bin/detect-agents.js +0 -226
- package/bin/task-manager.js +0 -1036
- package/framework/agents/README.md +0 -13
- package/framework/agents/ai-agents/ai-system-architect.md +0 -194
- package/framework/agents/ai-agents/vector-search-expert.md +0 -247
- package/framework/agents/architecture/architecture-expert.md +0 -98
- package/framework/agents/architecture/morph-spec-architect.md +0 -66
- package/framework/agents/architecture/packages-validator.md +0 -89
- package/framework/agents/architecture/security-expert.md +0 -79
- package/framework/agents/architecture/seo-growth-hacker.md +0 -168
- package/framework/agents/architecture/standards-architect.md +0 -153
- package/framework/agents/backend/dotnet-senior.md +0 -163
- package/framework/agents/backend/ef-modeler.md +0 -119
- package/framework/agents/backend/hangfire-orchestrator.md +0 -133
- package/framework/agents/backend/ms-agent-expert.md +0 -55
- package/framework/agents/backend/neon-expert.md +0 -379
- package/framework/agents/frontend/css-specialist.md +0 -89
- package/framework/agents/frontend/design-system-validator.md +0 -100
- package/framework/agents/frontend/gsap-animator.md +0 -131
- package/framework/agents/frontend/nextjs-component-validator.md +0 -97
- package/framework/agents/frontend/nextjs-expert.md +0 -201
- package/framework/agents/frontend/ui-designer.md +0 -259
- package/framework/agents/infrastructure/infra-architect.md +0 -215
- package/framework/agents/integrations/asaas-financial.md +0 -138
- package/framework/agents/integrations/neon-auth.md +0 -151
- package/framework/agents/integrations/resend-email.md +0 -127
- package/framework/hooks/claude-code/post-tool-use/agent-complete.js +0 -122
- package/framework/hooks/claude-code/post-tool-use/dispatch.js +0 -363
- package/framework/hooks/claude-code/post-tool-use/handle-tool-failure.js +0 -78
- package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +0 -194
- package/framework/hooks/claude-code/pre-compact/save-morph-context.js +0 -199
- package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +0 -55
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +0 -323
- package/framework/hooks/claude-code/session-start/post-compact-restore.js +0 -41
- package/framework/hooks/claude-code/session-start/resume-context.js +0 -77
- package/framework/hooks/claude-code/teammate-idle/teammate-idle.js +0 -90
- package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +0 -114
- package/framework/hooks/shared/compact-restore.js +0 -123
- package/framework/hooks/shared/dispatch-helpers.js +0 -147
- package/framework/hooks/shared/skill-reminder-helpers.js +0 -79
- package/framework/hooks/shared/stale-task-reset.js +0 -57
- package/framework/hooks/shared/worktree-helpers.js +0 -75
- package/framework/skills/level-0-meta/README.md +0 -7
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +0 -472
- package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +0 -174
- package/framework/skills/level-0-meta/morph-brainstorming/references/proposal-example.md +0 -138
- package/framework/skills/level-0-meta/morph-checklist/SKILL.md +0 -145
- package/framework/skills/level-0-meta/morph-code-review/SKILL.md +0 -250
- package/framework/skills/level-0-meta/morph-code-review/references/review-example.md +0 -164
- package/framework/skills/level-0-meta/morph-code-review/references/review-guidelines.md +0 -100
- package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +0 -151
- package/framework/skills/level-0-meta/morph-code-review-nextjs/SKILL.md +0 -165
- package/framework/skills/level-0-meta/morph-code-review-nextjs/references/review-example-nextjs.md +0 -254
- package/framework/skills/level-0-meta/morph-code-review-nextjs/scripts/scan-nextjs.mjs +0 -189
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +0 -344
- package/framework/skills/level-0-meta/morph-frontend-review/scripts/scan-accessibility.mjs +0 -376
- package/framework/skills/level-0-meta/morph-gemini-image/SKILL.md +0 -290
- package/framework/skills/level-0-meta/morph-init/SKILL.md +0 -537
- package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +0 -225
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +0 -401
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-dev-server.mjs +0 -153
- package/framework/skills/level-0-meta/morph-post-implementation/scripts/detect-stack.mjs +0 -234
- package/framework/skills/level-0-meta/morph-simulation-checklist/SKILL.md +0 -106
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +0 -243
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +0 -169
- package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +0 -148
- package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +0 -110
- package/framework/skills/level-1-workflows/README.md +0 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +0 -269
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +0 -453
- package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +0 -44
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-authoring-guide.md +0 -55
- package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +0 -253
- package/framework/skills/level-1-workflows/morph-phase-implement/.morph/logs/activity.json +0 -38
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +0 -634
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +0 -50
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +0 -54
- package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +0 -47
- package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +0 -132
- package/framework/skills/level-1-workflows/morph-phase-implement/references/vsa-implementation-guide.md +0 -92
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +0 -257
- package/framework/skills/level-1-workflows/morph-phase-tasks/.morph/logs/activity.json +0 -14
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +0 -291
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +0 -143
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +0 -404
- package/framework/skills/level-1-workflows/morph-phase-tasks/scripts/validate-tasks.mjs +0 -112
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +0 -995
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +0 -147
- package/framework/squad-templates/backend-only.json +0 -47
- package/framework/squad-templates/frontend-only.json +0 -56
- package/framework/squad-templates/full-stack.json +0 -74
- package/src/commands/agents/agent-metrics.js +0 -174
- package/src/commands/agents/audit-dispatch.js +0 -82
- package/src/commands/agents/detect-agents.js +0 -275
- package/src/commands/agents/dispatch-agents.js +0 -461
- package/src/commands/agents/index.js +0 -13
- package/src/commands/agents/manage-agents.js +0 -352
- package/src/commands/agents/project-metrics.js +0 -74
- package/src/commands/phase/phase-reset.js +0 -74
- package/src/commands/scope/escalate.js +0 -217
- package/src/commands/state/advance-phase.js +0 -600
- package/src/commands/state/phase-runner.js +0 -215
- package/src/commands/state/state.js +0 -444
- package/src/commands/task/expand.js +0 -100
- package/src/commands/tasks/index.js +0 -4
- package/src/commands/tasks/task.js +0 -105
- package/src/commands/templates/index.js +0 -5
- package/src/commands/templates/template-render.js +0 -93
- package/src/commands/trust/trust.js +0 -118
- package/src/lib/agents/agents-manager.js +0 -349
- package/src/lib/build/build-runner.js +0 -99
- package/src/lib/checkpoints/checkpoint-hooks.js +0 -111
- package/src/lib/contracts/sprint-contract-generator.js +0 -137
- package/src/lib/scope/impact-analyzer.js +0 -106
- package/src/lib/trust/trust-manager.js +0 -157
- package/src/utils/tier-helpers.js +0 -24
- /package/framework/hooks/claude-code/{post-tool-use → core}/loop-detect.js +0 -0
|
@@ -1,250 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: morph-code-review
|
|
3
|
-
description: .NET/C# code review checklist covering naming conventions, architecture layer integrity, async patterns, logging, error handling, DI lifetimes, and DTO contracts. Use after implementing .NET code, before creating PRs, or when reviewing C# code for compliance with MORPH-SPEC standards.
|
|
4
|
-
user-invocable: true
|
|
5
|
-
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
6
|
-
stacks:
|
|
7
|
-
- dotnet
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Code Review Checklist
|
|
11
|
-
|
|
12
|
-
> Comprehensive checklist for .NET code review: naming, architecture, clean code, duplication, and runtime patterns.
|
|
13
|
-
> **Example:** `references/review-example.md` — filled-in review showing expected finding format and severity levels.
|
|
14
|
-
> **Script:** `scripts/scan-csharp.mjs` — automated scan for CRITICAL/HIGH violations before manual review.
|
|
15
|
-
> **Ref:** `references/review-guidelines.md` — false-positive rubric, confidence scoring, evidence format.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Step 0 — Eligibility Check
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
git diff --name-only main...HEAD -- "*.cs" | wc -l
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
**Skip review if:**
|
|
26
|
-
- 0 `.cs` files changed → no backend code to review
|
|
27
|
-
- Only changes in `*.json`, `*.csproj`, `*.md`, migration files → infrastructure scope
|
|
28
|
-
- `.morph/features/$ARGUMENTS/5-implement/code-review.md` already exists from today → already reviewed
|
|
29
|
-
|
|
30
|
-
If skipping: output `"Skipping code-review: [reason]"` and stop.
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Naming & Style (ref: coding.md)
|
|
35
|
-
|
|
36
|
-
- [ ] `[CRITICAL]` Constants use PascalCase (`MaxRetryCount`, NOT `MAX_RETRY_COUNT`)
|
|
37
|
-
- [ ] `[CRITICAL]` No Hungarian notation (`strName`, `iCount`, `btnSubmit`)
|
|
38
|
-
- [ ] `[HIGH]` Private fields use `_camelCase` prefix
|
|
39
|
-
- [ ] `[HIGH]` Async methods have `Async` suffix
|
|
40
|
-
- [ ] `[HIGH]` Interfaces prefixed with `I`
|
|
41
|
-
- [ ] `[MEDIUM]` No abbreviations in public APIs (`repository` not `repo`)
|
|
42
|
-
- [ ] `[MEDIUM]` All classes `sealed` unless designed for inheritance
|
|
43
|
-
- [ ] `[MEDIUM]` File-scoped namespaces used
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Architecture Review (ref: architecture.md)
|
|
48
|
-
|
|
49
|
-
### Layer Integrity
|
|
50
|
-
- [ ] `[CRITICAL]` Domain has zero references to Infrastructure or Web
|
|
51
|
-
- [ ] `[CRITICAL]` No circular dependencies between projects
|
|
52
|
-
- [ ] `[HIGH]` Application does NOT reference Web project
|
|
53
|
-
- [ ] `[HIGH]` Infrastructure details don't leak into Application DTOs
|
|
54
|
-
|
|
55
|
-
### Responsibility & Organization
|
|
56
|
-
- [ ] `[HIGH]` No class > 300 lines (SRP violation)
|
|
57
|
-
- [ ] `[HIGH]` Controllers/pages are thin (< 50 lines logic)
|
|
58
|
-
- [ ] `[MEDIUM]` One class per file, file name = class name
|
|
59
|
-
- [ ] `[MEDIUM]` Files in correct project/folder per architecture.md
|
|
60
|
-
- [ ] `[LOW]` No "God classes" (10+ constructor dependencies)
|
|
61
|
-
|
|
62
|
-
### Dependencies
|
|
63
|
-
- [ ] `[HIGH]` External services accessed through interfaces
|
|
64
|
-
- [ ] `[HIGH]` No hardcoded connection strings, URLs, or secrets
|
|
65
|
-
- [ ] `[MEDIUM]` Configuration via Options pattern (`IOptions<T>`)
|
|
66
|
-
- [ ] `[LOW]` No over-abstraction (interface with 1 impl never mocked)
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
## Clean Code Review
|
|
71
|
-
|
|
72
|
-
### Method Quality
|
|
73
|
-
- [ ] `[HIGH]` No methods > 30 lines (extract sub-methods)
|
|
74
|
-
- [ ] `[HIGH]` No methods with > 4 parameters (use request object)
|
|
75
|
-
- [ ] `[MEDIUM]` No nested conditionals > 3 levels (use early return)
|
|
76
|
-
- [ ] `[MEDIUM]` No complex conditionals (> 3 conditions — extract to named method)
|
|
77
|
-
|
|
78
|
-
### Magic Values
|
|
79
|
-
- [ ] `[HIGH]` No magic strings in comparisons (use enum or constant)
|
|
80
|
-
- [ ] `[HIGH]` No magic numbers (extract to PascalCase constant)
|
|
81
|
-
- [ ] `[MEDIUM]` No hardcoded URLs, file paths, or config values
|
|
82
|
-
|
|
83
|
-
### Dead Code
|
|
84
|
-
- [ ] `[MEDIUM]` No unused private methods
|
|
85
|
-
- [ ] `[MEDIUM]` No unused parameters
|
|
86
|
-
- [ ] `[LOW]` No commented-out code blocks (> 3 lines)
|
|
87
|
-
- [ ] `[LOW]` No empty or near-empty files
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## Duplication Review
|
|
92
|
-
|
|
93
|
-
- [ ] `[HIGH]` No exact/near-exact duplicate methods across classes
|
|
94
|
-
- [ ] `[HIGH]` No duplicate model definitions (same properties in different classes)
|
|
95
|
-
- [ ] `[MEDIUM]` No thin wrappers that only delegate without adding value
|
|
96
|
-
- [ ] `[MEDIUM]` No mirrored interface methods (extract base interface)
|
|
97
|
-
- [ ] `[MEDIUM]` No duplicate enums across projects (centralize in Domain)
|
|
98
|
-
- [ ] `[LOW]` No passthrough service methods (controller -> service with zero logic)
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
## Async & Cancellation
|
|
103
|
-
|
|
104
|
-
- [ ] `[CRITICAL]` CancellationToken propagated through entire call chain
|
|
105
|
-
- [ ] `[CRITICAL]` No `.Result` or `.Wait()` (deadlock risk in ASP.NET)
|
|
106
|
-
- [ ] `[HIGH]` No `async void` (except event handlers)
|
|
107
|
-
- [ ] `[HIGH]` Timeout configured for external operations
|
|
108
|
-
- [ ] `[MEDIUM]` `ConfigureAwait(false)` only in library code
|
|
109
|
-
|
|
110
|
-
---
|
|
111
|
-
|
|
112
|
-
## Logging
|
|
113
|
-
|
|
114
|
-
- [ ] `[HIGH]` Logs at critical points (entry, exit, errors)
|
|
115
|
-
- [ ] `[HIGH]` No `$""` string interpolation in log methods (use message templates)
|
|
116
|
-
- [ ] `[HIGH]` No sensitive data logged (passwords, tokens, PII)
|
|
117
|
-
- [ ] `[MEDIUM]` Logs include correlation IDs (OrderId, UserId)
|
|
118
|
-
- [ ] `[MEDIUM]` Appropriate log level (Information, Warning, Error)
|
|
119
|
-
|
|
120
|
-
---
|
|
121
|
-
|
|
122
|
-
## Error Handling
|
|
123
|
-
|
|
124
|
-
- [ ] `[CRITICAL]` No empty catch blocks
|
|
125
|
-
- [ ] `[HIGH]` Specific exceptions (not generic `catch (Exception)` without re-throw)
|
|
126
|
-
- [ ] `[HIGH]` Inner exception preserved on re-throw
|
|
127
|
-
- [ ] `[HIGH]` Result pattern for expected business errors, exceptions for infrastructure
|
|
128
|
-
- [ ] `[MEDIUM]` Consistent error handling pattern across all services
|
|
129
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
## DI
|
|
133
|
-
|
|
134
|
-
- [ ] `[HIGH]` Constructor injection with interfaces (not concrete types)
|
|
135
|
-
- [ ] `[HIGH]` Correct lifetime (Scoped for DbContext, Singleton for factories)
|
|
136
|
-
- [ ] `[MEDIUM]` No service locator pattern (`IServiceProvider.GetService<T>()` in business code)
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## Services
|
|
141
|
-
|
|
142
|
-
### State Validation
|
|
143
|
-
- [ ] `[HIGH]` Validates INVALID states (not valid ones) for future extensibility
|
|
144
|
-
- [ ] `[HIGH]` Error states handled explicitly
|
|
145
|
-
|
|
146
|
-
```csharp
|
|
147
|
-
// Correct: validate INVALID states
|
|
148
|
-
if (order.Status >= OrderStatus.Completed || order.Status == OrderStatus.Failed)
|
|
149
|
-
throw new InvalidOperationException("Cannot process completed or failed order");
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### Transactions
|
|
153
|
-
- [ ] `[HIGH]` Multiple operations wrapped in transaction
|
|
154
|
-
- [ ] `[HIGH]` Rollback on error
|
|
155
|
-
|
|
156
|
-
```csharp
|
|
157
|
-
await using var transaction = await _context.Database.BeginTransactionAsync(ct);
|
|
158
|
-
try
|
|
159
|
-
{
|
|
160
|
-
await _repository.AddAsync(order, ct);
|
|
161
|
-
await _paymentService.ChargeAsync(order.Id, ct);
|
|
162
|
-
await transaction.CommitAsync(ct);
|
|
163
|
-
}
|
|
164
|
-
catch { await transaction.RollbackAsync(ct); throw; }
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
---
|
|
168
|
-
|
|
169
|
-
## Background Operations
|
|
170
|
-
|
|
171
|
-
### DbContext Safety
|
|
172
|
-
- [ ] `[CRITICAL]` Uses `IDbContextFactory` (not scoped DbContext)
|
|
173
|
-
- [ ] `[CRITICAL]` Repository created via Factory with `await using`
|
|
174
|
-
- [ ] `[HIGH]` No `Task.Delay` as race condition workaround
|
|
175
|
-
|
|
176
|
-
```csharp
|
|
177
|
-
// Correct pattern
|
|
178
|
-
_ = Task.Run(async () =>
|
|
179
|
-
{
|
|
180
|
-
try
|
|
181
|
-
{
|
|
182
|
-
await using var repo = _repoFactory.CreateScoped();
|
|
183
|
-
var order = await repo.GetByIdAsync(orderId);
|
|
184
|
-
// ...
|
|
185
|
-
}
|
|
186
|
-
catch (Exception ex) { _logger.LogError(ex, "Failed {OrderId}", orderId); }
|
|
187
|
-
});
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
### Hangfire Jobs
|
|
191
|
-
- [ ] `[HIGH]` `[AutomaticRetry]` configured
|
|
192
|
-
- [ ] `[HIGH]` Job is idempotent (safe to run multiple times)
|
|
193
|
-
- [ ] `[MEDIUM]` CancellationToken used
|
|
194
|
-
- [ ] `[MEDIUM]` No HTTP request state dependency
|
|
195
|
-
|
|
196
|
-
### Resilience
|
|
197
|
-
- [ ] `[MEDIUM]` Retry policy for transient operations
|
|
198
|
-
- [ ] `[MEDIUM]` State persisted before long operation
|
|
199
|
-
- [ ] `[MEDIUM]` Idempotency guaranteed for retries
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## DTOs / Contracts
|
|
204
|
-
|
|
205
|
-
### Naming
|
|
206
|
-
- [ ] `[HIGH]` Descriptive property names (not "Data", "Value", "Info")
|
|
207
|
-
- [ ] `[HIGH]` Correct suffix: `Request` (input), `Response` (output), `Dto` (generic), `Command`/`Query` (CQRS)
|
|
208
|
-
|
|
209
|
-
### Types
|
|
210
|
-
- [ ] `[MEDIUM]` `Stream` for large files, `byte[]` for small in-memory data
|
|
211
|
-
- [ ] `[MEDIUM]` `DateTimeOffset` for timestamps, `DateTime` for local dates
|
|
212
|
-
- [ ] `[MEDIUM]` Nullable `?` only where truly optional
|
|
213
|
-
|
|
214
|
-
### Structure
|
|
215
|
-
- [ ] `[MEDIUM]` Records for immutable DTOs, classes with `init` for mutable entities
|
|
216
|
-
- [ ] `[MEDIUM]` Response DTOs have all properties needed by UI
|
|
217
|
-
- [ ] `[MEDIUM]` Enums have explicit values with logical ordering (errors at 100+)
|
|
218
|
-
|
|
219
|
-
```csharp
|
|
220
|
-
public enum OrderStatus
|
|
221
|
-
{
|
|
222
|
-
Created = 0, PendingPayment = 1, Processing = 2, Completed = 3, // Normal flow
|
|
223
|
-
Failed = 100, Cancelled = 101, Refunded = 102 // Error states
|
|
224
|
-
}
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
### Service Interfaces
|
|
228
|
-
- [ ] `[HIGH]` Async methods with `Async` suffix
|
|
229
|
-
- [ ] `[HIGH]` CancellationToken as last parameter
|
|
230
|
-
- [ ] `[MEDIUM]` `Task<Result<T>>` for expected business errors
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
## Quick Pre-Merge Checklist
|
|
235
|
-
|
|
236
|
-
```
|
|
237
|
-
[ ] Naming follows coding.md (PascalCase constants, _camelCase fields, sealed classes)
|
|
238
|
-
[ ] CancellationToken propagated on all async methods
|
|
239
|
-
[ ] Structured logging at critical points (message templates, not $"")
|
|
240
|
-
[ ] No empty catch blocks, Result pattern for business errors
|
|
241
|
-
[ ] Background ops use IDbContextFactory + await using
|
|
242
|
-
[ ] No duplicate code, no magic values, no methods > 30 lines
|
|
243
|
-
[ ] Architecture layers respected (Domain has zero external refs)
|
|
244
|
-
[ ] DTOs have descriptive names + correct types
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
---
|
|
248
|
-
|
|
249
|
-
*Consolidated from: code-review-services.md + code-review-background.md + code-review-contracts.md + architecture/clean-code/duplication analysis.*
|
|
250
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
# Code Review Report — Photo Processing Pipeline
|
|
2
|
-
|
|
3
|
-
> Example of a well-structured code review output. Filled-in reference — not a template.
|
|
4
|
-
|
|
5
|
-
**Scope:** `src/Application/PhotoProcessing/` + `src/Web/Endpoints/PhotoProcessingEndpoints.cs`
|
|
6
|
-
**Date:** 2025-01-22
|
|
7
|
-
**Reviewer:** code-review skill
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## Findings by Severity
|
|
12
|
-
|
|
13
|
-
| Severity | Count |
|
|
14
|
-
|----------|-------|
|
|
15
|
-
| CRITICAL | 1 |
|
|
16
|
-
| HIGH | 2 |
|
|
17
|
-
| MEDIUM | 2 |
|
|
18
|
-
| LOW | 1 |
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
### [CRITICAL] Naming: UPPER_SNAKE_CASE constant
|
|
23
|
-
|
|
24
|
-
**File:** `src/Application/PhotoProcessing/PhotoProcessingService.cs:14`
|
|
25
|
-
|
|
26
|
-
**Current code:**
|
|
27
|
-
```csharp
|
|
28
|
-
private const int MAX_RETRY_COUNT = 3;
|
|
29
|
-
private const int DEFAULT_TIMEOUT_SECONDS = 30;
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**Suggested fix:**
|
|
33
|
-
```csharp
|
|
34
|
-
private const int MaxRetryCount = 3;
|
|
35
|
-
private const int DefaultTimeoutSeconds = 30;
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
**Why:** `ALL_CAPS` constants violate C# conventions (§ coding.md). Pascal case is the .NET standard.
|
|
39
|
-
|
|
40
|
-
---
|
|
41
|
-
|
|
42
|
-
### [HIGH] Async: Missing CancellationToken propagation
|
|
43
|
-
|
|
44
|
-
**File:** `src/Infrastructure/Storage/AzureBlobStorageService.cs:28`
|
|
45
|
-
|
|
46
|
-
**Current code:**
|
|
47
|
-
```csharp
|
|
48
|
-
public async Task<string> UploadAsync(Stream content, string blobName)
|
|
49
|
-
{
|
|
50
|
-
var blobClient = _containerClient.GetBlobClient(blobName);
|
|
51
|
-
await blobClient.UploadAsync(content); // no CT
|
|
52
|
-
return blobClient.Uri.ToString();
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
**Suggested fix:**
|
|
57
|
-
```csharp
|
|
58
|
-
public async Task<string> UploadAsync(Stream content, string blobName, CancellationToken ct = default)
|
|
59
|
-
{
|
|
60
|
-
var blobClient = _containerClient.GetBlobClient(blobName);
|
|
61
|
-
await blobClient.UploadAsync(content, cancellationToken: ct);
|
|
62
|
-
return blobClient.Uri.ToString();
|
|
63
|
-
}
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
**Why:** Cancellation cannot propagate into the Blob SDK call, meaning a cancelled HTTP request still holds the upload open.
|
|
67
|
-
|
|
68
|
-
---
|
|
69
|
-
|
|
70
|
-
### [HIGH] Logging: String interpolation in ILogger call
|
|
71
|
-
|
|
72
|
-
**File:** `src/Application/PhotoProcessing/PhotoProcessingJob.cs:52`
|
|
73
|
-
|
|
74
|
-
**Current code:**
|
|
75
|
-
```csharp
|
|
76
|
-
_logger.LogError($"Job {jobId} failed after {retryCount} retries: {ex.Message}");
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Suggested fix:**
|
|
80
|
-
```csharp
|
|
81
|
-
_logger.LogError("Job {JobId} failed after {RetryCount} retries: {ErrorMessage}",
|
|
82
|
-
jobId, retryCount, ex.Message);
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Why:** String interpolation allocates eagerly even if the log level is filtered. Message templates are structured and queryable in Application Insights / Seq.
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
### [MEDIUM] Architecture: Controller logic exceeds 50 lines
|
|
90
|
-
|
|
91
|
-
**File:** `src/Web/Endpoints/PhotoProcessingEndpoints.cs`
|
|
92
|
-
|
|
93
|
-
**Issue:** The upload endpoint inline lambda is 60+ lines — includes validation, service call, response mapping, and error handling all inline.
|
|
94
|
-
|
|
95
|
-
**Suggested fix:** Extract a `UploadPhotoRequestHandler` or move validation to a FluentValidation `IValidator<UploadPhotoCommand>`. The endpoint lambda should be < 10 lines.
|
|
96
|
-
|
|
97
|
-
**Why:** Fat endpoints are hard to test and violate the "thin API layer" rule (§ architecture.md).
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
### [MEDIUM] Error Handling: Generic catch without context
|
|
102
|
-
|
|
103
|
-
**File:** `src/Application/PhotoProcessing/PhotoProcessingService.cs:89`
|
|
104
|
-
|
|
105
|
-
**Current code:**
|
|
106
|
-
```csharp
|
|
107
|
-
catch (Exception ex)
|
|
108
|
-
{
|
|
109
|
-
_logger.LogError(ex, "Upload failed");
|
|
110
|
-
throw;
|
|
111
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**Suggested fix:**
|
|
115
|
-
```csharp
|
|
116
|
-
catch (Exception ex)
|
|
117
|
-
{
|
|
118
|
-
_logger.LogError(ex, "Upload failed for user {UserId}", userId);
|
|
119
|
-
throw;
|
|
120
|
-
}
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
**Why:** Log entries without correlation IDs (UserId, JobId) are unqueryable in production. Always include the relevant entity ID.
|
|
124
|
-
|
|
125
|
-
---
|
|
126
|
-
|
|
127
|
-
### [LOW] Dead Code: Unused private method
|
|
128
|
-
|
|
129
|
-
**File:** `src/Application/PhotoProcessing/PhotoProcessingService.cs:110`
|
|
130
|
-
|
|
131
|
-
**Current code:**
|
|
132
|
-
```csharp
|
|
133
|
-
private string BuildBlobPath(Guid jobId, string suffix)
|
|
134
|
-
=> $"uploads/{jobId}/{suffix}";
|
|
135
|
-
// Called 0 times — path built inline everywhere
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
**Suggested fix:** Either use `BuildBlobPath` consistently or delete it.
|
|
139
|
-
|
|
140
|
-
**Why:** Dead code misleads future readers and increases maintenance surface.
|
|
141
|
-
|
|
142
|
-
---
|
|
143
|
-
|
|
144
|
-
## Top Priorities
|
|
145
|
-
|
|
146
|
-
1. `PhotoProcessingService.cs:14` — CRITICAL naming violation (5 min fix)
|
|
147
|
-
2. `AzureBlobStorageService.cs:28` — HIGH missing CancellationToken (10 min fix)
|
|
148
|
-
3. `PhotoProcessingJob.cs:52` — HIGH structured logging (5 min fix)
|
|
149
|
-
4. `PhotoProcessingEndpoints.cs` — MEDIUM endpoint refactor (30 min)
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## Passed Checks ✅
|
|
154
|
-
|
|
155
|
-
- Layer integrity: Domain has no Infrastructure references
|
|
156
|
-
- All async methods have `Async` suffix
|
|
157
|
-
- Private fields use `_camelCase`
|
|
158
|
-
- No hardcoded connection strings or secrets
|
|
159
|
-
- External services accessed through interfaces (`IBlobStorageService`, `IPhotoProcessingJob`)
|
|
160
|
-
- `ProcessingJob` entity uses private setters + domain methods
|
|
161
|
-
|
|
162
|
-
---
|
|
163
|
-
|
|
164
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
# Review Guidelines — False-Positive Rubric
|
|
2
|
-
|
|
3
|
-
> Documento de referência compartilhado por todos os review skills.
|
|
4
|
-
> Claude deve ler este documento antes de executar qualquer revisão de código.
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## Confidence Scoring
|
|
9
|
-
|
|
10
|
-
Antes de reportar um finding, atribua um confidence score de 0–100:
|
|
11
|
-
|
|
12
|
-
| Score | Interpretação | Ação |
|
|
13
|
-
|-------|--------------|------|
|
|
14
|
-
| 90–100 | Definitivamente real — reproduzível mecanicamente | Reportar |
|
|
15
|
-
| 75–89 | Provavelmente real — padrão forte de problema | Reportar com justificativa |
|
|
16
|
-
| 50–74 | Incerto — depende de contexto não visível | Só reportar se CRITICAL/HIGH |
|
|
17
|
-
| 25–49 | Provavelmente false positive | Ignorar |
|
|
18
|
-
| 0–24 | Definitivamente false positive | Nunca reportar |
|
|
19
|
-
|
|
20
|
-
**Threshold: reportar apenas findings com confidence ≥ 75.**
|
|
21
|
-
|
|
22
|
-
O confidence score não aparece no output — aplique o filtro internamente. Output limpo = só findings que passaram ≥ 75.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Categorias de False Positives (sempre ignorar)
|
|
27
|
-
|
|
28
|
-
1. **Violações pré-existentes** — código não alterado neste PR/branch. Se está no `git diff`, é relevante. Se não está, é dívida técnica para uma task de refactor separada.
|
|
29
|
-
|
|
30
|
-
2. **Linter/formatter-catchable** — naming trivial, whitespace, import order. Ferramentas automáticas cobrem isso; não gaste tempo de review manual neles.
|
|
31
|
-
|
|
32
|
-
3. **Preocupações arquiteturais especulativas** — "pode ser problema se escalar para 1M users", "esta abordagem pode causar N+1 se o dataset crescer". Só reporte se o problema for atual e concreto.
|
|
33
|
-
|
|
34
|
-
4. **Nitpicks abaixo de LOW** — preferências cosméticas sem impacto em corretude, performance ou manutenibilidade. Se não tem severidade, não reportar.
|
|
35
|
-
|
|
36
|
-
5. **Build errors** — se o código compila, não é build error. Não reporte problemas que o compilador já rejeitaria.
|
|
37
|
-
|
|
38
|
-
---
|
|
39
|
-
|
|
40
|
-
## Formato de Evidência Obrigatório
|
|
41
|
-
|
|
42
|
-
Todo finding DEVE incluir:
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
src/Application/Orders/OrderService.cs:42
|
|
46
|
-
```csharp
|
|
47
|
-
catch { } // ← snippet do código ofensivo (≤ 5 linhas)
|
|
48
|
-
```
|
|
49
|
-
Motivo: viola [EMPTY_CATCH] — exceções silenciadas impossibilitam diagnóstico.
|
|
50
|
-
Fix: adicionar logging mínimo + re-throw, ou tratar o caso explicitamente.
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Campos obrigatórios por finding:
|
|
54
|
-
- **File:line** — caminho relativo ao projeto + número da linha
|
|
55
|
-
- **Snippet** — trecho do código ofensivo, ≤ 5 linhas
|
|
56
|
-
- **Motivo** — por que viola o padrão (referência de regra ou checklist item)
|
|
57
|
-
- **Fix** — sugestão concreta e acionável
|
|
58
|
-
|
|
59
|
-
Findings sem file:line são invalidos e não devem ser reportados.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Escopo: Apenas Código Alterado
|
|
64
|
-
|
|
65
|
-
Focar exclusivamente em arquivos de:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
git diff --name-only main...HEAD
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
Issues pré-existentes = dívida técnica para tasks de refactor dedicadas, **não** para revisões de feature PR.
|
|
72
|
-
|
|
73
|
-
Se usando `scan-csharp.mjs` ou `scan-nextjs.mjs`, prefira `--diff` para limitar o escopo automaticamente.
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
## Saída Esperada
|
|
78
|
-
|
|
79
|
-
Após filtrar por confidence ≥ 75 e ignorar false positives, a saída deve ser uma das duas formas:
|
|
80
|
-
|
|
81
|
-
**Se há findings:**
|
|
82
|
-
```
|
|
83
|
-
## Code Review — [stack]
|
|
84
|
-
|
|
85
|
-
### CRITICAL
|
|
86
|
-
- `src/Foo/Bar.cs:15` — [EMPTY_CATCH] Empty catch block swallows exceptions.
|
|
87
|
-
```csharp
|
|
88
|
-
catch { }
|
|
89
|
-
```
|
|
90
|
-
Fix: log + re-throw, ou tratar o caso.
|
|
91
|
-
|
|
92
|
-
### HIGH
|
|
93
|
-
- `src/Foo/OrderService.cs:88` — [MISSING_CANCELLATION_TOKEN] Async method `ProcessOrderAsync` sem CancellationToken.
|
|
94
|
-
Fix: adicionar `CancellationToken ct = default` como último parâmetro.
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**Se sem findings CRITICAL/HIGH:**
|
|
98
|
-
```
|
|
99
|
-
✅ Sem CRITICAL/HIGH em [stack] alterado. [N arquivos revisados]
|
|
100
|
-
```
|
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* scan-csharp.mjs
|
|
4
|
-
*
|
|
5
|
-
* Scans .cs files for MORPH-SPEC coding standard violations.
|
|
6
|
-
* Executed locally by Claude via Bash — output returned to Claude.
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* node scan-csharp.mjs [path] # scan directory or file
|
|
10
|
-
* node scan-csharp.mjs src/ # scan src/ recursively
|
|
11
|
-
* node scan-csharp.mjs --summary # counts only, no details
|
|
12
|
-
* node scan-csharp.mjs --diff # scan only changed files (git diff main...HEAD)
|
|
13
|
-
* node scan-csharp.mjs --diff --base=develop # use different base branch
|
|
14
|
-
*
|
|
15
|
-
* Checks:
|
|
16
|
-
* - UPPER_SNAKE_CASE constants → should be PascalCase
|
|
17
|
-
* - Hungarian notation → strName, iCount, btnSubmit
|
|
18
|
-
* - Missing CancellationToken → async methods without CT param
|
|
19
|
-
* - String interpolation in logging → $"" in ILogger calls
|
|
20
|
-
* - Empty catch blocks → catch { } or catch (E) { }
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
import { readdirSync, readFileSync, statSync, existsSync } from 'fs';
|
|
24
|
-
import { join, extname, relative } from 'path';
|
|
25
|
-
import { execSync } from 'child_process';
|
|
26
|
-
|
|
27
|
-
const args = process.argv.slice(2);
|
|
28
|
-
const diffMode = args.includes('--diff');
|
|
29
|
-
const summaryOnly = args.includes('--summary');
|
|
30
|
-
const baseBranch = args.find(a => a.startsWith('--base='))?.split('=')[1] ?? 'main';
|
|
31
|
-
const targetPath = args.find(a => !a.startsWith('--')) ?? '.';
|
|
32
|
-
|
|
33
|
-
const CHECKS = [
|
|
34
|
-
{
|
|
35
|
-
id: 'UPPER_SNAKE_CASE',
|
|
36
|
-
severity: 'CRITICAL',
|
|
37
|
-
pattern: /\bconst\s+\w+\s+([A-Z][A-Z_]{2,})\s*=/g,
|
|
38
|
-
message: (m) => `Constant '${m[1]}' uses UPPER_SNAKE_CASE (anti-pattern in C#) → use PascalCase`,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
id: 'HUNGARIAN_NOTATION',
|
|
42
|
-
severity: 'CRITICAL',
|
|
43
|
-
pattern: /\b(str|int|bool|btn|lbl|txt|dbl|flt|obj|arr|lst)([A-Z]\w*)\b/g,
|
|
44
|
-
message: (m) => `'${m[0]}' looks like Hungarian notation → rename to '${m[2].charAt(0).toLowerCase() + m[2].slice(1)}'`,
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
id: 'MISSING_CANCELLATION_TOKEN',
|
|
48
|
-
severity: 'HIGH',
|
|
49
|
-
pattern: /public\s+async\s+Task(?:<[^>]+>)?\s+(\w+Async)\s*\(([^)]*)\)/g,
|
|
50
|
-
message: (m) => {
|
|
51
|
-
const params = m[2];
|
|
52
|
-
if (params.includes('CancellationToken') || params.includes('ct ') || params.includes('ct,') || params.includes('ct)')) return null;
|
|
53
|
-
return `Async method '${m[1]}' missing CancellationToken parameter`;
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
id: 'LOG_STRING_INTERPOLATION',
|
|
58
|
-
severity: 'HIGH',
|
|
59
|
-
pattern: /\.(LogInformation|LogWarning|LogError|LogDebug|LogCritical)\s*\(\s*\$"/g,
|
|
60
|
-
message: () => 'String interpolation ($"") in log call → use message template with structured parameters',
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
id: 'EMPTY_CATCH',
|
|
64
|
-
severity: 'CRITICAL',
|
|
65
|
-
pattern: /catch\s*(\([^)]*\))?\s*\{\s*\}/g,
|
|
66
|
-
message: () => 'Empty catch block — exceptions are silently swallowed',
|
|
67
|
-
},
|
|
68
|
-
];
|
|
69
|
-
|
|
70
|
-
function collectCsFiles(dir) {
|
|
71
|
-
if (!existsSync(dir)) return [];
|
|
72
|
-
const stat = statSync(dir);
|
|
73
|
-
if (stat.isFile()) return dir.endsWith('.cs') ? [dir] : [];
|
|
74
|
-
const results = [];
|
|
75
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
76
|
-
if (entry.isDirectory() && entry.name !== 'bin' && entry.name !== 'obj' && entry.name !== '.git') {
|
|
77
|
-
results.push(...collectCsFiles(join(dir, entry.name)));
|
|
78
|
-
} else if (entry.isFile() && extname(entry.name) === '.cs') {
|
|
79
|
-
results.push(join(dir, entry.name));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return results;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function getChangedFiles(base) {
|
|
86
|
-
try {
|
|
87
|
-
const raw = execSync(`git diff --name-only ${base}...HEAD`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
88
|
-
return raw.split('\n').map(f => f.trim()).filter(f => f.endsWith('.cs') && existsSync(f));
|
|
89
|
-
} catch {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
let mode = 'full';
|
|
95
|
-
let files;
|
|
96
|
-
if (diffMode) {
|
|
97
|
-
const changed = getChangedFiles(baseBranch);
|
|
98
|
-
if (changed !== null) {
|
|
99
|
-
files = changed;
|
|
100
|
-
mode = 'diff';
|
|
101
|
-
} else {
|
|
102
|
-
files = collectCsFiles(targetPath);
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
files = collectCsFiles(targetPath);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const findings = [];
|
|
109
|
-
|
|
110
|
-
for (const file of files) {
|
|
111
|
-
const content = readFileSync(file, 'utf-8');
|
|
112
|
-
const lines = content.split('\n');
|
|
113
|
-
|
|
114
|
-
for (const check of CHECKS) {
|
|
115
|
-
const regex = new RegExp(check.pattern.source, check.pattern.flags);
|
|
116
|
-
let match;
|
|
117
|
-
while ((match = regex.exec(content)) !== null) {
|
|
118
|
-
const msg = check.message(match);
|
|
119
|
-
if (!msg) continue;
|
|
120
|
-
const lineNum = content.slice(0, match.index).split('\n').length;
|
|
121
|
-
findings.push({
|
|
122
|
-
severity: check.severity,
|
|
123
|
-
id: check.id,
|
|
124
|
-
file: relative(process.cwd(), file).replace(/\\/g, '/'),
|
|
125
|
-
line: lineNum,
|
|
126
|
-
message: msg,
|
|
127
|
-
snippet: lines[lineNum - 1]?.trim().slice(0, 100),
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const bySeverity = { CRITICAL: 0, HIGH: 0, MEDIUM: 0, LOW: 0 };
|
|
134
|
-
for (const f of findings) bySeverity[f.severity] = (bySeverity[f.severity] ?? 0) + 1;
|
|
135
|
-
|
|
136
|
-
const summary = {
|
|
137
|
-
scanned: files.length,
|
|
138
|
-
findings: findings.length,
|
|
139
|
-
bySeverity,
|
|
140
|
-
clean: findings.length === 0,
|
|
141
|
-
mode,
|
|
142
|
-
...(mode === 'diff' && { baseBranch, changedFilesCount: files.length }),
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
if (summaryOnly) {
|
|
146
|
-
console.log(JSON.stringify(summary, null, 2));
|
|
147
|
-
} else {
|
|
148
|
-
console.log(JSON.stringify({ summary, findings }, null, 2));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
process.exit(findings.some(f => f.severity === 'CRITICAL') ? 1 : 0);
|