Skip to content

feat(website): one spec declares a site, composes it and checks any server (FD-4683) - #445

Open
williamloo-scale wants to merge 1 commit into
wloo/website-sdk-followupsfrom
wloo/website-spec
Open

williamloo-scale wants to merge 1 commit into
wloo/website-sdk-followupsfrom
wloo/website-spec

Conversation

@williamloo-scale

@williamloo-scale williamloo-scale commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #444.

A website spec: one TOML file declares a site, composes from parts, and checks any server against it. Design: docs/website-spec.md.

  • agentenv_protocol.website.spec reads [site], [source], [data], [users], [roles], [routes.ops], [state], [clock], [[uses]] and extends, with validation, overlays, composition, card params, from_card, from_environment, a JSON Schema and a CLI (validate, show, show --card).
  • A name table for a multi env: synthetic over live over the allowlist, two sites of one kind on a name refused.
  • @website(spec="website.toml"): the class is refused when its @api routes aren't the spec's, and its card is built from the spec. Notes now declares itself this way.
  • env.toml takes spec = "website.toml" or an inline [spec]; the env takes its name, health path and domains from it.
  • conformance --spec checks a server's card and routes against the spec; those checks are required.
  • Examples in examples/website/specs/: a replayed Slack overlay, an intranet, and an SSO component.

Live and replay sources, served_by and models = "mcp:..." are modelled in the spec but not deployable from env.toml yet.

Test

Unit suite: 12,793 passed (one GitHub-clone test from main, which hangs on the local machine on main too, deselected).

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 3/5

Fix the startup failures for existing route names and distinct converter routes before merging.

Fix All in CursorFindings

  1. P1 Existing route names prevent startup ▶
  2. P2 Mistyped specs crash validation ▶
  3. P2 Wrong sign-in mode passes ▶
Fix with agent prompt
### Issue 1
packages/agentenv-protocol/src/agentenv_protocol/website/spec/__init__.py:865-866
An existing site with an `@api` name such as `name="items:list"` now fails during `create_app()`, even without `spec=`. `@api` accepts that name, but card creation now passes it through `Routes._names`, which rejects the colon and raises `SpecError`.

Preserve names accepted by existing decorators when deriving their specs.

### Issue 2
packages/agentenv-protocol/src/agentenv_protocol/website/spec/__init__.py:577-580
A file containing `extends = "base.toml"` and `uses = 1` reaches this loop before `_validated()` and raises `TypeError`. An inherited `data.collections = [1]` in another folder similarly crashes `_rebased()` at `.values()`.

The CLI and bundle authoring catch only `SpecError`, so authors get a traceback instead of an error naming the bad field. Check these types before merging and rebasing, or turn these failures into `SpecError`.

### Issue 3
packages/agentenv-protocol/src/agentenv_protocol/website/conformance.py:338-340
`_spec_card()` accepts a cookie-based sign-in when the spec explicitly sets `users.sign_in = "none"`. `to_card_extension()` omits the session fields for that spec, and this loop checks only fields that are present. Authors can therefore get a passing card check for a server with a different sign-in mode. Check that session fields are absent when the spec disables sign-in.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds one website spec for loading, overlays, components, cards, bundle authoring and conformance checks. Notes now declares its site through website.toml.

  • Website specs now describe sites and compose reusable components.
  • SDK websites can now take their routes and settings from a spec.
  • Website environments can now take their name and domains from a spec.
  • Conformance checks can now compare a server with a website spec.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  TOML[Website TOML] --> Resolve[Resolve overlays and components]
  Resolve --> Spec[WebsiteSpec]
  Spec --> SDK[Check SDK class]
  SDK --> Card[Website card]
  Spec --> Bundle[Bundle name, health and domains]
  Spec --> Check[Conformance checks]
  Server[Running server] --> Check
Loading

Reviews (10) · Last reviewed commit: "feat(website): one spec declares a site,..." · Reviewed by Greptile

@williamloo-scale williamloo-scale changed the title website: one spec declares a site, composes it and checks any server (FD-4683) feat(website): one spec declares a site, composes it and checks any server (FD-4683) Nov 3, 2026
@williamloo-scale
williamloo-scale marked this pull request as ready for review November 3, 2026 00:24
@williamloo-scale
williamloo-scale requested a review from a team as a code owner November 3, 2026 00:24
Comment on lines +577 to +580
elif key == "uses" and not at:
kept = {_use_id(u): u for u in merged.get("uses", [])}
kept.update({_use_id(u): copy.deepcopy(u) for u in value})
merged["uses"] = list(kept.values())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Mistyped specs crash validation

