# Operator Questions

Blueprint Runtime Executor is the canonical operator question creation model.
`ai/tools/operator-questions` remains the durable state layout plus
compatibility command surface for answers, waits, status, and dispatcher
integration. All new human/operator question creation should flow through
`runtime.executor.createQuestion`.

Cross-cutting close/commit wording and operator-facing docs/help synchronization
rules live in `ai/standards/runtime-tooling-governance.md`.
Top-level runtime operating procedures live in `ai/standards/runtime-sop.md`.

## Model

- One question object.
- One accepted answer.
- Local Codex/operator console and Telegram are alternative answer channels.
- The first valid answer wins.
- Invalid answers do not consume the question.
- Late answers are stale and must not affect any future question.
- Waiting has no timeout by default. Tests may pass explicit short timeouts.

## Supported Answer Types

- `yes-no`: accepts yes/no and Telegram tokenized yes/no replies.
- `numbered`: accepts option number or exact option text.
- `fixed`: accepts one of the configured fixed textual answers. Telegram should
  render command-safe fixed answers as tokenized slash commands such as
  `/retry_<token>` and `/stop_<token>`. The bridge must also accept Telegram's
  bot-suffixed form, for example `/retry_<token>@BotName`, and return the
  compact answer-recorded response instead of generic command help.
- `freeform`: accepts arbitrary text only when explicitly enabled with
  `--freeform`; optional regex constraints may narrow accepted text.
- Freeform questions are valid only for `freeform_input_required` events under
  `ai/standards/runtime-event-semantics.md`. Stale runtime artifacts,
  validation failures, runtime warnings, lifecycle transitions, and
  Codex-internal context must not be converted into freeform questions.

## Public Commands

```sh
node ai/runtime/dist/cli.js request --method runtime.executor.createQuestion --params '{"type":"yes-no","question":"Continue?"}'
ai/tools/operator-questions/ask.sh --type yes-no --question "Continue?" --wait  # compatibility facade
ai/tools/operator-questions/answer.sh --id <question-id> --answer yes --source local
ai/tools/operator-questions/wait-answer.sh <question-id>
ai/tools/operator-questions/consume-pending.sh
ai/tools/operator-questions/status.sh
ai/tools/operator-questions/list.sh --pending
ai/tools/operator-questions/list.sh --pending --json
ai/tools/operator-questions/list-approved-actions.sh
ai/tools/operator-questions/resolve-stale.sh --id <id> --reason "<reviewed reason>"
ai/tools/approved-action-dispatcher/dispatch.sh --dry-run --once
```

Telegram mirroring is compatibility plumbing. New workflow code must not call
`create-checkpoint.mjs` directly for ordinary operator questions.

Operators and Codex must use `list.sh` or `status.sh --json|--kv` for
inspection. Raw `.tmp/operator-questions/*.env` files are runtime state, not a
normal operator interface.

Reviewed abandoned questions should be resolved with `resolve-stale.sh`; do not
delete question files blindly and do not record a fake accepted answer.

For registered trusted runtime actions, `ai/tools/operator-daemon` creates the
question and consumes the first valid local or Telegram answer. Telegram is an
answer channel, not the executor.

If Telegram has accepted a tokenized answer but the matching operator-question
record is still pending, run `ai/tools/operator-questions/consume-pending.sh`.
Telegram "Approved" is not complete until the canonical operator-question
answer file exists.

## Approval Validity

Approval authorizes operator intent. It does not guarantee execution. Execution
must either be consumed by the same live workflow run or explicitly resumed
through a validation dry-run.

Approval questions that authorize lifecycle-sensitive actions, including
close/commit and other registered daemon actions, must create a durable
approved-action intent record when the action can outlive the current Codex
run. The intent binds the approval to:

- question id.
- run id.
- approved action.
- action type.
- target chunks or files.
- git status hash at question creation.
- validation state hash.
- runtime state hash.
- creation and acceptance timestamps.
- execution status.
- approval metadata needed to reconstruct the target exactly.

