# Veect — v1 Technical Specification · rev 1.2.2

_Audience: a coding agent implementing v1. Status: implementation-ready._
_rev 1.2 (2026-07-10, founder direction): **distribution pivot** — v1 is a **standalone application** (desktop, bundled runtime) with an **additional Docker image**; repositories connect **remote-first** (GitHub / GitLab / Bitbucket / any git URL) with **local checkout** as the alternative mode; config becomes **`veect.config.json`** (machine-writable); **publishing branches to origin is now in scope**; worktrees & all Veect state move **outside the user's repo** into app-managed workspaces; the npm-installed-in-repo package (Storybook-style, rev 1.1 §1) is **deferred to a later distribution**._
_rev 1.2.1 (same day, post adversarial review of the new layers): `node` PATH shim + Electron ABI policy (the bundled-runtime promise made real) · workspace-trust model · committer identity · config carry rule · device-flow org-wall handling · SSH BatchMode/known-hosts · exact credential-helper mechanics · LFS/submodule policy · CoW worktree installs · Docker `safe.directory` + headless secret store · env manager pulled into M3 · M0 materialization contradiction resolved._
_rev 1.1 foundations retained: render-path invariant + parity check · split lazy `__veect__` registries, never committed · dedicated-origin adapters + Host validation · checkpoint transaction · docgen-at-scale rules · milestone discipline._
_Companions: `design-brief-veect.md` (UI/UX) · `design-app-prd.md` (product scope) · `company-context.md` (strategy)._
_Locked: **AI compose in v1 (Anthropic; key via app settings or env)** · **any React+TS repo, full Next.js first-class** · **SQLite locally + canonical design JSON committed in the branch** · **name: Veect / npm `veect` / scope `@veectjs/*`**._

---

## 1. What we are building

Veect is a **standalone design application that connects to a repository** — remote or local — and turns it into a design surface:

```
1. Download Veect (macOS / Windows / Linux)         — no system Node required
2. Connect a repository:
     · GitHub (device-flow sign-in) → pick a repo   — the default path
     · any git URL (HTTPS + token / SSH via agent)  — GitLab, Bitbucket, self-hosted
     · or a local folder                            — an existing checkout
3. Veect clones (remote) or attaches (local), installs dependencies with its
   bundled runtime, reads veect.config.json — or walks the user through picking
   the component folders visually and writes it
4. The designer composes on a canvas of the repo's REAL components;
   each design project is a git worktree + branch (veect/<slug>)
5. Checkpoint → real TSX + design.veect.json committed → Publish pushes
   veect/<slug> to origin → the team reviews an ordinary PR
```

A **Docker image** ships alongside: the same server, headless, for a team box, self-hosting, or CI (`docker run veect export`). The npm-in-repo package (`npm i -D veect`, Storybook-style) is a **later distribution** on the same core (§17).

