@rashidee/co2 1.2.5
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/dist/index.js +25449 -0
- package/drizzle/0000_remarkable_charles_xavier.sql +210 -0
- package/drizzle/0001_wise_wolf_cub.sql +13 -0
- package/drizzle/0002_secret_spyke.sql +16 -0
- package/drizzle/0003_workspace_config_singleton.sql +16 -0
- package/drizzle/0004_terminal_claim_v120.sql +11 -0
- package/drizzle/0005_brainstorm_v124.sql +10 -0
- package/drizzle/0006_project_v140.sql +6 -0
- package/drizzle/0007_prd_wizard_v124.sql +14 -0
- package/drizzle/0008_workflow_artifact_v126.sql +24 -0
- package/drizzle/0009_project_import_v150.sql +7 -0
- package/drizzle/0010_quality_v151.sql +3 -0
- package/drizzle/meta/0000_snapshot.json +1335 -0
- package/drizzle/meta/0001_snapshot.json +1416 -0
- package/drizzle/meta/0002_snapshot.json +1510 -0
- package/drizzle/meta/_journal.json +83 -0
- package/package.json +41 -0
- package/plugin/.claude-plugin/marketplace.json +36 -0
- package/plugin/.claude-plugin/plugin.json +22 -0
- package/plugin/README.md +625 -0
- package/plugin/SKILLS.md +350 -0
- package/plugin/co2.svg +5 -0
- package/plugin/commands/brainstorm-loop.md +129 -0
- package/plugin/skills/conductor-defect/SKILL.md +898 -0
- package/plugin/skills/conductor-feature-develop/SKILL.md +1383 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-domain-template.md +131 -0
- package/plugin/skills/conductor-feature-develop/references/implementation-master-template.md +89 -0
- package/plugin/skills/conductor-feature-develop/references/playwright-setup.md +224 -0
- package/plugin/skills/conductor-feature-prepare/SKILL.md +604 -0
- package/plugin/skills/depgen-k8s/SKILL.md +468 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-laravel.md +197 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-nodejs.md +163 -0
- package/plugin/skills/depgen-k8s/references/dockerfile-spring-boot.md +174 -0
- package/plugin/skills/depgen-k8s/references/k8s-patterns.md +869 -0
- package/plugin/skills/mockgen-shadcn/SKILL.md +1067 -0
- package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +1467 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-shadcn/references/mockup-index-template.md +216 -0
- package/plugin/skills/mockgen-tailwind/SKILL.md +904 -0
- package/plugin/skills/mockgen-tailwind/references/admin-layout-template.md +720 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +498 -0
- package/plugin/skills/mockgen-tailwind/references/mockup-index-template.md +190 -0
- package/plugin/skills/modelgen-nosql/SKILL.md +617 -0
- package/plugin/skills/modelgen-nosql/references/model-extraction-methodology.md +497 -0
- package/plugin/skills/modelgen-nosql/references/nosql-design-guide.md +258 -0
- package/plugin/skills/modelgen-relational/SKILL.md +707 -0
- package/plugin/skills/modelgen-relational/references/model-extraction-methodology.md +499 -0
- package/plugin/skills/specgen-flutter-riverpod/SKILL.md +898 -0
- package/plugin/skills/specgen-flutter-riverpod/references/component-patterns.md +971 -0
- package/plugin/skills/specgen-flutter-riverpod/references/notification-patterns.md +457 -0
- package/plugin/skills/specgen-flutter-riverpod/references/routing-patterns.md +362 -0
- package/plugin/skills/specgen-flutter-riverpod/references/security-patterns.md +709 -0
- package/plugin/skills/specgen-flutter-riverpod/references/spec-template.md +1566 -0
- package/plugin/skills/specgen-flutter-riverpod/references/state-patterns.md +518 -0
- package/plugin/skills/specgen-flutter-riverpod/references/storage-patterns.md +370 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/SKILL.md +984 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/batch-patterns.md +359 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/messaging-patterns.md +468 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/modulith-patterns.md +461 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/reporting-patterns.md +803 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/security-patterns.md +501 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/spec-template.md +1268 -0
- package/plugin/skills/specgen-laravel-eloquent-bladehtmx/references/web-patterns.md +301 -0
- package/plugin/skills/specgen-node-cli-web/SKILL.md +561 -0
- package/plugin/skills/specgen-node-cli-web/references/auth-patterns.md +293 -0
- package/plugin/skills/specgen-node-cli-web/references/cli-packaging-patterns.md +250 -0
- package/plugin/skills/specgen-node-cli-web/references/server-patterns.md +247 -0
- package/plugin/skills/specgen-node-cli-web/references/spec-template.md +401 -0
- package/plugin/skills/specgen-node-cli-web/references/testing-patterns.md +174 -0
- package/plugin/skills/specgen-react-mui/SKILL.md +789 -0
- package/plugin/skills/specgen-react-mui/references/component-patterns.md +1077 -0
- package/plugin/skills/specgen-react-mui/references/reporting-patterns.md +1998 -0
- package/plugin/skills/specgen-react-mui/references/routing-patterns.md +461 -0
- package/plugin/skills/specgen-react-mui/references/security-patterns.md +599 -0
- package/plugin/skills/specgen-react-mui/references/spec-template.md +1030 -0
- package/plugin/skills/specgen-react-mui/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-react-tailwind/SKILL.md +859 -0
- package/plugin/skills/specgen-react-tailwind/references/component-patterns.md +1212 -0
- package/plugin/skills/specgen-react-tailwind/references/reporting-patterns.md +1529 -0
- package/plugin/skills/specgen-react-tailwind/references/routing-patterns.md +669 -0
- package/plugin/skills/specgen-react-tailwind/references/security-patterns.md +656 -0
- package/plugin/skills/specgen-react-tailwind/references/spec-template.md +1237 -0
- package/plugin/skills/specgen-react-tailwind/references/state-patterns.md +327 -0
- package/plugin/skills/specgen-sdk-java/SKILL.md +933 -0
- package/plugin/skills/specgen-sdk-java/references/api-extraction-patterns.md +247 -0
- package/plugin/skills/specgen-sdk-java/references/http-client-patterns.md +495 -0
- package/plugin/skills/specgen-sdk-java/references/packaging-patterns.md +366 -0
- package/plugin/skills/specgen-sdk-java/references/spec-template.md +956 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/SKILL.md +1284 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/jasper-patterns.md +1638 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/modulith-patterns.md +354 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/security-patterns.md +551 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/spec-template.md +2502 -0
- package/plugin/skills/specgen-spring-jpa-jtehtmx/references/web-patterns.md +503 -0
- package/plugin/skills/specgen-spring-jpa-restapi/SKILL.md +990 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/batch-patterns.md +1224 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/messaging-patterns.md +512 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/modulith-patterns.md +279 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/reporting-patterns.md +1536 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/restapi-patterns.md +722 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/security-patterns.md +351 -0
- package/plugin/skills/specgen-spring-jpa-restapi/references/spec-template.md +553 -0
- package/plugin/skills/specgen-ts-cli/SKILL.md +861 -0
- package/plugin/skills/specgen-ts-cli/references/async-patterns.md +1101 -0
- package/plugin/skills/specgen-ts-cli/references/command-patterns.md +383 -0
- package/plugin/skills/specgen-ts-cli/references/config-patterns.md +430 -0
- package/plugin/skills/specgen-ts-cli/references/database-patterns.md +750 -0
- package/plugin/skills/specgen-ts-cli/references/packaging-patterns.md +426 -0
- package/plugin/skills/specgen-ts-cli/references/spec-template.md +1391 -0
- package/plugin/skills/specgen-ts-cli/references/testing-patterns.md +909 -0
- package/plugin/skills/testgen-functional/SKILL.md +1381 -0
- package/plugin/skills/tracegen-matrix/SKILL.md +243 -0
- package/plugin/skills/util-preparek8senv/SKILL.md +422 -0
- package/plugin/skills/util-projectinit/SKILL.md +307 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-loop.md +129 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-protocol.md +144 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-setup-guide.md +153 -0
- package/plugin/skills/util-projectinit/reference/brainstorm-template-appdev.md +87 -0
- package/plugin/skills/util-projectinit/reference/co2-context-generation-guide.md +761 -0
- package/plugin/skills/util-projectsync/SKILL.md +501 -0
- package/plugin/skills/util-updprd/SKILL.md +324 -0
- package/plugin/skills/util-usanalyzer/SKILL.md +307 -0
- package/plugin/skills/util-ustagger/SKILL.md +154 -0
- package/static/assets/abnfDiagram-VRR7QNED-DvPuwyCV.js +1 -0
- package/static/assets/arc-DRKw7sjs.js +1 -0
- package/static/assets/architectureDiagram-ZJ3FMSHR-CaNcxUbC.js +36 -0
- package/static/assets/blockDiagram-677ZJIJ3-Demd4AOK.js +132 -0
- package/static/assets/c4Diagram-LMCZKHZV-PVKUfc2x.js +10 -0
- package/static/assets/channel-C2nVbuwv.js +1 -0
- package/static/assets/chunk-2Q5K7J3B-CExf8ww_.js +1 -0
- package/static/assets/chunk-32BRIVSS-CFPaoONF.js +1 -0
- package/static/assets/chunk-5VM5RSS4-DE2esM67.js +15 -0
- package/static/assets/chunk-EX3LRPZG-SON_YdDG.js +231 -0
- package/static/assets/chunk-JWPE2WC7-Qxah8LNr.js +1 -0
- package/static/assets/chunk-MOJQB5TN-BcWGQV3j.js +88 -0
- package/static/assets/chunk-RYQCIY6F-DkDj-OR5.js +1 -0
- package/static/assets/chunk-V7JOEXUC-BlI1Mrah.js +206 -0
- package/static/assets/chunk-VR4S4FIN-Agf__duj.js +1 -0
- package/static/assets/chunk-XXDRQBXY-B5CfAh85.js +1 -0
- package/static/assets/classDiagram-OUVF2IWQ-DXJIljsR.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-DXJIljsR.js +1 -0
- package/static/assets/cose-bilkent-JH36ORCC-DO0cQkcy.js +1 -0
- package/static/assets/cynefin-VYW2F7L2-CfOnsnXc.js +178 -0
- package/static/assets/cynefinDiagram-TSTJHNR4-BJ8tULAv.js +62 -0
- package/static/assets/cytoscape.esm-DTSO7Bv0.js +331 -0
- package/static/assets/dagre-VKFMJZFB-DfjwzHqP.js +4 -0
- package/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/static/assets/diagram-FQU43EPY-CndSOIIM.js +3 -0
- package/static/assets/diagram-G47NLZAW-Czf5hN0Z.js +24 -0
- package/static/assets/diagram-NH7WQ7WH-COAyojGh.js +24 -0
- package/static/assets/diagram-OA4YK3LP-H8PoiN2m.js +30 -0
- package/static/assets/diagram-WEI45ONY-DzcaVzjp.js +41 -0
- package/static/assets/ebnfDiagram-CCIWWBDH-DPazGYLJ.js +1 -0
- package/static/assets/erDiagram-Q63AITRT-Bo-fubgF.js +85 -0
- package/static/assets/flowDiagram-23GEKE2U-CSvxUDwh.js +156 -0
- package/static/assets/ganttDiagram-NO4QXBWP-DnBveWX6.js +292 -0
- package/static/assets/gitGraphDiagram-IHSO6WYX-B3rVL8c-.js +106 -0
- package/static/assets/graph-C9eacEi8.js +1 -0
- package/static/assets/index-LZSQkLE2.css +41 -0
- package/static/assets/index-y7XDSgMG.js +495 -0
- package/static/assets/infoDiagram-FWYZ7A6U-CvWV9yy2.js +2 -0
- package/static/assets/init-Gi6I4Gst.js +1 -0
- package/static/assets/ishikawaDiagram-FXEZZL3T-DF28a1N6.js +70 -0
- package/static/assets/journeyDiagram-5HDEW3XC-CB6CugYk.js +139 -0
- package/static/assets/kanban-definition-HUTT4EX6-ResSLF6d.js +89 -0
- package/static/assets/katex-C5jXJg4s.js +257 -0
- package/static/assets/layout-DEXfKzaS.js +1 -0
- package/static/assets/linear-CLO--ryk.js +1 -0
- package/static/assets/map-Czzmt4hB.js +1 -0
- package/static/assets/mindmap-definition-LN4V7U3C-BvW3EbxX.js +96 -0
- package/static/assets/ordinal-Cboi1Yqb.js +1 -0
- package/static/assets/pegDiagram-2B236MQR-BHCgqrrM.js +1 -0
- package/static/assets/pieDiagram-ENE6RG2P-DVmCQ5FA.js +39 -0
- package/static/assets/quadrantDiagram-ABIIQ3AL-B3e5KR52.js +7 -0
- package/static/assets/railroadDiagram-RFXS5EU6-Csv5ysLc.js +1 -0
- package/static/assets/requirementDiagram-TGXJPOKE-DOMVzvnB.js +84 -0
- package/static/assets/sankeyDiagram-HTMAVEWB-B1dcTOvy.js +40 -0
- package/static/assets/sequenceDiagram-DBY2YBRQ-BY414yeO.js +162 -0
- package/static/assets/sizeCapture-X5ZJPWSS-CuJfrfBN.js +1 -0
- package/static/assets/stateDiagram-2N3HPSRC-FX4rJkzV.js +1 -0
- package/static/assets/stateDiagram-v2-6OUMAXLB-BVufbO_H.js +1 -0
- package/static/assets/swimlanes-5IMT3BWC-Sfy58mmv.js +2 -0
- package/static/assets/swimlanesDiagram-G3AALYLV-wDCDw08H.js +8 -0
- package/static/assets/timeline-definition-FHXFAJF6-CmV7by8p.js +120 -0
- package/static/assets/vennDiagram-L72KCM5P-FqIyFZFt.js +34 -0
- package/static/assets/wardleyDiagram-EHGQE667-BZ06O45i.js +78 -0
- package/static/assets/xychartDiagram-FW5EYKEG-BOkffR06.js +7 -0
- package/static/index.html +24 -0
|
@@ -0,0 +1,956 @@
|
|
|
1
|
+
# Specification Template — Java SDK (Multi-Release Fat JAR)
|
|
2
|
+
|
|
3
|
+
This is the authoritative template for the generated specification. The specification is
|
|
4
|
+
split into **two types of files**:
|
|
5
|
+
|
|
6
|
+
1. **`SPECIFICATION.md`** (root) — Table of Contents, shared infrastructure, and all
|
|
7
|
+
library-level sections. Generated once per SDK.
|
|
8
|
+
2. **`<module>/SPEC.md`** (per-module) — Self-contained module blueprint. Generated
|
|
9
|
+
once per module from PRD.md.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
specification/
|
|
13
|
+
├── SPECIFICATION.md
|
|
14
|
+
├── auth/
|
|
15
|
+
│ └── SPEC.md
|
|
16
|
+
├── orders/
|
|
17
|
+
│ └── SPEC.md
|
|
18
|
+
└── ...
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Placeholders use `{{VARIABLE}}` syntax and must be replaced with values gathered from
|
|
22
|
+
context files.
|
|
23
|
+
|
|
24
|
+
Sections marked **[CONDITIONAL]** must only be included when the corresponding option
|
|
25
|
+
is selected.
|
|
26
|
+
|
|
27
|
+
**CRITICAL: Sample code requirement.** Every component in the spec MUST include a
|
|
28
|
+
complete, continuous code sample in a fenced code block (Java, XML, or JSON as
|
|
29
|
+
appropriate). The code must be self-explanatory and directly usable as a reference for
|
|
30
|
+
a coding agent. Do not describe components with bullet points alone — always accompany
|
|
31
|
+
descriptions with full code.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# Part A: Root SPECIFICATION.md
|
|
36
|
+
|
|
37
|
+
Everything from this point until "Part B" goes into the root `SPECIFICATION.md` file.
|
|
38
|
+
A coding agent implements these shared sections **first**, before any module work.
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Table of Contents
|
|
43
|
+
|
|
44
|
+
```markdown
|
|
45
|
+
## Table of Contents
|
|
46
|
+
|
|
47
|
+
### Shared Infrastructure
|
|
48
|
+
- [1. Project Overview](#1-project-overview)
|
|
49
|
+
- [2. Maven Configuration](#2-maven-configuration)
|
|
50
|
+
- [3. Source Layout](#3-source-layout)
|
|
51
|
+
- [4. OkHttpClient Setup](#4-okhttpclient-setup)
|
|
52
|
+
- [5. JSON Support](#5-json-support)
|
|
53
|
+
- [6. Error Model](#6-error-model)
|
|
54
|
+
- [7. Retry Strategy](#7-retry-strategy)
|
|
55
|
+
- [8. Authentication](#8-authentication)
|
|
56
|
+
- [9. Top-Level Client Facade](#9-top-level-client-facade)
|
|
57
|
+
- [10. Diagnostic Main](#10-diagnostic-main)
|
|
58
|
+
- [11. Logging Strategy](#11-logging-strategy)
|
|
59
|
+
- [12. Testing Strategy](#12-testing-strategy)
|
|
60
|
+
- [13. Packaging & Distribution](#13-packaging-distribution)
|
|
61
|
+
- [14. Public API Surface & Compatibility](#14-public-api-surface)
|
|
62
|
+
- [15. Fat JAR Verification](#15-fat-jar-verification)
|
|
63
|
+
- [16. Architecture Patterns](#16-architecture-patterns)
|
|
64
|
+
|
|
65
|
+
### Modules
|
|
66
|
+
- [Auth](auth/SPEC.md)
|
|
67
|
+
- [Orders](orders/SPEC.md)
|
|
68
|
+
- ...(one link per module)...
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Section 1: Project Overview
|
|
74
|
+
|
|
75
|
+
```markdown
|
|
76
|
+
# {{APPLICATION_NAME}} — SDK Technical Specification
|
|
77
|
+
|
|
78
|
+
**Application Name**: {{APPLICATION_NAME}}
|
|
79
|
+
**Artifact ID**: {{ARTIFACT_ID}}
|
|
80
|
+
**Group ID**: com.bestinet.urp
|
|
81
|
+
**Base Package**: {{BASE_PACKAGE}}
|
|
82
|
+
**JDK Baseline**: 8
|
|
83
|
+
**JDK Overlay**: 11 (via Multi-Release JAR)
|
|
84
|
+
**Build Tool**: Maven 3.9.x
|
|
85
|
+
**Description**: {{APP_DESCRIPTION}}
|
|
86
|
+
**Versions Covered**: v1.0.0 — v{{LATEST_VERSION}}
|
|
87
|
+
|
|
88
|
+
### API Surface Source
|
|
89
|
+
|
|
90
|
+
**Source**: {{Swagger UI URL | OpenAPI Spec URL | OpenAPI Spec Path | "Inferred from user stories — no OpenAPI spec available"}}
|
|
91
|
+
**URL/Path**: {{actual url or path or "n/a"}}
|
|
92
|
+
**Number of operations**: {{count from spec or "unknown"}}
|
|
93
|
+
|
|
94
|
+
### Optional Components (Auto-Determined)
|
|
95
|
+
|
|
96
|
+
**Protocols**: REST{{ + WebSocket | + SSE | + AMQP | + MQTT}}
|
|
97
|
+
**Authentication**: {{Bearer | API Key | mTLS | Basic | None}}
|
|
98
|
+
**Async API**: {{yes (all) | yes (long ops) | no}}
|
|
99
|
+
**Logging**: {{SLF4J facade | JUL}}
|
|
100
|
+
|
|
101
|
+
### Technology Stack
|
|
102
|
+
|
|
103
|
+
| Component | Version |
|
|
104
|
+
|--------------------------|---------------|
|
|
105
|
+
| Java baseline (`release`)| 8 |
|
|
106
|
+
| Java overlay (`release`) | 11 |
|
|
107
|
+
| Maven | 3.9.x |
|
|
108
|
+
| OkHttp | 4.12.0 |
|
|
109
|
+
|
|
110
|
+
(Test-only and optional protocol dependencies omitted.)
|
|
111
|
+
|
|
112
|
+
### Target Consumers
|
|
113
|
+
|
|
114
|
+
(List each consumer of the SDK, with a one-line description.)
|
|
115
|
+
|
|
116
|
+
| Consumer Application | Use Case |
|
|
117
|
+
|----------------------|----------|
|
|
118
|
+
| ... | ... |
|
|
119
|
+
|
|
120
|
+
### Modules
|
|
121
|
+
|
|
122
|
+
| Module | Stories | Versions | Spec |
|
|
123
|
+
|--------|---------|----------|------|
|
|
124
|
+
| Auth | 4 | 1.0.0 | [SPEC](auth/SPEC.md) |
|
|
125
|
+
| Orders | 8 | 1.0.0 | [SPEC](orders/SPEC.md) |
|
|
126
|
+
| ... | ... | ... | ... |
|
|
127
|
+
|
|
128
|
+
### Default Base URL
|
|
129
|
+
|
|
130
|
+
`{{DEFAULT_BASE_URL}}` (override at runtime via `Client.Builder#baseUrl`)
|
|
131
|
+
|
|
132
|
+
### Input Sources
|
|
133
|
+
|
|
134
|
+
- CLAUDE.md: {{path}}
|
|
135
|
+
- PRD.md: {{path}}
|
|
136
|
+
- Module Model: {{path to MODEL.md or "n/a"}}
|
|
137
|
+
- OpenAPI: {{url or path or "n/a"}}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Section 2: Maven Configuration
|
|
143
|
+
|
|
144
|
+
Generate a complete `pom.xml` inside a code block. Key requirements:
|
|
145
|
+
|
|
146
|
+
- `<packaging>jar</packaging>`
|
|
147
|
+
- `<properties>` with `maven.compiler.release=8` baseline
|
|
148
|
+
- One runtime dependency: `com.squareup.okhttp3:okhttp:4.12.0`
|
|
149
|
+
- Test-scoped: JUnit 5, AssertJ, OkHttp MockWebServer
|
|
150
|
+
- `<scope>provided</scope>` for `org.slf4j:slf4j-api` (only if Logging = SLF4J facade)
|
|
151
|
+
- `maven-compiler-plugin` with two executions:
|
|
152
|
+
- Default: compiles `src/main/java` to `target/classes` with `<release>8</release>`
|
|
153
|
+
- `compile-java11`: compiles `src/main/java11` to `target/classes-java11` with
|
|
154
|
+
`<release>11</release>`
|
|
155
|
+
- `maven-jar-plugin`: adds the JDK 11 overlay into `META-INF/versions/11/` and writes
|
|
156
|
+
`Multi-Release: true` in the manifest
|
|
157
|
+
- `maven-shade-plugin`:
|
|
158
|
+
- `<phase>package</phase>` execution
|
|
159
|
+
- Relocates `okhttp3` → `{{BASE_PACKAGE}}.shaded.okhttp3`
|
|
160
|
+
- Relocates `okio` → `{{BASE_PACKAGE}}.shaded.okio`
|
|
161
|
+
- Relocates `kotlin` → `{{BASE_PACKAGE}}.shaded.kotlin`
|
|
162
|
+
- `<ManifestResourceTransformer>` preserving `Multi-Release: true`
|
|
163
|
+
- `<filters>` excluding `META-INF/*.SF`, `*.DSA`, `*.RSA`
|
|
164
|
+
- `maven-source-plugin`, `maven-javadoc-plugin`, `maven-surefire-plugin`
|
|
165
|
+
|
|
166
|
+
The complete `pom.xml` MUST be written out in this section. See
|
|
167
|
+
`references/packaging-patterns.md` for the canonical configuration to copy in.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Section 3: Source Layout
|
|
172
|
+
|
|
173
|
+
Render the full directory tree (see SKILL.md "Source Layout"). Annotate each directory
|
|
174
|
+
with one line explaining its purpose. Highlight that:
|
|
175
|
+
|
|
176
|
+
- `src/main/java/` is the JDK 8 baseline that supplies the COMPLETE public API
|
|
177
|
+
- `src/main/java11/` contains overlay implementations only — every overlay class MUST
|
|
178
|
+
also exist in `src/main/java/` with an identical signature
|
|
179
|
+
- `src/test/java/` contains MockWebServer-driven tests
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Section 4: OkHttpClient Setup
|
|
184
|
+
|
|
185
|
+
### `http/HttpClientFactory.java`
|
|
186
|
+
|
|
187
|
+
Sample code (mandatory):
|
|
188
|
+
|
|
189
|
+
```java
|
|
190
|
+
package {{BASE_PACKAGE}}.http;
|
|
191
|
+
|
|
192
|
+
import okhttp3.ConnectionPool;
|
|
193
|
+
import okhttp3.OkHttpClient;
|
|
194
|
+
|
|
195
|
+
import java.time.Duration;
|
|
196
|
+
import java.util.concurrent.TimeUnit;
|
|
197
|
+
|
|
198
|
+
final class HttpClientFactory {
|
|
199
|
+
|
|
200
|
+
private HttpClientFactory() {}
|
|
201
|
+
|
|
202
|
+
static OkHttpClient build({{ClientName}}Config config) {
|
|
203
|
+
OkHttpClient.Builder builder = new OkHttpClient.Builder()
|
|
204
|
+
.connectTimeout(config.connectTimeout().toMillis(), TimeUnit.MILLISECONDS)
|
|
205
|
+
.readTimeout(config.readTimeout().toMillis(), TimeUnit.MILLISECONDS)
|
|
206
|
+
.writeTimeout(config.writeTimeout().toMillis(), TimeUnit.MILLISECONDS)
|
|
207
|
+
.connectionPool(new ConnectionPool(
|
|
208
|
+
config.maxIdleConnections(),
|
|
209
|
+
config.keepAliveDuration().toMillis(),
|
|
210
|
+
TimeUnit.MILLISECONDS));
|
|
211
|
+
|
|
212
|
+
if (config.credentialProvider() != null) {
|
|
213
|
+
builder.addInterceptor(new AuthInterceptor(config.credentialProvider()));
|
|
214
|
+
}
|
|
215
|
+
builder.addInterceptor(new CorrelationIdInterceptor());
|
|
216
|
+
builder.addInterceptor(new RetryInterceptor(config.retryStrategy()));
|
|
217
|
+
if (Slf4jPresence.AVAILABLE) {
|
|
218
|
+
builder.addInterceptor(new LoggingInterceptor());
|
|
219
|
+
}
|
|
220
|
+
return builder.build();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Document thread-safety rules:
|
|
226
|
+
|
|
227
|
+
- The returned `OkHttpClient` is built ONCE per `{{ClientName}}` instance.
|
|
228
|
+
- Consumers MUST reuse a single `{{ClientName}}` across the application lifetime.
|
|
229
|
+
- Closing: call `client.close()` on application shutdown to release the connection pool.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Section 5: JSON Support
|
|
234
|
+
|
|
235
|
+
### `json/Json.java`
|
|
236
|
+
|
|
237
|
+
A minimal hand-written JSON read/write utility. Key requirements:
|
|
238
|
+
|
|
239
|
+
- No reflection
|
|
240
|
+
- Operates on `Object` for read (`Map<String, Object>`, `List<Object>`, `String`,
|
|
241
|
+
`Number`, `Boolean`, `null`) and `JsonWriter`/`JsonReader` token streams for write.
|
|
242
|
+
- Supports the surface area the SDK actually needs — no full JSON Pointer, no
|
|
243
|
+
schema validation, no streaming for arbitrary trees.
|
|
244
|
+
|
|
245
|
+
Sample skeleton:
|
|
246
|
+
|
|
247
|
+
```java
|
|
248
|
+
package {{BASE_PACKAGE}}.json;
|
|
249
|
+
|
|
250
|
+
import java.io.IOException;
|
|
251
|
+
import java.io.Reader;
|
|
252
|
+
import java.io.StringReader;
|
|
253
|
+
import java.io.Writer;
|
|
254
|
+
|
|
255
|
+
public final class Json {
|
|
256
|
+
|
|
257
|
+
private Json() {}
|
|
258
|
+
|
|
259
|
+
public static Object parse(String text) {
|
|
260
|
+
return new JsonReader(new StringReader(text)).readValue();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
public static String stringify(Object value) {
|
|
264
|
+
StringBuilder sb = new StringBuilder();
|
|
265
|
+
new JsonWriter(sb).writeValue(value);
|
|
266
|
+
return sb.toString();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// JsonReader and JsonWriter implementations follow.
|
|
270
|
+
// Each model class provides its own:
|
|
271
|
+
// public void toJson(JsonWriter w) { ... }
|
|
272
|
+
// public static Foo fromJson(JsonReader r) { ... }
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Per-model serializer pattern (every request/response DTO follows this):
|
|
277
|
+
|
|
278
|
+
```java
|
|
279
|
+
public Map<String, Object> toJsonMap() {
|
|
280
|
+
Map<String, Object> m = new LinkedHashMap<>();
|
|
281
|
+
if (id != null) m.put("id", id);
|
|
282
|
+
if (name != null) m.put("name", name);
|
|
283
|
+
if (status != null) m.put("status", status.wireValue());
|
|
284
|
+
return m;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
public static OrderDto fromJsonMap(Map<String, Object> m) {
|
|
288
|
+
return OrderDto.builder()
|
|
289
|
+
.id((String) m.get("id"))
|
|
290
|
+
.name((String) m.get("name"))
|
|
291
|
+
.status(OrderStatus.fromWireValue((String) m.get("status")))
|
|
292
|
+
.build();
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The full `JsonReader`/`JsonWriter` implementations are non-trivial — generate them in
|
|
297
|
+
this section, fully expanded, even if the spec is otherwise terse.
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## Section 6: Error Model
|
|
302
|
+
|
|
303
|
+
```java
|
|
304
|
+
package {{BASE_PACKAGE}}.error;
|
|
305
|
+
|
|
306
|
+
public class SdkException extends RuntimeException {
|
|
307
|
+
public SdkException(String message) { super(message); }
|
|
308
|
+
public SdkException(String message, Throwable cause) { super(message, cause); }
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
public class SdkApiException extends SdkException {
|
|
312
|
+
private final int statusCode;
|
|
313
|
+
private final String body;
|
|
314
|
+
private final String method;
|
|
315
|
+
private final String url;
|
|
316
|
+
|
|
317
|
+
public SdkApiException(int statusCode, String body, String method, String url) {
|
|
318
|
+
super("HTTP " + statusCode + " " + method + " " + url);
|
|
319
|
+
this.statusCode = statusCode;
|
|
320
|
+
this.body = body;
|
|
321
|
+
this.method = method;
|
|
322
|
+
this.url = url;
|
|
323
|
+
}
|
|
324
|
+
public int statusCode() { return statusCode; }
|
|
325
|
+
public String body() { return body; }
|
|
326
|
+
public String method() { return method; }
|
|
327
|
+
public String url() { return url; }
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
public class SdkAuthException extends SdkApiException { /* 401 / 403 */ }
|
|
331
|
+
public class SdkClientException extends SdkException { /* local I/O */ }
|
|
332
|
+
public class SdkTimeoutException extends SdkClientException { /* socket timeout */ }
|
|
333
|
+
public class SdkRetryExhaustedException extends SdkClientException { /* retry budget */ }
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Section 7: Retry Strategy
|
|
339
|
+
|
|
340
|
+
```java
|
|
341
|
+
public interface RetryStrategy {
|
|
342
|
+
Decision evaluate(int attempt, Response response, IOException error);
|
|
343
|
+
|
|
344
|
+
final class Decision {
|
|
345
|
+
public final boolean retry;
|
|
346
|
+
public final long delayMillis;
|
|
347
|
+
Decision(boolean retry, long delayMillis) {
|
|
348
|
+
this.retry = retry; this.delayMillis = delayMillis;
|
|
349
|
+
}
|
|
350
|
+
public static Decision stop() { return new Decision(false, 0L); }
|
|
351
|
+
public static Decision retryIn(long delayMs) { return new Decision(true, delayMs); }
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Default implementation `ExponentialBackoffRetryStrategy` retries on:
|
|
357
|
+
|
|
358
|
+
- `IOException` with cause `ConnectException` / `SocketTimeoutException`
|
|
359
|
+
- HTTP 502, 503, 504
|
|
360
|
+
|
|
361
|
+
Backoff: `min(maxBackoff, base * 2^attempt + jitter)`. Default base = 200ms,
|
|
362
|
+
maxBackoff = 5s, max attempts = 3.
|
|
363
|
+
|
|
364
|
+
Provide the full implementation in this section.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Section 8: Authentication [CONDITIONAL]
|
|
369
|
+
|
|
370
|
+
Refer to the SKILL.md "Authentication" section. Generate the relevant code:
|
|
371
|
+
|
|
372
|
+
- `BearerCredentialProvider` interface + `StaticBearerCredentialProvider`
|
|
373
|
+
- `AuthInterceptor` that reads from the provider, sets `Authorization` header, and on 401
|
|
374
|
+
invokes `provider.refresh()` and replays the request once.
|
|
375
|
+
- Redact the `Authorization` header in `LoggingInterceptor`.
|
|
376
|
+
|
|
377
|
+
For API key, mTLS, or Basic, follow the patterns documented in
|
|
378
|
+
`references/http-client-patterns.md`.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Section 9: Top-Level Client Facade
|
|
383
|
+
|
|
384
|
+
```java
|
|
385
|
+
package {{BASE_PACKAGE}};
|
|
386
|
+
|
|
387
|
+
import okhttp3.OkHttpClient;
|
|
388
|
+
import {{BASE_PACKAGE}}.http.HttpClientFactory;
|
|
389
|
+
import {{BASE_PACKAGE}}.auth.AuthService;
|
|
390
|
+
import {{BASE_PACKAGE}}.orders.OrdersService;
|
|
391
|
+
|
|
392
|
+
import java.time.Duration;
|
|
393
|
+
import java.util.Objects;
|
|
394
|
+
|
|
395
|
+
public final class {{ClientName}} implements AutoCloseable {
|
|
396
|
+
|
|
397
|
+
private final {{ClientName}}Config config;
|
|
398
|
+
private final OkHttpClient http;
|
|
399
|
+
private final AuthService authService;
|
|
400
|
+
private final OrdersService ordersService;
|
|
401
|
+
|
|
402
|
+
private {{ClientName}}({{ClientName}}Config config, OkHttpClient http) {
|
|
403
|
+
this.config = config;
|
|
404
|
+
this.http = http;
|
|
405
|
+
this.authService = new {{BASE_PACKAGE}}.auth.internal.AuthServiceImpl(http, config);
|
|
406
|
+
this.ordersService = new {{BASE_PACKAGE}}.orders.internal.OrdersServiceImpl(http, config);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
public static Builder builder() { return new Builder(); }
|
|
410
|
+
|
|
411
|
+
public AuthService auth() { return authService; }
|
|
412
|
+
public OrdersService orders() { return ordersService; }
|
|
413
|
+
|
|
414
|
+
@Override
|
|
415
|
+
public void close() {
|
|
416
|
+
http.dispatcher().executorService().shutdown();
|
|
417
|
+
http.connectionPool().evictAll();
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
public static final class Builder {
|
|
421
|
+
private String baseUrl;
|
|
422
|
+
private Duration connectTimeout = Duration.ofSeconds(30);
|
|
423
|
+
private Duration readTimeout = Duration.ofSeconds(30);
|
|
424
|
+
private Duration writeTimeout = Duration.ofSeconds(30);
|
|
425
|
+
private RetryStrategy retryStrategy = RetryStrategy.defaultStrategy();
|
|
426
|
+
private CredentialProvider credentialProvider;
|
|
427
|
+
// ...one setter per Config field...
|
|
428
|
+
|
|
429
|
+
public Builder baseUrl(String baseUrl) { this.baseUrl = baseUrl; return this; }
|
|
430
|
+
public Builder connectTimeout(Duration d) { this.connectTimeout = d; return this; }
|
|
431
|
+
// ...etc...
|
|
432
|
+
|
|
433
|
+
public {{ClientName}} build() {
|
|
434
|
+
Objects.requireNonNull(baseUrl, "baseUrl is required");
|
|
435
|
+
{{ClientName}}Config cfg = new {{ClientName}}Config(
|
|
436
|
+
baseUrl, connectTimeout, readTimeout, writeTimeout,
|
|
437
|
+
retryStrategy, credentialProvider /* , ... */);
|
|
438
|
+
return new {{ClientName}}(cfg, HttpClientFactory.build(cfg));
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Generate `{{ClientName}}Config` as a final class with `private final` fields, all
|
|
445
|
+
accessors named without `get` prefix (e.g., `baseUrl()`), and a package-private
|
|
446
|
+
constructor. No setters.
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
|
|
450
|
+
## Section 10: Diagnostic Main [CONDITIONAL]
|
|
451
|
+
|
|
452
|
+
If included:
|
|
453
|
+
|
|
454
|
+
```java
|
|
455
|
+
package {{BASE_PACKAGE}}.cli;
|
|
456
|
+
|
|
457
|
+
import {{BASE_PACKAGE}}.{{ClientName}};
|
|
458
|
+
|
|
459
|
+
public final class Diagnose {
|
|
460
|
+
public static void main(String[] args) throws Exception {
|
|
461
|
+
String baseUrl = System.getenv("BASE_URL");
|
|
462
|
+
String token = System.getenv("API_TOKEN");
|
|
463
|
+
if (baseUrl == null) {
|
|
464
|
+
System.err.println("BASE_URL env var is required");
|
|
465
|
+
System.exit(2);
|
|
466
|
+
}
|
|
467
|
+
try ({{ClientName}} client = {{ClientName}}.builder()
|
|
468
|
+
.baseUrl(baseUrl)
|
|
469
|
+
/* .credentials(BearerCredentialProvider.of(token)) */
|
|
470
|
+
.build()) {
|
|
471
|
+
System.out.println("SDK initialised against " + baseUrl);
|
|
472
|
+
// Make a single smoke-test call here.
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
The fat-JAR manifest's `Main-Class` points to this class only if Diagnose is included.
|
|
479
|
+
|
|
480
|
+
---
|
|
481
|
+
|
|
482
|
+
## Section 11: Logging Strategy
|
|
483
|
+
|
|
484
|
+
If Logging = SLF4J facade:
|
|
485
|
+
|
|
486
|
+
- `org.slf4j:slf4j-api` is `<scope>provided</scope>` — NOT bundled.
|
|
487
|
+
- The SDK uses `LoggerFactory.getLogger(...)`. If the consumer has no SLF4J binding,
|
|
488
|
+
no-op output is the standard SLF4J behaviour.
|
|
489
|
+
- The `LoggingInterceptor` is registered ONLY if `org.slf4j.Logger` is on the classpath
|
|
490
|
+
(use a class-presence check):
|
|
491
|
+
|
|
492
|
+
```java
|
|
493
|
+
final class Slf4jPresence {
|
|
494
|
+
static final boolean AVAILABLE = isAvailable();
|
|
495
|
+
|
|
496
|
+
private static boolean isAvailable() {
|
|
497
|
+
try {
|
|
498
|
+
Class.forName("org.slf4j.Logger");
|
|
499
|
+
return true;
|
|
500
|
+
} catch (ClassNotFoundException e) {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
If Logging = JUL: every component uses `java.util.logging.Logger.getLogger(...)`.
|
|
508
|
+
No external dependency. No facade.
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Section 12: Testing Strategy
|
|
513
|
+
|
|
514
|
+
```java
|
|
515
|
+
package {{BASE_PACKAGE}}.orders;
|
|
516
|
+
|
|
517
|
+
import okhttp3.mockwebserver.MockResponse;
|
|
518
|
+
import okhttp3.mockwebserver.MockWebServer;
|
|
519
|
+
import {{BASE_PACKAGE}}.{{ClientName}};
|
|
520
|
+
import {{BASE_PACKAGE}}.orders.model.OrderDto;
|
|
521
|
+
import org.junit.jupiter.api.AfterEach;
|
|
522
|
+
import org.junit.jupiter.api.BeforeEach;
|
|
523
|
+
import org.junit.jupiter.api.Test;
|
|
524
|
+
|
|
525
|
+
import static org.assertj.core.api.Assertions.assertThat;
|
|
526
|
+
|
|
527
|
+
class OrdersServiceTest {
|
|
528
|
+
|
|
529
|
+
private MockWebServer server;
|
|
530
|
+
private {{ClientName}} client;
|
|
531
|
+
|
|
532
|
+
@BeforeEach
|
|
533
|
+
void setUp() throws Exception {
|
|
534
|
+
server = new MockWebServer();
|
|
535
|
+
server.start();
|
|
536
|
+
client = {{ClientName}}.builder()
|
|
537
|
+
.baseUrl(server.url("/").toString())
|
|
538
|
+
.build();
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
@AfterEach
|
|
542
|
+
void tearDown() throws Exception {
|
|
543
|
+
client.close();
|
|
544
|
+
server.shutdown();
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
@Test
|
|
548
|
+
void getById_returnsOrder() {
|
|
549
|
+
server.enqueue(new MockResponse()
|
|
550
|
+
.setResponseCode(200)
|
|
551
|
+
.setHeader("Content-Type", "application/json")
|
|
552
|
+
.setBody("{\"id\":\"ORD-123\",\"status\":\"NEW\"}"));
|
|
553
|
+
|
|
554
|
+
OrderDto order = client.orders().getById("ORD-123");
|
|
555
|
+
|
|
556
|
+
assertThat(order.id()).isEqualTo("ORD-123");
|
|
557
|
+
assertThat(order.status()).isEqualTo(OrderStatus.NEW);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
Every module's `*ServiceTest` follows this pattern. Document one full class here; per-
|
|
563
|
+
module SPEC.md only lists the test scenarios.
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
567
|
+
## Section 13: Packaging & Distribution
|
|
568
|
+
|
|
569
|
+
Refer to `references/packaging-patterns.md`. Render the full `maven-shade-plugin` and
|
|
570
|
+
`maven-jar-plugin` configurations and the `mvn package` / `mvn deploy` invocations.
|
|
571
|
+
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
## Section 14: Public API Surface & Compatibility
|
|
575
|
+
|
|
576
|
+
- Every public type and method is javadoc'd.
|
|
577
|
+
- Every introduction is annotated with `@since` matching the introducing version.
|
|
578
|
+
- Removed APIs are `@Deprecated(forRemoval = true, since = "x.y.z")` for at least one
|
|
579
|
+
minor version before removal.
|
|
580
|
+
- Optional: `revapi-maven-plugin` configured in the build to fail the build on
|
|
581
|
+
binary-incompatible changes.
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## Section 15: Fat JAR Verification
|
|
586
|
+
|
|
587
|
+
Manual verification commands:
|
|
588
|
+
|
|
589
|
+
```
|
|
590
|
+
mvn clean package
|
|
591
|
+
jar tf target/{{ARTIFACT_ID}}-{{VERSION}}.jar | head -50
|
|
592
|
+
unzip -p target/{{ARTIFACT_ID}}-{{VERSION}}.jar META-INF/MANIFEST.MF
|
|
593
|
+
jar tf target/{{ARTIFACT_ID}}-{{VERSION}}.jar | grep "META-INF/versions/11/"
|
|
594
|
+
jar tf target/{{ARTIFACT_ID}}-{{VERSION}}.jar | grep -E "^okhttp3/|^okio/|^kotlin/" || echo "ok: no unrelocated transitives"
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Expected:
|
|
598
|
+
|
|
599
|
+
- `META-INF/MANIFEST.MF` contains `Multi-Release: true`
|
|
600
|
+
- `META-INF/versions/11/` is non-empty
|
|
601
|
+
- No `okhttp3/`, `okio/`, or `kotlin/` packages at root (they live under
|
|
602
|
+
`{{BASE_PACKAGE}}/shaded/...`)
|
|
603
|
+
|
|
604
|
+
Functional verification:
|
|
605
|
+
|
|
606
|
+
```
|
|
607
|
+
JAVA_HOME=/path/to/jdk-8 java -jar target/{{ARTIFACT_ID}}-{{VERSION}}.jar
|
|
608
|
+
JAVA_HOME=/path/to/jdk-11 java -jar target/{{ARTIFACT_ID}}-{{VERSION}}.jar
|
|
609
|
+
JAVA_HOME=/path/to/jdk-17 java -jar target/{{ARTIFACT_ID}}-{{VERSION}}.jar
|
|
610
|
+
JAVA_HOME=/path/to/jdk-21 java -jar target/{{ARTIFACT_ID}}-{{VERSION}}.jar
|
|
611
|
+
```
|
|
612
|
+
|
|
613
|
+
(Only if Diagnose main is included.)
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## Section 16: Architecture Patterns
|
|
618
|
+
|
|
619
|
+
Render the table extracted in the SKILL.md "Architecture Principle Extraction" step,
|
|
620
|
+
with one row per pattern and a one-paragraph explanation of where it appears in the
|
|
621
|
+
codebase. Include only patterns that the PRD's Architecture Principle section actually
|
|
622
|
+
mentions, plus the always-on defaults (Adapter, Builder, Strategy).
|
|
623
|
+
|
|
624
|
+
---
|
|
625
|
+
|
|
626
|
+
# Part B: Module SPEC.md Template
|
|
627
|
+
|
|
628
|
+
Everything below is the template for each `<module>/SPEC.md` file. Generate one file
|
|
629
|
+
per module from PRD.md.
|
|
630
|
+
|
|
631
|
+
Each module SPEC.md is **self-contained** — a coding agent picks it up and implements
|
|
632
|
+
the entire module independently (after the shared infrastructure from
|
|
633
|
+
`SPECIFICATION.md` is in place).
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
## Module SPEC.md Structure
|
|
638
|
+
|
|
639
|
+
```markdown
|
|
640
|
+
# {{ModuleName}} Module — SDK Specification
|
|
641
|
+
|
|
642
|
+
> Part of [{{APPLICATION_NAME}} SDK Technical Specification](../SPECIFICATION.md)
|
|
643
|
+
|
|
644
|
+
## Overview
|
|
645
|
+
|
|
646
|
+
**Module:** {{ModuleName}}
|
|
647
|
+
**Package:** {{BASE_PACKAGE}}.{{modulename}}
|
|
648
|
+
**Public Service:** {{Module}}Service
|
|
649
|
+
**Internal Implementation:** {{BASE_PACKAGE}}.{{modulename}}.internal.{{Module}}ServiceImpl
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
### Traceability
|
|
653
|
+
|
|
654
|
+
```markdown
|
|
655
|
+
## Traceability
|
|
656
|
+
|
|
657
|
+
### User Stories
|
|
658
|
+
| ID | Version | Description |
|
|
659
|
+
|---|---|---|
|
|
660
|
+
| USSDK00012 | v1.0.0 | List orders by status |
|
|
661
|
+
|
|
662
|
+
### Non-Functional Requirements
|
|
663
|
+
| ID | Version | Description |
|
|
664
|
+
|---|---|---|
|
|
665
|
+
| NFRSDK0006 | v1.0.0 | Default per-call timeout 30s |
|
|
666
|
+
|
|
667
|
+
### Constraints
|
|
668
|
+
| ID | Version | Description |
|
|
669
|
+
|---|---|---|
|
|
670
|
+
| CONSSDK003 | v1.0.0 | Backwards-compatible within major version |
|
|
671
|
+
|
|
672
|
+
### Tests
|
|
673
|
+
| ID | Version | Description |
|
|
674
|
+
|---|---|---|
|
|
675
|
+
| TSDK00009 | v1.0.0 | MockWebServer 200/empty array → empty list |
|
|
676
|
+
|
|
677
|
+
### References
|
|
678
|
+
| ID | Version | Description |
|
|
679
|
+
|---|---|---|
|
|
680
|
+
| REFSDK001 | v1.0.0 | Upstream API docs at https://api.example.com/docs |
|
|
681
|
+
|
|
682
|
+
### Removed / Replaced
|
|
683
|
+
| ID | Type | Removed In | Replaced By | Reason |
|
|
684
|
+
|---|---|---|---|---|
|
|
685
|
+
| _None._ | | | | |
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### OpenAPI Operation Mapping
|
|
689
|
+
|
|
690
|
+
```markdown
|
|
691
|
+
## OpenAPI Operation Mapping
|
|
692
|
+
|
|
693
|
+
| User Story | Operation ID | Method | Path | Request Schema | Response Schema |
|
|
694
|
+
|---|---|---|---|---|---|
|
|
695
|
+
| USSDK00012 | listOrders | GET | /orders | (query) | OrdersPage |
|
|
696
|
+
| USSDK00013 | getOrderById | GET | /orders/{id} | — | Order |
|
|
697
|
+
| USSDK00014 | createOrder | POST | /orders | OrderCreateRequest | Order |
|
|
698
|
+
```
|
|
699
|
+
|
|
700
|
+
If no OpenAPI spec was available, replace the table with one derived from user stories
|
|
701
|
+
alone and add `[TODO]` notes where the upstream operation ID is unknown.
|
|
702
|
+
|
|
703
|
+
### Public API
|
|
704
|
+
|
|
705
|
+
For each component, render a complete code sample.
|
|
706
|
+
|
|
707
|
+
#### Service Interface
|
|
708
|
+
|
|
709
|
+
```java
|
|
710
|
+
package {{BASE_PACKAGE}}.{{modulename}};
|
|
711
|
+
|
|
712
|
+
import {{BASE_PACKAGE}}.{{modulename}}.model.OrderDto;
|
|
713
|
+
import {{BASE_PACKAGE}}.common.Page;
|
|
714
|
+
|
|
715
|
+
import java.util.concurrent.CompletableFuture;
|
|
716
|
+
|
|
717
|
+
public interface {{Module}}Service {
|
|
718
|
+
|
|
719
|
+
/** USSDK00012 — list orders, paginated. @since 1.0.0 */
|
|
720
|
+
Page<OrderDto> list(OrderListQuery query);
|
|
721
|
+
|
|
722
|
+
/** USSDK00013 — get order by id. @since 1.0.0 */
|
|
723
|
+
OrderDto getById(String id);
|
|
724
|
+
|
|
725
|
+
/** USSDK00014 — create order. @since 1.0.0 */
|
|
726
|
+
OrderDto create(OrderCreateRequest request);
|
|
727
|
+
|
|
728
|
+
// Async overloads if Async API = yes
|
|
729
|
+
// CompletableFuture<Page<OrderDto>> listAsync(OrderListQuery query);
|
|
730
|
+
}
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
#### Request DTOs
|
|
734
|
+
|
|
735
|
+
```java
|
|
736
|
+
package {{BASE_PACKAGE}}.{{modulename}}.model;
|
|
737
|
+
|
|
738
|
+
import java.util.Objects;
|
|
739
|
+
|
|
740
|
+
public final class OrderCreateRequest {
|
|
741
|
+
|
|
742
|
+
private final String customerId;
|
|
743
|
+
private final String currency;
|
|
744
|
+
private final long amountCents;
|
|
745
|
+
|
|
746
|
+
private OrderCreateRequest(Builder b) {
|
|
747
|
+
this.customerId = Objects.requireNonNull(b.customerId, "customerId");
|
|
748
|
+
this.currency = Objects.requireNonNull(b.currency, "currency");
|
|
749
|
+
this.amountCents = b.amountCents;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
public String customerId() { return customerId; }
|
|
753
|
+
public String currency() { return currency; }
|
|
754
|
+
public long amountCents() { return amountCents; }
|
|
755
|
+
|
|
756
|
+
public static Builder builder() { return new Builder(); }
|
|
757
|
+
|
|
758
|
+
public static final class Builder {
|
|
759
|
+
private String customerId;
|
|
760
|
+
private String currency;
|
|
761
|
+
private long amountCents;
|
|
762
|
+
public Builder customerId(String s) { this.customerId = s; return this; }
|
|
763
|
+
public Builder currency(String s) { this.currency = s; return this; }
|
|
764
|
+
public Builder amountCents(long n) { this.amountCents = n; return this; }
|
|
765
|
+
public OrderCreateRequest build() { return new OrderCreateRequest(this); }
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
#### Response DTOs
|
|
771
|
+
|
|
772
|
+
```java
|
|
773
|
+
package {{BASE_PACKAGE}}.{{modulename}}.model;
|
|
774
|
+
|
|
775
|
+
public final class OrderDto {
|
|
776
|
+
|
|
777
|
+
private final String id;
|
|
778
|
+
private final String customerId;
|
|
779
|
+
private final OrderStatus status;
|
|
780
|
+
private final long amountCents;
|
|
781
|
+
|
|
782
|
+
private OrderDto(Builder b) { /* ... */ }
|
|
783
|
+
|
|
784
|
+
public String id() { return id; }
|
|
785
|
+
public String customerId() { return customerId; }
|
|
786
|
+
public OrderStatus status() { return status; }
|
|
787
|
+
public long amountCents() { return amountCents; }
|
|
788
|
+
|
|
789
|
+
public static Builder builder() { return new Builder(); }
|
|
790
|
+
|
|
791
|
+
public static OrderDto fromJsonMap(java.util.Map<String, Object> m) { /* ... */ }
|
|
792
|
+
public java.util.Map<String, Object> toJsonMap() { /* ... */ }
|
|
793
|
+
|
|
794
|
+
public static final class Builder { /* same shape as above */ }
|
|
795
|
+
}
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
#### Module Exception
|
|
799
|
+
|
|
800
|
+
```java
|
|
801
|
+
package {{BASE_PACKAGE}}.{{modulename}};
|
|
802
|
+
|
|
803
|
+
import {{BASE_PACKAGE}}.error.SdkApiException;
|
|
804
|
+
|
|
805
|
+
public class {{Module}}Exception extends SdkApiException {
|
|
806
|
+
public {{Module}}Exception(int statusCode, String body, String method, String url) {
|
|
807
|
+
super(statusCode, body, method, url);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
public static class NotFound extends {{Module}}Exception { /* 404 */ }
|
|
811
|
+
public static class Conflict extends {{Module}}Exception { /* 409 */ }
|
|
812
|
+
}
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### Internal Implementation
|
|
816
|
+
|
|
817
|
+
#### URL Constants
|
|
818
|
+
|
|
819
|
+
```java
|
|
820
|
+
package {{BASE_PACKAGE}}.{{modulename}}.internal;
|
|
821
|
+
|
|
822
|
+
final class OrderPaths {
|
|
823
|
+
private OrderPaths() {}
|
|
824
|
+
|
|
825
|
+
static final String LIST = "/orders";
|
|
826
|
+
static final String BY_ID = "/orders/%s";
|
|
827
|
+
|
|
828
|
+
static String byId(String id) { return String.format(BY_ID, id); }
|
|
829
|
+
}
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
#### Service Implementation
|
|
833
|
+
|
|
834
|
+
```java
|
|
835
|
+
package {{BASE_PACKAGE}}.{{modulename}}.internal;
|
|
836
|
+
|
|
837
|
+
import okhttp3.HttpUrl;
|
|
838
|
+
import okhttp3.MediaType;
|
|
839
|
+
import okhttp3.OkHttpClient;
|
|
840
|
+
import okhttp3.Request;
|
|
841
|
+
import okhttp3.RequestBody;
|
|
842
|
+
import okhttp3.Response;
|
|
843
|
+
|
|
844
|
+
import {{BASE_PACKAGE}}.{{ClientName}}Config;
|
|
845
|
+
import {{BASE_PACKAGE}}.error.SdkClientException;
|
|
846
|
+
import {{BASE_PACKAGE}}.json.Json;
|
|
847
|
+
import {{BASE_PACKAGE}}.{{modulename}}.{{Module}}Service;
|
|
848
|
+
import {{BASE_PACKAGE}}.{{modulename}}.{{Module}}Exception;
|
|
849
|
+
import {{BASE_PACKAGE}}.{{modulename}}.model.OrderDto;
|
|
850
|
+
|
|
851
|
+
import java.io.IOException;
|
|
852
|
+
import java.util.Map;
|
|
853
|
+
|
|
854
|
+
public final class {{Module}}ServiceImpl implements {{Module}}Service {
|
|
855
|
+
|
|
856
|
+
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
|
|
857
|
+
|
|
858
|
+
private final OkHttpClient http;
|
|
859
|
+
private final {{ClientName}}Config config;
|
|
860
|
+
|
|
861
|
+
public {{Module}}ServiceImpl(OkHttpClient http, {{ClientName}}Config config) {
|
|
862
|
+
this.http = http;
|
|
863
|
+
this.config = config;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
@Override
|
|
867
|
+
public OrderDto getById(String id) {
|
|
868
|
+
HttpUrl url = HttpUrl.parse(config.baseUrl())
|
|
869
|
+
.newBuilder()
|
|
870
|
+
.addPathSegments("orders/" + id)
|
|
871
|
+
.build();
|
|
872
|
+
|
|
873
|
+
Request req = new Request.Builder()
|
|
874
|
+
.url(url)
|
|
875
|
+
.header("Accept", "application/json")
|
|
876
|
+
.get()
|
|
877
|
+
.build();
|
|
878
|
+
|
|
879
|
+
try (Response resp = http.newCall(req).execute()) {
|
|
880
|
+
if (resp.code() == 404) {
|
|
881
|
+
throw new {{Module}}Exception.NotFound(404, body(resp), "GET", url.toString());
|
|
882
|
+
}
|
|
883
|
+
if (!resp.isSuccessful()) {
|
|
884
|
+
throw new {{Module}}Exception(resp.code(), body(resp), "GET", url.toString());
|
|
885
|
+
}
|
|
886
|
+
@SuppressWarnings("unchecked")
|
|
887
|
+
Map<String, Object> map = (Map<String, Object>) Json.parse(body(resp));
|
|
888
|
+
return OrderDto.fromJsonMap(map);
|
|
889
|
+
} catch (IOException e) {
|
|
890
|
+
throw new SdkClientException("GET " + url + " failed", e);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
private static String body(Response resp) throws IOException {
|
|
895
|
+
return resp.body() != null ? resp.body().string() : "";
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// Implementations for list(...) and create(...) follow the same pattern.
|
|
899
|
+
}
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
### Error Mapping
|
|
903
|
+
|
|
904
|
+
```markdown
|
|
905
|
+
## Error Mapping
|
|
906
|
+
|
|
907
|
+
| HTTP Status | Exception | Notes |
|
|
908
|
+
|-------------|------------------------------------|--------------------------------------|
|
|
909
|
+
| 400 | {{Module}}Exception (root) | Validation error from server |
|
|
910
|
+
| 401 | SdkAuthException | Triggers 1× refresh-and-retry |
|
|
911
|
+
| 403 | SdkAuthException | Surfaced to caller |
|
|
912
|
+
| 404 | {{Module}}Exception.NotFound | |
|
|
913
|
+
| 409 | {{Module}}Exception.Conflict | |
|
|
914
|
+
| 429 | retried by RetryInterceptor | Exhaustion → SdkRetryExhaustedException |
|
|
915
|
+
| 5xx | retried by RetryInterceptor | Exhaustion → {{Module}}Exception |
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
### Test Plan
|
|
919
|
+
|
|
920
|
+
Derive scenarios from the PRD `### Test` items for this module. Each row maps to one
|
|
921
|
+
JUnit `@Test`.
|
|
922
|
+
|
|
923
|
+
```markdown
|
|
924
|
+
## Test Plan
|
|
925
|
+
|
|
926
|
+
| Test ID | Scenario | Mock Response |
|
|
927
|
+
|------------|-------------------------------------------------|----------------------------------------|
|
|
928
|
+
| TSDK00009 | getById on existing order | 200 + `{"id":"ORD-1","status":"NEW"}` |
|
|
929
|
+
| TSDK00010 | getById on missing order | 404 |
|
|
930
|
+
| TSDK00011 | list with status filter | 200 + array of 3 items |
|
|
931
|
+
| TSDK00012 | create with invalid amount | 400 + error body |
|
|
932
|
+
| TSDK00013 | retry on 503 | 503, 503, 200 |
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
### External References
|
|
936
|
+
|
|
937
|
+
```markdown
|
|
938
|
+
## External References
|
|
939
|
+
|
|
940
|
+
| Ref ID | Version | Title | URL |
|
|
941
|
+
|-----------|---------|----------------------------------|----------------------------------|
|
|
942
|
+
| REFSDK001 | v1.0.0 | Upstream Orders API docs | https://api.example.com/orders |
|
|
943
|
+
```
|
|
944
|
+
|
|
945
|
+
### Critical Rules for Module SPEC.md
|
|
946
|
+
|
|
947
|
+
1. Use actual field names from MODEL.md / OpenAPI schemas.
|
|
948
|
+
2. Use actual operation IDs and paths from the OpenAPI spec when available.
|
|
949
|
+
3. Define service methods mapping to actual user stories (by tag ID).
|
|
950
|
+
4. Map HTTP status codes to specific exception subclasses.
|
|
951
|
+
5. List user story IDs, NFR IDs, constraint IDs, test IDs, reference IDs with versions.
|
|
952
|
+
6. Include complete, continuous code samples — no `// ...` gaps.
|
|
953
|
+
7. Follow all SDK constraints (constructor injection, immutable models, no Lombok, no
|
|
954
|
+
reflection-based JSON).
|
|
955
|
+
8. Include "Removed / Replaced" subsection.
|
|
956
|
+
9. Test plan rows MUST trace back to PRD `### Test` items where possible.
|