feat: site setup takes a managed repository's site to a live address - #722
Merged
Merged
Conversation
The seam `abcd site setup` routes a rendered site through (itd-2609061543533170, spc-2609212141407459 scope 2): an Adapter carries the repository half as data (the deploy secrets, the deploy step, the host configuration file) and Connect returns the host half (inspect, create, route, address). One provider ships: an assets-only Cloudflare Worker, the host abcd's own site uses. Its calls are written against the published v4 API reference and exercised only against cloudflaretest, an in-process fake that can fail every route it answers. The token lives inside the connected client, is scrubbed from every host message before it can reach an error, and redirects are refused rather than followed. Assisted-by: Claude:claude-opus-5-5
…scoped store The one reader adapters resolve a credential through. The interim source is ~/.abcd/credentials.json, a JSON object of names to values, refused loudly unless it is a regular file owned by the caller at mode 0600 or tighter. The credential store proper (itd-2609221017023290) replaces the source behind the same interface; no reader changes. Assisted-by: Claude:claude-opus-5-5
…tion The manifest gains a `pages` block (itd-2609061543533170 criterion 4): the landing page, the explorer, the record pages, the graph, the timeline, the glossary and the status page render for every repository, and a switch can only take one away. Switching a page off removes the page, its navigation entry and every link the renderer drew to it; the explorer's switch takes every explorer page with it. The landing page and the record pages carry the site, so switching either off beneath the explorer is refused, and a key outside the set is refused as any unknown manifest key is. The manifest also gains the `hosting` block `abcd site setup` reads (provider, host name, optional domain), validated against the shipped provider list; the build never reads it. Assisted-by: Claude:claude-opus-5-5
`abcd site setup` lays machinery into a managed repository the way the release scaffold does (spc-2609212141407459: the verb reuses the launch scaffold's workflow writer), so the two-pass classify-then-write logic moves into WriteFiles, which Scaffold now calls unchanged. It gains one disposition: a seed file the repository owns once it exists is kept, never refused and never overwritten. Assisted-by: Claude:claude-opus-5-5
…address The verb's core (itd-2609061543533170, spc-2609212141407459 scope 1), in three stages reported one by one: - the repository: the composition derived from the identity block and the documentation, the site's static inputs seeded from abcd's own, the render-on-release-then-deploy workflow, and the provider's host configuration, written through the scaffold's writer so a second run writes nothing and drifted machinery refuses the whole run; - the forge: the site-render and site environments, restricted to the default branch and release tags, created through the GitHub API as the person who invoked the verb, only after a confirmation naming the changes (adr-44); - the host: with a hosting credential on this machine, the adapter creates and routes the host and reports the address, again only once confirmed; without one the stage stops and the exact remaining step is printed. No secret value passes through abcd: the deploy environment's secrets are checked by name and their `gh secret set` commands printed. The forge and the provider are exercised against in-process fakes that fail every call. Assisted-by: Claude:claude-opus-5-5
… the render A workflow_run fires for every run of the release workflow, and its branch filter matches a head branch a fork can name. The render job now requires a successful run that is not a pull request's and whose head repository is this one. Nothing from the triggering run was used, so the gate removes noise redeploys rather than an injection path. Assisted-by: Claude:claude-opus-5-5
`abcd site setup` is reachable from the CLI (flags --name, --domain, --confirm, --yes) and documented on commands/site.md, with its sentence in the surface manifest. An unanswered run declines both remote stages and exits 1, as the remote apply does. The brief's site surface chapter states the verb and the page set, the internals chapters the hosting seam and the interim credential file, and the intent's Decisions record what the lane settled within the rulings, the credential's interim source among them with itd-2609221017023290 named as its successor. ACKNOWLEDGEMENTS.md credits the provider the adapter targets. Assisted-by: Claude:claude-opus-5-5
… the build never emits Refs: iss-2609252311550527 Assisted-by: Claude:claude-opus-5-5
…f its own The site internals chapter listed /record/timeline/ among the explorer's pages; the build never emitted it. The chapter now says where the genealogy renders, and the unreachable timelinePage and its route constant are gone. Refs: iss-2609252311550527 Assisted-by: Claude:claude-opus-5-5
Resolves: iss-2609252311550527 Assisted-by: Claude:claude-opus-5-5
The lane review of `abcd site setup` (criterion 6 of spc-2609212141407459) named two medium and two low findings. Each is captured before it is fixed. The duplicate-key finding in the credential file is deferred to the integration step: the strict decoder it needs lives on an unmerged lane, and copying it here would fork it. Refs: iss-2609260120383554 Refs: iss-2609260120384106 Refs: iss-2609260120380835 Refs: iss-2609260120380520 Assisted-by: Claude:claude-opus-5-5
The forge's environment write (PUT /repos/{r}/environments/{env})
replaces the environment's whole protection set, and setup sent only a
deployment policy through it. A `site` environment a person had put
required reviewers or a wait timer on lost them after a confirmation
that named only a branch restriction.
Setup now writes that endpoint only for an ABSENT environment. An
existing one already on custom policies gains the policies it lacks
through their own endpoint, which touches nothing else. An existing one
that admits more refs is left as it is, reported `unrestricted`, and
restricting it is a remaining step printed ahead of any secret step.
Resending the listed protection rules was not taken: the listing and
the write do not carry the same fields, so it cannot be shown lossless
offline.
A run that leaves an environment unrestricted and writes nothing else
still reports no_change: the overall status says what was written, and
the step says what is left.
The forge fake models the replacement (a put clears the environment's
protection rules), and TestAnExistingEnvironmentIsNeverRewritten was
watched fail against the old plan (a `put site`, the reviewers lost, no
step) before the change.
Refs: iss-2609260120383554
Assisted-by: Claude:claude-opus-5-5
… rewritten Resolves: iss-2609260120383554 Assisted-by: Claude:claude-opus-5-5
When the forge stage was refused (gh absent or unauthenticated, or a forge call failing) setup printed the `gh secret set … --env site` steps and no step to create the environments. A person following the steps set the token, and the committed workflow's first run then auto-created `site` with no deployment policy, deployable by any write-access dispatch from any branch. The refused path now prints the same "create the forge environments" step the unreachable path does, ahead of the secret steps, and the step says to do it before setting any deploy secret. TestEveryForgeFailureIsReportedAndStopsTheRemoteWrites was extended and watched fail for all four forge calls before the change. Refs: iss-2609260120380835 Assisted-by: Claude:claude-opus-5-5
… environment step Resolves: iss-2609260120380835 Assisted-by: Claude:claude-opus-5-5
On a repository `launch scaffold` laid out, the workflow `abcd site setup` writes never fired by itself. auto-release runs release.yml by workflow_call, inside its own run, so no run named `release` completes for workflow_run; a hand-pushed tag's `release` run has the tag as its head branch, which `branches: [<default>]` dropped; and a release made with the workflow's GITHUB_TOKEN fires no release:published. Only a hand dispatch deployed, while the verb said the site deploys on the next published release. The workflow_run entry now names both `release` and `auto-release` and filters no branch, and the render job's gate admits a head branch that is the default branch or starts with `v`, beside the fork and pull-request conditions it already held (TestAWorkflowRunFromAFork- NeverRenders still covers those). auto-release completes on every push to the default branch, so a push that released nothing redeploys the latest release over itself; the header says so. The intent's Decision 6 carries a dated addendum, and the verb's closing note names both publishers. TestTheWorkflowFiresOnEveryReleasePath renders the workflow for a non-default branch name, reads the trigger and the gate, and holds the listened-for names to the scaffolded workflows' own; it was watched fail on the old gate before the change. Refs: iss-2609260120384106 Assisted-by: Claude:claude-opus-5-5
…y release path Resolves: iss-2609260120384106 Assisted-by: Claude:claude-opus-5-5
…utput The lane's security review (item 5, informational) asked that the posture it accepted be named in the intent rather than left to be inferred: the render takes abcd's latest release, attestation-bound; a compromised release can deface the site but not reach the deploy secrets; wrangler has a named version and no integrity pin; `v*` tags are admitted with no tag ruleset checked; and a `--json` exit-1 run prints the result and then the error envelope. Assisted-by: Claude:claude-opus-5-5
The generated site workflow's header said a release-workflow completion that released nothing "redeploys the latest release over itself, which changes nothing". After a deliberate rollback dispatch (`tag: vOLD`) the next such completion puts the latest release back, so the claim was untrue in that case. The header and Decision 6's addendum, which carried the same sentence, now say so. The workflow itself is unchanged. Assisted-by: Claude:claude-opus-5-5
`site setup`'s text render padded every file and environment status to eight characters, so `unrestricted` (twelve), `unreachable` and `not_reached` (eleven) pushed their environment's name out of the column and left its change lines under the status instead. The column is now as wide as the longest status in the result, never narrower than eight, and an environment's change lines are indented to sit under its name. Assisted-by: Claude:claude-opus-5-5
…t a broad custom rule admits Review 2 of the site setup lane noted that an existing environment on custom deployment policies is judged only by the rules it lacks, never by the rules it holds, so one that also admits `*` is reported current. Refs: iss-2609260856423957 Assisted-by: Claude:claude-opus-5-5
…ricted An existing environment on custom deployment policies was judged only by the two rules setup needs, never by the rules it already held, so one that also admitted branch `*` was reported current (or gained the tag rule and was reported written) with no step, while it admitted every branch to the deploy environment's secrets. A rule beyond those two now makes it `unrestricted`, with a step that names the rules to remove; setup writes nothing to it, since removing a rule a person put there is theirs to do. Any extra rule counts, including one narrower than `v*`: the step is conservative rather than a glob comparison. The command page and the brief's surface chapter say so. Refs: iss-2609260856423957 Assisted-by: Claude:claude-opus-5-5
… rule Resolves: iss-2609260856423957 Assisted-by: Claude:claude-opus-5-5
`abcd site setup` lays out a managed repository's release-rendered site: the repository half, the forge's two deployment environments and the host, with every step it did not take named. The intent declares its impact (additive), so the close stamps none. Delivers: itd-2609061543533170 Assisted-by: Claude:claude-opus-5-5
The deployment-policy read is unpaginated past 100 rules, and the default branch the check compares against is derived locally rather than read from the forge. Refs: iss-2609260927217168, iss-2609260927214634 Assisted-by: Claude:claude-opus-5-5
…uild Refs: iss-2609260928152365 Assisted-by: Claude:claude-opus-5-5
scaffold.go: kept this branch's shared WriteFiles and carried main's CIChecks report field and its fourth scaffolded file (the reviews-charter check) into the planned list. Assisted-by: Claude:claude-opus-5-5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With this change, a managed repository can take its site to a live address with one verb:
abcd site setupchecks the forge, the hosting account and the deploy environment, writes a site workflow that renders with abcd's latest verified release, and names every step it cannot take itself.site setupwrites the site workflow and its page switches all-or-nothing, and never rewrites an existing deployment environment. An environment that a broad custom branch rule admits is reported unrestricted, gets no forge write, and is told to restrict before any secret step.releaserun, and a dispatch), and renders only this repository's own successful release run.gh secret set … --env siteline to run.Nothing here has run against the real hosting service or a live Actions run: every provider and forge call is exercised against in-process fakes.
Review: Fable 5.1, three rounds including a security review (criterion 6). Round 1 FIX FIRST (two mediums: an environment write that dropped required reviewers, and a workflow that never fired on a scaffolded repo), fixed in round 2; round 2 SHIP with four notes, fixed in round 3; round 3 grew new trust-path behaviour (the broad-rule check), so it was verified: SHIP. Three follow-ups are captured open: an unpaginated policy read, a default branch derived locally rather than read from the forge, and a header link to /docs/ on a repo with no docs build.
Delivers: itd-2609061543533170
Resolves: iss-2609252311550527
Resolves: iss-2609260120383554
Resolves: iss-2609260120380835
Resolves: iss-2609260120384106
Resolves: iss-2609260856423957
Refs: iss-2609260120380520, iss-2609260927217168, iss-2609260927214634, iss-2609260928152365
Assisted-by: Claude:claude-opus-5-5