# Project Onboarding and GOLIVE Setup

audience: AI coding agents first.

## Delivery scope

This request ends after reviewed design and implementation plan documents are written. MUST NOT launch Factory, agents, implementation, deployment, or landing from this request.

## Scope amendment

This is post-v1 work and intentionally amends `GOLIVE.md` line 36, which excluded editing GOLIVE files from v1 UI. Owner approved this amendment through the 2026-08-13 feature request; implementation MUST NOT ask again.

## Outcome

Add repositories from `/projects`. Missing `GOLIVE.md` MUST NOT block project registration. Project appears normally; GOLIVE card shows `Setup`. User may postpone or launch durable agent-driven setup with CLI, account, model, and effort selection.

## Decisions

- Use existing Factory orchestration, actions, decisions, run state, trace, and agent observability. NEVER build parallel orchestration or observability.
- Register project before GOLIVE setup. Missing GOLIVE is valid `setup-required`, not adapter failure.
- Reopen same onboarding modal from GOLIVE card `Setup` CTA.
- Default every recommended criterion checked. User may deselect before canonical write.
- Reviewer may auto-fix defects only within user-selected criteria. Reviewer MUST NOT add or remove criteria silently.
- Activate automatically after review passes: install canonical `GOLIVE.md` and checker atomically, refresh collector, replace progress with real GOLIVE results.

## Canonical GOLIVE contract

Version 1 document MUST contain:

- `# GOLIVE — <project name>` heading;
- `## Acceptance criteria (finite, testable, user-visible)` section;
- one or more criteria using `- [ ] AC-<positive integer>: <non-empty text>` or checked equivalent;
- unique stable criterion IDs; no duplicate IDs; maximum 100 criteria; maximum 500 UTF-8 bytes per criterion; maximum document size 64 KiB.

Checker contract:

```text
<repo>/.overdeck/golive/check
input: no arguments; cwd = canonical repository root
stdout: one JSON object per selected criterion
record: { "id": "AC-1", "status": "pass" | "fail" | "unknown", "summary": string, "evidence": string[] }
exit: 0 when every criterion produced a valid record; nonzero for checker failure, not criterion failure
limits: 5 minutes, 1 MiB combined output
```

Checker manifest `<repo>/.overdeck/golive/manifest.json` pins `schemaVersion: 1`, normalized selected-criteria hash, generated file inventory, checker command, and reviewer receipt. Normalization hashes ordered tuples of `{ id, title, verificationIntent }` after Unicode NFC normalization and whitespace collapse; any semantic text or ID change changes the hash. Collector MUST treat unsupported versions, malformed documents, missing records, duplicate records, or unknown IDs as `source-conflict`, never as a score.

Installed checker execution never occurs inside collector. Existing bounded Factory/sandbox execution runs checker after activation and on configured refresh triggers (initial activation, repository HEAD change, and manual refresh), then atomically writes `.overdeck/golive/results.json` with manifest hash, HEAD SHA, timestamp, and bounded records. Golive adapter reads only `GOLIVE.md`, manifest, and results JSON. Matching fresh results determine criterion status; missing/stale results produce `unknown` and enqueue sandbox refresh. Markdown checkbox text remains canonical user-selected intent and is never rewritten by checker output.

## Project identity and lifecycle

- `projectId` = lowercase SHA-256 of canonical realpath, prefixed `repo_`, first 24 hex characters. Same path remains stable across restarts; path moves create a new project.
- Config remains source of truth. External config edits reconcile on next load: additions appear; removals disappear after no active setup remains.
- Deleted/unreadable paths remain visible as unavailable while configured; removal is separate future scope.
- One active setup run per `projectId`. Duplicate start returns existing active `runId`; completed/failed runs require explicit retry.

## User flow

1. User clicks `Add Project` on `/projects`.
2. Modal accepts local repository path and validates:
   - absolute path;
   - existing readable directory;
   - Git repository;
   - no duplicate canonical path;
   - collector may read it.
