Skip to content

feat(trial): doctor after deploy, and the newsletter Worker on every path - #166

Merged
musingfox merged 3 commits into
mainfrom
muster/pr-trial
Nov 10, 2026
Merged

musingfox merged 3 commits into
mainfrom
muster/pr-trial

Conversation

@musingfox

Copy link
Copy Markdown
Owner

Summary

  • deploy_trial.py runs cyris doctor after the build matches, checking doctor's D1 id the way cyris reads it (first line wins), and a dry run makes the same checks of the config and the service token file as a real run.
  • Every trial path knows the per-trial newsletter Worker: the saved TRIAL_NEWSLETTER_KV_ID alone says whether a trial has one. Provision validates its config, deploy re-renders and deploys it, and reset clears its NEWSLETTERS KV. A stray config with no saved id is reported in one line and never read or written.
  • Reset's refusals print commands that run as pasted, and name the wizard stage that saves each missing state key.

Ticket: trial-newsletter-release-path (vault pm/cyris/tasks).

Verification

  • scripts/check.sh passed outside the sandbox at each commit (7f8c8e8: 5320 passed; f8cefd8: 5344 passed; the tip's tree equals the reviewed b490a7e8, which passed).
  • Three rounds of independent review; the last passed every item.
  • Not run against real Cloudflare, wrangler or the wizard.

After merge

  • The owner adds trial1's newsletter KV id to the wizard state so deploy and reset include its Worker.

🤖 Generated with Claude Code

musingfox and others added 3 commits November 10, 2026 13:36
The dry run skipped `.env.trial-<slug>.access`, which the real run refuses
on when it is missing or holds no service token, so a dry run passed for a
trial the real run would stop. It now reads the file first, before any
deploy, so a dry run refuses what the real run refuses.

Two tests pin it: a service token file that is empty or lacks the secret
gives a dry run the same error the real run gives, and a missing file is
refused with no deploy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On 2026-10-21 trial1 had not published since 10-15: its D1 lacked two
required settings keys, the run container exited in
require_complete_settings and wrote no digest_runs row, and the deploy
only compared /api/build.

After the build comparison passes, the script runs `cyris doctor` once
from a temporary directory holding an empty cyris.toml and a .env built
from .env.trial-<slug> with CYRIS_STORE_BACKEND=d1 (cyris reads only the
.env beside its config). The process starts from `env -i`. The run
role's other defaults, HTML output and publishing on, are added unless
the file sets them, so doctor judges what the scheduled run starts from.
A failed check exits non-zero and prints the failed lines, which name the
missing keys. A missing .env.trial-<slug> stops the script before
anything deploys, and so does one whose database id is not the trial's:

- Doctor applies the schema to the database the file names, and nothing
  compared that id with TRIAL_D1_ID. A mismatch, or no id, stops before any
  deploy and names both ids.
- The id is read as cyris reads it. cyris loads the file with
  os.environ.setdefault: the first line wins, stripped.
  provision_trial.env_file takes the last line, unstripped, so a file with
  another deployment's id early and the trial's later passed the check
  while doctor applied the schema to the other database, and a trailing
  space after the right id was refused. A file holding the key on more
  than one line is refused, naming the lines; a single line is compared
  stripped.

A dry run reads the file and makes the same check instead of skipping
doctor's inputs; it does not run doctor.

Tests pin what a review's mutations left green: the run role's defaults
never override the trial's own file value, the temporary .env is chmod
0600 whatever the umask, the temporary directory is removed after a
passing and a failing run, and the failure shows the failed checks and
their fixes and no passing line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR #102 let the wizard create and deploy a per-trial newsletter Worker,
but only the wizard knew about it. The state's TRIAL_NEWSLETTER_KV_ID
alone now says whether a trial has one, in provision, deploy and reset
alike; a trial wired before it has no id and each script leaves the
Worker out.

- provision_trial: rendered_paths() lists the newsletter config once it
  exists, load_rendered checks its name, binding and KV id for production
  names and drift, and production_names() includes the template's KV id.
  With no saved id, rendered_paths and load_rendered leave a config on
  disk out unread, so a stray one cannot stop reset or deploy. The old
  rule read and validated it either way.
- deploy_trial: a trial whose state holds the id renders the config again
  and deploys it like rss and promote (changed directory, --newsletter,
  skipped by --app-only), with its own baseline NEWSLETTER_WORKER_COMMIT.
  A trial without the id is rendered and deployed as before. _snapshot and
  _restore cover the config, removing one this run rendered when the
  deploy fails. A dry run of a trial whose config is missing used to
  render it, restore the tree (deleting it again) and then raise an
  uncaught KeyError looking it up for the Worker's dry run; the Workers'
  dry runs now run before the restore, on the configs this run rendered.
- reset_trial: clears the NEWSLETTERS KV's issues, keyed `nl:` as the
  Worker writes them, after checking the config's id against the state.
  Id saved, config missing: refused before any call, so the reset no
  longer prints "Reset" over a KV it did not clear. Config present, no id
  (trial1 today): the rest of the reset runs, the KV is left alone, and
  one line says so and to save the id in the wizard state. Reading the
  config on disk instead of the saved id made reset and deploy disagree
  both ways.
- reset_trial and deploy_trial print the same one line for a stray config
  (provision_trial.newsletter_note) and do nothing to the file; deploy no
  longer snapshots or restores it.
- The reset's refusal for an id with no config names the render command
  trial-wizard.sh stage 8 runs, built from the state's values, with a
  full deploy as the alternative. Each command sits on a line of its own
  with no trailing punctuation, so the d1 id does not read "<uuid>." and
  the fallback does not print `--from 8.`. When DOMAIN, ACCOUNT_ID or
  IMAGE_DIGEST is missing the refusal names each missing state key and
  the wizard stage that saves it (2, 4 or 5) rather than `--from 8`:
  stage 8 reads those under set -u and a --from 8 run does not run
  stage 2, which writes them.
- trial-wizard stage 14: a probe that gets no HTTP answer, or a 404, now
  says whether the deploy was refused, the URL could not be read from the
  deploy output, or the printed URL is not live, instead of "answered
  000, not 401" or "answered 404, not 401", which blamed the Worker's
  answer for a Worker that was never deployed. A 404 from a URL this
  run's deploy printed keeps the plain wrong-answer message. An
  unreadable log no longer overwrites the saved URL.

docs/architecture.md §4 states the contract for all three scripts, and
that the newsletter Worker shares the rss and app bearer on purpose:
WorkerConfig reads one CYRIS_WORKER_TOKEN for both server-to-server
Workers, and ADR-0008 records that holding them apart bought rotation
nobody used. operations.md and trial-deployment.md name the Worker too.

Tests pin the contract: the stray-config report is read-free (Path reads,
writes and mtime show deploy and its dry run never open, write or touch
the config), and the note is absent when the id is saved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 10, 2026

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@claude

claude Bot commented Nov 10, 2026

Copy link
Copy Markdown

Reviewed the script changes (deploy_trial.py, provision_trial.py, reset_trial.py, trial-wizard.sh) and the docs. I found nothing worth an inline comment, and I did not run the tests.

Checked and found sound:

  • The newsletter Worker is gated on TRIAL_NEWSLETTER_KV_ID the same way in render, snapshot/restore, load_rendered, deploy and reset. A config with no id is only reported. An id with no config is refused before any wrangler call.
  • _snapshot/_restore handle a newsletter config that did not exist before the render, and the dry run deletes it again.
  • doctor starts from env -i, so the Cloudflare credentials in the wrangler environment don't reach it. The check that CYRIS_STORE_DATABASE_ID matches the wizard state guards the schema write.
  • The reset only bulk-deletes when the key list is non-empty, and it recounts afterward.

Two small things, neither blocking:

  • doctor runs after the deploy and verify. A doctor failure exits non-zero after the Worker baselines have already moved, so a rerun won't redeploy those Workers. That seems intended, since the error text says the build matches.
  • env -i keeps only HOME and PATH. If a UV_* or XDG_CACHE_HOME setting matters on someone's machine, uv run will behave differently there.

🤖 Generated with Claude Code

@musingfox

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-10T14:14:03.487918Z 12583ad Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: 12583ad881

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@musingfox
musingfox merged commit 6a2350a into main Nov 10, 2026
3 checks passed
@musingfox
musingfox deleted the muster/pr-trial branch November 10, 2026 17:06
musingfox added a commit that referenced this pull request Nov 10, 2026
#167 changed nothing in §1–§6, so it has no row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant