From 4207a59c28beaf3bea2fac310884ea25f4eef433 Mon Sep 17 00:00:00 2001 From: John Dietz Date: Fri, 21 Aug 2026 00:45:13 -0400 Subject: [PATCH 1/3] feat(contract): site-forge ops + docs --- THEME.md | 28 +++++++++++++++++++++++++--- static/theme.js | 3 +++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/THEME.md b/THEME.md index 399847e..9421960 100644 --- a/THEME.md +++ b/THEME.md @@ -30,7 +30,8 @@ what it does for you. Allowed ops: `discover`, `zones`, `createZone`, `apps`, `appRepos`, `shipApp`, `updateApp`, `deleteApp`, `redeploy`, `buildLogs`, `metrics`, -`quota`, `character`, `saveCharacter`. +`quota`, `character`, `saveCharacter`, `forgeSite`, `forgeSiteJob`, +`forgeSiteJobs`. ## Streams (push, not request/response) @@ -55,8 +56,8 @@ sees a credential. `discover().capabilities` lists what this platform implements. Only offer a feature when its flag is present: `runtime-logs`, `quota`, `app-events`, -`volumes`, `custom-domains` (plus the original `apps`, `zones`, `themes`, -`character`). +`volumes`, `custom-domains`, `site-forge` (plus the original `apps`, `zones`, +`themes`, `character`). ## Newer app fields @@ -80,6 +81,27 @@ feature when its flag is present: `runtime-logs`, `quota`, `app-events`, Render the list as given — do not re-sort — so every theme agrees on environment order. +## Site forge (capability: `site-forge`) + +When `discover().capabilities` includes `site-forge`, the platform can +generate a complete static website from interview answers and push it to +git — the theme then ships the repo like any other app. + +- `theme.forgeSite(org, {name, kind, vibe, headline, extras})` starts a job. + `kind` is `portfolio | blog | landing | docs`; `name` a lowercase RFC-1123 + label. Returns the job `{id, phase, …}`. +- `theme.forgeSiteJob(org, id)` polls it. Phases: `Designing → Writing → + Pushing → Ready | Failed`. `phase_started_at` + `message` (heartbeat with + elapsed) power honest progress UI; `brief` is the design brief once + drafted. On `Ready`, `repo_url`/`repo_name` are exactly what `shipApp` + needs. +- `theme.forgeSiteJobs(org)` lists jobs newest-first (rehydration). + +The job does NOT register the app: call `shipApp({repo_url, repo_name, +zone_ref, region, size, port: 8080, public_url_enabled: true})` yourself +once Ready, then follow the app's build via `apps`/`deployments`/`buildLogs`. +Without the capability flag, hide the feature entirely. + v1 themes (token-in-fragment + direct API fetch) stop working once the platform removes theme-origin CORS: replace your copy of `theme.js` with this repo's version — the method signatures are unchanged. diff --git a/static/theme.js b/static/theme.js index e809223..e676dee 100644 --- a/static/theme.js +++ b/static/theme.js @@ -89,6 +89,9 @@ const theme = (() => { deleteApp: (org, name) => call("deleteApp", org, name), redeploy: (org, name) => call("redeploy", org, name), buildLogs: (org, name) => call("buildLogs", org, name), + forgeSite: (org, spec) => call("forgeSite", org, spec), + forgeSiteJob: (org, id) => call("forgeSiteJob", org, id), + forgeSiteJobs: (org) => call("forgeSiteJobs", org), metrics: (org, name, opts) => call("metrics", org, name, opts), quota: (org) => call("quota", org), character: (org) => call("character", org), From f85313b0bc952e3c2839770077c49b9473a7109b Mon Sep 17 00:00:00 2001 From: John Dietz Date: Fri, 21 Aug 2026 10:37:54 -0400 Subject: [PATCH 2/3] feat(contract): inferSite, forgeSiteUpdate, domainStatus + v2 docs --- THEME.md | 31 ++++++++++++++++++++++++++++--- static/theme.js | 3 +++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/THEME.md b/THEME.md index 9421960..a046618 100644 --- a/THEME.md +++ b/THEME.md @@ -31,7 +31,7 @@ what it does for you. Allowed ops: `discover`, `zones`, `createZone`, `apps`, `appRepos`, `shipApp`, `updateApp`, `deleteApp`, `redeploy`, `buildLogs`, `metrics`, `quota`, `character`, `saveCharacter`, `forgeSite`, `forgeSiteJob`, -`forgeSiteJobs`. +`forgeSiteJobs`, `inferSite`, `forgeSiteUpdate`, `domainStatus`. ## Streams (push, not request/response) @@ -56,8 +56,8 @@ sees a credential. `discover().capabilities` lists what this platform implements. Only offer a feature when its flag is present: `runtime-logs`, `quota`, `app-events`, -`volumes`, `custom-domains`, `site-forge` (plus the original `apps`, `zones`, -`themes`, `character`). +`volumes`, `custom-domains`, `site-forge`, `site-update`, `domain-status` +(plus the original `apps`, `zones`, `themes`, `character`). ## Newer app fields @@ -102,6 +102,31 @@ zone_ref, region, size, port: 8080, public_url_enabled: true})` yourself once Ready, then follow the app's build via `apps`/`deployments`/`buildLogs`. Without the capability flag, hide the feature entirely. +v2 additions: + +- `forgeSite` requests are free-text-first: `{name, headline, prompt}` where + `prompt` is whatever the user typed (any site type). The v1 `kind` field is + accepted but no longer an enum. +- `theme.inferSite(org, {prompt})` → `{name, headline, summary}` — + synchronous guesses for a confirm screen. It can 503/fail; always keep a + local slug fallback so the flow never dead-ends. +- `theme.forgeSiteUpdate(org, {name, instruction})` (capability + `site-update`) → an update job (`type: "update"`, phases `Reading → + Writing → Pushing → Ready | Failed`) that commits the change to the site + repo's `main` WITHOUT force — user commits are never erased, and the + platform rebuilds automatically (watch `deployments` for `reason: + "COMMIT"`). One running update per site; a second gets 409. Omitted files + keep their contents — updates cannot delete files. +- `theme.domainStatus(org, name)` (capability `domain-status`) → the live + public-resolver view of the app's custom-domain wiring: + `{domain, txt_record, txt_expected, txt_ok, verified, cname_target, + resolved_target, cname_ok, serving, checked_at}`. Render a copy-paste + record checklist from it, and NEVER present the domain as a clickable link + before `serving` is true — resolver caches lie, especially the user's. +- Forged sites are identifiable by convention: `repo_url` contains + `/site--`. The convention (plus a `konduit-site` GitLab topic) is the + only marker — deleting a theme leaves the sites as plain apps. + v1 themes (token-in-fragment + direct API fetch) stop working once the platform removes theme-origin CORS: replace your copy of `theme.js` with this repo's version — the method signatures are unchanged. diff --git a/static/theme.js b/static/theme.js index e676dee..bcb01cb 100644 --- a/static/theme.js +++ b/static/theme.js @@ -92,6 +92,9 @@ const theme = (() => { forgeSite: (org, spec) => call("forgeSite", org, spec), forgeSiteJob: (org, id) => call("forgeSiteJob", org, id), forgeSiteJobs: (org) => call("forgeSiteJobs", org), + inferSite: (org, spec) => call("inferSite", org, spec), + forgeSiteUpdate: (org, spec) => call("forgeSiteUpdate", org, spec), + domainStatus: (org, name) => call("domainStatus", org, name), metrics: (org, name, opts) => call("metrics", org, name, opts), quota: (org) => call("quota", org), character: (org) => call("character", org), From a7e5bf3c3668daf31d79d2c612241816f90dd22f Mon Sep 17 00:00:00 2001 From: John Dietz Date: Sat, 22 Aug 2026 12:07:58 -0400 Subject: [PATCH 3/3] feat(contract): deleteSiteRepo, platform_url deep-link base, two-step deletion docs --- THEME.md | 13 ++++++++++++- static/theme.js | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/THEME.md b/THEME.md index a046618..904bc96 100644 --- a/THEME.md +++ b/THEME.md @@ -31,7 +31,8 @@ what it does for you. Allowed ops: `discover`, `zones`, `createZone`, `apps`, `appRepos`, `shipApp`, `updateApp`, `deleteApp`, `redeploy`, `buildLogs`, `metrics`, `quota`, `character`, `saveCharacter`, `forgeSite`, `forgeSiteJob`, -`forgeSiteJobs`, `inferSite`, `forgeSiteUpdate`, `domainStatus`. +`forgeSiteJobs`, `inferSite`, `forgeSiteUpdate`, `domainStatus`, +`deleteSiteRepo`. ## Streams (push, not request/response) @@ -126,6 +127,16 @@ v2 additions: - Forged sites are identifiable by convention: `repo_url` contains `/site--`. The convention (plus a `konduit-site` GitLab topic) is the only marker — deleting a theme leaves the sites as plain apps. +- `discover().platform_url` is the platform frontend origin — the base for + deep links back into the platform (Konduit's app view is + `/theme/?zone=&app=`). Never + derive this from `document.referrer`. +- Deleting a forged site is two explicit steps: `deleteApp` removes the app, + its build and instances (the git repo survives); then, only if the user + opts in, `theme.deleteSiteRepo(org, name)` permanently deletes the + `site--` repository. Repo deletion is TeamAdmin+ (a 403 for + developers — offer the app-only path) and is NOT undoable: confirm + explicitly, never bundle it silently into app deletion. v1 themes (token-in-fragment + direct API fetch) stop working once the platform removes theme-origin CORS: replace your copy of `theme.js` with diff --git a/static/theme.js b/static/theme.js index bcb01cb..773e34d 100644 --- a/static/theme.js +++ b/static/theme.js @@ -95,6 +95,7 @@ const theme = (() => { inferSite: (org, spec) => call("inferSite", org, spec), forgeSiteUpdate: (org, spec) => call("forgeSiteUpdate", org, spec), domainStatus: (org, name) => call("domainStatus", org, name), + deleteSiteRepo: (org, name) => call("deleteSiteRepo", org, name), metrics: (org, name, opts) => call("metrics", org, name, opts), quota: (org) => call("quota", org), character: (org) => call("character", org),