Repository navigation
Expand file tree
/
Copy pathMakefile
More file actions
840 lines (685 loc) · 49.8 KB
/
Copy pathMakefile
File metadata and controls
840 lines (685 loc) · 49.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
.PHONY: t2-candidates-check setup install-browsers install-browsers-ci mod-tidy test test-go test-go-core test-harness test-go-ci test-save-integration test-deployment-backup validate-migrations test-client test-browser test-browser-ci test-game-ui-composed test-game-ui-performance typecheck build-client build-gameserver build-gameserver-linux-amd64 build-deployment-backup-linux-amd64 build-deployment-release-linux-amd64 create-release-builder build-gameserver-image deployment-config-check stage-release-content render-release-compose generate-release-metadata assemble-release-bundle release-secret-scan vectors vectors-check vectors-check-ci replay-fixture replay-fixture-check pitch-corpus pitch-corpus-check typer-corpus typer-corpus-check arcade-corpus arcade-corpus-check garden-corpus garden-corpus-check formulas formulas-check api-generate api-schema api-pin api-check harness harness-check harness-observe harness-observation-check relevance-registered-observe harness-guard-check content-harness epoch7-content-harness first-content-harness first-hour-harness t0-t1-role-check t0-t1-relevance t1-relevance relevance-branches t0-t1-branch-check t0-t1-branch-check-from-reports t0-t1-upgrade-check t0-t1-relevance-all relevance-beam commons-harness-check harness-update epoch-hash game-ui-copy-candidate game-ui-copy-candidate-check copy-generate copy-check publication-authority-check publication-authority-fresh-clone-check vet fuzz fuzz-ci verify-schema verify-routes-boundary verify-commons-boundary verify-client-boundary verify-kernel-version verify-ci-topology verify-combat-boundary verify-meters-boundary verify-achievements-boundary verify-server verify-server-core verify-harness-fast verify-harness verify-server-ci verify-harness-ci verify-client verify-game-ui verify-push verify
.PHONY: test-deployment-release test-deployment-operations test-deployment-rehearsal deployment-rehearsal-probe deployment-rehearsal-observe-host deployment-rehearsal-install-candidate deployment-rehearsal-run-browser deployment-rehearsal-recover-empty deployment-rehearsal-recover-populated deployment-rehearsal-lifecycle-release deployment-rehearsal-lifecycle-rollback deployment-rehearsal-supply-chain deployment-rehearsal-forge-proof build-deployment-rehearsal build-deployment-browser-linux-amd64 build-deployment-operations-linux-amd64 build-deployment-rehearsal-linux-amd64 generate-image-sbom
.PHONY: reputation-activation-corpus reputation-input-shape-corpus reputation-offer-corpus reputation-boundary-corpus
.PHONY: test-operations-alertmanager-native test-operations-native test-browser-focused test-browser-functional test-browser-ci-focused test-deployment-operations-profile
.PHONY: test-deployment-cleanup-runtime
.PHONY: test-deployment-storage-profile
.PHONY: test-deployment-restart-observer
.PHONY: test-deployment-restart-profile
.PHONY: test-deployment-backup-profile
.PHONY: test-deployment-ingress-profile
.PHONY: verify-combat-go-boundary
# Keep ordinary Go builds inside the writable repository sandbox. Override either
# variable when a developer deliberately wants another cache or a focused package set.
REPO_CACHE_DIR ?= $(CURDIR)/.cache
export GOCACHE ?= $(REPO_CACHE_DIR)/go-build
GO_PACKAGES ?= ./...
GO_TEST_FLAGS ?=
RELEASE_BUILDX_BUILDER ?= cloud-clicker-release-v1
RELEASE_BUILDKIT_IMAGE := moby/buildkit:v0.24.0@sha256:6eceb8971ce4fceb3daca562832642706238b7eea72941fcf9896c93c3c4a53e
SYFT_IMAGE := anchore/syft:v1.51.0@sha256:678bfa565b60f747aac0f8e964fe5588a24445b8d0a480e91f6efd70020dfbb0
PLAYWRIGHT_IMAGE := mcr.microsoft.com/playwright:v1.62.0-noble@sha256:02bbb2155cd7109e3e9c741941097ed1608cf8b6fa44ee2595896da2bdc1f471
PROMETHEUS_RULE_IMAGE := prom/prometheus:v3.12.0@sha256:69f5241418838263316593f7274a304b095c40bcf22e57272865da91bd60a8ac
CORE_TEST_COUNT ?= 1
HARNESS_TEST_COUNT ?= 1
HARNESS_WORKERS ?= 4
HARNESS_OBSERVATION ?=
HARNESS_CPU_PROFILE ?=
HARNESS_PROFILE_FLAGS ?= -top -nodecount=25
REGISTERED_RELEVANCE_ENTRY ?=
REGISTERED_RELEVANCE_OUTPUT ?=
SAVE_TEST_PACKAGES ?= ./...
SAVE_TEST_FLAGS ?= -run Integration
SAVE_TEST_COUNT ?= 1
CI_TEST_PACKAGES ?= ./...
CI_TEST_FLAGS ?=
CI_TEST_COUNT ?= 1
SAVE_TEST_HOST_PORT ?= 55432
export SAVE_TEST_HOST_PORT
CLIENT_BIN := $(CURDIR)/client/node_modules/.bin
BROWSER_TEST_FLAGS ?=
BROWSER_REPORT_DIR ?=
RELEVANCE_SCENARIO ?= balance/testdata/t0-t1/relevance-scenario-v2.json
RELEVANCE_OUTPUT ?= planning/archive/t0-t1-content/relevance-report.v5.json
RELEVANCE_BRANCH_INPUT ?=
RELEVANCE_BRANCH_OUTPUT ?= planning/archive/t0-t1-content/branch-report.v2.json
FIRST_HOUR_SCENARIO ?= balance/testdata/t0-t1/harness-scenario-v1.json
FIRST_HOUR_POLICY ?= balance/testdata/t0-t1/first-hour-policy-v1.json
FIRST_HOUR_OUTPUT ?= planning/archive/t0-t1-content/first-hour-experiment.v1.json
FIRST_HOUR_ACQUIHIRE_MINIMUM ?=
FIRST_HOUR_BURNOUT_FACTOR ?=
FIRST_HOUR_ROUTE_KNOWLEDGE_BONUS ?=
FIRST_HOUR_SEED_CAPITAL ?=
FIRST_HOUR_GENERATED_TOWERS ?=
setup:
pnpm --dir client install --frozen-lockfile
$(MAKE) install-browsers
mod-tidy:
cd server && go mod tidy
install-browsers:
pnpm --dir client run setup:browsers
install-browsers-ci:
pnpm --dir client run setup:browsers:ci
test: test-go test-client test-browser
test-go:
cd server && go test -p 1 -count=1 $(GO_TEST_FLAGS) $(GO_PACKAGES)
# The relevance content matrix is intentionally long-running. Keep it in the
# harness lane while allowing hosted CI to run the remaining server suite in
# parallel; the expensive beam is manual-only and is not part of either lane.
test-go-core:
cd server && go test -p 1 $(GO_TEST_FLAGS) $$(go list ./... | grep -v '^cloud-clicker/server/harness$$') -count=$(CORE_TEST_COUNT)
test-harness:
cd server && go test -p 1 $(GO_TEST_FLAGS) ./harness -count=$(HARNESS_TEST_COUNT)
# Reproduce the Actions server job on its real architecture, with Postgres and
# cold test execution. This intentionally runs every package rather than the
# Integration-named subset used by the focused save-integration target.
test-go-ci:
docker compose -f compose.save-test.yml -f compose.ci-test.yml run --rm test go test -p 1 $(CI_TEST_FLAGS) $(CI_TEST_PACKAGES) -count=$(CI_TEST_COUNT)
test-save-integration:
docker compose -f compose.save-test.yml run --rm test go test -p 1 $(SAVE_TEST_FLAGS) $(SAVE_TEST_PACKAGES) -count=$(SAVE_TEST_COUNT)
# Manual R-012 measurement writer; deliberately outside verify and CI.
.PHONY: research-axis-anchor-sql
research-axis-anchor-sql:
docker compose -f compose.save-test.yml run --rm -e UPDATE_ANCHOR_SQL_RESEARCH=1 test go test -p 1 -run TestAxisAnchorSQLIntegrationResearch -v ./production -count=1
# Manual Account census: a green package with skipped/missing populations is invalid.
# Deliberately outside verify/CI; fixed real-Postgres population, no retry/timeout change.
.PHONY: test-refresh-census test-refresh-observer
test-refresh-census:
node client/tools/observe-refresh-population.mjs
test-refresh-observer:
node --check client/tools/observe-refresh-population.mjs
node --test client/tools/observe-refresh-population.fixtures.mjs
# Manual API C1/C7/C9 evidence; refusal observations are not a completed contract.
.PHONY: research-refresh-generated-contract
research-refresh-generated-contract:
node --check client/tools/refresh-generated-contract.fixtures.mjs
node client/tools/refresh-generated-contract.fixtures.mjs
# Real Postgres 16 custom-format dump/restore witnesses run in a package-owned
# image that contains the exact pg_dump/pg_restore major used by production.
test-deployment-backup:
mkdir -p $(REPO_CACHE_DIR)/bin
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go test -c -o ../.cache/bin/deploymentbackup.test ./deploymentbackup
@status=0; \
docker compose --project-name cloud-clicker-backup-test -f compose.deployment-backup-test.yml run --rm backup-test || status=$$?; \
docker compose --project-name cloud-clicker-backup-test -f compose.deployment-backup-test.yml down --volumes; \
exit $$status
# Real Postgres + gameserver + Caddy witness for authenticated socket drain,
# admission refusal, admitted-work completion, bounded closure and restart.
test-deployment-release:
mkdir -p $(REPO_CACHE_DIR)/bin
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go test -c -o ../.cache/bin/deploymentrelease.test ./deploymentrelease
@status=0; \
docker compose --project-name cloud-clicker-release-test -f compose.deployment-release-test.yml run --rm --use-aliases release-test || status=$$?; \
docker compose --project-name cloud-clicker-release-test -f compose.deployment-release-test.yml down --volumes; \
exit $$status
# Manual operations lane: exact Prometheus rule evaluator plus cold Go
# privacy, textfile, host-policy and configuration tests. It is intentionally
# outside push CI under DP8.
test-deployment-operations:
docker run --rm --platform linux/amd64 --entrypoint promtool -v "$(CURDIR)/deployment/operations:/work:ro" "$(PROMETHEUS_RULE_IMAGE)" test rules /work/cloud-clicker-alerts.test.yml
$(MAKE) test-go GO_PACKAGES='./operations ./cmd/deployment-operations ./cmd/deployment-backup ./cmd/deployment-release ./gameserver ./transport ./production ./save ./releasepackage ./deploymentbackup ./deploymentrelease' GO_TEST_FLAGS='-count=1'
$(MAKE) test-deployment-cleanup-runtime
$(MAKE) test-deployment-restart-profile
$(MAKE) test-deployment-backup-profile
$(MAKE) test-deployment-ingress-profile
$(MAKE) test-deployment-operations-profile
# Real one-minute job timing belongs to manual Deployment acceptance, not push
# CI. Reuse the declared Linux-amd64/Postgres service; no shortened job interval.
test-deployment-cleanup-runtime:
docker compose -f compose.save-test.yml -f compose.ci-test.yml run --rm -e CLOUD_CLICKER_CLEANUP_RUNTIME_TEST=1 test go test -p 1 -run '^TestComposedCredentialCleanupRuntimeAndDrainIntegration$$' -v ./gameserver -count=1
# Optional real Docker-engine producer witness, outside push CI. The test owns
# one networkless, read-only, restart-capped container and validates ownership
# before deleting it. It does not deploy or inspect the official gameserver.
test-deployment-restart-observer: export CLOUD_CLICKER_RESTART_OBSERVER_TEST = 1
test-deployment-restart-observer:
$(MAKE) test-go GO_PACKAGES='./cmd/deployment-operations' GO_TEST_FLAGS='-run TestHostObservationActualDockerRestartIntegration -v -count=1'
# Same actual producer, now through the private pinned services and receiver.
# Native owner controls only its labelled containers and unique Compose project.
test-deployment-restart-profile: export CLOUD_CLICKER_RESTART_OBSERVER_TEST = 1
test-deployment-restart-profile: export CLOUD_CLICKER_OPERATIONS_ROOT = $(CURDIR)
test-deployment-restart-profile:
mkdir -p $(REPO_CACHE_DIR)/bin
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go test -c -o ../.cache/bin/operations.test ./operations
$(MAKE) test-go GO_PACKAGES='./cmd/deployment-operations' GO_TEST_FLAGS="-run 'TestHostObservationActualDockerRestartProfileIntegration|TestPrivateRestartStackCancellationCleanupIntegration' -v -count=1 -timeout=12m"
# Actual backup CLI, pg_dump/pg_restore and unshortened scheduled retry, through
# the existing private stack. The native supervisor owns one unique project.
test-deployment-backup-profile: export CLOUD_CLICKER_BACKUP_PROFILE_TEST = 1
test-deployment-backup-profile: export CLOUD_CLICKER_OPERATIONS_ROOT = $(CURDIR)
test-deployment-backup-profile:
mkdir -p $(REPO_CACHE_DIR)/bin
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ../.cache/bin/deployment-backup ./cmd/deployment-backup
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go test -c -o ../.cache/bin/operations.test ./operations
$(MAKE) test-go GO_PACKAGES='./cmd/deployment-operations' GO_TEST_FLAGS='-run TestActualBackupWorkerPrivateProfileIntegration -v -count=1 -timeout=14m'
# Actual isolated proxy stop/start with the unchanged five-minute alert bound.
test-deployment-ingress-profile: export CLOUD_CLICKER_INGRESS_PROFILE_TEST = 1
test-deployment-ingress-profile: export CLOUD_CLICKER_OPERATIONS_ROOT = $(CURDIR)
test-deployment-ingress-profile:
mkdir -p $(REPO_CACHE_DIR)/bin
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go test -c -o ../.cache/bin/operations.test ./operations
$(MAKE) test-go GO_PACKAGES='./cmd/deployment-operations' GO_TEST_FLAGS='-run TestActualCaddyOutagePrivateProfileIntegration -v -count=1 -timeout=14m'
# Focus the existing real-service fixture without skipping or changing the
# aggregate above. This is not the full operations or clean-host release gate.
test-deployment-operations-profile:
mkdir -p $(REPO_CACHE_DIR)/bin
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go test -c -o ../.cache/bin/operations.test ./operations
@status=0; \
docker compose --project-name cloud-clicker-operations-test -f compose.deployment-operations-test.yml run --rm --use-aliases operations-test || status=$$?; \
docker compose --project-name cloud-clicker-operations-test -f compose.deployment-operations-test.yml down --volumes; \
exit $$status
# Focused real bounded-filesystem producer and private receiver path. Its test
# name is distinct from the full profile; it cannot claim that larger population.
test-deployment-storage-profile:
mkdir -p $(REPO_CACHE_DIR)/bin
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go test -c -o ../.cache/bin/operations.test ./operations
@status=0; \
docker compose --project-name cloud-clicker-operations-test -f compose.deployment-operations-test.yml run --rm --use-aliases operations-test -test.run '^TestPrivateOperationsStorageIntegration$$' -test.v -test.count=1 || status=$$?; \
docker compose --project-name cloud-clicker-operations-test -f compose.deployment-operations-test.yml down --volumes; \
exit $$status
# Optional local notification test, not a replacement for the operations
# Compose or clean-host release lanes. No binary download or CI change.
test-operations-alertmanager-native: export CLOUD_CLICKER_ALERTMANAGER_TEST_BINARY = $(ALERTMANAGER_BINARY)
test-operations-alertmanager-native:
@test -n "$(ALERTMANAGER_BINARY)" || (echo "ALERTMANAGER_BINARY must name the pinned native 0.32.1 executable" >&2; exit 2)
cd server && go test -p 1 -run '^TestNativeAlertmanagerDeliveryIntegration$$' -v -count=1 ./operations
test-operations-native: export CLOUD_CLICKER_ALERTMANAGER_TEST_BINARY = $(ALERTMANAGER_BINARY)
test-operations-native: export CLOUD_CLICKER_PROMETHEUS_TEST_BINARY = $(PROMETHEUS_BINARY)
test-operations-native:
@test -n "$(ALERTMANAGER_BINARY)" -a -n "$(PROMETHEUS_BINARY)" || (echo "ALERTMANAGER_BINARY (0.32.1) and PROMETHEUS_BINARY (3.12.0) are required" >&2; exit 2)
cd server && go test -p 1 -run '^TestNative(AlertmanagerDelivery|PrometheusCleanup)Integration$$' -v -count=1 ./operations
# Manual R-006 lane. The package and validator tests are local and cold; the
# destructive clean-host execution requires an explicitly authorized host.
test-deployment-rehearsal:
$(MAKE) test-go GO_PACKAGES='./deploymentrehearsal ./cmd/deployment-rehearsal ./deploymentrelease ./cmd/deployment-release' GO_TEST_FLAGS='-count=1'
cd server && go run ./cmd/deployment-rehearsal validate-build --record=../planning/deployment-foundation/release-builds/previous.json
cd server && go run ./cmd/deployment-rehearsal validate-build --record=../planning/deployment-foundation/release-builds/candidate.json
deployment-rehearsal-probe:
@test -n "$(REHEARSAL_POPULATION)" -a -n "$(REHEARSAL_CANDIDATE_BUNDLE)" -a -n "$(REHEARSAL_PREVIOUS_BUNDLE)" -a -n "$(REHEARSAL_WORK_DIRECTORY)" || (echo "rehearsal probe inputs are required" >&2; exit 2)
cd server && go run ./cmd/deployment-rehearsal probe \
--population="$(REHEARSAL_POPULATION)" \
--candidate-bundle="$(abspath $(REHEARSAL_CANDIDATE_BUNDLE))" \
--previous-bundle="$(abspath $(REHEARSAL_PREVIOUS_BUNDLE))" \
--work-directory="$(abspath $(REHEARSAL_WORK_DIRECTORY))"
deployment-rehearsal-observe-host:
@test -n "$(REHEARSAL_SCENARIO_CONFIG)" || (echo "rehearsal scenario config is required" >&2; exit 2)
cd server && go run ./cmd/deployment-rehearsal observe-host --config="$(abspath $(REHEARSAL_SCENARIO_CONFIG))"
deployment-rehearsal-install-candidate:
@test -n "$(REHEARSAL_SCENARIO_CONFIG)" || (echo "rehearsal scenario config is required" >&2; exit 2)
cd server && go run ./cmd/deployment-rehearsal install-candidate --config="$(abspath $(REHEARSAL_SCENARIO_CONFIG))"
deployment-rehearsal-run-browser:
@test -n "$(REHEARSAL_SCENARIO_CONFIG)" || (echo "rehearsal scenario config is required" >&2; exit 2)
cd server && go run ./cmd/deployment-rehearsal run-browser --config="$(abspath $(REHEARSAL_SCENARIO_CONFIG))"
deployment-rehearsal-recover-empty:
@test -n "$(REHEARSAL_SCENARIO_CONFIG)" || (echo "rehearsal scenario config is required" >&2; exit 2)
cd server && go run ./cmd/deployment-rehearsal recover-empty --config="$(abspath $(REHEARSAL_SCENARIO_CONFIG))"
deployment-rehearsal-recover-populated:
@test -n "$(REHEARSAL_SCENARIO_CONFIG)" || (echo "rehearsal scenario config is required" >&2; exit 2)
cd server && go run ./cmd/deployment-rehearsal recover-populated --config="$(abspath $(REHEARSAL_SCENARIO_CONFIG))"
deployment-rehearsal-lifecycle-release:
@test -n "$(REHEARSAL_SCENARIO_CONFIG)" || (echo "rehearsal scenario config is required" >&2; exit 2)
cd server && go run ./cmd/deployment-rehearsal lifecycle-release --config="$(abspath $(REHEARSAL_SCENARIO_CONFIG))"
deployment-rehearsal-lifecycle-rollback:
@test -n "$(REHEARSAL_SCENARIO_CONFIG)" || (echo "rehearsal scenario config is required" >&2; exit 2)
cd server && go run ./cmd/deployment-rehearsal lifecycle-rollback --config="$(abspath $(REHEARSAL_SCENARIO_CONFIG))"
deployment-rehearsal-supply-chain:
@test -n "$(REHEARSAL_CANDIDATE_BUNDLE)" -a -n "$(REHEARSAL_PREVIOUS_BUNDLE)" -a -n "$(REHEARSAL_CANDIDATE_BUILD)" -a -n "$(REHEARSAL_PREVIOUS_BUILD)" -a -n "$(REHEARSAL_SECRET_SCAN)" -a -n "$(REHEARSAL_SUPPLY_CHAIN_OUTPUT)" || (echo "supply-chain inputs are required" >&2; exit 1)
cd server && go run ./cmd/deployment-rehearsal supply-chain \
--candidate-bundle="$(abspath $(REHEARSAL_CANDIDATE_BUNDLE))" --previous-bundle="$(abspath $(REHEARSAL_PREVIOUS_BUNDLE))" \
--candidate-build="$(abspath $(REHEARSAL_CANDIDATE_BUILD))" --previous-build="$(abspath $(REHEARSAL_PREVIOUS_BUILD))" \
--secret-scan="$(abspath $(REHEARSAL_SECRET_SCAN))" --output="$(abspath $(REHEARSAL_SUPPLY_CHAIN_OUTPUT))"
deployment-rehearsal-forge-proof:
@test -n "$(REHEARSAL_BASE_EVIDENCE)" -a -n "$(REHEARSAL_PLAN)" -a -n "$(REHEARSAL_RESULTS)" -a -n "$(REHEARSAL_ARTIFACTS)" -a -n "$(REHEARSAL_CANDIDATE_BUNDLE)" -a -n "$(REHEARSAL_FORGERY_OUTPUT)" || (echo "forgery-proof inputs are required" >&2; exit 1)
cd server && go run ./cmd/deployment-rehearsal forge-proof \
--base-evidence="$(abspath $(REHEARSAL_BASE_EVIDENCE))" --plan="$(abspath $(REHEARSAL_PLAN))" \
--results="$(abspath $(REHEARSAL_RESULTS))" --artifacts="$(abspath $(REHEARSAL_ARTIFACTS))" \
--candidate-bundle="$(abspath $(REHEARSAL_CANDIDATE_BUNDLE))" --output="$(abspath $(REHEARSAL_FORGERY_OUTPUT))"
build-deployment-rehearsal:
mkdir -p $(REPO_CACHE_DIR)/bin
cd server && go build -o ../.cache/bin/deployment-rehearsal ./cmd/deployment-rehearsal
# Validate the complete embedded migration chain on real Postgres while keeping
# the scope focused on the package that owns it. Migration-named unit probes and
# every save integration test both run cold.
validate-migrations:
docker compose -f compose.save-test.yml run --rm test go test -p 1 -run 'Integration|Migration' ./save -count=1
test-client:
cd client && $(CLIENT_BIN)/vitest run
# Focused input/DOM regressions do not need the separate performance population.
# The CI/full-browser target retains both lanes below.
test-browser-focused:
cd client && VITE_GAME_UI_PERFORMANCE=0 $(CLIENT_BIN)/vitest run --config vitest.browser.config.ts $(BROWSER_TEST_FLAGS)
# Preserve the complete functional population, but run Typer's real keyboard
# journeys after the broad workload. Focused selection above stays untouched.
# Separate reports prevent the second invocation from overwriting the first.
test-browser-functional:
$(if $(findstring --outputFile,$(BROWSER_TEST_FLAGS)),$(error use BROWSER_REPORT_DIR for separate full-functional reports; --outputFile is supported only by test-browser-focused))
$(if $(BROWSER_REPORT_DIR),mkdir -p "$(abspath $(BROWSER_REPORT_DIR))")
cd client && VITE_GAME_UI_PERFORMANCE=0 $(CLIENT_BIN)/vitest run --config vitest.browser.config.ts --exclude test/typer-table-browser.test.ts $(BROWSER_TEST_FLAGS) $(if $(BROWSER_REPORT_DIR),--reporter=default --reporter=json --outputFile.json="$(abspath $(BROWSER_REPORT_DIR))/functional-broad.json")
cd client && VITE_GAME_UI_PERFORMANCE=0 $(CLIENT_BIN)/vitest run --config vitest.browser.config.ts test/typer-table-browser.test.ts $(BROWSER_TEST_FLAGS) $(if $(BROWSER_REPORT_DIR),--reporter=default --reporter=json --outputFile.json="$(abspath $(BROWSER_REPORT_DIR))/functional-native.json")
test-browser: test-browser-functional
$(MAKE) test-game-ui-performance
test-browser-ci:
docker compose -f compose.browser-test.yml run --rm browser
# Same disposable Linux setup and containment as the full lane; focused checks
# do not stand in for its functional/performance population.
test-browser-ci-focused:
docker compose -f compose.browser-test.yml run --rm browser test-browser-focused "BROWSER_TEST_FLAGS=$(BROWSER_TEST_FLAGS)"
# Real browser -> composed gameserver/Postgres. Both drivers require the
# visible visitor counter: runtime.ts completed its actual Centrifuge
# WebSocket handshake, not a mocked socket exchange.
GAME_UI_COMPOSE_FILES ?= -f compose.game-ui-test.yml
.PHONY: test-garden-composed test-garden-harvest-composed test-clout-composed test-cosmetic-n5 diagnose-browser-session
# Fixture-only wall-clock journey; manual, deliberately not a push-CI dependency.
test-garden-composed: build-gameserver build-client
docker compose $(GAME_UI_COMPOSE_FILES) up -d --wait game-ui-postgres
node client/tools/test-garden-composed.mjs
# Bounded real-service harvest population with explicitly seeded mature plots.
# Does not replace the natural wall-clock/renewal journey above.
test-garden-harvest-composed: build-gameserver build-client
node --test client/tools/navigation-api-proof.fixtures.mjs
docker compose $(GAME_UI_COMPOSE_FILES) up -d --wait game-ui-postgres
node client/tools/test-garden-composed.mjs --harvest-fixture
# Clout AC11's unminted axis fixture; reuse the built-client/service journey.
# Focused selector; the normal composed CI target also runs this variant.
test-clout-composed:
docker compose $(GAME_UI_COMPOSE_FILES) up -d --wait game-ui-postgres
node client/tools/test-cosmetic-composed.mjs --axis-stack
.PHONY: test-tier2-composed
# Unminted candidate catalog and controlled cash; not pacing/Headcount proof.
test-tier2-composed:
docker compose $(GAME_UI_COMPOSE_FILES) up -d --wait game-ui-postgres
node client/tools/test-game-ui-composed.mjs --tier2-fixture
# MA3 natural-duration completion; bounded native recovery runs in normal CI.
# No cadence/catalog/time acceleration; normal CI verifies the bounded lifecycle.
.PHONY: test-soul-recovery-composed
test-soul-recovery-composed:
docker compose $(GAME_UI_COMPOSE_FILES) up -d --wait game-ui-postgres
node client/tools/test-game-ui-composed.mjs --soul-recovery-full
# AC13: real built-client negatives must fail specifically at the N5 trap.
test-cosmetic-n5:
docker compose $(GAME_UI_COMPOSE_FILES) up -d --wait game-ui-postgres
node client/tools/test-cosmetic-n5.mjs
# Manual Account/Transport diagnostic; not automatic-renewal or release proof.
diagnose-browser-session: build-gameserver build-client
docker compose $(GAME_UI_COMPOSE_FILES) up -d --wait game-ui-postgres
node client/tools/test-garden-composed.mjs --session-diagnostic
.PHONY: diagnose-browser-session-coordination
# R-011 real Account/browser observation; not production renewal or a CI gate.
diagnose-browser-session-coordination: build-gameserver
docker compose $(GAME_UI_COMPOSE_FILES) up -d --wait game-ui-postgres
node client/tools/test-garden-composed.mjs --session-coordination-diagnostic
.PHONY: research-browser-coordination
# R-011 primitive observation only; no default CI/release dependency or product auth.
research-browser-coordination:
docker compose -f compose.browser-test.yml run --rm --entrypoint bash browser -lc 'corepack enable && corepack prepare pnpm@11.15.1 --activate && pnpm --dir client install --frozen-lockfile && node client/tools/research-browser-coordination.mjs $(shell git rev-parse HEAD)'
.PHONY: research-browser-refresh-dispatch
# Manual R-011 native primitive research, never a renewal or full CI gate.
RESEARCH_BROWSER_ENGINES ?= chromium firefox webkit
research-browser-refresh-dispatch:
node client/tools/research-browser-refresh-dispatch.mjs $(shell git rev-parse HEAD) $(RESEARCH_BROWSER_ENGINES)
test-game-ui-composed:
node --test client/tools/production-client-proof.fixtures.mjs client/tools/care-snapshot-proof.fixtures.mjs client/tools/cosmetic-n5-fixture.fixtures.mjs
docker compose $(GAME_UI_COMPOSE_FILES) up -d --wait game-ui-postgres
node client/tools/test-game-ui-composed.mjs
node client/tools/test-cosmetic-composed.mjs
node client/tools/test-cosmetic-composed.mjs --axis-stack
$(MAKE) test-cosmetic-n5
$(MAKE) test-garden-harvest-composed
$(MAKE) test-tier2-composed
verify-game-ui: verify-client test-browser test-game-ui-composed
test-game-ui-performance:
cd client && VITE_GAME_UI_PERFORMANCE=1 $(CLIENT_BIN)/vitest run --config vitest.browser.config.ts test/game-ui-screens-browser.test.ts --testNamePattern 'observable 20 Hz / 10 Hz screen budget'
typecheck:
cd client && $(CLIENT_BIN)/tsc --noEmit && $(CLIENT_BIN)/svelte-check --tsconfig ./tsconfig.json
build-client:
cd client && $(CLIENT_BIN)/vite build
build-gameserver:
mkdir -p $(REPO_CACHE_DIR)/bin
cd server && go build -o $(REPO_CACHE_DIR)/bin/gameserver ./cmd/gameserver
build-gameserver-linux-amd64:
@test -n "$(RELEASE_SERVER_OUTPUT)" || (echo "RELEASE_SERVER_OUTPUT is required" >&2; exit 1)
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false \
-ldflags='-s -w -buildid=' \
-o "$(if $(filter /%,$(RELEASE_SERVER_OUTPUT)),$(RELEASE_SERVER_OUTPUT),../$(RELEASE_SERVER_OUTPUT))" \
./cmd/gameserver
build-deployment-backup-linux-amd64:
@test -n "$(RELEASE_BACKUP_OUTPUT)" || (echo "RELEASE_BACKUP_OUTPUT is required" >&2; exit 1)
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false \
-ldflags='-s -w -buildid=' \
-o "$(if $(filter /%,$(RELEASE_BACKUP_OUTPUT)),$(RELEASE_BACKUP_OUTPUT),../$(RELEASE_BACKUP_OUTPUT))" \
./cmd/deployment-backup
build-deployment-release-linux-amd64:
@test -n "$(RELEASE_HELPER_OUTPUT)" || (echo "RELEASE_HELPER_OUTPUT is required" >&2; exit 1)
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false \
-ldflags='-s -w -buildid=' \
-o "$(if $(filter /%,$(RELEASE_HELPER_OUTPUT)),$(RELEASE_HELPER_OUTPUT),../$(RELEASE_HELPER_OUTPUT))" \
./cmd/deployment-release
build-deployment-operations-linux-amd64:
@test -n "$(RELEASE_OPERATIONS_OUTPUT)" || (echo "RELEASE_OPERATIONS_OUTPUT is required" >&2; exit 1)
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false \
-ldflags='-s -w -buildid=' \
-o "$(if $(filter /%,$(RELEASE_OPERATIONS_OUTPUT)),$(RELEASE_OPERATIONS_OUTPUT),../$(RELEASE_OPERATIONS_OUTPUT))" \
./cmd/deployment-operations
build-deployment-rehearsal-linux-amd64:
@test -n "$(RELEASE_REHEARSAL_OUTPUT)" || (echo "RELEASE_REHEARSAL_OUTPUT is required" >&2; exit 1)
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false \
-ldflags='-s -w -buildid=' \
-o "$(if $(filter /%,$(RELEASE_REHEARSAL_OUTPUT)),$(RELEASE_REHEARSAL_OUTPUT),../$(RELEASE_REHEARSAL_OUTPUT))" \
./cmd/deployment-rehearsal
build-deployment-browser-linux-amd64:
@test -n "$(RELEASE_BROWSER_OUTPUT)" || (echo "RELEASE_BROWSER_OUTPUT is required" >&2; exit 1)
cd server && CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -buildvcs=false \
-ldflags='-s -w -buildid=' \
-o "$(if $(filter /%,$(RELEASE_BROWSER_OUTPUT)),$(RELEASE_BROWSER_OUTPUT),../$(RELEASE_BROWSER_OUTPUT))" \
./cmd/deployment-browser
# Syft discovers the image package graph; the repository normalizer binds its
# nondeterministic header to the exact runtime config and release timestamp.
# Set IMAGE_ARCHIVE only for the locally built gameserver docker-save archive.
generate-image-sbom:
@test -n "$(IMAGE_SBOM_OUTPUT)" -a -n "$(IMAGE_CONFIG_ID)" -a -n "$(RELEASE_CREATED_AT)" || (echo "IMAGE_SBOM_OUTPUT, IMAGE_CONFIG_ID, and RELEASE_CREATED_AT are required" >&2; exit 1)
@test -n "$(IMAGE_REFERENCE)" -o -n "$(IMAGE_ARCHIVE)" || (echo "IMAGE_REFERENCE or IMAGE_ARCHIVE is required" >&2; exit 1)
@test ! -e "$(IMAGE_SBOM_OUTPUT)" -a ! -e "$(IMAGE_SBOM_OUTPUT).raw" || (echo "image SBOM output already exists" >&2; exit 1)
docker run --rm --platform linux/amd64 \
-v "$(abspath $(dir $(IMAGE_SBOM_OUTPUT))):/output" \
$(if $(IMAGE_ARCHIVE),-v "$(abspath $(dir $(IMAGE_ARCHIVE))):/input:ro",) \
"$(SYFT_IMAGE)" \
"$(if $(IMAGE_ARCHIVE),docker-archive:/input/$(notdir $(IMAGE_ARCHIVE)),registry:$(IMAGE_REFERENCE))" \
--platform linux/amd64 --source-name "$(IMAGE_CONFIG_ID)" --output "spdx-json=/output/$(notdir $(IMAGE_SBOM_OUTPUT)).raw"
cd server && go run ./cmd/normalize-image-sbom \
-input="$(if $(filter /%,$(IMAGE_SBOM_OUTPUT)),$(IMAGE_SBOM_OUTPUT),../$(IMAGE_SBOM_OUTPUT)).raw" \
-output="$(if $(filter /%,$(IMAGE_SBOM_OUTPUT)),$(IMAGE_SBOM_OUTPUT),../$(IMAGE_SBOM_OUTPUT))" \
-runtime-config-sha256="$(IMAGE_CONFIG_ID)" -created="$(RELEASE_CREATED_AT)"
create-release-builder:
docker buildx create --name "$(RELEASE_BUILDX_BUILDER)" --driver docker-container \
--driver-opt image="$(RELEASE_BUILDKIT_IMAGE)"
docker buildx inspect "$(RELEASE_BUILDX_BUILDER)" --bootstrap
build-gameserver-image:
@test -n "$(RELEASE_IMAGE_CONTEXT)" -a -n "$(RELEASE_VERSION)" -a -n "$(RELEASE_COMMIT)" \
-a -n "$(RELEASE_SOURCE_DATE_EPOCH)" -a -n "$(GAMESERVER_IMAGE_TAG)" -a -n "$(GAMESERVER_IMAGE_ARCHIVE)" || \
(echo "gameserver image inputs are required" >&2; exit 1)
docker buildx build --builder "$(RELEASE_BUILDX_BUILDER)" --no-cache --platform linux/amd64 \
--file "$(RELEASE_IMAGE_CONTEXT)/Dockerfile" --provenance=false \
--build-arg RELEASE_VERSION="$(RELEASE_VERSION)" --build-arg SOURCE_COMMIT="$(RELEASE_COMMIT)" \
--build-arg SOURCE_DATE_EPOCH="$(RELEASE_SOURCE_DATE_EPOCH)" \
--output "type=docker,name=$(GAMESERVER_IMAGE_TAG),dest=$(GAMESERVER_IMAGE_ARCHIVE),rewrite-timestamp=true" \
"$(RELEASE_IMAGE_CONTEXT)"
# Production startup and this preflight use the same environment and
# file-secret decoder. The release-bundle profile extends this in DP-B.
deployment-config-check:
cd server && go run ./cmd/gameserver validate-config
stage-release-content:
@test -n "$(RELEASE_CONTENT_OUTPUT)" || (echo "RELEASE_CONTENT_OUTPUT is required" >&2; exit 1)
cd server && go run ./cmd/stage-release-content -root=.. \
-output="$(if $(filter /%,$(RELEASE_CONTENT_OUTPUT)),$(RELEASE_CONTENT_OUTPUT),../$(RELEASE_CONTENT_OUTPUT))"
render-release-compose:
@test -n "$(RELEASE_COMPOSE_OUTPUT)" -a -n "$(ALERTMANAGER_IMAGE)" -a -n "$(CADDY_IMAGE)" -a -n "$(GAMESERVER_IMAGE)" \
-a -n "$(NODE_EXPORTER_IMAGE)" -a -n "$(POSTGRES_IMAGE)" -a -n "$(PROMETHEUS_IMAGE)" || \
(echo "release Compose output and all six immutable images are required" >&2; exit 1)
cd server && go run ./cmd/render-release-compose \
-output="$(if $(filter /%,$(RELEASE_COMPOSE_OUTPUT)),$(RELEASE_COMPOSE_OUTPUT),../$(RELEASE_COMPOSE_OUTPUT))" \
-alertmanager-image="$(ALERTMANAGER_IMAGE)" -caddy-image="$(CADDY_IMAGE)" -gameserver-image="$(GAMESERVER_IMAGE)" \
-node-exporter-image="$(NODE_EXPORTER_IMAGE)" -postgres-image="$(POSTGRES_IMAGE)" -prometheus-image="$(PROMETHEUS_IMAGE)"
generate-release-metadata:
@test -n "$(RELEASE_METADATA_OUTPUT)" -a -n "$(RELEASE_VERSION)" -a -n "$(RELEASE_COMMIT)" \
-a -n "$(RELEASE_CREATED_AT)" || \
(echo "RELEASE_METADATA_OUTPUT, RELEASE_VERSION, RELEASE_COMMIT, and RELEASE_CREATED_AT are required" >&2; exit 1)
cd server && go run ./cmd/gen-release-metadata -root=.. \
-output="$(if $(filter /%,$(RELEASE_METADATA_OUTPUT)),$(RELEASE_METADATA_OUTPUT),../$(RELEASE_METADATA_OUTPUT))" \
-version="$(RELEASE_VERSION)" -commit="$(RELEASE_COMMIT)" -created="$(RELEASE_CREATED_AT)"
assemble-release-bundle:
@test -n "$(RELEASE_BUNDLE_OUTPUT)" -a -n "$(RELEASE_SERVER_OUTPUT)" -a -n "$(RELEASE_BACKUP_OUTPUT)" -a -n "$(RELEASE_HELPER_OUTPUT)" -a -n "$(RELEASE_OPERATIONS_OUTPUT)" -a -n "$(RELEASE_REHEARSAL_OUTPUT)" -a -n "$(RELEASE_BROWSER_OUTPUT)" -a -n "$(GAMESERVER_IMAGE_ARCHIVE)" -a -n "$(RELEASE_METADATA_OUTPUT)" \
-a -n "$(RELEASE_VERSION)" -a -n "$(RELEASE_COMMIT)" -a -n "$(RELEASE_DOCKER_VERSION)" \
-a -n "$(RELEASE_COMPOSE_VERSION)" -a -n "$(ALERTMANAGER_IMAGE)" -a -n "$(CADDY_IMAGE)" -a -n "$(GAMESERVER_IMAGE)" -a -n "$(NODE_EXPORTER_IMAGE)" -a -n "$(POSTGRES_IMAGE)" -a -n "$(PROMETHEUS_IMAGE)" \
-a -n "$(ALERTMANAGER_SBOM)" -a -n "$(CADDY_SBOM)" -a -n "$(GAMESERVER_SBOM)" -a -n "$(NODE_EXPORTER_SBOM)" -a -n "$(POSTGRES_SBOM)" -a -n "$(PROMETHEUS_SBOM)" \
-a -n "$(ALERTMANAGER_CONFIG_ID)" -a -n "$(CADDY_CONFIG_ID)" -a -n "$(GAMESERVER_CONFIG_ID)" -a -n "$(NODE_EXPORTER_CONFIG_ID)" -a -n "$(POSTGRES_CONFIG_ID)" -a -n "$(PROMETHEUS_CONFIG_ID)" \
-a -n "$(PLAYWRIGHT_IMAGE)" -a -n "$(PLAYWRIGHT_CONFIG_ID)" -a -n "$(PLAYWRIGHT_SBOM)" || (echo "release bundle inputs are required" >&2; exit 1)
cd server && go run ./cmd/assemble-release-bundle -root=.. \
-output="$(if $(filter /%,$(RELEASE_BUNDLE_OUTPUT)),$(RELEASE_BUNDLE_OUTPUT),../$(RELEASE_BUNDLE_OUTPUT))" \
-server-binary="$(if $(filter /%,$(RELEASE_SERVER_OUTPUT)),$(RELEASE_SERVER_OUTPUT),../$(RELEASE_SERVER_OUTPUT))" \
-backup-binary="$(if $(filter /%,$(RELEASE_BACKUP_OUTPUT)),$(RELEASE_BACKUP_OUTPUT),../$(RELEASE_BACKUP_OUTPUT))" \
-release-binary="$(if $(filter /%,$(RELEASE_HELPER_OUTPUT)),$(RELEASE_HELPER_OUTPUT),../$(RELEASE_HELPER_OUTPUT))" \
-operations-binary="$(if $(filter /%,$(RELEASE_OPERATIONS_OUTPUT)),$(RELEASE_OPERATIONS_OUTPUT),../$(RELEASE_OPERATIONS_OUTPUT))" \
-rehearsal-binary="$(if $(filter /%,$(RELEASE_REHEARSAL_OUTPUT)),$(RELEASE_REHEARSAL_OUTPUT),../$(RELEASE_REHEARSAL_OUTPUT))" \
-browser-binary="$(if $(filter /%,$(RELEASE_BROWSER_OUTPUT)),$(RELEASE_BROWSER_OUTPUT),../$(RELEASE_BROWSER_OUTPUT))" \
-browser-image="$(PLAYWRIGHT_IMAGE)" -browser-config-id="$(PLAYWRIGHT_CONFIG_ID)" \
-browser-sbom="$(if $(filter /%,$(PLAYWRIGHT_SBOM)),$(PLAYWRIGHT_SBOM),../$(PLAYWRIGHT_SBOM))" \
-gameserver-archive="$(if $(filter /%,$(GAMESERVER_IMAGE_ARCHIVE)),$(GAMESERVER_IMAGE_ARCHIVE),../$(GAMESERVER_IMAGE_ARCHIVE))" \
-client-dist=../client/dist \
-metadata="$(if $(filter /%,$(RELEASE_METADATA_OUTPUT)),$(RELEASE_METADATA_OUTPUT),../$(RELEASE_METADATA_OUTPUT))" \
-version="$(RELEASE_VERSION)" -commit="$(RELEASE_COMMIT)" \
-docker-version="$(RELEASE_DOCKER_VERSION)" -compose-version="$(RELEASE_COMPOSE_VERSION)" \
-alertmanager-image="$(ALERTMANAGER_IMAGE)" -caddy-image="$(CADDY_IMAGE)" -gameserver-image="$(GAMESERVER_IMAGE)" -node-exporter-image="$(NODE_EXPORTER_IMAGE)" -postgres-image="$(POSTGRES_IMAGE)" -prometheus-image="$(PROMETHEUS_IMAGE)" \
-alertmanager-config-id="$(ALERTMANAGER_CONFIG_ID)" -caddy-config-id="$(CADDY_CONFIG_ID)" -gameserver-config-id="$(GAMESERVER_CONFIG_ID)" -node-exporter-config-id="$(NODE_EXPORTER_CONFIG_ID)" -postgres-config-id="$(POSTGRES_CONFIG_ID)" -prometheus-config-id="$(PROMETHEUS_CONFIG_ID)" \
-alertmanager-sbom="$(if $(filter /%,$(ALERTMANAGER_SBOM)),$(ALERTMANAGER_SBOM),../$(ALERTMANAGER_SBOM))" \
-caddy-sbom="$(if $(filter /%,$(CADDY_SBOM)),$(CADDY_SBOM),../$(CADDY_SBOM))" \
-gameserver-sbom="$(if $(filter /%,$(GAMESERVER_SBOM)),$(GAMESERVER_SBOM),../$(GAMESERVER_SBOM))" \
-node-exporter-sbom="$(if $(filter /%,$(NODE_EXPORTER_SBOM)),$(NODE_EXPORTER_SBOM),../$(NODE_EXPORTER_SBOM))" \
-postgres-sbom="$(if $(filter /%,$(POSTGRES_SBOM)),$(POSTGRES_SBOM),../$(POSTGRES_SBOM))" \
-prometheus-sbom="$(if $(filter /%,$(PROMETHEUS_SBOM)),$(PROMETHEUS_SBOM),../$(PROMETHEUS_SBOM))"
release-secret-scan:
cd server && go run ./cmd/release-secret-scan -root=.. \
$(if $(GAMESERVER_IMAGE_ARCHIVE),-gameserver-archive="$(if $(filter /%,$(GAMESERVER_IMAGE_ARCHIVE)),$(GAMESERVER_IMAGE_ARCHIVE),../$(GAMESERVER_IMAGE_ARCHIVE))",) \
$(if $(SECRET_SCAN_MANIFEST_SHA256),-manifest-sha256="$(SECRET_SCAN_MANIFEST_SHA256)",) \
$(if $(SECRET_SCAN_OUTPUT),-output="$(abspath $(SECRET_SCAN_OUTPUT))",)
vectors:
node tools/gen-vectors.mjs
vectors-check: vectors
git diff --exit-code -- testdata/decimal-vectors.json
vectors-check-ci:
docker compose -f compose.numeric-test.yml run --rm test
git diff --exit-code -- testdata/decimal-vectors.json
replay-fixture:
cd server && go test ./production -run '^TestApplyLoggedCrossRuntimeFixture$$' -update-replay-fixture
replay-fixture-check:
cd server && go test ./production -run '^TestApplyLoggedCrossRuntimeFixture$$' -count=1
# Fixture authoring only; normal test/CI runs compare, never regenerate.
reputation-activation-corpus:
cd server && go test -p 1 ./production -run '^TestReputationEarlierFounderActivationChain$$' -count=1 -update-replay-fixture
reputation-input-shape-corpus:
cd server && go test -p 1 ./production -run '^TestReputationPurchaseInputShapeCorpus$$' -count=1 -update-replay-fixture
reputation-offer-corpus:
cd server && go test -p 1 ./production -run '^TestReputationOfferReplayCorpus$$' -count=1 -update-replay-fixture
reputation-boundary-corpus:
cd server && go test -p 1 ./production -run '^TestReputationBoundaryReplayCorpus$$' -count=1 -update-replay-fixture
pitch-corpus:
cd server && go test ./pitch -run '^TestPitchContentGate$$' -update-pitch-corpus
pitch-corpus-check:
cd server && go test ./pitch -run '^TestPitchContentGate$$' -count=1
typer-corpus:
cd server && go test ./typer -run '^TestTyperContentGate$$' -update-typer-corpus
typer-corpus-check:
cd server && go test ./typer -run '^TestTyperContentGate$$' -count=1
arcade-corpus:
cd server && go test ./arcade -run '^(TestArcadeContentGate|TestSnakeOddBoardContentGate|TestMineGridRejectionSamplingContentGate)$$' -update-arcade-corpus -update-arcade-odd-corpus -update-arcade-sampling-corpus -count=1
arcade-corpus-check:
cd server && go test ./arcade -run '^(TestArcadeContentGate|TestSnakeOddBoardContentGate|TestMineGridRejectionSamplingContentGate)$$' -count=1
garden-corpus:
cd server && go test ./garden -run '^TestGardenEngineCorpus$$' -update-garden-corpus
garden-corpus-check:
cd server && go test -count=1 ./garden -run '^TestGardenEngineCorpus$$'
# Tier 2 content candidates (rfc/tier2-content.md): derivation drift plus the
# Go/TS identity and reachability witnesses. Fixture-first; nothing is minted.
t2-candidates-check:
node client/tools/generate-t2-candidates.mjs --check
cd server && go test -count=1 ./production -run '^TestTier2'
cd client && $(CLIENT_BIN)/vitest run test/tier2-candidates.test.ts
formulas:
cd server && go run ./cmd/gen-formulas -output ../docs/generated/production-formulas.json -schema-output ../client/src/api/generated/formula-schema.ts
formulas-check: formulas
git diff --exit-code -- docs/generated/production-formulas.json client/src/api/generated/formula-schema.ts
api-generate:
cd server && go run ./cmd/gen-api -root=..
api-schema: api-generate
api-pin:
cd server && go run ./cmd/gen-api -root=.. -update-pin
api-check: api-generate
git diff --exit-code -- docs/generated/api.json docs/generated/api-compat-v1.json client/src/api/generated/types.ts
harness:
@test -n "$(HARNESS_OUTPUT)" || (echo "HARNESS_OUTPUT is required" >&2; exit 1)
cd server && go run ./cmd/balance-harness -mode=run -root=.. -workers=$(HARNESS_WORKERS) -output="$(HARNESS_OUTPUT)"
harness-check: commons-harness-check
cd server && go run ./cmd/balance-harness -mode=check -root=.. -workers=$(HARNESS_WORKERS)
# R-001 measurement-only lane. These targets do not change the governed
# population and are intentionally absent from CI/release dependencies.
harness-observe: commons-harness-check
@test -n "$(HARNESS_OBSERVATION)" || (echo "HARNESS_OBSERVATION is required" >&2; exit 1)
cd server && go run ./cmd/balance-harness -mode=check -root=.. -workers=$(HARNESS_WORKERS) \
-observation-output="$(if $(filter /%,$(HARNESS_OBSERVATION)),$(HARNESS_OBSERVATION),../$(HARNESS_OBSERVATION))"
harness-observation-check:
@test -n "$(HARNESS_OBSERVATION)" || (echo "HARNESS_OBSERVATION is required" >&2; exit 1)
cd server && go run ./cmd/balance-harness -mode=observation-validate \
-observation-input="$(if $(filter /%,$(HARNESS_OBSERVATION)),$(HARNESS_OBSERVATION),../$(HARNESS_OBSERVATION))"
# Read-only manual diagnostic. Build the bundled reader in the same repository
# cache even on toolchains that ship cmd/pprof source but no prebuilt tool.
.PHONY: harness-profile-report
harness-profile-report:
@test -n "$(HARNESS_CPU_PROFILE)" || (echo "HARNESS_CPU_PROFILE is required" >&2; exit 1)
cd server && go run cmd/pprof $(HARNESS_PROFILE_FLAGS) \
"$(if $(filter /%,$(HARNESS_CPU_PROFILE)),$(HARNESS_CPU_PROFILE),../$(HARNESS_CPU_PROFILE))"
relevance-registered-observe:
@test -n "$(HARNESS_OBSERVATION)" -a -n "$(REGISTERED_RELEVANCE_ENTRY)" \
-a -n "$(REGISTERED_RELEVANCE_OUTPUT)" || \
(echo "HARNESS_OBSERVATION, REGISTERED_RELEVANCE_ENTRY, and REGISTERED_RELEVANCE_OUTPUT are required" >&2; exit 1)
cd server && go run ./cmd/balance-harness -mode=relevance-registered -root=.. \
-relevance-entry="$(REGISTERED_RELEVANCE_ENTRY)" \
-output="$(if $(filter /%,$(REGISTERED_RELEVANCE_OUTPUT)),$(REGISTERED_RELEVANCE_OUTPUT),../$(REGISTERED_RELEVANCE_OUTPUT))" \
-observation-output="$(if $(filter /%,$(HARNESS_OBSERVATION)),$(HARNESS_OBSERVATION),../$(HARNESS_OBSERVATION))"
harness-guard-check:
cd server && go run ./cmd/balance-harness -mode=guard -root=..
content-harness:
cd server && go run ./cmd/balance-harness -mode=content -root=..
epoch7-content-harness:
cd server && go run ./cmd/balance-harness -mode=content-candidate -root=.. \
-candidate-manifest=planning/archive/t0-t1-content/promotion-manifest.candidate.v1.json \
-scenario=testdata/harness/content-dynamics/scenarios/epoch-7-candidate.v1.json \
-output=../planning/epoch7-content-harness/candidate-report.v1.json
first-content-harness:
cd server && go run ./cmd/balance-harness -mode=candidate -root=.. \
-workers=$(HARNESS_WORKERS) \
-candidate-manifest=planning/first-content-epoch/promotion-manifest.candidate.v1.json \
-output=../planning/first-content-epoch/composed-harness-report.v1.json
# Measurement lane for the owner-ratified first-hour personas. The five payoff
# values are explicit experiment inputs; this target never mints curriculum
# bytes or turns a measurement into balance authority.
first-hour-harness:
@test -n "$(FIRST_HOUR_ACQUIHIRE_MINIMUM)" -a -n "$(FIRST_HOUR_BURNOUT_FACTOR)" \
-a -n "$(FIRST_HOUR_ROUTE_KNOWLEDGE_BONUS)" -a -n "$(FIRST_HOUR_SEED_CAPITAL)" \
-a -n "$(FIRST_HOUR_GENERATED_TOWERS)" || \
(echo "FIRST_HOUR_ACQUIHIRE_MINIMUM, FIRST_HOUR_BURNOUT_FACTOR, FIRST_HOUR_ROUTE_KNOWLEDGE_BONUS, FIRST_HOUR_SEED_CAPITAL, and FIRST_HOUR_GENERATED_TOWERS are required" >&2; exit 1)
cd server && go run ./cmd/balance-harness -mode=first-hour -root=.. \
-workers=$(HARNESS_WORKERS) -scenario=$(FIRST_HOUR_SCENARIO) \
-first-hour-policy=$(FIRST_HOUR_POLICY) -output=../$(FIRST_HOUR_OUTPUT) \
-acquihire-minimum=$(FIRST_HOUR_ACQUIHIRE_MINIMUM) -burnout-factor=$(FIRST_HOUR_BURNOUT_FACTOR) \
-route-knowledge-bonus=$(FIRST_HOUR_ROUTE_KNOWLEDGE_BONUS) -seed-capital=$(FIRST_HOUR_SEED_CAPITAL) \
-generated-towers=$(FIRST_HOUR_GENERATED_TOWERS)
t0-t1-role-check:
$(MAKE) test-go GO_PACKAGES=./production GO_TEST_FLAGS='-run TestT0T1CandidateRoleActivations -count=1'
t0-t1-relevance:
cd server && go run ./cmd/balance-harness -mode=relevance -root=.. \
-scenario=$(RELEVANCE_SCENARIO) \
-output=../$(RELEVANCE_OUTPUT)
t1-relevance:
$(MAKE) t0-t1-relevance \
RELEVANCE_SCENARIO=balance/testdata/t0-t1/relevance-scenario-t1-v2.json \
RELEVANCE_OUTPUT=planning/archive/t0-t1-content/relevance-report-t1.v5.json
relevance-branches:
cd server && go run ./cmd/balance-harness -mode=relevance-branches -root=.. \
-scenario=$(RELEVANCE_SCENARIO) $(if $(RELEVANCE_BRANCH_INPUT),-relevance-report=$(if $(filter /%,$(RELEVANCE_BRANCH_INPUT)),$(RELEVANCE_BRANCH_INPUT),../$(RELEVANCE_BRANCH_INPUT))) \
-output=$(if $(filter /%,$(RELEVANCE_BRANCH_OUTPUT)),$(RELEVANCE_BRANCH_OUTPUT),../$(RELEVANCE_BRANCH_OUTPUT))
t0-t1-branch-check:
@status=0; \
$(MAKE) relevance-branches || status=1; \
$(MAKE) relevance-branches \
RELEVANCE_SCENARIO=balance/testdata/t0-t1/relevance-scenario-t1-v2.json \
RELEVANCE_BRANCH_OUTPUT=planning/archive/t0-t1-content/branch-report-t1.v2.json || status=1; \
exit $$status
# Fast path for a combined run that has just produced hash-matched diagnostics.
t0-t1-branch-check-from-reports:
@status=0; \
$(MAKE) relevance-branches \
RELEVANCE_BRANCH_INPUT=planning/archive/t0-t1-content/relevance-report.v5.diagnostic.json || status=1; \
$(MAKE) relevance-branches \
RELEVANCE_SCENARIO=balance/testdata/t0-t1/relevance-scenario-t1-v2.json \
RELEVANCE_BRANCH_INPUT=planning/archive/t0-t1-content/relevance-report-t1.v5.diagnostic.json \
RELEVANCE_BRANCH_OUTPUT=planning/archive/t0-t1-content/branch-report-t1.v2.json || status=1; \
exit $$status
# Compatibility alias for the pre-C29 upgrade-only gate name.
t0-t1-upgrade-check: t0-t1-branch-check
t0-t1-relevance-all: t0-t1-role-check
@status=0; \
$(MAKE) t0-t1-relevance || status=1; \
$(MAKE) t1-relevance || status=1; \
$(MAKE) t0-t1-branch-check-from-reports || status=1; \
exit $$status
# Manual diagnostic only: intentionally absent from verify, CI, and release gates.
relevance-beam:
cd server && go run ./cmd/balance-harness -mode=relevance-beam -root=.. \
-scenario=$(RELEVANCE_SCENARIO) $(if $(RELEVANCE_BEAM_OUTPUT),$(if $(filter /%,$(RELEVANCE_BEAM_OUTPUT)),-output=$(RELEVANCE_BEAM_OUTPUT),-output=../$(RELEVANCE_BEAM_OUTPUT)),)
commons-harness-check:
cd server && go test ./harness -run '^TestCommonsPopulationInvariance$$' -count=1
harness-update:
cd server && go run ./cmd/balance-harness -mode=update -root=.. -workers=$(HARNESS_WORKERS)
epoch-hash:
cd server && go run ./cmd/balance-harness -mode=epoch-hash -root=..
game-ui-copy-candidate:
node client/tools/assemble-game-ui-copy.mjs
game-ui-copy-candidate-check:
node client/tools/assemble-game-ui-copy.mjs --check
copy-generate:
node client/tools/generate-copy.mjs
cd server && go run ./cmd/gen-content-manifest -root=.. -output=deployment/content-manifest.v1.json
copy-check: game-ui-copy-candidate-check
node client/tools/verify-copy.mjs
cd server && go run ./cmd/gen-content-manifest -root=.. -output=deployment/content-manifest.v1.json -check
# D-002 local publication-memory gate. It is intentionally outside CI/release
# dependencies: the owner authorized one fresh-clone proof, not a permanent CI lane.
publication-authority-check:
node tools/verify-publication-authority.mjs --self-test
publication-authority-fresh-clone-check: publication-authority-check
node tools/verify-publication-authority.mjs --fresh-clone
vet:
cd server && go vet $(GO_PACKAGES)
fuzz:
cd server && go test ./decimal -run '^$$' -fuzz '^FuzzCanonicalRoundTrip$$'
fuzz-ci:
cd server && go test ./decimal -run '^$$' -fuzz '^FuzzCanonicalRoundTrip$$' -fuzztime=30s
verify-schema:
node client/tools/verify-schema.mjs
verify-routes-boundary:
@imports=$$(cd server && go list -f '{{range .Imports}}{{println .}}{{end}}' ./routes) || { echo 'routes import enumeration failed' >&2; exit 1; }; unexpected=$$(printf '%s\n' "$$imports" | grep '^cloud-clicker/server/' | grep -vx 'cloud-clicker/server/decimal' || true); if [ -n "$$unexpected" ]; then echo "routes package has disallowed internal imports:" >&2; echo "$$unexpected" >&2; exit 1; fi
verify-commons-boundary:
@if cd server && go list -deps ./commons | grep -qx 'cloud-clicker/server/production'; then echo 'commons package must not import production' >&2; exit 1; fi
@if cd server && go list -deps ./production | grep -qx 'cloud-clicker/server/commons'; then echo 'production package must not import commons' >&2; exit 1; fi
verify-client-boundary:
node client/tools/verify-shell-boundaries.mjs
verify-kernel-version:
node client/tools/verify-ci-kernel-history.mjs
node client/tools/verify-ci-kernel-history-fixtures.mjs
node client/tools/verify-kernel-version.mjs
node client/tools/verify-kernel-version-fixtures.mjs
verify-ci-topology:
node client/tools/verify-ci-topology.mjs
node client/tools/verify-ci-topology-fixtures.mjs
verify-combat-boundary:
node client/tools/verify-combat-boundaries.mjs
verify-combat-go-boundary:
cd server && go run ./cmd/check-combat-boundary -root=..
verify-meters-boundary:
node client/tools/verify-meters-boundaries.mjs
verify-achievements-boundary:
node client/tools/verify-achievements-boundaries.mjs
verify-cosmetic-boundary:
node client/tools/verify-cosmetic-boundaries.mjs
verify-no-payment:
node client/tools/verify-no-payment.mjs
verify-server: vet test-go pitch-corpus-check formulas-check api-check harness-check verify-routes-boundary verify-commons-boundary verify-combat-go-boundary
verify-server-core: vet test-go-core pitch-corpus-check formulas-check api-check verify-routes-boundary verify-commons-boundary verify-combat-go-boundary
reputation-harness-check:
cd server && CLOUD_CLICKER_REPUTATION_EXHAUSTIVE=1 go test -p 1 -count=1 -timeout 60m ./harness -run '^(TestReputationTreeRelevance|TestReputationCareerStartersShortenRunThree)$$' -v
verify-harness-fast: test-harness t0-t1-role-check commons-harness-check harness-guard-check
verify-harness: test-harness t0-t1-role-check harness-check
verify-server-ci:
docker compose -f compose.save-test.yml -f compose.ci-test.yml run --rm test sh -c 'cd /workspace && make verify-server-core'
verify-harness-ci:
docker compose -f compose.save-test.yml -f compose.ci-test.yml run --rm test sh -c 'cd /workspace && make verify-harness HARNESS_WORKERS=$(HARNESS_WORKERS)'
verify-client: typecheck build-client test-client verify-client-boundary verify-kernel-version verify-ci-topology verify-combat-boundary verify-meters-boundary verify-achievements-boundary verify-cosmetic-boundary verify-no-payment copy-check
verify-push: verify-server-core verify-harness-fast verify-client test-browser test-game-ui-composed verify-schema
verify: verify-server verify-client verify-schema test-browser