ci(pr-preview): register dynamic GitHub deployment per PR#2
Conversation
Reframe the MVP-only bootstrap checklist as a project-wide status doc covering all six roadmap phases. Adds spec coverage (15 specs), ADR coverage (66 ADRs with status + gap), phase status, and an ordered 15-item backlog with verifiable Done criteria so a fresh session can pick up "the next step" without rediscovering scope. Wires AGENTS.md to point at it as the first read. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add `environment: pr-preview-<N>` to the PR preview deploy job so GitHub surfaces a Deployments panel on the PR with the apex URL. Cleanup workflow deletes the environment on PR close so it exists iff the workers exist; this needs `administration: write` on the cleanup job.
WalkthroughThis PR implements automated cleanup for GitHub Actions PR preview environments and consolidates project status documentation. The Sequence Diagram(s)sequenceDiagram
participant ComponentA
participant ComponentB
ComponentA->>ComponentB: observable interaction
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/cleanup-pr-preview.yml:
- Line 16: Remove the invalid permission line "administration: write" and
instead mint a GitHub App installation token using the
actions/create-github-app-token@v2 action, then pass that token into the
environment deletion step via the github-token input; update the job to call
create-github-app-token (store its output as e.g. github_app_token) and use that
output in the deleteAnEnvironment call so the repository Administration write
scope is provided by the App token rather than GITHUB_TOKEN.
In `@AGENTS.md`:
- Around line 3-8: Update AGENTS.md to add explicit agent definitions and
configuration fields: for each agent (give them clear identifiers/names),
document role, capabilities, constraints, required inputs and environment
variables, ownership/escalation path, expected outputs, and any runtime/config
flags; derive implemented/partial/deferred status from
docs/ops/project-status.md and cross-reference relevant domain language in
CONTEXT.md and specs in docs/specs/README.md; ensure each agent section uses
consistent field names (e.g., name, role, capabilities, constraints,
required_env, escalation_owner, status) so downstream code can parse these
entries.
In `@docs/ops/project-status.md`:
- Around line 166-236: MD022 errors come from missing blank lines immediately
after level-3 backlog headings; edit the Markdown document and insert a single
blank line after each "###" backlog heading (e.g., "### 1. Wire `runCommand` and
`createOperationEvent` into mutation routes", "### 2. Enforce native rate-limit
bindings", etc.) so every level-3 heading is followed by exactly one empty line
before the next paragraph or list item, then run markdownlint to verify the
MD022 warnings are resolved.
- Line 5: The docs conflict: docs/ops/project-status.md currently states it is
read after CONTEXT.md/specs/ADRs while AGENTS.md says to read project-status.md
first; pick the canonical read order and make the wording consistent across both
files—either change the sentence in project-status.md that begins "First doc a
fresh agent reads after ..." to match AGENTS.md's “read first” phrasing, or
update AGENTS.md to instruct reading it after
CONTEXT.md/docs/specs/README.md/docs/adr/README.md; ensure the doc titles
(project-status.md, AGENTS.md, CONTEXT.md, docs/specs/README.md,
docs/adr/README.md) are referenced consistently and update the single line where
the order is described.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2850c318-c228-4dbf-8073-4a1c777c6785
📒 Files selected for processing (6)
.github/workflows/cleanup-pr-preview.yml.github/workflows/pr-preview.ymlAGENTS.mddocs/ops/mvp-bootstrap-checklist.mddocs/ops/project-status.mddocs/specs/mvp.md
💤 Files with no reviewable changes (1)
- docs/ops/mvp-bootstrap-checklist.md
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| administration: write |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .github/workflows/cleanup-pr-preview.yml | head -100Repository: zaks-io/agent-paste
Length of output: 3768
🌐 Web query:
GitHub Actions GITHUB_TOKEN workflow permissions available scopes list
💡 Result:
GitHub Actions’ automatically created GITHUB_TOKEN supports these workflow/job “permissions” scopes (parameterized names) that you can set via the permissions: key (values typically read/write/none depending on the scope): actions, checks, contents, deployments, issues, packages, pull-requests, repository-projects, security-events, statuses. [1][2][3] Note: The scopes list is used with the workflow permissions key to grant least privilege; any permission that is absent is set to none. [1] Example: permissions: contents: read packages: read pull-requests: none [1]
Citations:
- 1: https://github.blog/changelog/2021-04-20-github-actions-control-permissions-for-github_token/
- 2: https://docs.github.com/en/enterprise-server@3.0/actions/reference/authentication-in-a-workflow
- 3: https://docs.github.com/actions/concepts/security/github_token
🌐 Web query:
GitHub REST API deleteAnEnvironment authorization requirements permissions
💡 Result:
To delete an environment using the GitHub REST API (DELETE /repos/{owner}/{repo}/environments/{environment_name}), you must meet specific authorization and permission requirements depending on the authentication method used [1][2][3]. Personal Access Tokens (PATs) For personal access tokens (classic), the token must have the 'repo' scope to successfully call this endpoint [1][2][3]. Fine-grained Access Tokens and GitHub Apps For fine-grained personal access tokens, as well as GitHub App user access tokens and installation access tokens, the token must be granted the 'Administration' repository permission with 'write' access [3]. General Authentication Notes This endpoint is available for GitHub Apps (user-to-server and installation-to-server) and fine-grained personal access tokens, provided they carry the required 'Administration' write permissions [3]. Ensure that the {environment_name} is properly URL-encoded if it contains special characters such as slashes (e.g., using %2F) [1][2][3]. If you encounter a 404 error despite having correct permissions, verify that the environment name is correctly encoded and that the environment exists within the specified repository [4].
Citations:
- 1: https://docs.github.com/enterprise-cloud@latest/rest/deployments/environments
- 2: https://docs.github.com/rest/deployments/environments
- 3: https://docs.github.com/enterprise-server@3.10/rest/deployments/environments
- 4: https://stackoverflow.com/questions/79138595/github-api-for-deleting-an-environment-does-not-work
The cleanup workflow cannot authorize the environment deletion API call.
administration is not a valid GITHUB_TOKEN workflow permission scope. GitHub Actions workflows support only: actions, checks, contents, deployments, issues, packages, pull-requests, repository-projects, security-events, and statuses. Line 16 cannot grant the access the delete call requires—the deleteAnEnvironment endpoint requires repository Administration write permission, which is not available to GITHUB_TOKEN. This workflow will fail at the deletion step.
Mint a GitHub App installation token via actions/create-github-app-token@v2 and pass it to the deletion step with github-token:, then remove line 16.
Suggested shape
permissions:
contents: read
pull-requests: write
- administration: write
+ - name: Mint repo-admin token
+ id: app-token
+ uses: actions/create-github-app-token@v2
+ with:
+ app-id: ${{ vars.REPO_ADMIN_APP_ID }}
+ private-key: ${{ secrets.REPO_ADMIN_APP_PRIVATE_KEY }}
+
- name: Delete GitHub Preview environment
uses: actions/github-script@v8
with:
+ github-token: ${{ steps.app-token.outputs.token }}
script: |
const name = `pr-preview-${process.env.PR_NUMBER}`;
try {
await github.rest.repos.deleteAnEnvironment({🧰 Tools
🪛 actionlint (1.7.12)
[error] 16-16: unknown permission scope "administration". all available permission scopes are "actions", "artifact-metadata", "attestations", "checks", "contents", "deployments", "discussions", "id-token", "issues", "models", "packages", "pages", "pull-requests", "repository-projects", "security-events", "statuses"
(permissions)
🪛 zizmor (1.25.2)
[warning] 16-16: overly broad permissions (excessive-permissions): administration: write is overly broad at the workflow level
(excessive-permissions)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/cleanup-pr-preview.yml at line 16, Remove the invalid
permission line "administration: write" and instead mint a GitHub App
installation token using the actions/create-github-app-token@v2 action, then
pass that token into the environment deletion step via the github-token input;
update the job to call create-github-app-token (store its output as e.g.
github_app_token) and use that output in the deleteAnEnvironment call so the
repository Administration write scope is provided by the App token rather than
GITHUB_TOKEN.
| ## Start here | ||
|
|
||
| Read `docs/ops/project-status.md` first. It is the source of truth for what is implemented, what is partial, what is deferred, and the ordered backlog. When asked to "implement the next step," start at the top of the backlog in that file. | ||
|
|
||
| Then: `CONTEXT.md` (domain language), `docs/specs/README.md` (spec reading order), `docs/adr/README.md` (ADR index). | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add explicit agent definitions and config fields to AGENTS.md.
This section defines reading order only; it still does not document concrete agent definitions and their configurations (roles, capabilities, constraints, required inputs/env, escalation/ownership), which is required for this file.
As per coding guidelines AGENTS.md: "AGENTS.md file should document agent definitions and their configurations".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@AGENTS.md` around lines 3 - 8, Update AGENTS.md to add explicit agent
definitions and configuration fields: for each agent (give them clear
identifiers/names), document role, capabilities, constraints, required inputs
and environment variables, ownership/escalation path, expected outputs, and any
runtime/config flags; derive implemented/partial/deferred status from
docs/ops/project-status.md and cross-reference relevant domain language in
CONTEXT.md and specs in docs/specs/README.md; ensure each agent section uses
consistent field names (e.g., name, role, capabilities, constraints,
required_env, escalation_owner, status) so downstream code can parse these
entries.
|
|
||
| Last updated: 2026-05-21. | ||
|
|
||
| First doc a fresh agent reads after `AGENTS.md`, `CONTEXT.md`, `docs/specs/README.md`, and `docs/adr/README.md`. Answers: what is built, what is scaffolded, where the code diverges from the ADRs/specs, what the next concrete step is. |
There was a problem hiding this comment.
Align read-order wording with AGENTS.md to avoid contradictory onboarding guidance.
Line 5 says this doc is read after CONTEXT.md/specs/ADRs, while AGENTS.md now instructs reading this doc first. Keep one canonical order across both docs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/ops/project-status.md` at line 5, The docs conflict:
docs/ops/project-status.md currently states it is read after
CONTEXT.md/specs/ADRs while AGENTS.md says to read project-status.md first; pick
the canonical read order and make the wording consistent across both
files—either change the sentence in project-status.md that begins "First doc a
fresh agent reads after ..." to match AGENTS.md's “read first” phrasing, or
update AGENTS.md to instruct reading it after
CONTEXT.md/docs/specs/README.md/docs/adr/README.md; ensure the doc titles
(project-status.md, AGENTS.md, CONTEXT.md, docs/specs/README.md,
docs/adr/README.md) are referenced consistently and update the single line where
the order is described.
| ### 1. Wire `runCommand` and `createOperationEvent` into mutation routes | ||
| - Drives: ADR 0022, ADR 0035, `docs/specs/api.md` | ||
| - Files: `apps/api/src/index.ts`, `apps/upload/src/index.ts`, `packages/commands/src/index.ts` | ||
| - Done: every POST/PUT/DELETE route in `api` and `upload` is wrapped via `runCommand`; idempotency keys are honored from `Idempotency-Key` header; replay of the same idempotency key returns the original result; vitest covers replay for at least workspace-create, api-key-create, artifact-delete. | ||
|
|
||
| ### 2. Enforce native rate-limit bindings | ||
| - Drives: ADR 0039, ADR 0064 | ||
| - Files: `apps/api/src/index.ts`, `apps/upload/src/index.ts`, `apps/*/wrangler.jsonc` | ||
| - Done: every authenticated mutation calls `env.ACTOR_RATE_LIMIT.limit(...)` and `env.WORKSPACE_BURST_CAP.limit(...)`; over-limit returns 429 with envelope; a smoke test triggers 429 from an in-test client. | ||
|
|
||
| ### 3. Generate OpenAPI from Zod contracts | ||
| - Drives: ADR 0016, ADR 0017, ADR 0038 | ||
| - Files: `packages/contracts/src/*`, `apps/api/src/index.ts`, `apps/upload/src/index.ts`, `apps/content/src/index.ts` | ||
| - Done: `/openapi.json` on api/upload/content is generated from `packages/contracts` via `@hono/zod-openapi` (or equivalent); `pnpm verify` runs a schema-diff check against a checked-in golden; CI fails if contracts drift from served OpenAPI. | ||
|
|
||
| ### 4. Add `--yes` guards to destructive admin CLI commands | ||
| - Drives: `docs/specs/admin.md` | ||
| - Files: `apps/cli/src/index.ts` | ||
| - Done: `agent-paste admin api-key revoke`, `artifacts delete`, and `cleanup run` (non-dry-run) refuse to run without `--yes`; CLI test asserts refusal and a `--yes` happy path. | ||
|
|
||
| ### 5. Consolidate content-signing secret names | ||
| - Drives: ADR 0028, ADR 0058 | ||
| - Files: `scripts/bootstrap-secrets.mjs`, `apps/*/wrangler.jsonc`, `apps/api/src/index.ts`, `apps/content/src/index.ts` | ||
| - Done: only one of `CONTENT_SIGNING_SECRET` / `CONTENT_GATEWAY_SIGNING_KEY_V1` remains, named consistently across code, bootstrap script, and ADRs; a one-time rotation note is added to `docs/ops/runbook` (or this doc) for any environment that already holds both. | ||
|
|
||
| ### 6. Move runtime queries to Drizzle | ||
| - Drives: ADR 0018 | ||
| - Files: `packages/db/src/**`, callers in `apps/api`, `apps/upload` | ||
| - Done: workspace/api-key/artifact/upload-session reads and writes flow through Drizzle query objects (not raw SQL templates); `pnpm verify` runs a Drizzle introspection check against the migration file. Scope this to MVP routes; leave admin/cleanup queries as a follow-up if the change balloons. | ||
|
|
||
| ### 7. Apply Postgres RLS at runtime | ||
| - Drives: ADR 0044 | ||
| - Files: `packages/db/src/**`, `apps/api/src/index.ts`, `apps/upload/src/index.ts`, `packages/db/migrations/*` | ||
| - Done: Hyperdrive role is `NOBYPASSRLS`; every request opens a Postgres txn that issues `SET LOCAL app.workspace_id = $1` before any query; a vitest scenario inserts two workspaces and confirms cross-workspace reads return zero rows. | ||
|
|
||
| ### 8. Complete error envelope (`request_id`, `docs`) | ||
| - Drives: ADR 0036, `docs/specs/contracts.md` | ||
| - Files: `apps/api/src/index.ts`, `apps/upload/src/index.ts`, `apps/content/src/index.ts`, `packages/contracts/src/*` | ||
| - Done: every error response includes `request_id`; an optional `docs` URL is attached for codes that have a documented remediation; `X-Request-Id` header is echoed on every response (error or success); golden tests cover at least 404/401/409/422/429/500. | ||
|
|
||
| ### 9. Verify bytes-after-delete and bytes-after-expiry cleanup | ||
| - Drives: ADR 0048, `docs/specs/acceptance.md` | ||
| - Files: `apps/api/src/index.ts` (scheduled handler), `scripts/smoke-local-mvp.mjs`, `scripts/smoke-hosted.mjs` | ||
| - Done: smoke creates an artifact with a 1-day TTL, advances clock (or uses a forced-expiry test endpoint), runs cleanup, confirms R2 prefix is empty and signed URL returns 404 with denylist hit logged. | ||
|
|
||
| ### 10. Exercise PR preview lifecycle on a same-repo PR | ||
| - Drives: ADR 0007, ADR 0012, `.github/workflows/pr-preview.yml` | ||
| - Files: workflow itself, `scripts/deploy-pr-preview.mjs`, `scripts/cleanup-pr-preview.mjs` | ||
| - Done: a same-repo PR (the one carrying items 1-9 above is the natural candidate) creates a Neon branch, deploys preview Workers, runs hosted smoke, posts a comment with URLs, and tears everything down on close. Captured run links recorded in this doc. | ||
|
|
||
| ### 11. Wire Logpush → Axiom for `api`/`upload`/`content` | ||
| - Drives: ADR 0011, `docs/specs/phases.md` Phase 2 | ||
| - Files: Cloudflare console + `docs/ops/` runbook (no Worker code change required if using Cloudflare Logs config) | ||
| - Done: an Axiom dataset receives Worker logs for all three Workers; a basic dashboard shows 5xx rate and p95 latency; secrets/PII redaction confirmed (no API key secret or signed-URL token in logs). | ||
|
|
||
| ### 12. Review and merge `t3code/7bcd4587` | ||
| - Drives: this branch holds Apex/front-end and CI work that needs to land or be discarded. | ||
| - Files: TBD until review. | ||
| - Done: branch is either merged to `main` (with conflicts resolved and CI green) or closed with a written reason. Same decision for `t3code/5b6355f9` if still extant. | ||
|
|
||
| ### 13. Decide on remaining ADR 0046 drift (operator identity) | ||
| - Drives: ADR 0046 | ||
| - Files: ADR 0046 itself, possibly a new ADR | ||
| - Done: ADR 0046 is amended (or a new ADR supersedes it) to either accept the single-bearer admin token as the production model or to commit Cloudflare Access + email allowlist before Phase 3. No code change in this item — just a written decision. | ||
|
|
||
| ### 14. CSP allowlist audit | ||
| - Drives: ADR 0029, ADR 0030, `docs/specs/content-rendering.md` | ||
| - Files: `apps/content/src/index.ts` | ||
| - Done: CSP `script-src` and `connect-src` allowlists are validated against the current ADR 0029 list; SVG responses use a strict CSP override; a vitest snapshot pins the CSP header for HTML, CSS, JS, SVG, and PNG. | ||
|
|
||
| ### 15. Complete bootstrap hosting checklist |
There was a problem hiding this comment.
Fix markdownlint MD022 violations on backlog subsection headings.
Each ### backlog item heading needs a blank line after the heading. The current layout triggers the reported MD022 warnings.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 166-166: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 171-171: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 176-176: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 181-181: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 186-186: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 191-191: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 196-196: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 201-201: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 206-206: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 211-211: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 216-216: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 221-221: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 226-226: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 231-231: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 236-236: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/ops/project-status.md` around lines 166 - 236, MD022 errors come from
missing blank lines immediately after level-3 backlog headings; edit the
Markdown document and insert a single blank line after each "###" backlog
heading (e.g., "### 1. Wire `runCommand` and `createOperationEvent` into
mutation routes", "### 2. Enforce native rate-limit bindings", etc.) so every
level-3 heading is followed by exactly one empty line before the next paragraph
or list item, then run markdownlint to verify the MD022 warnings are resolved.
The smoke step was hitting ETIMEDOUT against the freshly deployed `*.workers.dev` URLs because Cloudflare propagation lags the deploy by several seconds on first publish. Poll each worker URL with a 5s timeout up to 30 times before running smoke.
|
agent-paste PR preview is ready. API: https://agent-paste-api-pr-2.isaac-a46.workers.dev |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/pr-preview.yml:
- Around line 110-119: The timeout message "did not become reachable within 60s"
is incorrect for the retry loop using i, curl --max-time 5 and sleep 2 across 30
attempts (worst-case ~210s); update the error reporting to reflect the actual
wait window by either changing the static message to the correct value (e.g.,
"~210s") or compute and report the real elapsed time using shell SECONDS (set
SECONDS=0 before the loop and include $SECONDS in the ::error:: message) so the
failure log accurately shows how long the script waited.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ebfcf8f3-ccfb-4fd5-978a-3ad51143ef82
📒 Files selected for processing (1)
.github/workflows/pr-preview.yml
| while [ $i -lt 30 ]; do | ||
| i=$((i+1)) | ||
| if curl -sS -o /dev/null --max-time 5 "$url"; then | ||
| echo "$url reachable after $i attempt(s)" | ||
| return 0 | ||
| fi | ||
| sleep 2 | ||
| done | ||
| echo "::error::$url did not become reachable within 60s" | ||
| return 1 |
There was a problem hiding this comment.
Timeout message is inconsistent with the implemented retry window.
The loop can run for ~210 seconds, but Line 118 reports a 60-second timeout. This makes failures harder to interpret during CI incidents.
Suggested fix
wait_for() {
local url="$1"
+ local attempts=30
+ local curl_timeout=5
+ local sleep_seconds=2
+ local max_wait=$((attempts * (curl_timeout + sleep_seconds)))
local i=0
- while [ $i -lt 30 ]; do
+ while [ $i -lt "$attempts" ]; do
i=$((i+1))
- if curl -sS -o /dev/null --max-time 5 "$url"; then
+ if curl -sS -o /dev/null --max-time "$curl_timeout" "$url"; then
echo "$url reachable after $i attempt(s)"
return 0
fi
- sleep 2
+ sleep "$sleep_seconds"
done
- echo "::error::$url did not become reachable within 60s"
+ echo "::error::$url did not become reachable within ${max_wait}s"
return 1
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/pr-preview.yml around lines 110 - 119, The timeout message
"did not become reachable within 60s" is incorrect for the retry loop using i,
curl --max-time 5 and sleep 2 across 30 attempts (worst-case ~210s); update the
error reporting to reflect the actual wait window by either changing the static
message to the correct value (e.g., "~210s") or compute and report the real
elapsed time using shell SECONDS (set SECONDS=0 before the loop and include
$SECONDS in the ::error:: message) so the failure log accurately shows how long
the script waited.
…ting (#9) * fix(api,upload): replay idempotent mutations before rate-limit accounting Adds a SELECT-only peekIdempotentReplay helper to @agent-paste/commands and uses it in both upload mutation routes to short-circuit known-good retries before either rate-limit binding is touched. Hosted smoke now hammers POST /v1/upload-sessions with unique idempotency keys to confirm 429 with the rate_limited_actor envelope on preview targets. * docs(status): close out item #1 and renumber backlog PR #9 finished the rate-limit work, so mark ADR 0039/0064 done, drop the backlog entry, and shift items #2-#11 down one slot. Updates the phase partition note to match the new numbering. * debug: dump content fetch context on failure Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: drop temporary debug print from smoke-hosted Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(smoke): run rate-limit probe after purge tests; reconcile backlog The new assertActorRateLimitFires helper exhausts the actor's 60/min budget by hammering an upload mutation. The next assertion, assertBytesPurgedAfterExpiry, republishes through the same user API key and was hitting rate_limited_actor at the publish step. Reorder so the rate-limit probe runs last; bytes-after-delete and -after-expiry now run on a fresh budget. Also reconcile docs/ops/project-status.md: PR #6 (CSP) and PR #8 (bytes-after-delete/expiry) closed two backlog items without updating this doc. Moved both into Recently Completed and renumbered. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(smoke): fire rate-limit probe in parallel waves Sequential 120 attempts at ~2/sec spread across multiple Cloudflare worker isolates means no single isolate ever sees the 60-in-60s threshold. Replace the serial loop with four parallel waves of 80 requests each. The burst forces a single isolate's counter past the limit before the trailing window slides. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
PR #2 added `permissions: administration: write` to the cleanup workflow, but `administration` is not a valid GITHUB_TOKEN scope. GitHub rejected the workflow at evaluation time, silently dropping every `pull_request.closed` event for PRs #2--#9 and accumulating eight stale `preview/pr-N` Neon branches that tripped the 10-branch free-tier cap (blocks PR #10/#11/#12 deploys with HTTP 422). Drop the invalid permission and the `deleteAnEnvironment` step that required it; rename the file so GitHub registers a fresh workflow id instead of reusing the wedged record; validate the resolved PR number is a positive integer before deleting anything. Stale Neon branches still need a one-time operator purge -- agent is forbidden from calling `neondatabase/delete-branch-action` autonomously. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
PR #2 added `permissions: administration: write` to the cleanup workflow, but `administration` is not a valid GITHUB_TOKEN scope. GitHub rejected the workflow at evaluation time, silently dropping every `pull_request.closed` event for PRs #2--#9 and accumulating eight stale `preview/pr-N` Neon branches that tripped the 10-branch free-tier cap (blocks PR #10/#11/#12 deploys with HTTP 422). Drop the invalid permission and the `deleteAnEnvironment` step that required it; rename the file so GitHub registers a fresh workflow id instead of reusing the wedged record; validate the resolved PR number is a positive integer before deleting anything. Stale Neon branches still need a one-time operator purge -- agent is forbidden from calling `neondatabase/delete-branch-action` autonomously. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* feat: add PATCH /v1/web/settings with per-workspace retention Backlog item #2 (Phase 3 dashboard). Persist auto-deletion retention per workspace and let admins edit workspace name + retention from the dashboard. - workspaces gains auto_deletion_days (int, default 30, CHECK 1..90) via migration 0007; schema + snapshot updated. - GET /v1/web/settings now reads the persisted value instead of the global USAGE_POLICY default. - PATCH /v1/web/settings (workos_access_token, admin scope, idempotent, actor rate limit) updates name + retention through runCommand and writes a workspace.settings.updated audit event. Bounds (1..90 days, name 1..120) enforced at the Zod contract and the DB check constraint. - Repo: workspaces.update port + both adapters; updateWebSettings in RepositoryCore; toWebSettings dedups the GET/PATCH shape. - Contract + OpenAPI golden regenerated; api + db tests extended. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix: fail closed on out-of-range retention in repository core Address CodeRabbit review on #44. The local adapter has no DB CHECK constraint, so a direct updateWebSettings call could persist an auto_deletion_days value Postgres would reject. Guard against it in RepositoryCore using the shared MIN/MAX_AUTO_DELETION_DAYS bounds and lock the behavior with a repo test. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Summary
Make GitHub show a Deployments panel on each PR when the PR preview workflow runs. Today the preview deploys to ephemeral
*-pr-NWorkers but GitHub has no record, so PRs don't surface the URL or status. A dynamic per-PR environment is created on deploy and deleted on PR close, so the environment exists iff the Workers exist.Changes
.github/workflows/pr-preview.yml: declareenvironment: pr-preview-<N>withurl: ${{ steps.deploy.outputs.apex_url }}on the deploy job. GitHub auto-creates the env on first run..github/workflows/cleanup-pr-preview.yml: addadministration: writeto the job permissions and a new step that callsDELETE /repos/{owner}/{repo}/environments/{name}viaactions/github-script. 404 is treated as a no-op so cleanups for never-deployed PRs (forks, failed setup) don't fail.Risk: LOW
administration: writeon the auto-issuedGITHUB_TOKEN. Scope is needed to delete environments; not used for anything else. If org policy denies the elevation, the cleanup step 403s but the workers/Neon branch already deleted before that point.Previewenvironment (empty, manually created) is left in place for a future long-lived preview deploy workflow.Test plan
pr-preview-<this-PR-number>environment, the deploy job runs against it, and the PR sidebar shows a Deployments panel with the apex URL.pr-preview-<N>environment fromgh api repos/zaks-io/agent-paste/environments.CodeRabbit notes
actions/github-script@v8to a commit SHA. Conflicts with the repo convention of floating major tags across allactions/*actions (seeci.yml,pr-preview.yml, and the existing comment step in this same file).Summary by CodeRabbit
Chores
Documentation