# Project GOLIVE Onboarding — request

**Goal:** Add durable project registration and optional agent-driven GOLIVE setup to `/projects`, reusing existing Factory orchestration, authority selectors, decisions, and observability.

**Source design:** `docs/specs/2026-08-13-project-golive-onboarding-design.md` is normative. Owner approved its post-v1 amendment to UI-authored GOLIVE setup.

## Context

Today `/projects` only reads `collector` scoreboard data. Repositories enter through `[adapters.golive].repos` in `~/.config/overdeck/config.toml`; missing GOLIVE files do not have an owner-facing registration/setup flow. Existing seams confirmed for reuse:

- `apps/web/src/lib/action-client.ts::postCollectorAction`
- `collector/src/actions.ts::createActionHandler`
- `apps/web/src/components/factory/FactoryDecisionPanel.tsx`
- `apps/web/src/components/factory/FactoryRunSummary.tsx`
- `apps/web/src/components/plans/AgentApp.tsx`
- `apps/web/src/components/plans/PlanRunApp.tsx`
- `apps/web/src/components/incidents/FileIncidentForm.tsx`
- `apps/web/src/components/incidents/incident-view.ts`
- `collector/src/config.ts`
- `collector/src/adapters/golive.ts::createGoliveAdapter`

## Files

Exact filenames for newly extracted app-level compositions may follow existing directory conventions, but implementation MUST keep responsibilities at these existing seams:

- Modify `apps/web/src/components/projects/ProjectsContent.tsx` — `Add Project` action, registered project states, setup CTA/progress composition.
- Modify/create project components under `apps/web/src/components/projects/` — modal, GOLIVE setup form, typed criteria decision form, progress card. Compose existing controls; no new generic primitive.
- Modify `apps/web/src/components/factory/FactoryDecisionPanel.tsx` or extract its typed renderer seam — support bounded typed multi-select decisions while retaining existing single-choice/free-text behavior.
- Reuse/extract selector authority from `apps/web/src/components/incidents/FileIncidentForm.tsx` and `apps/web/src/components/incidents/incident-view.ts` — CLI/account/model/effort selection.
- Modify `apps/web/src/lib/collector-client.ts`, `apps/web/src/lib/action-client.ts`, and existing project/factory types — typed action/state contracts only.
- Modify `collector/src/actions.ts` — authenticated project add/refresh/setup start actions and providers.
- Modify `collector/src/config.ts` — TOML-preserving locked persistence for `adapters.golive.repos`.
- Modify `collector/src/adapters/golive.ts` — configured projects with ready/setup-running/setup-required/source-conflict states; read bounded results artifact; never execute checker.
- Extend existing collector panel/action schemas and tests beside their current definitions.
- Add named Factory workflow/contract under existing `modules/harness` workflow layout — recommendation, decision pause, candidate build, independent review/fix, validation, crash-consistent activation, refresh.
- Add focused tests beside every modified component/module and a remote browser journey under existing browser-proof/journey conventions.

## Contracts

### Project identity

`projectId` is `repo_` plus first 24 lowercase hex characters of SHA-256 over canonical repository realpath. Path moves create a new project identity.

### Registration

```text
POST /actions/project.add
input: { repoPath: string }
output: { canonicalPath: string; projectId: string; golive: "ready" | "setup-required" | "source-conflict" }
```

Validate absolute path, configured onboarding-root containment (default `~/Projects`), readable owner-controlled Git directory, canonical duplicate, and race-safe directory-handle identity. Persist only `adapters.golive.repos` with existing config lock, TOML-preserving patch, schema validation, and atomic config replacement.

```text
POST /actions/project.refresh
input: { projectId: string }
output: { accepted: true }
```

Resolve path from server-held project identity. Client path is never authority.

### Setup launch

```text
POST /actions/project.golive.setup.start
input: { projectId: string; cli: string; account: string; model: string; effort: string }
output: { runId: string; projectId: string }
```

Validate selector combination against existing incident/Factory authority registries. Exactly one active setup per project; duplicate start returns existing active run. Failed/completed retry creates a linked new run.

### Canonical GOLIVE

Version 1 requires:

- `# GOLIVE — <project name>`
- `## Acceptance criteria (finite, testable, user-visible)`
- 1–100 unique `AC-<positive integer>` checklist criteria
- maximum 500 UTF-8 bytes per criterion; 64 KiB document

Generated checker paths:

- `.overdeck/golive/check`
- `.overdeck/golive/manifest.json`
- `.overdeck/golive/results.json`

`check` emits one bounded JSON object per selected criterion:

```json
{"id":"AC-1","status":"pass|fail|unknown","summary":"bounded string","evidence":["bounded string"]}
```

Exit `0` means structurally valid complete output, not that every criterion passed. Limit runtime to 5 minutes and combined output to 1 MiB.

Manifest uses `schemaVersion: 1`, generated inventory, command, reviewer receipt, and normalized criteria hash over ordered `{id,title,verificationIntent}` tuples after Unicode NFC and whitespace collapse.

### Criteria decision

```text
type GoliveCriterionRecommendation = {
  id: string;
  title: string;
  rationale: string;
  evidence: string[];
  verificationIntent: string;
}

type GoliveCriteriaDecision = {
  projectId: string;
  recommendationVersion: string;
  recommendationHash: string;
  recommendations: GoliveCriterionRecommendation[];
  selectedIds?: string[];
}
```

