Forage Cues (cue layer)¶
A cue (bee language: Forage Cue) is a project-local YAML shortcut under .paseka/cues/<id>.yaml that publishes one bus ingress action — emit: signal or emit: task — with optional templates for payload fields. One cue definition drives Queen Shell (paseka cue), Queen Console (Run cue), and Telegram (cue: in commands.custom). Cues are shareable colony config (included in Nuc export/import).
Optional standing binds a cue to a long-lived Standing Trail (stable traceId) so recurring procedures reuse the same comb instead of minting a new trail every tick.
Design records: spec 016, spec 028. Vocabulary: glossary (Forage Cue, Standing Trail).
Related: CLI, Telegram gateway, colony layout, task ledger, feature ideation flow, homelab deployment.
1. What cues are (and are not)¶
| Cues do | Cues do not |
|---|---|
Publish SIGNAL or INSIGHT/SIGNAL task plan (+ optional task.ready) |
Run bees, start sessions, or accept invites |
Seed optional per-cue initial honey on a fresh trace (energy_budget, or standing stipend); standing ticks then replace remaining |
Top up a live bloom or tick mid-flight (paseka energy add) — that extra is wiped on the next standing stipend |
Bind a recurring procedure to a Standing Trail (standing.trace) |
Ship a scheduler, webhook listener, or report_to callback |
| Share one definition across CLI, Console, Telegram | Replace raw paseka signal / task create for power users |
Stay the publish API for timers and GitHub-style hooks (via paseka cue run) |
Listen for HTTP, run cron, or reply to the webhook caller |
Cue success means the bus publish(es) succeeded (and honey seed or standing stipend, when applicable). AFK reactions still need paseka run as today. Timers and inbound HTTP stay outside Paseka — see §10.
2. Layout¶
.paseka/cues/
├── feature.yaml # signal → feature.requested (Scout intake)
├── hotfix.yaml # task → builder bugfix autorun (small energy_budget)
└── daily-triage.yaml # optional standing procedure (stable trace + stipend)
Cue id = filename without extension. paseka init scaffolds feature and hotfix when missing.
3. Authoring¶
Signal cue (feature)¶
# .paseka/cues/feature.yaml
description: Intake an idea or bug for Scout classification
emit: signal
type: SIGNAL
kind: feature.requested
title: "{{.Title}}"
body: "{{.Body}}"
Publishes one SIGNAL on a new trace (or --trace / API traceId when attaching). Omitted energy_budget → colony default applies on first seed (see §5).
Task cue (hotfix)¶
# .paseka/cues/hotfix.yaml
description: Urgent fix via builder (small honey reserve)
emit: task
bee: builder
intent: bugfix
review: none
autorun: true
energy_budget: 3
title: "{{.Title}}"
body: "{{.Body}}"
Reuses task create semantics: INSIGHT/task.plan, then SIGNAL/task.ready when autorun: true.
Standing cue (recurring procedure)¶
# .paseka/cues/daily-triage.yaml
description: Daily triage
emit: signal
type: SIGNAL
kind: triage.tick
standing:
trace: trail-daily-triage
stipend: 4
title: "{{.Title}}"
body: "{{.Body}}"
paseka cue run daily-triage "tick …" without --trace publishes on trail-daily-triage. An explicit --trace that equals that id is allowed; any other id fails closed. Non-standing cues still mint a new trail when --trace is omitted.
Standing is a cue binding, not a ledger flag. Recommended ids look like trail-daily-triage (hyphens, no dots). Two standing cues may not share one standing.trace. energy_budget is forbidden on a standing cue — stipend is the only honey number. A bare standing: key (null / empty) is rejected, not treated as a bloom cue.
Standing emit: task cues require review: none (or omit review) and the named bee must have worktree: false. Load/import errors name the cue, bee, and field.
First successful run seeds honey from standing.stipend and, if the trail has no INSIGHT/trace.title yet, publishes one from cue description (trimmed, 120-character cap) or the cue id when description is empty. Later ticks do not overwrite a human or bee title. Each later run publishes SIGNAL / energy.stipend before ingress, which sets energyRemaining to the stipend (does not add, does not change energyBudget / energyAdded, does not unblock leftover honey-blocked tasks). A killed standing trail refuses cue run (no stipend, no ingress); the error names system.kill. A second tick is refused while any ledger task is planned, ready, running, or waiting_review, or a live AFK adapter is still running on that trail (interactive bee chat does not count).
paseka doctor warns (does not fail the cue) when a standing emit: signal kind has only worktree: true direct subscribers, when a standing tick bee publishes isolated code.proposal, or when an isolated proposal already appears on a standing trail. Config profiles do not overlay cues or stipend (027 out of overlay).
Checkpoints and bloom spawn¶
Standing ticks observe and record. Procedure memory is the trail comb ({{.ArtifactsDir}} = .paseka/runs/<standing.trace>/artifacts/), not {{.Insights}}. Recommended files:
| File | artifactKind (014 basename-stem) |
Role |
|---|---|---|
checkpoint.json |
checkpoint |
Source of truth (skip lists, keys, SHAs) |
journal/YYYY-MM-DD.md |
date basename (e.g. 2026-09-04) |
Optional human journal; 014 kind is basename-stem, not the journal/ folder |
The comb path is stable for the standing traceId, so the next tick sees yesterday’s files. Runtime still scan-flushes artifact.written only on content change (014). Prompt partial standing-checkpoint (included from emit-howto) tells bees to read the checkpoint first, write JSON via temp-file replace, and spawn a bloom (paseka cue run <feature-or-hotfix> "…" with no standing binding, or signal/event emit with a new traceId) instead of task.plan a builder on the procedure. Optional watch-intent-tick is for Watch/Medic bees. Hivewright edits of cues/prompts belong on a bloom hivewright trail with code.proposal.root.
paseka purge --runs deletes standing checkpoints with the rest of .paseka/runs/. Facts the colony must keep belong in git (docs/, tracker), not the comb. Export --include artifacts still dumps the standing comb. Queen Console Artifacts can preview checkpoint.json like any comb JSON.
Ad-hoc bee run / bee chat on the standing id shares the comb and does not apply stipend.
Schema (MVP)¶
| Field | Required | Notes |
|---|---|---|
description |
no | Shown in cue list, Console picker, Telegram help fallback. First standing seed also uses it as INSIGHT/trace.title (cue id if empty) |
emit |
yes | signal or task |
energy_budget |
no | Positive int — initial honey override on an unseeded bloom trail (§5). Forbidden when standing is set |
standing.trace |
with standing |
Stable Flight Trail id used when the caller omits --trace / traceId. No spaces, path separators, ., *, or > (JetStream KV keys) |
standing.stipend |
with standing |
Positive int — first-tick seed and per-tick remaining ration (§5). Required whenever standing is present |
| signal | ||
type |
yes | Must be SIGNAL |
kind |
yes | payload.kind (e.g. feature.requested) |
static |
no | Extra string fields merged into payload |
title, body |
no* | text/template strings (*required when templates reference them) |
payload_template |
no | Escape hatch: rendered string → JSON object |
| task | ||
bee, intent, review, autorun |
per task create | Same semantics as paseka task create |
title, body |
no* | Templates for task plan fields |
Template engine: Go text/template (same family as prompt templates). Built-in context:
| Key | Meaning |
|---|---|
Text |
Operator text (positional CLI arg, Console modal, Telegram command body) |
Title |
First line of Text |
Body |
Full Text |
Source |
Calling channel: cli, console, or telegram (scripted cue run is cli today) |
TraceID |
Resolved flight trail |
Extra vars: --set key=val (CLI) or vars map (Console API). Missing template keys → error. Unused --set keys are ignored. Operator Text / --set wins over cue static defaults.
4. Queen Shell¶
| Command | Behavior |
|---|---|
cue list |
Stable sort by id; prints id, optional standing trail id, optional description |
cue run |
Publishes immediately (no confirm). Prints trace id; task id when emit: task. Standing cues resolve --trace as above |
Examples:
paseka cue run feature "OAuth callback returns 500 on refresh"
paseka cue run hotfix "Fix nil deref in token refresh"
paseka cue run feature "Follow-up on same trail" --trace trace-abc123
paseka cue run daily-triage "tick $(date -I)"
Requires NATS (same as paseka signal / task create). See CLI § paseka cue. systemd/cron and GitHub should invoke this command on the hive host — not a Paseka listener (§10).
5. Honey: energy_budget vs colony default vs energy add¶
Bloom seed and top-up, plus standing seed and per-tick replace:
| Mechanism | When | Effect |
|---|---|---|
defaults.energy_budget in colony.yaml |
First seed on a trace (task create, reactor ensure-seed, cue without override) | Sets initial energyBudget / energyRemaining (default 12) |
Cue energy_budget |
Fresh bloom trail only (energyBudget == 0 on snapshot) |
Seeds a smaller or custom initial reserve via ledger SeedEnergy — can be less than colony default |
| Standing first-tick seed | Fresh standing trail (energyBudget == 0) |
Seeds energyBudget / energyRemaining from standing.stipend (same SeedEnergy primitive) |
| Standing later tick | Standing trail already seeded, not killed, not busy | Publishes SIGNAL / energy.stipend before ingress; sets energyRemaining to stipend. Does not change budget or energyAdded. Does not unblock honey-blocked tasks from a previous tick |
paseka energy add |
Any time (live bus) | Increments energyRemaining; after seed also increments energyAdded. Does not change energyBudget. Unblocks honey-blocked tasks on a live tick; the next standing stipend wipes leftover remaining back to stipend |
Rules:
- Omit
energy_budgeton a non-standing cue → unchanged colony-default seeding. - Cue with
energy_budgeton a new bloom trail → seed before publish (signal and task paths). - Standing cue on a new trail → seed from
stipendbefore publish; also publishtrace.titlefrom description (or cue id) when none exists. - Standing cue on a seeded trail → replace remaining with stipend, then publish ingress (does not overwrite
trace.title). cue run --trace(or Console/TelegramtraceId) on a bloom trail that already has honey → cueenergy_budgetseed is ignored (no shrink, no re-seed).- Killed standing trail → cue run fails closed (names
system.kill); no stipend event, no ingress. - Standing trail with an open tick (
planned/ready/running/waiting_review) or a live AFK run on thattraceId→ cue run fails closed (error says the trail is busy); no stipend, no ingress.blocked/completed/failed/cancelledand interactive sessions do not block. - Cues never emit
energy.add— use CLI, Console, or Telegram/energyfor a live-tick extra token.
Full ledger model: task ledger § Honey reserve.
6. Queen Console¶
Hive Dashboard and header expose Run cue:
- Picker lists cue id + optional description (no emit-type badge). Standing cues also show
Standing: <traceId>.GET /api/cuesreturnsstandingTracewhen the cue declares standing. - Enter text (and optional trace id in the API; omit it on a standing cue to use
standing.trace). - On success: toast with clickable
traceIdlink to the trace view.
Trail lists (Dashboard and Traces) show a standing badge on ids declared by a loaded standing cue, and trail detail marks the flag plus honey as remaining / stipend. The badge follows colony YAML, not a ledger flag: delete the cue and the trail looks like an ordinary leftover trail. GET /api/traces, /api/traces/:id, and /api/dashboard carry standing: true for those ids.
API (NATS required):
| Method | Path | Body |
|---|---|---|
GET |
/api/cues |
— |
POST |
/api/cues/:id/run |
{"text":"…","traceId":"…","vars":{}} |
traceId and vars are optional. Omitting traceId on a standing cue uses standing.trace (same as CLI). agentId / source are console. This API is for Queen Console on a trusted network (homelab); it is not a public GitHub/webhook endpoint.
7. Telegram gateway¶
In ~/.config/paseka/<slug>/telegram.yaml, commands.custom.<name> may use cue: <id> instead of inline emit / type / kind:
- Loads
.paseka/cues/feature.yamlfrom the colony repo. - Preview + Confirm stay in the gate (same as
/taskand inlineemit: signal). Standing cues show the standing trail id and confirm publishing on that trail (not “a new trace”). descriptionin gate config wins; falls back to cuedescription.- Inline
emit: signalcustom commands still work — migration is optional.
Reserved command names unchanged: start, status, help, invites, traces, energy, task.
See Telegram gateway § Custom commands.
8. Nuc packs¶
Cues export and import with bees and prompts:
paseka nuc export -o pack.nuc.yaml # includes all cues when present
paseka nuc export --cues feature,hotfix -o … # filter cue ids
paseka nuc import ./pack.nuc.yaml
Nuc spec.cues maps id → raw YAML body. Import validates cue files. Details: Nuc packs.
9. Ideation entry¶
For the reference ideation flow, paseka cue run feature "…" is the everyday replacement for hand-written SIGNAL/feature.requested. Scout intake still reacts via subscribes when paseka run is active.
paseka cue run feature "Live bees indicator in Console header"
# equivalent to publishing feature.requested on a new traceId
See feature ideation flow § Entry paths and § Soft bootstrap.
10. External timers and webhooks¶
Colony YAML says what to publish (.paseka/cues/<id>.yaml). The apiary says when and from where. Paseka does not ship an HTTP webhook receiver, a cron dispatcher, or delivery back to the caller (report_to). Telegram mode: webhook is bot transport (Telegram gateway), not generic ingress.
| Layer | Owns | Lives in |
|---|---|---|
| Cue definition | Mnemonic + payload templates | .paseka/cues/ (git, Nuc) |
| Schedule / hook | systemd timer, GitHub Actions, a small signed-webhook wrapper | Machine / CI — not the colony repo |
| Hive liveness | JetStream + reactor | paseka run; probe with paseka status --check |
Cue success ≠ bees running. paseka cue run publishes even if the reactor is down. A timer that must start work should probe first (paseka status --check), then run the cue. See CLI (paseka status --check, paseka cue).
Do not point GitHub (or any internet webhook) at POST /api/cues/:id/run. Use SSH or a self-hosted runner on the hive host and call Queen Shell. Map provider JSON to cue Text / --set in the wrapper — cue files stay GitHub-agnostic (no stdin / --file on cue run).
Idempotency is the wrapper’s job. On a non-standing cue, omitting --trace always starts a new trail; a retried GitHub delivery or a double timer tick otherwise burns a second honey reserve. Remember delivery_id or job@slot and skip the second cue run. Recurring procedures should declare standing so omitted --trace reuses the procedure identity; Paseka also refuses a second tick while the trail is busy. The wrapper should still skip a double fire when it can.
Examples (apiary-local; adjust -C / paths):
# systemd / cron: probe hive, then tick a standing procedure
paseka status --check -C /path/to/colony && \
paseka cue run daily-triage "tick $(date -I)" -C /path/to/colony
# bloom hotfix from CI on a self-hosted runner next to the hive (new trail each run)
paseka cue run hotfix "$ISSUE_TITLE" -C "$COLONY_ROOT"
Scheduled cues still need an active paseka run (and NATS) for AFK bees. Put schedules in machine config, not in committed .paseka/.