3. Registration persists repository path in `[adapters.golive].repos` through an authenticated collector action using lock + atomic write.
4. Project appears immediately.
5. Existing valid `GOLIVE.md` → collector renders normal score.
6. Missing `GOLIVE.md` → GOLIVE card renders `Setup`, no fabricated score. Existing invalid GOLIVE/checker files → `source-conflict`; setup MUST NOT launch or overwrite them.
7. Setup modal offers:
   - `Postpone`: close; project stays registered; `Setup` remains.
   - `Launch agent`: require CLI, account, model, effort using existing selector components and registry data.
8. Factory run stages:
   - inspect repository and recommend measurable release criteria;
   - pause on durable decision containing recommendations;
   - render checkbox form, all checked initially;
   - user submits selected criteria;
   - write candidate canonical GOLIVE and construct checker;
   - independent reviewer reviews and may fix implementation defects within selected criteria;
   - run checker and contract validation;
   - atomically activate files;
   - refresh collector and verify real score.
9. While active, GOLIVE card replaces numbers with stage progress and plain-language description.
10. Clicking progress opens existing run/agent detail surface showing agents, turns, tools, logs, decisions, and errors.

## Confirmed reuse compatibility

Inspection on 2026-08-13 confirms:

- `apps/web/src/lib/action-client.ts::postCollectorAction` is generic and directly supports new authenticated project verbs.
- `collector/src/actions.ts::createActionHandler` owns schema validation, allowlisted verb routing, journaling, and provider injection; project actions belong there.
- `apps/web/src/components/factory/FactoryDecisionPanel.tsx` uses the existing `factory.decision.answer` action but supports only single-choice/free-text. Reuse its transport/state treatment; extend it with a typed multi-select renderer rather than copy it.
- `apps/web/src/components/factory/FactoryRunSummary.tsx` already renders run status/timing/summary and can compose into project progress detail.
- `apps/web/src/components/plans/AgentApp.tsx` and `PlanRunApp.tsx` already fetch run events, decisions, details, agent turns, and render `AgentFeed`/`SwimlaneTrace`; project progress MUST link to these routes, not embed duplicate timelines.
- Factory page has stop and decision actions but no generic start UI/action suitable for this workflow. Add a bounded `project.golive.setup.start` provider that launches the named workflow through canonical Factory launcher.
- CLI/account/model/effort selectors already exist in `apps/web/src/components/incidents/FileIncidentForm.tsx` using authority functions from `incident-view.ts`; extract/reuse that authority-backed selector composition. Factory metadata is display-only and is not a selector source.
- `collector/src/config.ts` has TOML-preserving locked atomic persistence only for project colors; generalize the narrow patch transaction for golive repos without rewriting unrelated keys.
- `collector/src/adapters/golive.ts::createGoliveAdapter` already accepts configured repositories and parses/scorers Markdown. Extend output/state reading; never execute checker there.

## Reuse map

### UI

- Compose modal fields from existing `@overdeck/deck-ui` controls and platform primitives.
- Reuse Factory CLI/account/model/effort selection source and components; extract shared composition only if currently private to Factory page.
- Reuse `FactoryDecisionPanel` decision transport. Add checkbox rendering for typed multi-select criteria; do NOT create a separate decision store.
- Reuse Factory run status mapping and progress data.
- Reuse compatibility gate: implementation MUST inspect `FactoryContent`, `FactoryDecisionPanel`, `FactoryRunSummary`, `AgentApp`, `PlanRunApp`, `postCollectorAction`, collector action registry, selector registry, and Factory trace schemas before extraction. Extend existing typed contracts where compatible; do not copy component bodies or fork event shapes.
- Reuse `AgentFeed`, `AgentStatusBar`, `SwimlaneTrace`, `DetailDrawer`, and current run/agent routes for drill-down.
- Reuse `SectionCard`, `Button`, `Checkbox`, `Select`, `StatusChip`, `DataCoveragePanel`, and tooltip primitives.
- Register any approved new deck-ui composition in gallery. Prefer app-level project composition; no new generic primitive expected.

### Backend

