KB-4F44

D28 — Deploy Build Verify Pack Report (Tier 1: Build Verify Only)

11 min read Revision 1
dieu28reportbuild-verifytier-1phase-1b-followup2026-05-10

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.md rev2


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

  1. B1 path BLOCKED → switched to B3-variant. docker compose -f docker-compose.local.yml run --rm --no-deps web failed at compose validation because directus service declares env_file: - .env.local and the file is absent on host. --no-deps does not bypass config-time env_file existence check. Resolution: built dev image directly via docker build -f web/Dockerfile.local -t d28-build-verify:tmp web/, then docker 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.

  2. PM ambiguity (informational). Both pnpm-lock.yaml and package-lock.json exist in web/. Per prompt's PM detection chain (pnpm checked first), pnpm wins. Dockerfile.local confirms canonical pnpm. The orphan package-lock.json may be tech-debt — not addressed in this pack.

  3. Secret scan FAIL on build log = false positive. Two matches for password are Nuxt-emitted chunk filenames forgot-password.*.mjs corresponding to the existing /forgot-password page route. No actual credential value present. Documented above; safe-tail print used grep -v -i -E 'token|secret|bearer|password|authorization' so no matched line was emitted to assistant/user.

  4. 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.

  5. 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 --porcelain empty; web/.nuxt, web/.output, web/node_modules all absent on host.

  6. 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