**Purpose (founder's words):** bring designers closer to developers so they work on prototypes directly on the codebase, with already-defined components and all the context the codebase actually holds.

**What v1 must prove:** a designer with _no development environment_ connects a repo, and in one sitting produces an on-brand, real-code prototype on a branch that survives an engineer's review.

### The core invariant (unchanged, stated honestly)

> **Same components, same toolchain.** Every component instance on the canvas is the repo's own module compiled by the repo's own framework toolchain. Veect never re-implements or approximates a component.

Enforced, not assumed: the doc renderer styles frames with **inline styles only** (never runtime-assembled Tailwind class strings); generated TSX uses Tailwind classes (Veect registers `output.dir` in the host's Tailwind content/`@source` as part of the first published branch); **checkpoint parity check** — the generated file is rendered in the adapter and perceptually diffed against the doc-rendered canvas (threshold ~1%); parity failure blocks the checkpoint with a specific error.

### Non-goals for v1

Multi-user realtime editing · cloud sync (Docker image is self-hosted, single-team-token) · non-React frameworks · Figma import · vector tools · editing arbitrary existing app pages (Onlook's lane) · token write-back · Storybook interop · npm-in-repo distribution (deferred, not dead) · **non-git sources** · **yarn PnP** (doctor fails with message; bun best-effort).

### Prototype inheritance (rev 1.2.2 — the editor exists)

The founder's interactive prototype + codebase (`veect-design-spec-current.md`; React 18 · TS strict · Tailwind · **React Flow board substrate** · **zustand** as the in-session store) **seeds `packages/editor`** and _amends this spec_ where it went further:

- **Multi-view frames ship in v1** (supersedes "desktop breakpoint only"): frames carry `views/view` (desktop = frame `w` · tablet 768 · mobile 390), rendered as side-by-side **view clusters** of the same tree; the **active view** drives preview, code, and export width. Adapters render each enabled view's width; the doc model's `FrameNode` gains `views: View[]` + `view: ViewId`.
- **Code panel edit-mode ships** (supersedes rev 1.1 "read-only"): the codegen grammar is **bidirectional** — the generated TSX parses back with line-referenced errors. The inverse parser is part of `packages/codegen` and must stay in lockstep with the emitter (golden tests run both directions).
- **AI UX inherited:** model picker (Haiku default / Sonnet option — a per-request override of `ai.model`), image attachments in context, scoped edits (amber ring + scope chip), dashed-ghost staging, refusal protocol. The prototype's offline heuristics are retained as the **record/replay eval harness + Playground demo mode**.
- **Vocabulary:** top-bar "Export" becomes **Checkpoint** (commit) + **Publish** (push + PR link); the export-zip dialog survives only in Playground/Docker-CI contexts.
- **Integration seams:** the codebase's declared seams map 1:1 onto this spec's subsystems (`engine/ai.ts#complete` → §14 AI engine; export zip → §10 checkpoint/publish; placeholder projects → §2/§11 workspaces; auth stub → §6 connections; zustand-only persistence → §11 SQLite/JSON write-through). Wiring table: `veect-ui-gap-spec.md` §4; remaining UI surface: same doc §2–§3.

---

## 2. Architecture overview

```
Veect app (desktop shell; bundled Node runtime — ELECTRON_RUN_AS_NODE for all child processes)
│
├── Editor UI (the web editor, in the shell's window)
├── Server core (Hono on 127.0.0.1) ──────────────── identical binary logic in the Docker image
│     ├── Connections (GitHub device flow · git URL+PAT · SSH-via-agent · local folder)
│     ├── Discovery engine (components + tokens, shared TS program, watch)
│     ├── Doc engine (model, validation, revisions)
│     ├── Codegen (doc → TSX, prettier, parity check)
│     ├── Git engine (clone/fetch/worktrees/checkpoint/publish — system git, execa)
│     └── AI engine (Anthropic, constrained to registry)
│
├── App data (per-OS app dir, e.g. ~/Library/Application Support/Veect)
│   └── workspaces/github.com/acme/shop/
│       ├── clone/                       ← managed clone (remote mode) — partial clone,
│       │   │                              .git/info/exclude hides nothing: repo stays PRISTINE
│       │   └── veect.config.json        ← if committed in the repo; else workspace-local (below)
│       ├── worktrees/checkout-flow/     ← git worktree, branch veect/checkout-flow
│       │   └── <output.dir>/checkout-page/{design.veect.json, CheckoutPage.tsx}
│       ├── veect.db                     ← SQLite (projects, revisions, AI runs, ports)
│       ├── config.json                  ← workspace-local config until committed to the repo
│       └── env.enc                      ← env values, OS-encrypted (never in the clone/repo)
│
└── Preview adapters (dedicated port = dedicated origin each; common runtime protocol)
      ├── vite-react: embedded Vite rooted at the worktree (cwd pinned to worktree)
      └── nextjs: managed `next dev` of the worktree + materialized /__veect__ route
           └── Editor canvas <iframe> — cross-origin, postMessage with pinned targetOrigin
```

**Local mode:** `workspaces/<hash-of-path>/` holds `worktrees/`, `veect.db`, `env.enc`; the user's checkout is attached read-mostly — Veect writes **nothing** into it (worktrees live in app data in **both** modes; rev 1.1's `.gitignore`/shields machinery is deleted — the repo is untouched except files the user chooses to commit on Veect branches).

---

## 3. Distribution & deployment

**Standalone desktop app (default).** Electron shell 🟡: chosen because Electron embeds a real Node runtime — every child process (`next dev`, Vite, package managers, prettier) is spawned with `process.execPath` + `ELECTRON_RUN_AS_NODE=1`, so **the user needs no system Node**. (Tauri + node-sidecar is the documented alternative if binary size becomes a priority; the UI is plain web either way, so the shell is swappable.) Auto-update via standard channels — **draining/stopping all adapter child processes before relaunch** (an updated bundle orphans workers whose `execPath` vanished). **System git ≥ 2.38 IS required** in v1 (worktrees + partial clone; bundling git is out of scope) — the app detects it and walks the user through the one-time install (macOS xcode-select / Git for Windows) 🟡 flagged as the residual environment friction.

**Runtime realities (rev 1.2.1, from adversarial review — these make the bundled-runtime promise actually hold):**

- **`node` PATH shim.** npm/pnpm `.bin` wrappers and lifecycle scripts invoke a binary literally named `node` — which doesn't exist on the target machines. Veect injects a shim directory at the front of `PATH` for every child process: `node` (POSIX) and `node.exe`/`node.cmd` (Windows) exec the app binary with `ELECTRON_RUN_AS_NODE=1`. A fixture with a `node`-invoking postinstall is part of the A5 acceptance test.
- **Native-module / ABI policy.** `ELECTRON_RUN_AS_NODE` reports a Node version but Electron's ABI differs — source builds against nodejs.org headers fail at require-time. Installs run with `npm_config_runtime=electron` + matching `disturl`/target; **doctor scans host dependencies for non-N-API native modules** (better-sqlite3-class) → playbook: prebuilt-only versions or Docker mode. N-API modules (modern sharp, @next/swc) are unaffected. `engines`/`engine-strict` mismatches are surfaced, not silently ignored.
- **Vendored package managers.** Corepack ships shims, not binaries — first use would fetch over the network (proxy/offline failure). Veect vendors pinned pnpm/yarn in the bundle, pins `COREPACK_HOME` to app data, and prefetches at connect.
- **`runAsNode` fuse stays ENABLED** — the architecture requires it. This deviates from Electron's default hardening guidance and makes the signed binary a general-purpose interpreter (possible EDR flags); the accepted risk is recorded in §16. Implementers must not "fix" this.
- **Windows:** workspaces live under `%LOCALAPPDATA%` (never the roaming profile — multi-GB workspaces in Roaming is a corporate disaster); workspace dirs use short hashed names (MAX_PATH); doctor suggests a Defender exclusion for the workspaces dir.

**Docker image (additional, same release).** `veect/veect` — the server core, headless, browser UI. **Token auth mandatory** (no localhost-trust in a container): boot prints/accepts `VEECT_TOKEN`; every HTTP/WS request requires it. Single-team, single-token in v1 (multi-user auth belongs to a later cloud layer — decision log). Volumes: `/data` (workspaces). Also the **CI story**: `docker run -v $PWD:/repo veect/veect export --local /repo` runs the export gate on a checked-out `veect/*` branch.

**npm package `veect`** — **register the name/org now, ship later** (§17). The core is layered so the CLI/npm distribution reuses server + engines unchanged.

**Monorepo layout** (rev 1.1 §3, amended): add `packages/shell/` (Electron main, auto-update, safeStorage bridge, deep links) and `packages/connections/` (providers, credential store, clone/fetch orchestration); everything else stands.

---

## 4. Surfaces: app UX + CLI

The **GUI is primary** — connect, browse projects, design, checkpoint, publish, doctor, settings all in-app (design brief §9 incl. the new Connect Repository + Environment screens). A **`veect` CLI ships inside the app bundle** (and in the Docker image) for power users and CI:

| Command                           | Behavior                                                                                                                                                                                                                                                        |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `veect open [path\|url]`          | Launch/focus the app on a workspace (registers as the `veect://` deep-link handler too).                                                                                                                                                                        |
| `veect export <project> [design]` | Regenerate TSX+JSON, prettier, full `tsc --noEmit`, parity check, checkpoint commit. **CI mode:** cwd is a plain checkout of a `veect/*` branch → operates in place, no app/workspace needed. Non-zero exit on failure.                                         |
| `veect doctor`                    | Environment & connection report: git version, credential reachability (never prints secrets), framework/appDir detection, Tailwind content registration, adapter health, disk usage per workspace, Windows long-path check. Pass/warn/fail + one-line fix each. |

`init` is gone as a user-facing requirement — connecting a repo **is** init. (The Storybook-style `npx veect init` returns with the npm distribution later.)

---

## 5. Configuration — `veect.config.json`

JSON, not TS (founder decision): the app must be able to **read and write** it safely from the GUI (the first-run wizard writes it; the settings screen edits it), and JSON travels through the schema. A published **JSON Schema** (`https://veect.dev/schema/config.v1.json`) gives editor autocomplete; the parser accepts JSONC (comments tolerated), the writer emits canonical JSON.

```jsonc
// veect.config.json — lives at the repo root, committed like any config
{
  "$schema": "https://veect.dev/schema/config.v1.json",
  "components": ["src/components/**/*.{tsx,ts}"], // REQUIRED — the design-session folders
  "exclude": ["**/*.test.*", "**/*.stories.*"],
  "framework": "auto", // "auto" | "nextjs" | "vite-react" | "react"
  "appDir": ".", // monorepos: app package to run, e.g. "apps/web"
  "preview": { "setupFile": "./veect.preview.tsx", "css": [] },
  "tokens": { "tailwind": true, "cssVars": ["src/styles/tokens.css"] },
  "output": {
    "dir": "veect-designs",
    "componentImportStyle": "alias-or-relative",
  },
  "git": {
    "baseBranch": "auto", // "auto" = origin default
    "branchPrefix": "veect/",
    "autoCheckpoint": true, // commit on explicit Save/Export actions
    "autoPublish": "remote-only", // push veect/<slug> after checkpoint: "remote-only" | "always" | "never"
    "verifyHooks": false, // commit with --no-verify by default
  },
  "ai": {
    "provider": "anthropic",
    "model": "claude-sonnet-latest",
    "maxTokensPerRequest": 8192,
  },
}
```

Notes: `veect.preview.tsx` (decorators/global CSS) remains a **code** file, hand-authored — the wizard scaffolds a stub. **AI key is NOT in config** (it's a secret): sources are the app's Settings (stored via OS `safeStorage`) or `ANTHROPIC_API_KEY` env — never config, never the repo, never SQLite. **Config resolution:** committed file in the repo wins; if the repo has none, the workspace-local `config.json` (written by the wizard) applies, and the app offers to include `veect.config.json` in the project's first published branch — so the config enters the repo through an ordinary PR, and `main` is never touched by Veect directly.

---

## 6. Repository connections (new in rev 1.2)

**Modes:** `remote` (default) | `local`.

**Remote providers, v1:**

- **GitHub — OAuth Device Flow** (client_id only; no secret shippable in a desktop app): user gets a code, approves in browser, app receives a token. Requested scope: `repo` (read/write to chosen repos). Fine-grained PAT paste is the equal-class fallback.
- **Any git URL** — HTTPS with PAT/basic (GitLab, Bitbucket, Gitea, Azure DevOps, self-hosted) or **SSH via the system agent** (the app never reads or stores private keys; if no agent, instruct — a Veect-generated deploy-key flow is fast-follow).
- Native GitLab/Bitbucket OAuth: fast-follow; the URL path covers them day one.

**Credential storage:** OS keychain via Electron `safeStorage` (Docker: `VEECT_TOKEN` + provider tokens via env or the encrypted store on `/data`). Secrets never in SQLite, never in config, never logged; git operations receive credentials via an **ephemeral credential-helper** (`git -c credential.helper=...` per invocation), never embedded in remote URLs (URLs with tokens leak into `.git/config` and process lists).

**Clone strategy:** partial clone `--filter=blob:none` (fast on big repos; fully compatible with worktrees), default branch checked out; `fetch` on workspace open and on a visible "refresh" action; staleness (commits behind origin/default) surfaced per project with **Update from base** (merge, never rebase; conflicts read-only with "ask a developer" guidance — rev 1.1 rules stand).

**Access levels:** write access → full flow. **Read-only connect is allowed**: design, checkpoint locally; Publish disabled with an explanatory state ("ask for write access, or export a patch") — `git format-patch` export is the escape hatch.

**First run in an unconfigured repo:** visual wizard — browse the repo tree, multi-select component folders, pick `appDir` (monorepo), framework/tokens autodetected and shown for confirmation → writes config per §5 resolution rules.

**Dependency install:** on clone and on lockfile change, the app runs the detected package manager using the bundled runtime (npm built-in; pnpm/yarn via bundled corepack; bun = system-only best effort). Progress is first-class UI (real repos take minutes cold). **Worktrees always get their own install** (they live outside the repo dir, so ancestor `node_modules` resolution does not apply — accepted cost; pnpm's content-addressed store makes it cheap, npm/yarn are slower and show honest progress).

**Environment manager (per workspace):** real apps crash without `DATABASE_URL`. The app detects env needs from **`.env.example`, `turbo.json` `globalEnv`, t3-env schemas (`src/env.ts` — pervasive in exactly this Next.js audience), and `next.config` env keys**, presents a form, stores values OS-encrypted (`env.enc`), and **injects them into adapter AND package-manager processes** (private registries: `.npmrc` with `${NPM_TOKEN}` needs it at install time; `~/.npmrc` precedence documented) — values never touch the clone, worktrees, or git. Injected process env correctly wins over Next's on-disk `.env` loading (Next does not overwrite existing vars). Local mode additionally offers one-click import from the checkout's own gitignored `.env*` files (with a staleness note when the source file changes). Doctor's crash-sniffing playbook stays. **This ships in M3** (local mode already needs it — worktrees never contain gitignored `.env.local`).

**Connection realities (rev 1.2.1, from adversarial review):**

- **Trust model (the one new attack surface).** Cloning a repo and running its package manager + dev server = executing that repo's code inside Veect's runtime, with injected env readable. First connect shows a **workspace-trust prompt** (VS Code-style) stating exactly this; per-repo **"install without scripts"** (`--ignore-scripts`) opt-in; threat model stated in §16. Doctor surfaces `safeStorage`'s backend — on Linux without a keyring it falls back to weak protection (`basic_text`), and the UI says so rather than implying encryption.
- **GitHub org walls.** OAuth `repo` scope hits third-party-app restrictions and SAML SSO: auth "succeeds," then the org repo 404s until approved. Post-auth, Veect probes repo visibility and turns the 404 into the org-grant/SSO-authorize guidance flow — never a dead end. The **GitHub App** model (per-repo install, no OAuth wall; 8h tokens + refresh handled) is the fast-follow auth upgrade.
- **Write-permission probe at connect** (provider API or `push --dry-run` to a throwaway ref) — Publish gating is known on day one, not discovered as a 403 after a week of design work.
- **SSH from a GUI app:** `GIT_SSH_COMMAND` with `BatchMode=yes` + explicit known-hosts policy (fingerprint-confirm UI, `accept-new` thereafter — first clone must never hang on a TTY prompt). macOS GUI apps often don't inherit `SSH_AUTH_SOCK` (launchd) — doctor distinguishes "no agent reachable" from "agent has no identities" and shows the fix.
- **Credential-helper mechanics, exactly:** every git invocation gets `-c credential.helper=` (blank — suppresses OS keychain helpers that would answer or cache) followed by `-c credential.helper=<veect-helper>`; the secret reaches the helper via fd/env, never argv. **The helper rides ALL git invocations** — partial clone lazily fetches blobs during `worktree add`/checkout/log, not just clone/fetch/push. (Repo tooling running bare `git` inside a worktree can hit an unauthenticated lazy fetch — documented limitation + doctor hint.)
- **git-lfs:** design-adjacent repos are exactly the LFS population, and lfs isn't bundled with macOS git — Veect detects `.gitattributes` LFS filters, doctor warns with install guidance, and tests assert credential propagation into LFS transfers (`GIT_CONFIG_PARAMETERS` carries `-c` into filters).
- **Submodules: unsupported in v1** (doctor detects and says so plainly).
- **Install strategy (kills the tens-of-GB npm case):** the workspace clone installs once; each worktree's `node_modules` is **CoW/hardlink-cloned** from it (APFS `clonefile` / Windows ReFS / Linux reflink; fresh-install fallback on plain filesystems or lockfile drift). No redundant double-install.

---

## 7. Component & token discovery — unchanged from rev 1.1

Scan the active project's **worktree**; zero-project state scans the clone's default branch read-only. Shared TS LanguageService per worktree; `react-docgen-typescript` + propFilter stripping node_modules-inherited props; per-file failure isolation; content-hash cache; budgets **cold < 10s / warm < 2s on `perf-500`**. `ComponentMeta`/`TokenRegistry` shapes, client/server classification with self-healing, Tailwind v3/v4 token reading, watch → `registry.updated`, HMR budget **< 500ms** for worktree-local edits — all stand. (_Live-sync covers the worktree; developer changes on `main` arrive via Update-from-base. Do not claim otherwise in product copy._)

## 8. Design document model — unchanged from rev 1.1

`DesignDoc v1` (one design = one folder = `design.veect.json` + generated TSX), Frame/Element/Text nodes, `lit | token | expr` prop values (`expr` renders a placeholder chip on canvas — never evaluated — and emits verbatim with a `// veect:expr` review marker), unknown-component loud error states, in-memory undo + durable revisions.

## 9. Preview & rendering — framework adapters (rev 1.1 rules stand, two amendments)

Dedicated-origin adapters (vite-react embedded Vite; nextjs managed `next dev` with **materialized, never-committed** `app/__veect__/**` via `.git/worktrees/<name>/info/exclude`; split lazy client/server registries with error boundaries; root-layout inheritance documented; middleware doctor check; protocol messages incl. `runtime.ready` queue-drain and `fonts.loaded`-gated measurement; LRU pool ≤ 2; crash auto-restart with backoff).
**Amendments:** (a) all adapter child processes are spawned with the **bundled runtime** (`ELECTRON_RUN_AS_NODE`), never the system Node; (b) env injection per §6 environment manager.

## 10. Git engine (rev 1.1 rules + publishing)

Execa around **system git**, array-args + `--` end-of-options, slugs `^[a-z0-9][a-z0-9-]{0,40}$`.

**Create project:** `git worktree add <appdata>/workspaces/<ws>/worktrees/<slug> -b veect/<slug> <base> --` → env wiring → per-worktree install → materialize `__veect__` (Next mode) → initial commit.

**Checkpoint transaction (unchanged):** validate → codegen to temp → prettier → LanguageService diagnostics on generated files (<2s; full `tsc` only in `veect export`) → parity check → atomic move → `git add <explicit paths>` → `git commit --no-verify` (`veect(<slug>): <design> — <summary>` + `Veect-Design:` trailer). Any failure: nothing moves, visible dirty state, idempotent retry.

**Committer identity (rev 1.2.1):** fresh designer machines have no `user.name`/`user.email` — the first commit would fail with git's "tell me who you are." Commits run with `-c user.name`/`-c user.email` sourced from the provider profile (GitHub login + noreply email) or a one-time in-app prompt; never written to the user's global gitconfig. Part of M3 acceptance on a fresh machine.

**Config carry rule (rev 1.2.1):** `veect.config.json` + the Tailwind content registration ride **every project branch as byte-identical content until Veect detects them merged into base** — identical bytes make cross-branch merges resolve cleanly, and any branch's PR can be the one that lands them. (A dedicated one-time `veect/setup` PR is the documented alternative for teams that prefer it.)

**Publish failure classes (rev 1.2.1, enumerated playbooks):** protected branches / rulesets restricting branch-creation patterns (`veect/*` may be disallowed — configurable `branchPrefix` is the fix) · **required signed commits** (Veect commits unsigned in v1 — documented; commit signing is a fast-follow) · pre-receive commit-message policies (conventional-commits rejects `veect(<slug>):` — `git.commitMessageTemplate` config is the escape hatch) · no write access (caught early by the connect-time probe; patch-export escape hatch).

**Publish (new):** `git push -u origin veect/<slug> --` after checkpoint, per `git.autoPublish` (`remote-only` default: auto in remote mode, manual button in local mode). After publish the UI surfaces the **compare/PR deep link** (`https://<host>/<owner>/<repo>/compare/<base>...veect/<slug>?expand=1` and GitLab/Bitbucket equivalents) — zero provider API needed in v1; native "Create PR" via API is fast-follow.

**Safety invariants (updated, test-enforced):** all writes inside the workspace dir (path assertion on every fs/git call) · the user's checkout (local mode) is never modified — no checkouts, stashes, index changes, not even `.gitignore` edits · **never push to the base/default branch** · never force-push · never delete remote branches · credentials only via ephemeral helper · branch collision → reattach or new slug · archive = `git worktree remove` (+ optional `push` of final state first), branch kept · hooks bypassed by default (`verifyHooks`).

## 11. Persistence — unchanged from rev 1.1, relocated

SQLite per workspace at `<appdata>/workspaces/<ws>/veect.db` (WAL + FS probe → TRUNCATE fallback; app-data dirs make network-FS problems rare). Tables as rev 1.1 (projects +`behind_base`, designs with `json_path/json_hash`, revisions ring-buffer, `ai_runs`, settings incl. adapter ports). Division of truth stands: SQLite = working state/history/provenance; `design.veect.json` in the branch = canonical/shareable; **file-wins reconciliation** with restore-revision safety net; no merging in v1.

## 12. Code generation — unchanged from rev 1.1

Deterministic TSX per design; imports via tsconfig alias-else-relative; frames → Tailwind classes when detected (content registration travels with the first published branch) else typed styles; autogenerated banner + **code-owned flip** on hand-edit (never overwrite humans); "Export as page" for Next routes; quality gates split checkpoint (fast diagnostics + parity) vs export (full `tsc` + eslint).

## 13. Server & API

Hono; desktop: bound to `127.0.0.1`, the shell's window is the only client. Docker: binds `0.0.0.0` with **mandatory token auth** + Host-header validation + strict CORS/WS-origin checks (rev 1.1 DNS-rebinding rules apply everywhere). REST/WS surface as rev 1.1, plus: `POST /api/connections` (provider flows), `GET /api/workspaces`, `POST /api/projects/:slug/publish`, `GET /api/env/template` · `PUT /api/env` (values write-only — the API never returns secrets), `project.staleness` + `publish.status` WS events.

**Docker realities (rev 1.2.1):** the image sets `safe.directory=*` (via `GIT_CONFIG_GLOBAL`) and documents the uid/volume contract (`--user $(id -u)` or chown guidance) — otherwise git ≥2.35.2 "dubious ownership" breaks the flagship CI one-liner on bind mounts. Headless secret store: `safeStorage` doesn't exist without a desktop — env values are encrypted **AES-GCM with a key derived (scrypt) from `VEECT_SECRET`** (falls back to the boot token with a logged warning); the secret-store abstraction lives in server core, the shell merely provides the OS backend. **Multi-seat honesty:** one token = one principal — every publish authors as whoever's credentials are stored, and the env store is shared; the self-host guide says so and recommends a machine account with narrow scopes. Device flow inside a container: the code+URL are printed/displayed for approval on any browser (no in-container browser needed).

## 14. AI compose — unchanged from rev 1.1, key handling amended

Context assembly with deterministic lexical relevance + always-include set; **"what was sent" disclosure**; tool-forced JSON Node subtree; ≤2 repair rounds; graceful-refusal contract; ghost-layer propose/accept; provenance to `ai_runs`; CI eval via record/replay (0 unregistered elements; refusals fire). **Key sources:** app Settings (safeStorage) or env — never config/repo/DB plaintext; no key → documented setup-hint state.

## 15. Testing & CI (rev 1.1 + connection layer)

Fixtures unchanged (`vite-kitchen`, `next-app-kitchen` incl. middleware + redirecting root layout, `pnpm-mono`, `perf-500`, `next-pages-kitchen` M8-lite). **Added suites:** connections (device-flow mock server; PAT/SSH against a local `git daemon`/`sshd` fixture; partial-clone + worktree matrix; read-only mode; publish + collision/permission failures; credential-helper never leaks tokens into `.git/config`/argv — asserted), env manager (injection, never-on-disk-in-clone), shell smoke (app boots on macOS/Windows/Linux CI runners, spawns adapter via bundled runtime with system Node **absent** from PATH — the A5 test), Docker (token auth enforced; export-in-CI path). Windows runs the full git/worktree/connection suite.

## 16. Performance, security, DX budgets

| Area                                                             | Budget / rule                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| ---------------------------------------------------------------- |   |
| App cold start → connect screen                                  | < 3s                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| Remote connect → canvas ready (vite fixture, warm npm cache)     | < 3 min incl. clone + install (progress always visible; Next fixture bounded by `next dev`)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| Canvas manipulation / server-component settle / HMR / checkpoint | rev 1.1 budgets stand (16ms · <1s · <500ms · <5s)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| Discovery                                                        | cold <10s / warm <2s on perf-500                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| Disk                                                             | per-workspace usage shown; archive/cleanup UI; warn > 5GB/workspace                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| Security                                                         | desktop server localhost-only; Docker token-auth mandatory; credentials via safeStorage/keychain + ephemeral git helper (all invocations); env values encrypted at rest, injected only into adapter/PM processes; **workspace-trust prompt before first install/run — executing repo code is the stated, accepted surface**; Linux `basic_text` keyring fallback surfaced to the user; **`runAsNode` fuse enabled by design (accepted LOLBin/EDR risk)**; AI payload = §14 disclosure; secrets never in config/DB/logs/URLs/argv; `__veect__` never committed; no telemetry v1 |
| Failure UX                                                       | every error carries a fix; doctor playbooks add: git missing, device-flow blocked (enterprise SSO), SSH agent absent, clone auth failed, install failed (registry/proxy), env missing, disk full                                                                                                                                                                                                                                                                                                                                                                               |

## 17. Milestones & acceptance (resequenced for rev 1.2)

**M0 — Walking skeleton (local mode, both adapters, minimal worktree).** Shell boots on all three OSes with **no system Node** (PATH shim active); open `vite-kitchen` AND `next-app-kitchen` as local folders; **minimal worktree creation ships in M0** — the Next adapter materializes `app/__veect__/**` only ever inside a worktree, never the user's checkout (resolves the materialization/"repo untouched" contradiction); one real component renders through each adapter; protocol round-trip.
_Accept:_ fresh machine (git present, Node absent from PATH) → open app → local fixture → rendered component < 60s; `git status` in the fixture checkout is untouched; a fixture postinstall that invokes `node` succeeds via the shim.

**M1 — Discovery & registry + config.** Full scan, props/tokens panels, watch/HMR, split lazy registries, classification self-heal, perf-500 budgets; JSON config load/validate + **first-run wizard writes it** (workspace-local, offer-to-commit).

**M2 — Canvas + doc + persistence.** Rev 1.1 M2 unchanged (SQLite in app data; JSON write-through + file-wins reconciliation).

**M3 — Worktrees + codegen + checkpoint (local complete).** Rev 1.1 M3, with worktrees in app data + CoW-derived installs; code-owned flip; update-from-base; **committer-identity flow**; **env manager core** (local `.env*` import + injection — real Next apps need it here, not M4).
_Accept (additions):_ first checkpoint succeeds on a fresh machine with no global gitconfig; `next-app-kitchen` with a required env var previews after env import.

**M4 — Remote repositories + publish.** Device flow (incl. org-restriction guidance), PAT/SSH (BatchMode + known-hosts UI), partial clone, trust prompt, install pipeline (vendored PMs, ABI policy), env manager remote polish, read-only mode, connect-time write probe, publish + compare-link, failure-class playbooks.
_Accept:_ on a Node-less machine, connect a **private** GitHub repo via device code → wizard → compose → checkpoint → **Publish** → the compare URL opens a reviewable PR containing the carry set per §10 (byte-identical `veect.config.json` + Tailwind registration, `design.veect.json`, generated TSX); token never appears in any file, log, argv, or `.git/config`.

**M5 — Next.js depth.** Rev 1.1 M4 unchanged (server-component subtrees + refresh, root-layout/middleware handling, `next/font` fidelity, adapter pool/crash recovery).

**M6 — AI compose.** Rev 1.1 M5 unchanged + key-in-Settings flow.

**M7 — Docker + hardening.** Docker image (token auth, volumes, CI export path), doctor complete, Windows pass, pages-router best-effort, docs (connect-your-repo guide, env guide, self-host guide).
_Accept:_ full matrix green; a designer following only the download-page instructions reaches first published branch on their own team's repo.

## 18. Decision log

| Decision                                                                                          | Status                                                                             |
| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| **Standalone app default + Docker additional; npm-in-repo package deferred**                      | ✅ locked (founder, 2026-07-10) — register npm `veect` now regardless              |
| **Remote-first repo connection (GitHub device flow / git URL / SSH) with local mode**             | ✅ locked (founder, 2026-07-10)                                                    |
| **Config = `veect.config.json`** (schema-validated, app-writable; `veect.preview.tsx` stays code) | ✅ locked (founder, 2026-07-10)                                                    |
| Electron shell with bundled Node (`ELECTRON_RUN_AS_NODE`); Tauri+sidecar = documented alternative | 🟡 architect's call — UI is shell-agnostic web; revisit on binary-size evidence    |
| Worktrees + all Veect state in app data, **user's repo never modified** (kills rev 1.1 shields)   | ✅ rev 1.2 — cost: per-worktree installs (pnpm cheap, npm slower, progress honest) |
| Publish in scope: push `veect/*` only; compare-link PR; provider PR-API fast-follow               | ✅ rev 1.2 (supersedes rev 1.1 "no push")                                          |
| System git required v1 (no bundled git; isomorphic-git can't do worktrees)                        | 🟡 residual A5 friction — guided install; revisit                                  |
| Docker v1 = single-team token; multi-user auth = later cloud layer                                | ✅ rev 1.2                                                                         |
| Workspace-trust prompt + per-repo `--ignore-scripts` opt-in; repo-code execution = stated surface | ✅ rev 1.2.1                                                                       |
| `node` PATH shim + `npm_config_runtime=electron` ABI policy + vendored PMs                        | ✅ rev 1.2.1                                                                       |
| Worktree installs derived via CoW/hardlink from the workspace clone                               | ✅ rev 1.2.1                                                                       |
| GitHub App auth (per-repo install, refresh tokens)                                                | fast-follow — device flow + PAT/SSH ship v1                                        |
| Commit signing; provider PR-API; GitLab/Bitbucket native OAuth                                    | fast-follow                                                                        |
| AI in v1 (key via Settings/env) · full Next adapter · SQLite+JSON-in-branch · name Veect          | ✅ carried forward                                                                 |
| License / open-core split                                                                         | strategy, not blocking 🔴 — build source-available                                 |
| Design-doc concurrent merge                                                                       | v1 = file-wins + revision net; deferred                                            |

---

_End of spec (rev 1.2). The editor's visual/interaction contract is `design-brief-veect.md` (see its Connect Repository and Environment screens); the founder's React prototype seeds `packages/editor`; this document is authoritative for everything behind it._