- Reuse same-origin collector action proxy and authenticated `/actions/:verb` dispatch.
- Reuse collector target locking and atomic-write utilities used by project-color persistence.
- Reuse `ConfigSchema` and TOML-preserving patch strategy; extend persistence for `adapters.golive.repos` without rewriting unrelated config.
- Reuse Golive parser/scorer. Extend adapter output with explicit setup state for configured repositories lacking valid source.
- Reuse Factory launch/decision/stop contracts and trace database. Add GOLIVE workflow prompt/contract, not a second runner.

## Contracts

### Project registration actions

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

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

`project.add` MUST canonicalize through filesystem realpath, reject duplicates after canonicalization, lock config target, patch only `adapters.golive.repos`, validate resulting TOML/schema, and atomically replace source.

### 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 }
```

Server MUST validate selector combination against existing installed/allowed registry. Client values are never authority.

### 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[];
}
```

Submit through existing Factory decision-answer action with `{ runId, decisionId, recommendationHash, selectedIds }`. Server MUST reject empty selection, more than 100 selections, duplicate/unknown IDs, settled decision replay, stale hash/version, and strings above contract bounds. Recommendation IDs and hash become immutable after presentation.

Factory decision payload MUST be typed and bounded. UI initializes `selectedIds` to every recommendation only on first display; settled decisions render immutable selected criteria.

### Progress

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

Progress derives from admitted Factory events, never elapsed-time guesses. Percent uses completed known stages; awaiting user remains stationary and labeled `Waiting for your criteria selection`. Failure shows last successful stage plus actionable failure reason.

## Trust boundaries and execution isolation

- Repository paths and contents are untrusted. Allow registration only under configured onboarding roots; default root is `~/Projects`. Server opens canonical repository directory once and pins its device/inode/owner identity for each operation; all descendant access uses beneath/no-symlink resolution (`openat2` semantics or existing fail-closed equivalent) relative to that handle. Clients never supply an authoritative write path.
- Revalidate configured-root membership and pinned identity before each operation. Descendant creation/rename MUST use directory-handle-relative race-safe operations; reject unsupported platforms rather than fall back to check-then-write, symlink escapes, path replacement, ownership changes, special files, and non-owner-writable targets.
- Repository instructions and source are prompt-injection input. Agent policy may follow repository build/test conventions only; it MUST NOT change credentials, host configuration, Overdeck configuration, git remotes, hooks, permissions, or files outside candidate-owned paths. Server-side sandbox enforces this; prompts alone are insufficient.
- Builder and reviewer run in isolated Factory seats with candidate workspace only, stripped secrets, explicit environment allowlist, no host home mounts, no network by default, bounded CPU/memory/process count, 30-minute phase timeout, and 10 MiB log cap. Network may be enabled only through existing Factory policy when repository verification already requires it.
- Store/display recommendation evidence, diffs, logs, checker output, and errors only after existing redaction; cap each displayed/stored field and retain full bounded artifacts through existing trace artifact storage.
- Reviewer MUST use a separate agent context and read-only access except candidate-owned paths. Admission compares pre/post semantic criterion IDs and selected hash; any addition/removal fails review regardless of reviewer verdict.

## Activation transaction

Installed owned paths are `GOLIVE.md` and `.overdeck/golive/**`. Transaction metadata lives under `.overdeck/.golive-transactions/<runId>/{journal.json,stage/}` and is removed after settlement. Activation is crash-consistent, not globally atomic. `GOLIVE.md` is final commit marker; adapter ignores staged/installed checker data while transaction journal exists or `GOLIVE.md` is absent.

1. Acquire per-project activation lock and revalidate repository identity/trust boundary through pinned directory handle.
2. Refuse dirty/conflicting owned paths, existing owned files, or active concurrent setup.
3. Validate candidate contract and checker in sandbox.
4. Copy candidate files into same-filesystem staging directory under `.overdeck/.golive-stage-<runId>`; fsync files and directory.
5. Install `.overdeck/golive` by atomic rename, then install `GOLIVE.md` by atomic rename.
6. Persist activation journal before each rename. Crash recovery completes the second rename only when manifest/hash and repository identity still match; otherwise removes newly installed owned paths and reports failure.
7. On any synchronous failure, remove only paths installed by this run using recorded hashes. Never touch pre-existing files.
8. Remove journal/staging after collector refresh receipt.