Late Telegram approval after Codex has stopped must not silently execute inside
Codex. It becomes an approved, unexecuted action. The deterministic
continuation path is the approved-action dispatcher, not Codex tmux/TUI wakeup
or pane scraping. Telegram approval records intent, approved-action validation
proves the intent is still fresh, and `ai/tools/approved-action-dispatcher`
executes a registered deterministic action or blocks with a structured reason.
In the trusted local/dev stack, `start-dispatcher.sh` runs this loop without
requiring Codex to stay alive.

Use:

```sh
ai/tools/operator-questions/list-approved-actions.sh --json
node ai/runtime/dist/cli.js request --method approvedActions.listActionable
node ai/runtime/dist/cli.js request --method approvedActions.summary
node ai/runtime/dist/cli.js batch --requests '[{"method":"approvedActions.listActionable"},{"method":"approvedActions.summary"}]'
ai/tools/approved-action-dispatcher/dispatch.sh --dry-run --once
ai/tools/approved-action-dispatcher/dispatch.sh --once
```

Read-only operator-question and approved-action classification is owned by
Runtime Core TypeScript modules and surfaced through Runtime API methods.
The shell entrypoints remain the stable operator interface; where migrated,
they should delegate to Runtime Core instead of duplicating stale/actionable
classification rules.

The dispatcher must block stale approvals. A fresh approval is required when
any canonical scope field changes:

- git diff/status changed.
- target chunks/files changed.
- validation state changed or became stale.
- trusted runtime state degraded or changed in a way that affects execution.
- approval is superseded, explicitly invalidated, already consumed, or a replay
  is detected.
- Codex stopped and the next run did not explicitly resume the action.
- the target cannot be reconstructed exactly.
- a conflicting approval or answer exists.
- files/chunks changed after approval.
- the target action is not approved or was denied.

Approval questions must not expire primarily because wall-clock time elapsed.
The default advisory age window is 48 hours, but age alone is not a stale
reason. Operator offline, travel, and sleep scenarios are expected: an approval
remains valid while the chunk, git, validation, runtime, and target
fingerprints remain unchanged. Operator surfaces may show advisory age, but
must explain state-based stale reasons when an approval is no longer
actionable.

Dry-run validation is mandatory before resumed execution:

```sh
ai/tools/approved-action-dispatcher/dispatch.sh --dry-run --once --question-id <id>
```

The dispatcher dry-run must print the action, run id, targets, approval source,
execution status, stale state, and stale reasons. It must not execute anything.
Stale approvals require a fresh question. When the dispatcher or trusted daemon
completes the approved action, the lifecycle record is marked executed. Doctor
and scorecard must surface approved-but-unexecuted, stale, and blocked
approvals with structured stale reasons.

## Telegram Wording

Telegram is primarily an interactive answer surface, not a shell-like command
menu. Global help should stay minimal and operator-focused; question-specific
answers are emitted dynamically by each question.

Telegram questions should be compact:

- direct question.
- question token.
- short context only.
- reply options.
- tokenized `/yes_<token>` and `/no_<token>` for yes/no.
- tokenized fixed answers such as `/retry_<token>` only when that question
  accepts them.
- `/details_<token>` for expanded context.
- advisory age only; approval validity is state/fingerprint based.

Do not dump full workflow summaries into Telegram question messages by default.
Answer confirmations should be one compact block: accepted answer, question
token, source, next actor, and request id when applicable.

`/help` should advertise only `/status`, `/summary`, `/pending`, `/timeline`,
and `/help`.
It must not globally advertise `/yes_<token>`, `/no_<token>`, fixed answers,
numbered answers, freeform replies, or legacy/debug commands. `/details_<token>`
returns expanded context for that question only, not a full workflow summary.
`/pending` should show open interactive questions and approved-but-unexecuted
actions from the operator-question layer as well as legacy Telegram
confirmations.

## Source Semantics

Accepted answers record:

- question id.
- answer value.
- raw answer value.
- answer source: `local` or `telegram`.
- matched answer type.
- accepted timestamp.

Local and Telegram answers must not block each other. Either channel may answer
first. The losing late channel is stale.
