Skip to content

Unblock the docs sync and backfill the mirror-only pages - #3627

Merged
kojiwakayama merged 7 commits into
mainfrom
fix/unblock-docs-sync-validator
Aug 12, 2026
Merged

kojiwakayama merged 7 commits into
mainfrom
fix/unblock-docs-sync-validator

Conversation

@kojiwakayama

@kojiwakayama kojiwakayama commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Why

The veryfront-docs Update Code Docs workflow has failed on every run since 2026-05-27. It fails at Validate synced code docs quality with 13 issues, so no documentation change made in this repo has reached veryfront.com in ~2.5 months. Latest failing run: 31564458030.

The validator runs against veryfront-docs' docs/code/** after the sync overwrites that tree from this repo's docs/. The failures are ours, so the fix has to land here. Fixing veryfront-docs alone unblocks nothing.

1. The 13 validator failures

Each fix was verified against the source, not assumed:

Fix Source of truth
veryfront dev prints ✓ Ready in <duration> then the URL on its own line, not Ready on <url> cli/commands/dev/command.ts:329
MCP address is two ports above the bound port, path /mcp; the dev server lists it only under --verbose cli/commands/dev/command.ts:240-243
Deploy prints Protected · Release <version> cli/commands/deploy/command.ts:171
An anonymous request gets a 302 to https://<apex>/sign-in?from=... src/proxy/proxy-access-control.ts:218
ASCII punctuation in deploying.md validator rule

Deploy pages now state that Cloud environments are protected by default, that an unauthenticated request is redirected to sign-in, that VERYFRONT_API_TOKEN does not open a protected environment, and they name the Public Environment Studio switch.

I also replaced the curl -sSf <environment-url> verification on the deploy page. Against a protected environment that only ever proved the sign-in redirect, so it never verified what it claimed to.

2. The backfill (this is the part that would have destroyed shipped work)

While the sync was broken, 20 docs PRs were merged directly into veryfront-docs to get fixes onto the live site. Those edits exist only in the mirror. The first green sync run would have silently reverted every one of them.

