# Observability Task 7 Cutover Correction Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Correct the fail-closed Host Nginx/Task 6 evidence interface, preserve the successful Task 6 authority, and complete Task 7 through one Host cutover, one OIDC membership transaction, one monolithic smoke, and one bounded regression pass. **Architecture:** Change only the Host Nginx production script and its focused test for the v2 evidence, first-cutover, command-boundary, and no-contact defects. Keep OIDC, smoke, and renderer production behavior unchanged; correct their orchestration in the central/manual documents, use a fresh inventory-only renderer root, then freeze exact non-Git identities before live execution. **Tech Stack:** Bash 5.2, Python 3.12, Nginx/systemd, Kubernetes/k3s/kubectl, Keycloak Admin API, Prometheus/Alertmanager/Grafana, hardened private `/tmp` handoffs, SHA-256 non-Git source manifests. ## Global Constraints - Do not read, print, hash, copy, or disclose Kubernetes Secret values, Slack webhook data, OAuth codes/state, passwords, cookies, bearer tokens, or private recovery payloads. - Preserve rollback ID `20260814T145009Z` and `/tmp/platform-observability-metrics.dw5gLZ` through Task 7; never use that handoff as complete-render publication output. - Preserve failed IDs `20260814T080303Z`, `20260814T140953Z`, `/tmp/platform-observability-metrics.LNzksC`, and all historical evidence without mutation or deletion. - The accepted inventory pins remain `79688d017d38eec9a6f100f8d0f784a5474e79802046ef1c2c11b30d170b0b0c` and `b1c3049206a1a88165ee672ae9aceac7945673a3bb9c3cf3670b7f0d56c3f291`. - The exact Nginx deny/full pins remain `dbef6d443bcba58b26a5351ea76f6d09f6da8c2ef07a806e22745cf26c88f518` and `7d2de2a92c3597a0859775da1d2ccbf5a3d72c0b2af5cac2439c82311361f801`. - Full cutover accepts only `platform-observability-rules-alerts-v2`; legacy v1 has no fallback. - First full cutover requires exact deny-only active state. `ALREADY_ACTIVE` is a STOP condition, not success. - Before the first full-cutover live preflight or APPLY prompt, the `host-nginx` subtree, ledger, payload directory, and first payload path must each be absent under both existence and symlink tests. Normal existing files/directories are residue and fail closed just like symlinks. - A proof older than 24 hours requires a fresh rollback ID, fresh proof, and fresh Task 6 acceptance. Never recreate only the proof or retry Host under the frozen ID. - Run `observability-smoke.sh --execute` exactly once, after membership reconciliation and external-client readiness. RC `2` remains partial implementation. - OIDC rollback is in-process automatic rollback; do not claim a persistent Keycloak reversal ledger under the Task 6 root. - Every live command gets immediate RC capture. Interpret rollback by stage: pre-transaction/pre-active-config failures have rollback N/A; active transaction failures require exact rollback PASS; ambiguity/manual recovery stops all later tasks. - Any Host ledger/payload/timestamp staging failure consumes the rollback ID for Host purposes: preserve it, create a fresh ID, and repeat source proof plus complete Task 6. Never repair staging or retry Host in place. - Before any operator-shell preparation command, either install the exact production PATH, export it, run `hash -r`, and verify the reviewed command inventory, or use literal absolute paths for every preparation executable. Do not mix an ambient-PATH setup command into a fixed-boundary procedure. - Do not mark existing Task 7 checkboxes until the corresponding live result is observed and independently reviewed. Task 8 remains unstarted. - This workspace is non-Git. Do not commit, create a branch, or claim worktree cleanliness; record exact file metadata and SHA-256 instead. After freeze this implementation plan is immutable authority; track progress only in reports. --- ### Task 1: Make the Host acceptance fixture production-faithful **Files:** - Modify: `scripts/validate/test-apply-host-nginx-observability.sh:102-143` - Modify: `scripts/bootstrap/apply-host-nginx-observability.sh:34,372-423,517-612` - Create: `.superpowers/sdd/2026-08-15-observability-task7-cutover-correction/task-1-report.md` **Interfaces:** - Consumes: Task 6 `access-rules-alerts/acceptance.env`, the two metric inventory hashes, and the same rollback ID. - Produces: `validate_full_evidence()` acceptance only for an exact nine-field v2 record and one of two exact Slack gate tuples. - [ ] **Step 1: Freeze the starting identities** Record SHA-256, mode, UID:GID, nlink, type, and size for the two files. Required starting hashes are: ```text apply = 518119a5e11fe8462c5d41921534351f78a2dd721c1bf69ac638e5bffe83536c test = 1c429f7eb2cb73ec25dacd9fe4fdc5e063f62866c6e36732a3e9116fa68b2f53 ``` If either hash differs, stop for source-drift review before editing. - [ ] **Step 2: Change the focused fixture to v2 and add a legacy-v1 rejection test** Replace the acceptance writer with an exact helper whose default is the successful Task 6 risk tuple: ```bash write_acceptance_v2() { local path=$1 initial_sha=$2 post_sha=$3 local gate=${4:-RISK_ACCEPTED} ref uid case "$gate" in RECOVERY) ref=strict-recovery-evidence-v1 uid=not-applicable ;; RISK_ACCEPTED) ref=2026-08-14-observability-slack-recovery-risk-acceptance-design uid=1000 ;; *) return 2 ;; esac cat >"$path" <`/`` placeholders, smoke `argc=3`, and complete renderer `argc=6`. Require root-owned, non-writable executable targets, safe PATH lineage, exact cardinality, no duplicate/missing row, and no username/Secret value. Add a verifier launcher row for literal `/usr/bin/python3`, the absolute verifier path and expected metadata, fixed digest sentinel ``, and the absolute manifest/sidecar pair (`argc=5`, counting the interpreter as argv[0]). The sentinel must occur exactly once in that row; the prelive/final manifest's own 64-character lowercase digest may not occupy that argv slot or be copied into another manifest-row file. Other independently required file/executable SHA metadata is unaffected. Generate a private comparison independently, require `cmp -s`, then remove only that comparison. Repeat the comparison immediately before live Host execution. This split is mandatory because `task-7-command-inventory.txt` is manifest row 21. Only after all 21 rows and the sidecar exist may independent review write the actual lowercase manifest digest to the non-governed `task-5-report`/review handoff. The launcher reads no digest from the governed inventory; the controller supplies the report's reviewed literal directly as verifier argv[2]. Add tests that replace the governed verifier row's sentinel slot with a 64-character manifest digest and require rejection, require sentinel exact-once plus exact argc/path/metadata shape, and refuse verifier invocation until the external report pin exists and matches the reviewed value. The command inventory validator uses the same non-widening metadata policy as Task 2: exact canonical `/usr/bin/sudo` is the only row permitted and required to carry owner-setuid and must be exact `regular|0:0|4755|1`; every other executable row forbids both setuid and setgid as well as group/world write. A private comparison with any sudo or non-sudo bit/path/owner/link drift must fail. Use these paths, once each and in this order: ```text /home/donghyeon/workspace/platform/scripts/bootstrap/apply-host-nginx-observability.sh /home/donghyeon/workspace/platform/scripts/validate/test-apply-host-nginx-observability.sh /home/donghyeon/workspace/platform/scripts/validate/test-host-nginx-observability-source.sh /home/donghyeon/workspace/platform/infrastructure/networking/host-nginx/learn-services-admin.conf /home/donghyeon/workspace/platform/infrastructure/networking/host-nginx/learn-services-metrics-guard.conf /home/donghyeon/workspace/platform/infrastructure/networking/host-nginx/learn-services-grafana-deny-guard.conf /home/donghyeon/workspace/platform/infrastructure/networking/host-nginx/learn-services-observability.conf /home/donghyeon/workspace/platform/scripts/bootstrap/apply-observability-access.sh /home/donghyeon/workspace/platform/scripts/validate/test-apply-observability-access.sh /home/donghyeon/workspace/platform/scripts/validate/render-observability-access.sh /home/donghyeon/workspace/platform/scripts/validate/test-render-observability-access.sh /home/donghyeon/workspace/platform/scripts/bootstrap/configure-keycloak-grafana-oidc.sh /home/donghyeon/workspace/platform/scripts/validate/test-configure-keycloak-grafana-oidc.sh /home/donghyeon/workspace/platform/scripts/validate/observability-smoke.sh /home/donghyeon/workspace/platform/scripts/validate/test-observability-smoke.sh /home/donghyeon/workspace/platform/bootstrap/manual/phase4-observability-access.md /home/donghyeon/workspace/docs/platform/plans/2026-08-14-observability-slack-risk-acceptance-implementation.md /home/donghyeon/workspace/docs/platform/plans/2026-08-15-observability-task7-cutover-correction-design.md /home/donghyeon/workspace/platform/docs/superpowers/plans/2026-08-15-observability-task7-cutover-correction.md /home/donghyeon/workspace/docs/platform/runbooks/2026-08-13-observability-phase4-resume-worklog.md /home/donghyeon/workspace/platform/.superpowers/sdd/2026-08-15-observability-task7-cutover-correction/baseline/task-7-command-inventory.txt ``` The verifier, manifest, and sidecar themselves are not rows. Create the sidecar with the literal canonical manifest path, pin the verifier separately as specified in Step 3, and run the descriptor-retaining strict launcher twice with the externally reviewed lowercase manifest SHA and literal absolute paths. Require RC `0` both times and unchanged verifier/manifest/sidecar descriptor/path identities after each invocation. - [ ] **Step 5: Independent pre-live review** Review spec coverage, C/I/M, v2 tuple matrix, first-cutover behavior, fixed PATH and command/launcher residue rejection, exact 21-row inventory, self-reference-free sentinel row plus external digest pin, separately pinned verifier identity and rebind tests, both correction authority documents, RED provenance, mutation evidence, original handoff preservation, corrected OIDC/smoke sequencing, scanner result, and manifest/sidecar identity. Required verdict: Critical `0`, Important `0`, Spec YES, Quality Approved, Ready for Host Task 7 YES. Any other verdict returns to the responsible source task and requires a new scan/freeze after correction. --- ### Task 6: Execute the single Host Nginx full cutover **Files:** - Preserve: `/var/lib/hyeonworks/platform-rollbacks/observability-20260814T145009Z` - Preserve: `/tmp/platform-observability-metrics.dw5gLZ` - Append after outcome: `.superpowers/sdd/2026-08-15-observability-task7-cutover-correction/task-6-report.md` **Interfaces:** - Consumes: Task 5 exact reviewed freeze, same Task 6 ID/root/handoff, current source proof, active deny-only config. - Produces: full Host Nginx PASS or a preserved fail-closed transaction that is never retried under this ID. - [ ] **Step 1: Bind the successful Task 6 identity in the existing operator shell** Before any external preparation command, install the reviewed operator boundary and invalidate Bash's command cache: ```bash TASK7_OPERATOR_PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PATH=$TASK7_OPERATOR_PATH export PATH hash -r ``` Using the exact Task 5 generation procedure and only commands resolved under that boundary, regenerate the command/launcher inventory into a fresh private comparison and require byte equality with the reviewed inventory. Require every lexical/canonical command row to match before continuing. If this comparison cannot be completed first, stop; do not run an ambient-PATH prep command. Then run short assignments only: ```bash cd /home/donghyeon/workspace/platform TASK7_ID=20260814T145009Z TASK7_METRIC_ROOT=/tmp/platform-observability-metrics.dw5gLZ export PLATFORM_OBSERVABILITY_ROLLBACK_ID="$TASK7_ID" [[ "$PLATFORM_OBSERVABILITY_ROLLBACK_ID" == "$TASK7_ID" ]] [[ "$(/usr/bin/readlink -f -- "$TASK7_METRIC_ROOT")" == "$TASK7_METRIC_ROOT" ]] [[ "$(/usr/bin/stat -c '%F|%u:%g|%a|%h' -- "$TASK7_METRIC_ROOT")" == 'directory|1000:1000|700|4' ]] ``` Use literal `/usr/bin/find`, `/usr/bin/sort`, `/usr/bin/stat`, and `/usr/bin/sha256sum` for the remaining preparation checks. Require the root name-only topology to be exactly two phase directories plus `dashboards.yaml`, `rules.yaml`, and `alertmanager.yaml`. Verify both known inventory hashes and metadata without printing inventory bodies. - [ ] **Step 2: Revalidate the frozen source and no-contact dry-run** Run the Task 5 descriptor-retaining strict launcher twice, exactly as independently reviewed: literal `/usr/bin/python3`, literal absolute verifier path, literal reviewed 64-character manifest SHA, and literal absolute prelive manifest/sidecar paths. The SHA must come from the non-governed task-5 review handoff, never from the command-inventory sentinel or another manifest-row file. Require RC `0` and unchanged separately pinned verifier SHA/type/owner/mode/nlink/size/canonical-path identity before and after both invocations. Then build `TASK7_HOST_DRY=(/usr/bin/bash /home/donghyeon/workspace/platform/scripts/bootstrap/apply-host-nginx-observability.sh)` and invoke it once under the same errexit save/`set +e`/immediate RC/restore envelope used by execute. Require: ```text HOST_NGINX_ACTIVE_SHA256=dbef6d443bcba58b26a5351ea76f6d09f6da8c2ef07a806e22745cf26c88f518 HOST_NGINX_GRAFANA_DENY_GUARD_SHA256=dbef6d443bcba58b26a5351ea76f6d09f6da8c2ef07a806e22745cf26c88f518 HOST_NGINX_FULL_SHA256=7d2de2a92c3597a0859775da1d2ccbf5a3d72c0b2af5cac2439c82311361f801 HOST_NGINX_CERTIFICATE_EXPECTED_SAN=grafana.learn.hyeonworks.com HOST_NGINX_CERTIFICATE_SAN=NOT_CHECKED_DRY_RUN HOST_NGINX_GRAFANA_PUBLIC_DNS=NOT_CHECKED_DRY_RUN HOST_NGINX_OBSERVABILITY_DRY_RUN=PASS TASK7_HOST_DRY_RC=0 ``` Also require legacy dry-run claims `HOST_NGINX_CERTIFICATE_EXACT_SAN=` and `HOST_NGINX_GRAFANA_PUBLIC_DNS=ABSENT` to be absent. Any different/unreadable/already-full active hash stops before sudo or execute. - [ ] **Step 3: Refresh sudo and build a paste-safe argv array** Run `/usr/bin/sudo -v`, then require `/usr/bin/sudo -n /usr/bin/true` and print only `SUDO_READY`. Build the command without placing the long option in pasted text: ```bash TASK7_VOD=--verified TASK7_VOD+=-output-dir TASK7_HOST=(/usr/bin/bash) TASK7_HOST+=(/home/donghyeon/workspace/platform/scripts/bootstrap/apply-host-nginx-observability.sh) TASK7_HOST+=(--execute) TASK7_HOST+=("$TASK7_VOD") TASK7_HOST+=("$TASK7_METRIC_ROOT") [[ "${#TASK7_HOST[@]}" -eq 5 ]] printf 'TASK7_HOST_ARGC=%d\n' "${#TASK7_HOST[@]}" ``` Do not print `%q` for usernames or sensitive data; this Host argv contains only safe paths/options. - [ ] **Step 4: Execute exactly once and capture immediate RC** ```bash case $- in *e*) TASK7_HOST_ERREXIT=1 ;; *) TASK7_HOST_ERREXIT=0 ;; esac set +e "${TASK7_HOST[@]}" TASK7_HOST_RC=$? printf 'TASK7_HOST_RC=%d\n' "$TASK7_HOST_RC" (( TASK7_HOST_ERREXIT == 0 )) || set -e ``` Before entering anything, require exact pre-prompt stage and previous/candidate hashes. Only the operator enters `APPLY`. The prompt itself is admissible only after no-follow absence checks have proved all four paths absent under both `test -e` and `test -L`: ```text /var/lib/hyeonworks/platform-rollbacks/observability-20260814T145009Z/host-nginx /var/lib/hyeonworks/platform-rollbacks/observability-20260814T145009Z/host-nginx/stages.tsv /var/lib/hyeonworks/platform-rollbacks/observability-20260814T145009Z/host-nginx/payloads /var/lib/hyeonworks/platform-rollbacks/observability-20260814T145009Z/host-nginx/payloads/full-prior-0001.conf ``` An existing normal path or symlink is a pre-prompt STOP, not resumable ledger state. Success requires exact `HOST_NGINX_FULL_STAGE=PASS` and RC `0`. `ALREADY_ACTIVE`, missing PASS, nonzero or ambiguous RC is failure. - [ ] **Step 5: Classify failure without retry** - Pre-prompt failure: active deny unchanged, Host ledger absent, rollback N/A. - Post-prompt/pre-arm failure: active deny and reload count unchanged; root-owned ledger/payload or timestamp backup may remain and must be preserved; rollback N/A. - Armed failure: require `HOST_NGINX_OBSERVABILITY_ROLLBACK=PASS` and active deny hash restored. - `ROLLBACK=FAIL`, `MANUAL_RECOVERY_REQUIRED=YES`, unknown active hash, response loss, or missing evidence: STOP for manual recovery. If any residue is present before prompt, or any ledger directory/file, payload, or timestamp-backup staging step fails, preserve the entire root and start a new Task 6 design/execution with a fresh ID. The same route applies when source proof is stale/future. Do not alter this root or proof, recreate only one object, or invoke the Host command again under `20260814T145009Z`. - [ ] **Step 6: Record the Host terminal result** Record safe argv cardinality, dry/execute RC, exact stage/hashes, whether APPLY was entered, Nginx active/test/reload outcome, three stable LAN/local postchecks, rollback invocation/outcome, original Task 6 evidence metadata unchanged, and process/temp residue delta. Do not mark later Task 7 steps yet. --- ### Task 7: Reconcile memberships and run the monolithic acceptance once **Files:** - Append: `.superpowers/sdd/2026-08-15-observability-task7-cutover-correction/task-7-report.md` - Preserve unchanged: `scripts/bootstrap/configure-keycloak-grafana-oidc.sh` - Preserve unchanged: `scripts/validate/observability-smoke.sh` **Interfaces:** - Consumes: Host full PASS, prepared human identities, Slack view, and a real external client. - Produces: OIDC transaction PASS plus one complete machine/OIDC/Slack/external smoke PASS. - [ ] **Step 1: Prove human/external readiness before mutation** Prepare four distinct realm usernames for admin, viewer, no-group, and membership-removal testing; local break-glass access; a Slack channel view; and an outside-LAN/Tailscale client with proxy disabled. If any is unavailable, stop without running membership or smoke. - [ ] **Step 2: Build and execute the OIDC membership argv once** ```bash read -r -p 'Grafana organization admin realm username: ' OBS_ADMIN_USER read -r -p 'Grafana viewer realm username: ' OBS_VIEWER_USER TASK7_OIDC=(/usr/bin/bash /home/donghyeon/workspace/platform/scripts/bootstrap/configure-keycloak-grafana-oidc.sh) TASK7_OIDC+=(--execute) TASK7_OIDC+=(--admin "$OBS_ADMIN_USER") TASK7_OIDC+=(--viewer "$OBS_VIEWER_USER") case $- in *e*) TASK7_OIDC_ERREXIT=1 ;; *) TASK7_OIDC_ERREXIT=0 ;; esac set +e "${TASK7_OIDC[@]}" TASK7_OIDC_RC=$? unset OBS_ADMIN_USER OBS_VIEWER_USER TASK7_OIDC printf 'TASK7_OIDC_RC=%d\n' "$TASK7_OIDC_RC" (( TASK7_OIDC_ERREXIT == 0 )) || set -e ``` Enter exact `APPLY default` and `RECOVERY KEYCLOAK default`. Require `GRAFANA_OIDC_TRANSACTION=PASS` and RC `0`. A failure before `transaction_active=true` has no managed Keycloak/OIDC Secret/membership mutation and rollback marker N/A. An active transaction failure requires `GRAFANA_OIDC_ROLLBACK=PASS`; rollback FAIL/manual recovery or unknown stage stops. Do not claim or search for a persistent Task 6 Keycloak ledger. - [ ] **Step 3: Run the smoke no-arg plan once before execute** Build an absolute two-element argv array and use the errexit save/`set +e`/immediate RC/restore envelope: ```bash TASK7_SMOKE_DRY=(/usr/bin/bash) TASK7_SMOKE_DRY+=(/home/donghyeon/workspace/platform/scripts/validate/observability-smoke.sh) ``` Invoke it exactly once. Require no-arg RC `0`, exact `OBSERVABILITY_SMOKE_DRY_RUN=PASS`, `HUMAN_EXTERNAL_CLIENT=required`, and `MUTATION=NOT_REQUESTED`. This is not acceptance and does not replace execute. - [ ] **Step 4: Execute the smoke exactly once with immediate RC** ```bash TASK7_SMOKE=(/usr/bin/bash) TASK7_SMOKE+=(/home/donghyeon/workspace/platform/scripts/validate/observability-smoke.sh) TASK7_SMOKE+=(--execute) case $- in *e*) TASK7_SMOKE_ERREXIT=1 ;; *) TASK7_SMOKE_ERREXIT=0 ;; esac set +e "${TASK7_SMOKE[@]}" TASK7_SMOKE_RC=$? printf 'TASK7_SMOKE_RC=%d\n' "$TASK7_SMOKE_RC" (( TASK7_SMOKE_ERREXIT == 0 )) || set -e ``` Only the operator supplies the requested identities and exact confirmations. Confirm actual admin/viewer/denied/break-glass/session-revoke/relogin behavior, Slack firing then resolved message, and run the script-rendered `--resolve` command from the true external client before entering its exact dynamic token. - [ ] **Step 5: Require the full acceptance marker set** Success requires all: ```text OBSERVABILITY_MACHINE_ACCEPTANCE=PASS OBSERVABILITY_OIDC_ACCEPTANCE=PASS OBSERVABILITY_SLACK_ACCEPTANCE=PASS OBSERVABILITY_EXTERNAL_BOUNDARY=PASS OBSERVABILITY_SMOKE=PASS TASK7_SMOKE_RC=0 ``` Machine evidence must include LAN and Tailscale Grafana success, denied local/public boundary, external `403`, public metrics `404`, unknown SNI rejection, LAN NodePorts refused, loopback NodePort exact Traefik identity, exact `30/30` targets, datasource/trace link, persistence, network-policy denial, and Certbot/Nginx health. RC `2` or `OBSERVABILITY_EXTERNAL_BOUNDARY=PARTIAL` leaves Task 7 incomplete. Any cleanup ambiguity/manual recovery stops and forbids automatic rerun; review exact owned-object cleanup first. - [ ] **Step 6: Record payload-free OIDC/smoke evidence** Record roles and PASS/FAIL only, never identity values or browser/session data. Record temporary object kinds and cleanup disposition, not payloads. Verify Task 6 root/handoff metadata unchanged and no smoke-owned process/root remains. --- ### Task 8: Run complete rendering from a fresh clone and existing regressions once **Files:** - Create: one fresh `/tmp/platform-observability-metrics.XXXXXX` renderer root - Preserve: `/tmp/platform-observability-metrics.dw5gLZ` - Append: `.superpowers/sdd/2026-08-15-observability-task7-cutover-correction/task-8-report.md` **Interfaces:** - Consumes: Task 7 final acceptance and byte-preserved inventories from the Task 6 handoff. - Produces: complete renderer and existing platform regression RCs without a second smoke. - [ ] **Step 1: Fingerprint the original handoff without changing it** Reinstall `TASK7_OPERATOR_PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin`, export `PATH`, run `hash -r`, and require the independently regenerated command/launcher inventory to remain byte-equal before any external preparation command. From this point, the procedure below nevertheless uses literal absolute paths for every preparation executable. Require exact canonical root, UID:GID `1000:1000`, root mode `0700`, phase modes `0700`, file modes `0600`, nlink `1`, exact root/phase entry sets, and the two known inventory hashes. Record device/inode/type/owner/mode/nlink/size/mtime/ctime/SHA for both inventories and checksums plus the three Task 6 YAML files. Use only literal `/usr/bin/readlink`, `/usr/bin/stat`, `/usr/bin/find`, `/usr/bin/sort`, and `/usr/bin/sha256sum` for these checks. - [ ] **Step 2: Create the fresh inventory-only root** ```bash TASK7_RENDER_ROOT="$(/usr/bin/mktemp -d /tmp/platform-observability-metrics.XXXXXX)" /usr/bin/chmod 0700 "$TASK7_RENDER_ROOT" for phase in target-initial post-substrate; do /usr/bin/install -d -m 0700 -- "$TASK7_RENDER_ROOT/$phase" for file in inventory.json inventory.sha256; do source_file="$TASK7_METRIC_ROOT/$phase/$file" destination_file="$TASK7_RENDER_ROOT/$phase/$file" [[ -f "$source_file" && ! -L "$source_file" ]] /usr/bin/cp --no-dereference --reflink=never -- "$source_file" "$destination_file" /usr/bin/chmod 0600 "$destination_file" /usr/bin/cmp -s -- "$source_file" "$destination_file" done done unset source_file destination_file ``` Require the exact six-entry topology, current-user ownership, modes, nlinks, byte equality, known hashes, and unchanged original fingerprints. Any failure preserves the new root and stops; do not repair or reuse it. - [ ] **Step 3: Run core and complete access renderers once** Build and invoke the core renderer exactly once with an absolute two-element argv and exact Helm environment: ```bash TASK7_CORE=(/usr/bin/bash) TASK7_CORE+=(/home/donghyeon/workspace/platform/scripts/validate/render-observability-core.sh) [[ "${#TASK7_CORE[@]}" -eq 2 ]] case $- in *e*) TASK7_CORE_ERREXIT=1 ;; *) TASK7_CORE_ERREXIT=0 ;; esac set +e PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm "${TASK7_CORE[@]}" TASK7_CORE_RC=$? (( TASK7_CORE_ERREXIT == 0 )) || set -e printf 'TASK7_CORE_RC=%d\n' "$TASK7_CORE_RC" ``` Require `OBSERVABILITY CORE STATIC RENDER PASS` and `TASK7_CORE_RC=0`. Do not invoke the core renderer in Step 4 or through another script. Then build and invoke the complete access renderer exactly once: ```bash TASK7_VOD=--verified TASK7_VOD+=-output-dir TASK7_COMPLETE=(/usr/bin/bash) TASK7_COMPLETE+=(/home/donghyeon/workspace/platform/scripts/validate/render-observability-access.sh) TASK7_COMPLETE+=(--component complete) TASK7_COMPLETE+=("$TASK7_VOD" "$TASK7_RENDER_ROOT") [[ "${#TASK7_COMPLETE[@]}" -eq 6 ]] case $- in *e*) TASK7_COMPLETE_ERREXIT=1 ;; *) TASK7_COMPLETE_ERREXIT=0 ;; esac set +e PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm "${TASK7_COMPLETE[@]}" TASK7_COMPLETE_RC=$? (( TASK7_COMPLETE_ERREXIT == 0 )) || set -e printf 'TASK7_COMPLETE_RC=%d\n' "$TASK7_COMPLETE_RC" ``` Require `OBSERVABILITY ACCESS STATIC RENDER PASS` and `TASK7_COMPLETE_RC=0`; verify seven YAML outputs and original handoff fingerprints unchanged. Do not invoke the complete renderer again. - [ ] **Step 4: Run the remaining regressions once each** The remaining set has exactly five commands; neither renderer from Step 3 is a member. Build each absolute argv and give every invocation its own errexit save/`set +e`/immediate RC/restore envelope: ```bash TASK7_ADMIN_UI=(/usr/bin/bash) TASK7_ADMIN_UI+=(/home/donghyeon/workspace/platform/scripts/validate/admin-ui-smoke.sh) [[ "${#TASK7_ADMIN_UI[@]}" -eq 2 ]] case $- in *e*) TASK7_ADMIN_UI_ERREXIT=1 ;; *) TASK7_ADMIN_UI_ERREXIT=0 ;; esac set +e "${TASK7_ADMIN_UI[@]}" TASK7_ADMIN_UI_RC=$? (( TASK7_ADMIN_UI_ERREXIT == 0 )) || set -e printf 'TASK7_ADMIN_UI_RC=%d\n' "$TASK7_ADMIN_UI_RC" TASK7_AISTOR_S3=(/usr/bin/bash) TASK7_AISTOR_S3+=(/home/donghyeon/workspace/platform/scripts/validate/aistor-s3-smoke.sh) TASK7_AISTOR_S3+=(--execute) [[ "${#TASK7_AISTOR_S3[@]}" -eq 3 ]] case $- in *e*) TASK7_AISTOR_S3_ERREXIT=1 ;; *) TASK7_AISTOR_S3_ERREXIT=0 ;; esac set +e "${TASK7_AISTOR_S3[@]}" TASK7_AISTOR_S3_RC=$? (( TASK7_AISTOR_S3_ERREXIT == 0 )) || set -e printf 'TASK7_AISTOR_S3_RC=%d\n' "$TASK7_AISTOR_S3_RC" TASK7_PHASE1=(/usr/bin/bash) TASK7_PHASE1+=(/home/donghyeon/workspace/platform/scripts/validate/render-phase1.sh) [[ "${#TASK7_PHASE1[@]}" -eq 2 ]] case $- in *e*) TASK7_PHASE1_ERREXIT=1 ;; *) TASK7_PHASE1_ERREXIT=0 ;; esac set +e PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm "${TASK7_PHASE1[@]}" TASK7_PHASE1_RC=$? (( TASK7_PHASE1_ERREXIT == 0 )) || set -e printf 'TASK7_PHASE1_RC=%d\n' "$TASK7_PHASE1_RC" TASK7_PHASE2=(/usr/bin/bash) TASK7_PHASE2+=(/home/donghyeon/workspace/platform/scripts/validate/render-phase2.sh) [[ "${#TASK7_PHASE2[@]}" -eq 2 ]] case $- in *e*) TASK7_PHASE2_ERREXIT=1 ;; *) TASK7_PHASE2_ERREXIT=0 ;; esac set +e PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm "${TASK7_PHASE2[@]}" TASK7_PHASE2_RC=$? (( TASK7_PHASE2_ERREXIT == 0 )) || set -e printf 'TASK7_PHASE2_RC=%d\n' "$TASK7_PHASE2_RC" TASK7_ADMIN_RENDER=(/usr/bin/bash) TASK7_ADMIN_RENDER+=(/home/donghyeon/workspace/platform/scripts/validate/render-admin-services.sh) [[ "${#TASK7_ADMIN_RENDER[@]}" -eq 2 ]] case $- in *e*) TASK7_ADMIN_RENDER_ERREXIT=1 ;; *) TASK7_ADMIN_RENDER_ERREXIT=0 ;; esac set +e PLATFORM_HELM_BIN=/home/donghyeon/.local/bin/helm "${TASK7_ADMIN_RENDER[@]}" TASK7_ADMIN_RENDER_RC=$? (( TASK7_ADMIN_RENDER_ERREXIT == 0 )) || set -e printf 'TASK7_ADMIN_RENDER_RC=%d\n' "$TASK7_ADMIN_RENDER_RC" ``` The fixed operator `PATH` excludes `/home/donghyeon/.local/bin`, and both phase renderers fall back to `command -v helm` when `PLATFORM_HELM_BIN` is unset. Therefore their exact inline assignments above are mandatory and PATH fallback is forbidden. Before execution, run the Task 4 fenced-Bash structural checker against both authority documents. Require its current-document RC `0`, then require nonzero RC for four identity-checked private mutations that respectively remove the phase1 assignment, remove the phase2 assignment, replace the phase1 value, or replace the phase2 value. Exercise each mutation in both document positions with the other document unchanged; require all eight final gates nonzero. Remove only those private copies. Do not run nonexistent `observability-core-smoke.sh`. Do not run `observability-smoke.sh --execute` again. Require every RC `0`, expected terminal PASS, and no new residue. - [ ] **Step 5: Preserve regression evidence through closeout** Preserve the original Task 6 handoff, fresh successful Task 7 renderer root, scanner root, rollback root, terminal status/logs, and name-only residue baseline through Task 9 review. Cleanup belongs to the later Task 8 final-closeout plan, not this task. --- ### Task 9: Freeze terminal results, update status truthfully, and review Task 7 completion **Files:** - Modify after observed results: `bootstrap/manual/phase4-observability-access.md` - Modify after observed results: `/home/donghyeon/workspace/docs/platform/plans/2026-08-14-observability-slack-risk-acceptance-implementation.md` - Append after observed results: `/home/donghyeon/workspace/docs/platform/runbooks/2026-08-13-observability-phase4-resume-worklog.md` - Create: `.superpowers/sdd/2026-08-15-observability-task7-cutover-correction/baseline/task-7-final-source-manifest.txt` - Create: `.superpowers/sdd/2026-08-15-observability-task7-cutover-correction/baseline/task-7-final-source-manifest.sha256` - Create: `.superpowers/sdd/2026-08-15-observability-task7-cutover-correction/task-9-report.md` **Interfaces:** - Consumes: exact terminal RCs/markers and preserved pre-live authority. - Produces: evidence-backed Task 7 completion or an explicit partial/failed state; Task 8 remains separate. - [ ] **Step 1: Write only observed terminal facts** Record Host dry/execute markers and RC, active full hash, rollback invocation state, OIDC transaction/rollback RC, one smoke marker set and RC, external-client result, renderer/platform regression RCs, original handoff preservation, fresh renderer root, temporary-object cleanup, and residue delta. Do not record usernames, external client address beyond the safe public edge already printed by smoke, Secret-derived data, or response bodies. - [ ] **Step 2: Update Task 7 checkboxes conditionally** Mark a Step 1–6 checkbox only when its exact evidence exists. Mark all Task 7 steps complete only if Tasks 6–8 of this plan are RC `0` with every required marker and no unresolved cleanup/recovery. Otherwise leave failed/unrun steps unchecked and state `부분 구현` with the exact blocker. Do not mark any Task 8 checkbox. - [ ] **Step 3: State the remaining Slack DR exception** Even after Task 7 PASS, retain: ```text Phase 4 operational acceptance: complete with active Slack DR exception Slack off-host disaster recovery: deferred / not complete ``` Never convert `RISK_ACCEPTED` into recovery-complete language. - [ ] **Step 4: Regenerate and strictly verify the final source manifest** Regenerate the same exact 21 paths in the same order after terminal documentation changes, bind the final sidecar, and independently review the new literal final manifest SHA. Regenerate and compare the command inventory first with the same exact-once `` sentinel; reject any actual manifest digest in its verifier row. Only after the final manifest is complete may the non-governed task-9 report/review handoff pin its actual digest. Re-pin the still-excluded verifier's canonical absolute path, SHA, type, owner, mode, nlink, and size. Run the descriptor-retaining launcher twice using literal `/usr/bin/python3`, the literal absolute verifier, the reviewed literal SHA, and the literal absolute final manifest/sidecar pair; require RC `0` plus unchanged descriptor/path identities before and after each invocation. Record prelive/final manifest and sidecar file hashes separately; do not overwrite or delete the prelive pair. - [ ] **Step 5: Final independent Task 7 review** Verify design/spec coverage, source quality, RED/GREEN/mutation provenance, fixed command boundary, v2 exact tuple, proof age/ID, prompt-before empty Host residue, Host state/rollback classification, OIDC truth, exactly one smoke per token/argv structural document assertion and exactly one live smoke, public/Tailscale/external evidence, `30/30` targets, NodePort boundary, exactly one core plus one complete renderer, exact phase1/phase2 Helm assignments with structural mutation rejection, clone/original preservation, every regression RC, self-reference-free 21-row manifest/sidecar binding and external digest pin, separate verifier pin/rebind defense, mutator/process/temp residue, and accurate docs/check boxes. Required terminal verdicts: ```text Critical=0 Important=0 Spec=YES Quality=APPROVED Task7=COMPLETE ReadyToStartTask8=YES ``` Any Critical/Important finding or missing evidence returns to the responsible task; do not claim completion because the day ended or because only server-side checks passed. --- ## Execution Handoff Plan implementation must use either `superpowers:subagent-driven-development` with a fresh implementer and spec/quality review per task, or `superpowers:executing-plans` with explicit checkpoints. The recommended route is subagent-driven development because Tasks 1–5 are independently reviewable before any live mutation, while Tasks 6–9 require controller/operator coordination.