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
26 changes: 26 additions & 0 deletions docs/reference/worker.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,32 @@ override of the saved preference, but the named CLI must still be detected: it e
operator intent without silently enabling that agent for later workers. Manage the allowlist from
the web console's **Agents** menu; see [User settings](user-settings.md).

## Process ownership and run receipts

Workers run in the foreground. Keep bounded work attached to its invoking command; continuous
work needs an identified owner, such as a retained terminal or the existing web-hosted worker
supervisor. Model reasoning is not needed to keep the executable running. Terminal/session exit,
app cancellation, logout, and restart may end it; Wrighty provides no detached process manager,
service installation, startup registration, or automatic restart.

The CLI emits a `worker-run-started` NDJSON receipt after host registration and before processing:
`schemaVersion: 1`, `runId`, `registered`, `configurationPathHash`, `configurationRevision`,
`scheduling`, `owner`, `statusCommand`, and `logs`. Scheduling contains the effective target,
agent selection, profile, workspace, filters and limits. Owner identifies the foreground PID and
lifetime. Logs are the invoking terminal's stdout/stderr, not a persisted CLI log file. Run the
status command with the same configuration/cache context. A receipt is not an item success event.

A registration failure reports `registered: false` and a null run ID while foreground execution
continues; retain that command's output instead of launching a duplicate. A `--once` preflight with
no work exits without starting a host or emitting a receipt. A normal host return emits
`worker-run-completed` with run ID, summary, and reason (`finished`, `drained`, `OperatorStopNow`,
or `HostShutdown`). Exceptions instead use the existing error path. Missing output or registration
is not proof that work never started: inspect the item/session and the terminal before retrying.

Use [worker discovery and control](workers.md) to inspect an exact run or request drain/interrupt.
Worker-spawned assessment and implementation processes carry `WRIGHTY_WORKER_CHILD=1`; live worker
launch from that context is refused with `WORKER_RECURSIVE_LAUNCH` to prevent recursive scheduling.

## Requirements-readiness assessment

By default, every fresh worker session starts with a separate requirements-only turn under a
Expand Down
30 changes: 28 additions & 2 deletions docs/reference/workers.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Worker discovery
# Worker discovery and control

`wrighty workers` lists registered worker runs in the current local configuration scope. Plain
listing reads configuration and registry files and probes only registered PIDs and their start
Expand All @@ -8,6 +8,7 @@ up expired registry records.
```sh
wrighty workers --json
wrighty workers --item local:42 --json
wrighty workers show <run-id> --json
```

`--item` resolves the canonical item ID and adds advisory pickup assessment using tracker state
Expand Down Expand Up @@ -66,5 +67,30 @@ workers, failed registration, other users/configurations, and remote machines ca
scope. An unreadable record makes coverage incomplete. Discovery does not broaden permissions or
scan process command lines/environments. Cooperative stop still requires fresh verified identity.

`workers` is read-only. For launching see [worker.md](worker.md); for item action discovery see
`workers` without a control subcommand is read-only. `workers show <run-id>` selects one exact run; a missing run
returns `WORKER_NOT_RUNNING` only with complete registry coverage, otherwise
`WORKER_CONTROL_UNAVAILABLE`. A missing registration alone does not establish the item outcome.
For launching see [worker.md](worker.md); for item action discovery see
[actions.md](actions.md). OS service installation and startup management are operator-managed.

## Cooperative control

```sh
wrighty workers drain <run-id> --yes --json
wrighty workers interrupt <run-id> --yes --json
```

Both commands require explicit `--yes` and operate in the current configuration/cache scope.
They use the same registry protocol polled by CLI and web-hosted workers. Drain closes intake and
finishes the active item; interrupt stops its agent process tree and runs bounded finalization.
The request revalidates the record's run/PID/start identity, host kind, configuration path scope,
liveness, protocol version, and supported mode. No raw process kill is used. Several hosted runs
can share a PID and remain independently controllable. Configuration changes do not prevent
stopping a verified run of that same configuration path; its startup snapshot remains visible.

