Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
eae7528
docs(specs): automations redesign — scheduled triggers, redesigned su…
pat-lewczuk Sep 14, 2026
985063d
feat(web): promote Chip, Segmented, Kbd and BranchChip primitives; Ca…
pat-lewczuk Sep 14, 2026
0459da6
feat(automations): scheduled triggers, default-on, stats, templates, …
pat-lewczuk Sep 14, 2026
bab24e9
feat(web): automations route shell, data hook, formatting/template/CL…
pat-lewczuk Sep 14, 2026
513358e
feat(automations): create a GitHub automation from a prompt — cez aut…
pat-lewczuk Sep 13, 2026
02e9c8a
feat(automations): from-prompt CLI, skill and prompt learn schedules;…
pat-lewczuk Sep 14, 2026
95f5087
feat(web): automation execution log screen
pat-lewczuk Sep 14, 2026
c66db7f
feat(web): automations list, stats strip, row actions, next-runs rail…
pat-lewczuk Sep 14, 2026
10e0ad9
feat(web): automation editor — template palette, schedule and GitHub …
pat-lewczuk Sep 14, 2026
6a106bf
test(e2e): automations journeys — schedule from a template, run by ha…
pat-lewczuk Sep 14, 2026
b29e192
fix(web): automation editor header wraps on phones; e2e fetch closes …
pat-lewczuk Sep 14, 2026
572bc61
Merge remote-tracking branch 'origin/main' into review/pr-985
pat-lewczuk Sep 14, 2026
ca836ad
fix(automations): close review blockers on the default-on redesign
pat-lewczuk Sep 14, 2026
fba4843
feat(web): hide automation costs for now; Day view shows the GitHub p…
pat-lewczuk Sep 14, 2026
f41382e
Merge branch 'main' into feat/automations-redesign
pat-lewczuk Sep 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .ai/runs/2026-09-13-automations-from-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Automations from a prompt — implementation

Source doc: .ai/specs/2026-09-13-automations-from-prompt.md
Builds on: .ai/specs/2026-07-25-github-automations.md, .ai/specs/2026-09-10-dispatch.md

## Goal

Let a user create a GitHub automation from the New task window by describing it — either by picking
the built-in `create-cezar-automation` skill or by simply asking, with the agent recognising the
intent from its system prompt — without adding routes, state or flags.

## What was built

- `packages/cezar/src/automations/prompts.ts` — `AUTOMATIONS_PROMPT` (always-on part),
`AUTOMATION_SCHEMA_REFERENCE` (the definition shape `cez automation schema` prints),
`CREATE_AUTOMATION_SKILL_BODY` (the playbook).
- `packages/cezar/src/automations/automation-cli.ts` — `cez automation
schema|create|update|check|list|show|enable|pause|delete`, a thin client over the existing
automations routes; routed in `src/index.ts` ahead of the cockpit parser like `cez task`.
- `packages/cezar/src/automations/builtin-skill.ts` — `automationsReachable()` (flag AND
`CEZ_API_URL`), the built-in `Skill` (`source: 'builtin'`, `interactive: true`) and
`builtinSkills()`; `src/skills.ts` appends it last in `discoverSkills`. `Skill.source` gains
`'builtin'` in `src/skills.ts` and `packages/contract/src/skills.ts` (additive).
- `packages/cezar/src/workflows/run.ts` — `ActiveRun.automationsPrompt`,
`prepareAutomationsSession` at both session construction sites, composed after the dispatch part
at both `startSession` calls.
- `packages/web/src/lib/prompt-templates.ts` — built-in `create-automation` template assigned to
the skill; `AUTOMATIONS_TEMPLATE_IDS` gate in `availablePromptTemplates` (now reads
`capabilities.automations` too; the three composers already pass the whole capabilities object).
- Docs: README (env row + CLI), `.env.example`, `BACKWARD_COMPATIBILITY.md` (CLI section), this spec.

## Tests

