Skip to content
docs-bundle okf-loom wiki
GraphIndex

Live studio unavailable. Showing a read-only view. Browse the index.

Reference 7 min read

Comment lifecycle and archive rules

The full current comment state model — states, the separate archive track, the request_summary/summary pair, ts vs updated_at, threading, and server-side archive rules.

/reference/comment_lifecycle.md commentsstudioreference

Comment lifecycle and archive rules

A comment (also called a directive) is a user-authored ask that lands in <bundle>/.okf-loom/session/directives.jsonl. Comments drive the live studio's comment-driven development loop: the user directs via comments, the agent consumes them via scripts/okf-loom wait, and the loop closes when the agent resolves the comment. See index.md for the rest of the reference quadrant.

This document describes the current okf-loom v1.0 state model.

Two independent tracks

A comment carries two independent pieces of state:

TrackFieldValues
Lifecycle statestateopen / claimed / resolved / dismissed
Archive flagarchivedtrue / false

Archive is a soft hide, not a lifecycle state. Archiving a resolved comment keeps it resolved; unarchiving restores the prior visibility without flipping state. The two tracks never interact except through the server-side archive gate (see below).

Lifecycle states

StateMeaningWho sets it
openNew user comment; not yet picked up. Default on creation.User (POST /__comment) or reopen.
claimedAgent has picked it up and is working on it. Sets claimed_by.Agent (scripts/okf-loom comment-claim).
resolvedAgent completed the work and posted a reply + activity links.Agent (scripts/okf-loom comment-resolve).
dismissedUser cancelled an unclaimed comment.User (POST /__comment-update {state:"dismissed"}).

State diagram

stateDiagram-v2 [*] --> open : POST /__comment open --> dismissed : /__comment-update {state:"dismissed"} dismissed --> open : /__comment-update {state:"open"} (reopen) open --> claimed : scripts/okf-loom comment-claim claimed --> resolved : scripts/okf-loom comment-resolve --reply --activity resolved --> open : /__comment-update {state:"open"} (re-open wakes wait_for_work) note right of claimed server refuses dismiss: 409 (agent is mid-flight) end note

The agent never dismisses; the agent resolves. The user never claims or resolves; the user dismisses or reopens.

Transitions and timestamps

Each comment carries two timestamps:

FieldMutabilityWhen it changes
tsImmutableSet once at creation.
updated_atBumped on every transitionClaim, resolve, dismiss, reopen, archive, unarchive, reply, summary update.

scripts/okf-loom wait treats a re-opened comment as fresh work even though its id is unchanged: it tracks the latest updated_at (with a compatibility fallback to ts for older records).

The summary field

A short agent-authored blurb shown in the collapsed thread preview.

scripts/okf-loom comment-claim   <bundle> <id> --summary "adding depends_on + Order entity"
scripts/okf-loom comment-resolve <bundle> <id> --summary "done: link + entity added" \
                                --reply   "Done — added depends_on + Order entity" \
                                --activity 01JABED5K7,01JABED5K8

summary is separate from reply (the long-form resolution message). The collapsed preview shows the actor prefix (You: / Agent:), the body ellipsis, and the summary chip; the user expands the thread to read the full reply.

Threading (2-level cap)

A comment may carry a parent_id linking a reply to its parent. The studio renders up to 2 levels of nesting (parent → reply → reply-to-reply); deeper nesting is hoisted to root.

PropertyDetail
parent_idSet on the reply; the reply is anchored to the parent's concept.
2-level capParent → reply → reply-to-reply. Further replies hoist to root.
Anchor inheritanceA reply inherits the parent's concept.

Use replies to ask the user a clarifying question on an existing directive rather than opening a new top-level comment.

Archive rules

Separate track

archived is a boolean field, not a lifecycle state. Archive does not change state; it only flips the boolean the UI uses to hide the thread.

OperationEffect on stateEffect on archived
Archiveunchangedtrue
Unarchiveunchangedfalse

Server-side rules

RuleFailure response
Archive only on the root comment of a thread.400 "archive is only available on the top-level comment of a thread".
Archive only when every comment in the thread (root + all replies) is resolved.409 with unresolved_ids: [...].
Dismiss a claimed comment.409 "cannot dismiss a claimed comment".

