diff --git a/.env.example b/.env.example index f4996cb18..ac401ef55 100644 --- a/.env.example +++ b/.env.example @@ -17,6 +17,7 @@ VITE_API_BASE_URL=https://pray.doxa.life # Set to a remote name (origin) or a repo URL (https://…/repo.git) and `bun run dev` fetches every few # seconds and fast-forwards when this checkout is on that branch with no local edits or commits; otherwise # it only reports "development is N commits ahead" (in the log and at /api/dev-sync). It never merges over your work. +# Following needs read access to the repo (a git credential helper, or an SSH remote); without it the log says so. DX_SYNC_REMOTE= # The branch to follow. Empty = development (the shared table; main is production). DX_SYNC_BRANCH= diff --git a/documentation/CLOUD-DEVELOPMENT-ENVIRONMENT.md b/documentation/CLOUD-DEVELOPMENT-ENVIRONMENT.md index 703baad5c..094c558f7 100644 --- a/documentation/CLOUD-DEVELOPMENT-ENVIRONMENT.md +++ b/documentation/CLOUD-DEVELOPMENT-ENVIRONMENT.md @@ -36,12 +36,12 @@ is never written to a log. **Your checkout has to be in a state where a fast-forward is safe.** All three of these: -- you are on the branch it follows (`DX_SYNC_BRANCH`, default `main`) +- you are on the branch it follows (`DX_SYNC_BRANCH`, default `development`) - you have no unsaved edits - you have no local commits of your own > **This is the one that surprises people.** If you have your own work in progress, the sync **stops -> pulling** — it will not touch your files. It reports `main is N commits ahead` instead and waits. +> pulling** — it will not touch your files. It reports `development is N commits ahead` instead and waits. > > That is deliberate and it is protecting you. But it means: **push your own work, or you will drift.** > If you leave local commits sitting for a day while your agent lands twenty changes, the pull you @@ -51,7 +51,7 @@ Two optional dials: | | what it does | default | |---|---|---| -| `DX_SYNC_BRANCH` | which branch to follow | `main` | +| `DX_SYNC_BRANCH` | which branch to follow | `development` | | `DX_SYNC_INTERVAL` | seconds between checks | `5` | --- diff --git a/documentation/HOWTO-join-the-table.md b/documentation/HOWTO-join-the-table.md index d256978c5..38f9295d5 100644 --- a/documentation/HOWTO-join-the-table.md +++ b/documentation/HOWTO-join-the-table.md @@ -93,6 +93,14 @@ DX_SYNC_REMOTE=origin That is the only line you need. `.env.local` is never committed, so it stays yours. +Two things it needs from you, once: + +- **Be on `development`.** It only fast-forwards the branch it follows, and a fresh clone starts on `main`, + so run `git switch development` (or clone with `-b development`). +- **Read access to the repo.** It fetches with your laptop's own git credentials: a credential helper, or an + SSH remote your key can read. Without them nothing is pulled, and the log (and `/api/dev-sync`) says + `dev-sync needs git credentials for ` rather than failing quietly. + > **Either a remote name or a full repo URL works.** `origin` is what almost everyone wants. A URL — > `https://…/your-repo.git` — works too and saves you a `git remote add`; if the repo is private it uses > the git credentials your laptop already has. Leave it unset and the whole feature is off. diff --git a/internal/dev-server.mjs b/internal/dev-server.mjs index 2a4a3b0d7..930dff6f1 100644 --- a/internal/dev-server.mjs +++ b/internal/dev-server.mjs @@ -1,11 +1,11 @@ /** * dev-server.mjs — the SHARED DEV SERVER every agent can check against (headless: curl-style fetches). * - * One `bun run dev` on this machine follows origin/main (fast-forward, ~30 s) from a checkout that is - * nobody's worktree, so it shows what a developer running the dev server on main sees. It answers + * One `bun run dev` on this machine follows origin/development (fast-forward, ~30 s) from a checkout that is + * nobody's worktree, so it shows what a developer running the dev server on development sees. It answers * /__dev-sha with the commit it is serving. * - * USE IT FOR: "does main work under the dev server", dev-parity checks, and waiting for a landing + * USE IT FOR: "does development work under the dev server", dev-parity checks, and waiting for a landing * to appear. DO NOT use it for an UNLANDED change (it does not have it): start a short-lived dev * server in your own worktree, or BUNDLE= bun x vite build. Release/embed checks (SRI, manifest, * iframe/script cells) stay on the BUILT tree. diff --git a/internal/dev-sync-plugin.mjs b/internal/dev-sync-plugin.mjs index b6f0e933e..0b047e8d0 100644 --- a/internal/dev-sync-plugin.mjs +++ b/internal/dev-sync-plugin.mjs @@ -21,7 +21,16 @@ import { execFile } from 'node:child_process' const git = (root, args) => new Promise((ok) => execFile('git', args, { cwd: root, timeout: 60000, env: { ...process.env, GIT_TERMINAL_PROMPT: '0' } }, - (e, out, err) => ok({ ok: !e, out: String(out || '').trim(), err: String(err || e?.message || '').trim().split('\n')[0] }))) + (e, out, err) => { + const all = String(err || e?.message || '').trim() + ok({ ok: !e, out: String(out || '').trim(), err: all.split('\n')[0], errAll: all }) + })) + +// A fetch refused for want of CREDENTIALS is not a network blip: it fails identically every interval until +// this laptop can read the repo. git runs with GIT_TERMINAL_PROMPT=0 (a prompt would hang the dev server), so +// with no credential helper it can only fail, and a bare "fetch-failed / remote: Unauthorized" read as a +// broken server. Measured on a fresh clone: the same fetch succeeded once credentials were supplied. +const AUTH_RE = /unauthori[sz]ed|authentication failed|could not read (username|password)|terminal prompts disabled|permission denied \(publickey|\b40[13]\b/i // What the log and /api/dev-sync show: never the credentials of a https://user:token@host/… URL. const shown = (remote) => remote.replace(/\/\/[^/@]*@/, '//') @@ -37,7 +46,15 @@ export async function syncOnce(root, remote, branch, log = { info() {}, warn() { const src = label(remote, branch) const s = { enabled: true, remote: shown(remote), branch, checkedAt: new Date().toISOString() } const f = await git(root, ['fetch', '-q', remote, branch]) - if (!f.ok) return (REG.status = { ...s, state: 'fetch-failed', message: `could not fetch ${src}: ${shown(f.err)}` }) + if (!f.ok) { + const auth = AUTH_RE.test(f.errAll) + const state = auth ? 'needs-credentials' : 'fetch-failed' + const message = auth + ? `dev-sync needs git credentials for ${shown(remote)} (a credential helper, or an SSH remote your key can read); git said: ${shown(f.err)}` + : `could not fetch ${src}: ${shown(f.err)}` + if (REG.status.state !== state || REG.status.message !== message) log.warn(`[dev-sync] ${message}`) + return (REG.status = { ...s, state, message }) + } const up = (await git(root, ['rev-parse', 'FETCH_HEAD'])).out const head = (await git(root, ['rev-parse', 'HEAD'])).out const cur = (await git(root, ['symbolic-ref', '-q', '--short', 'HEAD'])).out diff --git a/internal/landing-preconditions.mjs b/internal/landing-preconditions.mjs index 856dc0f88..3bfeb5a3f 100644 --- a/internal/landing-preconditions.mjs +++ b/internal/landing-preconditions.mjs @@ -106,7 +106,7 @@ if (process.env.DOXA_SKIP_FRESHNESS === '1') { // dependencies) reaches ANY bundle — measured 2026-09-25: one Arabic catalogue edit in library/i18n left 12 // of 25 bundles stale, most of them not composites, and a composite-only check saw none of it. Otherwise: // every composite, plus each bundle whose own folder the landing touched. - const base = run('git', ['merge-base', 'HEAD', `origin/${process.env.MAIN_CAPTURE_BRANCH || 'main'}`]).stdout.trim() + const base = run('git', ['merge-base', 'HEAD', `origin/${process.env.MAIN_CAPTURE_BRANCH || process.env.LANDING_BRANCH || 'development'}`]).stdout.trim() const changed = base ? run('git', ['diff', '--name-only', base, 'HEAD']).stdout.split('\n').filter(Boolean) : [] // ★ A library/ change no longer means EVERY bundle. It means every bundle whose SOURCE GRAPH contains a // changed file, or that reads a changed i18n area: measured, a one-file library fix reached 1 of 24 diff --git a/skills/plugin-designer/SKILL.md b/skills/plugin-designer/SKILL.md index cd91acb27..1126c9445 100644 --- a/skills/plugin-designer/SKILL.md +++ b/skills/plugin-designer/SKILL.md @@ -213,7 +213,7 @@ read the pin and label layers in `plugin-designs/template-bundle/template-map-a. ```bash bun run dev # the Plugin Studio at http://localhost:5173/ BUNDLE= bun x vite build # just your bundle → public/dx-maps// -git add -A && git commit -m "…" && git pull --rebase origin main && git push origin HEAD:main +git add -A && git commit -m "…" && git pull --rebase origin development && git push origin HEAD:development ``` - Your plugin appears in the studio as soon as its folder exists; edits reload live. One screen diff --git a/skills/plugin-designer/references/glossary.md b/skills/plugin-designer/references/glossary.md index 0771906ca..bc674116a 100644 --- a/skills/plugin-designer/references/glossary.md +++ b/skills/plugin-designer/references/glossary.md @@ -76,4 +76,4 @@ So "give it a label" is a gate, not a convention. Run `bun run check:studio-rail - `targets.md` — capabilities, `targets`, and where a plugin is allowed to run - `backend.md` — data and the user tiers (design intent, not built) -- `workflow.md` — the loop from empty folder to landed on main +- `workflow.md` — the loop from empty folder to landed on development diff --git a/skills/plugin-designer/references/workflow.md b/skills/plugin-designer/references/workflow.md index 47534f941..297a3e939 100644 --- a/skills/plugin-designer/references/workflow.md +++ b/skills/plugin-designer/references/workflow.md @@ -1,4 +1,4 @@ -# The workflow: empty folder to landed on main +# The workflow: empty folder to landed on development ## 1. Develop in the Plugin Studio @@ -43,14 +43,17 @@ Run the gates that cover what you touched. The ones most often relevant: `package.json` holds the full list; they are all prefixed `check:`. -## 4. Land it on main +## 4. Land it on development + +`development` is where everyone lands; `main` is production and moves only when `development` is merged +into it, so never push to `main`. Three commands, in this order, every time: ```bash git add -A && git commit -m "what you changed" -git pull --rebase origin main -git push origin HEAD:main +git pull --rebase origin development +git push origin HEAD:development ``` **A rejected push is normal, not an error.** Someone landed first: run the middle command again and