Skip to content

feat(apex): add marketing worker at agent-paste.sh#1

Merged
isuttell merged 9 commits into
mainfrom
t3code/7bcd4587
May 21, 2026
Merged

feat(apex): add marketing worker at agent-paste.sh#1
isuttell merged 9 commits into
mainfrom
t3code/7bcd4587

Conversation

@isuttell

@isuttell isuttell commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

  • New apps/apex Cloudflare Worker serving the marketing surface at agent-paste.sh plus the agent-discoverable /llms.txt and /agents.md files. Per ADR 0014, every authenticated product path (/dashboard, /artifacts, /keys, /audit, /settings, /admin, /al/, /r/, /login, /logout, /auth/) is 308-redirected to app.agent-paste.sh with query strings preserved. No cookies, no bindings besides static assets, no DB.
  • Hand-coded landing page following docs/specs/style-guide.md ("Quiet Confidence"): Hanken Grotesk + JetBrains Mono self-hosted, emerald-teal accent, hero with primary Get an API key CTA, the canonical npx agent-paste publish ./report block with copy affordance, and the §5.11 identifier component (mono, tinted, click-to-copy with 700ms accent flash) rendering the sample Artifact ID. Honors prefers-color-scheme and prefers-reduced-motion.
  • Wired into the existing CI/CD: scripts/deploy-preview.mjs deploys it in production/preview order, scripts/deploy-pr-preview.mjs builds an apex.json config with the assets binding and deploys to agent-paste-apex-pr-{N}.workers.dev, the PR-comment step now includes the apex URL, and scripts/smoke-hosted.mjs asserts / returns 200 HTML, /llms.txt text/plain, /dashboard 308 to the app domain, and no set-cookie.

Test plan

  • pnpm --filter @agent-paste/apex typecheck clean.
  • pnpm --filter @agent-paste/apex test — 14/14 vitest tests pass (HTML render, content types, 308 targets, query preservation, no-cookie on every response, assets fallthrough, 405 on non-GET, §11 banned-token absence).
  • pnpm --filter @agent-paste/apex lint clean.
  • pnpm verify at the root — 45/45 turbo tasks pass.
  • CI green on this PR.
  • pr-preview.yml posts an apex_url and the rendered apex is visible at that URL.
  • After merge + Production env approval, https://agent-paste.sh/ returns the rendered page and curl -sI https://agent-paste.sh/dashboard returns 308 -> https://app.agent-paste.sh/dashboard.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an Apex marketing domain that serves public docs, text guides, a sitemap/robots, and handles safe redirects to the main app; PR preview comments and preview pipeline now include an Apex preview link.
  • Documentation

    • New Apex docs and workspace README updates describing public endpoints, marketing surface, and non-authenticated behavior.
  • Tests

    • Added smoke and unit tests validating Apex endpoints, content types, sitemap/robots, and redirect behavior.
  • Chores

    • Deployment, preview, cleanup and smoke flows updated to build/deploy/verify Apex; install-hooks and workspace configs adjusted.

Review Change Stack

Per ADR 0014, the apex domain serves the marketing surface plus
agent-discoverable /llms.txt and /agents.md, and 308-redirects every
authenticated product path to app.agent-paste.sh. This worker holds no
state, sets no cookies, and has no bindings besides static assets.

Wires the worker into deploy-preview.mjs, the PR preview script (with
apex_url emitted to the PR comment), and the hosted smoke script so the
existing CI/CD pipeline picks it up on the next merge.

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

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Important

Review skipped

This PR was authored by the user configured for CodeRabbit reviews. CodeRabbit does not review PRs authored by this user. It's recommended to use a dedicated user account to post CodeRabbit review feedback.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6a3b76ea-c2b1-4913-9ed3-e49a5548bc0d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR adds apps/apex: a TypeScript Cloudflare Worker that serves public marketing and agent-discoverable content (/, /llms.txt, /agents.md, /robots.txt, /sitemap.xml), renders a styled homepage, redirects product/auth routes to app.agent-paste.sh with 308s, falls back to static asset serving via an ASSETS binding, includes Vitest integration tests, and integrates apex into preview/production deployment scripts, the PR-preview workflow, and smoke tests.

Sequence Diagram: Apex Worker Request Handling

