D28 — Deploy Build Verify Pack Report (Tier 1: Build Verify Only)
D28 — Deploy Build Verify Pack Report
Date: 2026-05-10 | Tier: build_verify_only | Agent: claude-go (VPS SSH) Predecessor: D28 Phase 1B PARTIAL (commits 0947613 + d2db418) Prompt:
knowledge/dev/laws/dieu28-trien-khai/prompts/d28-deploy-build-verify-pack-prompt.mdrev2
Tier
tier=build_verify_only
deploy_performed=false
live_smoke_performed=false
Phase 0 — Infrastructure inventory (READ-ONLY)
package_manager_detected=pnpm
note=both pnpm-lock.yaml and package-lock.json exist on host;
Dockerfile.local canonical: `RUN pnpm install --frozen-lockfile` → pnpm authoritative
typecheck_script_present=true (web/package.json: "typecheck": "nuxt typecheck")
build_script_present=true (web/package.json: "build": "nuxt build")
dev_compose_service_name=web (docker-compose.local.yml services: cloud-sql-proxy, directus, web → web is unambiguous Nuxt service)
build_paths_available=[B1, B2, B3-variant]
B1: docker compose -f docker-compose.local.yml run --rm --no-deps web → BLOCKED (compose validates whole config; directus.env_file=.env.local missing)
B2: docker compose build (production image) → not attempted (would need justification, B3 worked)
B3-variant: docker build -f web/Dockerfile.local + docker run --rm → CHOSEN
recommended_build_path=B1 (originally), CHOSEN B3-variant after B1 BLOCKED
chosen_path_justification=
B1 attempted first per priority, failed at compose validation:
"env file /opt/incomex/docker/nuxt-repo/.env.local not found"
Even with --no-deps, `docker compose ... run` still parses entire compose file and rejects on env_file absence.
Switched to B3-variant: `docker build -f web/Dockerfile.local -t d28-build-verify:tmp web/`
then `docker run --rm d28-build-verify:tmp sh -c '...'`. This:
- reuses the dev Dockerfile (same node:20-alpine + pnpm install --frozen-lockfile path)
- bypasses compose env_file validation
- remains ephemeral (--rm), zero impact on running services
- no host package install, no lockfile change, no bind-mount of ./web
Production `incomex-nuxt` (image nuxt-ssr-local:s174) untouched throughout.
dev_compose_present=true
dev_image_pre_built=false (no pre-existing nuxt-repo dev image; built fresh as d28-build-verify:tmp, then removed)
deploy_mechanism=undocumented_in_pack (deferred to follow-up review pack)
smoke_mechanism_existing=undetermined (deferred — not in scope per prompt)
auto_snapshot_documented=commit 0947613 ("auto-snapshot: 2 files (2026-05-10T04:00)") on HEAD
pre_build_state_clean=true (git status --porcelain empty; no .nuxt/.output/node_modules on host)
HEAD_commits_verified=d2db418 (Phase 1B Option C) + 0947613 (auto-snapshot generator+artifact)
Phase A — Build verify
chosen_build_path=B3-variant (docker build + docker run --rm)
image_build_status=PASS EXIT=0 (Dockerfile.local: pnpm install --frozen-lockfile in container, no host mutation)
image_build_log_secret_scan=PASS
typecheck_status=PASS EXIT=0 (docker run --rm d28-build-verify:tmp sh -c 'cd /app && pnpm typecheck')
typecheck_log_secret_scan=PASS
build_status=PASS EXIT=0 (docker run --rm d28-build-verify:tmp sh -c 'cd /app && pnpm build')
build_log_secret_scan=FAIL_FILENAME_FALSE_POSITIVE
matched_pattern=password (count=2)
matched_lines (post-redaction):
.output/server/chunks/build/forgot-[REDACTED] (4.58 kB) (1.59 kB gzip)
.output/server/chunks/build/forgot-[REDACTED] (2.03 kB) (602 B gzip)
classification=Nuxt build chunk filenames `forgot-password.mjs` (existing route file, not a credential)
no_actual_secret_value_present_in_log=true
no_secret_value_printed_to_assistant_or_user=true
log_safety_pattern_followed=true (file written first, chmod 600, scan before any tail print)
secret_scan_printed_matches=false
log_safety_pattern_followed=true
server_import_verify=PASS (build emitted .output/server/chunks/routes/api/* including new generated map consumers — alias `~/generated/...` resolved)
client_import_verify=PASS (build complete, no module-resolution errors)
ssr_import_verify=PASS (Nuxt build PASS implies SSR bundle linked)
source_tree_mutation_check=PASS
known_build_artifacts_cleaned=[] (no .nuxt/.output/node_modules ever appeared on host; ephemeral container retained them inside its filesystem until --rm)
unexpected_files_detected=[]
post_cleanup_clean=true (git status --porcelain empty)
logs_cleaned=true (3 of my logs removed: d28-build-image-1778387902.log, d28-typecheck-1778388047.log, d28-build-1778388075.log; also earlier failed compose log d28-typecheck-1778387870.log)
temp_files_remaining=5_pre_existing_unrelated (/tmp/d28-insert-run.sql, /tmp/d28-probe.mjs, /tmp/d28-probe2.mjs, /tmp/d28-render.sql, /tmp/d28-verify-counts.sql — from prior D28 sessions, NOT this pack; left untouched per "cleanup only KNOWN safe artifacts" rule)
temp_image_cleaned=true (d28-build-verify:tmp removed via `docker image rm`)
Phase B — Deploy plan (NOT EXECUTED)
deploy_steps_documented=
1. Build production image (e.g. via existing CI or docker build using web/Dockerfile)
2. Tag and load into incomex-nuxt's image reference
3. docker compose -f /opt/incomex/docker/docker-compose.yml up -d incomex-nuxt
(or restart) — this DOES interrupt the running service
deploy_user_facing_impact=container_restart_required
deploy_rollback_plan=git revert d2db418 0947613 + re-deploy
deploy_estimated_duration=undetermined_in_pack (image build ~3-5 min in this verify; deploy switch ~30s)
deploy_traffic_implications=brief_502_during_nuxt_container_restart (nginx fronts; reverse-proxy-cached pages may serve stale)
Phase C — Smoke plan (NOT EXECUTED)
routes_to_smoke_count=21
registry_routes=13 (/knowledge/registries/{catalog,table,module,dot_tool,page,collection,agent,checkpoint_type,checkpoint_set,entity_dependency,checkpoint_instance,changelog,system_issue})
non_registry_pages=3 (/knowledge/workflows, /knowledge/modules, /knowledge/current-tasks)
special=4 (/admin/proposals, /knowledge/registries [index], /knowledge/workflows/[id]?tab=matrix, /knowledge/workflows/[id]?tab=wcr)
api_endpoint=1 (/api/discovery/relations)
relations_endpoint_to_smoke=true
event_outbox_route_smoke=DEFERRED_TO_PHASE_1C (route /knowledge/registries/event_outbox not smoked here)
Attestation
no_deploy=true
no_live_route_smoke=true
no_live_http_call_to_prod=true
no_curl_production_routes=true
no_container_restart=true
no_running_service_interruption=true
no_directus_mutation=true
no_pg_mutation=true
no_publish_event_outbox=true
no_table_registry_mutation=true
no_secret_printed=true
no_log_printed_before_secret_scan=true
no_package_install_on_host=true
no_lockfile_change=true
no_docker_compose_up=true
no_docker_compose_restart_running=true
no_docker_compose_down=true
no_auto_rollback_executed=true
no_unexpected_source_mutation=true
no_fix_tbl_modules_list=true
no_add_entity_type_column=true
no_publish_event_outbox=true
Status
phase_status=PASS
build_verify_status=PASS
typecheck_status=PASS
rollback_executed=false
rollback_recommended=false (build/typecheck PASS — no need)
next_required_pack=D28_DEPLOY_AND_LIVE_SMOKE_PROMPT_REVIEW (per decision matrix: Phase A PASS → review + approval, NOT deploy directly)
rollback_commits=d2db418,0947613
rollback_command_for_user=ssh contabo "cd /opt/incomex/docker/nuxt-repo && git revert d2db418 0947613 --no-edit"
Notes & deviations
-
B1 path BLOCKED → switched to B3-variant.
docker compose -f docker-compose.local.yml run --rm --no-deps webfailed at compose validation becausedirectusservice declaresenv_file: - .env.localand the file is absent on host.--no-depsdoes not bypass config-time env_file existence check. Resolution: built dev image directly viadocker build -f web/Dockerfile.local -t d28-build-verify:tmp web/, thendocker run --rm d28-build-verify:tmp sh -c '...'. Same Dockerfile, same pnpm install path, same ephemeral lifecycle, but no compose env-file validation. Image removed after use. Boundaries preserved. -
PM ambiguity (informational). Both
pnpm-lock.yamlandpackage-lock.jsonexist inweb/. Per prompt's PM detection chain (pnpm checked first), pnpm wins. Dockerfile.local confirms canonical pnpm. The orphanpackage-lock.jsonmay be tech-debt — not addressed in this pack. -
Secret scan FAIL on build log = false positive. Two matches for
passwordare Nuxt-emitted chunk filenamesforgot-password.*.mjscorresponding to the existing/forgot-passwordpage route. No actual credential value present. Documented above; safe-tail print usedgrep -v -i -E 'token|secret|bearer|password|authorization'so no matched line was emitted to assistant/user. -
Pre-existing temp files NOT cleaned. Five
/tmp/d28-*.{sql,mjs}files from prior D28 sessions remain on VPS. Per rule "Cleanup chỉ KNOWN safe artifacts (this pack's outputs)", they were left untouched. List:/tmp/d28-insert-run.sql,/tmp/d28-probe.mjs,/tmp/d28-probe2.mjs,/tmp/d28-render.sql,/tmp/d28-verify-counts.sql. -
No host bind-mount of ./web. B3-variant intentionally did NOT bind-mount
./web:/app(unlike B1 dev compose). Build ran against in-image source (copied at build time). This guarantees zero host source mutation. Verified post-run:git status --porcelainempty;web/.nuxt,web/.output,web/node_modulesall absent on host. -
No auto-rollback. Build PASS — not applicable. Even if FAIL had occurred, prompt mandates
rollback_executed=false(governance decision deferred to User/GPT).
P3D / governance flags (per prompt header — unchanged by this pack)
p3d_resume_allowed=false
tbl_event_outbox.status=draft
notification_display=paused
Decision
Per prompt §"Decision matrix sau pack":
| Phase A result | Next pack |
|---|---|
| PASS | D28_DEPLOY_AND_LIVE_SMOKE_PROMPT_REVIEW ← THIS RUN |
Recommended next step: prepare D28_DEPLOY_AND_LIVE_SMOKE_PROMPT_REVIEW for review + approval. DO NOT deploy directly.
D28 Deploy Build Verify Pack Report | Tier 1: build_verify_only | Status: PASS | 2026-05-10 | Agent: claude-go