Skip to content

feat(infra): safe-dashboard-deploy wrapper + 2 lints + headless canary + 3 bug-class entries#344

Open
mitwilli-create wants to merge 1 commit into
mainfrom
feat/safe-dashboard-deploy-2026-05-30-claude-159402d0
Open

feat(infra): safe-dashboard-deploy wrapper + 2 lints + headless canary + 3 bug-class entries#344
mitwilli-create wants to merge 1 commit into
mainfrom
feat/safe-dashboard-deploy-2026-05-30-claude-159402d0

Conversation

@mitwilli-create
Copy link
Copy Markdown
Owner

Summary

Codifies the dashboard deploy ritual into a single 6-gate wrapper script. Closes the structural gap that produced the 2026-05-29/30 regression cascade in PRs #338/#343, where four independent failure modes each cost ~30-45 minutes of debug time.

Per spec at data/spec-safe-dashboard-deploy-2026-05-30.md. ~$0 LLM spend (deterministic infrastructure — no AI calls in scripts).

What landed

File Purpose
scripts/safe-dashboard-deploy.sh Single canonical entry point. 6 gates, each with distinct exit code (2-9). Per-gate failure messages cite the relevant AGENTS.md bug-class entry.
scripts/lint-browser-context-refs.mjs Acorn AST walker over inline <script> blocks. Cross-block shared scope (top-level defs + window.X = ...) + per-block typeof guards. Catches the PR #343 sanitizeObjectStrings bug.
scripts/lint-backtick-in-comment.mjs Parse-based detection: Acorn run on scripts/build-dashboard.mjs. Parse failure → diagnose nearby unescaped backtick in JS comments.
scripts/dashboard-headless-canary.mjs Playwright headless. Clicks 3 apply-now rows. Asserts zero forbidden patterns: is not defined, SyntaxError, [object Object], Uncaught, Failed to fetch.
scripts/hooks/commit-msg EXTENDED with safe-deploy guard. Refuses dashboard/index.html commits without safe-dashboard-deploy or SKIP_SAFE_DEPLOY=1 reference.
tests/safe-dashboard-deploy.test.mjs 17 unit tests. 17/17 pass.
AGENTS.md 3 new bug-class entries (client-side-reference-to-server-side-import, stale-worktree-cp-backward-merge, ad-hoc-cp-of-build-artifact).
CLAUDE.md Session Notes 2026-05-30. Full landed-files table + verified gate-by-gate cascade prevention.
package.json + package-lock.json acorn-walk@8.3.5 added as dev dep.

The 4-fold cascade this prevents

Each gate corresponds to a real 2026-05-29/30 failure mode:

# 2026-05-30 incident Wrapper gate Exit Verification
1 cp from worktree branched off main BEFORE PRs #339/#340 merged → backward-merge Gate 1 (git merge-base --is-ancestor) 2 Synthetic trigger: HEAD~5 worktree → exit 2 with "source HEAD is BEHIND origin/main"
2 sanitizeObjectStrings() ref from inline <script> to Node-side import; runtime crash on every drawer Gate 3 (lint-browser-context-refs.mjs) 5 End-to-end on current main: wrapper passes gates 1+2+build+2b, then exit 5 at line 5777 — the EXACT PR #343 bug
3 Backtick in JS comment inside outer template literal → build SyntaxError Gate 4 (lint-backtick-in-comment.mjs) 6 Unit test: synthetic backtick-in-comment → Acorn parse failure → exit 1
4 data/ symlink evaporation mid-build → 1.4MB HTML instead of 9.4MB Gate 2 + Gate 2b (pre+post-build file existence) 3 / 4 First-run trigger: missing data/applications.md symlink → exit 3 with actionable ln -sfn recovery

Lint iteration history