The UI disables the Archive button client-side with a tooltip until the whole-thread-resolved gate is met; the server enforces the same rule so a hand-crafted POST cannot bypass it.

Auto-unarchive on reply

Replying to an archived thread auto-unarchives the parent. The reply itself is never archived. Adding to a thread reopens it for business; archive is a soft hide, not a closed state.

This is enforced in Studio.post_comment: when parent_id points at an archived parent, the parent is unarchived before the reply is appended.

Compatibility normalisation

Some older records used state == "archived" as a lifecycle state before archive became a separate track. On read, these are normalised to archived=true, state="open" (best-effort migration). The on-disk record is not rewritten; normalisation is view-only.

Reading comments

SourceAPI
CLIscripts/okf-loom comment-list <bundle> — filters --state, --concept.
HTTPGET /__comments — see http_routes.md.
LibraryStudio.list_comments(state=…, concept=…) / Studio.get_comment(id).

directives.jsonl is append-only: a transition appends a new full record for the same id. Readers keep only the latest record per id (last-write-wins).

JSON shape of a comment record

A single record as returned by GET /__comments or scripts/okf-loom comment-list --format json:

{
  "id": "01JABED9K0",
  "ts": "2026-06-29T18:12:39Z",
  "updated_at": "2026-06-29T18:14:02Z",
  "concept": "tables/orders",
  "anchor": {"heading": "Schema", "snippet": "customer_id"},
  "actor": "user",
  "body": "Add a depends_on relation to customers.",
  "state": "resolved",
  "claimed_by": "agent",
  "resolved_activity": ["01JABED5K7", "01JABED5K8"],
  "reply": "Done — added depends_on + Order entity",
  "request_summary": "ask: add depends_on relation",
  "summary": "done: link + entity added",
  "detail": {"intent": "add_relation"},
  "archived": false,
  "parent_id": null
}
FieldTypeNotes
idstringULID; sortable as a string.
tsstring (ISO 8601)Immutable creation timestamp.
updated_atstring (ISO 8601)Bumped on every transition.
conceptstringConcept id the comment is anchored to.
anchorobjectHeading/snippet for inline placement (may be {}).
actorstringuser (default) or agent.
bodystringThe comment text.
statestringopen / claimed / resolved / dismissed.
claimed_bystring | nullSet on claim; cleared on reopen.
resolved_activitylist of stringsChange-list entry ids linked to the resolution (group undo).
replystring | nullLong-form resolution message (set on resolve).
request_summarystring | nullShort blurb for the ASK — written by comment-claim --summary, auto-derived from the first line of body when absent.
summarystring | nullShort blurb for the DONE — written by comment-resolve --summary.
detailobjectFree-form metadata (e.g. Quick Action intent).
archivedboolArchive flag. Independent of state.
parent_idstring | nullParent comment id for a threaded reply.

Storage and rotation

FileLocationRotation
directives.jsonl<bundle>/.okf-loom/session/directives.jsonl2 MiB active cap, 3 files kept. Comment spam cannot fill the disk.
events.jsonl<bundle>/.okf-loom/session/events.jsonl8 MiB active cap (studio.events_max_bytes), 7 files kept (studio.events_keep).
history/<hex>/<rev>.md<bundle>/.okf-loom/session/history/Ring-capped; pruned entries surface as 404 snapshots_pruned on undo.

Staleness recovery

A dropped agent process is auto-recovered by the watcher sweep:

StateReverted toAfter
Agent presence (editing/thinking/watching)idlestudio.presence_ttl_seconds (default 300).
A claimed commentopenstudio.claim_stale_seconds (default 600).

No directive is orphaned if the agent crashes mid-task.

Workflow

The canonical agent loop:

scripts/okf-loom wait           <bundle> --for comment     # foreground block-once
scripts/okf-loom comment-claim  <bundle> <id> --summary "…"
scripts/okf-loom presence       <bundle> --state editing --focus tables/orders
# …mutators (link-add / entity-add / update) with --group-id PASS1…
scripts/okf-loom comment-resolve <bundle> <id> --reply "Done — …" \
       --activity 01JABED5K7,01JABED5K8 --summary "done: …"

See /tutorials/author_with_agent.md for the end-to-end walk-through and /how-to/archive_threads.md for the archive/unarchive workflow.

See also

All fields
timestamp2026-06-29T00:00:00Z