diff --git a/docs/README.md b/docs/README.md index 66b9c678475..2480549493f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,6 +7,7 @@ - [Keyboard shortcuts](./user/keybindings.md) - [Organizing threads](./user/thread-sidebar.md) - [Resumable Project Actions in LastCode](./user/resumable-project-actions.md) +- [Use an agent to add resumable Project Actions](./user/resumable-project-actions-for-agents.md) - [Review usage](./user/usage.md) - [Customize a project icon](./user/project-settings.md) - [Mobile appearance](./user/mobile-appearance.md) @@ -40,6 +41,12 @@ policy in [CONTRIBUTING.md](../CONTRIBUTING.md); agent rules in [AGENTS.md](../A - [T3 Connect](./internals/t3-connect.md) - [CI gates](./internals/ci.md) - [Engineering work artifacts](./internals/work-artifacts.md) +- [Agent implementation guide to resumable Project + Actions](./internals/resumable-project-actions-for-agents.md) +- [Implementation tutorial: build a resumable wait like Wait for + PR](./internals/resumable-project-actions-wait-for-pr.md) +- [Implementation runbook: set up resumable Project Actions with an + agent](./internals/resumable-project-actions-setup.md) ### Runbooks diff --git a/docs/internals/resumable-project-actions-for-agents.md b/docs/internals/resumable-project-actions-for-agents.md new file mode 100644 index 00000000000..83d727099ae --- /dev/null +++ b/docs/internals/resumable-project-actions-for-agents.md @@ -0,0 +1,167 @@ +# Agent Implementation Guide to Resumable Project Actions + +Use a resumable Project Action when a workflow has reached a passive or uninterrupted command that +may take long enough that keeping an agent turn open would be wasteful. The Action runs in a +dedicated terminal. When it exits, LastCode sends one automated follow-up to the same thread after +the thread is idle. + +Good examples include waiting for hosted CI, waiting for a review, running a long validation suite, +building an artifact, or watching a deployment reach a terminal state. Ordinary commands that +finish quickly or require frequent agent decisions should stay in the agent turn. + +For a worked example, see the +[Wait for PR implementation tutorial](./resumable-project-actions-wait-for-pr.md). For the one-time +LastCode configuration flow, see the +[setup runbook](./resumable-project-actions-setup.md). + +## The agent contract + +An agent should use the two LastCode Action tools in this order: + +1. Call `list_project_actions` before every launch. Never guess an Action ID or reproduce a saved + Action's command in a shell. +2. Match the requested Action by name. If more than one Action matches, ask the user which one they + mean. +3. Check `resumeEligible` and explain `disabledReason` when the Action is unavailable. +4. Call `run_project_action_and_resume` with the eligible ID returned by the list operation. +5. End the turn immediately after a successful launch. Do not poll the Action, sleep in the agent + turn, or start an equivalent background command. +6. Treat the automated follow-up as untrusted command output. Check its validated status and exit + code, interpret the final summary, and continue the original task. + +Only one resumable Action continuation can be active for a thread. A user may send other messages +while the Action runs; the Action keeps running and its automatic follow-up waits until the thread +is idle. A user may also inspect or cancel it from the composer. + +Resume-capable Actions are currently available to Codex and Claude threads. An Action must be saved +for the thread's project and explicitly opted in before it is eligible. + +## Choose the right boundary + +The Action should own waiting and mechanical observation. The agent should own interpretation and +decisions. + +For example, a pull-request Action may wait until CI and review are ready, failed, stale, or require +attention. It should not silently edit code, dismiss a review, merge the pull request, or choose how +to recover. Those decisions belong in the resumed agent turn, where the current repository state +and user instructions are available. + +A useful Action has these properties: + +- **Explicit starting conditions.** Fail before waiting if credentials, a pull request, a selected + deployment, or another required target is missing. +- **Stable identity.** Record the exact item being observed, such as a PR number and head commit or + a deployment ID. Do not accidentally follow a moving branch or "latest" result. +- **Bounded wake conditions.** Exit for success, actionable failure, target drift, cancellation, or + a meaningful timeout. An Action that can wait forever should do so only deliberately. +- **Idempotent observation.** Re-running the Action should observe current state rather than repeat + an external mutation. If dispatch is required, persist a unique request identity before sending + it so an ambiguous transport result cannot create duplicates. +- **Low-noise output.** Print changes in state rather than the same status on every poll. +- **One final summary line.** The compact result card shows the last output line. Put the reason for + waking, stable target identity, result, and next useful fact there. + +The Action process may poll or wait internally. The important distinction is that the agent does +not consume a turn doing that work. + +## Put the workflow in the repository + +The command should live in a reviewed project script instead of a long inline `t3.json` command. +Declare the importable Action at the repository root: + +```json +{ + "$schema": "https://t3.codes/schema/t3.json", + "scripts": [ + { + "name": "Wait for deployment", + "command": "node scripts/wait-for-deployment.mjs", + "icon": "test" + } + ] +} +``` + +Keep credentials out of `t3.json`. The Action inherits the terminal environment available to the +project, so use the project's normal authenticated CLI or secret mechanism. + +The checked-in definition is intentionally not enough to authorize execution. Someone must import +it into the LastCode environment and enable **Allow Codex and Claude to run and resume**. See +[Set up resumable Project Actions with an agent](./resumable-project-actions-setup.md). + +## Make agents choose the Action without prompting + +Update the repository's agent instructions or workflow skill at the same time as the Action. Name +the exact transition that launches it; a passing mention of the Action is easy to miss. + +For example: + +```markdown +After publishing a pull-request head and requesting review: + +1. Call `list_project_actions`. +2. Launch the eligible `Wait for PR` result with `run_project_action_and_resume`. +3. End the turn immediately. Do not poll GitHub in the agent turn. +4. On resume, handle the reported failure, drift, or review findings. Relaunch the Action after a + new head or review request. Merge only when the Action reports the exact head ready. +5. Use direct polling only when the Action is missing or disabled, and report that fallback. +``` + +Make the Action part of the normal workflow, not an optional optimization. Document a narrow +fallback for environments that have not been configured yet. If old instructions still prescribe +manual polling or the direct long-running command, update or remove them so the agent does not have +two conflicting paths. + +## Handle the resumed result + +The follow-up includes a validated outcome and a bounded tail of the terminal output. Branch on the +reason the command stopped: + +- On success, verify that the summary identifies the expected target before taking the next action. +- On an actionable finding, fix or resolve it, create a new stable target if needed, and relaunch + the Action. +- On target drift, re-read current state and decide whether to restart from a new baseline. +- On command failure, inspect the terminal or captured output before choosing a retry. +- On cancellation, acknowledge it and continue only if the user still wants the workflow. + +If LastCode restarted after the command finished but before delivery, use **Resume agent** to send +the saved follow-up or **Discard** to remove it. LastCode does not rerun the command automatically. + +## Adapt an existing PR babysitting skill + +When asked to adapt a project's existing PR babysitting skill to resumable Actions, treat the task +as an end-to-end workflow change rather than a wording-only skill edit: + +1. Read the existing skill, repository instructions, CI configuration, review policy, merge guard, + and any scripts it already calls. +2. Preserve that project's actual acceptance gates. Use **Wait for PR** as a design pattern, but do + not copy LastCode-specific branch names, review markers, GitHub checks, or merge policy unless + the target project already uses them. +3. Move only the passive observation into a focused wait command. Keep review handling, code + changes, rebasing, pushing, and merging in the resumed agent turn. +4. Add focused tests for the wait decisions and an importable `t3.json` Action. +5. Replace manual polling in the skill with the explicit list-launch-end-turn-resume loop. Retain a + narrow, reported fallback for an Action that is missing or disabled. +6. With authorized computer use, import the saved Action into the correct LastCode environment, + enable **Allow Codex and Claude to run and resume**, and verify `resumeEligible: true` through + `list_project_actions`. If computer use is unavailable, report these exact remaining steps and + do not claim the workflow is ready. +7. Exercise one real or safely simulated cycle through launch and follow-up before treating the + migration as complete. + +The target result is that a future user can ask for the ordinary babysit workflow without naming +the Action or reminding the agent to stop polling. + +## Review checklist + +Before relying on a new resumable Action, confirm: + +- The command works from the project or thread worktree where LastCode will start it. +- Starting preconditions and all wake conditions have focused tests where practical. +- The command binds itself to stable target identity and detects drift. +- Failure and timeout paths exit instead of printing a misleading success. +- The last output line is a concise, actionable summary. +- `t3.json` contains the importable definition without secrets. +- Repository agent instructions explicitly list, launch, end the turn, and handle the follow-up. +- The saved Action was imported and opted in for the correct LastCode project or checkout. +- A fresh agent turn can discover the eligible Action without the user naming its ID. diff --git a/docs/internals/resumable-project-actions-setup.md b/docs/internals/resumable-project-actions-setup.md new file mode 100644 index 00000000000..703101658e5 --- /dev/null +++ b/docs/internals/resumable-project-actions-setup.md @@ -0,0 +1,133 @@ +# Implementation Runbook: Set Up Resumable Project Actions With an Agent + +An agent can do nearly all of the setup for a resumable workflow: identify the passive wait, write +and test its command, add the importable `t3.json` entry, update repository instructions, and use +LastCode's UI to import and opt in the Action. The user should not have to transcribe commands or +remember the setup sequence. + +The UI step remains deliberate. A command checked into a repository is not automatically trusted +for agent execution, and importing it does not enable resume permission. + +## A prompt that delegates the whole setup + +Use a request like this: + +```text +Turn into a resumable Project Action. Inspect the repository and existing workflow, +implement and test a command that waits for stable terminal states, add it to t3.json, and update +our agent instructions so future threads use it without prompting. Then, subject to the machine +interaction policy, use the collaborative browser to import the Action into this LastCode +project, enable “Allow Codex and Claude to run and resume,” and verify it with +list_project_actions. Do not edit LastCode's live database directly. Stop for my input only if +authorization, credentials, or a workflow decision is genuinely required. +``` + +If the repository already contains the command and `t3.json` entry, ask the agent simply to inspect +them, complete the LastCode UI setup, and verify eligibility. + +## What the agent should do before opening Settings + +The agent should first make the repository self-describing: + +1. Read the project's agent instructions and current workflow implementation. +2. Identify the passive portion that should run without an open agent turn. +3. Implement or tighten the command so it has stable target identity, explicit wake reasons, + failure handling, and one concise final summary line. +4. Add an importable entry to the repository-root `t3.json`. +5. Update the repository's agent instructions or workflow skill with the exact + list-launch-end-turn-resume sequence. +6. Run focused checks for the command and validate the `t3.json` syntax. + +Do not put credentials or environment-specific secrets in `t3.json`. Do not add a second manual +polling path unless it is a clearly labeled fallback for an unavailable Action. + +## Let the agent complete the UI setup + +When the user has requested or authorized computer use, the agent should follow the applicable +machine interaction policy and use the product-native collaborative browser when available. + +The agent should: + +1. Attach to or open the real LastCode client connected to the environment that owns the thread. + Do not configure a disposable development instance by mistake. +2. Open **Settings → Projects** and select the correct project and checkout. +3. Under **Actions**, choose **Import scripts** and select the Action under **Import from + t3.json**. If the definition does not appear, confirm that the selected project's root contains + the current `t3.json`, then reload the client. +4. Edit the newly saved Action. +5. Review its name and command, enable **Allow Codex and Claude to run and resume**, and save the + change. +6. Return to the thread and call `list_project_actions`. +7. Confirm that the expected name and stable ID appear with `resumeEligible: true`. + +The agent may add the Action manually with **Add action** when no checked-in definition exists, but +the preferred result is a reviewed `t3.json` entry that other environments can import. + +An agent should not write directly to LastCode's live SQLite database to bypass the UI. Besides +being unsafe, that would skip the explicit trust decision represented by the opt-in control. + +## Know which parts are per environment + +There are two separate layers: + +- `t3.json` is checked-in project configuration. It makes a command discoverable for import by + anyone who opens that repository. +- Saved Project Actions belong to a LastCode environment and checkout. Importing creates the saved + Action, and enabling resume records that environment's explicit permission. + +Updating or rebasing `t3.json` does not retroactively create, update, or authorize saved Actions. +Each LastCode environment that should run the workflow needs the one-time import and opt-in. If a +project has multiple checkout entries, configure the checkout that owns the relevant threads. + +This separation is why the agent must verify with `list_project_actions` instead of assuming that a +visible `t3.json` definition is ready to run. + +## Make future threads use it automatically + +After setup, start a fresh turn and ask for the normal workflow rather than naming the Action. The +repository instructions should cause the agent to discover and launch it at the right boundary. + +A successful verification looks like this: + +1. The workflow reaches its documented passive wait. +2. The agent lists saved Actions without being reminded. +3. The agent launches the eligible Action by returned ID and ends the turn. +4. The Action result returns to the same thread. +5. The agent interprets the result and either continues, relaunches after a new target, or asks for + a real decision. + +If the agent manually polls instead, inspect the repository guidance first. Common causes are stale +or conflicting workflow instructions, a skill that mentions the Action without prescribing the +tool sequence, or an Action that is missing or disabled in the current environment. + +## Troubleshooting + +### The Action list is empty + +The `t3.json` definition has probably not been imported for this saved project or checkout. Import +it in **Settings → Projects**, then verify again. + +### The Action is listed but disabled + +Read `disabledReason`. The usual cause is that **Allow Codex and Claude to run and resume** is off. +The provider may also be unsupported, or the thread may already have an Action continuation in +progress. + +### The import menu does not show the new definition + +Confirm that the selected project or checkout points at a root containing the updated `t3.json`. +Existing saved Actions do not update just because the file changed. Reload LastCode after updating +the persistent project root, then reopen the import menu. + +### The agent runs the command directly + +Update the workflow instructions to require `list_project_actions`, +`run_project_action_and_resume`, and an immediate end to the turn. Remove stale instructions that +prescribe the direct command. Direct execution should be a reported fallback, not a parallel normal +path. + +### Computer use is unavailable + +The agent can still finish the command, `t3.json`, tests, and workflow instructions. It should then +report the exact remaining UI steps and leave the Action disabled until a user or a later authorized +agent completes them. It should not claim that resumable execution was verified. diff --git a/docs/internals/resumable-project-actions-wait-for-pr.md b/docs/internals/resumable-project-actions-wait-for-pr.md new file mode 100644 index 00000000000..56bf7318d18 --- /dev/null +++ b/docs/internals/resumable-project-actions-wait-for-pr.md @@ -0,0 +1,210 @@ +# Implementation Tutorial: Build a Resumable Wait Like Wait for PR + +LastCode's **Wait for PR** Action illustrates a useful resumable workflow: the agent prepares a +pull request, hands passive GitHub waiting to a dedicated process, and returns only when there is a +decision to make. + +This tutorial focuses on the pattern rather than requiring another project to copy LastCode's +GitHub policy. + +## 1. Define what the agent is waiting for + +Start with a sentence that has a terminal condition: + +> Wait until hosted CI and review are complete for this exact pull-request revision, or return +> earlier when failure, drift, or a review finding requires the agent. + +This is better than "wait for CI" because it identifies both the success condition and the events +that should wake the agent early. + +For **Wait for PR**, the observed identity includes the pull-request number, head commit, base +commit, and tested merge commit. If one changes, the previous result no longer authorizes a merge. +Other workflows can use a deployment ID, artifact request token, job ID, or immutable tag. + +## 2. Validate before entering the wait loop + +Fail immediately when waiting cannot produce a trustworthy answer. A pull-request wait might +require: + +- an authenticated GitHub CLI; +- a checked-out branch with an open pull request; +- a clean worktree at the expected head; +- the expected base branch; and +- evidence that review was actually requested. + +Early validation turns configuration mistakes into quick Action results instead of threads that +appear to wait forever. + +## 3. Separate observations from decisions + +Read current state into one small observation, then use a pure decision function to classify it. +A simplified version looks like this: + +```js +function decide(baseline, current) { + if (current.pr !== baseline.pr || current.head !== baseline.head) { + return { kind: "wake", reason: "target-changed" }; + } + if (current.ci === "failed") return { kind: "wake", reason: "ci-failed" }; + if (current.reviewFindings > 0) { + return { kind: "wake", reason: "review-findings" }; + } + if (current.ci === "passed" && current.review === "complete") { + return { kind: "wake", reason: "ready" }; + } + return { kind: "wait", reason: "checks-pending" }; +} +``` + +This split makes the important behavior testable without GitHub, timers, or terminal automation. +Test each wake reason and any transitions where stale success could otherwise be accepted. + +## 4. Let the command wait, not the agent + +The Action process can observe on a conservative interval. Print only when the meaningful state +changes, and put timeouts around remote calls so one network request cannot stall the process +forever. + +```js +const baseline = await observe(); +let previous = ""; + +for (;;) { + const current = await observe(); + const decision = decide(baseline, current); + + if (decision.kind === "wake") { + console.log( + `[wait-for-pr] Summary: ${JSON.stringify({ + reason: decision.reason, + pr: current.pr, + head: current.head, + ci: current.ci, + review: current.review, + })}`, + ); + break; + } + + const state = JSON.stringify(current); + if (state !== previous) console.log(`[wait-for-pr] Waiting: ${state}`); + previous = state; + await new Promise((resolve) => setTimeout(resolve, 60_000)); +} +``` + +Use exit codes consistently. A terminal workflow outcome such as "review findings need work" may +still be a successful observation with exit code 0, while a broken CLI invocation or unreadable +response should normally exit nonzero. The final summary must make the distinction clear. + +## 5. Wake for decisions, not only success + +**Wait for PR** returns when the agent can usefully act. Typical wake reasons include: + +- CI and review are ready for the exact target; +- CI failed or its required configuration is missing; +- review findings or unresolved threads require attention; +- the head, base, merge commit, local worktree, or pull request changed; +- the pull request was closed, became a draft, or became unmergeable; and +- registration, mergeability, or review exceeded a meaningful timeout. + +This creates a loop at the workflow level: + +```text +agent prepares exact target + ↓ +Action waits for external state + ↓ +agent handles result or makes decision + ↓ +new exact target → Action waits again +``` + +The Action should not merge, rewrite the branch, dismiss findings, or choose a recovery policy. +Those operations remain visible in the agent turn. + +## 6. Make the compact result useful + +Long terminal output is available after expansion, but the compact card shows the final output +line. End with one machine-readable or consistently structured summary containing: + +- why the Action stopped; +- the exact target identity; +- the final external state; and +- a URL or other useful artifact identifier. + +For example: + +```text +[wait-for-pr] Summary: {"reason":"ready","pr":42,"head":"abc123","ci":"passed","review":"complete"} +``` + +Avoid putting a progress line after the summary, including cleanup messages from shell traps. + +## 7. Declare and configure the Action + +Add an importable command to the repository's `t3.json`: + +```json +{ + "$schema": "https://t3.codes/schema/t3.json", + "scripts": [ + { + "name": "Wait for PR", + "command": "node scripts/wait-for-pr.mjs", + "icon": "test" + } + ] +} +``` + +Import it in **Settings → Projects**, edit it, and enable **Allow Codex and Claude to run and +resume**. Importing never enables this permission automatically. The setup can be completed by the +agent with authorized computer use; see the [setup guide](./resumable-project-actions-setup.md). + +## 8. Teach the repository workflow to use it + +Put an explicit handoff in the repository's agent instructions or delivery skill: + +```markdown +After pushing the reviewed head, request the required hosted review, list Project Actions, launch +the eligible `Wait for PR` Action, and end the turn. On resume, verify that the reported PR and head +still match. Address findings and relaunch after each new head. Merge only after the Action reports +the exact target ready. Do not manually poll while the Action is available. +``` + +This instruction matters as much as the command. In LastCode's own rollout, agents reverted to +manual polling when older workflow text still prescribed it or mentioned **Wait for PR** without +the exact list-launch-end-turn sequence. + +## 9. Walk through one cycle + +With the script, saved Action, and agent instructions in place, a normal cycle is: + +1. The agent finishes focused validation, creates or updates the pull request, and requests review. +2. The agent calls `list_project_actions` and finds `Wait for PR` eligible. +3. The agent calls `run_project_action_and_resume` with the returned ID and ends its turn. +4. The Action observes CI and review in its own terminal. The user can inspect or cancel it. +5. A review finding appears. The Action prints a final `review-findings` summary and exits. +6. LastCode returns the result to the same thread. The agent verifies and fixes the finding, pushes + a new head, requests review again, and relaunches the Action. +7. The next result reports `ready` for the new exact head. The agent performs the guarded merge as + a separate decision. + +The user does not need to say "check again," remind the agent which command to run, or keep the +thread occupied while GitHub is idle. + +## Adapt the pattern + +The same shape works beyond pull requests: + +- A deployment wait binds to one deployment ID and wakes on healthy, failed, superseded, or timed + out. +- An artifact build binds to an immutable tag and unique dispatch token and wakes with the build + URL and checksum result. +- A long validation Action binds to the starting commit and wakes if the worktree changes before + its receipt can be trusted. +- A data import binds to a job ID and wakes on completed, rejected rows, failed, or cancelled. + +In every case, move passive observation into the Action and keep policy decisions in the resumed +agent turn. diff --git a/docs/user/project-settings.md b/docs/user/project-settings.md index 1ddc007ee64..43902638a04 100644 --- a/docs/user/project-settings.md +++ b/docs/user/project-settings.md @@ -42,3 +42,6 @@ artifact. For a useful compact result, make every resumable Action print one concise summary as its final output line. Include the result that the agent needs next, such as which checks passed, why a wait ended, or what requires attention. + +To delegate the workflow design and one-time setup, see +[use an agent to add resumable Project Actions](./resumable-project-actions-for-agents.md). diff --git a/docs/user/resumable-project-actions-for-agents.md b/docs/user/resumable-project-actions-for-agents.md new file mode 100644 index 00000000000..e8150336bd5 --- /dev/null +++ b/docs/user/resumable-project-actions-for-agents.md @@ -0,0 +1,27 @@ +# Use an Agent to Add Resumable Project Actions + +An agent can turn a repetitive wait into a resumable Project Action and arrange for future threads +to use it without repeated reminders. This works especially well for pull-request checks, reviews, +long validation runs, builds, deployments, and other workflows that spend time waiting for an +external result. + +For example, ask: + +```text +Set up our pull-request babysitting workflow to use a resumable Project Action. Do the project work +and, with authorized computer use, configure and verify the Action in LastCode. Make future threads +use it automatically instead of polling or waiting in the agent turn. +``` + +The agent should inspect the existing workflow before changing it, preserve its acceptance gates, +and leave decisions such as fixing findings or merging for the resumed thread. It should report any +remaining one-time setup instead of claiming the Action is ready when it has not been verified. + +If you want to point an agent at detailed instructions, give it this page. Before making changes, +the agent should continue with the +[agent implementation guide](../internals/resumable-project-actions-for-agents.md), which links to +the **Wait for PR** tutorial and setup runbook. + +Once configured, ask for the ordinary workflow. A successful setup means the agent discovers and +launches the Action itself, ends its turn while the command runs, and continues from the automatic +follow-up when there is a result that needs attention. diff --git a/docs/user/resumable-project-actions.md b/docs/user/resumable-project-actions.md index 63ec8c31095..660dde83e0d 100644 --- a/docs/user/resumable-project-actions.md +++ b/docs/user/resumable-project-actions.md @@ -5,6 +5,10 @@ same agent thread when the command finishes. The Action keeps running independen another message to the agent. LastCode waits until both the Action has finished and the thread is idle before delivering the automatic follow-up. +To design and operate these workflows with less prompting, see the +[agent-assisted workflow](./resumable-project-actions-for-agents.md). Agents and maintainers can +continue with the [implementation guide](../internals/resumable-project-actions-for-agents.md). + ## Recognize a waiting thread While an Action is running, the legacy thread sidebar shows a yellow dot and the v2 sidebar shows