sequenceDiagram
  participant Client
  participant handleRequest
  participant routeApex
  participant productRedirect
  participant ASSETS
  participant Response
  Client->>handleRequest: HTTP Request
  handleRequest->>routeApex: routeApex(request)
  alt routeApex returns Response
    routeApex->>Response: HTML/text/xml response (with SECURITY_HEADERS)
  else routeApex returns null
    routeApex-->>handleRequest: null
    handleRequest->>productRedirect: productRedirect(new URL(...))
    alt productRedirect returns location
      productRedirect-->>handleRequest: redirect URL
      handleRequest->>Response: 308 Redirect (Location header)
    else no product redirect
      alt env.ASSETS is configured
        handleRequest->>ASSETS: env.ASSETS.fetch(request)
        ASSETS->>Response: asset Response (or 404)
      else no ASSETS
        handleRequest->>Response: 404 plain text "not_found"
      end
    end
  end
  Response->>Client: HTTP response
Loading

"I'm a rabbit on a hill so spry,
Apex pages waving to the sky,
Llms and agents, links in line,
Redirects tidy, styles align,
A tiny hop — preview live! 🐰"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(apex): add marketing worker at agent-paste.sh' directly and clearly summarizes the primary change: introducing a new Cloudflare Worker (apex) for serving marketing content at agent-paste.sh.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@apps/apex/src/routes.ts`:
- Around line 11-16: The SECURITY_HEADERS constant is missing browser hardening
for scripts and framing; update the SECURITY_HEADERS object to include a strict
Content-Security-Policy (content-security-policy) and frame protection header
(x-frame-options). Add a CSP that at minimum restricts scripts to 'self' and
disallows unsafe-inline/unsafe-eval (for example: default-src 'self'; script-src
'self'; object-src 'none'; style-src 'self' 'unsafe-hashes' or trusted CDNs as
needed) and add x-frame-options: DENY (or SAMEORIGIN if embedding is required);
ensure the header names match existing casing and HeadersInit usage so the new
keys are included wherever SECURITY_HEADERS is used.

In `@scripts/smoke-hosted.mjs`:
- Around line 77-98: In smokeApex, add assertions to validate the full
redirect/cookie contract: for non-home endpoints (llms, agents, dashboard)
assert that response.headers.get("set-cookie") is falsy to ensure they never set
cookies; and for the dashboard redirect test (redirect variable) perform the
request with a sample query string (e.g., /dashboard?foo=bar) and assert the
returned Location header preserves that query (location includes "?foo=bar" or
equivalent), while keeping the existing equality check for the host/path; update
the fetch calls for /llms.txt, /agents.md and the /dashboard redirect case in
function smokeApex accordingly.
🪄 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: fd3c71ec-312e-4e40-b761-f38f554d120a

📥 Commits

Reviewing files that changed from the base of the PR and between aac05ba and 0bfe6fb.

⛔ Files ignored due to path filters (5)
  • apps/apex/public/favicon.svg is excluded by !**/*.svg
  • apps/apex/public/fonts/HankenGrotesk-Variable.woff2 is excluded by !**/*.woff2
  • apps/apex/public/fonts/JetBrainsMono-Medium.woff2 is excluded by !**/*.woff2
  • apps/apex/public/fonts/JetBrainsMono-Regular.woff2 is excluded by !**/*.woff2
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • .github/workflows/pr-preview.yml
  • README.md
  • apps/apex/README.md
  • apps/apex/package.json
  • apps/apex/src/agents.ts
  • apps/apex/src/copy.ts
  • apps/apex/src/index.test.ts
  • apps/apex/src/index.ts
  • apps/apex/src/llms.ts
  • apps/apex/src/page.ts
  • apps/apex/src/redirects.ts
  • apps/apex/src/routes.ts
  • apps/apex/src/styles.ts
  • apps/apex/tsconfig.json
  • apps/apex/wrangler.jsonc
  • pnpm-workspace.yaml
  • scripts/deploy-pr-preview.mjs
  • scripts/deploy-preview.mjs
  • scripts/smoke-hosted.mjs

Comment thread apps/apex/src/routes.ts
Comment thread scripts/smoke-hosted.mjs
isuttell and others added 2 commits May 21, 2026 12:44
Single-column layout: wordmark, headline, lead, terminal transcript
showing `npx agent-paste publish` returning a durable URL, primary CTA,
and a minimal footer. Removed the showcase/manifest/CTA sections and
infrastructure-link footer; kept a subtle /agents.md pointer for
crawlers. Transcript now renders origin + id together so the result
reads as a URL, matching the lead copy.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add Content-Security-Policy and X-Frame-Options: DENY to apex responses
to harden the marketing surface. CSP allows 'self' plus 'unsafe-inline'
for the inline style and bootstrap script blocks.