Success returns `schemaVersion: 1`, with `result.runId`, `requestedMode`, `accepted`, `code`,
`message`, and `completed: false`. This acknowledges persistence, not completed shutdown.
Interrupt escalates drain; a later drain cannot downgrade it. Check the owning terminal's final
output and the item state after exit. Refusals use the normal error envelope, including
`WORKER_NOT_VERIFIED`, `WORKER_IDENTITY_CHANGED`, and `WORKER_CONTROL_UNSUPPORTED`.
External restart/startup configuration is not changed by a cooperative stop.
6 changes: 4 additions & 2 deletions skills/wrighty/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
---
name: wrighty
description: Safely operate Wrighty through the `wrighty` CLI. Use only when the user explicitly asks to use Wrighty, the Wrighty CLI, or a Wrighty work-item ID, including board overview, worker discovery, pickup assessment, Queue/Send back/Resume actions, and requests to list, inspect, create, pick, claim, edit, move, finish, archive, or release tracked work. Do not trigger for generic tasks, GitHub issues, planning, backlogs, or project management that do not explicitly identify Wrighty.
description: Safely operate Wrighty through the `wrighty` CLI. Use only when the user explicitly asks to use Wrighty, the Wrighty CLI, or a Wrighty work-item ID, including board overview, worker discovery, launch/control, pickup assessment, Queue/Send back/Resume actions, and requests to list, inspect, create, pick, claim, edit, move, finish, archive, or release tracked work. Do not trigger for generic tasks, GitHub issues, planning, backlogs, or project management that do not explicitly identify Wrighty.
---

# Wrighty

<!-- wrighty-skill-version: 0.19.0 -->
<!-- wrighty-skill-version: 0.20.0 -->

Operate Wrighty state only through the `wrighty` command. Never mutate tracked state by editing
local Markdown, invoking `gh`, calling GitHub APIs/MCP, writing claim comments, or changing Project
Expand All @@ -15,6 +15,8 @@ For board overview, triage, individual and batch Queue/Send back/Resume actions,
[references/board-and-workers.md](references/board-and-workers.md). Use `list --json` for workflow
columns, `actions <id> --json` for the shared action catalogue, and `workers [--item <id>] --json`
for scoped worker evidence. Discovery does not authorize mutations or worker launches.
For authorized foreground/terminal worker launch, exact-run inspection, drain, or interrupt, read
[references/worker-control.md](references/worker-control.md).

## Workflow

Expand Down
2 changes: 2 additions & 0 deletions skills/wrighty/references/board-and-workers.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,5 @@ wait or when confirming an authorized operation, and stop on meaningful progress
needed user decision. A conversation is not a guarantee of unattended monitoring. Worker-spawned
implementation sessions must not recursively launch workers. Never run a paid provider probe
merely to strengthen an assessment.

For authorized launches and cooperative control, read [worker-control.md](worker-control.md).
103 changes: 103 additions & 0 deletions skills/wrighty/references/worker-control.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Worker launch and control

Read [board-and-workers.md](board-and-workers.md) for discovery and pickup evidence. Queueing,
resuming an item, and launching a worker are separate effects. Use the user's existing authorization
when it covers the selected items, agent, processing limits, and unattended execution; do not ask
again for an already authorized effect.

## Decide whether to launch

Before launching for a named item, run `get <id> --json`, `actions <id> --json`, and
`workers --item <id> --json`. For queue processing, inspect `workers --json` and the requested board
selection. If a run already processes the item, report it; do not launch competing work. If a
suitable worker could pick it up, wait only when requested, with a bounded observation period.
Unknown or incomplete evidence is not permission to launch a replacement. Explain the missing
information and resolve it or get an explicit decision about launching despite that uncertainty.

Choose a worker agent from explicit user intent, item policy, or configured default. The agent
hosting this conversation is not a default. Preserve configured workspace and profile choices
unless the user requests an override. Never probe a paid vendor merely to check readiness.

Use the smallest processing scope that meets the request:

```shell
wrighty worker --item <id> --item-timeout 30m --yes --json
wrighty worker --once --item-timeout 30m --yes --json
wrighty worker --max-items 3 --idle-timeout 5m --item-timeout 30m --yes --json
```

`--item` selects exactly one item, including existing continuation rules; `--once` selects the next
eligible item. Do not replace one with the other. Use `--agent`, `--profile`, `--filter name=value`,
`--workspace-mode`, `--from`, and `--to` only within the requested scope. `--item-timeout` bounds each
item, not the whole worker. `--max-items` bounds item count, and `--idle-timeout` bounds an idle
period. A targeted run already has an effective item limit of one. Do not take over claims or force
`--fresh`, `--resume`, or `--handoff` merely to get past a refusal.

## Own the process honestly

For Codex, Claude, Copilot, and OpenCode surfaces, foreground execution is the portable path.
Keep a bounded run attached to the invoking command, consuming its output through completion.
For authorized continuous work, use a user-owned terminal or a host-provided retained terminal
whose lifetime is established. For example, in that terminal:

```shell
wrighty worker --idle-timeout 30m --item-timeout 30m --yes --json
```

State the terminal/process owner and its actual cancellation and exit behavior before launching.
A retained terminal keeps the executable running without a model reasoning loop, but is not a
promise of survival across task cancellation, app exit, logout, or restart. There is no Wrighty
detach command. Do not emulate one with background shell syntax or invent a keepalive loop.
If the available tool cannot retain the process for the required duration, provide the exact
foreground command for the user's terminal and say it has not been started.