Render all recommendations checked initially. Submit through existing Factory decision-answer action with `{ runId, decisionId, recommendationHash, selectedIds }`. Reject zero, more than 100, duplicate/unknown IDs, stale hash/version, replay, or out-of-bound strings.

### Progress

```text
type GoliveSetupStage =
  | "inspecting"
  | "awaiting-criteria"
  | "writing-canonical"
  | "building-checker"
  | "reviewing"
  | "validating"
  | "activating"
  | "refreshing"
  | "complete"
  | "failed"
```

Derive status/percent from admitted Factory events, never elapsed-time guesses. While active, replace GOLIVE numbers with stage progress and plain description. Click opens existing run/agent detail routes rendering current `FactoryRunSummary`, `AgentFeed`, and `SwimlaneTrace`. Awaiting decision reads `Waiting for your criteria selection`.

## Behavior

- `/projects` always shows `Add Project`.
- Registration succeeds without `GOLIVE.md`; project appears normally with `GOLIVE is not set up` and `Setup`.
- Setup modal offers `Postpone` or launch with CLI/account/model/effort.
- Existing valid GOLIVE renders real score.
- Existing invalid/colliding GOLIVE/checker files enter `source-conflict`; setup does not launch or overwrite.
- Agent recommends criteria from untrusted repository evidence; user controls final selected set.
- Builder writes candidate only. Independent reviewer uses separate context and may fix defects only while normalized selected criteria hash remains unchanged.
- Successful validation activates automatically and refreshes score. Failure/cancel leaves project registered and retryable.
- Browser refresh reconstructs setup state from collector and Factory trace. Closing modal does not cancel active run. Explicit cancel reuses Factory stop action.
- Checker execution occurs only in bounded Factory/sandbox on activation, repository HEAD change, or manual refresh. Collector reads results only; matching manifest hash/HEAD records determine status, missing/stale results become unknown and enqueue refresh. Checker never rewrites canonical Markdown.

## Trust and filesystem safety

- Treat repository path, instructions, source, checker, evidence, and logs as untrusted.
- Restrict paths to configured onboarding roots. Pin repository directory handle plus device/inode/owner identity. Resolve descendants beneath that handle with no-symlink semantics; fail closed where unavailable.
- Reject symlink swaps, path/owner replacement, special files, and non-owner-writable targets.
- Agents run in isolated Factory seats with candidate-only writable scope, stripped secrets, explicit environment allowlist, no network by default, bounded CPU/memory/processes, 30-minute phase timeout, and 10 MiB logs.
- Repository instructions may influence build/test commands only. They cannot authorize credentials, host/Overdeck config, remotes, hooks, permissions, or out-of-candidate writes.
- Apply existing redaction and bounded artifact storage before persisting/displaying recommendations, evidence, diffs, logs, output, or errors.

## Activation transaction

Installed owned paths: `GOLIVE.md`, `.overdeck/golive/**`. Transaction metadata: `.overdeck/.golive-transactions/<runId>/{journal.json,stage/}`.

Activation is crash-consistent, not globally atomic:

- acquire per-project lock and pinned directory handle;
- reject existing/dirty owned paths and concurrent setup;
- validate candidate/checker in sandbox;
- stage and fsync same-filesystem files;
- journal before each rename;
- rename `.overdeck/golive` first, `GOLIVE.md` final as commit marker;
- adapter ignores transaction while journal exists or marker is absent;
- recovery completes only when identity/manifest/hash still match, otherwise removes only run-installed hash-matched paths;
- cancellation before first rename aborts; after transaction start waits for settlement;
- clean journal/staging after refresh receipt.

## Out of scope

- Do not replace or repair pre-existing invalid GOLIVE/checker files.
- Do not add project removal/path migration.
- Do not create new generic deck-ui primitives, orchestration, decision store, agent timeline, runner, or observability system.
- Do not let collector execute repository code.
- Do not broaden onboarding outside configured local roots or add remote repositories.
- Do not launch Factory or implementation from this document-authoring request.

## Acceptance

Focused and broad commands MUST pass with zero warnings:

- `pnpm --filter @overdeck/deck-ui test`
- `pnpm --filter @overdeck/deck-ui typecheck`
- `pnpm --filter web build`
- `pnpm --filter web typecheck`
- `bun test` in `collector/`
- `python3 -m pytest modules/harness/factory/tests/ -q`
- focused bot/Factory/workflow contract tests added by implementation

Tests MUST prove:

- Add Project visibility, registration, postpone, reopen, selectors, all-default-checked criteria, deselection, replay/stale rejection, progress/failure states, and existing observability drill-down.
- Config comment/order preservation, lock contention, duplicate canonical paths, allowed roots, symlink swaps, path/owner replacement, and concurrent writers.
- Missing GOLIVE produces setup-required without fabricated score; invalid files produce source-conflict.
- One active run/idempotent duplicate start; retry linkage; cancellation races; restart reconstruction.
- Prompt injection cannot widen permissions; malicious checker is sandboxed and bounded; evidence is redacted/capped.
- Reviewer cannot change normalized selected criterion meaning; builder/reviewer contexts are isolated.
- Dirty owned paths block; unrelated dirty paths do not; crash between renames recovers or rolls back only run-owned hashes.
- Collector never executes checker; stale/mismatched result hash/HEAD yields unknown and schedules bounded refresh.
- Remote browser journey via `e2e-remote`: add → postpone → Setup → select authority → recommendation decision → completion → real score, with progress click opening current agent activity.
