Imported from Nebulit-GmbH/Eventmodelers-Build-Kits (
eventmodelers-cli/stacks/modeling-kit/templates/kit/AGENTS.md). Install upstream withnpx skills add Nebulit-GmbH/Eventmodelers-Build-Kits --skill kit. Copyright stays with the author.
Learnings
Reusable learnings accumulated while processing prompts for this board. Append new ones in a compressed, reusable form; only add if not already covered here.
/place-elementrequires an existing column — create one via the timeline API if missing./wdytposts comments onto nodes — use for analysis only, not modifications. In a chat it shows its findings as a snippet to tick first. A chat message that is "wdyt" always runs it; a review asked in other words may be answered in prose.- The
board_id,timeline_id, andorganization_idfrom each prompt provide full context — pass them to skills that need them. - If a prompt's
context.timelineIdis present and non-null, it overrules the prompt's owntimeline_idfield — it's the chapter the user was pointing at on the canvas, which can differ from whatever chapter the prompt/voice session was scoped to. ResolveTIMELINE_IDfromcontext.timelineIdfirst, falling back totimeline_idonly when it's absent, before passing it to any skill. - Same pattern for node references: if a prompt's
context.selectedNodesarray is present and non-empty, its first entry overrules the prompt's ownnode_idfield (e.g. for/handle-comment'snodeId) — it reflects the actual canvas selection at prompt time, whereasnode_idis only set when the prompt originated from a specific node/comment. - Same pattern for cell references: if a prompt's
context.selectedCell.idis present, it overrules any cell reference (e.g."A2") parsed from the prompt text — pass it as/place-element'scellNameargument and skip the text-parsing fast path entirely. - Node events POST to
/api/boards/:boardId/nodes/eventsusingnode:created,node:changed,node:deleted. /update-slice-statusrejects moving a slice into a status it's already in — this is a concurrency guard so two agents can't both claim the same slice. Treat this asALREADY_IN_STATUS, not a task failure: drop the prompt, move on to the next task, and do not retry the same update.- macOS/BSD
datesilently ignores GNU-only format specifiers like%N/%3N(sub-second precision) instead of erroring — it prints the literal characters, producing a malformed timestamp that only fails downstream. Don't shell out todatefor sub-second precision; use$(( $(date +%s) * 1000 ))for whole-second-in-ms, or a runtime call (Date.now(),process.hrtime()) instead. - Before retrying a failed shell command a second time, diagnose why it failed (e.g. a GNU/BSD flag mismatch) rather than re-running it unchanged — repeating the same command produces the same failure and just burns retries.
- An existing element is addressed by its
nodeId, never by a cell. A cell address (B2,AE6) is only needed to place something into a cell, and it is always read, never computed:get_board_outlineis the only read that reports a node'scellName(get_nodes/get_nodedon't). An address inferred from a column letter plus a node's place in a list is a guess, and a guess that happens to name an occupied cell is accepted silently by every cell-addressed tool. - Orientation and working set are two different reads, in that order:
get_board_outline(orget_nodeswithprojection: "line") answers where is the work, and only then does one full-metaget_nodes, scoped bychapterId/nodeIds, cover the nodes actually being touched. Opening with an unscoped full-metaread pulls every field body and rendered HTML page on the board to answer the cheapest question of the turn. - Pick each read by what the step needs, cheapest first:
get_nodesprojection: "line"when ids/titles/types/attribute names suffice (never carriessliceStatus, meta or field types),get_nodeprojection: "cells"/"edges"for a grid or one node's wiring,get_slice_dataprojection: "outline"/"fields"/"specs"(outline/specs only in json/yaml/toon) over the full graph. Full reads only when a later step reads field types, examples, descriptions, positions, HTML or comments. Table:learn-eventmodelers-api→ Which read to use. - Both reads are once per turn. A chapter's outline and grid don't move unless something writes to them, so re-read only after a structural write. Three
get_board_outlinecalls in one turn means the first two were thrown away. - Mint ids once per turn, in one shell:
for i in $(seq <n>); do uuidgen; done— one per new node, plus one per event, since every event needs its own freshid(required; not the node id). Nothing in it depends on anything being read, so splitting it across several calls buys round trips for nothing. Never mint a per-eventboardIdortimestamp— both are server-derived and ignored if sent. - A subagent is a fresh session: it gets
token=/org=/baseUrl=/board=inline as already-resolved values (which satisfiesconnectat its Step 0 — tell it not to invoke/connect) plus the board state already read for it, inline. Handed bare node ids instead, it has exactly one way to recover the rest — re-reading the whole board, once per agent. - Dispatch executor agents with
model:set to the session header'ssubagent_model(defaultsonnet). The judging happened before the dispatch, on this session's model; what's left is execution against a written brief. Keep an agent on the session model only where its piece re-derives modeling structure (a chain's shape, a slice boundary).