Dirty changes outside owned paths do not block activation. Dirty or untracked owned paths block it. Cancellation before first rename aborts; cancellation after activation begins waits for transaction completion, then reports final state.

## Candidate and activation safety

- Agents MUST write to isolated candidate workspace inside project worktree or Factory candidate path.
- Candidate includes canonical `GOLIVE.md`, checker files, and tests required by repository conventions.
- Selected criteria IDs form immutable review boundary.
- Reviewer gets candidate diff, selected criteria, repository instructions, and checker output.
- Reviewer may fix correctness/security/test defects. Reviewer MUST reject semantic additions/removals and return to criteria decision if criteria must change.
- Activation MUST be atomic and path-confined to target repository.
- One active setup per project; duplicate starts return existing run. Decision answers are single-use and hash-bound. Retries create a new run linked to prior evidence; never reuse settled decisions or candidates.
- Existing `GOLIVE.md` or checker files MUST NOT be overwritten by missing-GOLIVE flow. Existing invalid files enter `source-conflict` immediately and leave files untouched.
- Failed/cancelled runs leave project registered and `Setup` available. Candidate artifacts remain linked from run evidence but never activate.

## Honest UI states

- `ready`: real score and source freshness.
- `setup-required`: no number; `GOLIVE is not set up` + `Setup` CTA.
- `setup-running`: progress description replaces numbers; click opens run detail.
- `awaiting-criteria`: progress shows waiting state; CTA opens checkbox decision.
- `setup-failed`: failure summary + `Retry setup` + run details.
- `source-conflict`: existing invalid/colliding files; no write; explain conflict.
- Collector unavailable: registration/setup actions disabled with existing query error treatment.

## Failure handling

- Config lock contention → bounded retry then visible failure; no lost update.
- Agent/Factory launch failure → project remains registered; setup becomes failed/retryable.
- Browser refresh → reconstruct modal/run state from collector + Factory trace.
- User postpones or closes modal → never cancel active run implicitly. Active run continues and card shows progress.
- User explicitly cancels → reuse Factory stop action; candidate never activates.
- Reviewer/checker failure → no activation; retain evidence and retry path.
- Collector refresh failure after activation → files remain canonical; status reports refresh failure and retries through existing adapter polling.

## Testing

- Config persistence: canonicalization, allowed-root enforcement, duplicate paths, concurrent writers, comments/order preservation, malformed TOML, unreadable/non-Git paths, symlink swap, path replacement, ownership change.
- Golive adapter: missing source emits setup-required project; no fabricated metrics; valid source unchanged.
- Actions: auth, schema bounds, selector allowlist, path confinement, launch/refresh failures.
- UI: Add Project visibility; postpone; Setup reopen; selector validation; all recommendations default checked; deselection submission; refresh reconstruction; each progress/failure state; drill-down links existing run/agent surface.
- Workflow contract: one active run/idempotent duplicate start, stale/replayed decisions rejected, criteria boundary, candidate-only writes, malicious checker containment, reviewer isolated auto-fix within boundary, cancellation before/during activation, crash recovery between renames, dirty owned paths block activation, failed validation blocks activation, successful transaction and collector refresh.
- Existing deck-ui tests/typecheck, web build/typecheck, collector tests, Factory suite, and browser journey MUST pass cleanly.
- Browser journey MUST use remote E2E wrapper and prove `/projects` add → postpone → reopen → agent setup → decision → completion → real score.

## Architecture Decisions

- Keep orchestration in Factory: passes deletion test; removing it would scatter durable state, recovery, decisions, and traces across UI/collector.
- Keep project registration persistence in collector: collector owns config schema and atomic persistence.
- Keep onboarding UI composition in `apps/web`: one product consumer; no premature generic deck-ui workflow primitive.
- Reuse existing observability surfaces by stable IDs; do NOT duplicate agent timelines inside modal.
- Collapse separate onboarding coordinator proposal: decorative single-adapter boundary duplicating Factory.
