Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 20 additions & 7 deletions .agents/skills/lastcode-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ promotes the newest one after the PR queue is empty.

## Babysit and Merge

When the user asks to babysit or merge:
Use this workflow whenever the authorized task includes waiting for a LastCode
PR's checks or review, including PRs created while repairing checkpoint or build
failures. The user does not need to say “babysit.” Creating a PR alone does not
authorize merging it; preserve the requested stopping point.

Read `.agents/skills/_references/external-review-mechanics.md` for the repository's
current GitHub thread and review-query mechanics.
Expand All @@ -57,9 +60,18 @@ current GitHub thread and review-query mechanics.
`../_references/external-review-mechanics.md`. Do not merge until Codex gives
an explicit clean result or every finding for the exact current head has a
durable handled state, and no review thread remains unresolved.
4. Use the independent **Wait for PR** Project Action while exact-head GitHub CI
or Codex review is passive. It resumes only for an actionable result. Rebase,
fix, retry, or request another review only after inspecting that result.
4. While exact-head GitHub CI or Codex review is passive and no current finding
needs judgement, call `list_project_actions`. Select the eligible **Wait for
PR** action by its returned stable ID, preferring repository-managed
`lc-wait-for-pr` over the older saved `wait-for-pr` if both exist. Call
`run_project_action_and_resume` and end the turn immediately. Do not follow
the launch with GitHub queries, sleeps, process checks, or output polling.
Do not execute `scripts/lastcode-wait-for-pr.ts` through a shell tool: running
the wait script directly keeps the agent turn open and loses the resume handoff.
After resume, inspect the wake reason and verify the current head/base before
deciding whether to fix, rebase, retry, or request another review. If the
action is missing or disabled, report the setup blocker; do not substitute a
manual polling loop.
5. Use `pnpm lastcode:merge`; do not bypass the guarded merge in the GitHub UI.
The wrapper independently revalidates a successful exact-head/base GitHub CI
run and aggregate `CI Gate`, a clean current PR, and an unchanged fetched
Expand All @@ -72,9 +84,10 @@ current GitHub thread and review-query mechanics.
npm --silent run markover -- done <pr-url> --pr-status merged
```

Follow the repository's bounded CI polling rule. Stay quiet when no new review
or check result exists, and stop only when the latest commit is clean or a real
external blocker requires the user.
One immediate status snapshot can establish that a wait is needed. For this
LastCode workflow, the resumable action owns subsequent passive checks; the
general repository CI polling rule does not require agent-side sleeps or
repeated queries. Stop at the requested ready/merged state or a real blocker.

## Handoff

Expand Down
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,15 @@ An empty database is a bad test. Seed your worktree's `.t3` with a copy of real
- The server is event-sourced and its async flows emit typed receipts. Wait on receipts and worker drains, never on sleeps or polling. A test that needs a timeout to pass is wrong.
- Upon request, user-visible frontend changes should get one integrated pass in a real client: `test-t3-app` for web, `test-t3-mobile` for mobile. The primary agent does this once after integrating. Subagents do not launch their own dev servers. Ask permission before doing computer use or spinning up browsers.

For an already-started LastCode checkpoint service run, use the resumable
**Wait for Checkpoint** action instead of agent-side sleep/status loops. Follow
the action handoff and result rules in `docs/lastcode/release.md`; this action
does not start or repair the service.

## Pull requests

- Never make a PR unless the developer explicitly asks you to do so.
- For LastCode PRs targeting `lastcode/main`, including authorized checkpoint or build repair PRs, follow `.agents/skills/lastcode-pr/SKILL.md`. When CI or review is passive and no current finding needs judgement, list Project Actions, launch the eligible **Wait for PR** action by its returned ID (prefer `lc-wait-for-pr`), and end the turn immediately. Use the action again after a fixing push when another wait is needed; do not spend agent turns polling GitHub or the running action. This applies without a separate “babysit” request and does not authorize creating or merging a PR.
- Conventional commit titles, plain language: `fix(web): new threads no longer spike CPU`.
- Body: the problem in a sentence or two, then how you fixed it. End with the model and harness that did the work.
- UI changes need before/after images. Motion or timing needs a short video.
Expand Down
37 changes: 37 additions & 0 deletions docs/lastcode/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ one stable aggregate `CI Gate`. Head or base drift invalidates the result and
requires the agent to decide whether to rebase, republish, and request review
again.

This applies to authorized repair PRs opened during checkpoint or build recovery,
without a separate request to babysit. After handling existing findings, call
`list_project_actions`, select the eligible **Wait for PR** action (prefer
`lc-wait-for-pr`), call `run_project_action_and_resume`, and end the turn.
Executing `scripts/lastcode-wait-for-pr.ts` directly in a shell keeps the agent
turn open and defeats the quota-saving handoff. On resume, inspect the result
and recheck the current revision before continuing.

Merge the current ready PR with:

```bash
Expand All @@ -124,6 +132,35 @@ repair path. The request never terminates a daemon run already in progress.

