ga-plugins-cli 0.1.0 → 0.1.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/dist/config-patcher.d.ts +20 -50
- package/dist/config-patcher.d.ts.map +1 -1
- package/dist/config-patcher.js +138 -102
- package/dist/config-patcher.js.map +1 -1
- package/dist/index.js +41 -5
- package/dist/index.js.map +1 -1
- package/dist/installer.d.ts +0 -18
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +19 -39
- package/dist/installer.js.map +1 -1
- package/dist/types.d.ts +10 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/uninstaller.d.ts +0 -23
- package/dist/uninstaller.d.ts.map +1 -1
- package/dist/uninstaller.js +22 -68
- package/dist/uninstaller.js.map +1 -1
- package/package.json +3 -2
- package/plugins/go-reviewer/.claude-plugin/plugin.json +12 -0
- package/plugins/go-reviewer/commands/go-review.md +424 -0
- package/plugins/go-reviewer/mcp-servers/go-reviewer-mcp/README.md +236 -0
- package/plugins/go-reviewer/mcp-servers/go-reviewer-mcp/main.go +678 -0
- package/plugins/go-scaffolder/.claude-plugin/plugin.json +12 -0
- package/plugins/go-scaffolder/commands/scaffold-service.md +802 -0
- package/plugins/go-scaffolder/reference-service/.env.example +27 -0
- package/plugins/go-scaffolder/reference-service/Dockerfile +55 -0
- package/plugins/go-scaffolder/reference-service/REFERENCE-SERVICE-NOTICE.md +104 -0
- package/plugins/go-scaffolder/reference-service/cmd/server/main.go +266 -0
- package/plugins/go-scaffolder/reference-service/config/config.go +67 -0
- package/plugins/go-scaffolder/reference-service/go.mod +17 -0
- package/plugins/go-scaffolder/reference-service/internal/domain/booking.go +118 -0
- package/plugins/go-scaffolder/reference-service/internal/handler/booking.go +242 -0
- package/plugins/go-scaffolder/reference-service/internal/handler/booking_test.go +451 -0
- package/plugins/go-scaffolder/reference-service/internal/repository/booking_postgres.go +124 -0
- package/plugins/go-scaffolder/reference-service/internal/usecase/booking.go +181 -0
- package/plugins/go-standards/.claude-plugin/plugin.json +22 -0
- package/plugins/go-standards/commands/go-standards-check.md +232 -0
- package/plugins/go-standards/skills/concurrency.md +336 -0
- package/plugins/go-standards/skills/config.md +267 -0
- package/plugins/go-standards/skills/error-handling.md +286 -0
- package/plugins/go-standards/skills/http-chi.md +390 -0
- package/plugins/go-standards/skills/logging-observability.md +340 -0
- package/plugins/go-standards/skills/naming-and-style.md +315 -0
- package/plugins/go-standards/skills/project-layout.md +313 -0
- package/plugins/go-standards/skills/testing.md +366 -0
- package/plugins/java2go-porter/.claude-plugin/plugin.json +21 -0
- package/plugins/java2go-porter/agents/analyzer.md +232 -0
- package/plugins/java2go-porter/agents/reviewer.md +241 -0
- package/plugins/java2go-porter/agents/test-pairer.md +365 -0
- package/plugins/java2go-porter/agents/translator.md +419 -0
- package/plugins/java2go-porter/commands/port-java-service.md +149 -0
- package/plugins/java2go-porter/skills/idiom-mapping.md +75 -0
- package/plugins/migration-safety/.claude-plugin/plugin.json +20 -0
- package/plugins/migration-safety/commands/gen-characterization-test.md +452 -0
- package/plugins/migration-safety/commands/strangler-plan.md +356 -0
- package/plugins/migration-safety/skills/strangler-fig.md +382 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
# Agent: translator
|
|
2
|
+
|
|
3
|
+
You are the **Translator** agent in the java2go-porter pipeline. Your job is to read `ANALYSIS.md` and the original Java source, then produce idiomatic Go draft files following the chi + go-ga-lib conventions used at Garuda Airlines. Every file you produce is a DRAFT. Every ambiguity becomes a `// TODO(human):` comment. You do NOT make architectural decisions — you surface them.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Inputs
|
|
8
|
+
|
|
9
|
+
- `<analysis-path>`: path to `ANALYSIS.md` produced by the analyzer agent
|
|
10
|
+
- `<java-path>`: original Java source directory (for reference during translation)
|
|
11
|
+
- `<go-service-name>`: target Go service name (e.g., `flight-ops-svc`)
|
|
12
|
+
- `<java-service-name>`: source Java service name (e.g., `booking-java-svc`)
|
|
13
|
+
- `<total-java-services>`: how many Java services are being merged into `<go-service-name>`
|
|
14
|
+
- `<output-path>`: root output directory (`<go-service-name>/draft-<java-service-name>/`)
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Mandatory File Header
|
|
19
|
+
|
|
20
|
+
Every Go file you produce MUST begin with this comment block:
|
|
21
|
+
|
|
22
|
+
```go
|
|
23
|
+
// AUTO-GENERATED DRAFT — requires human review
|
|
24
|
+
// Source: <java-service-name> (Java/Spring Boot)
|
|
25
|
+
// Target: <go-service-name> (Go, chi + go-ga-lib)
|
|
26
|
+
// Generated by: java2go-porter translator agent
|
|
27
|
+
// DO NOT MERGE until REVIEW.md is resolved and all tests pass.
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Idiom Mapping Reference
|
|
33
|
+
|
|
34
|
+
Before translating any pattern, consult `skills/idiom-mapping.md`. Use the DECIDED mapping if one exists. If the mapping is marked NEEDS DECISION, use the first listed option as the default AND add a `// TODO(human): NEEDS DECISION — see idiom-mapping.md: <pattern name>` comment at every usage site.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Output Structure
|
|
39
|
+
|
|
40
|
+
Write all files under `<output-path>/`. The layout must be:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
<output-path>/
|
|
44
|
+
cmd/
|
|
45
|
+
main.go # entry point: wire dependencies, start server
|
|
46
|
+
config/
|
|
47
|
+
config.go # env-tagged config struct
|
|
48
|
+
internal/
|
|
49
|
+
handler/
|
|
50
|
+
<resource>_handler.go # one file per REST resource group
|
|
51
|
+
router.go # chi router setup
|
|
52
|
+
usecase/
|
|
53
|
+
<resource>_usecase.go # one file per use case group
|
|
54
|
+
interfaces.go # usecase interfaces (for testability)
|
|
55
|
+
domain/
|
|
56
|
+
<entity>.go # domain structs (from JPA entities)
|
|
57
|
+
interfaces.go # repository interfaces
|
|
58
|
+
repository/
|
|
59
|
+
<entity>_repository.go # repository implementations using go-ga-lib DB
|
|
60
|
+
go.mod # module: github.com/zokypesch/go-ga-lib
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Translation Rules
|
|
66
|
+
|
|
67
|
+
### Rule 1: @RestController → chi Handler
|
|
68
|
+
|
|
69
|
+
For each endpoint in ANALYSIS.md Section 2:
|
|
70
|
+
|
|
71
|
+
```go
|
|
72
|
+
// internal/handler/<resource>_handler.go
|
|
73
|
+
|
|
74
|
+
package handler
|
|
75
|
+
|
|
76
|
+
import (
|
|
77
|
+
"encoding/json"
|
|
78
|
+
"net/http"
|
|
79
|
+
|
|
80
|
+
"github.com/go-chi/chi/v5"
|
|
81
|
+
"github.com/zokypesch/go-ga-lib/response"
|
|
82
|
+
"<module>/internal/usecase"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
type BookingHandler struct {
|
|
86
|
+
uc usecase.BookingUsecase
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
func NewBookingHandler(uc usecase.BookingUsecase) *BookingHandler {
|
|
90
|
+
return &BookingHandler{uc: uc}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// RegisterRoutes registers all booking-related routes on the given chi router.
|
|
94
|
+
func (h *BookingHandler) RegisterRoutes(r chi.Router) {
|
|
95
|
+
r.Post("/api/v1/bookings", h.CreateBooking)
|
|
96
|
+
r.Get("/api/v1/bookings/{id}", h.GetBooking)
|
|
97
|
+
// TODO(human): verify path prefixes match the Java @RequestMapping base path
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
func (h *BookingHandler) CreateBooking(w http.ResponseWriter, r *http.Request) {
|
|
101
|
+
var req CreateBookingRequest
|
|
102
|
+
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
103
|
+
response.Error(w, http.StatusBadRequest, "invalid request body")
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
// TODO(human): validate request fields — Java used @Valid; add validation here
|
|
107
|
+
result, err := h.uc.CreateBooking(r.Context(), req)
|
|
108
|
+
if err != nil {
|
|
109
|
+
// TODO(human): map domain errors to HTTP status codes — see REVIEW.md error mapping section
|
|
110
|
+
response.Error(w, http.StatusInternalServerError, err.Error())
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
response.JSON(w, http.StatusCreated, result)
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Rules:
|
|
118
|
+
- Use `response.JSON` and `response.Error` from go-ga-lib for all responses
|
|
119
|
+
- Map `@PathVariable` → `chi.URLParam(r, "paramName")`
|
|
120
|
+
- Map `@RequestParam` → `r.URL.Query().Get("paramName")`
|
|
121
|
+
- Map `@RequestBody` → `json.NewDecoder(r.Body).Decode(&req)`
|
|
122
|
+
- Every `@PreAuthorize` annotation → `// TODO(human): add authorization middleware for this route`
|
|
123
|
+
- Every `@Valid` annotation → `// TODO(human): add input validation (consider go-playground/validator)`
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### Rule 2: @Service → Usecase
|
|
128
|
+
|
|
129
|
+
```go
|
|
130
|
+
// internal/usecase/<resource>_usecase.go
|
|
131
|
+
|
|
132
|
+
package usecase
|
|
133
|
+
|
|
134
|
+
import (
|
|
135
|
+
"context"
|
|
136
|
+
|
|
137
|
+
"<module>/internal/domain"
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
// BookingUsecase defines the business operations for bookings.
|
|
141
|
+
// AUTO-GENERATED DRAFT — requires human review
|
|
142
|
+
type BookingUsecase interface {
|
|
143
|
+
CreateBooking(ctx context.Context, req CreateBookingRequest) (*domain.Booking, error)
|
|
144
|
+
CancelBooking(ctx context.Context, id string) error
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
type bookingUsecase struct {
|
|
148
|
+
repo domain.BookingRepository
|
|
149
|
+
flight domain.FlightClient
|
|
150
|
+
// TODO(human): if <total-java-services> > 1, verify that shared dependencies
|
|
151
|
+
// (e.g., flight client) are not duplicated across merged use cases.
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
func NewBookingUsecase(repo domain.BookingRepository, flight domain.FlightClient) BookingUsecase {
|
|
155
|
+
return &bookingUsecase{repo: repo, flight: flight}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Rules:
|
|
160
|
+
- Every `@Autowired` dependency → explicit constructor parameter
|
|
161
|
+
- Every `@Transactional` method → `// TODO(human): wrap in explicit DB transaction — see idiom-mapping.md: @Transactional`
|
|
162
|
+
- Static fields / Singleton state → `// TODO(human): SHARED STATE — verify thread-safety in Go (goroutine-safe?)`
|
|
163
|
+
- AOP aspects → `// TODO(human): Java had @Aspect <AspectName> here — replicate with middleware or explicit call`
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### Rule 3: @Repository → domain Interface + Repository Implementation
|
|
168
|
+
|
|
169
|
+
Domain interface (no DB import — pure Go):
|
|
170
|
+
|
|
171
|
+
```go
|
|
172
|
+
// internal/domain/interfaces.go
|
|
173
|
+
|
|
174
|
+
package domain
|
|
175
|
+
|
|
176
|
+
import "context"
|
|
177
|
+
|
|
178
|
+
// BookingRepository defines persistence operations for Booking.
|
|
179
|
+
// AUTO-GENERATED DRAFT — requires human review
|
|
180
|
+
type BookingRepository interface {
|
|
181
|
+
FindByID(ctx context.Context, id string) (*Booking, error)
|
|
182
|
+
Save(ctx context.Context, b *Booking) error
|
|
183
|
+
FindByFlightIDAndStatus(ctx context.Context, flightID string, status BookingStatus) ([]*Booking, error)
|
|
184
|
+
// TODO(human): verify all custom query methods from Java BookingRepository are covered
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Implementation using go-ga-lib:
|
|
189
|
+
|
|
190
|
+
```go
|
|
191
|
+
// internal/repository/booking_repository.go
|
|
192
|
+
|
|
193
|
+
package repository
|
|
194
|
+
|
|
195
|
+
import (
|
|
196
|
+
"context"
|
|
197
|
+
|
|
198
|
+
"github.com/zokypesch/go-ga-lib/database"
|
|
199
|
+
"<module>/internal/domain"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
type bookingRepository struct {
|
|
203
|
+
db *database.DB
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
func NewBookingRepository(db *database.DB) domain.BookingRepository {
|
|
207
|
+
return &bookingRepository{db: db}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
func (r *bookingRepository) FindByID(ctx context.Context, id string) (*domain.Booking, error) {
|
|
211
|
+
// TODO(human): write the SQL query — Java used Spring Data JPA findById()
|
|
212
|
+
// Suggested query: SELECT * FROM bookings WHERE id = $1
|
|
213
|
+
var b domain.Booking
|
|
214
|
+
if err := r.db.QueryRowContext(ctx, "SELECT id, status, flight_id, created_at FROM bookings WHERE id = $1", id).Scan(&b.ID, &b.Status, &b.FlightID, &b.CreatedAt); err != nil {
|
|
215
|
+
return nil, err
|
|
216
|
+
}
|
|
217
|
+
return &b, nil
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Rules for lazy-loading (from ANALYSIS.md Section 4.3):
|
|
222
|
+
- Every `FetchType.LAZY` relationship → do NOT embed in primary query
|
|
223
|
+
- Add `// TODO(human): LAZY LOADING — Java loaded <RelatedEntity> lazily; decide: (a) JOIN in same query, (b) separate query on demand, (c) always omit`
|
|
224
|
+
- Do NOT silently include the relation in the struct scan
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
### Rule 4: JPA Entity → Go Struct
|
|
229
|
+
|
|
230
|
+
```go
|
|
231
|
+
// internal/domain/booking.go
|
|
232
|
+
|
|
233
|
+
package domain
|
|
234
|
+
|
|
235
|
+
import "time"
|
|
236
|
+
|
|
237
|
+
// Booking is the domain model translated from BookingEntity.
|
|
238
|
+
// AUTO-GENERATED DRAFT — requires human review
|
|
239
|
+
type Booking struct {
|
|
240
|
+
ID string `json:"id" db:"id"`
|
|
241
|
+
Status BookingStatus `json:"status" db:"status"`
|
|
242
|
+
FlightID string `json:"flight_id" db:"flight_id"`
|
|
243
|
+
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
|
244
|
+
// TODO(human): BookingEntity had @OneToMany Passengers — omitted here (lazy-loading).
|
|
245
|
+
// Decide whether to include inline or fetch separately.
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
type BookingStatus string
|
|
249
|
+
|
|
250
|
+
const (
|
|
251
|
+
BookingStatusPending BookingStatus = "PENDING"
|
|
252
|
+
BookingStatusConfirmed BookingStatus = "CONFIRMED"
|
|
253
|
+
BookingStatusCancelled BookingStatus = "CANCELLED"
|
|
254
|
+
)
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Rules:
|
|
258
|
+
- Lombok `@Data` → plain struct (no generated methods) with `// TODO(human): add String(), Equal() if needed`
|
|
259
|
+
- `UUID` → `string` with `// TODO(human): consider using github.com/google/uuid`
|
|
260
|
+
- `LocalDateTime` / `ZonedDateTime` → `time.Time`
|
|
261
|
+
- `BigDecimal` → `float64` + `// TODO(human): consider shopspring/decimal for monetary values`
|
|
262
|
+
- Enum → Go `type <Name> string` with `const` block
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
### Rule 5: @Value / application.properties → Config Struct
|
|
267
|
+
|
|
268
|
+
```go
|
|
269
|
+
// config/config.go
|
|
270
|
+
|
|
271
|
+
package config
|
|
272
|
+
|
|
273
|
+
import (
|
|
274
|
+
"github.com/zokypesch/go-ga-lib/config"
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
// Config holds all configuration for <java-service-name> translated service.
|
|
278
|
+
// AUTO-GENERATED DRAFT — requires human review
|
|
279
|
+
type Config struct {
|
|
280
|
+
// Database
|
|
281
|
+
DatabaseURL string `env:"DATABASE_URL" required:"true"`
|
|
282
|
+
DatabaseMaxConns int `env:"DATABASE_MAX_CONNS" default:"10"`
|
|
283
|
+
|
|
284
|
+
// Business rules from application.properties
|
|
285
|
+
MaxSeatsPerBooking int `env:"MAX_SEATS_PER_BOOKING" default:"9"`
|
|
286
|
+
// TODO(human): verify this default matches the Java default: booking.max-seats-per-booking=9
|
|
287
|
+
|
|
288
|
+
// External services
|
|
289
|
+
FlightServiceURL string `env:"FLIGHT_SERVICE_URL" required:"true"`
|
|
290
|
+
|
|
291
|
+
// Server
|
|
292
|
+
Port string `env:"PORT" default:"8080"`
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Load reads config from environment using go-ga-lib.
|
|
296
|
+
func Load() (*Config, error) {
|
|
297
|
+
var cfg Config
|
|
298
|
+
if err := config.Load(&cfg); err != nil {
|
|
299
|
+
return nil, err
|
|
300
|
+
}
|
|
301
|
+
return &cfg, nil
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### Rule 6: cmd/main.go — Dependency Wiring
|
|
308
|
+
|
|
309
|
+
```go
|
|
310
|
+
// cmd/main.go
|
|
311
|
+
// AUTO-GENERATED DRAFT — requires human review
|
|
312
|
+
// Source: <java-service-name> (Java/Spring Boot)
|
|
313
|
+
|
|
314
|
+
package main
|
|
315
|
+
|
|
316
|
+
import (
|
|
317
|
+
"log"
|
|
318
|
+
"net/http"
|
|
319
|
+
|
|
320
|
+
"github.com/go-chi/chi/v5"
|
|
321
|
+
"github.com/go-chi/chi/v5/middleware"
|
|
322
|
+
"github.com/zokypesch/go-ga-lib/database"
|
|
323
|
+
"github.com/zokypesch/go-ga-lib/logger"
|
|
324
|
+
|
|
325
|
+
"<module>/config"
|
|
326
|
+
"<module>/internal/handler"
|
|
327
|
+
"<module>/internal/repository"
|
|
328
|
+
"<module>/internal/usecase"
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
func main() {
|
|
332
|
+
cfg, err := config.Load()
|
|
333
|
+
if err != nil {
|
|
334
|
+
log.Fatalf("failed to load config: %v", err)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
log := logger.New()
|
|
338
|
+
|
|
339
|
+
db, err := database.New(cfg.DatabaseURL)
|
|
340
|
+
if err != nil {
|
|
341
|
+
log.Fatal("failed to connect to database", "error", err)
|
|
342
|
+
}
|
|
343
|
+
defer db.Close()
|
|
344
|
+
|
|
345
|
+
// Repositories
|
|
346
|
+
bookingRepo := repository.NewBookingRepository(db)
|
|
347
|
+
// TODO(human): wire additional repositories from consolidated Java services
|
|
348
|
+
|
|
349
|
+
// External clients
|
|
350
|
+
// TODO(human): wire FlightClient HTTP client — go-ga-lib HTTP client pattern
|
|
351
|
+
// TODO(human): wire message broker if Kafka/RabbitMQ was used — see ANALYSIS.md Section 5
|
|
352
|
+
|
|
353
|
+
// Use cases
|
|
354
|
+
bookingUC := usecase.NewBookingUsecase(bookingRepo /*, flightClient */)
|
|
355
|
+
// TODO(human): wire additional use cases from consolidated Java services
|
|
356
|
+
|
|
357
|
+
// Handlers
|
|
358
|
+
bookingHandler := handler.NewBookingHandler(bookingUC)
|
|
359
|
+
|
|
360
|
+
r := chi.NewRouter()
|
|
361
|
+
r.Use(middleware.Logger)
|
|
362
|
+
r.Use(middleware.Recoverer)
|
|
363
|
+
r.Use(middleware.RequestID)
|
|
364
|
+
// TODO(human): add authentication middleware (JWT filter was present in Java — see ANALYSIS.md Section 9)
|
|
365
|
+
|
|
366
|
+
bookingHandler.RegisterRoutes(r)
|
|
367
|
+
// TODO(human): register routes for additional handlers from consolidated services
|
|
368
|
+
|
|
369
|
+
log.Info("starting server", "port", cfg.Port)
|
|
370
|
+
if err := http.ListenAndServe(":"+cfg.Port, r); err != nil {
|
|
371
|
+
log.Fatal("server error", "error", err)
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
### Rule 7: Consolidation Markers
|
|
379
|
+
|
|
380
|
+
If `<total-java-services>` > 1, add at the top of `cmd/main.go` and `internal/usecase/interfaces.go`:
|
|
381
|
+
|
|
382
|
+
```go
|
|
383
|
+
// TODO(human): CONSOLIDATION — this draft covers 1 of <total-java-services> Java services
|
|
384
|
+
// being merged into <go-service-name>. Do not finalize wiring until all
|
|
385
|
+
// <total-java-services> drafts are reviewed together.
|
|
386
|
+
// Consolidation decisions (shared domain models, merged DB schemas, unified error codes)
|
|
387
|
+
// are NOT made by this tool — they require architectural review.
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
392
|
+
### Rule 8: go.mod
|
|
393
|
+
|
|
394
|
+
```
|
|
395
|
+
module github.com/zokypesch/go-ga-lib/<go-service-name>
|
|
396
|
+
|
|
397
|
+
go 1.22
|
|
398
|
+
|
|
399
|
+
require (
|
|
400
|
+
github.com/go-chi/chi/v5 v5.0.12
|
|
401
|
+
github.com/zokypesch/go-ga-lib v0.0.0 // TODO(human): pin to correct version
|
|
402
|
+
github.com/stretchr/testify v1.9.0
|
|
403
|
+
// TODO(human): add other dependencies as needed
|
|
404
|
+
)
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
## Completion
|
|
410
|
+
|
|
411
|
+
After writing all files, print:
|
|
412
|
+
|
|
413
|
+
```
|
|
414
|
+
TRANSLATOR COMPLETE
|
|
415
|
+
Files written to: <output-path>/
|
|
416
|
+
TODO(human) comments added: <count>
|
|
417
|
+
NEEDS DECISION items (from idiom-mapping.md): <list patterns that were NEEDS DECISION>
|
|
418
|
+
Proceed to test-pairer agent.
|
|
419
|
+
```
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Command: port-java-service
|
|
2
|
+
|
|
3
|
+
Translate a Java/Spring Boot service folder into a Go draft using the chi router and go-ga-lib framework. Output is a DRAFT only — it requires human review and green tests before any merge.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Step 1: Gather Inputs
|
|
8
|
+
|
|
9
|
+
### 1.1 Java Service Path
|
|
10
|
+
|
|
11
|
+
If `<java-path>` was passed as an argument, use it directly. Otherwise, ask the user:
|
|
12
|
+
|
|
13
|
+
> "Please provide the path to the Java/Spring Boot service folder you want to port (e.g., `services/booking-svc` or an absolute path)."
|
|
14
|
+
|
|
15
|
+
Validate that the path exists and contains at least one `.java` file. If not, inform the user and ask again.
|
|
16
|
+
|
|
17
|
+
### 1.2 Target Go Service Name
|
|
18
|
+
|
|
19
|
+
Ask the user:
|
|
20
|
+
|
|
21
|
+
> "What is the name of the target Go service this Java code will be merged into?
|
|
22
|
+
> (Because 10 Java services consolidate into 1 Go service, we need to know the destination Go service name.
|
|
23
|
+
> Example: `flight-ops-svc`)"
|
|
24
|
+
|
|
25
|
+
Record the answer as `<go-service-name>`. This becomes the top-level output folder.
|
|
26
|
+
|
|
27
|
+
### 1.3 Consolidation Context
|
|
28
|
+
|
|
29
|
+
Ask the user:
|
|
30
|
+
|
|
31
|
+
> "Is this one of multiple Java services being ported into the same Go service?
|
|
32
|
+
> - If YES: how many Java services total will be merged into `<go-service-name>`? (e.g., 10)
|
|
33
|
+
> - If NO: just this one Java service is mapping to the Go service."
|
|
34
|
+
|
|
35
|
+
Record the total count as `<total-java-services>`. If the user says YES, note this context so the translator can leave appropriate `// TODO(human): consolidation` markers for shared logic.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Step 2: Derive Names and Paths
|
|
40
|
+
|
|
41
|
+
- `<java-service-name>`: the leaf folder name of `<java-path>` (e.g., `booking-java-svc`)
|
|
42
|
+
- Output root: `<go-service-name>/draft-<java-service-name>/`
|
|
43
|
+
|
|
44
|
+
Example: if go-service-name is `flight-ops-svc` and java-service-name is `booking-java-svc`, output goes into `flight-ops-svc/draft-booking-java-svc/`.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Step 3: Run the Pipeline
|
|
49
|
+
|
|
50
|
+
Execute the following agents **in sequence**. Each agent reads the previous agent's output. Do not skip any step.
|
|
51
|
+
|
|
52
|
+
### 3.1 Analyzer Agent
|
|
53
|
+
|
|
54
|
+
Invoke the `analyzer` agent against `<java-path>`.
|
|
55
|
+
|
|
56
|
+
The analyzer will:
|
|
57
|
+
- Read the Java source tree under `<java-path>`
|
|
58
|
+
- Produce `<go-service-name>/draft-<java-service-name>/ANALYSIS.md`
|
|
59
|
+
|
|
60
|
+
Wait for the analyzer to complete and confirm `ANALYSIS.md` was written before proceeding.
|
|
61
|
+
|
|
62
|
+
### 3.2 Translator Agent
|
|
63
|
+
|
|
64
|
+
Invoke the `translator` agent with:
|
|
65
|
+
- Input: `<go-service-name>/draft-<java-service-name>/ANALYSIS.md` and the original Java source at `<java-path>`
|
|
66
|
+
- Context: `<go-service-name>`, `<java-service-name>`, `<total-java-services>`
|
|
67
|
+
|
|
68
|
+
The translator will:
|
|
69
|
+
- Read `ANALYSIS.md` and the Java source
|
|
70
|
+
- Consult `skills/idiom-mapping.md` for every translation decision
|
|
71
|
+
- Write Go source files into `<go-service-name>/draft-<java-service-name>/`:
|
|
72
|
+
- `internal/handler/` — chi HTTP handlers
|
|
73
|
+
- `internal/usecase/` — business logic
|
|
74
|
+
- `internal/domain/` — interfaces and domain structs
|
|
75
|
+
- `internal/repository/` — repository implementations
|
|
76
|
+
- `config/config.go` — environment-tagged config struct
|
|
77
|
+
- `cmd/main.go` — entry point wiring
|
|
78
|
+
- Every file begins with: `// AUTO-GENERATED DRAFT — requires human review`
|
|
79
|
+
- Every ambiguity is marked `// TODO(human): <description>`
|
|
80
|
+
|
|
81
|
+
Wait for the translator to complete before proceeding.
|
|
82
|
+
|
|
83
|
+
### 3.3 Test-Pairer Agent
|
|
84
|
+
|
|
85
|
+
Invoke the `test-pairer` agent with:
|
|
86
|
+
- Input: Java source at `<java-path>` + all Go files in `<go-service-name>/draft-<java-service-name>/`
|
|
87
|
+
|
|
88
|
+
The test-pairer will:
|
|
89
|
+
- Generate `*_test.go` files alongside the corresponding Go files
|
|
90
|
+
- Produce table-driven characterization tests
|
|
91
|
+
- Mark tests that need real Java runtime data with `t.Skip("NEEDS GOLDEN DATA: ...")`
|
|
92
|
+
|
|
93
|
+
Wait for the test-pairer to complete before proceeding.
|
|
94
|
+
|
|
95
|
+
### 3.4 Reviewer Agent
|
|
96
|
+
|
|
97
|
+
Invoke the `reviewer` agent with:
|
|
98
|
+
- Input: all files in `<go-service-name>/draft-<java-service-name>/`
|
|
99
|
+
|
|
100
|
+
The reviewer will:
|
|
101
|
+
- Produce `<go-service-name>/draft-<java-service-name>/REVIEW.md`
|
|
102
|
+
- Categorize all risks into MUST DECIDE / SHOULD VERIFY / LOW RISK
|
|
103
|
+
- Include a human sign-off checklist
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Step 4: Summarize Output
|
|
108
|
+
|
|
109
|
+
After all agents complete, print a directory summary. Example:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Draft created at: flight-ops-svc/draft-booking-java-svc/
|
|
113
|
+
ANALYSIS.md (analyzer output)
|
|
114
|
+
REVIEW.md (reviewer output — START HERE)
|
|
115
|
+
internal/handler/ (chi handlers)
|
|
116
|
+
internal/usecase/ (business logic)
|
|
117
|
+
internal/domain/ (interfaces + structs)
|
|
118
|
+
internal/repository/ (repository implementations)
|
|
119
|
+
config/config.go (env-tagged config)
|
|
120
|
+
cmd/main.go (entry point)
|
|
121
|
+
internal/handler/*_test.go
|
|
122
|
+
internal/usecase/*_test.go
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Step 5: Final Warning Message
|
|
128
|
+
|
|
129
|
+
End the command with this exact message (substituting values):
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
==========================================================================
|
|
133
|
+
DRAFT ONLY — DO NOT MERGE UNTIL ALL THREE CONDITIONS ARE MET:
|
|
134
|
+
|
|
135
|
+
(a) A human has read REVIEW.md and resolved every item in MUST DECIDE.
|
|
136
|
+
(b) All *_test.go files pass (go test ./...) with no skipped golden-data
|
|
137
|
+
tests still unresolved.
|
|
138
|
+
(c) The team has completed the consolidation plan for <go-service-name>
|
|
139
|
+
(this is <N> of <total-java-services> Java services being merged —
|
|
140
|
+
consolidation decisions are NOT made by this tool).
|
|
141
|
+
|
|
142
|
+
Next suggested steps:
|
|
143
|
+
1. Open <go-service-name>/draft-<java-service-name>/REVIEW.md
|
|
144
|
+
2. Resolve every MUST DECIDE item with your team
|
|
145
|
+
3. Capture golden data for any t.Skip("NEEDS GOLDEN DATA") tests
|
|
146
|
+
4. Run: go test ./... inside the draft folder
|
|
147
|
+
5. Schedule consolidation review with the architecture team
|
|
148
|
+
==========================================================================
|
|
149
|
+
```
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Skill: idiom-mapping
|
|
2
|
+
|
|
3
|
+
Java→Go translation reference for the java2go-porter pipeline at Garuda Airlines.
|
|
4
|
+
Module: `github.com/zokypesch/go-ga-lib`. Router: `chi`. Target: idiomatic Go 1.22+.
|
|
5
|
+
|
|
6
|
+
This file is consumed by the **translator** and **reviewer** agents. For every Java pattern encountered, find the row below. If the Decision Status is DECIDED, use the Go Equivalent column as-is. If it is NEEDS DECISION, use Option A as the default AND add a `// TODO(human): NEEDS DECISION — idiom-mapping: <Row Name>` comment at every usage site.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Mapping Table
|
|
11
|
+
|
|
12
|
+
| # | Java Pattern | Go Equivalent | Decision Status | Notes / Options |
|
|
13
|
+
|---|-------------|---------------|----------------|----------------|
|
|
14
|
+
| 1 | `@Service` / `@Component` | Plain Go struct with a constructor function `func NewXxx(deps...) *Xxx` | DECIDED | No magic. DI is explicit via constructor parameters. Register in `cmd/main.go`. |
|
|
15
|
+
| 2 | `@Repository` | Interface in `internal/domain/interfaces.go` + struct implementation in `internal/repository/` using go-ga-lib DB client | DECIDED | Interface lives in domain (no DB import). Implementation imports go-ga-lib. Keeps domain package dependency-free. |
|
|
16
|
+
| 3 | `@RestController` + `@RequestMapping` | chi router + handler struct in `internal/handler/`. `RegisterRoutes(r chi.Router)` method on handler. | DECIDED | Use `github.com/go-chi/chi/v5`. Group routes by resource. |
|
|
17
|
+
| 4 | `@GetMapping` / `@PostMapping` / `@PutMapping` / `@DeleteMapping` / `@PatchMapping` | `r.Get(path, handler)` / `r.Post(...)` / `r.Put(...)` / `r.Delete(...)` / `r.Patch(...)` | DECIDED | Path params: `{id}` in chi, read with `chi.URLParam(r, "id")`. |
|
|
18
|
+
| 5 | `@PathVariable` | `chi.URLParam(r, "paramName")` | DECIDED | Always check for empty string — chi returns `""` if param is not in route pattern. |
|
|
19
|
+
| 6 | `@RequestParam` | `r.URL.Query().Get("paramName")` | DECIDED | For required params, return 400 if empty. For optional, use default value. |
|
|
20
|
+
| 7 | `@RequestBody` | `json.NewDecoder(r.Body).Decode(&req)` | DECIDED | Always check decode error. Use `http.MaxBytesReader` to limit body size. |
|
|
21
|
+
| 8 | `@Autowired` (field injection) | Constructor parameter — no field injection in Go | DECIDED | `// TODO(human): NEEDS DECISION` is NOT needed here — always use constructor injection. |
|
|
22
|
+
| 9 | `@Autowired` (constructor injection) | Same pattern — Go constructor parameter | DECIDED | Direct 1:1 translation. |
|
|
23
|
+
| 10 | `@Value("${key}")` | Field in `Config` struct with `env:"KEY"` tag, loaded by go-ga-lib config loader | DECIDED | All config lives in `config/config.go`. Use `required:"true"` for mandatory keys. Use `default:"value"` for optional. |
|
|
24
|
+
| 11 | `Optional<T>` | `(T, bool)` tuple for simple presence checks; `*T` (pointer) + nil check for domain types | NEEDS DECISION | Option A: `(*T, error)` — idiomatic Go, most common. Option B: `(T, bool)` — explicit, no error info. Option C: keep pointer `*T`, nil means absent. **Trade-off**: Option A is preferred for usecase methods; Option B for simple lookups. `// TODO(human): NEEDS DECISION — idiom-mapping: Optional<T>` |
|
|
25
|
+
| 12 | `Stream().filter().map().collect()` | `for` loop with slice append, or a helper using generics (`slices.Map`, `slices.Filter`) | NEEDS DECISION | Option A: plain `for` loop — most readable, zero dependencies, easiest to debug. Option B: `golang.org/x/exp/slices` functional helpers. Option C: custom helper in go-ga-lib. **Trade-off**: Option A is strongly preferred for maintainability. Only use Option B/C if the pipeline is long and nested. `// TODO(human): NEEDS DECISION — idiom-mapping: Stream.filter.map` |
|
|
26
|
+
| 13 | `try { ... } catch (Exception e) { ... } finally { ... }` | `if err != nil { return err }` for error handling; `defer` for cleanup | DECIDED | Go uses explicit error returns, not exceptions. `finally` → `defer`. Never use `panic` for business logic errors. |
|
|
27
|
+
| 14 | `@Transactional` (method-level) | Explicit: `tx, err := db.BeginTx(ctx, nil)` → `defer tx.Rollback()` → `tx.Commit()` at end | NEEDS DECISION | Option A: wrap method body in explicit Begin/Commit/Rollback — most explicit, copy Java semantics. Option B: use go-ga-lib transaction helper (if available). Option C: outbox pattern for distributed scenarios. **Trade-off**: Option A is the safe default. Option C is needed if @Transactional was spanning a DB write + event publish. `// TODO(human): NEEDS DECISION — idiom-mapping: @Transactional` |
|
|
28
|
+
| 15 | `@Transactional(propagation = REQUIRES_NEW)` | Nested transaction: `tx.SavePoint()` or new `db.BeginTx()` in a goroutine with isolated DB conn | NEEDS DECISION | Go DB driver support for savepoints varies. `// TODO(human): NEEDS DECISION — idiom-mapping: @Transactional REQUIRES_NEW` |
|
|
29
|
+
| 16 | Builder pattern (`SomeClass.builder().field(v).build()`) | Functional options: `func WithField(v T) Option { return func(s *S) { s.field = v } }` OR explicit struct literal `SomeStruct{Field: v}` | NEEDS DECISION | Option A: explicit struct literal — simplest, idiomatic Go, preferred when struct is in same package. Option B: functional options — good for structs with many optional fields and external callers. `// TODO(human): NEEDS DECISION — idiom-mapping: Builder pattern` |
|
|
30
|
+
| 17 | Inheritance / `class Child extends Parent` | Interface embedding + struct composition. `Child` struct embeds `*Parent` struct and implements same interface. | DECIDED | Go has no inheritance. Use composition. Every "is-a" relationship becomes "has-a" + "implements interface". |
|
|
31
|
+
| 18 | Abstract class (with some concrete methods) | Interface (for the abstract contract) + embedded base struct (for the shared concrete methods) | DECIDED | Example: `type BaseHandler struct { logger *zap.Logger }` embedded in concrete handler structs. |
|
|
32
|
+
| 19 | `instanceof` check | Type assertion `v, ok := x.(ConcreteType)` for single type; `switch v := x.(type) { case *A: ... }` for multiple | DECIDED | Prefer type switch for multiple cases. Always handle the `default` case. |
|
|
33
|
+
| 20 | `@Async` on method | `go func() { ... }()` with error propagation via channel or `golang.org/x/sync/errgroup` | NEEDS DECISION | Option A: fire-and-forget goroutine — simple but errors are lost. Option B: `errgroup.Group` — errors collected, panics recovered. Option C: work queue / job system via go-ga-lib. **Trade-off**: Option B is strongly preferred unless truly fire-and-forget. `// TODO(human): NEEDS DECISION — idiom-mapping: @Async` |
|
|
34
|
+
| 21 | `@Scheduled(fixedRate = N)` | `time.NewTicker(duration)` in a goroutine, started in `cmd/main.go`, stopped via context cancellation | DECIDED | Always pass `ctx context.Context` into the ticker goroutine. Use `select { case <-ctx.Done(): return; case <-ticker.C: ... }` pattern. |
|
|
35
|
+
| 22 | JPA `@OneToMany` | Explicit JOIN query in repository, returns slice in parent struct OR separate query on demand | NEEDS DECISION | Option A: JOIN and hydrate parent struct with slice field — single query, larger result set. Option B: N+1 guard — load parent, then call `ListChildrenByParentID()` explicitly at call sites. **Trade-off**: Option B is safer to start; Option A optimizes later. `// TODO(human): NEEDS DECISION — idiom-mapping: @OneToMany` |
|
|
36
|
+
| 23 | JPA `@ManyToOne` | Foreign key field in Go struct + explicit JOIN or separate lookup | DECIDED | Store the FK as `ParentID string`. Include `Parent *ParentType` only if always joined. Never load lazily by default. |
|
|
37
|
+
| 24 | JPA lazy loading (`FetchType.LAZY`) | Separate query on demand, called explicitly at call sites that need the data | DECIDED | Never auto-load. Mark all sites with `// TODO(human): LAZY LOADING — add explicit query here`. |
|
|
38
|
+
| 25 | JPA `@Query("SELECT ...")` custom JPQL | Raw SQL in repository implementation using go-ga-lib DB client | DECIDED | Convert JPQL to standard SQL. Use positional parameters (`$1`, `$2` for PostgreSQL). Verify column names match actual schema. |
|
|
39
|
+
| 26 | Spring `@ExceptionHandler` in `@ControllerAdvice` | chi middleware that wraps all handlers + sentinel error type check in middleware | DECIDED | Pattern: define domain errors as sentinel `var ErrXxx = errors.New("...")`. Handler returns error up. Middleware maps errors to HTTP status. See go-ga-lib response package for `response.Error(w, status, msg)`. |
|
|
40
|
+
| 27 | Lombok `@Data` (generates getter/setter/equals/hashCode/toString) | Plain Go struct with exported fields. Add `String()` method manually only if needed. | DECIDED | No generated code. Export fields directly. If `equals()` semantics matter (e.g., in tests), implement `Equal(other T) bool` method explicitly. |
|
|
41
|
+
| 28 | Lombok `@Builder` | See row 16 (Builder pattern). | NEEDS DECISION | Same options as row 16. |
|
|
42
|
+
| 29 | Lombok `@Slf4j` + `log.info("msg {}", arg)` | `zap.Logger` with structured fields: `logger.Info("msg", zap.String("key", arg))` | DECIDED | Use go-ga-lib logger package to construct `*zap.Logger`. Pass logger as constructor parameter — do NOT use global logger. |
|
|
43
|
+
| 30 | `log.error("msg", exception)` | `logger.Error("msg", zap.Error(err))` | DECIDED | Always pass `zap.Error(err)` as the last field for errors. |
|
|
44
|
+
| 31 | `ApplicationContext.getBean(SomeService.class)` | Not translatable to a runtime lookup. Must be replaced with explicit constructor injection. | MUST DECIDE | This is dynamic bean lookup — no Go equivalent. Every usage site is a `// TODO(human): MUST DECIDE — ApplicationContext.getBean() has no Go equivalent; wire dependency explicitly`. |
|
|
45
|
+
| 32 | `@Scope("prototype")` (new instance per injection) | `NewXxx()` called at each usage site — constructors are cheap in Go | DECIDED | Go structs have no scope concept. If a new instance is needed per request, call the constructor inside the handler function, not once at startup. |
|
|
46
|
+
| 33 | Spring Security `@PreAuthorize("hasRole('ADMIN')")` | chi middleware checking JWT claims, applied per-route or per-group | NEEDS DECISION | Option A: chi `r.With(authMiddleware).Get(...)` per route. Option B: route group with `r.Group(func(r chi.Router) { r.Use(authMiddleware); ... })`. Option C: go-ga-lib auth middleware if available. `// TODO(human): NEEDS DECISION — idiom-mapping: @PreAuthorize` |
|
|
47
|
+
| 34 | Spring Security JWT filter (global) | chi `r.Use(jwtMiddleware)` at router root, before all route registrations | DECIDED | Middleware stack order matters in chi — apply auth middleware BEFORE business route registration. |
|
|
48
|
+
| 35 | `ResponseEntity<T>` with dynamic status | `response.JSON(w, statusCode, body)` from go-ga-lib response package | DECIDED | Status code is set explicitly. No wrapper type needed. |
|
|
49
|
+
| 36 | `@Valid` / `javax.validation` annotations | Manual validation in handler or use `github.com/go-playground/validator` | NEEDS DECISION | Option A: explicit `if req.Field == ""` checks in handler — simple, no dependency. Option B: `validator.v10` struct tags — declarative, mirrors Java `@NotNull`, `@Size`. `// TODO(human): NEEDS DECISION — idiom-mapping: @Valid` |
|
|
50
|
+
| 37 | `@Cacheable` | In-memory cache with `sync.Map` or `github.com/patrickmn/go-cache`; or Redis via go-ga-lib | NEEDS DECISION | Option A: `sync.Map` — built-in, no TTL, simple. Option B: `go-cache` — TTL support, thread-safe. Option C: Redis via go-ga-lib — distributed, consistent across instances. **Trade-off**: If Java cache was local only (single JVM), Option A or B. If multiple Go pods, Option C required. `// TODO(human): NEEDS DECISION — idiom-mapping: @Cacheable` |
|
|
51
|
+
| 38 | `BigDecimal` (monetary / high-precision) | `github.com/shopspring/decimal.Decimal` | NEEDS DECISION | Option A: `shopspring/decimal` — safe for money, immutable, serializable. Option B: `float64` — simple but unsafe for monetary arithmetic (rounding errors). **Garuda context**: aviation fares are monetary values — strongly prefer Option A. `// TODO(human): NEEDS DECISION — idiom-mapping: BigDecimal` |
|
|
52
|
+
| 39 | `UUID` (Java) | `string` (simplest) or `github.com/google/uuid.UUID` (type-safe) | NEEDS DECISION | Option A: `string` — simple, compatible with JSON/DB as-is. Option B: `uuid.UUID` type — type safety, validates format. `// TODO(human): NEEDS DECISION — idiom-mapping: UUID` |
|
|
53
|
+
| 40 | `LocalDateTime` / `ZonedDateTime` | `time.Time` (always store and compare in UTC) | DECIDED | Go `time.Time` includes timezone. Always use `time.UTC` when constructing. When reading from DB: ensure `parseTime=true` in DSN (PostgreSQL handles this natively). |
|
|
54
|
+
| 41 | `enum` with methods (Java) | `type StatusName string` + `const` block + methods on the type | DECIDED | String-backed enum. Add `IsValid() bool` method if validation is needed. Never use `iota` for string enums — makes DB serialization explicit. |
|
|
55
|
+
| 42 | `@Aspect` (AOP) | No direct equivalent. Depending on what the aspect does: logging → middleware or explicit call; transactions → see row 14; auth → middleware | NEEDS DECISION | Each aspect must be evaluated individually. Common cases: (a) logging aspect → add structured log lines at method start/end, or use go-ga-lib observability middleware; (b) transaction aspect → explicit tx wrapping (row 14); (c) retry aspect → `github.com/avast/retry-go`. `// TODO(human): NEEDS DECISION — idiom-mapping: @Aspect; specify what this aspect did` |
|
|
56
|
+
| 43 | Static fields / class-level state (`static`) | Package-level `var` (avoid) or injectable singleton struct field | NEEDS DECISION | Option A: package-level `var` — simple but hidden global state, hard to test. Option B: field on a struct, initialized once in constructor and injected — testable, explicit. **Strongly prefer Option B**. If the Java static field was a cache or counter, protect with `sync.Mutex` or `sync.RWMutex`. `// TODO(human): NEEDS DECISION — idiom-mapping: static field` |
|
|
57
|
+
| 44 | Spring `@EventListener` / `ApplicationEventPublisher` | Explicit function call, channel, or go-ga-lib event bus | NEEDS DECISION | Option A: direct function call in same goroutine — simplest, synchronous. Option B: goroutine + channel — async, like @Async. Option C: go-ga-lib event bus / Kafka producer. **Context**: if events crossed service boundaries in Java, Option C is required. `// TODO(human): NEEDS DECISION — idiom-mapping: ApplicationEventPublisher` |
|
|
58
|
+
| 45 | Spring Retry (`@Retryable`) | `github.com/avast/retry-go` or manual retry loop with backoff | NEEDS DECISION | Option A: `retry-go` library — declarative, similar to @Retryable. Option B: manual loop with `time.Sleep` backoff — more control. `// TODO(human): NEEDS DECISION — idiom-mapping: @Retryable` |
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Decision Legend
|
|
63
|
+
|
|
64
|
+
| Status | Meaning |
|
|
65
|
+
|--------|---------|
|
|
66
|
+
| DECIDED | Use the Go Equivalent column. No `// TODO(human):` needed for the mapping itself (there may still be site-specific TODOs). |
|
|
67
|
+
| NEEDS DECISION | Default to Option A AND add `// TODO(human): NEEDS DECISION — idiom-mapping: <Row Name>` at every usage site. The human must confirm the choice before merging. |
|
|
68
|
+
| MUST DECIDE | No valid default. Do NOT generate code. Add `// TODO(human): MUST DECIDE — idiom-mapping: <Row Name> — see options` and leave a stub. |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Usage in Agents
|
|
73
|
+
|
|
74
|
+
- **Translator agent**: before translating any Java pattern, search this table. Apply DECIDED mappings directly. Apply NEEDS DECISION mappings with Option A + comment. Escalate MUST DECIDE to a stub.
|
|
75
|
+
- **Reviewer agent**: scan the Go draft for any pattern that should have a `// TODO(human): NEEDS DECISION` comment and verify it is present. If missing, add it to REVIEW.md MUST DECIDE section.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "migration-safety",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Characterization/contract tests capturing old Java behavior + Strangler Fig guidance for incremental traffic cutover.",
|
|
5
|
+
"commands": [
|
|
6
|
+
{
|
|
7
|
+
"name": "gen-characterization-test",
|
|
8
|
+
"description": "Generate tests capturing the OLD Java service behavior for comparison with the new Go service",
|
|
9
|
+
"path": "commands/gen-characterization-test.md"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"name": "strangler-plan",
|
|
13
|
+
"description": "Produce an incremental Strangler Fig traffic-cutover checklist for a domain",
|
|
14
|
+
"path": "commands/strangler-plan.md"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"skills": [
|
|
18
|
+
{ "name": "strangler-fig", "path": "skills/strangler-fig.md" }
|
|
19
|
+
]
|
|
20
|
+
}
|