Command reference
oko tasks kind
Records what one operator message is to the ledger, with the kinds Oko’s model reads from, when the model has not read it or read it wrong. Many messages are about one piece of work, so only `task` starts a task.
Invocation#
oko tasks kind <session> <number> <KIND> --by (operator | <session>) [--json]Inputs and options#
- <session> and <number> name the message by its place among the session’s stored operator messages — the number `oko tasks unread` lists it under. The message is taken from the shared ledger (`oko_operator_messages`), so a reading lands on the message the untasked list named even when the live transcript numbers differently (a harness marker such as an interruption, a branch that lost turns); only a session the ledger has not stored yet is read from its live transcript on this machine, and one with no live transcript either from Transcript Lake’s archive of it (`transcript-lake show <session> --include user`, its user turns numbered by the same reading as a file’s, each identified by its time and masked text since Lake keeps no harness turn id).
- <KIND> is one of the kinds in Oko’s reading declaration: `task` (work no earlier message asked for), `detail` (a fact, correction, constraint or decision for work an earlier message asked for), `doIt`, `question`, `acknowledgement`, `rule`, `order`, `resume`, `paste`, `defect` or `remark`.
- --by names who reads the message: a session id records that session’s reading, the literal word operator records his. --json prints the result as JSON.
Output and state effect#
- Writes the reading into `oko_message_readings` by the message’s identity, with `model` “session:<by>” and no confidence, replacing any reading the model kept. Oko’s model never reads that message again, so its reading cannot be overwritten by the next oko tasks read or control pass. A message can hold one reading per member of the organization; every reader derives the ledger from the newest of them by `read_at`, so the reading just recorded is the one oko tasks show, map and the control pass see, instead of an older member’s row the database happened to list last.
- A message this member had recorded as a task with oko tasks add, read now as anything but `task`, loses that member’s addition in the same database transaction that writes the reading. A database refusal rolls both changes back; another member’s addition is not withdrawn. Parts recorded with --part N stay. The returned withdrawn recorder comes from the transaction, not an earlier client read. Install canonical migration `20261010182000_oko_record_message_reading.sql` before updating the client; an unavailable RPC refuses rather than reverting to separate writes. CLI and Desktop Read as use this same transaction.
- After the transaction succeeds, Oko reads the resulting ledger. If that read fails, the diagnostic explicitly says the message was recorded and the transaction succeeded, then preserves the read failure; it does not imply rollback. A transport failure before the transaction response is received does not establish whether the server committed. Inspect persisted reading and addition state before repeating a mutation.
- Reads the ledger again and prints “message #<n> of <session> reads as <kind>, read by session:<by> from <source>; <effect>”, where the source is “the stored ledger” (also for a session whose transcript is no longer on this machine), “the live transcript <path>” when the live copy has grown past the stored one, or “Transcript Lake” for a session nothing stored and no file holds, and the effect is “it is task <task-id>”, “it continues task <task-id>” for a detail, or “it is no work owed”; then the earlier reading, when there was one, as “before: <kind>, <who read it>”, the withdrawn addition as “withdrew task <task-id>, which <who> had added”, and the message’s first line. JSON carries `session`, `ordinal`, `messageId`, `kind`, `readBy`, `source`, `task`, `withdrewAddition` and `before`.
- For a detail, the reported task identity comes from the ledger operation that attaches the message, not a search for its printed annotation. Quoting `#<n>: ` in another task cannot redirect the result. The real tasks-message-kinds journey uses OKO_TEST_DETAIL_SESSION, OKO_TEST_EARLIER_TASK, OKO_TEST_DETAIL_TASK, OKO_TEST_DETAIL, OKO_TEST_DETAIL_TEXT and OKO_TEST_SOURCE_REVISION to select an isolated session containing that genuine quotation and detail; it checks the reported identity against a fresh persisted task map and retains commands, revisions, exits and raw output under .build/tasks/detail-assignment.
- In Oko Desktop, Tasks → What each message is → Read as performs this same core operation. After a successful reading the Tasks screen shows its result under Last message update, including the continued task’s identity; a failed recording keeps the sheet open and names the message, chosen kind and underlying cause. Reading a detail is not presented as adding a new task.
- The native_api resident-service journey exercises that graphical operation with the same isolated detail fixture: it records the reading through the real native API, reads the persisted task back through a separate CLI process, then submits an invalid kind and checks that the refusal names it without changing the task. The existing service fixture retains the executable digest, exact source revision, native exchanges, CLI output and service exit under .build/api-*; this qualifies the native operation, not a visual interaction with the desktop screen.
- Before recording a reading, Oko compares the stored message with the local live transcript when present. Both its identity and complete text must still match: keeping the same native message UUID does not authorize classifying changed words. A stale-message refusal distinguishes changed identity from changed text, names the session, message number and transcript path, and leaves the reading and any task addition untouched. Refresh the session ledger with `oko tasks --session <session>`, read the complete updated message, then classify it again. Desktop Read as uses the same refusal.
- The real `readings-stored-only` journey requires `OKO_TEST_STALE_SESSION`, `OKO_TEST_STALE_MESSAGE`, `OKO_TEST_ATOMIC_MEMBER` and `OKO_TEST_SOURCE_REVISION` for a dedicated signed-in member and session whose stored and live message share a native UUID and first line but differ in later text. The member must have exactly one task addition at that message and another addition for a different message, so withdrawal isolation is exercised against an actual retained row. The journey checks stale classification preserves readings and additions, refreshes the genuine source twice, and classifies the refreshed message. It independently verifies the persisted member-specific reading, withdrawal of only that message’s addition, preservation of every other visible addition, and the actual withdrawn recorder returned by the command. Commands, exact revision, executable digest, exit statuses and responses are retained under `.build/readings/stale-text`; it does not manufacture transcript or database rows. Member row-level security hides other members’ additions; this single-member journey does not qualify their preservation.
- The real `tests/readings/atomic/rollback.mjs` journey requires a dedicated database member that can write its genuine non-task reading but is denied DELETE on its own retained task addition. Run it with a fixture environment declaring OKO_BIN, OKO_TEST_SOURCE_REVISION, OKO_TEST_SCHEMA_REPOSITORY, OKO_TEST_SCHEMA_REVISION, OKO_TEST_ATOMIC_SESSION, OKO_TEST_ATOMIC_MEMBER, OKO_TEST_ATOMIC_READING_ID and OKO_TEST_SUCCESS_EXIT. It submits a measured changed observation time through `oko database request`, requires the actual addition-table privilege refusal, and independently compares both persisted rows afterward. Exact application/schema revisions, binary and journey hashes, commands, bodies, statuses and readbacks are retained under `.build/readings/atomic`. Missing RPCs and preflight errors do not count as rollback proof. The fixture must be isolated; the journey neither grants privileges nor mutates operator work.
- If the rollback journey observes an unexpected committed outcome, it restores only its exact attempted reading with a conditional write and recreates its missing original addition without overwriting another row. Concurrent or unrecognized fixture state refuses cleanup. The report preserves both the initiating failure and any restoration failure; cleanup never turns failed qualification into a pass.
Refusals and safe exits#
- Refuses without a session, a number and a kind with “oko tasks kind takes <session> <number> <kind>; the kinds are <kinds>”, without --by with “oko tasks kind requires --by <session>”, a kind the declaration does not offer with “<kind> is not a kind a message can be; the kinds are <kinds>”, a session no source holds with “session <session> has no operator messages anywhere it was looked for: no stored rows in oko_operator_messages, no live transcript under <the declared transcript roots and named paths under the home directory>, and Transcript Lake does not hold the session”, a Transcript Lake that cannot be run or answers unreadably with Lake’s own refusal, stored rows that do not read as messages with “the stored operator messages of session <session> do not read as messages: <error>”, and a number the session does not hold with “session <session> has no operator message #<n>; it has <count>”. These input and source refusals do not replace the reading or withdraw its addition. A database transaction refusal rolls back both writes; a failure reading the ledger after a confirmed commit explicitly reports that the reading was saved. A lost transaction response leaves the commit outcome unconfirmed, not proven rolled back.