lint-browser-context-refs.mjs: v1 per-block-only scope (13 false positives), v2 + cross-block top-level scope (12 FPs), v3 + per-block typeof-guard + cross-block window.X member refs → 1 violation (the actual PR #343 bug).

lint-backtick-in-comment.mjs: v1 heuristic text scan (17 false positives), v2 parse-based via Acorn → clean exit when source parses + helpful error when it doesn't.

Test plan

Predecessor + downstream

Mitchell action needed post-merge

  1. bash scripts/install-hooks.sh to refresh .git/hooks/commit-msg with the new guard
  2. Wait for PR fix(drawer): three regressions from PR #338 — client-side sanitizer crash + HTP card bypassed sanitizer + STORIES header still first-person #343 to merge, then trial-run bash scripts/safe-dashboard-deploy.sh from main
  3. Treat any new deploy as a wrapper-only deploy going forward. Pre-commit hook will refuse ad-hoc dashboard/index.html commits.

Rollback

  • Any gate failure: backup exists at dashboard/index.html.pre-deploy.<unix-ts>; cp <backup> dashboard/index.html + restart server.
  • Whole-PR revert: purely additive; git revert <merge-sha> safe.
  • Per-gate bypass: env vars listed above for emergency only.

🤖 Generated with Claude Code

… 2026-05-30 4-fold cascade

Codifies the dashboard deploy ritual into a single guarded wrapper. Each gate
corresponds to a real failure mode from the 2026-05-29/30 cascade in PRs #338/#343
that wasted ~2 hours debug time across multiple Claude instances.

What landed (10 files):
- scripts/safe-dashboard-deploy.sh — 6-gate orchestrator, exit codes 2-9
- scripts/lint-browser-context-refs.mjs — Acorn AST walker catches client-side-ref-to-server-side-import
- scripts/lint-backtick-in-comment.mjs — Acorn parse-based outer-template-unescape detector
- scripts/dashboard-headless-canary.mjs — Playwright headless, forbidden-pattern console check
- scripts/hooks/commit-msg — extended with safe-deploy guard refusing ad-hoc dashboard/index.html commits
- tests/safe-dashboard-deploy.test.mjs — 17/17 pass
- AGENTS.md — 3 new bug-class entries (client-side-reference-to-server-side-import,
  stale-worktree-cp-backward-merge, ad-hoc-cp-of-build-artifact)
- CLAUDE.md — Session Notes 2026-05-30
- package.json + package-lock.json — acorn-walk@8.3.5 dev dep

Verified gate-by-gate:
- Gate 1 (exit 2): synthetic stale-worktree at HEAD~5 → "source HEAD is BEHIND origin/main"
- Gate 2 (exit 3): missing data symlink → actionable ln -sfn recovery
- Gate 3 (exit 5): end-to-end run on current main → catches the PR #343 sanitizeObjectStrings bug
- Gate 4 (exit 6): unit-tested with synthetic backtick-in-comment
- Gates 5+6: unit-tested at contract level

PR #343 (sanitizer hotfix) is the precondition for the wrapper's first clean
end-to-end run. Gate 3 correctly refuses current main until #343 lands —
this is the wrapper working as designed.

Spec: data/spec-safe-dashboard-deploy-2026-05-30.md

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

⚠️ Critical file overlap detected

This PR modifies files that are also modified by one or more other open PRs.
Merging in the wrong order can produce structural merge conflicts.


⚠️  CRITICAL FILE OVERLAP DETECTED
============================================================

PR #344 shares critical files with 1 other open PR(s).
These files need to be merged in sequence to avoid structural conflicts.

  PR #341 "fix(launchd): move cron-run.sh wrapper to ~/.local/ to escape Tahoe TCC exec block (network-database-build + 4 siblings)"
    Branch: fix/tahoe-tcc-cron-run-wrapper-2026-05-29
    Overlap: AGENTS.md

Options:
  1. Merge the other PR first, then rebase this branch onto main
  2. Coordinate with the other PR author to split responsibility
  3. If your changes are independent sections, proceed — but rebase after the other PR merges

See AGENTS.md § Bug class: pr-conflict-mirage-from-parallel-shipping

This is a warning, not a block. CI still passes. Merge order matters — coordinate before merging.

See AGENTS.md § Bug class: pr-conflict-mirage-from-parallel-shipping for the triage + recovery playbook.


// Extract every inline <script> block. Returns [{ idx, attrs, body, blockOffset }]
function extractScripts(html) {
const SCRIPT_RE = /<script\b([^>]*)>([\s\S]*?)<\/script>/gi;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants