# Feature Research

**Domain:** Static-page publishing pipeline / dynamic page registry for an existing SPA, operated primarily by an LLM agent on behalf of a marketer (single-user, internal)
**Researched:** 2026-05-21
**Confidence:** MEDIUM-HIGH (HIGH on industry table-stakes; MEDIUM on LLM-ergonomic differentiators — emerging space; HIGH on anti-features given clearly scoped milestone)

---

## Context Anchors

Three constraints shape the entire feature landscape and are referenced throughout:

1. **Single user, LLM-mediated.** Max is the only consumer. Claude Code agents will read manifests, run the CLI, and interpret output. Features that exist purely for human-in-loop UI affordances are anti-features here.
2. **Brownfield SPA, not greenfield SSG.** The product is *additive* to a live CRA SPA — not Netlify, not Vercel, not Sanity. Many "standard" CMS features (build pipeline, scheduler, edge functions) are out-of-band concerns the existing Jenkins+CloudFront+Builder.io stack already covers or explicitly excludes.
3. **Git is the version of record** (locked in PROJECT.md Key Decisions). This eliminates an enormous surface area of feature complexity — rollback, history, audit trail, diffs, version pointers all collapse into "use git."

---

## Feature Landscape

### Table Stakes (Required for Max to Ship a Page)

These are the features without which the milestone literally cannot deliver Core Value ("Max can publish and update a webpage to a live URL via CLI"). Missing any of these = pipeline is incomplete.

| Feature | Why Expected | Complexity | Notes |
|---------|--------------|------------|-------|
| **Per-page manifest file** (declarative, versioned in git) | Industry standard — Netlify `netlify.toml`, Hugo front-matter, Sanity manifest, Cloud Foundry `manifest.yml`. Manifest = contract between page author and pipeline. | LOW | YAML or JSON. Minimal fields: `path`, `source` (dir/file), `wrapper` (nav+footer vs standalone), `title`, `meta` (description/og). |
| **`publish` command** | Primary verb; deploys page content + registers route. | MEDIUM | Must: read manifest, upload assets to origin, update registry, invalidate cache. Idempotent — re-running with no changes is a no-op. |
| **`validate` command** (manifest schema + path conflicts) | LLM agents will iterate; catching errors locally is critical to LLM ergonomics. AgentSpec and APM emphasize validation commands. | LOW | JSON Schema for manifest. Path collision check against existing registry. Exit non-zero on failure with structured error. |
| **Live page registry** (runtime-readable by SPA) | Locked in PROJECT.md Active requirements: "no Jenkins redeploy required to surface a new URL." Without this, the milestone collapses to a slow Jenkins workflow. | MEDIUM | SPA fetches `/pages.json` (or API endpoint) on boot; dispatcher uses it instead of `App.js` regex. Single fetch on app mount, cached client-side. |
| **Routing model: flat + namespace paths** | Explicit Active requirement (`/10years` and `/10years/*`). Mirrors Netlify path matching and Hugo URL management. | LOW | Registry entry declares `path: /10years` with implicit `/10years/*` capture toggleable via `namespace: true`. |
| **Static asset hosting** (relative paths under page subtree) | The `bigscreen10` site is pre-built static HTML/CSS/JS with relative asset references — they must resolve. | MEDIUM | Open question per PROJECT.md: same S3 origin under `/<page>/...` vs separate bucket with CDN behavior. Manifest declares an asset dir; CLI uploads under matching prefix. |
| **Cache invalidation on publish** | Stale-content avoidance. CloudFront invalidation is the canonical AWS pattern. AWS recommends short-cache HTML + versioned assets to minimize invalidation cost. | LOW | Auto-invalidate `/<path>` and `/<path>/*` after publish. Hash-stamped asset filenames where possible to avoid invalidation entirely. |
| **Auth via Arda OAuth** | Locked in PROJECT.md Constraints. Marketing must NOT have Jenkins; publish must be gated. | MEDIUM | OAuth device flow ideal for CLI (browser opens, user grants, token stored). Avoid PAT-style long-lived secrets. |
| **`list` / `ls` command** | LLM agents need to enumerate state — what pages exist, what routes are live, where do they point. AI-agent-driven CLI design treats output as a stable API contract. | LOW | Structured output (JSON flag) for agent consumption; tabular for humans. Reads from registry. |
| **`preview` command (or local serve)** | Industry standard: `netlify dev`, `vercel dev`, Hugo `server`. Max needs to see the page before live-publishing it. | LOW-MED | Two viable shapes: (a) local server that wraps the page in the SPA nav+footer; (b) draft URL on production CDN under unique path (e.g., `/_preview/<id>`). (a) is simpler for v1. |
| **404 fallback when registry says path doesn't exist** | Without this, mistyped URLs go to whatever the SPA dispatcher's catch-all does today. | LOW | Dispatcher consults registry; on miss falls through to existing logic (which already handles 404s). |
| **HTTP status codes in routing model (200, 301, 302, 404)** | Even minimal redirect handling is table-stakes — pages get renamed. Netlify `_redirects` is the canonical format. | LOW | Manifest field `redirect_from: [paths]` produces 301 entries in registry. |

### Differentiators (LLM-Friendly Operations)

These are where this pipeline is *different from* Netlify/Vercel/Sanity. The Core Value mentions "via a CLI + Claude Code skill" — LLM ergonomics is the explicit competitive axis.

| Feature | Value Proposition | Complexity | Notes |
|---------|-------------------|------------|-------|
| **Structured, parseable CLI output** (JSON flag on every command) | LLM agents consume CLI output as data, not prose. Treating CLI output as a stable API contract is a 2025-era pattern from MCP/agent-driven CLI design. | LOW | `--json` flag on every command. Default human-readable. Errors include `code`, `message`, `suggestion`, `docs_url`. |
| **Self-describing manifest schema** (`--print-schema`, `validate --explain`) | Lets an LLM agent discover the contract without preloading docs. Mirrors AgentSpec's "every tool reads from a root manifest." | LOW | `pages schema --json` outputs JSON Schema for the manifest. `validate <file> --explain` outputs per-field guidance. |
| **CLAUDE.md / Claude Code skill bundled with the CLI** | Explicit Active requirement. The toolchain ships with its own LLM operator manual. Skilldex-style "skill package" pattern. | LOW | `.claude/skills/publish-page/SKILL.md` — concise, example-driven. Commit alongside the CLI tool source. |
| **Manifest comments / annotations preserved** | YAML with comments; LLM-edited manifests stay readable. JSON loses comments — bad for agent-iterated docs. | LOW | Use YAML (`yq`-parseable) or JSONC. Avoid bare JSON for human-and-LLM-editable files. |
| **`diff` command (registry-vs-local, or page-vs-prod)** | LLM agents validate before they commit. Knowing what will change is the difference between cautious and chaotic publishes. | MEDIUM | `pages diff <page>` shows: routing changes, manifest field deltas, files added/removed. |
| **`unpublish` command** | Inverse of publish. Removes from registry; optionally deletes assets. LLMs do try/undo loops. | LOW | Soft (registry only) by default; `--purge` removes assets. Git history retains the manifest. |
| **No build hook required (but supported)** | PROJECT.md Key Decision: "Optional build hook." `bigscreen10` is already-static; future pages may add a build step. Lets LLMs ship a static folder immediately. | LOW | Manifest field `build` is optional; if absent, content is copied as-is. If present, run command in directory before upload. |
| **Wrapper preference field** (nav+footer vs standalone) | PROJECT.md Constraint: site-wide chrome wraps `/10years`. But future pages (auth-style, BigOrder-style) may want minimal chrome. Mirrors `App.js`'s six layout branches as an explicit data field. | LOW | `wrapper: react` (full nav+footer) / `wrapper: standalone` (header-only) / `wrapper: none`. Maps to existing layout components. |
| **Path-conflict detection vs hardcoded SPA routes** | The SPA still owns `/scan/*`, `/account/*`, `/token2/*`, `/browser/*`, etc. (per ARCHITECTURE.md). Publishing a page at `/account` must fail loudly. | LOW | Maintain a static "reserved paths" list inside the CLI/registry; `validate` and `publish` check against it. |
| **Idempotent `publish`** | LLM agents retry on uncertain failures. Idempotency = re-running publish with no changes is a no-op (no cache invalidation, no asset re-upload). | LOW-MED | Hash manifest+assets; skip if hash matches what's in registry. |
| **`status` command** (is this page live? when was it last published? from what commit?) | LLMs need single-call introspection. Mirrors GitOps "Git is source of truth — what's deployed = what's at HEAD." | LOW | `pages status <path>` → `{live: true, last_published: "...", git_sha: "...", manifest_hash: "..."}`. |
| **Operation log** (append-only, structured, in-repo or in registry) | Audit trail for a single-user system isn't compliance — it's *recallability*. An LLM agent reviewing recent state benefits from a log. | LOW | Append to `.pages-log.jsonl` on each publish/unpublish. Optional; can be reconstructed from git + registry. |

### Anti-Features (Defer, Reject, or Explicitly Out of Scope)

These are the gravity wells. The CMS/SSG ecosystem will pull this design toward feature parity with Netlify+Contentful+Sanity. Resist.

| Feature | Why It's Tempting | Why It's Wrong (Here) | Alternative |
|---------|---------------|-----------------|-------------|
| **Web GUI / admin dashboard for Max** | Every CMS has one; "marketer needs a UI" intuition | PROJECT.md Out of Scope: "GUI/web UI for Max — Max uses LLM agents + CLI." Building a GUI defeats the LLM-native premise and quadruples scope. | Invest in CLI + Claude Code skill polish. The "UI" is the LLM conversation. |
| **Multi-user RBAC / approval workflows** | Contentful/Sanity have draft→review→publish flows; "what if multiple editors?" | Single user (Max). Brownfield Arda already authenticates. Adding workflow approvals adds modeling complexity (states, transitions, notifications) with zero current users requesting them. | Single role on Arda (`site-editor` per PROJECT.md Open Investigation). Git PR review *is* the approval workflow if needed. |
| **Built-in A/B testing / experiments** | Mature CMSes ship with experimentation; "marketer wants A/B." Feature-flag tools like GrowthBook/Unleash promote integration. | Out of scope for v1 — no requirement. Industry consensus: A/B testing belongs in a *separate* layer (Optimizely, GrowthBook, custom JS) not the page deploy pipeline. Mixes concerns. | If needed later, A/B variants are two pages at separate URLs + client-side traffic splitter. Don't bake into manifest. |
| **Built-in feature flags / scheduled publish / expiry** | Convenient: "publish on Monday, expire Friday." Feature-flag systems support this. | Adds a runtime scheduler dependency (cron, Lambda, etc.). Max + LLM can invoke `publish`/`unpublish` at any time — agents *are* the scheduler. Cognitive overhead exceeds value at single-user scale. | Calendar reminders or LLM-scheduled cron job *outside* the publish tool. Defer to v2 with proven demand. |
| **Image optimization / responsive image pipeline** | Next.js, Vercel, Cloudinary ship this; "modern sites need it." | Out-of-band. The first deployable (`bigscreen10`) is already optimized in its source repo. Adding an optimizer = new CLI dependency (sharp/imagemagick) + build-time runtime + format decisions. | Authors optimize before publishing (it's a one-time cost per page). Document recommended sizes in CLAUDE.md. v2: optional `optimize: true` manifest flag. |
| **Generic build hook / SSG abstraction** ("supports any framework") | Tempting to make pipeline framework-agnostic — Astro, Next.js export, 11ty, Hugo all just "build then deploy." | Yes-AND-no. Manifest field `build` is a differentiator (above), but generalizing to "we run *any* build" pulls in dependency management, env vars, secrets, build caching, build timeouts. Out of scope. | Manifest's optional `build` runs *one* shell command in the page directory. The page repo owns its own toolchain. No magic. |
| **Multi-environment promotion (dev→staging→prod)** | Netlify/Vercel branch deploys → preview URL → promote. Sanity has datasets. | The SPA has one production environment. Preview = local serve or single-shot draft URL. Two environments (preview, prod) is enough. | Local preview + a "draft" URL prefix on the same CDN (`/_preview/<id>/<path>`). No environment promotion graph. |
| **Real-time collaborative editing / live reload at edge** | Modern CMSes flex on this | Single user; not a CMS; pages are git-tracked HTML, not a live-edit experience | git pull → edit → publish loop is the workflow. |
| **Per-page CDN configuration / edge functions** | "What if a page needs middleware?" Vercel/Cloudflare Workers route this way | The SPA already sits behind CloudFront with established behaviors. Per-page CDN config = config sprawl. | Document the global CDN rules in ARCHITECTURE.md; pages stay vanilla. |
| **Multi-tenant / multi-site support** | "What if we add `store.bigscreenvr.com` or another property?" | One site (bigscreenvr.com). Store is on Shopify (PROJECT.md Out of Scope). Designing for tenants now is premature abstraction. | Hardcode the site identity. If multi-site emerges, refactor later. |
| **Webhook / event subscriptions on publish** | Sanity, Contentful expose webhooks for integrations | No declared consumer of these events in v1. CloudFront invalidation is the only "downstream effect" that matters and it's invoked synchronously by the CLI. | If integrations emerge, add later. YAGNI. |
| **Built-in analytics / view tracking per page** | "We need to know if /10years is performing." | SPA already has GA + Reddit/Twitter/Meta pixels (per ARCHITECTURE.md `public/index.html`). Published pages inherit them via the nav+footer wrapper. | Reuse existing analytics. No new tracking pipeline. |
| **Versioned manifest schema migration tooling** | Sanity does manifest version migrations | Single user. If the schema changes in v1.1, update the 3 manifests by hand or with an LLM agent. | Defer to v2+. |
| **Localization / i18n routing** | The SPA's Builder.io branch already supports `?locale=jp`/`en` | Per ARCHITECTURE.md, React-owned screens are English-only; Builder.io handles its own locales. Out of scope for the new pipeline. | If a published page needs i18n in v2, declare locale variants in the manifest as separate paths. |
| **Search / content indexing of published pages** | Algolia/Pagefind integrate with SSGs | No requirement; tiny page count (1 in v1, low single digits anticipated). Site search doesn't exist on bigscreenvr.com today. | Defer indefinitely. |

---

## Feature Dependencies

```
publish (table stakes)
  ├── requires ──> manifest schema (table stakes)
  ├── requires ──> live page registry (table stakes)
  ├── requires ──> static asset hosting (table stakes)
  ├── requires ──> Arda OAuth auth (table stakes)
  └── requires ──> cache invalidation (table stakes)

validate (table stakes)
  ├── requires ──> manifest schema
  └── requires ──> path-conflict detection (differentiator)

preview (table stakes)
  └── enhances ──> publish  (catches mistakes before live)

diff (differentiator)
  ├── requires ──> live page registry
  └── enhances ──> publish  (LLM agents inspect before mutating)

status / list (table stakes/differentiator)
  └── requires ──> live page registry

unpublish (differentiator)
  ├── requires ──> live page registry
  └── enhances ──> publish (full lifecycle)

JSON output mode (differentiator)
  └── enhances ──> ALL commands (LLM ergonomics)

CLAUDE.md skill (differentiator)
  └── enhances ──> ALL commands (LLM discoverability)

wrapper preference (differentiator)
  ├── requires ──> manifest schema
  └── conflicts with ──> "build pipeline owns layout" anti-feature

optional build hook (differentiator)
  └── requires ──> manifest schema

routing: redirects (table stakes)
  ├── requires ──> live page registry
  └── enhances ──> SPA dispatcher

routing: namespaces (table stakes)
  ├── requires ──> live page registry
  └── conflicts with ──> hardcoded SPA reserved paths (must be reconciled)

A/B testing (anti-feature)
  └── conflicts with ──> single-manifest-per-path model
```

### Dependency Notes

- **publish requires registry:** Without a live registry, "publishing" means rebuilding the SPA in Jenkins — defeats the milestone.
- **validate requires schema:** Manifest must have a published, versioned JSON Schema before validation is meaningful. Schema = contract for both Max's LLM agent and the CLI.
- **JSON output enhances every command:** This is the single highest-leverage LLM-ergonomic feature. Implementing it as a cross-cutting concern (not per-command) is cheaper and more consistent.
- **wrapper preference conflicts with "pipeline owns layout":** The SPA's nav+footer is owned by React (`src/components/Page/index.js`). The publish pipeline must *delegate* layout to the SPA, not duplicate or substitute it. Wrapper field is a *signal* to the SPA dispatcher.
- **routing namespaces require reserved-path reconciliation:** Per ARCHITECTURE.md, SPA dispatcher already owns `/scan`, `/account`, `/browser`, `/big-order`, `/token2`, `/auth`, plus all Builder.io paths. Publishing a page at any of these = breakage. Reconciliation must happen at validate time.

---

## MVP Definition

### Launch With (v1.0 — this milestone)

The smallest set that delivers PROJECT.md's Core Value and the atomic milestone success criteria (deploy `/10years` + test mutation, neither via Jenkins or Builder.io).

- [ ] **Manifest schema** (YAML, with JSON Schema validator) — `path`, `source`, `wrapper`, `title`, `meta.description`, optional `build`, optional `redirect_from`, optional `namespace` (boolean), optional `assets_dir`
- [ ] **Live page registry endpoint** — served by some `cloud` repo API (TBD per PROJECT.md Open Investigation), readable by SPA on boot
- [ ] **SPA dynamic dispatcher** — replaces `App.js` regex for new pages, consults registry, preserves all existing six-branch routing for legacy paths
- [ ] **`publish` command** — auth via Arda OAuth, idempotent, invalidates CloudFront on success
- [ ] **`validate` command** — schema check + path conflict check against reserved SPA paths
- [ ] **`list` command** — enumerates registry, JSON output flag
- [ ] **`status` command** — single-page introspection
- [ ] **`unpublish` command** — registry-only removal
- [ ] **`preview` command** — local serve, page inside SPA nav+footer
- [ ] **JSON output flag** on all commands (`--json`)
- [ ] **`.claude/skills/publish-page/SKILL.md`** — operator guide for LLM agents
- [ ] **CloudFront cache invalidation** on publish (synchronous; CLI exits after invalidation submitted)
- [ ] **Static asset upload** — page directory uploaded under matching S3 prefix
- [ ] **301 redirect support** via `redirect_from` manifest field
- [ ] **Namespace routing** (`/10years/*` matched and served from same page tree)
- [ ] **Reserved-path list** (auth, scan, account, token2, browser, big-order, builder-io known paths) — enforced at validate

### Add After Validation (v1.x — once Max has shipped 2–3 pages)

- [ ] **`diff` command** — only meaningful once Max has muscle-memory and wants to inspect changes
- [ ] **`pages schema` command** — once the schema has versioned and the LLM agent needs to refetch it
- [ ] **Operation log** (`.pages-log.jsonl`) — once history-recall comes up in practice
- [ ] **Draft URL on production CDN** (`/_preview/<id>/<path>`) — once local-serve preview proves insufficient (e.g., for sharing with a teammate)
- [ ] **Optional asset hash-stamping** — once CloudFront invalidation costs or latency become noticeable

### Future Consideration (v2+ — defer until justified by real demand)

- [ ] **Image optimization step** — if/when a non-pre-optimized page is the source
- [ ] **Scheduled publish / expiry** — if/when Max requests it concretely
- [ ] **Multi-environment promotion** — if/when staging needs diverge from local-serve preview
- [ ] **Webhook / event subscriptions** — if/when a downstream system needs to react to publish events
- [ ] **Multi-user RBAC** — if/when additional editors actually exist
- [ ] **Builder.io route subsumption** — PROJECT.md notes the registry "remains capable of routing to Builder.io later." Not in v1.
- [ ] **GUI** — only if CLI + LLM proves insufficient (PROJECT.md explicitly defers)

---

## Feature Prioritization Matrix

| Feature | User Value (Max) | Implementation Cost | Priority |
|---------|------------|---------------------|----------|
| Manifest schema (YAML + JSON Schema) | HIGH | LOW | P1 |
| Live page registry endpoint | HIGH | MEDIUM | P1 |
| SPA dynamic dispatcher (replaces regex) | HIGH | MEDIUM-HIGH | P1 |
| `publish` command (auth + upload + invalidate) | HIGH | MEDIUM | P1 |
| `validate` command + reserved-path check | HIGH | LOW | P1 |
| `list` / `status` commands | HIGH | LOW | P1 |
| `unpublish` command | MEDIUM | LOW | P1 |
| `preview` command (local serve) | HIGH | MEDIUM | P1 |
| JSON output mode (all commands) | HIGH (LLM) | LOW | P1 |
| Arda OAuth integration | HIGH | MEDIUM | P1 |
| CloudFront cache invalidation | HIGH | LOW | P1 |
| Static asset upload + path scheme | HIGH | MEDIUM | P1 |
| `redirect_from` (301s) | MEDIUM | LOW | P1 |
| Namespace path routing (`/10years/*`) | HIGH | LOW | P1 |
| Wrapper preference manifest field | HIGH | LOW | P1 |
| Optional `build` hook (single shell command) | MEDIUM | LOW | P1 |
| `.claude/skills/publish-page/SKILL.md` | HIGH (LLM) | LOW | P1 |
| Idempotent publish (hash-skip) | MEDIUM | LOW-MED | P1 |
| `diff` command | MEDIUM | MEDIUM | P2 |
| `pages schema` (self-describing) | MEDIUM (LLM) | LOW | P2 |
| Operation log | LOW | LOW | P2 |
| Draft URL on prod CDN | MEDIUM | MEDIUM | P2 |
| Asset hash-stamping | LOW | MEDIUM | P2 |
| GUI / admin dashboard | NEGATIVE | HIGH | ANTI |
| Multi-user RBAC | NONE | HIGH | ANTI |
| A/B testing built-in | NONE | HIGH | ANTI |
| Scheduled publish / expiry | LOW | MEDIUM | ANTI (defer) |
| Image optimization | LOW | MEDIUM | ANTI (defer) |
| Multi-environment promotion | LOW | HIGH | ANTI |
| Multi-tenant / multi-site | NONE | HIGH | ANTI |
| Per-page CDN edge config | NONE | HIGH | ANTI |
| Built-in analytics | NONE (already exists) | MEDIUM | ANTI |
| Manifest schema migration tooling | NONE | MEDIUM | ANTI |
| i18n routing | NONE | MEDIUM | ANTI (use Builder.io for i18n) |
| Site search / content indexing | NONE | HIGH | ANTI |
| Webhook subscriptions | NONE | MEDIUM | ANTI |
| Real-time collaboration | NONE | HIGH | ANTI |

**Priority key:**
- **P1:** Must have for v1.0 milestone success
- **P2:** Should have once v1.0 proves out (post-`/10years` validation)
- **ANTI:** Deliberately not built — see Anti-Features section for rationale

---

## Competitor Feature Analysis

Comparing against the closest analogues in the ecosystem.

| Feature | Netlify | Vercel | Sanity (with SSG) | Builder.io (current) | Our Approach |
|---------|---------|--------|-------------------|----------------------|--------------|
| Manifest format | `netlify.toml` + `_redirects` (file in build output) | `vercel.json` (root) | Sanity schema + manifest in `dist/static` | Visual model in CMS, no local manifest | YAML per page directory (LLM-editable) |
| CLI publish | `netlify deploy --prod` | `vercel --prod` | `sanity deploy` | Web UI publish in Builder.io | `pages publish <dir>` (idempotent, JSON output) |
| Preview | Deploy-preview URLs per PR/branch | Preview URL per branch/commit | Sanity Studio + presentation tool | Builder.io preview in admin | Local serve (v1); draft URL (v2) |
| Rollback | "Publish this deploy" in UI; CLI history | One-click rollback in dashboard | Schema versioning + dataset history | Builder.io revision history | `git revert` + republish (git as source of truth) |
| Routing | `_redirects` + framework router | `vercel.json` rewrites | Framework router | Builder.io URL field | Live registry consumed by SPA dispatcher |
| Auth | Netlify dashboard accounts | Vercel team accounts | Sanity org SSO/roles | Builder.io org | Reuse Arda OAuth (existing identity) |
| Audit log | Built-in (enterprise) | Built-in (enterprise) | Built-in (with permissions) | Builder.io activity log | Git history + operation log file |
| GUI for non-devs | Dashboard | Dashboard | Sanity Studio (CMS-style) | Visual page builder | None — LLM agent is the interface |
| A/B testing | Netlify Edge Functions / split-testing addon | Vercel Edge Config / experiments | Personalization plugin | Builder.io built-in A/B | Out of scope (anti-feature) |
| Build hook | Auto-detected per framework | Auto-detected per framework | `sanity build` | N/A (visual editor) | Manifest-declared shell command (optional) |
| Multi-environment | Branch deploys + contexts | Preview/production environments | Datasets (`dev`/`prod`) | Builder.io environments | One environment + local preview |
| Cache invalidation | Automatic (instant atomic deploys) | Automatic (immutable deployments) | CDN-managed | CDN-managed | Explicit CloudFront invalidation on publish |

**Key positioning insight:** This is *not* a competitor to Netlify or Vercel — those are full platforms. It's a *thin* operations tool layered on top of existing AWS infrastructure (CloudFront + S3), designed for one user and one LLM agent. The differentiators are scoped narrowly: LLM-ergonomic CLI surface, manifest-as-contract, git-as-version-of-record. Everything else borrows from the AWS substrate or the existing SPA.

---

## LLM-Ergonomics: Why It's the Differentiating Dimension

Per PROJECT.md: "Max collaborates with LLM agents — GUI not required, but the CLI + manifest format must be clearly documentable in a Claude Code skill."

The 2026-era industry has converged on patterns that this design must adopt:

1. **CLI output as stable API contract** (InfoQ "Patterns for AI Agent Driven CLIs," 2025). `--json` is not a v2 nicety; it's a v1 requirement. Treat schema changes to output as breaking changes.
2. **Manifest-first development** (AgentSpec, APM, Manifest.build, Skilldex). A single YAML/JSON file that an LLM can read, validate, and edit is the lowest-friction contract.
3. **Self-describing tools.** `--print-schema`, `validate --explain`, `--help` with embedded examples. LLM agents cold-start by introspecting; the CLI must answer "what can I do?" without external docs.
4. **Skill/manual bundled with the tool.** Skilldex's "skill package" pattern, AgentSpec's `agent.yaml` — the operator manual ships *with* the binary, not on a separate docs site. For us: `.claude/skills/publish-page/SKILL.md` in the `static-pages` repo.
5. **Structured errors with remediation hints.** `{"code": "PATH_CONFLICT", "message": "...", "suggestion": "Try a different path", "docs_url": "..."}`. LLM agents recover from structured errors gracefully; from prose, poorly.

These five together are the differentiator. Each individually is cheap; collectively they make the difference between "Max's LLM agent can do this autonomously" and "Max keeps having to step in."

---

## Sources

**Static site CMS / manifest patterns:**
- [Sanity static-site CMS overview](https://www.sanity.io/static-site-cms)
- [Sanity deploy CLI reference](https://www.sanity.io/docs/cli-reference/deploy) — manifest schema fields
- [Sanity schema deployment](https://www.sanity.io/docs/apis-and-sdks/schema-deployment)
- [Headless CMS SEO checklist 2026](https://elmapicms.com/blog/headless-cms-seo-technical-checklist-2026) — metadata fields
- [Hygraph SSG + headless CMS guide](https://hygraph.com/resources/static-site-generators-and-headless-cms-ebook)
- [Hugo URL management & aliases](https://gohugo.io/content-management/urls/)
- [Grav routing & aliases](https://learn.getgrav.org/17/content/routing)

**Deploy CLI patterns (Netlify / Vercel / others):**
- [Netlify CLI deploy command](https://cli.netlify.com/commands/deploy/)
- [Netlify Get Started CLI](https://docs.netlify.com/cli/get-started/)
- [Netlify deploy overview](https://docs.netlify.com/deploy/deploy-overview/)
- [Netlify _redirects file](https://docs.netlify.com/manage/routing/redirects/overview/)
- [Netlify redirect options + 301/302](https://docs.netlify.com/manage/routing/redirects/redirect-options/)
- [Netlify rewrites and proxies](https://docs.netlify.com/manage/routing/redirects/rewrites-proxies/)
- [Netlify SPA redirect rules](https://www.netlify.com/blog/2020/04/07/creating-better-more-predictable-redirect-rules-for-spas/)
- [Netlify Deploy Previews](https://docs.netlify.com/deploy/deploy-types/deploy-previews/)
- [Netlify manage deploys (rollback)](https://docs.netlify.com/site-deploys/manage-deploys/)
- [Vercel KB: Vercel vs Netlify](https://vercel.com/kb/guide/vercel-vs-netlify)
- [Vercel environments & previews](https://vercel.com/docs/deployments/environments)
- [Vercel vs Netlify vs Cloudflare comparison](https://www.pkgpulse.com/blog/vercel-vs-netlify-vs-cloudflare-pages-deployment-2026)
- [Azure Static Web Apps CLI deploy](https://learn.microsoft.com/en-us/azure/static-web-apps/static-web-apps-cli-deploy)
- [Cloudflare Stout (deploy + rollback)](https://github.com/cloudflare/Stout)
- [Flightplan deployment tool](https://usersnap.com/blog/deploying-static-websites-flightplan/)

**CloudFront / S3 cache invalidation:**
- [AWS: Controlling Amazon CloudFront cache duration for S3](https://docs.aws.amazon.com/whitepapers/latest/build-static-websites-aws/controlling-how-long-amazon-s3-content-is-cached-by-amazon-cloudfront.html)
- [Configure CloudFront for static assets](https://oneuptime.com/blog/post/2026-01-26-cloudfront-static-assets/view)
- [Invalidate CloudFront cache](https://oneuptime.com/blog/post/2026-02-12-invalidate-cloudfront-cache/view)
- [Fix CloudFront caching stale content](https://oneuptime.com/blog/post/2026-02-12-fix-cloudfront-caching-stale-content/view)
- [AWS S3 + CloudFront cache invalidation in practice](https://caseyli.ca/aws-s3-cloudfront-invalidate-cache/)

**Auth / permissions / audit log (industry references):**
- [Contentful workflow rules](https://www.contentful.com/help/workflows/workflows-steps-management/workflow-rules-edit-publish-permissions/)
- [Contentful audit logs](https://www.contentful.com/developers/docs/tutorials/general/audit-logs/)
- [Sanity roles](https://www.sanity.io/docs/user-guides/roles)
- [Sanity roles & permissions concepts](https://www.sanity.io/docs/content-lake/roles-concepts)
- [Strapi audit logs](https://docs.strapi.io/cms/features/audit-logs)
- [Strapi roles & permissions guide](https://strapi.io/blog/permissions-in-strapi)

**LLM-friendly / agent-driven CLI design:**
- [InfoQ: Patterns for AI Agent Driven CLIs](https://www.infoq.com/articles/ai-agent-cli/) — CLI output as stable API contract, MCP integration
- [AgentSpec: agent.yaml root manifest](https://agents-oss.github.io/agentspec/)
- [APM (Agent Package Manager)](https://pypi.org/project/apm-cli/) — manifest-first agent dependencies
- [Microsoft APM](https://github.com/microsoft/apm)
- [Skilldex: skill package manager for agents](https://arxiv.org/html/2604.16911v1)
- [Manifest.build: human- and LLM-friendly YAML DSL](https://manifest.build/)
- [Manifest backend framework write-up](https://medevel.com/manifest/)

**Runtime config / dynamic routes for SPAs:**
- [Runtime config for single-page apps (Jon Sharpe)](https://blog.jonrshar.pe/2020/Sep/19/spa-config.html)
- [Runtime config for SPAs (Kharkevich)](https://kharkevich.org/2024/12/20/spa-runtime-config/)
- [React Router SPA mode](https://reactrouter.com/how-to/spa)
- [Cloudflare Workers SPA routing](https://developers.cloudflare.com/workers/static-assets/routing/single-page-application/)

**Git-as-source-of-truth / rollback patterns:**
- [Git revert in GitOps / Kubernetes (Komodor)](https://komodor.com/learn/git-revert-rolling-back-in-gitops-and-kubernetes/)
- [GitOps rollbacks paradigm (OneUptime)](https://oneuptime.com/blog/post/2026-02-26-gitops-rollbacks-paradigm/view)
- [Why git must be the source of truth (OrgFlow)](https://www.orgflow.io/blog/why-git-must-be-the-source-of-truth-in-modern-salesforce-devops)

**A/B testing / feature flags (assessed as anti-feature here):**
- [GrowthBook: what are feature flags](https://blog.growthbook.io/what-are-feature-flags/)
- [Unleash A/B testing with feature flags](https://docs.getunleash.io/guides/a-b-testing)
- [Statsig: A/B testing for feature flags best practices](https://www.statsig.com/perspectives/ab-testing-feature-flags-best-practices)
- [Martin Fowler: Feature Toggles](https://martinfowler.com/articles/feature-toggles.html)

**Preview / staging workflows:**
- [Storyblok: preview & production environments](https://www.storyblok.com/tp/create-preview-production-environments-and-deploy)
- [GOV.UK content preview / auth_bypass_ids](https://docs.publishing.service.gov.uk/manual/content-preview.html) — shareable draft URLs

---

*Feature research for: dynamic page registry + static-page publishing pipeline for marketing micropages on existing CRA SPA*
*Researched: 2026-05-21*
