Feature 01 task 4: Makefile and multi-stage Dockerfile #5

Member

Implements task e86810e7 (Feature 01, task 4 of 6).

Done-condition: Makefile provides dev (Go + Vite), test (gofmt check, go vet, go test -race, npm typecheck/test), build (npm build -> copy to internal/frontend/dist -> CGO_ENABLED=0 go build with -ldflags version stamp) and docker targets. Dockerfile has a Node stage, a Go stage, and a minimal runtime stage running as non-root with /data volume, EXPOSE 8080, HEALTHCHECK /health; base images pinned by digest.

Evidence (orchestrator-run): make test exit 0 (gofmt clean, go vet, go test -race all packages, npm typecheck, Vitest 1/1); make build exit 0 producing bin/mealquest with version stamp 4dfc7ba; docker build . succeeded; docker run /health -> HTTP 200 {"status":"ok","version":"verify"}, / -> 200, uid 10001 (non-root), inspect user=10001:10001.

Dockerfile is at the repository root so docker build . works as the done-condition requires; the CTXD layout names deploy/Dockerfile. Discrepancy recorded; no second Dockerfile created.

Base: feature/19ad7feb-aba0-4b76-ab70-c47d71ed5e76-repository-scaffold-and-ci (85569b4).

Implements task e86810e7 (Feature 01, task 4 of 6). Done-condition: Makefile provides dev (Go + Vite), test (gofmt check, go vet, go test -race, npm typecheck/test), build (npm build -> copy to internal/frontend/dist -> CGO_ENABLED=0 go build with -ldflags version stamp) and docker targets. Dockerfile has a Node stage, a Go stage, and a minimal runtime stage running as non-root with /data volume, EXPOSE 8080, HEALTHCHECK /health; base images pinned by digest. Evidence (orchestrator-run): make test exit 0 (gofmt clean, go vet, go test -race all packages, npm typecheck, Vitest 1/1); make build exit 0 producing bin/mealquest with version stamp 4dfc7ba; docker build . succeeded; docker run /health -> HTTP 200 {"status":"ok","version":"verify"}, / -> 200, uid 10001 (non-root), inspect user=10001:10001. Dockerfile is at the repository root so `docker build .` works as the done-condition requires; the CTXD layout names deploy/Dockerfile. Discrepancy recorded; no second Dockerfile created. Base: feature/19ad7feb-aba0-4b76-ab70-c47d71ed5e76-repository-scaffold-and-ci (85569b4).
dev_agent added 1 commit 2026-10-02 22:51:44 -04:00
Add Makefile dev/test/docker targets and multi-stage Dockerfile
Independent review Not signed off — 0 high, 2 medium (#5)
4dfc7baa2d
Makefile: dev runs the Go server (:8080) and Vite (:5173) together and
stops both on Ctrl-C; test runs the gofmt check, go vet, go test -race and
npm ci/typecheck/test; build stamps the version via -ldflags into a static
CGO_ENABLED=0 binary; docker builds the root Dockerfile. VERSION defaults
to git describe with a dev fallback.

Dockerfile: digest-pinned Node 22, Go 1.25 and Alpine 3.22 stages; the
runtime runs as uid 10001 with /data as a volume, EXPOSE 8080 and a
HEALTHCHECK on /health. .dockerignore keeps VCS, design, dependencies,
build output, databases and secrets out of the context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review_agent left a comment
Member

Independent review — Codex gpt-6-sol, reasoning high

Verdict: CHANGES_REQUIRED
Reviewed SHA: 4dfc7baa2d818a26825dbf0019795fdb707aaf43

{
  "verdict": "CHANGES_REQUIRED",
  "reviewed_sha": "4dfc7baa2d818a26825dbf0019795fdb707aaf43",
  "summary": "The build targets and Docker stages meet the main requirements, but the health check can report a healthy server as unhealthy and nested private keys can enter the Docker build context. gofmt, go vet, go test -race -count=1, and go build ./... passed. Docker build and run were not repeated in this read-only review.",
  "findings": [
    {
      "id": "F1",
      "severity": "medium",
      "location": "Dockerfile:35",
      "failure_case": "If MEALQUEST_HTTP_ADDR is set to a port other than 8080, the server can run normally while Docker marks the container unhealthy.",
      "evidence": "The server listens on the configured HTTP address, but HEALTHCHECK always requests 127.0.0.1:8080/health.",
      "suggested_correction": "Make the probe use the configured listen port, or provide a separately configurable health-check target."
    },
    {
      "id": "F2",
      "severity": "medium",
      "location": ".dockerignore:19",
      "failure_case": "A local nested file such as web/certs/private.key can be sent to the Docker builder and copied into the Go build stage.",
      "evidence": "The ignore rules use *.pem and *.key without a recursive path pattern, while Dockerfile:16 runs COPY . .",
      "suggested_correction": "Add recursive exclusions such as **/*.pem and **/*.key, and exclude any other directories used for local secrets."
    }
  ]
}

Posted unaltered by review_agent. This is transport, not a second review.

## Independent review — Codex `gpt-6-sol`, reasoning high Verdict: **CHANGES_REQUIRED** Reviewed SHA: `4dfc7baa2d818a26825dbf0019795fdb707aaf43` ```json { "verdict": "CHANGES_REQUIRED", "reviewed_sha": "4dfc7baa2d818a26825dbf0019795fdb707aaf43", "summary": "The build targets and Docker stages meet the main requirements, but the health check can report a healthy server as unhealthy and nested private keys can enter the Docker build context. gofmt, go vet, go test -race -count=1, and go build ./... passed. Docker build and run were not repeated in this read-only review.", "findings": [ { "id": "F1", "severity": "medium", "location": "Dockerfile:35", "failure_case": "If MEALQUEST_HTTP_ADDR is set to a port other than 8080, the server can run normally while Docker marks the container unhealthy.", "evidence": "The server listens on the configured HTTP address, but HEALTHCHECK always requests 127.0.0.1:8080/health.", "suggested_correction": "Make the probe use the configured listen port, or provide a separately configurable health-check target." }, { "id": "F2", "severity": "medium", "location": ".dockerignore:19", "failure_case": "A local nested file such as web/certs/private.key can be sent to the Docker builder and copied into the Go build stage.", "evidence": "The ignore rules use *.pem and *.key without a recursive path pattern, while Dockerfile:16 runs COPY . .", "suggested_correction": "Add recursive exclusions such as **/*.pem and **/*.key, and exclude any other directories used for local secrets." } ] } ``` --- Posted unaltered by `review_agent`. This is transport, not a second review.
dev_agent added 1 commit 2026-10-02 22:56:25 -04:00
Fix review findings F1 and F2 for the Dockerfile and .dockerignore
Independent review Not signed off — 0 high, 2 medium, 1 low (#5)
2e5724d150
F1: derive the HEALTHCHECK port from MEALQUEST_HTTP_ADDR so an overridden
listen port is still probed at /health.
F2: make key/certificate exclusions recursive (**/*.pem, **/*.key) and add
**/*.p12, **/*.pfx, **/*.jks, **/*.keystore so nested local secrets never
enter the build context.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review_agent left a comment
Member

Independent review — Codex gpt-6-sol, reasoning high

Verdict: CHANGES_REQUIRED
Reviewed SHA: 2e5724d15009ee8d41e101d79c4aa6ea7494ba04

{
  "verdict": "CHANGES_REQUIRED",
  "reviewed_sha": "2e5724d15009ee8d41e101d79c4aa6ea7494ba04",
  "summary": "The requested build targets and Docker stages are present. I ran gofmt -l ., go vet ./..., go test -race -count=1 ./..., and go build ./...; all passed. Docker access was denied, so I could not independently run docker build or docker run. Three build and health-check issues remain.",
  "findings": [
    {
      "id": "F1",
      "severity": "medium",
      "location": ".dockerignore:12",
      "failure_case": "After make build, generated internal/frontend/dist assets enter the Docker build context and the Go builder stage, despite the requirement to exclude build outputs.",
      "evidence": ".dockerignore excludes web/dist and bin/ but has no rule for internal/frontend/dist; Dockerfile:16 copies the remaining context before deleting that directory.",
      "suggested_correction": "Exclude generated internal/frontend/dist assets from the Docker context."
    },
    {
      "id": "F2",
      "severity": "medium",
      "location": "Dockerfile:38",
      "failure_case": "With a supported address such as MEALQUEST_HTTP_ADDR=[::1]:8080, the server can listen successfully while the container health check reports it unhealthy.",
      "evidence": "The health check always probes 127.0.0.1, while config.go accepts an IPv6 listen host and the server binds to the configured address.",
      "suggested_correction": "Probe an address that matches the configured listener, including IPv6 and specific interface addresses."
    },
    {
      "id": "F3",
      "severity": "low",
      "location": "Makefile:10",
      "failure_case": "A second make build from an initially clean checkout stamps the version with -dirty solely because the first build replaced the tracked placeholder index.html.",
      "evidence": "VERSION uses git describe --dirty, and the embed target deletes and replaces internal/frontend/dist/index.html, which is tracked.",
      "suggested_correction": "Exclude generated frontend changes when computing the dirty version, or avoid leaving the tracked placeholder modified after building."
    }
  ]
}

Posted unaltered by review_agent. This is transport, not a second review.

## Independent review — Codex `gpt-6-sol`, reasoning high Verdict: **CHANGES_REQUIRED** Reviewed SHA: `2e5724d15009ee8d41e101d79c4aa6ea7494ba04` ```json { "verdict": "CHANGES_REQUIRED", "reviewed_sha": "2e5724d15009ee8d41e101d79c4aa6ea7494ba04", "summary": "The requested build targets and Docker stages are present. I ran gofmt -l ., go vet ./..., go test -race -count=1 ./..., and go build ./...; all passed. Docker access was denied, so I could not independently run docker build or docker run. Three build and health-check issues remain.", "findings": [ { "id": "F1", "severity": "medium", "location": ".dockerignore:12", "failure_case": "After make build, generated internal/frontend/dist assets enter the Docker build context and the Go builder stage, despite the requirement to exclude build outputs.", "evidence": ".dockerignore excludes web/dist and bin/ but has no rule for internal/frontend/dist; Dockerfile:16 copies the remaining context before deleting that directory.", "suggested_correction": "Exclude generated internal/frontend/dist assets from the Docker context." }, { "id": "F2", "severity": "medium", "location": "Dockerfile:38", "failure_case": "With a supported address such as MEALQUEST_HTTP_ADDR=[::1]:8080, the server can listen successfully while the container health check reports it unhealthy.", "evidence": "The health check always probes 127.0.0.1, while config.go accepts an IPv6 listen host and the server binds to the configured address.", "suggested_correction": "Probe an address that matches the configured listener, including IPv6 and specific interface addresses." }, { "id": "F3", "severity": "low", "location": "Makefile:10", "failure_case": "A second make build from an initially clean checkout stamps the version with -dirty solely because the first build replaced the tracked placeholder index.html.", "evidence": "VERSION uses git describe --dirty, and the embed target deletes and replaces internal/frontend/dist/index.html, which is tracked.", "suggested_correction": "Exclude generated frontend changes when computing the dirty version, or avoid leaving the tracked placeholder modified after building." } ] } ``` --- Posted unaltered by `review_agent`. This is transport, not a second review.
dev_agent added 1 commit 2026-10-02 23:03:10 -04:00
Fix review findings F1-F3 for Docker context, health check and version
Independent review Approved — 0 high findings (#5)
9b66ba0ff5
F1: exclude generated internal/frontend/dist from the Docker build context.
F2: derive the health check host from MEALQUEST_HTTP_ADDR, mapping wildcard
hosts and localhost to 127.0.0.1 and keeping IPv6 brackets.
F3: compute the -dirty suffix ignoring the generated embed output, so a
rebuild of a clean checkout is not stamped dirty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review_agent left a comment
Member

Independent review — Codex gpt-6-sol, reasoning high

Verdict: APPROVED
Reviewed SHA: 9b66ba0ff5ee569aac0077fec56f5498d17cd4be

{
  "verdict": "APPROVED",
  "reviewed_sha": "9b66ba0ff5ee569aac0077fec56f5498d17cd4be",
  "summary": "The Makefile, Dockerfile, ignore rules, and tracked files meet the stated done-condition. I ran gofmt -l ., go vet ./..., go test -race -count=1 ./..., and go build ./...; all passed. I did not rerun npm or Docker builds in this read-only review.",
  "findings": []
}

Posted unaltered by review_agent. This is transport, not a second review.

## Independent review — Codex `gpt-6-sol`, reasoning high Verdict: **APPROVED** Reviewed SHA: `9b66ba0ff5ee569aac0077fec56f5498d17cd4be` ```json { "verdict": "APPROVED", "reviewed_sha": "9b66ba0ff5ee569aac0077fec56f5498d17cd4be", "summary": "The Makefile, Dockerfile, ignore rules, and tracked files meet the stated done-condition. I ran gofmt -l ., go vet ./..., go test -race -count=1 ./..., and go build ./...; all passed. I did not rerun npm or Docker builds in this read-only review.", "findings": [] } ``` --- Posted unaltered by `review_agent`. This is transport, not a second review.
approver_agent merged commit c28dff18cd into feature/19ad7feb-aba0-4b76-ab70-c47d71ed5e76-repository-scaffold-and-ci 2026-10-02 23:06:58 -04:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Maniacs_Mansion/MealQuest#5