# Worker Error Contract (canonical)

This audience-neutral file is the single source of truth for every initial worker's error path and typed write command. Read it end-to-end from `**Worker Error Contract Path:**` before work.

## Error reporting

Record a tool call only when its failure blocked the step you intended. A non-zero exit code is NOT by itself a failure to record. Do not record an entry when:

- a search/probe exits non-zero solely because it found no matches and work proceeds unaffected;
- an optional / expected-absent path is absent and detecting absence was the purpose;
- output was truncated but a later read covered the content end-to-end.

If the non-zero exit changed nothing about what you could do next, it is normal. When in doubt, record the real blocker: under-recording a real blocker is worse than one noisy entry.

For each real failure, invoke this typed command. Replace only the angle-bracket values supplied by the dispatch prompt or observed from the failed call. Do not create a JSON file or pass JSON text.

```bash
okstra error-log append-observed \
  --out <absolute-errors-log-path> \
  --task-key <task-key> --phase <phase> \
  --agent <assigned-agent> --agent-role worker --model <assigned-model> \
  --error-type tool-failure --command-file <failed-command-file> \
  --command-kind <command-kind> --message-file <message-markdown-file> \
  --cause sandbox-denied \
  --evidence-file targetProbe=<target-probe-file> \
  --evidence-file controlProbe=<control-probe-file>
```

For a cause other than `sandbox-denied`, replace the cause value and omit both `--evidence-file` arguments unless you observed the required probes. Put any free-form message or probe output in the supplied Markdown file rather than relying on shell quoting.

### Rules

- Use only `--error-type tool-failure`. CLI wrappers write `cli-failure` directly to the run log; the lead records `contract-violation`.
- To record a block (sandbox/permission) as the cause, pass `--cause sandbox-denied` together with both `--evidence-file targetProbe=<target-probe-file>` and `--evidence-file controlProbe=<control-probe-file>`. Without the probes, pass `--cause unknown` — and then keep the `--message-file` content to what you observed (`connection refused`, `1045 access denied`), not what you infer. A block asserted in message prose is rejected exactly like one asserted in the cause. Failing to reach a target and the target being down are not the same fact. Only `sandbox-denied` requires evidence; `service-unavailable` and `auth-failed` do not. Each probe is stored truncated to 256 bytes, so lead with the decisive command and its output.
- [Guideline] Put raw output in `stderrExcerpt` verbatim. Never merge several commands' output into one line, summarize it, or elide it with `...`. Once the host, port, and errno are gone, the cause cannot be recovered later.
- Continue after recording unless the failure makes the task impossible.

### Path extraction (BLOCKING)

Extract `**Errors log path:**` verbatim from the dispatch prompt. If it is absent, return the worker-specific `<SENTINEL_PREFIX>_ERRORS_PATH_MISSING` sentinel and stop. Never synthesize a path from a run-directory pattern.

## Path anchoring

Every `.okstra/...` path is anchored at `**Project Root:**`, never at cwd. A task or stage worktree does **not** contain the project's `.okstra/` directory. Mutating project commands may use `**Worktree:**`, but okstra artifact reads and error writes use absolute paths resolved from Project Root. If a relative read fails in a worktree, re-anchor it to `<Project Root>` instead of retrying the same path.