Existing web-hosted runs can also provide continuous processing. Their launch remains in the web
console; starting `wrighty web` alone does not start a worker. Do not spoof its browser requests.
No vendor-specific detached launch path is offered by this skill.

Worker-spawned sessions must not start workers recursively. `WRIGHTY_WORKER_CHILD=1` marks that
context and live launch returns `WORKER_RECURSIVE_LAUNCH`; do not remove the marker to bypass it.

## Read the launch outcome

`worker --json` streams NDJSON. Preserve the `worker-run-started` receipt: its `runId`, `registered`,
configuration scope/revision, structured `scheduling`, foreground `owner`, `statusCommand`, and
`logs` identify the actual run and limits. Registration precedes work execution; it is not proof
that an agent has started or that an item succeeded. Item events report that progress.

Run inspection/control in the same configuration and cache context as the launch. Logs remain in
the invoking terminal's stdout/stderr; Wrighty does not create a durable CLI run-log file. Save or
retain that output using the host's supported facility when the request needs it.

`registered: false` means execution may continue without registry control. Keep the owning
command attached and observe it; do not start a duplicate to obtain a run ID. An empty `--once`
preflight exits without a launch receipt. `worker-run-completed` reports the summary and stop
reason after the host has unwound. Read item state as well when interruption or failure needs
recovery. An error or missing receipt alone does not establish that nothing started: inspect
workers, item/session state, and terminal output before any retry.

## Inspect, drain, or interrupt an exact run

```shell
wrighty workers show <run-id> --json
wrighty workers drain <run-id> --yes --json
wrighty workers interrupt <run-id> --yes --json
```

Inspect the exact run and explain the requested effect unless already authorized. Drain closes
intake and lets the active item and bookkeeping finish. Interrupt cancels the active agent process
tree and invokes the existing bounded finalizer; it is not a normal successful completion.

Both CLI and web-hosted runs use the existing registry/control protocol. Never kill a PID: hosted
runs can share a web process. Control re-reads the record and verifies process/start identity,
configuration scope, protocol, and supported mode. Stale, unknown, changed, or legacy identities
produce a refusal. Do not bypass it or fall back to an OS process kill.

An accepted response has `completed: false`: the request was persisted, not necessarily observed
by the worker yet. Observe the owning terminal and re-inspect the run within a bounded interval.
A missing registration is not proof that item bookkeeping succeeded; inspect the item after exit.
Interrupt can escalate a pending drain; a later drain never downgrades an interrupt. If a control
response is ambiguous, refresh before retrying the same run; never select a replacement run
implicitly. An external supervisor may restart a stopped process; stopping does not disable that
supervisor. OS services, startup installation, automatic restart, and boot persistence remain
outside this skill's workflow.
22 changes: 11 additions & 11 deletions src/Highbyte.Wrighty.Cli/CliApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1399,6 +1399,7 @@ private async Task<int> ExecuteWorkerAsync(WorkerOptions options, CancellationTo
};
ValidateWorkerInvocation(
checkOnly, item, requireResume, requireFresh, requireHandoff, options.Profile);
if (!options.DryRun) WorkerLaunchGuard.EnsureAllowed();
if (checkOnly)
{
await workerService.CheckAsync(options.Agent ?? config.EffectiveWorker.DefaultAgent,
Expand Down Expand Up @@ -1539,28 +1540,27 @@ private async Task<WorkerRunSummary> RunWorkerAsync(
: string.Empty);
using var control = new WorkerRunControl();
var host = new WorkerRunHost(workerService!, workerInstances);
var identity = new WorkerRunIdentity(workingDirectory, configPath, revision,
WorkerInvocationSummary(options, selection.Item, selection.Intent), WorkerHostKind.CliProcess);
var runSelection = new WorkerRunSelection(
selection.Item is null ? null : tracker.ResolveId(config, selection.Item), selection.Intent, selection.ClaimToken);
var summary = await host.RunAsync(
config,
options,
new WorkerRunIdentity(
workingDirectory,
configPath,
revision,
WorkerInvocationSummary(options, selection.Item, selection.Intent),
WorkerHostKind.CliProcess),
new WorkerRunSelection(
selection.Item is null ? null : tracker.ResolveId(config, selection.Item),
selection.Intent,
selection.ClaimToken),
identity,
runSelection,
control,
new WorkerRunCallbacks(
ordinaryOutput,
message =>
{
CliDiagnostics.WorkerRuntimeWarning(diagnostics, message);
return Task.CompletedTask;
}),
},
runId => WriteWorkerLaunchAsync(runId, identity,
WorkerScheduling.From(config, options, identity, runSelection), options.Json)),
cancellationToken);
await WriteWorkerCompletionAsync(control, summary, options.Json);
cancellationToken.ThrowIfCancellationRequested();
return summary;
}
Expand Down
Loading