# Align

> Turn a task prompt into a prompt-align brief page — reading, scope, done-means, and autonomy as editable sentences with ghost alternatives and AI steering — wait for the human's saved brief, then implement to completion from the persisted result. Trigger: /align <task>.

- Skill: `johnlindquist/align` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add johnlindquist/align`
- Raw SKILL.md: https://api.skillmd.com/api/skills/johnlindquist/align/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: johnlindquist (https://skillmd.com/u/johnlindquist)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/johnlindquist/align

---


# Align (prompt-align edition)

`/align <task>` is a three-phase contract between an agent and a human, mediated by
[prompt-align](https://github.com/johnlindquist/prompt-align):

1. **Align** — the agent inspects the code, finds the genuine forks, and publishes ONE
   brief page where every decision is an editable sentence.
2. **Edit** — the human picks ghosts, types over anything, and steers the AI with
   `// comments`. Sending the brief saves `result.json`.
3. **Implement** — the saved result is standing authorization. Implement, verify, and
   report to completion. Never re-ask "should I proceed?" after the result exists.

The invocation itself is the human's opt-in to all three phases.

## Requirements

- [Bun](https://bun.sh) and a checkout of prompt-align. Resolve its location in this order:
  1. `$PROMPT_ALIGN_DIR` if set
  2. `~/dev/prompt-align` or `~/prompt-align` if present
  3. otherwise clone it: `git clone https://github.com/johnlindquist/prompt-align ~/prompt-align`
- Call the CLI as `bun <dir>/src/server.ts …` (referred to as `prompt-align …` below).

## Phase 1 — Inspect, then compose the job

1. **Inspect before asking.** Read the relevant code, config, and logs first. Never put a
   question on the page that the repository can answer. Every alternative must be grounded
   in what actually exists.
2. **Find the genuine forks.** A fork is real only when two readings of the prompt lead to
   materially different work. If there is no real fork in a section, still fill the slot —
   one honest recommended sentence plus one clearly-worse alternative keeps the human's
   veto cheap.
3. **Compose the job** — four slots, each a complete sentence the human can keep, replace,
   edit, or steer. Put the **recommended sentence first** in every slot. Put your
   inspection findings in `context` so the page is decidable without another window.

```json
{
  "jobId": "align-<short-task-slug>-<4 random hex>",
  "title": "Align: <the task in a few words>",
  "seed": "<the user's task prompt, one line>",
  "context": "<what you found during inspection: key files, current behavior, constraints>",
  "slots": [
    { "key": "reading", "name": "READING", "detail": "what to build",
      "alternatives": [
        "<recommended reading, as a build-this sentence>",
        "<plausible bigger/smaller reading, stated fairly>" ] },
    { "key": "scope", "name": "HOW FAR", "detail": "quality bar",
      "alternatives": [
        "<recommended scope sentence, e.g. cover the obvious edge cases — nothing more>",
        "<proof-only sentence>",
        "<fully-polished sentence>" ] },
    { "key": "proof", "name": "PROOF", "detail": "done means",
      "alternatives": [
        "<observable success signal + how it will be verified>",
        "<weaker alternative, stated honestly>" ] },
    { "key": "cadence", "name": "CADENCE", "detail": "check-ins",
      "alternatives": [
        "<recommended check-in cadence>",
        "<show-first-slice sentence>",
        "<run-unattended sentence>" ] }
  ]
}
```

Slot rules (enforced by the server): slug keys, names ≤ 24 chars, 1-9 alternatives of
≤ 400 chars each. Add extra slots (max 12) only for forks that genuinely exist —
constraints, migrations, naming — never filler.

## Phase 2 — Serve, push, deliver, wait

```bash
# 1. ensure a server is running (idempotent — check first)
curl -sf "http://127.0.0.1:$(bun -e 'console.log(JSON.parse(await Bun.file(process.env.HOME + "/.prompt-align/server.json").text()).port)' 2>/dev/null)/api/health" >/dev/null 2>&1 \
  || (nohup prompt-align serve --port auto >/tmp/prompt-align.log 2>&1 & sleep 1)

# 2. push the job — prints { jobId, url } with the token baked into the url
prompt-align push /path/to/job.json

# 3. hand the url to the human (open it locally, or send it however you reach them)
open "<url>"

# 4. ARM THE WAIT BEFORE ENDING THE TURN — as a background task, never foreground
prompt-align wait <jobId> --timeout 4h
```

Never end a turn with a live page and no armed wait: a brief nobody is waiting for is a
dead end. When the wait exits 0, its stdout is the result JSON and Phase 3 starts
immediately — no confirmation prompt, no summary-and-stop.

The server binds `127.0.0.1` only and every URL carries a token; do not expose the page
beyond the machine without the human's say-so.


## Phase 2½ — reach the human on their phone (optional)

If the operator has set up a Tailscale serve route to this server and a tfy push
server (https://github.com/johnlindquist — tfy: a self-hosted Web Push PWA), deliver
the brief to the human wherever they are:

1. Build the phone URL by swapping the loopback origin for the serve route, keeping
   path and token (the page derives its API base from the request Host, so
   generation and submit work from any device on the tailnet):
   `PHONE_URL="https://<machine>.<tailnet>.ts.net:<port>/j/<jobId>?t=<token>"`
2. Notify, if `~/.config/tfy/env` exists (defines TFY_URL, TFY_TOKEN, TFY_TOPIC):

```bash
source ~/.config/tfy/env 2>/dev/null && curl -s -X POST "$TFY_URL/$TFY_TOPIC" \
  -H "Authorization: Bearer $TFY_TOKEN" \
  -H "Title: Align: <task in a few words>" -H "Click: $PHONE_URL" \
  -d "Your brief is ready — tap to review, steer, and send." >/dev/null
```

3. The armed `wait` is unchanged — a submission from any device resumes the agent.

## Phase 3 — Implement from the result

Continue from the persisted file — `<data-dir>/jobs/<jobId>/result.json` (default data
dir `~/.prompt-align`) — never from conversation memory.

- `result.brief` is the contract. Its sentences ARE the selected reading, scope,
  done-means, and cadence. Implement the smallest coherent version that satisfies them.
- `sentences[].source` tells you how much the human engaged: `edited` and `generated`
  sentences are strong signals — quote them back in the report. `steeringUsed` holds the
  human's own words; treat them as constraints.
- Honor the cadence sentence literally: if it says check in early, check in early.
- The result authorizes local edits and verification for the selected work only. It does
  not authorize pushing, deploying, or publishing unless a sentence explicitly says so.
- Verify at the layer of each claim in the PROOF sentence, then report: map every
  sentence of the brief to what was done, and cite the result.json path.

## Failure handling

- `wait` timed out (exit 1): implement nothing. Report the page URL, the jobId, and the
  exact resume command (`prompt-align wait <jobId> --timeout 4h`).
- Server died: `prompt-align serve` again — jobs and results are on disk and survive.
- A result that exists is final; to re-run alignment, push a NEW jobId rather than
  overwriting (submit refuses duplicates without `?replace=1` for exactly this reason).

## Job template

A ready-to-edit starter lives next to this file: `template.job.json`.