A file containing extends = "base.toml" and uses = 1 reaches this loop before _validated() and raises TypeError. An inherited data.collections = [1] in another folder similarly crashes _rebased() at .values().

The CLI and bundle authoring catch only SpecError, so authors get a traceback instead of an error naming the bad field. Check these types before merging and rebasing, or turn these failures into SpecError.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/agentenv-protocol/src/agentenv_protocol/website/spec/__init__.py
Line: 577-580

Comment:
**Mistyped specs crash validation**

A file containing `extends = "base.toml"` and `uses = 1` reaches this loop before `_validated()` and raises `TypeError`. An inherited `data.collections = [1]` in another folder similarly crashes `_rebased()` at `.values()`.

The CLI and bundle authoring catch only `SpecError`, so authors get a traceback instead of an error naming the bad field. Check these types before merging and rebasing, or turn these failures into `SpecError`.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

Comment on lines +331 to +333
for key in ("session_cookie", "session_path", "state", "clock"):
if key in declared and site.get(key) != declared[key]:
problems.append(f"the card's {key} is {site.get(key)!r}, the spec's {declared[key]!r}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Wrong sign-in mode passes

_spec_card() accepts a cookie-based sign-in when the spec explicitly sets users.sign_in = "none". to_card_extension() omits the session fields for that spec, and this loop checks only fields that are present. Authors can therefore get a passing card check for a server with a different sign-in mode. Check that session fields are absent when the spec disables sign-in.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/agentenv-protocol/src/agentenv_protocol/website/conformance.py
Line: 331-333

Comment:
**Wrong sign-in mode passes**

`_spec_card()` accepts a cookie-based sign-in when the spec explicitly sets `users.sign_in = "none"`. `to_card_extension()` omits the session fields for that spec, and this loop checks only fields that are present. Authors can therefore get a passing card check for a server with a different sign-in mode. Check that session fields are absent when the spec disables sign-in.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

Comment on lines +865 to +866
raw["routes"] = {"ops": {r.name: {"method": r.method, "path": openapi_path(r.path), "roles": list(r.roles)}
for r in routes}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Distinct routes prevent startup

from_environment() removes path converters before checking for duplicate routes. An existing class can serve /items/{id:int} first and /items/{id} second, under different route names, so numbers reach one handler and other values reach the other. Both paths now become /items/{id}, causing resolve() to raise SpecError while create_app() builds the card. The site no longer starts, even without spec=.

Preserve the distinction between these served routes when deriving the spec.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/agentenv-protocol/src/agentenv_protocol/website/spec/__init__.py
Line: 865-866

Comment:
**Distinct routes prevent startup**

`from_environment()` removes path converters before checking for duplicate routes. An existing class can serve `/items/{id:int}` first and `/items/{id}` second, under different route names, so numbers reach one handler and other values reach the other. Both paths now become `/items/{id}`, causing `resolve()` to raise `SpecError` while `create_app()` builds the card. The site no longer starts, even without `spec=`.

Preserve the distinction between these served routes when deriving the spec.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

…erver (FD-4683)

This PR's change, rebased as one commit onto #444.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comment on lines +865 to +866
raw["routes"] = {"ops": {r.name: {"method": r.method, "path": openapi_path(r.path), "roles": list(r.roles)}
for r in routes}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Existing route names prevent startup

An existing site with an @api name such as name="items:list" now fails during create_app(), even without spec=. @api accepts that name, but card creation now passes it through Routes._names, which rejects the colon and raises SpecError.

Preserve names accepted by existing decorators when deriving their specs.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/agentenv-protocol/src/agentenv_protocol/website/spec/__init__.py
Line: 865-866

Comment:
**Existing route names prevent startup**

An existing site with an `@api` name such as `name="items:list"` now fails during `create_app()`, even without `spec=`. `@api` accepts that name, but card creation now passes it through `Routes._names`, which rejects the colon and raises `SpecError`.

Preserve names accepted by existing decorators when deriving their specs.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

This branch has not been deployed

No deployments
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