- `automations/automation-cli.test.ts` — request shape per command, `--enable` lifting, stdin,
malformed input, refusal relay, update merge + revision echo, check polling, no-cockpit exit.
- `automations/prompts.test.ts` — the reference names every event and key of the storage schema
and parses as a valid definition once comments are stripped; prompt and skill pin the CLI and
the paused/preview rules.
- `skills.test.ts` — built-in absent with the flag off or no transport, present and `builtin`
when reachable, shadowed by a repo skill of the same name.
- `workflows/system-prompt.test.ts` — the part rides only when on AND reachable.
- `web/src/lib/prompt-templates.test.ts` — the automations gate, independent of the dispatch gate.

## Validation

`npm run typecheck`, `npm test` (server + web), `npm run test:unit`, `npm run build` — see the PR.

## Not done

The redesigned Automations screens from the Claude Design export (the export was not available in
the session); the composer keeps its existing skill picker and template menu as the entry points.
61 changes: 61 additions & 0 deletions .ai/runs/2026-09-14-automations-redesign.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Run notes — automations redesign (spec `2026-09-14-automations-redesign`)

Source doc: `.ai/specs/2026-09-14-automations-redesign.md`
Builds on: `.ai/specs/2026-07-25-github-automations.md`, `.ai/specs/2026-09-13-automations-from-prompt.md`,
`.ai/specs/2026-09-10-dispatch.md`. Design: `.ai/specs/assets/automations-redesign/`.

## Goal

Add a schedule trigger kind, turn automations on by default, rebuild the Automations surface
to the Claude Design export from named primitives, and land the "create an automation from a
prompt" patch on top — one PR (owner decision Q5).

## What was built, by phase

1. **Primitives** — `Chip`, `Segmented`, `Kbd`, `BranchChip` promoted into
`packages/web/src/components`; `Card` gained `flush`. `PickerPill` and the Tasks "group by"
control now consume them.
2. **Contract + engine** — `packages/contract/src/automation-schedule.ts` (four bounded shapes,
`cronOf`/`parseCron`, DST-safe `occurrencesBetween`/`nextOccurrence`) and
`packages/contract/src/zoned-time.ts` (moved out of `core/usage-limit.ts`);
`packages/cezar/src/automations/schedule-runner.ts` (the age rule: scheduled / one catch-up /
skipped, receipts per occurrence, auto-pause after three launch failures, lease and duplicate
never counted); `scheduler.ts` arms one timer for both kinds and gives every registered
project a handle (`github` only for a github.com remote); `task-template.ts` renders schedule
placeholders, maps `task.dispatch` to the intent and the review-child suffix, and writes the
new optional `RunRecord.automationTrigger`; `store.setState` is read-modify-write.
3. **Routes + gating** — kind-aware create/update (PUT inherits the kind, a switch is 409),
`POST /automations/:id/run`, kind-aware `check`/`retry`, `GET /automations` with `timeZone`,
`stats` and per-row `nextRunAt`/`lastRun`/`runs7d`/`costUsd7d` (`stats.ts`, derived), the
log's `runs` map with dispatch children, `GET /workspace/automation-templates`
(`templates.ts`), `capabilities.automations = CEZ_AUTOMATIONS !== '0'` with the boot
re-baseline of idle polls (`rebaselineIdleAutomations`). README, `.env.example`, AGENTS.md
and BACKWARD_COMPATIBILITY.md carry the break.
4. **List, rail, calendars** — `routes/automations/{automations-list,stats-strip,
automations-table,row-actions,next-runs-rail,calendar-parts,week-view,day-view}.tsx` over one
query (`use-automations.ts`) invalidated by `automation-change`.
5. **Editor** — `editor.tsx` + `editor-draft.ts`, schedule/GitHub fields, run-as row (composer
pills), dispatch row, template palette (built-in + other projects), next-runs preview, Copy as
CLI (`lib/automation-cli.ts`), last-run card.
6. **Log** — `log.tsx` with nested dispatch children and kind-aware retry.
7. **From-prompt patch** — applied from `.ai/specs/assets/automations-redesign/patch/` with a
3-way merge (README and `.env.example` conflicted on the flag wording); the schema reference,
the always-on prompt part and the skill playbook learned both kinds; `cez automation add`
(flag sugar, exit 2 for a flag it cannot express) and `cez automation run` were added.
8. **Verification** — the gate (`typecheck`, `test`, `test:unit`, `build`, `test:package`),
the e2e journeys and the design-fidelity screenshots; see the PR.

