@dv.nghiem/flowdeck 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/bin/flowdeck.js +108 -0
- package/dist/agents/architect.d.ts +3 -0
- package/dist/agents/architect.d.ts.map +1 -0
- package/dist/agents/code-explorer.d.ts +3 -0
- package/dist/agents/code-explorer.d.ts.map +1 -0
- package/dist/agents/coder.d.ts +3 -0
- package/dist/agents/coder.d.ts.map +1 -0
- package/dist/agents/debug.d.ts +4 -0
- package/dist/agents/debug.d.ts.map +1 -0
- package/dist/agents/doc-updater.d.ts +3 -0
- package/dist/agents/doc-updater.d.ts.map +1 -0
- package/dist/agents/flowdeck.d.ts +5 -0
- package/dist/agents/flowdeck.d.ts.map +1 -0
- package/dist/agents/index.d.ts +38 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/mapper.d.ts +3 -0
- package/dist/agents/mapper.d.ts.map +1 -0
- package/dist/agents/orchestrator.d.ts +10 -0
- package/dist/agents/orchestrator.d.ts.map +1 -0
- package/dist/agents/performance.d.ts +4 -0
- package/dist/agents/performance.d.ts.map +1 -0
- package/dist/agents/planner.d.ts +3 -0
- package/dist/agents/planner.d.ts.map +1 -0
- package/dist/agents/policy-enforcer.d.ts +3 -0
- package/dist/agents/policy-enforcer.d.ts.map +1 -0
- package/dist/agents/researcher.d.ts +3 -0
- package/dist/agents/researcher.d.ts.map +1 -0
- package/dist/agents/reviewer.d.ts +3 -0
- package/dist/agents/reviewer.d.ts.map +1 -0
- package/dist/agents/risk-analyst.d.ts +3 -0
- package/dist/agents/risk-analyst.d.ts.map +1 -0
- package/dist/agents/security-auditor.d.ts +3 -0
- package/dist/agents/security-auditor.d.ts.map +1 -0
- package/dist/agents/specialist.d.ts +5 -0
- package/dist/agents/specialist.d.ts.map +1 -0
- package/dist/agents/tester.d.ts +3 -0
- package/dist/agents/tester.d.ts.map +1 -0
- package/dist/agents/types.d.ts +20 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/writer.d.ts +3 -0
- package/dist/agents/writer.d.ts.map +1 -0
- package/dist/commands/analysis/analysis.test.d.ts +2 -0
- package/dist/commands/analysis/analysis.test.d.ts.map +1 -0
- package/dist/commands/analysis/analyze-change.d.ts +148 -0
- package/dist/commands/analysis/analyze-change.d.ts.map +1 -0
- package/dist/commands/analysis/evaluate-risk.d.ts +77 -0
- package/dist/commands/analysis/evaluate-risk.d.ts.map +1 -0
- package/dist/commands/analysis/guarded-edit.d.ts +72 -0
- package/dist/commands/analysis/guarded-edit.d.ts.map +1 -0
- package/dist/commands/execution/deploy-check.d.ts +91 -0
- package/dist/commands/execution/deploy-check.d.ts.map +1 -0
- package/dist/commands/execution/fix-bug.d.ts +187 -0
- package/dist/commands/execution/fix-bug.d.ts.map +1 -0
- package/dist/commands/execution/new-feature.d.ts +171 -0
- package/dist/commands/execution/new-feature.d.ts.map +1 -0
- package/dist/commands/execution/review-code.d.ts +130 -0
- package/dist/commands/execution/review-code.d.ts.map +1 -0
- package/dist/commands/execution/write-docs.d.ts +94 -0
- package/dist/commands/execution/write-docs.d.ts.map +1 -0
- package/dist/commands/governance/approve.d.ts +80 -0
- package/dist/commands/governance/approve.d.ts.map +1 -0
- package/dist/commands/intelligence/blast-radius.d.ts +67 -0
- package/dist/commands/intelligence/blast-radius.d.ts.map +1 -0
- package/dist/commands/intelligence/impact-radar.d.ts +71 -0
- package/dist/commands/intelligence/impact-radar.d.ts.map +1 -0
- package/dist/commands/intelligence/intelligence.test.d.ts +2 -0
- package/dist/commands/intelligence/intelligence.test.d.ts.map +1 -0
- package/dist/commands/intelligence/regression-predict.d.ts +75 -0
- package/dist/commands/intelligence/regression-predict.d.ts.map +1 -0
- package/dist/commands/intelligence/review-route.d.ts +65 -0
- package/dist/commands/intelligence/review-route.d.ts.map +1 -0
- package/dist/commands/intelligence/test-gap.d.ts +73 -0
- package/dist/commands/intelligence/test-gap.d.ts.map +1 -0
- package/dist/commands/intelligence/translate-intent.d.ts +87 -0
- package/dist/commands/intelligence/translate-intent.d.ts.map +1 -0
- package/dist/commands/intelligence/volatility-map-cmd.d.ts +68 -0
- package/dist/commands/intelligence/volatility-map-cmd.d.ts.map +1 -0
- package/dist/commands/planning/ask.d.ts +62 -0
- package/dist/commands/planning/ask.d.ts.map +1 -0
- package/dist/commands/planning/ask.test.d.ts +2 -0
- package/dist/commands/planning/ask.test.d.ts.map +1 -0
- package/dist/commands/planning/dashboard.d.ts +30 -0
- package/dist/commands/planning/dashboard.d.ts.map +1 -0
- package/dist/commands/planning/discuss.d.ts +39 -0
- package/dist/commands/planning/discuss.d.ts.map +1 -0
- package/dist/commands/planning/plan.d.ts +67 -0
- package/dist/commands/planning/plan.d.ts.map +1 -0
- package/dist/commands/planning/roadmap.d.ts +105 -0
- package/dist/commands/planning/roadmap.d.ts.map +1 -0
- package/dist/commands/setup/doctor.d.ts +10 -0
- package/dist/commands/setup/doctor.d.ts.map +1 -0
- package/dist/commands/setup/map-codebase.d.ts +62 -0
- package/dist/commands/setup/map-codebase.d.ts.map +1 -0
- package/dist/commands/setup/new-project.d.ts +19 -0
- package/dist/commands/setup/new-project.d.ts.map +1 -0
- package/dist/commands/setup/settings.d.ts +57 -0
- package/dist/commands/setup/settings.d.ts.map +1 -0
- package/dist/commands/state/checkpoint.d.ts +27 -0
- package/dist/commands/state/checkpoint.d.ts.map +1 -0
- package/dist/commands/state/multi-repo.d.ts +63 -0
- package/dist/commands/state/multi-repo.d.ts.map +1 -0
- package/dist/commands/state/progress.d.ts +57 -0
- package/dist/commands/state/progress.d.ts.map +1 -0
- package/dist/commands/state/resume.d.ts +11 -0
- package/dist/commands/state/resume.d.ts.map +1 -0
- package/dist/commands/state/workspace-commands.d.ts +207 -0
- package/dist/commands/state/workspace-commands.d.ts.map +1 -0
- package/dist/dashboard/lib/port-finder.d.ts +10 -0
- package/dist/dashboard/lib/port-finder.d.ts.map +1 -0
- package/dist/dashboard/lib/port-finder.test.d.ts +2 -0
- package/dist/dashboard/lib/port-finder.test.d.ts.map +1 -0
- package/dist/dashboard/lib/state-reader.d.ts +3 -0
- package/dist/dashboard/lib/state-reader.d.ts.map +1 -0
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.d.ts.map +1 -0
- package/dist/dashboard/server.mjs +13649 -0
- package/dist/dashboard/types.d.ts +72 -0
- package/dist/dashboard/types.d.ts.map +1 -0
- package/dist/dashboard/views/index.ejs +391 -0
- package/dist/dashboard/views/partials/blockers.ejs +10 -0
- package/dist/dashboard/views/partials/header.ejs +20 -0
- package/dist/dashboard/views/partials/phase-timeline.ejs +40 -0
- package/dist/dashboard/views/partials/progress.ejs +12 -0
- package/dist/hooks/approval-hook.d.ts +13 -0
- package/dist/hooks/approval-hook.d.ts.map +1 -0
- package/dist/hooks/compaction-hook.d.ts +23 -0
- package/dist/hooks/compaction-hook.d.ts.map +1 -0
- package/dist/hooks/context-window-monitor.d.ts +21 -0
- package/dist/hooks/context-window-monitor.d.ts.map +1 -0
- package/dist/hooks/decision-trace-hook.d.ts +13 -0
- package/dist/hooks/decision-trace-hook.d.ts.map +1 -0
- package/dist/hooks/file-tracker.d.ts +29 -0
- package/dist/hooks/file-tracker.d.ts.map +1 -0
- package/dist/hooks/guard-rails.d.ts +33 -0
- package/dist/hooks/guard-rails.d.ts.map +1 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/notifications.d.ts +21 -0
- package/dist/hooks/notifications.d.ts.map +1 -0
- package/dist/hooks/patch-trust.d.ts +27 -0
- package/dist/hooks/patch-trust.d.ts.map +1 -0
- package/dist/hooks/patch-trust.test.d.ts +2 -0
- package/dist/hooks/patch-trust.test.d.ts.map +1 -0
- package/dist/hooks/session-events.d.ts +8 -0
- package/dist/hooks/session-events.d.ts.map +1 -0
- package/dist/hooks/session-idle-hook.d.ts +21 -0
- package/dist/hooks/session-idle-hook.d.ts.map +1 -0
- package/dist/hooks/session-start.d.ts +10 -0
- package/dist/hooks/session-start.d.ts.map +1 -0
- package/dist/hooks/shell-env-hook.d.ts +21 -0
- package/dist/hooks/shell-env-hook.d.ts.map +1 -0
- package/dist/hooks/telemetry-hook.d.ts +25 -0
- package/dist/hooks/telemetry-hook.d.ts.map +1 -0
- package/dist/hooks/todo-hook.d.ts +25 -0
- package/dist/hooks/todo-hook.d.ts.map +1 -0
- package/dist/hooks/tool-guard.d.ts +41 -0
- package/dist/hooks/tool-guard.d.ts.map +1 -0
- package/dist/hooks/tool-guard.test.d.ts +2 -0
- package/dist/hooks/tool-guard.test.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6639 -0
- package/dist/lib/confirmation.d.ts +20 -0
- package/dist/lib/confirmation.d.ts.map +1 -0
- package/dist/lib/impact-radar.d.ts +35 -0
- package/dist/lib/impact-radar.d.ts.map +1 -0
- package/dist/lib/signatures.d.ts +12 -0
- package/dist/lib/signatures.d.ts.map +1 -0
- package/dist/lib/timestamps.d.ts +23 -0
- package/dist/lib/timestamps.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +20 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/services/agent-performance.d.ts +29 -0
- package/dist/services/agent-performance.d.ts.map +1 -0
- package/dist/services/approval-manager.d.ts +30 -0
- package/dist/services/approval-manager.d.ts.map +1 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/model-router.d.ts +35 -0
- package/dist/services/model-router.d.ts.map +1 -0
- package/dist/services/policy-compiler.d.ts +27 -0
- package/dist/services/policy-compiler.d.ts.map +1 -0
- package/dist/services/run-trace.d.ts +34 -0
- package/dist/services/run-trace.d.ts.map +1 -0
- package/dist/services/services.test.d.ts +2 -0
- package/dist/services/services.test.d.ts.map +1 -0
- package/dist/services/telemetry.d.ts +34 -0
- package/dist/services/telemetry.d.ts.map +1 -0
- package/dist/tools/agent-dispatch.test.d.ts +2 -0
- package/dist/tools/agent-dispatch.test.d.ts.map +1 -0
- package/dist/tools/codebase-state.d.ts +4 -0
- package/dist/tools/codebase-state.d.ts.map +1 -0
- package/dist/tools/context-generator.d.ts +3 -0
- package/dist/tools/context-generator.d.ts.map +1 -0
- package/dist/tools/council.d.ts +4 -0
- package/dist/tools/council.d.ts.map +1 -0
- package/dist/tools/decision-trace.d.ts +16 -0
- package/dist/tools/decision-trace.d.ts.map +1 -0
- package/dist/tools/delegate.d.ts +4 -0
- package/dist/tools/delegate.d.ts.map +1 -0
- package/dist/tools/failure-replay.d.ts +19 -0
- package/dist/tools/failure-replay.d.ts.map +1 -0
- package/dist/tools/failure-replay.test.d.ts +2 -0
- package/dist/tools/failure-replay.test.d.ts.map +1 -0
- package/dist/tools/hash-edit.d.ts +3 -0
- package/dist/tools/hash-edit.d.ts.map +1 -0
- package/dist/tools/planning-state-lib.d.ts +65 -0
- package/dist/tools/planning-state-lib.d.ts.map +1 -0
- package/dist/tools/planning-state.d.ts +3 -0
- package/dist/tools/planning-state.d.ts.map +1 -0
- package/dist/tools/policy-engine.d.ts +19 -0
- package/dist/tools/policy-engine.d.ts.map +1 -0
- package/dist/tools/repo-memory.d.ts +20 -0
- package/dist/tools/repo-memory.d.ts.map +1 -0
- package/dist/tools/repo-memory.test.d.ts +2 -0
- package/dist/tools/repo-memory.test.d.ts.map +1 -0
- package/dist/tools/run-parallel.d.ts +4 -0
- package/dist/tools/run-parallel.d.ts.map +1 -0
- package/dist/tools/run-pipeline.d.ts +4 -0
- package/dist/tools/run-pipeline.d.ts.map +1 -0
- package/dist/tools/volatility-map.d.ts +18 -0
- package/dist/tools/volatility-map.d.ts.map +1 -0
- package/dist/tools/volatility-map.test.d.ts +2 -0
- package/dist/tools/volatility-map.test.d.ts.map +1 -0
- package/dist/tools/workspace-state.d.ts +3 -0
- package/dist/tools/workspace-state.d.ts.map +1 -0
- package/docs/USER_GUIDE.md +20 -0
- package/docs/agents.md +562 -0
- package/docs/best-practices.md +47 -0
- package/docs/command-migration.md +175 -0
- package/docs/commands/fd-analyze-change.md +107 -0
- package/docs/commands/fd-ask.md +51 -0
- package/docs/commands/fd-checkpoint.md +10 -0
- package/docs/commands/fd-dashboard.md +11 -0
- package/docs/commands/fd-deploy-check.md +11 -0
- package/docs/commands/fd-discuss.md +28 -0
- package/docs/commands/fd-evaluate-risk.md +134 -0
- package/docs/commands/fd-fix-bug.md +24 -0
- package/docs/commands/fd-guarded-edit.md +105 -0
- package/docs/commands/fd-map-codebase.md +27 -0
- package/docs/commands/fd-multi-repo.md +63 -0
- package/docs/commands/fd-new-feature.md +25 -0
- package/docs/commands/fd-new-project.md +24 -0
- package/docs/commands/fd-plan.md +33 -0
- package/docs/commands/fd-progress.md +11 -0
- package/docs/commands/fd-resume.md +10 -0
- package/docs/commands/fd-review-code.md +29 -0
- package/docs/commands/fd-roadmap.md +10 -0
- package/docs/commands/fd-settings.md +10 -0
- package/docs/commands/fd-write-docs.md +10 -0
- package/docs/commands.md +476 -0
- package/docs/configuration.md +211 -0
- package/docs/feature-integration-architecture.md +255 -0
- package/docs/index.md +75 -0
- package/docs/installation.md +134 -0
- package/docs/intelligence.md +294 -0
- package/docs/multi-repo.md +201 -0
- package/docs/notifications.md +170 -0
- package/docs/parallel-execution.md +227 -0
- package/docs/quick-start.md +174 -0
- package/docs/rules.md +459 -0
- package/docs/skills.md +408 -0
- package/docs/workflows.md +376 -0
- package/package.json +58 -0
- package/postinstall.mjs +102 -0
- package/src/rules/README.md +37 -0
- package/src/rules/common/agent-orchestration.md +86 -0
- package/src/rules/common/coding-style.md +120 -0
- package/src/rules/common/git-workflow.md +77 -0
- package/src/rules/common/security.md +94 -0
- package/src/rules/common/testing.md +105 -0
- package/src/rules/golang/patterns.md +187 -0
- package/src/rules/java/patterns.md +204 -0
- package/src/rules/python/patterns.md +141 -0
- package/src/rules/rust/patterns.md +210 -0
- package/src/rules/typescript/patterns.md +168 -0
- package/src/skills/api-design/SKILL.md +143 -0
- package/src/skills/arch-constraint-guard/SKILL.md +61 -0
- package/src/skills/blast-radius-preview/SKILL.md +65 -0
- package/src/skills/change-impact-radar/SKILL.md +63 -0
- package/src/skills/code-review/SKILL.md +108 -0
- package/src/skills/code-tour/SKILL.md +101 -0
- package/src/skills/codebase-mapping/SKILL.md +87 -0
- package/src/skills/codebase-onboarding/SKILL.md +133 -0
- package/src/skills/confidence-aware-planning/SKILL.md +67 -0
- package/src/skills/context-load/SKILL.md +63 -0
- package/src/skills/debug-flow/SKILL.md +75 -0
- package/src/skills/decision-trace/SKILL.md +72 -0
- package/src/skills/dependency-audit/SKILL.md +126 -0
- package/src/skills/deploy-check/SKILL.md +87 -0
- package/src/skills/documentation-writer/SKILL.md +154 -0
- package/src/skills/failure-replay-engine/SKILL.md +59 -0
- package/src/skills/git-release/SKILL.md +94 -0
- package/src/skills/git-workflow/SKILL.md +177 -0
- package/src/skills/golang-patterns/SKILL.md +511 -0
- package/src/skills/human-review-routing/SKILL.md +65 -0
- package/src/skills/intent-translator/SKILL.md +57 -0
- package/src/skills/java-patterns/SKILL.md +479 -0
- package/src/skills/multi-repo/SKILL.md +187 -0
- package/src/skills/parallel-execute/SKILL.md +92 -0
- package/src/skills/patch-trust-score/SKILL.md +44 -0
- package/src/skills/performance-profiling/SKILL.md +153 -0
- package/src/skills/plan-task/SKILL.md +101 -0
- package/src/skills/python-patterns/SKILL.md +529 -0
- package/src/skills/refactor-guide/SKILL.md +117 -0
- package/src/skills/regression-prediction/SKILL.md +57 -0
- package/src/skills/repo-memory-graph/SKILL.md +49 -0
- package/src/skills/rust-patterns/SKILL.md +492 -0
- package/src/skills/security-scan/SKILL.md +91 -0
- package/src/skills/self-healing-policies/SKILL.md +76 -0
- package/src/skills/tdd-workflow/SKILL.md +126 -0
- package/src/skills/test-coverage/SKILL.md +94 -0
- package/src/skills/test-gap-detector/SKILL.md +58 -0
- package/src/skills/volatility-map/SKILL.md +52 -0
- package/src/workflows/debug-flow.md +119 -0
- package/src/workflows/deploy-check-flow.md +98 -0
- package/src/workflows/discuss-flow.md +97 -0
- package/src/workflows/execute-flow.md +233 -0
- package/src/workflows/execute-phase.md +142 -0
- package/src/workflows/fix-bug-flow.md +210 -0
- package/src/workflows/map-codebase-flow.md +92 -0
- package/src/workflows/multi-repo-flow.md +226 -0
- package/src/workflows/parallel-execution-flow.md +236 -0
- package/src/workflows/plan-flow.md +126 -0
- package/src/workflows/plan-phase.md +101 -0
- package/src/workflows/refactor-flow.md +122 -0
- package/src/workflows/review-code-flow.md +105 -0
- package/src/workflows/spec-driven-flow.md +43 -0
- package/src/workflows/write-docs-flow.md +95 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Java Patterns
|
|
2
|
+
|
|
3
|
+
Java conventions for FlowDeck projects. Targets Java 17+.
|
|
4
|
+
|
|
5
|
+
## Constructor Injection Only
|
|
6
|
+
|
|
7
|
+
Use constructor injection for all Spring-managed beans. Never use field injection.
|
|
8
|
+
|
|
9
|
+
```java
|
|
10
|
+
// ❌ Field injection — hides dependencies, cannot be tested without Spring
|
|
11
|
+
@Service
|
|
12
|
+
public class ReportService {
|
|
13
|
+
@Autowired private UserRepository users;
|
|
14
|
+
@Autowired private EmailService email;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ✅ Constructor injection — explicit, testable, fields can be final
|
|
18
|
+
@Service
|
|
19
|
+
public class ReportService {
|
|
20
|
+
private final UserRepository users;
|
|
21
|
+
private final EmailService email;
|
|
22
|
+
|
|
23
|
+
public ReportService(UserRepository users, EmailService email) {
|
|
24
|
+
this.users = users;
|
|
25
|
+
this.email = email;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Never Return null from Public Methods
|
|
31
|
+
|
|
32
|
+
Public methods must not return `null`. Use `Optional<T>` for absence, throw for impossible states.
|
|
33
|
+
|
|
34
|
+
```java
|
|
35
|
+
// ❌ null return — callers can't tell if null is expected or a bug
|
|
36
|
+
public User findUser(long id) {
|
|
37
|
+
return repository.findById(id); // returns null when not found
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ✅ Optional signals intentional absence
|
|
41
|
+
public Optional<User> findUser(long id) {
|
|
42
|
+
return repository.findById(id);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ✅ Throw when absence is a contract violation
|
|
46
|
+
public User getUser(long id) {
|
|
47
|
+
return repository.findById(id)
|
|
48
|
+
.orElseThrow(() -> new UserNotFoundException(id));
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## All DTOs Should Be Records
|
|
53
|
+
|
|
54
|
+
Use Java records (Java 16+) for data transfer objects, API request/response bodies, and value objects.
|
|
55
|
+
|
|
56
|
+
```java
|
|
57
|
+
// ❌ Mutable class with boilerplate
|
|
58
|
+
public class CreateUserRequest {
|
|
59
|
+
private String email;
|
|
60
|
+
private String name;
|
|
61
|
+
// getters, setters, equals, hashCode, toString...
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ✅ Record — immutable, compact, compiler-generated methods
|
|
65
|
+
public record CreateUserRequest(String email, String name) {}
|
|
66
|
+
|
|
67
|
+
// Records can include validation
|
|
68
|
+
public record PageRequest(int page, int size) {
|
|
69
|
+
public PageRequest {
|
|
70
|
+
if (page < 0) throw new IllegalArgumentException("page must be >= 0");
|
|
71
|
+
if (size < 1 || size > 100) throw new IllegalArgumentException("size must be 1–100");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Use var for Obvious Local Variable Types
|
|
77
|
+
|
|
78
|
+
Use `var` when the right-hand side makes the type immediately apparent. Do not use `var` when it obscures the type.
|
|
79
|
+
|
|
80
|
+
```java
|
|
81
|
+
// ✅ Type is obvious from the right-hand side
|
|
82
|
+
var users = new ArrayList<User>();
|
|
83
|
+
var config = PaymentConfig.load();
|
|
84
|
+
var client = new HttpClient();
|
|
85
|
+
|
|
86
|
+
// ❌ Type is not obvious — write it out
|
|
87
|
+
var result = process(data); // what type is result?
|
|
88
|
+
var x = getValue(); // unclear
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Avoid Checked Exceptions in New Code
|
|
92
|
+
|
|
93
|
+
New code must not declare checked exceptions. Wrap checked exceptions from third-party libraries in unchecked exceptions and preserve the cause.
|
|
94
|
+
|
|
95
|
+
```java
|
|
96
|
+
// ❌ Checked exception propagates through every caller
|
|
97
|
+
public List<User> loadUsers() throws IOException { ... }
|
|
98
|
+
|
|
99
|
+
// ✅ Unchecked exception at the boundary
|
|
100
|
+
public List<User> loadUsers() {
|
|
101
|
+
try {
|
|
102
|
+
return parser.parse(Files.readString(configPath));
|
|
103
|
+
} catch (IOException e) {
|
|
104
|
+
throw new UserLoadException("failed to read users from " + configPath, e);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## All Database Queries Must Have Explicit Transaction Boundaries
|
|
110
|
+
|
|
111
|
+
Annotate the service method with `@Transactional`, not the repository method. The service defines the unit of work.
|
|
112
|
+
|
|
113
|
+
```java
|
|
114
|
+
// ❌ Transaction on repository — too granular, two separate transactions
|
|
115
|
+
public void transfer(long fromId, long toId, BigDecimal amount) {
|
|
116
|
+
accountRepo.debit(fromId, amount); // tx 1
|
|
117
|
+
accountRepo.credit(toId, amount); // tx 2 — debit committed even if credit fails
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ✅ Transaction on service method — single atomic unit
|
|
121
|
+
@Transactional
|
|
122
|
+
public void transfer(long fromId, long toId, BigDecimal amount) {
|
|
123
|
+
accountRepo.debit(fromId, amount);
|
|
124
|
+
accountRepo.credit(toId, amount); // rolls back both on failure
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Never Use String.format() in Hot Paths
|
|
129
|
+
|
|
130
|
+
Avoid `String.format()` in loops or frequently-called methods. Use `StringBuilder` or text blocks.
|
|
131
|
+
|
|
132
|
+
```java
|
|
133
|
+
// ❌ String.format in a loop — allocates a new formatter each iteration
|
|
134
|
+
for (var item : items) {
|
|
135
|
+
log.debug(String.format("Processing item id=%d name=%s", item.id(), item.name()));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ✅ SLF4J lazy substitution — string only built if DEBUG is enabled
|
|
139
|
+
for (var item : items) {
|
|
140
|
+
log.debug("Processing item id={} name={}", item.id(), item.name());
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ✅ StringBuilder for building large strings
|
|
144
|
+
var sb = new StringBuilder();
|
|
145
|
+
for (var item : items) {
|
|
146
|
+
sb.append(item.name()).append(", ");
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Testing Strategy
|
|
151
|
+
|
|
152
|
+
- **Unit tests**: pure JUnit 5 + Mockito, annotated with `@ExtendWith(MockitoExtension.class)`. No Spring context.
|
|
153
|
+
- **Web slice tests**: `@WebMvcTest` for controller layer only.
|
|
154
|
+
- **Integration tests**: `@SpringBootTest` for end-to-end paths. Use `@Transactional` to roll back data after each test.
|
|
155
|
+
- Do not use `@SpringBootTest` for pure service or repository unit tests — it loads the full context unnecessarily.
|
|
156
|
+
|
|
157
|
+
```java
|
|
158
|
+
// Unit test — no Spring
|
|
159
|
+
@ExtendWith(MockitoExtension.class)
|
|
160
|
+
class OrderServiceTest {
|
|
161
|
+
@Mock OrderRepository orders;
|
|
162
|
+
@InjectMocks OrderService service;
|
|
163
|
+
...
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Integration test
|
|
167
|
+
@SpringBootTest
|
|
168
|
+
@Transactional
|
|
169
|
+
class OrderFlowIntegrationTest { ... }
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Lombok Usage
|
|
173
|
+
|
|
174
|
+
Lombok is permitted only for the following annotations:
|
|
175
|
+
|
|
176
|
+
- `@Builder` — for complex object construction
|
|
177
|
+
- `@Value` — for immutable classes (prefer records for new code)
|
|
178
|
+
- `@Slf4j` — for logger injection
|
|
179
|
+
|
|
180
|
+
Do **not** use `@Data` on JPA entities. It generates `equals`/`hashCode` based on all fields, which causes problems with proxies and lazy loading. Implement them manually using only the primary key.
|
|
181
|
+
|
|
182
|
+
```java
|
|
183
|
+
// ❌ @Data on entity
|
|
184
|
+
@Data
|
|
185
|
+
@Entity
|
|
186
|
+
public class Order { ... }
|
|
187
|
+
|
|
188
|
+
// ✅ @Slf4j and manual equals/hashCode on entity
|
|
189
|
+
@Slf4j
|
|
190
|
+
@Entity
|
|
191
|
+
public class Order {
|
|
192
|
+
@Id private Long id;
|
|
193
|
+
|
|
194
|
+
@Override
|
|
195
|
+
public boolean equals(Object o) {
|
|
196
|
+
if (this == o) return true;
|
|
197
|
+
if (!(o instanceof Order other)) return false;
|
|
198
|
+
return id != null && id.equals(other.id);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
@Override
|
|
202
|
+
public int hashCode() { return getClass().hashCode(); }
|
|
203
|
+
}
|
|
204
|
+
```
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Python Patterns
|
|
2
|
+
|
|
3
|
+
Python conventions for FlowDeck projects.
|
|
4
|
+
|
|
5
|
+
## Module Organization
|
|
6
|
+
|
|
7
|
+
- Single `.py` file for small, cohesive utilities.
|
|
8
|
+
- Convert to a package (`dir/__init__.py`) when the file grows past ~300 lines or needs sub-modules.
|
|
9
|
+
- Keep `__init__.py` thin — expose only the public API; don't execute side effects.
|
|
10
|
+
- Declare `__all__` in every module that has a meaningful public surface.
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
# mypackage/__init__.py
|
|
14
|
+
from .core import Engine
|
|
15
|
+
from .config import Config
|
|
16
|
+
|
|
17
|
+
__all__ = ["Engine", "Config"]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Naming Conventions
|
|
21
|
+
|
|
22
|
+
| Construct | Style | Example |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| Variable / function / method | `snake_case` | `get_user`, `is_active` |
|
|
25
|
+
| Class | `PascalCase` | `UserService`, `HttpClient` |
|
|
26
|
+
| Module / package | `snake_case` | `user_service.py` |
|
|
27
|
+
| Constant (module-level) | `SCREAMING_SNAKE_CASE` | `MAX_RETRIES = 3` |
|
|
28
|
+
| "Private" member | `_leading_underscore` | `_internal_cache` |
|
|
29
|
+
| Name-mangled member | `__double_leading` | `__secret` (avoid in most cases) |
|
|
30
|
+
|
|
31
|
+
## Never Use Mutable Default Arguments
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
# ❌ The list is shared across all calls
|
|
35
|
+
def add_item(item, lst=[]):
|
|
36
|
+
lst.append(item)
|
|
37
|
+
return lst
|
|
38
|
+
|
|
39
|
+
# ✅ Use None and guard inside the function
|
|
40
|
+
def add_item(item, lst=None):
|
|
41
|
+
if lst is None:
|
|
42
|
+
lst = []
|
|
43
|
+
lst.append(item)
|
|
44
|
+
return lst
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Always Use f-strings
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
# ❌ %-formatting — old, hard to read
|
|
51
|
+
msg = "Hello, %s! You have %d messages." % (name, count)
|
|
52
|
+
|
|
53
|
+
# ❌ .format() — verbose
|
|
54
|
+
msg = "Hello, {}! You have {} messages.".format(name, count)
|
|
55
|
+
|
|
56
|
+
# ✅ f-string — concise, readable, evaluated at runtime
|
|
57
|
+
msg = f"Hello, {name}! You have {count} messages."
|
|
58
|
+
|
|
59
|
+
# f-strings support expressions and format specs
|
|
60
|
+
price = f"${amount:.2f}"
|
|
61
|
+
debug = f"{obj!r}"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Use pathlib.Path, Not os.path
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
# ❌ os.path — verbose and easy to misuse
|
|
68
|
+
import os
|
|
69
|
+
full_path = os.path.join(base_dir, "data", "users.csv")
|
|
70
|
+
if os.path.exists(full_path):
|
|
71
|
+
with open(full_path) as f: ...
|
|
72
|
+
|
|
73
|
+
# ✅ pathlib.Path — object-oriented, composable
|
|
74
|
+
from pathlib import Path
|
|
75
|
+
full_path = Path(base_dir) / "data" / "users.csv"
|
|
76
|
+
if full_path.exists():
|
|
77
|
+
content = full_path.read_text()
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Prefer Explicit Over Implicit
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
# ❌ Wildcard import — pollutes namespace, hides dependencies
|
|
84
|
+
from mymodule import *
|
|
85
|
+
|
|
86
|
+
# ✅ Explicit import — clear origin of each name
|
|
87
|
+
from mymodule import SpecificClass, helper_function
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Type Annotations on All Public Functions
|
|
91
|
+
|
|
92
|
+
Every public function and method must have type annotations on all parameters and the return type.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
# ❌ No annotations
|
|
96
|
+
def find_users(active, limit):
|
|
97
|
+
...
|
|
98
|
+
|
|
99
|
+
# ✅ Fully annotated
|
|
100
|
+
def find_users(active: bool, limit: int = 100) -> list[User]:
|
|
101
|
+
...
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Private/internal functions (prefixed with `_`) should be annotated where the types are non-obvious.
|
|
105
|
+
|
|
106
|
+
## Testing with pytest
|
|
107
|
+
|
|
108
|
+
- All tests use `pytest`. Do not use `unittest` for new code (legacy code may remain).
|
|
109
|
+
- Test files: `tests/test_<module>.py` or co-located `<module>_test.py`.
|
|
110
|
+
- Use fixtures for setup/teardown — not `setUp`/`tearDown` class methods.
|
|
111
|
+
- Use `@pytest.mark.parametrize` for functions that need more than two input cases.
|
|
112
|
+
- Tests must be deterministic — no time-dependent, order-dependent, or network-dependent tests without explicit mocking.
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
# ✅ parametrize instead of copy-pasted test functions
|
|
116
|
+
@pytest.mark.parametrize("value,expected", [
|
|
117
|
+
(0, False),
|
|
118
|
+
(1, True),
|
|
119
|
+
(-1, False),
|
|
120
|
+
])
|
|
121
|
+
def test_is_positive(value: int, expected: bool) -> None:
|
|
122
|
+
assert is_positive(value) == expected
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Virtual Environments — Never Global pip Install
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# ✅ Create a venv and install there
|
|
129
|
+
python -m venv .venv
|
|
130
|
+
source .venv/bin/activate
|
|
131
|
+
pip install -e ".[dev]"
|
|
132
|
+
|
|
133
|
+
# ✅ uv (preferred — much faster)
|
|
134
|
+
uv venv && source .venv/bin/activate
|
|
135
|
+
uv pip install -e ".[dev]"
|
|
136
|
+
|
|
137
|
+
# ❌ Never install packages globally for a project
|
|
138
|
+
pip install requests # installs into system Python
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
All project dependencies must live in `pyproject.toml`. Development tools (pytest, mypy, ruff) go in `[project.optional-dependencies]` under `dev`.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Rust Patterns
|
|
2
|
+
|
|
3
|
+
Rust conventions for FlowDeck projects.
|
|
4
|
+
|
|
5
|
+
## Prefer Result<T, E> Over Panicking in Library Code
|
|
6
|
+
|
|
7
|
+
Library code must not panic on expected failure conditions. Return `Result` and let the caller decide how to handle the error.
|
|
8
|
+
|
|
9
|
+
```rust
|
|
10
|
+
// ❌ Panics on invalid input — caller has no recovery path
|
|
11
|
+
pub fn parse_port(s: &str) -> u16 {
|
|
12
|
+
s.parse().unwrap()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// ✅ Return Result — caller decides what to do
|
|
16
|
+
pub fn parse_port(s: &str) -> Result<u16, std::num::ParseIntError> {
|
|
17
|
+
s.parse()
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Application code (binaries, CLI entrypoints) may use `?` propagation up to `main`, which prints the error and exits.
|
|
22
|
+
|
|
23
|
+
## Use thiserror for Library Errors, anyhow for Application Errors
|
|
24
|
+
|
|
25
|
+
```rust
|
|
26
|
+
// Library crate: thiserror — structured, matchable error variants
|
|
27
|
+
use thiserror::Error;
|
|
28
|
+
|
|
29
|
+
#[derive(Debug, Error)]
|
|
30
|
+
pub enum CacheError {
|
|
31
|
+
#[error("key {0:?} not found")]
|
|
32
|
+
NotFound(String),
|
|
33
|
+
#[error("serialization failed: {0}")]
|
|
34
|
+
Serialize(#[from] serde_json::Error),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Application binary: anyhow — ergonomic propagation with context
|
|
38
|
+
use anyhow::{Context, Result};
|
|
39
|
+
|
|
40
|
+
fn run() -> Result<()> {
|
|
41
|
+
let config = load_config("app.toml")
|
|
42
|
+
.context("loading application config")?;
|
|
43
|
+
serve(config).await
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Never Use unwrap() in Production Code
|
|
48
|
+
|
|
49
|
+
`unwrap()` panics on `None` or `Err`. Use `expect()` with a message that explains the invariant being asserted, or propagate with `?`.
|
|
50
|
+
|
|
51
|
+
```rust
|
|
52
|
+
// ❌ Panics with an unhelpful message
|
|
53
|
+
let port: u16 = env::var("PORT").unwrap().parse().unwrap();
|
|
54
|
+
|
|
55
|
+
// ✅ expect() with a reason
|
|
56
|
+
let port_str = env::var("PORT")
|
|
57
|
+
.expect("PORT environment variable must be set");
|
|
58
|
+
let port: u16 = port_str
|
|
59
|
+
.parse()
|
|
60
|
+
.expect("PORT must be a valid u16");
|
|
61
|
+
|
|
62
|
+
// ✅ Propagate with ? when inside a Result-returning function
|
|
63
|
+
let port: u16 = env::var("PORT")?.parse()?;
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## All Public Items Must Have rustdoc Comments
|
|
67
|
+
|
|
68
|
+
Every public function, struct, enum, trait, and module requires a doc comment (`///`).
|
|
69
|
+
|
|
70
|
+
```rust
|
|
71
|
+
// ❌ No documentation
|
|
72
|
+
pub fn compress(data: &[u8]) -> Vec<u8> { ... }
|
|
73
|
+
|
|
74
|
+
// ✅ Describe what it does, mention important contracts
|
|
75
|
+
/// Compresses `data` using LZ4 and returns the compressed bytes.
|
|
76
|
+
///
|
|
77
|
+
/// Returns an empty `Vec` if `data` is empty.
|
|
78
|
+
/// Panics if the internal compressor is exhausted (should never happen in practice).
|
|
79
|
+
pub fn compress(data: &[u8]) -> Vec<u8> { ... }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Use the Newtype Pattern for Semantically Distinct Primitives
|
|
83
|
+
|
|
84
|
+
Wrapping primitives prevents accidentally mixing values of different meaning.
|
|
85
|
+
|
|
86
|
+
```rust
|
|
87
|
+
// ❌ Easy to confuse which u64 is which
|
|
88
|
+
fn transfer(from: u64, to: u64, amount: u64) { ... }
|
|
89
|
+
|
|
90
|
+
// ✅ Distinct types — compiler catches transpositions
|
|
91
|
+
pub struct AccountId(u64);
|
|
92
|
+
pub struct CentAmount(u64);
|
|
93
|
+
|
|
94
|
+
fn transfer(from: AccountId, to: AccountId, amount: CentAmount) { ... }
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Newtype wrappers have zero runtime overhead.
|
|
98
|
+
|
|
99
|
+
## Prefer iter() Chains Over Explicit Loops When Semantics Are Clear
|
|
100
|
+
|
|
101
|
+
```rust
|
|
102
|
+
// ❌ Explicit loop for a pure transformation
|
|
103
|
+
let mut result = Vec::new();
|
|
104
|
+
for item in &items {
|
|
105
|
+
if item.is_active() {
|
|
106
|
+
result.push(item.name.clone());
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ✅ Iterator chain — intent is immediately clear
|
|
111
|
+
let result: Vec<String> = items.iter()
|
|
112
|
+
.filter(|item| item.is_active())
|
|
113
|
+
.map(|item| item.name.clone())
|
|
114
|
+
.collect();
|
|
115
|
+
|
|
116
|
+
// Use explicit loops when there are side effects or early returns that
|
|
117
|
+
// would obscure the logic if written as a chain
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Derive Debug, Clone, PartialEq by Default on Data Structs
|
|
121
|
+
|
|
122
|
+
```rust
|
|
123
|
+
// ✅ Standard derives on data-holding structs
|
|
124
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
125
|
+
pub struct Config {
|
|
126
|
+
pub host: String,
|
|
127
|
+
pub port: u16,
|
|
128
|
+
pub max_connections: usize,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Add Eq when PartialEq implies total equality (no floats)
|
|
132
|
+
// Add Hash when the type will be used as a map key
|
|
133
|
+
// Add Serialize/Deserialize when crossing I/O boundaries
|
|
134
|
+
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
|
135
|
+
pub struct UserId(u64);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Test Organization
|
|
139
|
+
|
|
140
|
+
- **Unit tests**: inside the module in a `#[cfg(test)]` block, next to the code they test.
|
|
141
|
+
- **Integration tests**: in a top-level `tests/` directory. These test the public API of the crate.
|
|
142
|
+
- **Doctests**: write examples in doc comments — they are compiled and run by `cargo test`.
|
|
143
|
+
|
|
144
|
+
```rust
|
|
145
|
+
// src/parser.rs
|
|
146
|
+
pub fn parse(input: &str) -> Result<Ast, ParseError> { ... }
|
|
147
|
+
|
|
148
|
+
#[cfg(test)]
|
|
149
|
+
mod tests {
|
|
150
|
+
use super::*;
|
|
151
|
+
|
|
152
|
+
#[test]
|
|
153
|
+
fn empty_input_returns_error() {
|
|
154
|
+
assert!(parse("").is_err());
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#[test]
|
|
158
|
+
fn valid_expression_parses_correctly() {
|
|
159
|
+
let ast = parse("1 + 2").unwrap();
|
|
160
|
+
assert_eq!(ast.eval(), 3);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```rust
|
|
166
|
+
// tests/integration_test.rs — tests/ is a separate crate
|
|
167
|
+
use mylib::parse;
|
|
168
|
+
|
|
169
|
+
#[test]
|
|
170
|
+
fn parses_complex_expression() {
|
|
171
|
+
let result = parse("(a + b) * c");
|
|
172
|
+
assert!(result.is_ok());
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## clippy Must Pass with No Warnings
|
|
177
|
+
|
|
178
|
+
All code must pass `cargo clippy -- -D warnings` with no warnings. Enforce this in CI.
|
|
179
|
+
|
|
180
|
+
If a lint must be suppressed, add `#[allow(...)]` on the smallest scope possible with a comment explaining why.
|
|
181
|
+
|
|
182
|
+
```rust
|
|
183
|
+
// ❌ Global allow — hides all issues of that category
|
|
184
|
+
#![allow(clippy::unwrap_used)]
|
|
185
|
+
|
|
186
|
+
// ✅ Local suppression with explanation
|
|
187
|
+
#[allow(clippy::unwrap_used)]
|
|
188
|
+
// Safety: regex pattern is a compile-time constant and always valid
|
|
189
|
+
let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Never Use unsafe Without a Safety Comment
|
|
193
|
+
|
|
194
|
+
Every `unsafe` block must have a comment that documents the invariants being upheld and why the operation is sound.
|
|
195
|
+
|
|
196
|
+
```rust
|
|
197
|
+
// ❌ unsafe with no explanation
|
|
198
|
+
unsafe {
|
|
199
|
+
std::ptr::write(ptr, value);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ✅ Safety comment explains the contract
|
|
203
|
+
// SAFETY: `ptr` is non-null, properly aligned, and uniquely owned here.
|
|
204
|
+
// The caller guaranteed these conditions in the function contract.
|
|
205
|
+
unsafe {
|
|
206
|
+
std::ptr::write(ptr, value);
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
If you cannot write a clear safety comment, reconsider whether `unsafe` is necessary.
|