Rather than trust the PR list, I replayed the sync locally (copy docs/ into a checkout of veryfront-docs origin/main, apply the workflow's link-rewrite transform) and diffed the result against the live mirror. That isolates mirror-only content exactly. Note the real drift starts at #357, not #366 — the last successful content sync was c10037f (2026-08-02).

Backfilled:

  • installation — blank-install wiring steps (module type, CLI scripts, base tsconfig, first page). Verified the package really ships tsconfig.json and react/react-dom as dependencies: scripts/build/build-npm-dnt.ts:313,337.
  • project-structure — .cache/ and dist/ generated-directory reference, the self-ignoring .cache/.gitignore, and VERYFRONT_CACHE_DIR (src/utils/cache-dir.ts:154).
  • providers — absolute GitHub URL for the ext-llm-openai reference. The relative path escaped the docs tree and 404s once published.
  • deploy-project / deploying / quickstart — the environment-access content described above.

Deliberately not backfilled, because this repo is genuinely ahead and the sync should overwrite the mirror: open --site (#3605), install --target (#3558), npx veryfront@latest, --output/outDir (#3565), tasks//schedules//webhooks//evals/ discovery, and the skills-authorization and allowed-tools semantics in agents.md. I checked each against the CLI source rather than assuming the newer commit date wins.

3. Why it broke silently, and why it will not again

scripts/docs/validate-public-docs.ts already had the em-dash rule — and the em dash still reached main. It only runs under deno task docs:validate, which CI never invokes; CI's lint shard runs lint:ci. The accuracy contract was enforced only downstream, in another repo, on a repository_dispatch run nobody watches. sync-docs.yml fires on every docs push regardless of validity.

So this PR mirrors the deploy-access, dev-server-URL, and MCP-endpoint coverage checks into the local validator and wires it into lint:ci as docs:public:check. A PR that breaks the sync now fails here, in the repo that caused it.

No validator rule was weakened, skipped, or removed. The change is purely additive.

Verification

I reproduced the pipeline rather than eyeballing the docs.

  • Replayed the real workflow locally (sync + link-rewrite + sync-code-navigation.mjs) and ran veryfront-docs' unmodified check-code-docs-quality.mjs against the result:
    • before: 13 code docs quality issue(s) found — byte-identical to the CI failure
    • after: Validated code docs quality across 113 file(s).
    • check-legacy-identity-docs.mjs also passes.
  • Wrote 9 negative tests that break each newly added rule one at a time on a throwaway copy. All 9 fire; baseline stays clean. The rules are enforcing, not decorative.
  • deno task lint:ci — passes, with docs:public:check running inside it.
  • deno task docs:validate — passes, 1238 doc links OK.
  • deno task fmt:check, docs:api-reference:check, docs:errors:check — pass.

The gate has already earned its keep

While this PR was open, #3616 landed overlapping deploy-access work on deploy-project.md — carrying two em dashes. Merging main here, the new docs:public:check failed immediately:

docs/getting-started/deploy-project.md:153: Use ASCII punctuation in public docs.
docs/getting-started/deploy-project.md:154: Use ASCII punctuation in public docs.

Without this gate that content would have reached main and broken the docs sync again, on the same rule, days after it was repaired. Fixed in the merge commit.

On the merge itself I dropped my ## Environment access section in favour of #3616's. Theirs is better researched (it covers the 403 non-member case and the fact that the sign-in apex varies by host) and is pinned by the regression test that PR added. tests/docs/guide-content.test.ts passes, all 33 contracts. The #environment-access anchor my other pages link to still resolves, since both versions use the same heading.

Companion PR

None needed. Once this merges, the sync's next run regenerates docs/code/** from this branch's content and reconciles the mirror on its own. A veryfront-docs PR would only duplicate what the pipeline is now able to do.

Summary by CodeRabbit

  • Documentation

    • Clarified development-server URLs, localhost behavior, alternate ports, and MCP endpoint configuration.
    • Expanded installation guidance for ES modules, CLI scripts, TypeScript, and starter pages.
    • Documented protected and public deployment environments, authentication, and verification steps.
    • Added guidance for generated directories, cache configuration, and cache regeneration.
    • Updated links and improved project setup and coding-agent instructions.
  • Chores

    • Added automated validation to ensure public documentation includes required setup, deployment, and development-server guidance.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kojiwakayama, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 4 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 71dd65e3-5149-4ecc-bbd5-fb18e5185ba6

📥 Commits

Reviewing files that changed from the base of the PR and between 87c73af and bb36f3b.

📒 Files selected for processing (5)
  • docs/getting-started/deploy-project.md
  • docs/guides/coding-agents.md
  • docs/guides/deploying.md
  • docs/guides/project-structure.md
  • tests/docs/guide-content.test.ts
📝 Walkthrough

Walkthrough

The pull request adds public-documentation validation to CI and updates documentation for development URLs, MCP ports, deployment access, installation, project structure, generated directories, and external links.

Changes

Public documentation validation

Layer / File(s) Summary
Documentation validation and CI wiring
scripts/docs/validate-public-docs.ts, deno.json
The validator checks wrapped wording and required development URL and deployment-access coverage. CI runs it through docs:public:check.
Local development and MCP guidance
docs/getting-started/*.md, docs/guides/coding-agents.md
The documentation describes printed local URLs, localhost resolution, MCP endpoint paths, port offsets, fallback ports, and verbose logging.
Deployment access and verification
docs/getting-started/deploy-project.md, docs/getting-started/quickstart.md, docs/guides/deploying.md
The documentation describes protected and public environments, authentication behavior, output-path validation, and production verification.
Project setup and generated directories
docs/guides/project-structure.md, docs/guides/providers.md
The documentation updates prerequisites, cache behavior, generated directories, cache overrides, regeneration, and the hosted web-search link.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CI
  participant DenoTasks
  participant PublicDocsValidator
  participant PublicDocs
  CI->>DenoTasks: run lint:ci
  DenoTasks->>PublicDocsValidator: run docs:public:check
  PublicDocsValidator->>PublicDocs: validate required wording and coverage
  PublicDocsValidator-->>CI: report documentation issues
Loading

Possibly related PRs

Suggested reviewers: kwakayama, ariskemper

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main objectives: unblocking documentation synchronization and backfilling mirror-only pages.
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.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/unblock-docs-sync-validator

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

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 820a74ea41

ℹ️ 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".

Comment thread docs/getting-started/installation.md Outdated
Comment thread scripts/docs/validate-public-docs.ts Outdated

@kwakayama kwakayama left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Findings

  • [P2] Correct the protected-environment access contract in docs/getting-started/deploy-project.md:91-108 and docs/guides/deploying.md:134-145. They state that only a signed-in browser can access a protected environment and that every other request gets a 302 sign-in redirect. The deployment client supports authenticated non-browser probes via an authToken cookie (cli/shared/deployment/deploy-project.ts:850-905) and explicitly treats a sign-in redirect, 401, and 403 as valid protection challenges (cli/shared/deployment/deploy-project.ts:927-994). The existing test confirms a programmatic protected-environment request authenticates with the stored session token (cli/shared/deployment/deploy-project.test.ts:653-669). Document these as possible authentication responses and distinguish an API key from a user session cookie.

Score breakdown: correctness 34/40, tests 17/20, reliability/security 13/15, maintainability 14/15, scope/docs 6/10.

Review-Gate:
Reviewer: Codex
Reviewed-SHA: 629746b
Score: 84/100
Actionable-Findings: 1
Verdict: COMMENT

@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 12, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 12, 2026
The veryfront-docs "Update Code Docs" workflow has failed on every run since
2026-05-27. It fails at "Validate synced code docs quality" with 13 issues, so
no documentation change made here has reached veryfront.com in ~2.5 months.

That validator runs on veryfront-docs' docs/code/** *after* the sync overwrites
that tree from this repo's docs/, so the failures are ours. Fix the source:

- create-project, quickstart: show the URL `veryfront dev` actually prints
  (`✓ Ready in <duration>` then `http://veryfront.me:3000` on its own line),
  and drop the `Ready on http://localhost:3000` line the CLI never prints
  (cli/commands/dev/command.ts:329).
- installation, coding-agents: `veryfront dev` only lists the MCP address under
  --verbose, so state the address instead: dev `--port` + 2 (default 3002), path
  `/mcp` (DEV_MCP_PORT_OFFSET, cli/commands/dev/command.ts:242).
- deploying: replace an em dash with ASCII punctuation.
- deploy-project, deploying, quickstart: state that Cloud environments are
  protected by default, that an unauthenticated request is redirected to
  sign-in, that VERYFRONT_API_TOKEN does not open a protected environment, and
  name the "Public Environment" Studio switch. Verified against
  cli/commands/deploy/command.ts:171 ("Protected · Release <version>") and
  src/proxy/proxy-access-control.ts:218 (the /sign-in?from=... redirect).

Backfill the work that exists only in the mirror. While the sync was broken, 20
docs PRs were merged directly into veryfront-docs to get fixes onto the live
site. A repaired pipeline would silently revert them on its first green run.
Diffing a local replay of the sync against veryfront-docs origin/main isolates
the content that exists only downstream:

- installation: the blank-install wiring steps (module type, CLI scripts,
  base tsconfig, first page). The published package ships tsconfig.json and
  react/react-dom as dependencies (scripts/build/build-npm-dnt.ts:313,337).
- project-structure: the `.cache/` and `dist/` generated-directory reference,
  including the self-ignoring `.cache/.gitignore` and VERYFRONT_CACHE_DIR
  (src/utils/cache-dir.ts:154).
- providers: an absolute GitHub URL for the ext-llm-openai reference; the
  relative path escaped the docs tree and 404s once published.
- deploy-project: replace the curl-based verification, which only ever proved
  the sign-in redirect, with a check that works against a protected
  environment.

The rest of the drift is this repo legitimately ahead of the mirror
(`open --site`, `install --target`, `npx veryfront@latest`, `--output`,
tasks/schedules/webhooks/evals discovery, skills authorization), so it is left
to overwrite the mirror as intended.

Close the hole that let this happen. scripts/docs/validate-public-docs.ts
already had the em-dash rule, but it only runs under `deno task docs:validate`,
which CI never invokes -- CI's lint shard runs `lint:ci`. The accuracy contract
was enforced only downstream, in another repo, on a repository_dispatch run
nobody watches. Mirror the deploy-access, dev-server-URL, and MCP-endpoint
coverage checks into the local validator and add it to `lint:ci` as
`docs:public:check`, so a PR that breaks the sync fails here instead.
The dev server computes MCP as `started.port + 2`, where `started.port` is the
port `startDevServerOnFreePort` actually bound after scanning forward
(cli/commands/dev/command.ts:240-243). Documenting the offset against the
requested `--port` sends a reader to the wrong endpoint whenever the requested
port is taken: `--port 4000` that falls forward to 4001 serves MCP on 4003, not
4002.

State the rule against the bound port on all three pages that carry it, and
tell readers to take the port from the URL the dev server printed. Update the
validator message to match.

Also record why the wrapped MCP pattern stays byte-identical to the rule in
veryfront-docs' check-code-docs-quality.mjs. The pattern is broad enough to
reject an accurate sentence about `--verbose` output, but this validator exists
to predict the downstream one; narrowing it here alone would let a page pass
locally and still break the sync, which is the failure this check was added to
prevent. Changing it means changing both repositories together.
The validator this PR adds found three em dashes that reached main while no
authoring-time check existed: two from #3606 in project-structure.md and one
from #3616 in deploy-project.md. Rewritten as sentences and parentheses with
the meaning unchanged.

The rebase also took main's 'Verify it worked' section rather than this
branch's older copy, so #3616's reviewed route-probing guidance survives.
@kojiwakayama
kojiwakayama force-pushed the fix/unblock-docs-sync-validator branch from ba9db71 to 87c73af Compare August 12, 2026 07:24

@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: 4

🧹 Nitpick comments (1)
scripts/docs/validate-public-docs.ts (1)

244-306: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add focused tests for public-document validation.

The new WRAPPED_RULES and collectCoverageIssues paths have no direct regression tests. Cover wrapped MCP text, missing deploy requirements, and missing http://veryfront.me:3000 URLs.

🤖 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/docs/validate-public-docs.ts` around lines 244 - 306, Add focused
regression tests for the public-document validation logic, covering wrapped MCP
text detected by WRAPPED_RULES, missing deploy requirements reported by
collectCoverageIssues, and pages missing the http://veryfront.me:3000
development URL. Use controlled fixture content and assert the resulting issue
paths, messages, and line/text details.

Source: Coding guidelines

🤖 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 `@docs/getting-started/create-project.md`:
- Around line 106-109: Specify the text language on all three CLI output fences
by changing their opening fences to text: docs/getting-started/create-project.md
lines 106-109 and 126-131, and docs/getting-started/quickstart.md lines 82-85.

In `@docs/guides/coding-agents.md`:
- Line 199: Update the MCP port instructions at the earlier references around
lines 108-109 and 212 to derive the endpoint from the actual port in the dev
server’s printed URL, adding two to that bound port. Remove wording that
calculates from the requested --port while preserving the existing MCP URL
format.

In `@docs/guides/project-structure.md`:
- Line 188: Update the cache-location wording in the project structure guide to
state directly that development writes .cache/ in the project root and
production does not. Use concise, active, present-tense language and remove the
ambiguous phrase.
- Around line 236-264: Remove the duplicate “Generated directories” section
beginning at the later heading, including its directory table and cache
guidance. Preserve the earlier “Generated directories” section and its complete
production, cache-root, and environment-variable documentation.

---

Nitpick comments:
In `@scripts/docs/validate-public-docs.ts`:
- Around line 244-306: Add focused regression tests for the public-document
validation logic, covering wrapped MCP text detected by WRAPPED_RULES, missing
deploy requirements reported by collectCoverageIssues, and pages missing the
http://veryfront.me:3000 development URL. Use controlled fixture content and
assert the resulting issue paths, messages, and line/text details.
🪄 Autofix

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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7335d58b-84dc-4034-9699-2adfaf12327c

📥 Commits

Reviewing files that changed from the base of the PR and between 2b3a0e0 and 87c73af.

📒 Files selected for processing (10)
  • deno.json
  • docs/getting-started/create-project.md
  • docs/getting-started/deploy-project.md
  • docs/getting-started/installation.md
  • docs/getting-started/quickstart.md
  • docs/guides/coding-agents.md
  • docs/guides/deploying.md
  • docs/guides/project-structure.md
  • docs/guides/providers.md
  • scripts/docs/validate-public-docs.ts

Comment thread docs/getting-started/create-project.md
Comment thread docs/guides/coding-agents.md
Comment thread docs/guides/project-structure.md Outdated
Comment thread docs/guides/project-structure.md Outdated
The backfill replayed a "Generated directories" section from the docs mirror
into project-structure.md, but this repo already had a newer one: #3606 landed
the production cache root and VF_CACHE_DIR here after the mirror copy was
written. The result was two H2 sections with the same heading giving different
answers, the later one claiming the CLI writes .cache/ into the project root
with no mention of production. Drop the stale copy; the retained section is a
strict superset of it.

Also rewords the summary line above that section: "`.cache/` is too during
development" elided its verb across a sentence boundary.

coding-agents.md still stated the MCP port as `--port + 2` in two places. The
earlier fix only corrected the verification section, and one of the two misses
was the "Port already in use" heading, which is precisely the case where the
requested port and the bound port differ. mcpPort is computed from
started.port (cli/commands/dev/command.ts:240-243), so a --port 4000 that falls
forward to 4001 serves MCP on 4003.
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

CI: three red checks, one cause, and it is not this PR

The three failures collapse to a single shard flake that this branch cannot reach.

coverage gate never ran a test. Its whole failing step is:

COVERAGE_SHARDS_RESULT: failure
##[error]Coverage shards finished with failure

tests (unit) is the "Require all unit test shards" aggregator. Both are reporting the same upstream result, so there is one real failure to look at: coverage shard 4/8.

That shard passed every test it ran and then died on the way out:

ok | 376 passed (3341 steps) | 0 failed (57s)

error: Promise resolution is still pending but the event loop has already resolved
error: Uncaught (in promise) Error: deno test ... exited with 1

0 failed followed by exit 1 is a runtime teardown leak, not an assertion. The shard also produced no lcov.info, which is why the gate had nothing to consume (No files were found with the provided path: coverage-shard-4/lcov.info).

Why it is not this branch

This PR touches 10 files: 8 markdown pages, deno.json, and the new scripts/docs/validate-public-docs.ts. No source file, no test file, nothing on any runtime path a shard executes.

Shard 4's file list includes src/transforms/esm/http-cache.test.ts, and #3631 ("fix(test): drive the shared-fetch follower's bounded wait to completion") changes exactly one file: src/transforms/esm/http-cache.test.ts. A shared-fetch follower left parked on a bounded wait at process exit is precisely the "Promise resolution is still pending but the event loop has already resolved" signature. #3631 is in the merge queue.

So: pre-existing flake, already fixed, fix already queued. I have not touched that test.

Status

The review-thread fixes in 4acf390 triggered a fresh run, which supersedes the failed jobs. If shard 4 flakes again before #3631 lands, it is the same leak and wants a re-run rather than a change here.

The full pre-push suite (fmt check + tests) passed locally on 4acf390 before this was pushed.

@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 12, 2026
@kwakayama
kwakayama removed this pull request from the merge queue due to a manual request Aug 12, 2026

@kwakayama kwakayama left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Findings

  1. [Medium] docs/getting-started/installation.md:101 makes the new “Node.js, Deno, or Bun” manual-install path runnable only with npm. A Deno-only or Bun-only user can follow the shown deno add npm:veryfront / bun add veryfront setup, then immediately fail because npm need not be installed. The supported Deno scaffold instead writes deno run -A npm:veryfront@<version> dev (cli/commands/init/deno-config-generator.ts:8-14). Provide runtime-specific run commands (and the required Deno task/config).

  2. [Medium] docs/guides/coding-agents.md:108-109 says the HTTP MCP server listens “two ports above” the bound dev port, while :199 diagnoses connection refusal only as a stopped/non-default dev server. This is not guaranteed: cli/commands/dev/command.ts:242-248 deliberately continues when MCP startup fails, and cli/mcp/server.ts:224-235 disables MCP when that port cannot bind. A local process on boundPort + 2 therefore leaves the documented endpoint refused despite a healthy dev server. Document the collision case/actual availability, or implement and report an MCP fallback port.

Rubric Score
Correctness 31/40
Tests 16/20
Reliability/security 12/15
Maintainability 14/15
Scope/docs 7/10
Total 80/100

Review-Gate:
Reviewer: Codex
Reviewed-SHA: c7b48f5
Score: 80/100
Actionable-Findings: 2
Verdict: REQUEST_CHANGES

The deploy pages said a protected environment serves only a signed-in browser
and that every other request sees the sign-in redirect. The second half is
wrong. checkProtectedProxyAccess reads the session out of an `authToken` cookie
(extractUserToken, src/proxy/proxy-token-resolution.ts:52) and never inspects
the client, so any HTTP client that sends that cookie is served. `veryfront
deploy` relies on it: its readiness probe sets `Cookie: authToken=<token>` when
the stored credential is a session token (deploy-project.ts:969), and withholds
an opaque API key because extractUserIdFromToken resolves no userId from one.

The genuinely true part is kept and narrowed to what the code does: an API key
does not open deployment traffic, because the gate has no API-key branch and
answers such a request with the same 302 an anonymous one gets. Also records
that Deploy treats a sign-in redirect, a 401, and a 403 alike as the protection
challenge (deploy-project.ts:989), which is what lets a keyless probe pass.

Pins the corrected contract in tests/docs/guide-content.test.ts; the new case
fails against both pages as they read before this change.
@kojiwakayama

Copy link
Copy Markdown
Contributor Author

Re: the P2 on the protected-environment access contract (review). Fixed in 0d5fbdf.

What the source actually says

I read the gate and the client before writing, and the axis is the credential, not the client:

  • checkProtectedProxyAccess (src/proxy/proxy-access-control.ts:229-303) takes userToken and nothing about the caller. That token comes from extractUserToken (src/proxy/proxy-token-resolution.ts:52-74), which reads one cookie named authToken. Nothing inspects a User-Agent, an Accept header, or anything else browser-shaped, so any HTTP client that sets that cookie takes the same path a browser does.
  • The gate's three outcomes: no token at all -> 302 to the sign-in apex for the request host (:268-276); a token that resolves to no userId -> the same 302 (:284-292); a verified userId that is not in users -> 403 (:293-300). extractUserIdFromToken (:104-168) only decodes and verifies a JWT, so an opaque vf_ API key falls into the second case. That is why the "an API key does not open deployment traffic" half was right, and why the reason is "the gate has no API-key branch", not "it is not a browser".
  • On the client side, isSessionCredential (cli/shared/deployment/deploy-project.ts:849-869) reads exactly what the gate reads: three JWT segments, an alg header, a non-empty userId payload claim. buildEnvironmentReadinessProbes (:871-907) then sets authenticate when that holds and acceptAuthenticationChallenge when it does not, and waitForEnvironmentReady sends Cookie: authToken=${target.apiToken} for an authenticating probe (:967-970). So a programmatic client authenticating with a stored session is not hypothetical, it is what Deploy does. deploy-project.test.ts:653-669 pins the cookie, and :672-698 pins that an API key is withheld rather than leaked and the resulting redirect accepted.
  • The challenge set is signInRedirect || 401 || 403 (:988-991), used two ways: accepted as proof of routing when the probe could not authenticate, and fatal when it could (describeAuthenticationChallenge, :927-940). I attributed the 401 to Deploy's probe rather than to the gate, because the protection gate itself only ever answers 302 or 403; the 401s in src/proxy/handler.ts are control-plane binding failures, not this gate.

What changed

docs/getting-started/deploy-project.md, the offending sentence:

  • before: "VERYFRONT_API_TOKEN does not open a protected environment. It authenticates the CLI against the Cloud API, not deployment traffic, so curl, a CI smoke test, or an uptime monitor sees the sign-in redirect either way."
  • after: "VERYFRONT_API_TOKEN carrying an API key (a vf_ key) does not open a protected environment. It authenticates the CLI against the Cloud API, not deployment traffic. The environment gate reads a user id out of a verified session token and has no API-key branch, so an API key presented to it resolves to no user and draws the same sign-in redirect an anonymous request gets. veryfront deploy acts on that distinction instead of leaking the key: it sends the stored credential to a protected environment only when that credential is a session token, and otherwise probes anonymously and accepts the challenge as proof the environment is serving. Its readiness probe counts a sign-in redirect, a 401, and a 403 alike as that challenge." Followed by a new paragraph: "A non-browser client can still authenticate. The gate inspects the cookie, not the client, so curl, a CI smoke test, or an uptime monitor reaches a protected environment by sending the authToken cookie with the session token of a project member. That token is a member's own session, so it expires and belongs in a secret store rather than a checked-in workflow file. For an unattended check with no session to spend, make the environment public instead."

Two smaller edits on the same page for coherence: the section opener now says a protected environment "serves only requests that carry a Veryfront user session for a member of the project", with the cookie named and the browser described as how a browser gets one, instead of "serves only that signed-in member"; and the Verify-it-worked remedy now offers repeating the request with a member's session in an authToken cookie alongside opening it in a member's browser or making it public. The existing 403 sentence ("A request signed in as a user who is not a member of the project gets a 403 instead") is untouched and is now the only place that owns 403, so nothing duplicates or contradicts it.

docs/guides/deploying.md:

  • before: "A protected environment serves requests only to a browser signed in to Veryfront as a member of the project. Anonymous requests, including requests to API routes and requests carrying VERYFRONT_API_TOKEN, are redirected to the Veryfront sign-in page."
  • after: "A protected environment serves a request only when it carries a Veryfront user session for a member of the project. A signed-in browser is the usual client, not the only one: the session travels in an authToken cookie, so curl or a CI smoke test reaches a protected environment by sending a project member's session token. An API key is not a session, so VERYFRONT_API_TOKEN does not open one. It authenticates the CLI against the Cloud API, not deployment traffic. A request with no session, or one carrying a credential the gate cannot verify, is redirected to the Veryfront sign-in page on every path including API routes. A session belonging to a non-member gets a 403 instead." The cross-link tail now reads "for the responses a protected environment returns" rather than "for the redirect".

No em dashes added; the new prose uses commas, colons, and separate sentences.

Tests

No test asserted the old wording, so nothing had to be relaxed. scripts/docs/validate-public-docs.ts:159-163 requires /VERYFRONT_API_TOKEN[^.]{0,120}does not open/i on the Getting Started page; the corrected sentence still satisfies it, and it is still true as narrowed. One stale rationale comment in tests/docs/guide-content.test.ts:213 ("VERYFRONT_API_TOKEN does not change that") was tightened to say an API key, since the browser-only half of it was the defect.

I added a case instead: tests/docs/guide-content.test.ts, "does not present a browser as the only client a protected environment serves". It pins that both pages name the authToken cookie and that the Getting Started page says a non-browser client can authenticate, and rejects the two exact old sentences. Red-checked by restoring both docs to c7b48f5ab and re-running the file: 1 failed step, the new one. Green on the corrected pages.

Gates

  • deno task docs:validate -> exit 0 (validate-api-reference, validate-guides, validate-public-docs, docs-coverage, guide-contracts + guide-content, guide-examples + guide-code-examples, and check-doc-links.ts: "All 1238 doc links OK", 48 passed / 95 steps / 0 failed). The one warning, getting-started/index.md: Guide "webhook" not listed in a section index, is pre-existing and unrelated.
  • deno fmt --check and deno lint clean on all three changed files.
  • Committed and pushed with Deno 2.7.7 first on PATH, matching the CI pin in .github/actions/setup-deno; pre-commit and pre-push both passed and the doc regeneration produced no line-pin churn.

Not re-queued: leaving that to @kwakayama.

@kojiwakayama
kojiwakayama added this pull request to the merge queue Aug 12, 2026
Merged via the queue into main with commit 82ae2f7 Aug 12, 2026
35 checks passed
@kojiwakayama
kojiwakayama deleted the fix/unblock-docs-sync-validator branch August 12, 2026 08:40
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.

2 participants