## Review trail

The spec went through an independent architectural review before implementation (see the
spec's git history). Four findings changed the design before a line was written: scheduled
automations would never have fired in a repo without a GitHub remote; loosening the run
record's `githubUrl` would have made a downgrade drop the whole runs index; a laptop asleep for
days would have fired one launch per missed occurrence; and two cockpits on one project would
have clobbered each other's state file.

## Not done

New GitHub events, a repo-committed definition format, per-run budgets outside dispatch, a
`reviewChild` field on the dispatch intent, full cron expressions, automations while the server
is stopped — all deliberate (spec § Not done).
84 changes: 84 additions & 0 deletions .ai/specs/2026-09-13-automations-from-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# Automations from a prompt — a task may create a GitHub automation

> Slug: `automations-from-prompt` · Status: implemented · Gated by the existing `CEZ_AUTOMATIONS=1`
> (→ `capabilities.automations`) plus the cockpit transport (`CEZ_API_URL`), exactly like dispatch.
> Extends: `2026-07-25-github-automations.md` (the feature), `2026-09-10-dispatch.md` (the mechanism).

## TLDR

A user types "whenever a PR is opened, review it" into the **New task** window and the agent creates
the automation instead of doing the work once. Two ways in, one mechanism underneath:

- **Pick the skill.** A built-in skill, `create-cezar-automation`, is listed in the composer's skill
picker (and `/create-cezar-automation`) on any cockpit with automations on. Picking it pre-fills the
prompt box with the two questions every automation needs answered (trigger, task) through a
built-in prompt template assigned to it, and runs the task under a playbook: understand → write the
definition → `cez automation create` (paused) → `cez automation check` (preview) → report the link.
- **Or just ask.** Every task's system prompt carries a short **automations part** that teaches the
agent to recognise "whenever / every time a PR or issue …" as an automation and names the CLI. An
agent that reads it runs `cez automation schema` for the shape and proceeds the same way.

Underneath: a `cez automation` CLI (`packages/cezar/src/automations/automation-cli.ts`), a thin HTTP
client over the existing `/api/v1/p/:projectId/automations*` routes, addressed by the same
`CEZ_API_URL` / `CEZ_PROJECT_ID` / `CEZ_BIN` variables the dispatch CLI uses. No new routes, no new
state, no new flag.

## Why this shape

- **Reuse the dispatch transport.** Spec 2026-09-10 found that turn-end markers were the cause of
every fatal refusal; a CLI call gets a synchronous, typed answer, and the engine already publishes
the cockpit's address and its own entrypoint to every agent. The automation CLI is the second
client of that transport, not a third mechanism.
- **JSON, not flags.** A definition has nested filters and a task block. An agent writes JSON to a
file more reliably than it quotes a dozen flags in a shell — so `create`/`update` take `--file`,
`--json` or stdin, and `cez automation schema` prints the shape with every bound.
- **Paused by default, preview before enable.** The automations spec's Q1 (enabling establishes a
baseline; a preview never launches) is exactly the safety an unattended agent needs: it cannot
fan out a hundred tasks by creating one automation. The prompt and the skill both say create
paused, preview, and enable only when the user asked.
- **Tell an agent only about what it can use.** The dispatch lesson (A2/A8: an agent taught a CLI
whose every call is refused reads the refusal as "stop"): the prompt part, the built-in skill and
the composer template are all gated on `capabilities.automations` **and** the transport
(`automationsReachable()`), so a headless `cezar run` or a cockpit with the flag off composes
nothing, lists nothing, offers nothing.
- **A built-in skill is the exception, named.** cezar has never shipped a skill file — every skill
is discovered from the repo, the `npx skills` dirs or a team repo. This playbook belongs to the
cockpit that runs the automations, not to any repository, so it ships as a TypeScript constant
(`automations/builtin-skill.ts`, `source: 'builtin'`), appended LAST to the catalog so a repo
skill of the same name shadows it. The `files` list, the pack gate and the discovery walk are
untouched.

