Use this runbook during the first 14 launch days to keep the daily pages fresh, fast, and easy to trace.
Publish the newest verified daily puzzle data within five minutes of Wend's midnight reset in America/Los_Angeles, confirm the exact record is visible in production, and keep a traceable provenance record.
The daily operating target is:
- Midnight Pacific Time: Wend is expected to release a new puzzle. This is 07:00 UTC during PDT and 08:00 UTC during PST.
- Reset to +5 minutes: verify, publish, and confirm the exact date and puzzle number in production.
- If the official page is slow, blocked, or ambiguous, do not publish placeholder data as verified.
The automated entry point is:
npm run publish:wendThe script supports these environment variables:
WEND_DAILY_SOURCE_URL: normalized official-page capture that returns the daily Wend JSON, an HTML page containing awend-puzzle-dataJSON script tag, or an HTML page withdata-row,data-col,data-word-index, anddata-letter-indexcell attributes.WEND_DAILY_FALLBACK_SOURCE_URL: optional public fallback source used whenWEND_DAILY_SOURCE_URLis not configured. The script defaults this to the public HTML fallback source currently used by the automation.WEND_DAILY_SECONDARY_SOURCE_URL: independent public word-list source required to agree with automatic public ingestion. Defaults tohttps://wendgames.org/src/answers-data.js.WEND_DAILY_INPUT_JSON: trusted normalized JSON supplied only through an authenticated manual GitHub workflow run.WEND_DAILY_INPUT_FILE: local JSON file fallback for manual emergency publishing. Trusted JSON still requiresWEND_VERIFIED_BYand geometry validation.WEND_VERIFIED_BY: authenticated operator identity. GitHub Actions sets this togithub.actorfor trusted manual input.WEND_DEPLOY_COMMAND: optional deployment command to run after generation and fast tests.WEND_PERSIST_TO_GIT: set totruein CI so generated JSON andsrc/lib/generated/wend-puzzles.tsare committed and pushed before deploy.OPS_ALERT_WEBHOOK_URL: preferred Discord-compatible webhook for publish failures and production monitoring failures.OPS_ALERT_TELEGRAM_BOT_TOKENandOPS_ALERT_TELEGRAM_CHAT_ID: optional Telegram alert channel.WEND_ALERT_WEBHOOK_URL: legacy Discord-compatible webhook for publish failures. Keep it only for backward compatibility; new setup should useOPS_ALERT_WEBHOOK_URL.WEND_EXPECTED_DATE: optional override for manual backfills. Normal publishing uses the current date inAmerica/Los_Angeles.MAX_PUBLISH_WINDOW_MS: defaults to 300000, or five minutes.ALLOW_UNVERIFIED_WEND_PUBLISH: only set totruefor private dry runs. Public publishing should keep this unset.
GitHub Actions keeps fallback retries at 7,22,37,52 7 * * * and 7,22,37,52 8 * * * UTC so both PDT and PST reset windows are covered at off-peak minutes. Scheduled Actions can be delayed or dropped, so they are not the primary five-minute trigger.
Configure an external scheduler to call the secured application route throughout both reset windows, for example at minutes 00, 02, 05, and 08 of 07:00 and 08:00 UTC. Calls are idempotent and the application calculates the expected date in Los Angeles; no caller-supplied source URL is accepted.
curl -X POST https://wendanswertoday.org/api/ops/wend-dispatch \
-H "Authorization: Bearer $CRON_SECRET"The Vercel application requires CRON_SECRET, GITHUB_DISPATCH_TOKEN, and WEND_GITHUB_REPOSITORY=owner/repository. The GitHub token must be restricted to the target repository and only the permission needed to create repository dispatches. The route returns 202 after GitHub accepts the trigger, 401 for invalid credentials, 503 for missing server configuration, and a sanitized 502 for upstream failure.
The current public fallback strategy is:
wendanswertoday.meembeds the current solved board directly in server-rendered HTML using cell attributes such asdata-row,data-col,data-word-index, anddata-letter-index. The importer reconstructs the grid, paths, and words from those attributes but does not trust that page alone.wendgames.org/src/answers-data.jsis an independent, manually maintained word-list source. Automatic publishing fails closed unless its date, puzzle number, and complete normalized answer-word set agree with the primary source.- LinkedIn's official Wend page remains the preferred source if a stable, compliant, verified capture is available. Public competitor pages are fallback signals and must pass the same geometry validator before publishing.
Trusted manual JSON has higher priority than the emergency file, which has higher priority than automatic public sources. A manual workflow run can provide expected_date and puzzle_json; GitHub records the authenticated actor as the verifier. Never copy trusted JSON into repository_dispatch client payloads or public endpoints.
For an official human-verified correction, create a temporary request body in your operator environment (do not commit it):
{
"ref": "main",
"inputs": {
"expected_date": "2026-07-10",
"puzzle_json": "<single-line normalized Wend JSON>"
}
}Then dispatch the authenticated workflow through GitHub:
curl -L -X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/OWNER/REPOSITORY/actions/workflows/publish-wend-daily.yml/dispatches \
--data-binary @workflow-dispatch.jsonDelete workflow-dispatch.json after the request. The token needs access to run this workflow; it is separate from the application dispatch token and must never be written into repository files or logs.
After a Git push, npm run wait:wend-production polls /api/wend-status until production reports the exact local date and puzzle number. Only then may production smoke and IndexNow run. Production monitoring runs separately as a 15-minute GitHub fallback; an external uptime service should poll the status endpoint every minute around reset. See docs/MONITORING_RUNBOOK.md.
Before treating official LinkedIn scraping as the primary path, run a spike:
- Confirm whether the official Wend page exposes puzzle data without a logged-in session.
- If a logged-in session is required, treat scraping as a risky auxiliary signal because session cookies, two-factor prompts, and account checks can fail without warning.
- Until that is proven stable, use the authenticated
WEND_DAILY_INPUT_JSONpath for human-verified official data or the automatic two-public-source quorum as a fallback. - Never publish old answers as today. If the latest local puzzle is stale or unverified, public pages show a visible verification-pending notice and label the fallback as the latest verified puzzle.
The publish script refuses data unless:
- Required fields are present.
gameiswend.dateequals the expected publish date.isVerifiedistrue.- Trusted JSON has a non-empty verifier identity, or automatic public data has date/number/word agreement from two sources.
- New records include a stable SHA-256 source hash and real capture/verification timestamps.
- Answer paths stay inside the grid.
- Consecutive path cells are adjacent.
- Each path spells the declared answer word.
- No path enters a blocked
nullcell. - Answer paths do not overlap.
- The final answer set uses every open letter cell exactly once.
Match LinkedIn's live board exactly:
gridcan be any rectangular board size, not only 5x5.- Use a string for visible letter cells, for example
"Y". - Use
nullfor gray blocked wall cells. - Do not fill
answerswith placeholder words. If the board is captured but the official solution is not verified, keepanswers: []andisVerified: false. - When
isVerifiedisfalse, do not publish the puzzle as today's answer. Public pages should keep showing the latest verified module and production monitoring should alert on the freshness gap.
- Add a new JSON file under
data/puzzles/wend/using this naming format:
YYYY-MM-DD.json
Example:
data/puzzles/wend/2026-06-27.json
- Fill the JSON fields carefully:
puzzleNumberdatedateLabelupdatedAtdifficultygridhintsanswersexplanationquickHintfastTipcommonMistakedifficultyNoterelatedGamesisVerified- optional
publicationprovenance for older records; all newly published records receive it automatically
- Regenerate the Wend puzzle index:
npm run generate:wendThis updates src/lib/generated/wend-puzzles.ts from every YYYY-MM-DD.json file under data/puzzles/wend/.
Do not hand-edit dated Wend imports in src/lib/puzzles.ts. The generated list is split into an internal raw dataset and a public verified dataset:
export const allWendPuzzles = generatedWendPuzzles as unknown as WendPuzzle[];
export const verifiedWendPuzzles = allWendPuzzles.filter((puzzle) => puzzle.isVerified);
export const wendPuzzles = verifiedWendPuzzles;
export const todayWend = verifiedWendPuzzles[0] ?? allWendPuzzles[0];Public homepage, Solver, Archive, sitemap, and archive detail routes must consume wendPuzzles / todayWend, not allWendPuzzles, so unverified captures never appear as public answers. /linkedin-wend-answer-today is a redirect-only compatibility route.
- If the new puzzle creates a new archive detail page slug, add that route to
scripts/smoke-local.mjsso local smoke testing covers it.
Canonical archive URLs use this format:
/wend-answer-puzzle-{puzzleNumber}-{month-day-year}
Example:
/wend-answer-puzzle-18-june-26-2026
The older /linkedin-wend-answer-{number}-{date} URLs are legacy and should redirect to the canonical archive URL.
- Update
docs/CHANGELOG.mdwith:
- Date of change.
- New data file added.
- Import or route changes.
- Verification commands that passed.
- Any known risk, especially if
isVerifiedisfalse.
Historical backfills use a separate explicit command so an old date cannot enter the daily latest-date workflow by accident:
npm run backfill:wend-history -- --numbers=1,2,3 --dry-run
npm run backfill:wend-history -- --numbers=1,2,3
npm run generate:wend
npm run test:wend-datasetRules:
- The script accepts only its reviewed historical puzzle-number allowlist.
- Always run dry-run first and retain the primary and corroborating URLs in
publication.sourceUrls. - A historical record must pass date/number/word source agreement and full geometry validation before it is written.
- Do not use
publish:wendfor historical dates; its latest-date guard is intentionally designed for daily publishing.
Run:
npm run latest:wendExpected output shape:
{
"latestDate": "2026-06-27",
"latestFile": "data\\puzzles\\wend\\2026-06-27.json",
"count": 6
}If the latest date is wrong, check:
- The file name matches
YYYY-MM-DD.json. - The file is placed under
data/puzzles/wend/. - The date was not accidentally saved in another game folder.
Run these before considering the daily update done:
npm run generate:wend
npm run test:latest-date
npm run test:wend-freshness
npm run test:wend-schedule
npm run test:wend-validator
npm run test:wend-source-verification
npm run test:wend-ops-routes
npm run test:wend-production-gate
npm run test:wend-mvp
npm run test:wend-publish
npm run test:seo-metadata
npm run test:seo-routes
npm run typecheck
npm run buildThen start the local server:
npm run start -- -H 127.0.0.1 -p 3000In another terminal, run:
npm run smoke:localOpen these pages locally:
http://127.0.0.1:3000/http://127.0.0.1:3000/linkedin-wend-answer-todayreturns301to/.- The newest Wend archive detail page, for example
http://127.0.0.1:3000/wend-answer-puzzle-18-june-26-2026 - The matching legacy archive URL redirects, for example
http://127.0.0.1:3000/linkedin-wend-answer-18-june-26-2026
Confirm the visible page shows the newest date and puzzle number.
Extra checks after UI or routing changes:
/still exportsrevalidate = 60, notforce-dynamic.- The homepage uses the Ready metadata template only when the latest puzzle is verified for the current Wend release window; Pending metadata includes
(Verifying)and the expected puzzle identity. - The Solver page uses the same colored word cards, letter bubbles, and reveal controls as the homepage answer module.
- The FAQ disclosure control rotates consistently on the homepage.
- The verification-pending card states the expected puzzle and identifies the latest verified fallback.
- A 375-390px wide mobile viewport keeps Wend board tubes, start markers, check badges, and letter bubbles legible without horizontal overflow.
- A legacy archive URL returns a real
308redirect in the running app, not just in source-level tests.
If the homepage shows yesterday's puzzle:
- Run
npm run generate:wend. - Run
npm run latest:wend. - Check
src/lib/generated/wend-puzzles.tslists the newest JSON first. - Confirm the newest current-day JSON has
isVerified: true; otherwise public pages intentionally keep showing the latest verified puzzle. - Confirm
todayWendcomes fromverifiedWendPuzzles[0], withallWendPuzzles[0]used only as an emergency empty-data fallback. - Restart
npm run devif the dev server was already running. - Inspect
https://wendanswertoday.org/api/wend-status; HTTP503means the fallback is intentional and the current puzzle is still pending.
If a new archive page returns 404:
- Confirm
findWendByArchiveSlug()can find the puzzle. - Confirm the generated slug matches
/wend-answer-puzzle-{number}-{date}. - Confirm the puzzle has
isVerified: trueand is included in publicwendPuzzles; unverified raw files stay out of archive detail pages.
If smoke testing fails:
- Read the failed path in the smoke output.
- Open that exact localhost URL.
- Check whether the page is missing security headers, brand text, or returning a non-2xx response.
If build fails after adding JSON:
- Check JSON syntax first.
- Check
gridshape andanswers.pathcoordinate arrays. - Run
npm run typecheckto locate TypeScript import or type issues.