Expand smoke-hosted apex assertions: no-cookie checks on llms.txt,
agents.md, and the dashboard redirect, plus a query-preserving redirect
assertion.

Addresses CodeRabbit review.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/apex/src/index.test.ts (3)

88-92: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add no-cookie assertion to match redirect test pattern.

The /dashboard redirect test (line 85) checks set-cookie is null, but this query-preserved redirect test does not. For consistency and given the critical "no cookies" requirement, all redirect tests should verify the header.

🛡️ Suggested addition
   const response = await get("/login?return_to=%2Fartifacts%2Fart_1");
   expect(response.status).toBe(308);
   expect(response.headers.get("location")).toBe("https://app.agent-paste.sh/login?return_to=%2Fartifacts%2Fart_1");
+  expect(response.headers.get("set-cookie")).toBeNull();
 });
🤖 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 `@apps/apex/src/index.test.ts` around lines 88 - 92, The test "preserves query
strings on product redirects" fails to assert that no cookies are set; update
this test (the it block with description "preserves query strings on product
redirects") to include an assertion that response.headers.get("set-cookie") is
null (e.g., expect(response.headers.get("set-cookie")).toBeNull()) immediately
after obtaining response so it matches the `/dashboard` redirect test pattern
and enforces the no-cookie requirement.

139-145: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Expand blanket no-cookie test to match comprehensive intent.

The test is named "never sets cookies on any apex response" but only checks a subset of paths. Other tests exercise redirects (/login, /artifacts/*, /r/*), 404 responses, asset delegation, and 405 errors without verifying set-cookie. Given the critical "no cookies" requirement per ADR 0014, include examples of each response type in this blanket assertion.

🛡️ Suggested expansion
 it("never sets cookies on any apex response", async () => {
-  const paths = ["/", "/llms.txt", "/agents.md", "/robots.txt", "/sitemap.xml", "/dashboard"];
+  const paths = [
+    "/",
+    "/llms.txt",
+    "/agents.md",
+    "/robots.txt",
+    "/sitemap.xml",
+    "/dashboard",
+    "/login",
+    "/artifacts/art_01HZ8K2X9NPQR3VW7TYBE5MCDF",
+    "/r/token-abc",
+    "/no-such-page",
+  ];
   for (const path of paths) {
     const response = await get(path);
     expect(response.headers.get("set-cookie"), `cookie on ${path}`).toBeNull();
   }
 });

Note: Asset and 405 responses are harder to test in this loop (require custom env/method), but the expanded list covers public routes, all redirect types, and 404.

🤖 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 `@apps/apex/src/index.test.ts` around lines 139 - 145, The test "never sets
cookies on any apex response" only checks a few paths; update the paths array
used in the test (the one iterated with for (const path of paths) and the
get(path) call) to include representative examples of all response types
mentioned in the suite: public pages ("/", "/llms.txt", "/agents.md",
"/robots.txt", "/sitemap.xml", "/dashboard"), redirect endpoints (e.g. "/login",
"/artifacts/some-id", "/r/some-short"), a 404 path (e.g. "/non-existent-path"),
and an asset/405 example if feasible; ensure each response returned by get(path)
is asserted with expect(response.headers.get("set-cookie")).toBeNull() so every
response type covered by ADR 0014 is validated.

40-40: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider more robust word-boundary regex (optional).

The current regex /["\s,]inter["\s,]/ requires both leading and trailing boundary characters, which might miss edge cases like font-family:inter (no space) or "inter" at string edges. A word-boundary regex like /\binter\b/i would be more comprehensive.

However, given this is a defensive check and the style guide is enforced upstream, the current regex likely catches real violations in typical HTML/CSS contexts (e.g., "inter", in font stacks).

♻️ Alternative regex
-    expect(body).not.toMatch(/["\s,]inter["\s,]/);
+    expect(body).not.toMatch(/\binter\b/i);
🤖 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 `@apps/apex/src/index.test.ts` at line 40, The test uses a fragile regex
`/["\s,]inter["\s,]/` to assert "inter" isn't present; replace it with a
word-boundary, case-insensitive pattern such as `/\binter\b/i` so matches cover
edge cases like font-family:inter or "inter" at string boundaries—update the
expect assertion that currently calls
expect(body).not.toMatch(/["\s,]inter["\s,]/) to use the new `/\binter\b/i`
pattern.
🤖 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 `@apps/apex/src/page.ts`:
- Around line 36-43: The hero is only rendering the primary CTA; restore the
secondary "View on GitHub" CTA by adding a second anchor after the existing
primary anchor that uses HERO.secondary.href and HERO.secondary.label (escaped
with esc like the primary), e.g. an anchor with the ghost/secondary classes
(e.g. "button button-ghost" or "button button-secondary") and any necessary aria
attributes; locate the hero section where HERO and esc are used (the existing
primary anchor) and mirror that pattern to render the secondary CTA.

---

Outside diff comments:
In `@apps/apex/src/index.test.ts`:
- Around line 88-92: The test "preserves query strings on product redirects"
fails to assert that no cookies are set; update this test (the it block with
description "preserves query strings on product redirects") to include an
assertion that response.headers.get("set-cookie") is null (e.g.,
expect(response.headers.get("set-cookie")).toBeNull()) immediately after
obtaining response so it matches the `/dashboard` redirect test pattern and
enforces the no-cookie requirement.
- Around line 139-145: The test "never sets cookies on any apex response" only
checks a few paths; update the paths array used in the test (the one iterated
with for (const path of paths) and the get(path) call) to include representative
examples of all response types mentioned in the suite: public pages ("/",
"/llms.txt", "/agents.md", "/robots.txt", "/sitemap.xml", "/dashboard"),
redirect endpoints (e.g. "/login", "/artifacts/some-id", "/r/some-short"), a 404
path (e.g. "/non-existent-path"), and an asset/405 example if feasible; ensure
each response returned by get(path) is asserted with
expect(response.headers.get("set-cookie")).toBeNull() so every response type
covered by ADR 0014 is validated.
- Line 40: The test uses a fragile regex `/["\s,]inter["\s,]/` to assert "inter"
isn't present; replace it with a word-boundary, case-insensitive pattern such as
`/\binter\b/i` so matches cover edge cases like font-family:inter or "inter" at
string boundaries—update the expect assertion that currently calls
expect(body).not.toMatch(/["\s,]inter["\s,]/) to use the new `/\binter\b/i`
pattern.
🪄 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: 90388072-3f24-4cf4-9fed-b9e2c2f3add9

📥 Commits

Reviewing files that changed from the base of the PR and between 0bfe6fb and f3e16be.

📒 Files selected for processing (6)
  • apps/apex/src/agents.ts
  • apps/apex/src/copy.ts
  • apps/apex/src/index.test.ts
  • apps/apex/src/llms.ts
  • apps/apex/src/page.ts
  • apps/apex/src/styles.ts

Comment thread apps/apex/src/page.ts
Comment on lines +36 to +43
<section class="hero">
<h1 class="hero-headline">${esc(HERO.headline)}<span class="hero-headline-stop">.</span></h1>
<p class="hero-lead">${esc(HERO.lead)}</p>

<pre class="transcript" aria-label="Example agent-paste session">${renderTranscript(TRANSCRIPT)}</pre>

<a class="button button-primary button-lg" href="${esc(HERO.primary.href)}">${esc(HERO.primary.label)}</a>
</section>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restore the required secondary “View on GitHub” hero CTA.

The hero currently renders only the primary action, but the stated PR objective requires both the primary CTA and a ghost “View on GitHub” link.

🤖 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 `@apps/apex/src/page.ts` around lines 36 - 43, The hero is only rendering the
primary CTA; restore the secondary "View on GitHub" CTA by adding a second
anchor after the existing primary anchor that uses HERO.secondary.href and
HERO.secondary.label (escaped with esc like the primary), e.g. an anchor with
the ghost/secondary classes (e.g. "button button-ghost" or "button
button-secondary") and any necessary aria attributes; locate the hero section
where HERO and esc are used (the existing primary anchor) and mirror that
pattern to render the secondary CTA.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/apex/src/index.test.ts (1)

34-35: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Test expectation contradicts the required “View on GitHub” CTA.

Line 34 and Line 35 assert the homepage must not include GitHub/text, which conflicts with the PR objective requiring a ghost “View on GitHub” link. This will enforce the wrong behavior.

Suggested test fix
-    expect(body).not.toContain("github.com");
-    expect(body).not.toContain("View on GitHub");
+    expect(body).toContain("github.com");
+    expect(body).toContain("View on GitHub");
🤖 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 `@apps/apex/src/index.test.ts` around lines 34 - 35, The two assertions using
the variable body (expect(body).not.toContain("github.com") and
expect(body).not.toContain("View on GitHub")) contradict the PR requirement to
include a ghost "View on GitHub" CTA; update the test in index.test.ts to assert
the presence of that CTA instead (e.g., change to expect(body).toContain("View
on GitHub") and/or expect(body).toContain("github.com") so the test verifies the
link is rendered), and if the CTA uses a specific CSS marker or href pattern,
assert that marker/href appears in body to make the check robust.
🤖 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.

Outside diff comments:
In `@apps/apex/src/index.test.ts`:
- Around line 34-35: The two assertions using the variable body
(expect(body).not.toContain("github.com") and expect(body).not.toContain("View
on GitHub")) contradict the PR requirement to include a ghost "View on GitHub"
CTA; update the test in index.test.ts to assert the presence of that CTA instead
(e.g., change to expect(body).toContain("View on GitHub") and/or
expect(body).toContain("github.com") so the test verifies the link is rendered),
and if the CTA uses a specific CSS marker or href pattern, assert that
marker/href appears in body to make the check robust.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e9921a60-2d24-4d2e-9f27-ecf4f03d6d96

📥 Commits

Reviewing files that changed from the base of the PR and between f3e16be and 6491d79.

📒 Files selected for processing (3)
  • apps/apex/src/index.test.ts
  • apps/apex/src/routes.ts
  • scripts/smoke-hosted.mjs

isuttell and others added 2 commits May 21, 2026 12:54
Wraps `lefthook install` so it does not fail CI when the platform
binary is absent. Also widens supportedArchitectures so pnpm keeps
cross-platform optional deps in the lockfile.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Neon project only has `production` and `preview` branches; there is
no `main`. PR preview should diverge from `preview`, which is the
right staging baseline.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 `@scripts/install-hooks.mjs`:
- Around line 10-12: The warning message inside the failure branch of the
lefthook install check (the if (result.status !== 0) block) is misleading;
update the console.warn in scripts/install-hooks.mjs to clearly state that
installation failed and hooks will not be installed or modified (do not imply
any uninstall occurred). Locate the console.warn call that currently logs
"[prepare] lefthook install failed; skipping (hooks will be uninstalled)." and
replace its message with a concise clarification such as indicating installation
failed and existing hooks remain unchanged.
🪄 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: 91c03ed4-b191-4ee7-8e09-717bda84a828

📥 Commits

Reviewing files that changed from the base of the PR and between 6491d79 and 273bf4a.

📒 Files selected for processing (3)
  • package.json
  • pnpm-workspace.yaml
  • scripts/install-hooks.mjs

Comment thread scripts/install-hooks.mjs
Comment on lines +10 to +12
if (result.status !== 0) {
console.warn("[prepare] lefthook install failed; skipping (hooks will be uninstalled).");
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the warning message.

The message "hooks will be uninstalled" is misleading—hooks are simply not being installed, not being actively uninstalled. This could confuse developers into thinking previously installed hooks are being removed.

Suggested fix
 if (result.status !== 0) {
-  console.warn("[prepare] lefthook install failed; skipping (hooks will be uninstalled).");
+  console.warn("[prepare] lefthook install failed; continuing without git hooks.");
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (result.status !== 0) {
console.warn("[prepare] lefthook install failed; skipping (hooks will be uninstalled).");
}
if (result.status !== 0) {
console.warn("[prepare] lefthook install failed; continuing without git hooks.");
}
🤖 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 `@scripts/install-hooks.mjs` around lines 10 - 12, The warning message inside
the failure branch of the lefthook install check (the if (result.status !== 0)
block) is misleading; update the console.warn in scripts/install-hooks.mjs to
clearly state that installation failed and hooks will not be installed or
modified (do not imply any uninstall occurred). Locate the console.warn call
that currently logs "[prepare] lefthook install failed; skipping (hooks will be
uninstalled)." and replace its message with a concise clarification such as
indicating installation failed and existing hooks remain unchanged.

isuttell and others added 4 commits May 21, 2026 12:59
The Neon primary branch was renamed to `main`, matching the workflow's
original intent.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
pnpm's isolated nodeLinker does not place wrangler on PATH, so direct
spawn fails with ENOENT in CI. Routing through `pnpm exec` resolves the
binary from the workspace store. Also adds apex to cleanup-pr-preview
since the worker now exists.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The lockfile didn't track @cloudflare/workerd-* or lefthook-* platform
optional deps, so Linux CI runners couldn't resolve workerd-linux-64 when
wrangler loaded miniflare. Declare the full matrix as root
optionalDependencies and drop `current` from supportedArchitectures so
pnpm includes every variant in the lockfile.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@isuttell isuttell merged commit 4bde837 into main May 21, 2026
4 checks passed
@github-actions

Copy link
Copy Markdown

agent-paste PR preview resources were cleaned up.

@isuttell isuttell deleted the t3code/7bcd4587 branch May 22, 2026 01:02
isuttell added a commit that referenced this pull request May 22, 2026
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.
isuttell added a commit that referenced this pull request May 22, 2026
…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>
isuttell added a commit that referenced this pull request May 22, 2026
* chore(branches): close obsolete t3code branches

Backlog item #7 in docs/ops/project-status.md asked to review and merge
t3code/7bcd4587 and t3code/5b6355f9. Both branches no longer exist on
origin (verified via git fetch and gh api repos/:owner/:repo/branches);
the underlying commits are unreachable. The only Apex/front-end work
that ever landed from the t3code family was PR #1 (the marketing
worker scaffold). Remove the stale references from project-status.md
and move the item to Recently Completed.

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

* docs: clarify former backlog item #7 reference

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
isuttell added a commit that referenced this pull request May 24, 2026
Snapshot now points at ad85175 (#46); record the operator lockdown (#45) and
web loader-wiring (#46) merges in Recently Completed; strike backlog #4 as done;
update the Phase 3 (~55%), web.md, ADR 0055 rows. Remaining Phase 3 code work is
CLI login (#5) and smoke:web (#6), both gated on the WorkOS/Access click-ops in
backlog item #1.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
isuttell added a commit that referenced this pull request May 25, 2026
…loy (#50)

Record that agent-paste-web-preview is live at app.preview.agent-paste.sh
with a verified WorkOS login flow, the GET /v1/web/admin/lockdowns list
endpoint (#48) shipped, and the CLOUDFLARE_ENV build/deploy mechanism
(#49). Update the snapshot main pointer, backlog items #1 and #6, the
web.md / ADR 0033 / 0040 / 0059 rows, and the lockdown deferred-follow-up
note.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
isuttell added a commit that referenced this pull request Jun 14, 2026
…st-login provisioning, /v tests

Cursor Bugbot (blocking, Medium) + first-pass review findings:

- **Revision-link retry duplicates (Bugbot).** createAndMintAccessLink's salted-key
  retry ran for both make_public and create_revision_link. Share links dedupe on
  create (findActiveShareLink), so the retry is idempotent; revision links do NOT
  dedupe, so a create-ok/mint-fail retry inserted a second revision link for the
  same revision. Scope the retry to `type:'share'` only; revision links return the
  original failure. Test asserts create_revision_link does not retry.

- **/v first-login provisioning (finding #1).** /v is a standalone handoff route,
  not under _authed, so it never provisioned the workspace member. A brand-new user
  signing in directly via a private link had a valid token but no member row, so the
  owner-scoped artifact read missed and showed the empty state. Provision via
  webSessionQuery in the loader before the artifact read.

- **/v route tests (finding #4).** New apps/web/test/v-route.test.tsx pins the gate:
  unauthed → redirect + no artifact read; authed → provision-then-read in order;
  chromeless viewer renders; redirect path works.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
isuttell added a commit that referenced this pull request Jun 14, 2026
…ic, unified scopes (#525)

* feat(publish): make publish content-only and private-first; one private_url, explicit make_public

Publish and add_revision are now content-only on every surface (CLI, MCP, REST).
They take no visibility input and return exactly one link, `private_url`, the
login-walled clean viewer at `/v/<artifactId>` for a Workspace Member. There is
no `share` input and no `shared` output anywhere; the misleading viewer_url/
shared flip is gone. Going public is a separate, explicit, revocable step:
`make_public` (MCP) / `agent-paste make-public` (CLI), with `revoke_access_link`
as the go-private verb. The `/artifacts/<id>` console is never returned to an
agent or user.

Private Link is a plain `_authed` route, not an access_links row: nothing to
sign, nothing to leak, nothing to revoke. The public `/al/<publicId>` resolve
gate is untouched, so existing Share Links keep resolving. A new shared
ArtifactLiveViewer backs both the console and the clean `/v` viewer.

make_public now reuses an Artifact's one active (non-revoked, unexpired) Share
Link instead of minting a duplicate, so an Artifact has at most one live Share
Link — making the "mints or reuses the one Share Link" contract true in code,
not just in copy. revision links are never deduped.

Unify the scope vocabulary: one set `read`/`publish`/`admin` shared verbatim by
API and MCP. The old MCP-only `write`/`share` names are gone, and the
mcpScopesToApiScopes/apiScopesToMcpScopes translation layer is deleted (MCP
scopes are the member's stored API scopes verbatim, per ADR 0079). The four
agent access-link routes require `publish` (managing your own Artifact's public
access is content authority); `admin` is dashboard-only and no MCP tool needs
it. web.accessLinks.lockdown.* stay `admin`.

Supersedes ADR 0085 (publish-returns-one-viewer-url, which made the link flip
and surfaced the lie); amends ADR 0084 (output shape) and ADR 0079 (scope
vocabulary unified). Adds ADR 0086. Specs are updated as the source of truth
(api/web/cli/ephemeral-publish, CONTEXT vocab, docs/mcp.md, apex agent docs).
OpenAPI goldens regenerated.

Early-alpha break: CLI and MCP ship in lockstep; the deployed MCP server
`instructions` text still teaches `share:true` and updates on deploy.

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

* fix(web): clean viewer header links to /dashboard, not the banned /artifacts console

The /v clean viewer's own header Wordmark linked to /artifacts/$artifactId — the
dashboard console page that is explicitly never to be handed to a user. Point it
at /dashboard (brand home) instead. The console stays reachable only from the
dashboard's own artifact list.

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

* fix(mcp): make_public self-heals when a replayed create points mint at a revoked link

If a client reuses the same idempotency key across a revoke (make_public →
revoke_access_link → make_public with the same key), the create step replays the
now-revoked link from its idempotency record and mint fails on the dead link.
Real MCP clients use monotonic JSON-RPC ids and the CLI uses random UUIDs so
neither collides, but the failure mode is sharp: a revoke must never lock you out
of going public again.

createAndMintAccessLink now retries once on a salted idempotency key when the
first create→mint fails. The salted create runs the command fresh, so for a share
link it reuses the artifact's active link (the DB-layer findActiveShareLink path)
or mints a new one. The happy path never retries.

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

* docs: state the Private Link permanence guarantee explicitly

We kept tripping over what the link guarantees, so write it down where the specs
are the source of truth. The Private Link (`/v/<artifactId>`) is permanent and
stable: derived only from the Artifact id with no token/signature/expiry, and
add_revision republishes into the same id, so it never changes across revisions
and live-updates to the latest Published Revision. It is always private (member
only; publish never grants public access) and stops resolving only when the
Artifact is deleted or swept by Auto Deletion — the expires_at in the publish
response is the Artifact's content lifetime, not a link expiry. Going public is
the separate, revocable Share Link.

Documented in CONTEXT.md (Private Link + Share Link vocab), docs/specs/api.md
(publish behavior), and ADR 0086 (decision trail).

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

* fix(mcp,web): address Cursor review — revision-link retry dup, /v first-login provisioning, /v tests

Cursor Bugbot (blocking, Medium) + first-pass review findings:

- **Revision-link retry duplicates (Bugbot).** createAndMintAccessLink's salted-key
  retry ran for both make_public and create_revision_link. Share links dedupe on
  create (findActiveShareLink), so the retry is idempotent; revision links do NOT
  dedupe, so a create-ok/mint-fail retry inserted a second revision link for the
  same revision. Scope the retry to `type:'share'` only; revision links return the
  original failure. Test asserts create_revision_link does not retry.

- **/v first-login provisioning (finding #1).** /v is a standalone handoff route,
  not under _authed, so it never provisioned the workspace member. A brand-new user
  signing in directly via a private link had a valid token but no member row, so the
  owner-scoped artifact read missed and showed the empty state. Provision via
  webSessionQuery in the loader before the artifact read.

- **/v route tests (finding #4).** New apps/web/test/v-route.test.tsx pins the gate:
  unauthed → redirect + no artifact read; authed → provision-then-read in order;
  chromeless viewer renders; redirect path works.

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

---------

Co-authored-by: Claude Opus 4.8 <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