## Checkpoint CI

### Waiting for the checkpoint service

After an authorized service run has started, use the independent **Wait for
Checkpoint** Project Action (`lc-wait-for-checkpoint`) to observe its completion.
List Actions, launch the eligible returned ID with
`run_project_action_and_resume`, and end the turn immediately. Do not poll
`launchctl`, processes, dashboard output, or log tails while it runs.

The action observes the active local macOS checkpoint service. It does not start
a checkpoint, rebuild, recover, install, or restart anything. It captures the
active process and launch count, waits for it to finish, and reports the terminal
state whose recorded supervisor PID matches that process. A result written while
the supervisor is still delivering notifications is accepted after exit. The
installed supervisor must include this PID field. A missing active run,
unavailable service, replaced run, missing matching
result, or one-hour timeout requires attention; an old success is not proof of
the requested checkpoint. Cancelling the action leaves the service running.
After resume, inspect the result and use `lastcode-checkpoints --verbose` once
for the concrete next recovery or verification step.

The importable declaration is in `t3.json`. As with the PR action, the owning
environment must import it and grant resume permission. Managed installations
use the `lc-wait-for-checkpoint` trust entry. The command loads from
`T3CODE_PROJECT_ROOT`, so an older maintenance-thread worktree can use the
updated observer after the primary checkout is refreshed. If the action is missing or
disabled, report that setup problem rather than reverting to a sleep loop.

### Validating a checkpoint

A release build uses a different full-CI context because rebasing intentionally
rewrites ancestry. Check out the immutable checkpoint or revision and run:

Expand Down
5 changes: 5 additions & 0 deletions scripts/lastcode-checkpoint-supervisor.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,8 @@ export function checkpointFailureMessage(failure, fingerprint) {
"Run lastcode-checkpoints --verbose, inspect the live daemon and retained recovery state, and address the concrete problem.",
"The bounded service logs are ~/.lastcode/automation/nightly-checkpoint.stdout.log and nightly-checkpoint.stderr.log.",
"Use this thread for the recovery; do not create a new maintenance thread and do not only report the alert.",
"For an already-started service run, use the resumable Wait for Checkpoint action as described in docs/lastcode/release.md instead of sleep/status loops. It only observes completion and does not start or repair the service.",
"If the authorized recovery includes a LastCode PR, read .agents/skills/lastcode-pr/SKILL.md. When CI or review is passive and no current finding needs judgement, call list_project_actions, launch the eligible Wait for PR action with run_project_action_and_resume (prefer lc-wait-for-pr), and end the turn immediately without polling. This reminder does not authorize creating or merging a PR.",
]
.filter(Boolean)
.join("\n");
Expand Down Expand Up @@ -652,6 +654,7 @@ export function runCheckpointSupervisor(options = {}, overrides = {}) {
loadConfig: () => readJson(paths.configPath, "checkpoint supervisor config"),
loadState: () => readJson(paths.statePath, "checkpoint supervisor state"),
now: () => new Date().toISOString(),
supervisorPid: process.pid,
notify: (title, message) => notify(title, message, environment),
reconcileProjectActions: (primaryWorktree, trustedProjectActionIds) =>
reconcilePrimaryProjectActions(primaryWorktree, home, trustedProjectActionIds, environment),
Expand Down Expand Up @@ -798,6 +801,7 @@ export function runCheckpointSupervisor(options = {}, overrides = {}) {
}
let state = {
schemaVersion: 1,
supervisorPid: dependencies.supervisorPid,
status: "failed",
phase: failure.phase,
startedAt,
Expand Down Expand Up @@ -857,6 +861,7 @@ export function runCheckpointSupervisor(options = {}, overrides = {}) {
}
let state = {
schemaVersion: 1,
supervisorPid: dependencies.supervisorPid,
status: "success",
phase: "complete",
startedAt,
Expand Down
2 changes: 2 additions & 0 deletions scripts/lastcode-checkpoint-supervisor.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ describe("LastCode checkpoint supervisor", () => {

expect(runCheckpointSupervisor({}, test.dependencies)).toMatchObject({
status: "success",
supervisorPid: process.pid,
phase: "complete",
});
expect(test.dependencies.runPhase.mock.calls.map(([phase]) => phase)).toEqual([
Expand Down Expand Up @@ -544,6 +545,7 @@ describe("LastCode checkpoint supervisor", () => {
expect(() => runCheckpointSupervisor({}, test.dependencies)).toThrow("checkpoint failed");
expect(test.states[0]).toMatchObject({
status: "failed",
supervisorPid: process.pid,
phase: "checkpoint",
incident: { alertDelivery: "pending", deliveryAttempts: 0 },
});
Expand Down
Loading