## Resolved assumptions

| # | Question | Applied default | Why |
|---|---|---|---|
| A1 | How a task creates an automation | `cez automation …` over the existing automations routes, through `node "$CEZ_BIN"`, with `CEZ_API_URL` and `CEZ_PROJECT_ID` from the agent's env. | Same transport and reasoning as dispatch A1; the routes already validate, baseline and log. |
| A2 | Gating | `automationsReachable()` = `capabilities.automations && CEZ_API_URL`. Off or unreachable: no prompt part, no built-in skill, no template; the CLI without `CEZ_API_URL` exits 2 saying so; with the flag off the routes' 409 (naming `CEZ_AUTOMATIONS=1`) is relayed verbatim and exits 1. | Dispatch A2/A8. |
| A3 | Enabled or paused on create | Paused unless `--enable`; the prompt says enable only when the user asked, and never without a preview. A definition copied from `show` carrying `enabled: true` is lifted into the route's `enable` flag rather than refused. | Automations Q1: no backfill, no surprise fan-out. |
| A4 | How the skill ships | One built-in `Skill` with `source: 'builtin'`, `path: builtin:create-cezar-automation`, `interactive: true`; `discoverSkills` appends `builtinSkills()` last. Contract enum gains `'builtin'` (additive). | The user's repo is the source of truth; nothing to isolate in a worktree, and the agent may have to ask which label was meant. |
| A5 | What the composer shows | The skill in the picker; a built-in template `create-automation` (Settings → Prompt templates, editable) assigned to the skill, hidden with `AUTOMATIONS_TEMPLATE_IDS` unless `capabilities.automations`. | Same gate as `DISPATCH_TEMPLATE_IDS`, same reason. |
| A6 | Where the prompt part sits | `ActiveRun.automationsPrompt`, resolved by `prepareAutomationsSession` at BOTH session construction sites, composed after the dispatch part and before the run's extra prompt. | AGENTS.md § "every construction site" — a Continue must keep it. |
| A7 | `update` semantics | Reads the current definition, merges the patch over the six editable keys, restates `enabled` and echoes `expectedRevision`. | A partial JSON from an agent must not blank keys it did not mention; a cockpit edit in between is a 409, not a clobber. |
| A8 | `check` | POSTs `{mode}` and polls the workspace-level `/automation-checks/:id` every 500 ms for up to 120 s; preview prints the count and "nothing was launched". | The check is asynchronous server-side; the agent needs one command that answers. |

## CLI (`packages/cezar/src/automations/automation-cli.ts`)

`cez automation schema` · `create [--file <def.json> | --json '<json>'] [--enable]` (stdin when
neither) · `update <id> [--file | --json]` · `check <id> [--execute]` · `list` · `show <id>` ·
`enable <id>` · `pause <id>` · `delete <id>`. Every mutating command prints the automation's cockpit
page (`<CEZ_API_URL>/p/<project>/automations/<id>`). Exit 0 ok, 1 refused/failed, 2 usage or no
cockpit. Routed in `index.ts` before the cockpit's own parser, like `cez task`.

## Prompt (`packages/cezar/src/automations/prompts.ts`)

`AUTOMATIONS_PROMPT` (the short always-on part), `AUTOMATION_SCHEMA_REFERENCE` (what `schema` prints:
every key of `automationDefinitionSchema` with its bounds, the events, the placeholders, the paused
rule — pinned against the storage schema by `prompts.test.ts`) and `CREATE_AUTOMATION_SKILL_BODY`
(the playbook, embedding the reference).

## Not done (deliberately)

A composer toggle or a dedicated "New automation from prompt" screen — the skill picker and the
template are the composer's existing affordances and cost nothing. Automation creation from a
headless `cezar run`. A `--dry-run` that validates a definition without a cockpit (the routes are
the validator). Changing the Automations page itself.
Loading