# Website deploy: why the commit author decides whether the site updates

Loaded by `/multi-agent:sync` Step 4. Read it when a website sync pushed cleanly
and the live site did not change.

## The failure

The deploy platform builds a commit only when its author is a contributor on the
project. A commit carrying any other identity is accepted by `git push` and then
never built:

```
vercel ls   ->  Status UNKNOWN   Duration ?   Builds: . [0ms]
API         ->  readyState: "BLOCKED"
                readyStateReason: "The Deployment was blocked because the commit
                                   author does not have contributing access ..."
                seatBlock: { blockCode: "TEAM_ACCESS_REQUIRED", gitProvider: "github" }
```

Nothing in the push output says so, the deployment exists, and the site keeps
serving the previous version. Pipeline v16.4.0 and v16.5.0 were both pushed this
way; neither was ever built, and both syncs reported the website as done.

## The rule

The identity is the one `prefs.global.identities[]` routes to the website owner
through `platformIdentityRouting`, which is not always the account the current run
is working under. A run driven from a work account, or an exported
`GIT_AUTHOR_EMAIL`, is exactly how the wrong author gets recorded.

`$HOME/.claude/scripts/website-deploy-commit.sh` applies the rule:

1. Read the clone's own `user.name` / `user.email` and write only on a mismatch.
   The website clone is usually already configured correctly; overwriting it with
   the caller's identity is the defect, not the fix.
2. Commit only when something is staged.
3. Read the author back off the commit with `git log -1 --format=%ae`. Setting
   `git config` is not proof: an exported `GIT_AUTHOR_EMAIL` outranks it. On a
   mismatch the script halts before pushing, so the bad commit stays local.
4. Push, then wait for a Ready production build. A push is not a deploy.

Exit codes: `0` committed and pushed (or nothing to do), `1` wrong author and
nothing pushed, `2` usage or environment, `3` pushed but no Ready build.

Env: `WEBSITE_SYNC_NO_PUSH=1` (local only), `WEBSITE_SYNC_NO_VERIFY=1` (skip the
deployment check), `WEBSITE_SYNC_WAIT=<sec>` (default 45).

## Recovery when a deployment is already blocked

History does not need rewriting, and `main` is never force-pushed. The platform
checks the HEAD commit of each new deployment, so a fresh commit under the right
identity is enough:

```bash
git commit --allow-empty -m "chore(site): redeploy under the maintainer identity"
git push origin main
```

Or deploy from the CLI, which attaches no rejected author:

```bash
cd "$WEBSITE_DIR" && vercel --prod --yes
```

Blocked deployments can be left in place; they hold no alias.

## Diagnosing

The CLI prints `UNKNOWN` and hides the reason; the API gives it:

```
GET https://api.vercel.com/v13/deployments/<dpl_id>?teamId=<team_id>
    -> readyState, readyStateReason, seatBlock
```

Read the CLI token out of its own auth file into a variable. Never into argv, a
log or a reply.

## Verifying the live site

- The repo directory name is not the domain. Check the domain the project
  actually serves, not `$HOME/{website-host}`.
- Version strings and counts are server-rendered and appear in the initial HTML.
  Feature prose and lazily-loaded components do not: grep the JS chunks for those.
- A `curl` on the HTML can return 403 bot mitigation (`x-vercel-mitigated:
  challenge`) rather than the page, which reads like content that never shipped.
  Static assets are not challenged.
