theslopmachine 0.6.2 → 0.7.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/MANUAL.md +21 -6
- package/README.md +55 -7
- package/RELEASE.md +16 -1
- package/assets/agents/developer.md +41 -1
- package/assets/agents/slopmachine-claude.md +101 -60
- package/assets/agents/slopmachine.md +40 -17
- package/assets/claude/agents/developer.md +42 -5
- package/assets/skills/clarification-gate/SKILL.md +25 -5
- package/assets/skills/claude-worker-management/SKILL.md +290 -57
- package/assets/skills/developer-session-lifecycle/SKILL.md +83 -38
- package/assets/skills/development-guidance/SKILL.md +21 -1
- package/assets/skills/evaluation-triage/SKILL.md +34 -23
- package/assets/skills/final-evaluation-orchestration/SKILL.md +88 -50
- package/assets/skills/hardening-gate/SKILL.md +17 -3
- package/assets/skills/integrated-verification/SKILL.md +3 -3
- package/assets/skills/planning-gate/SKILL.md +32 -3
- package/assets/skills/planning-guidance/SKILL.md +72 -13
- package/assets/skills/retrospective-analysis/SKILL.md +2 -2
- package/assets/skills/scaffold-guidance/SKILL.md +129 -124
- package/assets/skills/submission-packaging/SKILL.md +33 -27
- package/assets/skills/verification-gates/SKILL.md +44 -14
- package/assets/slopmachine/backend-evaluation-prompt.md +1 -1
- package/assets/slopmachine/frontend-evaluation-prompt.md +5 -5
- package/assets/slopmachine/scaffold-playbooks/android-kotlin-compose.md +81 -0
- package/assets/slopmachine/scaffold-playbooks/android-kotlin-views.md +191 -0
- package/assets/slopmachine/scaffold-playbooks/android-native-java.md +203 -0
- package/assets/slopmachine/scaffold-playbooks/angular-default.md +181 -0
- package/assets/slopmachine/scaffold-playbooks/backend-baseline.md +142 -0
- package/assets/slopmachine/scaffold-playbooks/backend-family-matrix.md +80 -0
- package/assets/slopmachine/scaffold-playbooks/database-module-matrix.md +80 -0
- package/assets/slopmachine/scaffold-playbooks/django-default.md +166 -0
- package/assets/slopmachine/scaffold-playbooks/docker-baseline.md +189 -0
- package/assets/slopmachine/scaffold-playbooks/docker-shared-contract.md +334 -0
- package/assets/slopmachine/scaffold-playbooks/electron-vite-default.md +124 -0
- package/assets/slopmachine/scaffold-playbooks/expo-react-native-default.md +73 -0
- package/assets/slopmachine/scaffold-playbooks/fastapi-default.md +134 -0
- package/assets/slopmachine/scaffold-playbooks/frontend-baseline.md +160 -0
- package/assets/slopmachine/scaffold-playbooks/frontend-family-matrix.md +134 -0
- package/assets/slopmachine/scaffold-playbooks/generic-unknown-tech-guide.md +136 -0
- package/assets/slopmachine/scaffold-playbooks/go-chi-default.md +160 -0
- package/assets/slopmachine/scaffold-playbooks/ios-linux-portable.md +93 -0
- package/assets/slopmachine/scaffold-playbooks/ios-native-objective-c.md +151 -0
- package/assets/slopmachine/scaffold-playbooks/ios-native-swift.md +188 -0
- package/assets/slopmachine/scaffold-playbooks/laravel-default.md +216 -0
- package/assets/slopmachine/scaffold-playbooks/livewire-default.md +265 -0
- package/assets/slopmachine/scaffold-playbooks/overlay-module-matrix.md +130 -0
- package/assets/slopmachine/scaffold-playbooks/platform-family-matrix.md +79 -0
- package/assets/slopmachine/scaffold-playbooks/selection-matrix.md +72 -0
- package/assets/slopmachine/scaffold-playbooks/spring-boot-default.md +182 -0
- package/assets/slopmachine/scaffold-playbooks/tauri-default.md +80 -0
- package/assets/slopmachine/scaffold-playbooks/vue-vite-default.md +162 -0
- package/assets/slopmachine/scaffold-playbooks/web-default.md +96 -0
- package/assets/slopmachine/templates/AGENTS.md +41 -3
- package/assets/slopmachine/templates/CLAUDE.md +111 -0
- package/assets/slopmachine/test-coverage-prompt.md +561 -0
- package/assets/slopmachine/utils/claude_create_session.mjs +3 -2
- package/assets/slopmachine/utils/claude_live_channel.mjs +188 -0
- package/assets/slopmachine/utils/claude_live_common.mjs +411 -0
- package/assets/slopmachine/utils/claude_live_hook.py +47 -0
- package/assets/slopmachine/utils/claude_live_launch.mjs +187 -0
- package/assets/slopmachine/utils/claude_live_status.mjs +25 -0
- package/assets/slopmachine/utils/claude_live_stop.mjs +46 -0
- package/assets/slopmachine/utils/claude_live_turn.mjs +277 -0
- package/assets/slopmachine/utils/claude_resume_session.mjs +3 -2
- package/assets/slopmachine/utils/claude_wait_for_rate_limit_reset.mjs +23 -0
- package/assets/slopmachine/utils/claude_wait_for_rate_limit_reset.sh +5 -0
- package/assets/slopmachine/utils/claude_worker_common.mjs +361 -4
- package/assets/slopmachine/utils/cleanup_delivery_artifacts.py +4 -0
- package/assets/slopmachine/utils/export_ai_session.mjs +1 -1
- package/assets/slopmachine/utils/normalize_claude_session.py +153 -0
- package/assets/slopmachine/utils/package_claude_session.mjs +123 -0
- package/assets/slopmachine/utils/prepare_strict_audit_workspace.mjs +65 -0
- package/package.json +1 -1
- package/src/constants.js +42 -3
- package/src/init.js +173 -28
- package/src/install.js +156 -8
- package/src/send-data.js +56 -57
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Database Module Matrix
|
|
2
|
+
|
|
3
|
+
Use this after selecting the backend family. This matrix defines the standard database wiring modules we should compose into backend and fullstack scaffolds.
|
|
4
|
+
|
|
5
|
+
## Shared database wiring contract
|
|
6
|
+
|
|
7
|
+
- if the scaffold has persistence, it gets a real `./init_db.sh`
|
|
8
|
+
- app runtime reads one generated config path such as `APP_CONFIG_PATH=/runtime/config/app-config.json`
|
|
9
|
+
- no checked-in `.env`
|
|
10
|
+
- no committed secrets or baked database credentials
|
|
11
|
+
- database services stay internal-only in Compose unless the prompt explicitly requires host access
|
|
12
|
+
- healthchecks are mandatory for every database service container
|
|
13
|
+
- `./run_tests.sh` must prove both app readiness and real database access
|
|
14
|
+
|
|
15
|
+
At the config level, normalize on a small shared shape even when each framework maps it into native config objects:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"database": {
|
|
20
|
+
"driver": "postgresql|mysql|sqlite|mongodb",
|
|
21
|
+
"url": "...",
|
|
22
|
+
"host": "db",
|
|
23
|
+
"port": 5432,
|
|
24
|
+
"name": "app",
|
|
25
|
+
"user": "app",
|
|
26
|
+
"password_file": "/run/secrets/db_password"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Database selection defaults
|
|
32
|
+
|
|
33
|
+
| Prompt / selector state | Default database module | Why |
|
|
34
|
+
| --- | --- | --- |
|
|
35
|
+
| `database=postgresql` | PostgreSQL | neutral service-container SQL default across most backend families |
|
|
36
|
+
| `database=mysql` | MySQL | explicit MySQL ask, existing MySQL estate, or conventional Laravel/MySQL pairing |
|
|
37
|
+
| `database=sqlite` | SQLite | smallest honest local persistence path with no credentials |
|
|
38
|
+
| `database=mongodb` | MongoDB | explicit document-store or flexible-schema requirement |
|
|
39
|
+
| `database=not_specified` | SQLite for the smallest honest baseline | baseline should still prove persistence without forcing a network DB |
|
|
40
|
+
|
|
41
|
+
When the prompt or repo clearly requires a **service-container relational DB** but leaves the engine open, prefer **PostgreSQL** except for conventional PHP/Laravel defaults where MySQL is still reasonable.
|
|
42
|
+
|
|
43
|
+
## Database module matrix
|
|
44
|
+
|
|
45
|
+
| Module | Safe baseline direction | Use it when | Secret / config handling model | Migration / init expectations | Docker composition expectations | State location default |
|
|
46
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
47
|
+
| **PostgreSQL** | PostgreSQL `17.x`, internal service name `db`, healthcheck via `pg_isready` | prompt wants a real service DB, relational integrity, concurrent writers, or production-shaped SQL infrastructure | app reads one generated DSN or config object; local disposable credentials may live in Compose only when the DB is internal-only and clearly documented as throwaway; otherwise mount secret files | `./init_db.sh` waits for healthy DB then runs family-native migrations (Alembic, Django migrations, Flyway/Liquibase, Prisma/TypeORM/Drizzle, Eloquent, goose, etc.); idempotent seed/bootstrap is allowed | `db` service on the internal bridge network, `expose: 5432`, named volume for data, readiness-gated `depends_on`, no host port by default | **service container state** with a named Docker volume |
|
|
48
|
+
| **MySQL** | MySQL `8.4 LTS`, internal service name `db`, healthcheck via `mysqladmin ping` | prompt explicitly wants MySQL/MariaDB-family behavior, existing repo already uses it, or Laravel/PHP conventions dominate | same config-file contract as PostgreSQL; never commit credentials; local-only disposable credentials are acceptable only for isolated internal Compose use | `./init_db.sh` runs framework-native migrations after healthcheck; schema/bootstrap stays idempotent; avoid raw hand-edited SQL dumps as the primary scaffold contract | `db` service on internal network, `expose: 3306`, named volume, readiness-gated startup, no host port unless explicitly requested | **service container state** with a named Docker volume |
|
|
49
|
+
| **SQLite** | SQLite `3` via language/runtime driver, DB file under a mounted runtime path such as `/runtime/data/app.sqlite` | prompt leaves DB open, the scaffold only needs the smallest honest persistence layer, or the backend family is still in category-open baseline mode | no DB credentials; keep the file path in generated runtime config; never commit live `.sqlite` state into the repo | `./init_db.sh` creates the file path, runs schema creation or migrations against that file, and can safely recreate it for tests; once schema evolution becomes real, switch to the family's migration tool instead of ad hoc SQL | **no separate DB service**; the app service mounts a named volume for DB state; app health should still prove it can open/query the file | **local app-mounted state** in a named Docker volume by default; use a gitignored host bind mount only if the prompt explicitly needs inspectable local files |
|
|
50
|
+
| **MongoDB** | MongoDB `8.x`, internal service name `db`, healthcheck via `mongosh --eval 'db.adminCommand({ ping: 1 })'` | prompt explicitly wants a document store, event/log style documents, flexible schema, or an existing Mongo repo | same generated config-file contract; use URI plus optional secret-file paths; local disposable users are acceptable only for internal-only Compose bootstrap | `./init_db.sh` should create users/databases as needed, apply indexes/validators, and seed any baseline documents idempotently; think “indexes + validators + bootstrap” rather than SQL migrations | `db` service on internal network, `expose: 27017`, named volume, healthcheck, readiness-gated startup; no host port by default | **service container state** with a named Docker volume |
|
|
51
|
+
|
|
52
|
+
## Host-volume local state vs service-container state
|
|
53
|
+
|
|
54
|
+
| Database module | Default state model | When to use host-volume local state instead |
|
|
55
|
+
| --- | --- | --- |
|
|
56
|
+
| PostgreSQL | service-container named volume | almost never at scaffold time; only when the prompt explicitly needs inspectable raw database files on the host |
|
|
57
|
+
| MySQL | service-container named volume | almost never at scaffold time; same rule as PostgreSQL |
|
|
58
|
+
| SQLite | app-mounted named volume | when the prompt explicitly wants a visible local file for manual inspection or offline tooling; keep the bind mount gitignored |
|
|
59
|
+
| MongoDB | service-container named volume | almost never at scaffold time |
|
|
60
|
+
|
|
61
|
+
## Family-to-database pairing defaults
|
|
62
|
+
|
|
63
|
+
| Backend family | Smallest honest default when DB is open | Default network/service DB | Notes |
|
|
64
|
+
| --- | --- | --- | --- |
|
|
65
|
+
| Nest + Fastify | SQLite | PostgreSQL | MongoDB only when the prompt explicitly wants a document model |
|
|
66
|
+
| Express + TypeScript | SQLite | PostgreSQL | MySQL is fine when the repo already uses it; not the neutral default |
|
|
67
|
+
| FastAPI | SQLite | PostgreSQL | this is the current category-open backend default pairing |
|
|
68
|
+
| Flask | SQLite | PostgreSQL | stay lightweight unless the prompt proves a larger service DB is needed |
|
|
69
|
+
| Django | SQLite for tiny local scaffolds only | PostgreSQL | keep Django migrations first-class; use DRF only when the API surface warrants it |
|
|
70
|
+
| Spring Boot | SQLite only for rare tiny local baselines | PostgreSQL | MySQL is acceptable for explicit compatibility; PostgreSQL remains the neutral default |
|
|
71
|
+
| Go + Chi / Gin | SQLite | PostgreSQL | use MongoDB only for explicit document-store prompts |
|
|
72
|
+
| Laravel | SQLite for tiny local/test baselines | MySQL | PostgreSQL is still acceptable when the repo or prompt asks for it |
|
|
73
|
+
|
|
74
|
+
## Placeholder handling
|
|
75
|
+
|
|
76
|
+
- `database=not_specified` means **choose the smallest honest DB module for the selected backend family**, not “skip the database.”
|
|
77
|
+
- if both backend family and database are open, default to **FastAPI + SQLite** per the current backend baseline direction.
|
|
78
|
+
- if the prompt explicitly implies a multi-user or production-shaped service DB but does not name an engine, default to **PostgreSQL**.
|
|
79
|
+
- if the prompt says PHP only and leaves the DB open, default to **Laravel + MySQL** unless the repo already standardized on PostgreSQL.
|
|
80
|
+
- `not_specified` never means “commit secrets later”; keep the config-file + secret-file contract from day one.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Django Default Scaffold Playbook
|
|
2
|
+
|
|
3
|
+
Use this playbook when the request explicitly wants Django or when a backend baseline should prefer Django's official project/app bootstrap path over lighter Python microframeworks.
|
|
4
|
+
|
|
5
|
+
Verified lab: `/Users/yohannesakd/code/eaglepoint/demonstration/scaffold-lab/django-baseline`
|
|
6
|
+
|
|
7
|
+
## Goal
|
|
8
|
+
|
|
9
|
+
Create a Django baseline that:
|
|
10
|
+
|
|
11
|
+
- uses Django's official bootstrap path
|
|
12
|
+
- keeps defaults pinned and conservative
|
|
13
|
+
- wires the backend to a safe default database pattern without `.env`
|
|
14
|
+
- ships a real `docker compose up --build` path
|
|
15
|
+
- ships a real containerized `./run_tests.sh`
|
|
16
|
+
- includes minimal real Django tests
|
|
17
|
+
- keeps the README honest about included versus excluded scope
|
|
18
|
+
- stops at baseline infrastructure instead of product-specific features
|
|
19
|
+
|
|
20
|
+
## Runtime contract
|
|
21
|
+
|
|
22
|
+
- required runtime command: `docker compose up --build`
|
|
23
|
+
- required broad test command: `./run_tests.sh`
|
|
24
|
+
- required database bootstrap command: `./init_db.sh`
|
|
25
|
+
- all three commands must be real and working
|
|
26
|
+
|
|
27
|
+
## Official/bootstrap path
|
|
28
|
+
|
|
29
|
+
Per Django's documented admin commands, the baseline should start from:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
django-admin startproject config .
|
|
33
|
+
python manage.py startapp core
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Those commands preserve the standard Django project shape while keeping the app split explicit.
|
|
37
|
+
|
|
38
|
+
## Chosen default stack and version pins
|
|
39
|
+
|
|
40
|
+
- Python `3.12`
|
|
41
|
+
- Django `5.2.6`
|
|
42
|
+
- Gunicorn `23.0.0`
|
|
43
|
+
- SQLite `3` via Django's built-in SQLite backend
|
|
44
|
+
|
|
45
|
+
Why this default won:
|
|
46
|
+
|
|
47
|
+
- Django provides the official project generator, ORM, migrations, and test runner in one maintained stack
|
|
48
|
+
- SQLite keeps the baseline fast, reproducible, and secret-free while still proving real persistence
|
|
49
|
+
- Gunicorn provides a safer container runtime than `runserver` while keeping the app bootstrap simple
|
|
50
|
+
|
|
51
|
+
## Safe database/config pattern
|
|
52
|
+
|
|
53
|
+
- always route runtime configuration through one explicit config file path such as `APP_CONFIG_PATH=/runtime/config/app-config.json`
|
|
54
|
+
- generate that config file at startup with a small bootstrap script instead of committing `.env`
|
|
55
|
+
- generate `SECRET_KEY` into a Docker-managed runtime file under `/runtime/state`
|
|
56
|
+
- default the database to a SQLite file under `/runtime/db/django-baseline.sqlite3` in a named volume
|
|
57
|
+
- provide `./init_db.sh` as the single database bootstrap entrypoint and make it run `python manage.py migrate --noinput`
|
|
58
|
+
|
|
59
|
+
## Docker / Compose pattern
|
|
60
|
+
|
|
61
|
+
- one `app` service
|
|
62
|
+
- one disposable `test` service behind a `test` profile
|
|
63
|
+
- host exposure loopback-bound only via `127.0.0.1::8000`
|
|
64
|
+
- named volumes for runtime config, generated secret state, and SQLite data
|
|
65
|
+
- app healthcheck must hit `/health` inside the container and the endpoint must perform a real DB query
|
|
66
|
+
|
|
67
|
+
## Minimal baseline behavior
|
|
68
|
+
|
|
69
|
+
At scaffold time, include at least:
|
|
70
|
+
|
|
71
|
+
- `GET /health` proves app boot + DB connectivity
|
|
72
|
+
- `POST /items` and `GET /items` prove ORM-backed persistence
|
|
73
|
+
- one migration for the minimal model
|
|
74
|
+
- `python manage.py test` wired into `./run_tests.sh`
|
|
75
|
+
|
|
76
|
+
Minimum real Django tests:
|
|
77
|
+
|
|
78
|
+
- one test for `GET /health`
|
|
79
|
+
- one test for `POST /items` then `GET /items`
|
|
80
|
+
|
|
81
|
+
## README requirements
|
|
82
|
+
|
|
83
|
+
`README.md` must already state:
|
|
84
|
+
|
|
85
|
+
- scaffold status versus implemented scope
|
|
86
|
+
- chosen stack and pinned versions
|
|
87
|
+
- required runtime command: `docker compose up --build`
|
|
88
|
+
- required test command: `./run_tests.sh`
|
|
89
|
+
- database bootstrap command: `./init_db.sh`
|
|
90
|
+
- how runtime config and `SECRET_KEY` are generated safely without `.env`
|
|
91
|
+
- what is intentionally excluded from the baseline
|
|
92
|
+
|
|
93
|
+
## Verification commands actually run for this prototype
|
|
94
|
+
|
|
95
|
+
Commands run in `/Users/yohannesakd/code/eaglepoint/demonstration/scaffold-lab/django-baseline`:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
docker compose build
|
|
99
|
+
docker compose up --build -d
|
|
100
|
+
docker compose ps
|
|
101
|
+
docker compose port app 8000
|
|
102
|
+
curl -fsS "http://127.0.0.1:32781/health"
|
|
103
|
+
curl -fsS -X POST "http://127.0.0.1:32781/items" -H "content-type: application/json" -d '{"name":"manual-smoke-item"}'
|
|
104
|
+
curl -fsS "http://127.0.0.1:32781/items"
|
|
105
|
+
docker compose down --volumes --remove-orphans
|
|
106
|
+
./run_tests.sh
|
|
107
|
+
python3 - <<'PY'
|
|
108
|
+
import signal
|
|
109
|
+
import subprocess
|
|
110
|
+
import time
|
|
111
|
+
from urllib.request import urlopen
|
|
112
|
+
|
|
113
|
+
cwd = "/Users/yohannesakd/code/eaglepoint/demonstration/scaffold-lab/django-baseline"
|
|
114
|
+
proc = subprocess.Popen(["docker", "compose", "up", "--build"], cwd=cwd)
|
|
115
|
+
error = None
|
|
116
|
+
try:
|
|
117
|
+
deadline = time.time() + 180
|
|
118
|
+
while time.time() < deadline:
|
|
119
|
+
try:
|
|
120
|
+
port_output = subprocess.check_output(["docker", "compose", "port", "app", "8000"], cwd=cwd, text=True).strip()
|
|
121
|
+
if port_output:
|
|
122
|
+
host = port_output.split(":")[-1]
|
|
123
|
+
with urlopen(f"http://127.0.0.1:{host}/health", timeout=2) as response:
|
|
124
|
+
body = response.read().decode("utf-8")
|
|
125
|
+
if response.status == 200 and '"status": "ok"' in body and '"database": "ok"' in body:
|
|
126
|
+
print(f"docker compose up --build reached healthy /health on http://127.0.0.1:{host}/health")
|
|
127
|
+
break
|
|
128
|
+
except Exception as exc:
|
|
129
|
+
error = exc
|
|
130
|
+
time.sleep(2)
|
|
131
|
+
else:
|
|
132
|
+
raise RuntimeError(f"django baseline never became ready: {error}")
|
|
133
|
+
finally:
|
|
134
|
+
proc.send_signal(signal.SIGINT)
|
|
135
|
+
try:
|
|
136
|
+
proc.wait(timeout=30)
|
|
137
|
+
except subprocess.TimeoutExpired:
|
|
138
|
+
proc.kill()
|
|
139
|
+
proc.wait(timeout=30)
|
|
140
|
+
subprocess.run(["docker", "compose", "down", "-v", "--remove-orphans"], cwd=cwd, check=True)
|
|
141
|
+
PY
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Observed results:
|
|
145
|
+
|
|
146
|
+
- `docker compose ps` reported the app `Up ... (healthy)` and `docker compose port app 8000` returned `127.0.0.1:32781`
|
|
147
|
+
- `GET /health` returned `{"status": "ok", "database": "ok", "app_name": "django-baseline-lab", "environment": "development"}`
|
|
148
|
+
- `POST /items` returned `{"id": 1, "name": "manual-smoke-item", "created_at": "2026-04-14T21:27:33.090Z"}`
|
|
149
|
+
- `GET /items` returned `[{"id": 1, "name": "manual-smoke-item", "created_at": "2026-04-14T21:27:33.090Z"}]`
|
|
150
|
+
- `./run_tests.sh` reported `Ran 2 tests in 0.005s`, `OK`, and `Django smoke check passed at http://127.0.0.1:32794`
|
|
151
|
+
- the interactive `docker compose up --build` path reached a healthy `/health` endpoint and printed `docker compose up --build reached healthy /health on http://127.0.0.1:32796/health`
|
|
152
|
+
|
|
153
|
+
## Known caveats
|
|
154
|
+
|
|
155
|
+
- this baseline uses direct Django migrations for the initial schema; introduce more advanced migration workflows only when the prompt grows beyond a baseline lab
|
|
156
|
+
- SQLite is the safe default for local baseline work, not a production recommendation for concurrent high-write deployments
|
|
157
|
+
- admin/auth are present only as framework defaults; they are not configured as finished product features
|
|
158
|
+
|
|
159
|
+
## What to reuse later
|
|
160
|
+
|
|
161
|
+
- Django official bootstrap shape
|
|
162
|
+
- config-file-plus-secret-file runtime pattern instead of `.env`
|
|
163
|
+
- Docker-first runtime contract
|
|
164
|
+
- `./run_tests.sh` wrapper pattern
|
|
165
|
+
- `./init_db.sh` as the single DB bootstrap entrypoint
|
|
166
|
+
- health endpoint + one persistence-backed route as the minimum real backend proof
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Docker Baseline Scaffold Playbook
|
|
2
|
+
|
|
3
|
+
Use this playbook when a scaffold needs a reusable Docker-first baseline rather than product-specific feature work.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Ship a Docker/Compose baseline that is honest, reusable, and already verified in a minimal frontend + backend + database lab.
|
|
8
|
+
|
|
9
|
+
## Runtime contract
|
|
10
|
+
|
|
11
|
+
- required runtime command: `docker compose up --build`
|
|
12
|
+
- preferred readiness check in automation: `docker compose up --build --wait`
|
|
13
|
+
- required broad test command: `./run_tests.sh`
|
|
14
|
+
- no `.env` dependency for the baseline itself
|
|
15
|
+
|
|
16
|
+
Verified lab: `/Users/yohannesakd/code/eaglepoint/demonstration/scaffold-lab/docker-baseline`
|
|
17
|
+
|
|
18
|
+
## Compose defaults
|
|
19
|
+
|
|
20
|
+
- use `compose.yaml`
|
|
21
|
+
- set an explicit project name with `name:` or require a clean `COMPOSE_PROJECT_NAME`
|
|
22
|
+
- expose only one app-facing edge service to the host by default
|
|
23
|
+
- keep backend, worker, and database services on the internal bridge network only
|
|
24
|
+
- put test runners behind a `test` profile or run them with `docker compose run --rm ...`
|
|
25
|
+
- use `depends_on: condition: service_healthy` for readiness gating, not just start order
|
|
26
|
+
- keep runtime defaults in Compose only when they are clearly local-only and non-sensitive
|
|
27
|
+
|
|
28
|
+
## Known good service shape
|
|
29
|
+
|
|
30
|
+
- `frontend`: edge/static or reverse-proxy container; only host-published service
|
|
31
|
+
- `backend`: app container; internal only; healthchecked over localhost inside container
|
|
32
|
+
- `db`: stateful service with named volume and native healthcheck
|
|
33
|
+
- `tester`: containerized verification runner, excluded from default runtime path
|
|
34
|
+
|
|
35
|
+
## Dockerfile patterns
|
|
36
|
+
|
|
37
|
+
- use slim official base images
|
|
38
|
+
- run as non-root where practical
|
|
39
|
+
- copy dependency manifests before source to preserve build cache hits
|
|
40
|
+
- use BuildKit cache mounts for package managers when supported
|
|
41
|
+
- keep entrypoints single-purpose and deterministic
|
|
42
|
+
- prefer one process per container
|
|
43
|
+
|
|
44
|
+
Lab patterns that worked:
|
|
45
|
+
|
|
46
|
+
- backend: `python:3.12-slim`, non-root `appuser`, `pip` cache mount, `CMD ["python", "-m", "app.server"]`
|
|
47
|
+
- frontend: `nginxinc/nginx-unprivileged:1.27-alpine`
|
|
48
|
+
|
|
49
|
+
## Secret / bootstrap rules
|
|
50
|
+
|
|
51
|
+
- baseline must not require a checked-in `.env`
|
|
52
|
+
- never bake production secrets into images or Compose
|
|
53
|
+
- for local-only scaffold bootstrap, generate any disposable credentials or runtime values into ignored files or Docker-managed runtime state instead of checking placeholder literals into tracked files
|
|
54
|
+
- production or shared-environment secrets must come from external overrides, secret stores, CI injection, or generated ignored files
|
|
55
|
+
- document every required secret/input explicitly instead of relying on hidden env state
|
|
56
|
+
|
|
57
|
+
## Port / network rules
|
|
58
|
+
|
|
59
|
+
- bind host ports to loopback by default: `127.0.0.1:HOST_PORT:CONTAINER_PORT`
|
|
60
|
+
- prefer high, low-collision local ports
|
|
61
|
+
- publish only the edge service to host
|
|
62
|
+
- use `expose` for internal services instead of `ports`
|
|
63
|
+
- use one explicit bridge network unless the scaffold has a real reason for more
|
|
64
|
+
|
|
65
|
+
Known good lab default:
|
|
66
|
+
|
|
67
|
+
- frontend published on `127.0.0.1:18080:8080`
|
|
68
|
+
- backend exposed internally on `8000`
|
|
69
|
+
- Postgres exposed internally on `5432`
|
|
70
|
+
|
|
71
|
+
## Healthcheck / readiness rules
|
|
72
|
+
|
|
73
|
+
- every long-running runtime service gets a real healthcheck
|
|
74
|
+
- database healthcheck should use its native readiness command (`pg_isready`, `mysqladmin ping`, etc.)
|
|
75
|
+
- backend healthcheck should hit an in-container localhost readiness endpoint that checks critical dependencies
|
|
76
|
+
- edge service healthcheck should hit its own local health endpoint
|
|
77
|
+
- downstream services should wait on upstream health, not arbitrary sleeps
|
|
78
|
+
- `docker compose up --build --wait` is the honest CI/readiness path
|
|
79
|
+
|
|
80
|
+
Known good lab behavior:
|
|
81
|
+
|
|
82
|
+
- db healthy first
|
|
83
|
+
- backend starts only after db healthy and its `/healthz` performs a real Postgres query
|
|
84
|
+
- frontend starts only after backend healthy
|
|
85
|
+
|
|
86
|
+
## Cache / volume guidance
|
|
87
|
+
|
|
88
|
+
- use named volumes only for mutable service state that should survive container replacement
|
|
89
|
+
- use build cache mounts for package manager caches
|
|
90
|
+
- avoid bind-mounting package caches as a default scaffold behavior
|
|
91
|
+
- keep test runners stateless and disposable
|
|
92
|
+
- default cleanup path should support `docker compose down -v --remove-orphans`
|
|
93
|
+
|
|
94
|
+
Known good lab defaults:
|
|
95
|
+
|
|
96
|
+
- named volume: Postgres data only
|
|
97
|
+
- build cache mount: pip cache in backend image build
|
|
98
|
+
|
|
99
|
+
## `./run_tests.sh` containerization rules
|
|
100
|
+
|
|
101
|
+
- `./run_tests.sh` owns the portable broad-path verification
|
|
102
|
+
- it should start the stack with `docker compose up -d --build --wait`
|
|
103
|
+
- it should run a dedicated test container with `docker compose run --rm tester`
|
|
104
|
+
- it should clean up containers, networks, and named volumes on exit
|
|
105
|
+
- tests should verify edge reachability, backend readiness, and database connectivity from containers, not just static lint output
|
|
106
|
+
|
|
107
|
+
Known good lab behavior:
|
|
108
|
+
|
|
109
|
+
- `tester` uses the backend image plus a mounted verification script
|
|
110
|
+
- runtime stack stays out of the test profile
|
|
111
|
+
- cleanup removes one-off tester containers before `down`
|
|
112
|
+
|
|
113
|
+
## Verification commands actually run
|
|
114
|
+
|
|
115
|
+
All commands below were executed against the lab directory above.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
docker compose -f "/Users/yohannesakd/code/eaglepoint/demonstration/scaffold-lab/docker-baseline/compose.yaml" config
|
|
119
|
+
|
|
120
|
+
docker compose -f "/Users/yohannesakd/code/eaglepoint/demonstration/scaffold-lab/docker-baseline/compose.yaml" up -d --build --wait
|
|
121
|
+
|
|
122
|
+
curl -fsS "http://127.0.0.1:18080/api/message"
|
|
123
|
+
|
|
124
|
+
./run_tests.sh
|
|
125
|
+
|
|
126
|
+
python3 - <<'PY'
|
|
127
|
+
import signal
|
|
128
|
+
import subprocess
|
|
129
|
+
import time
|
|
130
|
+
from urllib.request import urlopen
|
|
131
|
+
|
|
132
|
+
cwd = "/Users/yohannesakd/code/eaglepoint/demonstration/scaffold-lab/docker-baseline"
|
|
133
|
+
proc = subprocess.Popen(["docker", "compose", "up", "--build"], cwd=cwd)
|
|
134
|
+
error = None
|
|
135
|
+
try:
|
|
136
|
+
deadline = time.time() + 180
|
|
137
|
+
while time.time() < deadline:
|
|
138
|
+
try:
|
|
139
|
+
with urlopen("http://127.0.0.1:18080/healthz", timeout=2) as response:
|
|
140
|
+
if response.status == 200:
|
|
141
|
+
print("frontend healthz reached while docker compose up --build was active")
|
|
142
|
+
break
|
|
143
|
+
except Exception as exc:
|
|
144
|
+
error = exc
|
|
145
|
+
time.sleep(2)
|
|
146
|
+
else:
|
|
147
|
+
raise RuntimeError(f"frontend healthz never became ready: {error}")
|
|
148
|
+
finally:
|
|
149
|
+
proc.send_signal(signal.SIGINT)
|
|
150
|
+
try:
|
|
151
|
+
proc.wait(timeout=30)
|
|
152
|
+
except subprocess.TimeoutExpired:
|
|
153
|
+
proc.kill()
|
|
154
|
+
proc.wait(timeout=30)
|
|
155
|
+
subprocess.run(["docker", "compose", "down", "-v", "--remove-orphans"], cwd=cwd, check=True)
|
|
156
|
+
PY
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Observed results:
|
|
160
|
+
|
|
161
|
+
- `docker compose config` resolved cleanly
|
|
162
|
+
- `docker compose up -d --build --wait` reached healthy frontend/backend/db state
|
|
163
|
+
- `curl` returned the proxied backend payload through the frontend edge container
|
|
164
|
+
- `./run_tests.sh` passed and printed `Verified frontend, backend, and database baseline.`
|
|
165
|
+
- the interactive `docker compose up --build` path reached frontend readiness before shutdown
|
|
166
|
+
|
|
167
|
+
## Recommended defaults to reuse later
|
|
168
|
+
|
|
169
|
+
- Docker-first runtime contract with `docker compose up --build`
|
|
170
|
+
- `compose.yaml` plus explicit project naming
|
|
171
|
+
- one host-published edge service, loopback-bound, high port
|
|
172
|
+
- internal-only backend and db with `expose`
|
|
173
|
+
- healthchecks on every runtime service
|
|
174
|
+
- readiness-gated `depends_on`
|
|
175
|
+
- non-root containers where practical
|
|
176
|
+
- build cache mounts for dependencies; named volumes only for persistent state
|
|
177
|
+
- containerized `./run_tests.sh` with disposable tester service
|
|
178
|
+
- no `.env` dependency in the baseline itself
|
|
179
|
+
|
|
180
|
+
## Acceptance checklist
|
|
181
|
+
|
|
182
|
+
Baseline is acceptable when:
|
|
183
|
+
|
|
184
|
+
- `docker compose up --build` works
|
|
185
|
+
- `./run_tests.sh` works
|
|
186
|
+
- the sample proves frontend/backend/db composition
|
|
187
|
+
- host port exposure is minimal and loopback-bound
|
|
188
|
+
- healthchecks drive readiness cleanly
|
|
189
|
+
- the baseline does not rely on `.env`
|