This document specifies the observable protocol implemented by
@pulse-computing/dsh-tick-loop. It describes Harness behavior, not a theory
of hidden model cognition.
- Control command:
/tick ..., consumed by the DSH command plane. - Time-step message: the ordinary user-role follow-up generated by the plugin. Its source identifies the plugin.
- Natural context: at least one earlier user message whose source is
user, followed by a durable terminal. - Terminal: the
turn/endevent for an ordinary model turn. - Run: one explicitly armed, bounded sequence using one frozen policy.
- Policy: a named, immutable UTF-8 time-step text.
/tick [<count>] [--policy <id>] [--resume]
/tick policies
/tick status
/tick stop
countdefaults todefaultTicksand must be in[1, 256].policydefaults todefaultPolicyId.- Policy IDs start with an ASCII letter and contain at most 64 ASCII letters, digits, dots, underscores, or hyphens.
- Policy text must be non-blank, well-formed Unicode and at most 4,096 UTF-8 bytes.
- Unknown options, duplicated options, invalid counts, and unknown policies fail before a model-facing message is created.
/tick policies, /tick status, and /tick stop are control operations.
They never become model input.
Every generated time-step message has this observable shape:
{
"role": "user",
"content": [{ "type": "text", "text": "tick" }],
"source": {
"kind": "plugin",
"plugin": "@pulse-computing/dsh-tick-loop"
}
}The text may differ for a named policy. Historical counting uses source, not
string equality. A human typing tick is therefore not counted as a plugin
time step.
inactive
| /tick and natural context has a completed terminal
v
armed -- latest terminal missing --------------------------> waiting
| latest terminal completed
v
scheduled -- delay expires and state is still identical --> enqueued
| |
| state changed / disposed | ordinary model turn
v v
cancelled terminal observed
|
+----------------------------------------+
| completed and remaining > 0 -> scheduled
| completed and remaining = 0 -> complete
| error/unknown/inconsistent -> stopped
Only one timer and one run may exist per live Agent object. The timer rechecks the complete scheduling predicate before enqueue. An active turn is never steered.
A new run is admitted only when:
- at least one natural user message exists;
- a latest closed turn exists;
- the latest terminal is
completed, except for the explicit recovery case; - no other Tick run is active for that Agent;
- the selected policy exists and is valid.
The next message is scheduled only when:
- the observed plugin time-step count is between the run's frozen start and target counts;
- the latest turn has a durable terminal;
- that terminal is
completed, or it is the single exact interrupted boundary authorized by--resume; - fewer than the requested number of additional messages have been observed.
The scheduler does not infer success from assistant text, elapsed time, or provider transport state.
At plugin startup, every configured policy is compiled to:
{
"id": "reflective-en",
"timeStepText": "...",
"timeStepBytes": 143,
"timeStepSha256": "..."
}The value is immutable. A run captures exactly one compiled policy. Mutating the original configuration object cannot change a later step in that run.
The default directory is .pulse-tick-audit under the DSH process working
directory. Each session has one append-only JSONL file whose name is sanitized
from the session ID.
All records use schema pulse.tick-loop.audit.v1 and include recordedAt,
sessionId, runId, policyId, timeStepSha256, and timeStepBytes where
applicable.
Record types:
loop_armedtick_scheduledtick_enqueuedtick_terminaltick_cancelled_before_enqueueloop_stoppedloop_complete
The audit is an operational trace, not an immutable or independently signed scientific ledger. Protect it like session metadata and do not publish it without reviewing paths, timestamps, provider data, and workspace details.
Without --resume, any latest non-completed terminal rejects or stops the
run. With --resume, the latest terminal may be exactly interrupted. That
boundary is allowed once, before the first new message of the new run.
This does not:
- restore an old in-memory run ID or timer;
- count the interrupted turn as completed;
- replay an earlier tool call;
- claim that an unknown side effect did or did not happen.
- no unbounded or infinite run;
- no ordinary-conversation auto-start;
- no goal, persona, or purpose injection;
- no active-turn steering;
- no timer-based retry;
- no silent switch to Goal, Ralph, compaction, or another loop;
- no claim that a terminal turn contains useful thought.
These constraints keep Tick a separable experimental primitive.