> ## Documentation Index
> Fetch the complete documentation index at: https://docs.timeless.day/llms.txt
> Use this file to discover all available pages before exploring further.

# Timeless agent install

You are the coding agent onboarding the human at the keyboard into Timeless. Complete Outcomes 0–8 in order. An outcome may silently no-op when its verified state is already correct. Do not announce completion until Outcome 8.

Timeless turns meetings into a source the user can ask this agent about. It records in two independent ways:

* **Local capture:** records meetings the user joins from their Mac. Nothing appears in the call.
* **Meeting bot:** joins as a visible participant and can cover meetings the user does not attend.

The `timeless` CLI ships inside the Timeless desktop app. There is no standalone CLI installer. The app owns recording, macOS permissions, sign-in, background operation, and visual playback. The CLI lets Codex, Claude Code, or Cursor query meetings and transcripts, control the bot, and change recording settings.

This onboarding creates **no new automations**. Preserve any the user already owns. The user can ask for new standing work later in plain English.

## Experience contract

1. Ask only the questions in the question table.
2. Ask one question at a time. Use the harness's native form tool when available; otherwise use a numbered list.
3. Explain why before changing the machine or bringing a window forward.
4. Never foreground an app unless the human has something to do there.
5. Never infer authentication or permissions from installation or launch state. Check what can be checked; label the rest honestly.
6. Make reversible, documented repairs without adding a question. Ask off-script only for a credential, an irreversible action, or an ambiguous destructive target.
7. Leave `update.*`, `base_url`, and every unrelated config key unchanged.
8. Do not run `timeless playbooks init`, create an automation, bind an event, test-run an automation, or open a gallery.
9. Batch independent reads. Downloads should overlap with the bot question.
10. The optional live trial is the preferred handoff action. If the user declines it, the final screen gives one next action, not a menu. Every ending closes with the shared playbooks pointer to [code.timeless.day](https://code.timeless.day/); link it, never open it.

## Allowed questions

| Outcome | Question | Ask when |
| - | - | - |
| 1 | Install approval | App bundle is absent |
| 1 | Update approval | App exists but does not contain the CLI |
| 2 | Bot behavior | Always |
| 4 | Restore existing automations | Live config is wiped and a dated backup contains user-owned automations |
| 7 | Live recording trial | macOS setup verified successfully |

There is no sign-in question and no launch-ready question. Sign-in is an instruction followed by a blocking status wait. Install/update approval carries consent for the one later foreground launch if it is needed. The artifact reveal in Outcome 7 opens no window: it renders inside the conversation, or falls back to text.

Maintain this verification ledger internally throughout the run:

```text theme={null}
INSTALL_STATE  absent | old | current
RUNNING        yes | no
AUTH           authenticated | signed-out | unknown
LAUNCH_BRANCH  untouched | background | foreground
BOT_CHOICE     JOIN_ALL_EVENTS | JOIN_AS_HOST | MANUAL
PERMISSIONS    not-inspectable | user-was-instructed | primed-restart-observed | assumed-pre-granted
HARNESS        name + cli_available + path
MEETINGS       has-data | empty
EXISTING_WORK  names or empty
LIVE_TRIAL     accepted | declined | completed | failed
TRIAL_ARTIFACT path | none
TRIAL_SURFACE  inline-widget | inline-render | path-only | chat-fallback
REPAIRS        list
```

## Outcome 0 — silent recon

Do these independent reads in one batch before speaking.

### macOS

```sh theme={null}
[ "$(uname -m)" = "arm64" ] || { echo "Timeless requires an Apple Silicon Mac"; exit 1; }

APP=/Applications/Timeless.app
[ -d "$APP" ] || APP="$HOME/Applications/Timeless.app"
CLI="$APP/Contents/Resources/timeless-cli/timeless"

if [ ! -d "$APP" ]; then
  echo "INSTALL_STATE=absent"
elif [ -x "$CLI" ]; then
  echo "INSTALL_STATE=current"
else
  echo "INSTALL_STATE=old"
fi

pgrep -x Timeless >/dev/null && echo "RUNNING=yes" || echo "RUNNING=no"
grep '^base_url:' "$HOME/.config/timeless/config.yaml" 2>/dev/null || :
ls -la "$HOME/.config/timeless/config.yaml"* 2>/dev/null || :
grep -c '^playbooks:' "$HOME/.config/timeless/config.yaml" 2>/dev/null || :

# Only possible before install when the bundled CLI already exists.
[ -x "$CLI" ] && "$CLI" status --json || :
```

### Windows

```powershell theme={null}
$app = "$env:LOCALAPPDATA\Programs\Timeless"
$cli = "$app\resources\timeless-cli\timeless.exe"
if (-not (Test-Path $app)) { $installState = "absent" }
elseif (Test-Path $cli) { $installState = "current" }
else { $installState = "old" }
"INSTALL_STATE=$installState"

if (Get-Process Timeless -ErrorAction SilentlyContinue) { "RUNNING=yes" } else { "RUNNING=no" }
if (Test-Path $cli) { & $cli status --json }
```

Important: an absent app bundle does **not** prove the user is signed out. Config, Keychain state, and a running process can survive a failed swap. Treat auth as unknown until `timeless status --json` runs after the CLI exists.

## Outcome 1 — orient and get install/update approval

Silently no-op when `INSTALL_STATE=current`.

Lead with value, then packaging, then the screen-impact promise. Use this shape in your own natural voice:

> Timeless turns your meetings into something you can ask about here: what was decided, who owns what, or where a topic came up, answered from the actual transcript. After setup, you can also send the bot to a future call or change recording behavior in plain English. The desktop app is needed because it owns recording, sign-in, and macOS permissions; its CLI is what connects those meetings to this agent. It is signed and notarized from [timeless.day](https://timeless.day). Nothing will open or take over your screen during this step.

### Absent app — ask exactly one question

> This downloads Timeless, puts it in Applications, and links its CLI. If sign-in is needed at the end, I’ll open the app so you can complete it; that is the one moment a window may come forward. Recording permissions come later, as their own guided step. Good to go?

After yes, start the download without waiting for it to finish and immediately continue to Outcome 2:

```sh theme={null}
curl -fL -o /tmp/Timeless.dmg \
  https://github.com/supertools/timeos-desktop-releases-2/releases/latest/download/Timeless.dmg
```

### Old app — ask exactly one question

> You already have Timeless, but this version predates its bundled CLI. I’ll replace the app with the current release; your account, meetings, sign-in, settings, and files stay outside the app bundle. Timeless will close during the swap and may reopen afterward if sign-in needs attention. If it is recording right now, tell me to hold; otherwise, update it now?

After yes, start the same download in the background and continue to Outcome 2.

On Windows, download the official installer but do not run it until Outcome 3:

```powershell theme={null}
$exe = "$env:TEMP\Timeless-Setup.exe"
Invoke-WebRequest -UseBasicParsing `
  -Uri "https://github.com/supertools/timeos-desktop-releases/releases/latest/download/Timeless-Setup.exe" `
  -OutFile $exe
```

## Outcome 2 — choose visible-bot behavior

State local capture as the baseline, then ask only about the visible participant:

> Once the app is running, Timeless can offer to record meetings you join directly from your Mac. Nothing appears in the call; that local option remains available regardless of what you choose next.

> Should a bot join your meetings too? It appears as a participant, so everyone can see it, and it can cover calls you are not in. You can change this any time by telling me.

Options:

1. **Yes, every meeting on my calendar (recommended)** → `JOIN_ALL_EVENTS`
2. **Only meetings I host** → `JOIN_AS_HOST`
3. **No bot; I’ll record from my Mac** → `MANUAL`

Store the answer. Do not apply it yet.

## Outcome 3 — install and link

When a download is running, wait for that existing process. Do not start a second download.

### macOS app replacement

Run this only for `absent` or `old`. The approval in Outcome 1 authorizes replacing the exact Timeless app bundle. Stop the resident process before touching the bundle.

```sh theme={null}
osascript -e 'if application "Timeless" is running then quit application "Timeless"' 2>/dev/null || :
for _ in 1 2 3 4 5; do pgrep -x Timeless >/dev/null || break; sleep 1; done
if pgrep -x Timeless >/dev/null; then pkill -x Timeless; sleep 2; fi
if pgrep -x Timeless >/dev/null; then pkill -9 -x Timeless; sleep 1; fi
pgrep -x Timeless >/dev/null && { echo "Timeless is still running"; exit 1; }

MOUNT=$(hdiutil attach /tmp/Timeless.dmg -nobrowse -noverify | grep -o '/Volumes/.*' | tail -1)
DEST=/Applications
[ -w "$DEST" ] || { mkdir -p "$HOME/Applications" && DEST="$HOME/Applications"; }

# Resolve only this exact bundle. If present, move it aside so recovery remains possible.
if [ -e "$DEST/Timeless.app" ]; then
  OLD="$DEST/Timeless.app.pre-agent-install-$(date +%Y%m%d-%H%M%S)"
  mv "$DEST/Timeless.app" "$OLD"
fi

ditto "$MOUNT/Timeless.app" "$DEST/Timeless.app"
xattr -dr com.apple.quarantine "$DEST/Timeless.app" 2>/dev/null || :
hdiutil detach "$MOUNT" -quiet
codesign --verify "$DEST/Timeless.app"
[ -n "${OLD:-}" ] && rm -rf -- "$OLD"
RUNNING=no
```

If signature verification fails, remove the failed new copy, restore the exact moved-aside bundle when one exists, retry the mount/copy once, and record the repair. Stop only if the second verification fails.

### Link the bundled CLI

Run on every install state:

```sh theme={null}
CLI=/Applications/Timeless.app/Contents/Resources/timeless-cli/timeless
[ -x "$CLI" ] || CLI="$HOME/Applications/Timeless.app/Contents/Resources/timeless-cli/timeless"
[ -x "$CLI" ] || { echo "timeless CLI not found in the installed app"; exit 1; }

if ln -sf "$CLI" /usr/local/bin/timeless 2>/dev/null; then
  TIMELESS_LINK=/usr/local/bin/timeless
else
  BIN="$HOME/.local/bin"
  mkdir -p "$BIN"
  ln -sf "$CLI" "$BIN/timeless"
  TIMELESS_LINK="$BIN/timeless"

  SHELL_NAME=$(basename "${SHELL:-sh}")
  case "$SHELL_NAME" in
    zsh) RC="$HOME/.zshrc" ;;
    bash) RC="$HOME/.bashrc" ;;
    fish) RC="$HOME/.config/fish/config.fish" ;;
    *) RC="$HOME/.profile" ;;
  esac
  if [ "$SHELL_NAME" = fish ]; then
    LINE='fish_add_path $HOME/.local/bin'
  else
    LINE='export PATH="$HOME/.local/bin:$PATH"'
  fi
  mkdir -p "$(dirname "$RC")"
  grep -qF "$HOME/.local/bin" "$RC" 2>/dev/null || printf '\n%s\n' "$LINE" >> "$RC"
  export PATH="$HOME/.local/bin:$PATH"
fi

"$TIMELESS_LINK" version --json
```

Do not launch the macOS app yet.

On Windows, run the downloaded installer for `absent` or `old`, then link or add the bundled CLI directory to user PATH and verify:

```powershell theme={null}
if ($installState -in @("absent", "old")) {
  Start-Process -FilePath "$env:TEMP\Timeless-Setup.exe" -Wait
}

$cli = "$env:LOCALAPPDATA\Programs\Timeless\resources\timeless-cli\timeless.exe"
if (-not (Test-Path $cli)) { throw "timeless CLI not found in the installed app" }

$bin = "$env:LOCALAPPDATA\Programs\Timeless\bin"
New-Item -ItemType Directory -Force -Path $bin | Out-Null
try {
  New-Item -ItemType SymbolicLink -Force -Path "$bin\timeless.exe" -Target $cli | Out-Null
} catch {
  $bin = Split-Path $cli
}
if (";$env:PATH;" -notlike "*;$bin;*") {
  $userPath = [Environment]::GetEnvironmentVariable("PATH", "User")
  [Environment]::SetEnvironmentVariable("PATH", "$bin;$userPath", "User")
  $env:PATH = "$bin;$env:PATH"
}
timeless version --json
```

## Outcome 4 — connect this agent and preserve existing work

Everything here is local. Complete it before starting the macOS app.

### Select the harness

The harness running this onboarding is the destination the user chose. Always set the global Timeless harness to the current harness, even when the existing value is a different non-default harness. Re-running the command with the same value is idempotent.

You already know which harness you are: `claude-code`, `codex`, or `cursor`. Do not infer it from the existing config.

```sh theme={null}
CURRENT_HARNESS=<claude-code|codex|cursor>
timeless config get harness --json
timeless config set harness "$CURRENT_HARNESS"
timeless config get harness --json
```

The final readback must equal `CURRENT_HARNESS`. The old global value is useful only for the repair log; it does not outrank the harness in which installation was requested.

Then explicitly install the skill bundle into that harness and make a timestamped config backup:

```sh theme={null}
timeless skills install --harness "$CURRENT_HARNESS"
cp "$HOME/.config/timeless/config.yaml" \
  "$HOME/.config/timeless/config.yaml.bak-$(date +%Y%m%d-%H%M%S)" 2>/dev/null || :
```

This changes the global default and the destination of the skill files. It does not rewrite an existing automation's prompt, trigger, schedule, enabled state, model, or explicit per-automation `harness` override.

### Preserve automations; create none

```sh theme={null}
timeless playbooks list --json
```

* Empty + healthy live config: continue.
* Non-empty: store the names in `EXISTING_WORK` and change nothing.
* Empty + missing/near-empty live config + dated backups: inspect backups newest first for user-owned entries. If a backup contains them, ask the one allowed restore question, naming them and the backup date. Restore only after yes; validate with `timeless playbooks list --json`.

Do not run or create anything.

### Safety-gate every harness used by existing automations

```sh theme={null}
timeless harness list --json
```

Use `timeless playbooks list --json` to collect the effective `harness` value reported for every existing automation. Gate each unique harness in that set, plus `CURRENT_HARNESS`. A harness passes only when all are true:

* `cli_available: true`
* `path` is non-empty
* `path` resolves through `/usr/local/bin` or `~/.local/bin`, not through an app bundle
* auth check passes: `codex login status` or `claude auth status --json`; Cursor is exempt

If Codex is only bundled inside ChatGPT.app, link it and re-read the harness list:

```sh theme={null}
mkdir -p "$HOME/.local/bin"
ln -sf /Applications/ChatGPT.app/Contents/Resources/codex "$HOME/.local/bin/codex"
timeless harness list --json
```

If a required harness is present but signed out, run its normal login and wait for the user to approve in the browser. If a harness used only by an explicit per-automation override is truly absent, do not silently rewrite that override or install another harness. Disable only affected event-bound automations to protect at-most-once events; leave scheduled ones unchanged and record the exact repair for Outcome 8.

## Outcome 5 — establish a live app session

The CLI now exists on every path. Determine auth before deciding how to launch:

```sh theme={null}
timeless status --json
```

Exit 0 means `AUTH=authenticated`. A normal signed-out response means `AUTH=signed-out`. Transport/backend errors mean `AUTH=unknown`; retry once after app readiness rather than treating them as sign-out.

Use this routing table:

| RUNNING | AUTH | Action |
| - | - | - |
| yes | authenticated | Do not launch or front anything. Wait for app readiness and sync. |
| no | authenticated | Launch in background with `open -g`, wait for readiness, sync. |
| yes | signed-out | Announce why, then activate the existing app and wait for sign-in. |
| no | signed-out | Announce why, foreground-launch, then wait for sign-in. |
| any | unknown | If needed, start the app in the background; wait for readiness, retry instant status, then use the matching row. Never foreground while auth is unknown. |

Resolve the actual bundle before any launch:

```sh theme={null}
APP=/Applications/Timeless.app
[ -x "$APP/Contents/Resources/timeless-cli/timeless" ] || APP="$HOME/Applications/Timeless.app"
```

For a background launch:

```sh theme={null}
open -g "$APP"
timeless app ready --wait 5m --json
timeless app sync
```

For a signed-out foreground path, announce and launch in the same message:

> Opening Timeless now. It will come forward so you can sign in with Google or Microsoft; I cannot do that part for you. You do not need to return here or tell me when it is done because I will detect it automatically.

Remember the current front app, then:

```sh theme={null}
FRONT_APP=$(osascript -e 'tell application "System Events" to name of first process whose frontmost is true' 2>/dev/null || true)
open "$APP" --args --agent-install
timeless app ready --wait 5m --json
timeless status --wait 10m --json
timeless app sync
[ -n "$FRONT_APP" ] && osascript -e "tell application \"$FRONT_APP\" to activate" || :
```

If the sign-in wait expires, run `timeless app authenticate`, then wait two more minutes. Use `timeless login` only as a last-resort CLI repair and do not claim it proves the app itself is signed in.

### Recording permissions

Local capture needs three macOS permissions: **Microphone**, **Screen & System Audio Recording**, and **Accessibility**. Two facts govern everything the onboarding says and does about them:

1. **Granting is not seamless.** Approving Screen Recording or Accessibility forces Timeless to restart. That restart is normal and expected — but it kills anything the app was doing at that moment.
2. **An ungranted recording fails silently.** If the user triggers a recording before all three permissions are granted, nothing records and nothing reports the failure. The lazy path — "it will ask when it first needs them" — does not produce a working first recording.

macOS exposes no reliable CLI check for these permissions, and `--agent-install` skips the app's normal permission onboarding. Therefore:

* Never say a launch requested or verified permissions.
* Never fire a recording and treat it as started without the priming pass in Outcome 7 having completed first.
* For `MANUAL`, tell the user once: “Because you chose local-only recording, Timeless needs microphone, screen-and-system-audio, and accessibility access before it can capture anything. We’ll grant those in a moment; Timeless restarts once when you approve them, which is normal.”
* For a bot choice, save the honest permissions note for Outcome 8: local capture remains available, but its permissions have not been granted or inspected unless the Outcome 7 priming pass ran.

Do not open System Settings yourself during the normal flow; macOS routes the user there from its own prompts. The one exception is the recovery step in the appendix, where opening the exact privacy pane is allowed after a failed trial.

## Outcome 6 — apply and verify

Tell the user you are applying their choice, then:

```sh theme={null}
timeless bot automation set <BOT_CHOICE>
timeless bot automation get --json
```

Run these independent reads in one batch:

```sh theme={null}
timeless status --json
timeless meetings list --json --limit 1
timeless meetings upcoming --json --limit 1
timeless harness list --json
```

Record:

* authentication and `base_url`
* whether recorded meeting data exists
* the next calendar meeting, when present
* configured harness, `cli_available`, and exact `path`

If existing automations were preserved, verify only that they parse and are still listed. Do not run them; a test run costs money and is outside onboarding.

## Outcome 7 — offer one live local-recording trial

Run this outcome only on macOS and only after every Outcome 6 verification passed. This is a real recording, not a synthetic test: it uses the Mac's microphone and system audio, is uploaded and processed by Timeless, and remains in the user's Timeless meeting history.

Ask one final optional question:

> Want to try Timeless now with a 30-second live recording? It captures your Mac's mic and system audio (nothing joins a call) and saves to your account like a normal meeting. The first time, macOS asks for recording permissions and Timeless restarts once; I’ll walk you through it. When the transcript lands, I’ll turn your own words into your first designed Timeless page, right here.

Options:

1. **Try a live recording (recommended)**
2. **Not now**

If the user declines, set `LIVE_TRIAL=declined` and continue directly to Outcome 8. Do not ask again.

### Before starting

Capture the current meeting IDs so the trial can be identified without confusing it with an older or concurrently processed meeting, and capture the app's process ID so a permission-triggered restart can be detected:

```sh theme={null}
TRIAL_BEFORE=$(timeless meetings list --json --limit 100)
TRIAL_BEFORE_IDS=$(printf '%s' "$TRIAL_BEFORE" | jq -r '.data[].id')
TRIAL_STARTED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
TIMELESS_PID_BEFORE=$(pgrep -x Timeless)
```

### Permission priming pass

A recording triggered before macOS has cleared Timeless for capture fails silently, and granting the permissions restarts the app (see Outcome 5). So the first `timeless://record` is fired as a **priming attempt**: its job is to surface the permission prompts, not to record. Tell the user exactly what to expect before firing it:

> First, macOS needs to clear Timeless for recording. I’m triggering it now. Approve everything it asks for: **Microphone**, **Screen & System Audio Recording**, and **Accessibility**. The last two open System Settings; flip the Timeless switch there. **Timeless will restart itself after you grant them. That’s expected; don’t reopen anything.** If no prompts appear at all, you’ve granted these before and the recording has simply started. In that case begin talking, starting with “This is my Timeless test recording,” and when you’re done choose **Stop and Process Recording** from the Timeless menu-bar icon.

```sh theme={null}
open "timeless://record"
```

`open` returning 0 proves nothing. Now watch the process for the grant-restart, bounded at about three minutes:

```sh theme={null}
PRIMING=granting
for _ in $(seq 1 36); do
  TIMELESS_PID_NOW=$(pgrep -x Timeless)
  if [ -n "$TIMELESS_PID_NOW" ] && [ "$TIMELESS_PID_NOW" != "$TIMELESS_PID_BEFORE" ]; then
    PRIMING=restarted
    break
  fi
  sleep 5
done
```

Branch on what actually happened:

* **`PRIMING=restarted`** — the user granted permissions and the app restarted, which also killed the priming attempt. Set `PERMISSIONS=primed-restart-observed`. Wait for readiness, then fire the real recording:
  ```sh theme={null}
  timeless app ready --wait 5m --json
  TRIAL_STARTED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
  ```
  > Permissions are in. Starting the real recording now. Begin with “This is my Timeless test recording,” then talk for about 30 seconds about what you’re working on. When you’re finished, open the Timeless menu-bar icon and choose **Stop and Process Recording**. You do not need to return here or tell me; I’ll wait for the transcript.
  ```sh theme={null}
  open "timeless://record"
  ```
* **`PRIMING=granting` after the full wait (no restart)** — either the permissions were already granted (they survive reinstalls, so this is normal on a machine that ran Timeless before) and the priming attempt **is** the live trial already in progress, or the user has not finished granting yet. Do not fire `timeless://record` again — a second fire during an active recording is undefined. Set `PERMISSIONS=assumed-pre-granted` and proceed to the transcript wait; its failure path already covers the case where nothing was actually recording.

The Timeless tray changes to its recording state when capture actually starts; the user can see this, you cannot. There is no CLI stop command. Never simulate menu clicks, keystrokes, or UI automation, and never open System Settings yourself outside the recovery appendix. The user stops the recording from the Timeless menu-bar icon, where the recording-state action is **Stop and Process Recording**.

### Wait for the new processed meeting

Poll for a meeting ID that was not present before the trial. Do not use “latest meeting” alone: another meeting can finish processing during the trial.

```sh theme={null}
TRIAL_MEETING_ID=""
TRIAL_MEETING_JSON=""

for _ in $(seq 1 60); do
  TRIAL_LIST=$(timeless meetings list --json --limit 20)
  TRIAL_MEETING_JSON=$(printf '%s' "$TRIAL_LIST" | jq -c \
    --arg ids "$TRIAL_BEFORE_IDS" \
    --arg started "$TRIAL_STARTED_AT" '
      .data[] as $meeting
      | select(($ids | split("\n") | index($meeting.id)) == null)
      | select($meeting.created_at >= $started or $meeting.start_time >= $started)
      | $meeting
    ' | head -1)

  if [ -n "$TRIAL_MEETING_JSON" ]; then
    TRIAL_STATUS=$(printf '%s' "$TRIAL_MEETING_JSON" | jq -r '.status')
    TRIAL_MEETING_ID=$(printf '%s' "$TRIAL_MEETING_JSON" | jq -r '.id')
    [ "$TRIAL_STATUS" = "completed" ] && break
  fi
  sleep 5
done
```

If no completed new meeting appears after five minutes, say:

> I haven’t seen the test recording finish processing yet. Check the Timeless menu-bar icon: if it shows it’s recording, choose **Stop and Process Recording** and I’ll keep watching. If it does **not** show a recording state, the recording never started, which almost always means a permission is still off. I’ll keep watching either way.

Then repeat the same bounded wait once. This is an instruction, not another question. If it still does not arrive and `PERMISSIONS=assumed-pre-granted`, the silent-failure case is the likely cause — run the “Live trial never started” recovery in the appendix once before giving up. If it still does not arrive after that, set `LIVE_TRIAL=failed`, report the exact observed state in Outcome 8, and finish setup without substituting an older meeting.

### Fetch and render the trial

Once the new meeting is `completed`, fetch its details and transcript as independent reads:

```sh theme={null}
timeless meetings get "$TRIAL_MEETING_ID" --json
timeless meetings transcript "$TRIAL_MEETING_ID" --json
```

Confirm that the transcript is non-empty and consistent with a just-recorded test. The opening phrase is a useful identifier, but allow ordinary transcription variation. If multiple new meetings appeared and the candidate is ambiguous, inspect each new transcript and select the one containing the test phrase or the user's 30-second monologue. Never summarize an older meeting as the trial.

The recap is delivered as a **Timeless artifact** — a self-contained HTML file in the user's artifacts directory — not as chat prose. It is the user's first artifact, in the same format every playbook produces, rendered by the app they just installed. It is the handoff.

This is the only file onboarding creates. It is a real deliverable, not a placeholder, and it is written only after a transcript has actually arrived.

#### Step 1 — write the file (every harness)

Writing a file needs no special tooling, so this step is identical in Codex, Claude Code, and Cursor. Resolve the directory from the CLI rather than assuming a path:

```sh theme={null}
ARTIFACT_DIR=$(timeless harness list --json | jq -r '.workdir')
[ -d "$ARTIFACT_DIR" ] || { echo "artifact workdir missing: $ARTIFACT_DIR"; exit 1; }
ARTIFACT="$ARTIFACT_DIR/first_capture-$(date +%Y-%m-%d).html"
```

Read any `CLAUDE.md` or `AGENTS.md` in that directory first and obey it — it is authoritative over anything below.

**Hard constraints.** These are correctness requirements, not style preferences. The file is opened over `file://` with network access denied, so a violation still opens, still looks like a success, and reports nothing.

* **One self-contained file.** All CSS inline. No remote fonts, stylesheets, scripts, or images. Embed any asset as a data URI.
* **Motion may move content but must never hide it.** Never animate from `opacity: 0`, and never start a data mark at `scaleX(0)`; with `animation-fill-mode: both` the element stays invisible until time advances, so any renderer that does not advance time yields a blank page. Animate `transform` only, inside `@media (prefers-reduced-motion: no-preference)`. The page must be fully legible at t=0.
* **Both themes.** The viewer's theme is unknown. Define the full light palette on bare `:root`, redefine only tokens under `@media (prefers-color-scheme: dark)`, and paint `body` background from a token — a transparent body borrows the host ground.
* **Brand.** Rose `#FF0044` as the single accent on a pure white or black ground; no cards, shadows, or accent bars; large lowercase display type for headlines, normal sentence case in body copy, proper nouns always capitalized. Name Manrope then Inter first in the font stacks with system fallbacks after them, and do **not** link a webfont — the CDN is blocked and the fallback is silent. For the logo, inline the vector from the brand asset bundle when it is present; if it is absent, omit the logo. Never recreate the wordmark in a font.

**What it says.** Accuracy outranks impact. A short or empty recording is a normal outcome and the page says so plainly.

* A two-to-four sentence summary of what the user actually said.
* Up to three key points **only** where the content supports them.
* Action items **only** where the user stated a commitment or next step.
* When the capture is thin — a few seconds, a greeting, no substance — say so directly and state that there is nothing to extract. Never pad a page to look substantial; inventing decisions or action items is a worse failure than a sparse page.
* Include the verbatim transcript when it is short enough to show in full, with its original capitalization. Do not restyle a transcript to match brand casing.
* Separate what the run proved (permissions cleared, pipeline works, agent can read it) from what it did not (multi-speaker separation, far-end call audio, summary quality on real material).

#### Step 2 — verify it before showing it

An unverified artifact is the failure this format invites. Both greps are cheap:

```sh theme={null}
grep -oE 'https?://[^"'"'"' )]+' "$ARTIFACT" && { echo "external ref found"; exit 1; }
grep -nE 'opacity: *0[^.]|visibility: *hidden' "$ARTIFACT"
```

Any hit on the second grep outside an `@keyframes` block must be fixed, not waived.

Then render it and **look at the image**. On macOS `qlmanage` renders at t=0, which is exactly the "legible without animation" test:

```sh theme={null}
qlmanage -t -s 1200 -o "$SCRATCH" "$ARTIFACT" >/dev/null 2>&1
```

A blank or near-blank frame means the page fails the t=0 rule; fix the CSS and re-render. Write the PNG to a scratch directory, never beside the artifact, so it is not mistaken for a deliverable.

Set `TRIAL_ARTIFACT` to the path and `LIVE_TRIAL=completed`.

#### Step 3 — the reveal

The file already exists and is verified; this step shows it to the user **inside the conversation**. This is the wow moment the whole trial builds to — the user spoke into their Mac a minute ago, and now a designed page made from their own words appears where they are already looking. Do not open a browser window or foreground any app for this: the artifact's home is the Timeless app, and the reveal's home is the chat.

Probe your own tool list, take the richest surface you actually have, and record it in `TRIAL_SURFACE`:

| Order | Surface | Notes |
| - | - | - |
| 1 | **Inline visual widget** | If the harness exposes a tool that renders HTML or rich visual content inline in the conversation (a widget/visualize tool, an inline HTML canvas), rebuild the artifact's content in it: same headline, same facts row, same transcript with the rose accent, same proved/not-proved ledger. Follow the surface's own design constraints where they conflict with brand rules (fonts, theming) — but keep the structure, the wording, and the `#FF0044` accent. This is a preview; the file is the deliverable. |
| 2 | **Inline render of the local file** | If the harness can display a local file inline (Claude Code: send the file with a render disposition), do that. Local, no upload, no URL. |
| 3 | **Path only** | State the filename and that the Timeless app renders it. Correct in every harness; the floor, not the goal. |

Decide by capability, not by harness name: check whether the tool exists, use it if it does, and skip silently to the next tier if it does not. A tier that fails at call time falls through to the next — never report a failed reveal when a lower tier is available. Whatever tier is used, tell the user the page also lives in their Timeless app, which renders the real file.

Do not upload the artifact to an external host in order to display it. A hosted, shareable link is a separate thing the user may ask for later; it sends the transcript off the machine, so it is never part of onboarding and never done without an explicit request.

**Chat text carries two lines only** — the reveal (or, at the path-only floor, the artifact itself) carries the summary:

> That's your first capture. The real page lives in your Timeless artifacts folder as `first_capture-<date>.html`, rendered by the Timeless app.
>
> It was built from the local recording you just made; no bot joined anything.

Do not additionally restate the summary as chat prose when a tier-1 or tier-2 reveal succeeded, and do not create or run an automation to produce it; read the transcript directly. At the path-only floor, add a two-to-four sentence summary in chat so the user is not left with only a filename.

#### Failure paths

* **Trial failed** (`LIVE_TRIAL=failed`): write **nothing**. A non-empty file at an artifact path is treated as a finished deliverable and delivered to the user. Leave `TRIAL_ARTIFACT=none`.
* **The file could not be written, or could not pass verification**: fall back to a chat summary — two-to-four sentences, key points only where supported, action items only where stated, ending with the proof sentence. Set `TRIAL_SURFACE=chat-fallback` and say plainly in Outcome 8 that the artifact could not be produced. A silent downgrade to chat is not acceptable.

## Outcome 8 — report and hand over

Report only ledger facts that were verified:

* app installed/current and ready
* CLI linked and authenticated
* skill bundle installed
* launch branch: untouched, background, or foreground for sign-in
* harness name, `cli_available`, and exact path
* confirmed bot setting
* honest local-permission state
* no new automations were created; name preserved existing ones when present
* live-trial result: declined, delivered as an artifact (give the path and how it was surfaced), delivered as a chat summary because the artifact could not be produced, or the exact failure state
* repairs, fallback link location, update, non-default `base_url`, or unresolved limitations

Do not expose internal implementation vocabulary to a new user. If they already had automations, call them “your existing automations.”

If the live trial completed, the artifact in Outcome 7 was the handoff action. Do not follow it with another example query or menu. Finish with:

> That’s the full loop: record, transcript, designed page. Your real meetings work exactly the same way.

If the live trial was declined or failed, give exactly one next action selected from the verified data:

### Recorded meetings exist

> Try this now: “Summarize my last meeting and pull out the action items.”

### No recorded meetings + bot enabled

If an upcoming meeting exists:

> You’re set. The bot will cover **meeting name** at **local time**. Afterward, ask me what was decided or who owns what.

If none exists, say the bot will cover the next eligible calendar meeting automatically.

### No recorded meetings + `MANUAL`

If the priming pass never ran (trial declined), the permissions are still ungranted, and a recording attempted without them fails silently. The next action must close that gap **before** a real meeting depends on it:

> One thing before your first real meeting: start a throwaway recording from the Timeless menu-bar icon and approve everything macOS asks for (Microphone, Screen & System Audio Recording, Accessibility). Timeless restarts once; that’s normal. Do it now: a recording started before those are granted silently records nothing.

If the priming pass did run (`PERMISSIONS=primed-restart-observed`), skip the warning:

> You’re set. Keep Timeless running for your next meeting; it records from your Mac with nothing visible in the call. Afterward, ask me what happened.

### Shared closing — every ending

End every branch, trial included, with the playbooks pointer and one factual app sentence:

> When you want more than recaps, explore playbooks the community has built with Timeless Code at [code.timeless.day](https://code.timeless.day/): recaps delivered to Slack or your CRM, morning briefs, weekly summaries. Pick one and ask me for it in plain English; nothing runs until you do.
>
> The Timeless app is where you browse and replay meetings, and it needs to be running for recording and delivery.

Link the site; do not open it or any gallery yourself.

## Recovery appendix

Use this section only when the matching condition occurs.

### Mount reports “resource temporarily unavailable”

Check `hdiutil info` and `/Volumes` for an already-mounted Timeless image. Reuse the verified mount if present; otherwise retry the attach once. Record the repair.

### `/Applications` is not writable

Install into `~/Applications` and link from the bundle that actually contains the CLI. Record the fallback location.

### App process survives normal quit

Escalate exact-name termination from Apple Event to `pkill -x Timeless`, then `pkill -9 -x Timeless`. Never use `pkill -f`.

### `app ready` times out

Confirm the exact installed bundle path, launch it once more, and retry. Stop only when the app still cannot become ready.

### Non-default `base_url`

Do not change it. Report it clearly because every auth and meeting command is using that backend.

### Live config appears wiped

Wreckage means a missing or near-empty live config next to substantive dated backups. Never infer this merely from an empty automation list. Offer restoration only when a backup visibly contains the user's own named automations.

### Live trial never started

Condition: the transcript wait expired twice, no app restart was ever observed, and the user reports the menu-bar icon never showed a recording state. This is the silent permission failure. Walk the user through the panes directly — this is the one place opening System Settings is allowed:

```sh theme={null}
open "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone"
```

> Recording never started, which means macOS hasn’t cleared Timeless for capture. I’ve opened the Microphone pane; turn Timeless on there, then do the same under **Screen & System Audio Recording** and **Accessibility** in the same Privacy & Security list. Timeless will restart when you’re done; that’s expected.

Watch for the restart with the same PID check as the priming pass, wait for `timeless app ready`, then re-run the trial once from “Before starting” (fresh `TRIAL_BEFORE_IDS`, fresh `TRIAL_STARTED_AT`). If it fails again, set `LIVE_TRIAL=failed` and record exactly which pane, if any, the user could not enable.
