@eventmodelers/cli 1.0.96 → 1.0.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +70 -3
  2. package/cli.js +113 -6
  3. package/lib/modeling-exec.js +4 -86
  4. package/package.json +6 -1
  5. package/shared/build-kit/README.md +22 -4
  6. package/shared/build-kit/lib/mcp-register.js +96 -0
  7. package/shared/build-kit/lib/ralph.js +49 -0
  8. package/shared/build-kit/lib/tracing.js +6 -5
  9. package/shared/build-kit/ralph-claude.js +4 -1
  10. package/shared/build-kit/ralph-exec.js +8 -8
  11. package/shared/skills/learn-eventmodelers-api/SKILL.md +0 -1
  12. package/shared/skills/request-feedback/SKILL.md +23 -32
  13. package/stacks/axon/templates/build-kit/CLAUDE.md +1 -6
  14. package/stacks/axon/templates/build-kit/lib/backend-prompt.md +1 -8
  15. package/stacks/axon/templates/build-kit/lib/prompt.md +2 -2
  16. package/stacks/blank/templates/build-kit/CLAUDE.md +1 -6
  17. package/stacks/blank/templates/build-kit/lib/backend-prompt.md +1 -8
  18. package/stacks/blank/templates/build-kit/lib/prompt.md +2 -9
  19. package/stacks/kurrent/templates/build-kit/CLAUDE.md +1 -6
  20. package/stacks/kurrent/templates/build-kit/lib/backend-prompt.md +1 -8
  21. package/stacks/kurrent/templates/build-kit/lib/prompt.md +2 -2
  22. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +4 -4
  23. package/stacks/node/templates/build-kit/CLAUDE.md +1 -6
  24. package/stacks/node/templates/build-kit/lib/backend-prompt.md +1 -8
  25. package/stacks/node/templates/build-kit/lib/prompt.md +2 -2
  26. package/stacks/opencqrs/templates/build-kit/CLAUDE.md +1 -6
  27. package/stacks/opencqrs/templates/build-kit/lib/backend-prompt.md +1 -8
  28. package/stacks/opencqrs/templates/build-kit/lib/prompt.md +2 -2
  29. package/stacks/react/templates/build-kit/CLAUDE.md +1 -6
  30. package/stacks/react/templates/build-kit/lib/backend-prompt.md +1 -8
  31. package/stacks/react/templates/build-kit/lib/prompt.md +2 -9
  32. package/stacks/supabase/templates/build-kit/CLAUDE.md +1 -6
  33. package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +1 -8
  34. package/stacks/supabase/templates/build-kit/lib/prompt.md +2 -2
  35. package/stacks/supabase-react/templates/build-kit/CLAUDE.md +1 -6
  36. package/stacks/supabase-react/templates/build-kit/lib/backend-prompt.md +1 -8
  37. package/stacks/supabase-react/templates/build-kit/lib/prompt.md +2 -9
  38. package/stacks/umadb/templates/build-kit/CLAUDE.md +1 -6
  39. package/stacks/umadb/templates/build-kit/lib/backend-prompt.md +1 -8
  40. package/stacks/umadb/templates/build-kit/lib/prompt.md +2 -2
  41. package/stacks/modeling-kit/templates/.claude/settings.local.json +0 -13
@@ -89,6 +89,17 @@ function* configCandidates(kitDir) {
89
89
  yield join(homedir(), '.eventmodelers', 'config.json');
90
90
  }
91
91
 
92
+ const ENV_CONFIG_MAP = {
93
+ EVENTMODELERS_ORGANIZATION_ID: 'organizationId',
94
+ EVENTMODELERS_BOARD_ID: 'boardId',
95
+ EVENTMODELERS_TOKEN: 'token',
96
+ EVENTMODELERS_BASE_URL: 'baseUrl',
97
+ EVENTMODELERS_ANTHROPIC_BASE_URL: 'anthropicBaseUrl',
98
+ EVENTMODELERS_MODEL: 'model',
99
+ EVENTMODELERS_SUBAGENT_MODEL: 'subagentModel',
100
+ EVENTMODELERS_AGENT_NAME: 'agentName',
101
+ };
102
+
92
103
  function loadLocalConfig(kitDir) {
93
104
  const merged = {};
94
105
  const sources = [];
@@ -109,6 +120,11 @@ function loadLocalConfig(kitDir) {
109
120
  if (hasCredentials(merged)) break;
110
121
  }
111
122
 
123
+ // EVENTMODELERS_* env vars win over any config.json, as they do for the CLI's own commands —
124
+ // a container gets its credentials this way, and a stale file in a mounted project must not beat them.
125
+ for (const [envVar, field] of Object.entries(ENV_CONFIG_MAP)) {
126
+ if (process.env[envVar]) merged[field] = process.env[envVar];
127
+ }
112
128
  if (process.env.BASE_URL) merged.baseUrl = process.env.BASE_URL;
113
129
  else if (!merged.baseUrl) merged.baseUrl = 'https://api.eventmodelers.ai';
114
130
 
@@ -280,6 +296,37 @@ async function fetchAndPersistSlices(cfg, kitDir) {
280
296
  console.log(`[agent] Persisted ${slices.length} slice(s)`);
281
297
  }
282
298
 
299
+ // The summary refresh above leaves slice.json as an id/title/status stub, and a build agent that
300
+ // reads a stub reports the slice as empty. So before a slice is handed to an agent — a Planned
301
+ // build or a queued task — its full definition (elements, fields, specifications) is fetched
302
+ // here and merged in. Best effort: on failure the agent still has /load-slice to fall back on.
303
+ async function fetchFullSlice(cfg, kitDir, sliceId) {
304
+ if (!sliceId) return;
305
+ const slicesDir = join(kitDir, '.slices');
306
+ try {
307
+ let entry = null;
308
+ for (const ctx of readdirSync(slicesDir, { withFileTypes: true })) {
309
+ if (!ctx.isDirectory()) continue;
310
+ const indexPath = join(slicesDir, ctx.name, 'index.json');
311
+ if (!existsSync(indexPath)) continue;
312
+ const found = (JSON.parse(readFileSync(indexPath, 'utf-8')).slices ?? []).find((e) => e.id === sliceId);
313
+ if (found) { entry = { ...found, contextSlug: found.contextSlug ?? ctx.name }; break; }
314
+ }
315
+ if (!entry) return;
316
+ const query = new URLSearchParams({ contextName: entry.contextName, sliceId });
317
+ const url = `${cfg.baseUrl}/api/org/${cfg.organizationId}/boards/${cfg.boardId}/slicedata?${query}`;
318
+ const { slices } = await fetchJSON(url, { headers: { 'x-token': cfg.token, ...agentHeaders(cfg) } });
319
+ const full = slices?.find((sl) => sl.id === sliceId);
320
+ if (!full) return;
321
+ const sliceJsonPath = join(slicesDir, entry.contextSlug, entry.folder, 'slice.json');
322
+ const existing = existsSync(sliceJsonPath) ? JSON.parse(readFileSync(sliceJsonPath, 'utf-8')) : {};
323
+ mkdirSync(dirname(sliceJsonPath), { recursive: true });
324
+ writeFileSync(sliceJsonPath, JSON.stringify({ ...existing, ...full }, null, 2), 'utf-8');
325
+ } catch (err) {
326
+ console.log(`[ralph] full slice fetch failed for ${sliceId} (${err.message}) — the agent will have to /load-slice it`);
327
+ }
328
+ }
329
+
283
330
  // tasks.json has two writers: this process, queueing slice changes, and the agent, removing
284
331
  // the task it just handled (as each stack's prompt.md tells it to). An unlocked read-modify-
285
332
  // write from both sides at once lost whichever update landed first. So the two never overlap:
@@ -655,6 +702,7 @@ async function ralphLoop(kitDir, cfg, onTask, onPlannedSlice, localOnly = false)
655
702
 
656
703
  if (credentialed && hasPendingTasks(kitDir)) {
657
704
  const prompt = readFileSync(promptFile, 'utf-8');
705
+ for (const t of readTasks(join(kitDir, 'tasks.json'))) await fetchFullSlice(cfg, kitDir, t.payload?.sliceId);
658
706
  await runWithRetry('onTask: loading slice from board...', () => inTurn(kitDir, () => onTask(prompt)));
659
707
  await fetchAndPersistSlices(cfg, kitDir).catch(() => {});
660
708
  didWork = true;
@@ -674,6 +722,7 @@ async function ralphLoop(kitDir, cfg, onTask, onPlannedSlice, localOnly = false)
674
722
  }
675
723
 
676
724
  const prompt = readFileSync(backendPromptFile, 'utf-8');
725
+ if (credentialed) await fetchFullSlice(cfg, kitDir, planned.id);
677
726
  const built = await runWithRetry(`onPlannedSlice: building slice "${planned.title}"...`, () => onPlannedSlice(prompt, {
678
727
  sliceId: planned.id,
679
728
  sliceTitle: planned.title,
@@ -1,5 +1,5 @@
1
1
  // What a slice costs: one trace per build turn against one slice, posted to the platform's
2
- // /api/org/:orgId/agent-traces. Only the runner can see what a turn cost, so it reports it.
2
+ // /api/org/:orgId/boards/:boardId/agent-traces. Only the runner can see what a turn cost, so it reports it.
3
3
  //
4
4
  // Best effort: each trace is appended to a local JSONL file, then POSTed once. No retries, no
5
5
  // buffering. A failed upload is logged and forgotten; the file is the record.
@@ -110,13 +110,14 @@ export function sumUsage(parts) {
110
110
 
111
111
  /** Records slice build turns. The agent id is the session: the platform requires one, and a
112
112
  * restart of the same build agent is still the same agent building the same slices. */
113
- export function createSliceTracer({ baseUrl, token, organizationId, agentId, traceFile, log = () => {}, enabled = true } = {}) {
113
+ export function createSliceTracer({ baseUrl, token, organizationId, boardId: defaultBoardId, agentId, traceFile, log = () => {}, enabled = true } = {}) {
114
114
  const canUpload = !!(enabled && baseUrl && token && organizationId && agentId);
115
115
 
116
116
  async function post(trace) {
117
- if (!canUpload) return;
117
+ // The route is per board — a trace without a board has nowhere to go, so it stays local.
118
+ if (!canUpload || !trace.boardId) return;
118
119
  try {
119
- const res = await fetch(`${baseUrl}/api/org/${organizationId}/agent-traces`, {
120
+ const res = await fetch(`${baseUrl}/api/org/${organizationId}/boards/${trace.boardId}/agent-traces`, {
120
121
  method: 'POST',
121
122
  headers: { 'Content-Type': 'application/json', 'x-token': token, 'x-agent-id': agentId },
122
123
  body: JSON.stringify({ traces: [trace] }),
@@ -141,7 +142,7 @@ export function createSliceTracer({ baseUrl, token, organizationId, agentId, tra
141
142
  return {
142
143
  /** One build turn: `{sliceId, sliceTitle, context, ticketNumber, boardId, attempt, status}` plus its usage.
143
144
  * ticketNumber is the slice's ticket at build time — it can change, so it travels with each turn. */
144
- record({ sliceId, sliceTitle = null, context = null, ticketNumber = null, boardId = null, attempt = null, status = 'ok' }, usage) {
145
+ record({ sliceId, sliceTitle = null, context = null, ticketNumber = null, boardId = defaultBoardId ?? null, attempt = null, status = 'ok' }, usage) {
145
146
  if (!sliceId || !usage) return;
146
147
  const trace = {
147
148
  id: newId(), occurredAt: new Date().toISOString(),
@@ -26,6 +26,7 @@ const tracer = createSliceTracer({
26
26
  baseUrl: cfg.baseUrl,
27
27
  token: cfg.token,
28
28
  organizationId: cfg.organizationId,
29
+ boardId: cfg.boardId,
29
30
  agentId: cfg.agentId,
30
31
  traceFile: join(projectDir, '.eventmodelers', 'trace', 'slices.jsonl'),
31
32
  log: (line) => console.log(`[ralph] ${line}`),
@@ -40,7 +41,9 @@ const timeoutMs = turnTimeoutMs(cfg);
40
41
  const verbose = process.env.RALPH_VERBOSE === '1';
41
42
 
42
43
  const claudeArgs = ['--dangerously-skip-permissions', '--output-format', 'stream-json', '--verbose'];
43
- if (cfg.model) claudeArgs.push('--model', cfg.model);
44
+ // RALPH_MODEL is `eventmodelers run --model` — one run's choice, over the configured `model`.
45
+ const model = process.env.RALPH_MODEL || cfg.model;
46
+ if (model) claudeArgs.push('--model', model);
44
47
  const claudeEnv = {
45
48
  ...process.env,
46
49
  ...(cfg.anthropicBaseUrl ? { ANTHROPIC_BASE_URL: cfg.anthropicBaseUrl } : {}),
@@ -20,6 +20,7 @@
20
20
  import { startRalph, loadLocalConfig, resolveAgentIdentity, connectHeader } from './lib/ralph.js';
21
21
  import { createSliceTracer, usageFromHarnessEvent, sumUsage } from './lib/tracing.js';
22
22
  import { turnTimeoutMs, superviseTurn } from './lib/turn.js';
23
+ import { registerMcp, shellQuote } from './lib/mcp-register.js';
23
24
  import { spawn } from 'child_process';
24
25
  import { writeFileSync, mkdtempSync } from 'fs';
25
26
  import { tmpdir } from 'os';
@@ -33,9 +34,9 @@ const cfg = loadLocalConfig(kitDir);
33
34
  const localOnly = process.env.RALPH_LOCAL === '1';
34
35
  // Same as ralph-claude.js: childEnv is built at load time, before startRalph resolves identity.
35
36
  Object.assign(cfg, resolveAgentIdentity(kitDir, 'BUILD', cfg));
36
- const execCmd = process.env.RALPH_EXEC_CMD || cfg.localAi?.exec;
37
+ const configuredCmd = process.env.RALPH_EXEC_CMD || cfg.localAi?.exec;
37
38
 
38
- if (!execCmd) {
39
+ if (!configuredCmd) {
39
40
  console.error('[ralph-exec] No agent command configured.');
40
41
  console.error(' Set one for this run: eventmodelers run --exec "codex exec --full-auto"');
41
42
  console.error(' Or persist a default as localAi.exec in .eventmodelers/config.json');
@@ -60,6 +61,7 @@ const tracer = createSliceTracer({
60
61
  baseUrl: cfg.baseUrl,
61
62
  token: cfg.token,
62
63
  organizationId: cfg.organizationId,
64
+ boardId: cfg.boardId,
63
65
  agentId: cfg.agentId,
64
66
  traceFile: join(projectDir, '.eventmodelers', 'trace', 'slices.jsonl'),
65
67
  log: (line) => console.log(`[ralph-exec] ${line}`),
@@ -68,12 +70,10 @@ const tracer = createSliceTracer({
68
70
 
69
71
  const promptDir = mkdtempSync(join(tmpdir(), 'ralph-exec-'));
70
72
 
71
- // POSIX single-quote escaping: close, insert an escaped quote, reopen. The prompt is
72
- // multi-line Markdown with backticks and $ in it, so it cannot go in unquoted.
73
- function shellQuote(s) {
74
- return `'${String(s).replace(/'/g, `'\\''`)}'`;
75
- }
76
-
73
+ // A known harness (OpenCode, Gemini, Codex) learns about the eventmodelers MCP server here, with the
74
+ // platform URL from the config — rewritten every run so a changed baseUrl never goes stale. A local-only
75
+ // run has no platform to register, and an unknown command is left to the skills' REST fallback.
76
+ const execCmd = localOnly ? configuredCmd : registerMcp(configuredCmd, projectDir, cfg.baseUrl, (line) => console.log(`[ralph-exec] ${line}`)).command;
77
77
  console.log(`[ralph-exec] command: ${execCmd}`);
78
78
 
79
79
  // Parses a harness's stdout for usage while still passing it through. Most harnesses emit JSONL;
@@ -124,7 +124,6 @@ MODEL_CONTEXT // Context/domain modeling container
124
124
  CHAPTER // Timeline/sequence container — a "chapter" IS a timeline (chapterId === timelineId); the terms are used interchangeably across the API
125
125
  ACTOR // System participant (swimlane label)
126
126
  AUTOMATION // Automated action
127
- API // External service
128
127
  SCREEN // UI screen
129
128
  COMMAND // State-changing operation
130
129
  EVENT // Domain event
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: request-feedback
3
- description: Post a comment on a slice and mark it Blocked when the slice's requirements are genuinely ambiguous, contradictory, or missing something a decision depends on. This is an escalation path, not a routine step — reach for it only when you cannot proceed without guessing.
3
+ description: Post a comment on a slice and mark it Blocked — ONLY when the slice literally cannot be built, even with reasonable assumptions. The absolute exception; for unclear or missing details, assume and build instead.
4
4
  ---
5
5
 
6
6
  # Request Feedback
@@ -11,41 +11,32 @@ Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect`
11
11
 
12
12
  ---
13
13
 
14
- ## When to use this skill — the exception, not the rule
14
+ ## When to use this skill — the absolute exception
15
15
 
16
- Building a slice is normally unambiguous: `slice.json` names every command field, event field, and
17
- GWT scenario, and the matching build skill (`build-state-change`, `build-state-view`,
18
- `build-automation`, ...) tells you exactly how to turn that into code. **Read `slice.json` fully, and
19
- read the build skill's own instructions and reference docs, before ever concluding something is
20
- missing.** Most slices need none of this.
16
+ **The default is to assume and build.** A missing example value, an unclear field type, a status
17
+ mapping, a referenced event that isn't modeled yet, a dependency slice that isn't built yet — none of
18
+ these is a reason to stop. Pick the most sensible interpretation from `slice.json`, its
19
+ `specifications[]` and the surrounding model, build the slice, and record each assumption in one line
20
+ in `progress.txt` and as a code comment. **Read `slice.json` fully, and read the build skill's own
21
+ instructions and reference docs, before concluding anything is missing.**
21
22
 
22
- Only invoke `request-feedback` when, after that reading, a decision the implementation depends on
23
- still cannot be made without guessing — for example:
24
-
25
- - A business rule mentioned in `description`/`comments`/`notes` isn't backed by any field, event, or
26
- `specifications[]` scenario, so there's no way to encode it.
27
- - Two fields, or a field and an `idAttribute` flag, contradict each other about what the slice is
28
- supposed to do.
29
- - A `specifications[]` scenario references a prior event or state that no command/event in this slice
30
- (or its declared dependencies) actually produces.
31
- - The slice depends on another slice, screen, or read model that doesn't exist yet and it's unclear
32
- whether one should be created, or the dependency was meant to point elsewhere.
23
+ Invoke `request-feedback` **only if the slice literally cannot be built**: even with sensible
24
+ assumptions, nothing runnable and tested can be produced. For example, the slice has no elements at
25
+ all (no command/read model, no fields), or two parts of it contradict each other so that no single
26
+ implementation satisfies both.
33
27
 
34
28
  **Do not use this skill for:**
35
- - Implementation-detail choices the build skill's own instructions already answer (e.g. package
36
- naming, file layout, which annotation to use) — those aren't ambiguity in the slice, they're just
37
- reading the skill more carefully.
38
- - Style or naming preferences with no functional consequence — pick the reasonable option and move on.
39
- - "This would be nice to confirm" — if you *can* proceed correctly from what `slice.json` says, proceed.
40
-
41
- When in doubt, prefer finishing the slice over escalating. Escalating on every minor uncertainty
42
- defeats the purpose — it should be rare enough that a `Blocked` slice reliably means "a human needs to
43
- look at this," not "the agent didn't feel like deciding."
44
-
45
- **When this skill does apply: do not guess and build anyway.** Post the question and stop work on this
46
- slice for this run — do not implement your best interpretation first. A wrong guess encoded into
47
- working, tested, committed code is harder to catch and undo than an unbuilt slice waiting for an
48
- answer.
29
+ - Missing examples, field types, names, or status values — assume, build, note the assumption.
30
+ - A dependency (event, command, other slice) that isn't implemented yet — assume its definition from
31
+ the model and build against it.
32
+ - Implementation-detail choices the build skill's own instructions already answer.
33
+ - Style or naming preferences, or "this would be nice to confirm".
34
+
35
+ A `Blocked` slice must reliably mean "impossible without a human", never "the agent preferred to ask".
36
+ When in doubt, build.
37
+
38
+ **When this skill does apply:** post the question and stop work on this slice for this run — an
39
+ impossible build can't be finished by guessing.
49
40
 
50
41
  ---
51
42
 
@@ -42,12 +42,7 @@ When starting to work on a slice, invoke the `update-slice-status` skill with `I
42
42
  **CRITICAL: You MUST always use the provided skills to build slices. NEVER implement a slice manually.**
43
43
  **ALL fields, event names, command names, and business rules MUST come exclusively from slice.json. Do NOT invent, assume, or guess any field or logic not present in the slice definition.**
44
44
 
45
- **If, at any point below, the slice's requirements are genuinely ambiguous, contradictory, or missing
46
- a decision you need in order to proceed — do not guess, and do not build anyway.** Invoke the
47
- `request-feedback` skill with the specific question; it posts the question as a comment on the slice
48
- and marks it `Blocked`, and you then stop work on this slice for this run. This is an escalation path,
49
- not a routine step — read `slice.json` and the matching build skill's own instructions fully first;
50
- most slices are fully specified and need none of this.
45
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
51
46
 
52
47
  When asked to build a slice, always follow this flow:
53
48
 
@@ -50,14 +50,7 @@ You work within **exactly ONE context at a time** — the one named in `.build-k
50
50
 
51
51
  ## Escalating Ambiguity
52
52
 
53
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need
54
- in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the
55
- specific question; it posts the question as a comment on the slice and marks it `Blocked` on the
56
- board (overriding the `InProgress` set earlier), then stop this iteration without finishing the
57
- build — reply `<promise>DONE</promise>` as if the iteration's work was to raise the question, not to
58
- implement the slice. This is an escalation path, not a routine step — read the slice.json and the
59
- matching build skill's own instructions fully first; most slices are fully specified and need none of
60
- this.
53
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
61
54
 
62
55
  ## Progress Report Format
63
56
 
@@ -48,13 +48,13 @@ Inspect the `sliceStatus` in the payload:
48
48
 
49
49
  This is the build trigger. Setting `InProgress` and building are one atomic step:
50
50
 
51
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked` (superseding the `InProgress` set in step 1), then drop this task without finishing the build. This is an escalation path, not a routine step — most slices are fully specified.
51
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
52
52
 
53
53
  1. Immediately call `/update-slice-status` to set the slice to `InProgress` on the board.
54
54
 
55
55
  **Claim conflict**: if this call reports the slice is already in `InProgress` (or any status other than `Planned`), another agent already claimed it first — this is expected, not an error. Log it in `progress.txt`, drop this task without building, and continue the loop (the next task will naturally cover the next slice). Do not retry.
56
56
 
57
- 2. Read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json` (written by `/load-slice`).
57
+ 2. Run `/load-slice sliceId=<payload.sliceId>` to fetch the full slice, then read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json`. The runner keeps a `slice.json` fresh from a summary endpoint, so a file holding only `id`, `title`, `status` and `sliceType` is that stub, not an empty slice — never conclude a slice has no elements from it. Only if the full fetch itself returns no commands/events/read models/fields is the slice actually empty.
58
58
 
59
59
  3. Determine the **slice type** from the slice.json:
60
60
  - **Translation** — `sliceType === "TRANSLATION"` → read `description` and `notes` from slice.json for hints; default to `/build-automation` if nothing else is specified
@@ -32,12 +32,7 @@ When starting to work on a slice, invoke the `update-slice-status` skill with `I
32
32
  **CRITICAL: You MUST always use the provided skills to build slices. NEVER implement a slice manually.**
33
33
  **ALL fields, event names, command names, and business rules MUST come exclusively from slice.json. Do NOT invent, assume, or guess any field or logic not present in the slice definition.**
34
34
 
35
- **If, at any point below, the slice's requirements are genuinely ambiguous, contradictory, or missing
36
- a decision you need in order to proceed — do not guess, and do not build anyway.** Invoke the
37
- `request-feedback` skill with the specific question; it posts the question as a comment on the slice
38
- and marks it `Blocked`, and you then stop work on this slice for this run. This is an escalation path,
39
- not a routine step — read `slice.json` and the matching build skill's own instructions fully first;
40
- most slices are fully specified and need none of this.
35
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
41
36
 
42
37
  When asked to build a slice, always follow this flow:
43
38
 
@@ -40,14 +40,7 @@ You work within **exactly ONE context at a time** — the one named in `.build-k
40
40
 
41
41
  ## Escalating Ambiguity
42
42
 
43
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need
44
- in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the
45
- specific question; it posts the question as a comment on the slice and marks it `Blocked` on the
46
- board (overriding the `InProgress` set in step 5), then stop this iteration without finishing the
47
- build — reply `<promise>DONE</promise>` as if the iteration's work was to raise the question, not to
48
- implement the slice. This is an escalation path, not a routine step — read the slice.json and the
49
- matching build skill's own instructions fully first; most slices are fully specified and need none of
50
- this.
43
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
51
44
 
52
45
  ## Progress Report Format
53
46
 
@@ -52,7 +52,7 @@ This is the build trigger. Setting `InProgress` and building are one atomic step
52
52
 
53
53
  **Claim conflict**: if this call reports the slice is already in `InProgress` (or any status other than `Planned`), another agent already claimed it first — this is expected, not an error. Log it in `progress.txt`, drop this task without building, and continue the loop (the next task will naturally cover the next slice). Do not retry.
54
54
 
55
- 2. Read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json` (written by `/load-slice`).
55
+ 2. Run `/load-slice sliceId=<payload.sliceId>` to fetch the full slice, then read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json`. The runner keeps a `slice.json` fresh from a summary endpoint, so a file holding only `id`, `title`, `status` and `sliceType` is that stub, not an empty slice — never conclude a slice has no elements from it. Only if the full fetch itself returns no commands/events/read models/fields is the slice actually empty.
56
56
 
57
57
  3. Determine the **slice type** from the slice.json:
58
58
  - **Translation** — `sliceType === "TRANSLATION"` → read `description` and `notes` from slice.json for hints; default to `/build-automation` if nothing else is specified
@@ -93,14 +93,7 @@ After completing a task, remove it from the array and write the updated array ba
93
93
 
94
94
  ## Escalating Ambiguity
95
95
 
96
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need
97
- in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the
98
- specific question; it posts the question as a comment on the slice and marks it `Blocked` on the
99
- board (overriding the `InProgress` set earlier), then stop this iteration without finishing the
100
- build — reply `<promise>DONE</promise>` as if the iteration's work was to raise the question, not to
101
- implement the slice. This is an escalation path, not a routine step — read the slice.json and the
102
- matching build skill's own instructions fully first; most slices are fully specified and need none of
103
- this.
96
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
104
97
 
105
98
  ## Progress Report Format
106
99
 
@@ -76,12 +76,7 @@ doing anything else.
76
76
  **ALL fields, event names, command names, and business rules MUST come exclusively from slice.json. Do NOT
77
77
  invent, assume, or guess any field or logic not present in the slice definition.**
78
78
 
79
- **If, at any point below, the slice's requirements are genuinely ambiguous, contradictory, or missing a
80
- decision you need in order to proceed — do not guess, and do not build anyway.** Invoke the
81
- `request-feedback` skill with the specific question; it posts the question as a comment on the slice and
82
- marks it `Blocked`, and you then stop work on this slice for this run. This is an escalation path, not a
83
- routine step — read `slice.json` and the matching build skill's own instructions fully first; most slices
84
- are fully specified and need none of this.
79
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
85
80
 
86
81
  When asked to build a slice, always follow this flow:
87
82
 
@@ -50,14 +50,7 @@ You work within **exactly ONE context at a time** — the one named in `.build-k
50
50
 
51
51
  ## Escalating Ambiguity
52
52
 
53
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need
54
- in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the
55
- specific question; it posts the question as a comment on the slice and marks it `Blocked` on the
56
- board (overriding the `InProgress` set earlier), then stop this iteration without finishing the
57
- build — reply `<promise>DONE</promise>` as if the iteration's work was to raise the question, not to
58
- implement the slice. This is an escalation path, not a routine step — read the slice.json and the
59
- matching build skill's own instructions fully first; most slices are fully specified and need none of
60
- this.
53
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
61
54
 
62
55
  ## Progress Report Format
63
56
 
@@ -48,13 +48,13 @@ Inspect the `sliceStatus` in the payload:
48
48
 
49
49
  This is the build trigger. Setting `InProgress` and building are one atomic step:
50
50
 
51
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked` (superseding the `InProgress` set in step 1), then drop this task without finishing the build. This is an escalation path, not a routine step — most slices are fully specified.
51
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
52
52
 
53
53
  1. Immediately call `/update-slice-status` to set the slice to `InProgress` on the board.
54
54
 
55
55
  **Claim conflict**: if this call reports the slice is already in `InProgress` (or any status other than `Planned`), another agent already claimed it first — this is expected, not an error. Log it in `progress.txt`, drop this task without building, and continue the loop (the next task will naturally cover the next slice). Do not retry.
56
56
 
57
- 2. Read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json` (written by `/load-slice`).
57
+ 2. Run `/load-slice sliceId=<payload.sliceId>` to fetch the full slice, then read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json`. The runner keeps a `slice.json` fresh from a summary endpoint, so a file holding only `id`, `title`, `status` and `sliceType` is that stub, not an empty slice — never conclude a slice has no elements from it. Only if the full fetch itself returns no commands/events/read models/fields is the slice actually empty.
58
58
 
59
59
  3. Determine the **slice type** from the slice.json:
60
60
  - **Translation** — `sliceType === "TRANSLATION"` → read `description` and `notes` from slice.json for hints; default to `/build-automation` if nothing else is specified
@@ -91,7 +91,7 @@ For each group, create a chapter on the board **before placing any events**. Reu
91
91
 
92
92
  **Prefer MCP — create a chapter:**
93
93
  ```
94
- mcp__eventmodelers__create_chapter { "boardId": "<BOARD_ID>", "x": 0, "y": 1200, "columns": <numberOfEventsInGroup> }
94
+ mcp__eventmodelers__create_chapter { "boardId": "<BOARD_ID>", "columns": <numberOfEventsInGroup> }
95
95
  ```
96
96
  (`x`/`y` are optional — see the vertical-stacking note below. `columns` is optional too — the group's event count is already known at this point, so pass it here to create the chapter with exactly the columns this group needs, instead of the default 3 plus a follow-up `add_column` batch. Response includes the new `timelineId` and `columnIds` — one id per column, left to right, ready to use directly in Step A below.)
97
97
 
@@ -115,11 +115,11 @@ mcp__eventmodelers__submit_node_events {
115
115
  **Fallback (no MCP):** see `references/api-fallback.md` — "Timeline Discovery — Step 2: Create one chapter per group — set its title".
116
116
 
117
117
  **Stack timelines vertically so they do not overlap, and in general place new chapters close to existing ones they relate to.**
118
- After creating each chapter, position it below the previous one. Use `y = index * 1200` (0-based creation order), `x = 0`. If existing chapters are already on the board, query their positions first. Prefer placing the new chapter directly below the existing chapter it is most closely related to (e.g. the same bounded context or an adjacent workflow), rather than mechanically appending below the lowest one — this keeps related chapters visually near each other on the canvas. Only fall back to `y = maxExistingY + 1200` when no related chapter exists yet. Pass this directly as `x`/`y` on `create_chapter` above, or reposition an existing chapter with:
118
+ Omit `x`/`y` on `create_chapter`: the backend stacks the new chapter below the lowest existing one, using each chapter's real current size plus a generous gap, so titles never overlap. Do not use a fixed offset like `index * 1200` — chapters are taller than that. Only pass a position to put a chapter elsewhere; if the box overlaps an existing chapter the backend moves it below everything. Prefer placing the new chapter directly below the existing chapter it is most closely related to (e.g. the same bounded context or an adjacent workflow), rather than mechanically appending below the lowest one — this keeps related chapters visually near each other on the canvas. Otherwise leave the position to the backend. To place next to a related chapter, read `get_chapter_bounds` and use `y = related.y + related.height + 600`, or reposition an existing chapter with:
119
119
 
120
120
  **Prefer MCP:**
121
121
  ```
122
- mcp__eventmodelers__move_timeline_position { "boardId": "<BOARD_ID>", "timelineId": "<TL>", "x": 0, "y": 1200 }
122
+ mcp__eventmodelers__move_timeline_position { "boardId": "<BOARD_ID>", "timelineId": "<TL>", "x": 0, "y": <y> }
123
123
  ```
124
124
 
125
125
  **Fallback (no MCP):** see `references/api-fallback.md` — "Timeline Discovery — Step 2: Reposition an existing chapter".
@@ -426,6 +426,6 @@ Include error and boundary conditions:
426
426
  - [ ] Events group into at least one recognizable business process flow
427
427
  - [ ] No overlapping event semantics — two events don't mean the same thing
428
428
  - [ ] Every event is placed into a **named chapter** — no event left in an untitled or default timeline
429
- - [ ] **Multiple chapters are stacked vertically** (y offset of 1200 per chapter) — no two chapters overlap on the canvas
429
+ - [ ] **Multiple chapters are stacked vertically** (auto-stacked by the backend) — no two chapters overlap on the canvas
430
430
  - [ ] No unnecessary swimlanes created — a swimlane exists only to mark integration with another system; never added for a new actor, a new role, or visual grouping
431
431
  - [ ] **When a second (external-system) swimlane exists, events follow one continuous story from this chapter's own swimlane throughout** — the external swimlane only ever holds isolated single-column handovers, never a run of its own, and the story resumes in this chapter's swimlane immediately after each one
@@ -37,12 +37,7 @@ When starting to work on a slice, invoke the `update-slice-status` skill with `I
37
37
  **CRITICAL: You MUST always use the provided skills to build slices. NEVER implement a slice manually.**
38
38
  **ALL fields, event names, command names, and business rules MUST come exclusively from slice.json. Do NOT invent, assume, or guess any field or logic not present in the slice definition.**
39
39
 
40
- **If, at any point below, the slice's requirements are genuinely ambiguous, contradictory, or missing
41
- a decision you need in order to proceed — do not guess, and do not build anyway.** Invoke the
42
- `request-feedback` skill with the specific question; it posts the question as a comment on the slice
43
- and marks it `Blocked`, and you then stop work on this slice for this run. This is an escalation path,
44
- not a routine step — read `slice.json` and the matching build skill's own instructions fully first;
45
- most slices are fully specified and need none of this.
40
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
46
41
 
47
42
  When asked to build a slice, always follow this flow:
48
43
 
@@ -44,14 +44,7 @@ You work within **exactly ONE context at a time** — the one named in `.build-k
44
44
 
45
45
  ## Escalating Ambiguity
46
46
 
47
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need
48
- in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the
49
- specific question; it posts the question as a comment on the slice and marks it `Blocked` on the
50
- board (overriding the `InProgress` set earlier), then stop this iteration without finishing the
51
- build — reply `<promise>DONE</promise>` as if the iteration's work was to raise the question, not to
52
- implement the slice. This is an escalation path, not a routine step — read the slice.json and the
53
- matching build skill's own instructions fully first; most slices are fully specified and need none of
54
- this.
47
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
55
48
 
56
49
  ## Progress Report Format
57
50
 
@@ -48,13 +48,13 @@ Inspect the `sliceStatus` in the payload:
48
48
 
49
49
  This is the build trigger. Setting `InProgress` and building are one atomic step:
50
50
 
51
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked` (superseding the `InProgress` set in step 1), then drop this task without finishing the build. This is an escalation path, not a routine step — most slices are fully specified.
51
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
52
52
 
53
53
  1. Immediately call `/update-slice-status` to set the slice to `InProgress` on the board.
54
54
 
55
55
  **Claim conflict**: if this call reports the slice is already in `InProgress` (or any status other than `Planned`), another agent already claimed it first — this is expected, not an error. Log it in `progress.txt`, drop this task without building, and continue the loop (the next task will naturally cover the next slice). Do not retry.
56
56
 
57
- 2. Read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json` (written by `/load-slice`).
57
+ 2. Run `/load-slice sliceId=<payload.sliceId>` to fetch the full slice, then read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json`. The runner keeps a `slice.json` fresh from a summary endpoint, so a file holding only `id`, `title`, `status` and `sliceType` is that stub, not an empty slice — never conclude a slice has no elements from it. Only if the full fetch itself returns no commands/events/read models/fields is the slice actually empty.
58
58
 
59
59
  3. Determine the **slice type** from the slice.json:
60
60
  - **Translation** — `sliceType === "TRANSLATION"` → read `description` and `notes` from slice.json for hints; default to `/build-automation` if nothing else is specified
@@ -62,12 +62,7 @@ doing anything else.
62
62
  **ALL fields, event names, command names, and business rules MUST come exclusively from slice.json. Do NOT
63
63
  invent, assume, or guess any field or logic not present in the slice definition.**
64
64
 
65
- **If, at any point below, the slice's requirements are genuinely ambiguous, contradictory, or missing a
66
- decision you need in order to proceed — do not guess, and do not build anyway.** Invoke the
67
- `request-feedback` skill with the specific question; it posts the question as a comment on the slice and
68
- marks it `Blocked`, and you then stop work on this slice for this run. This is an escalation path, not a
69
- routine step — read `slice.json` and the matching build skill's own instructions fully first; most slices
70
- are fully specified and need none of this.
65
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
71
66
 
72
67
  When asked to build a slice, always follow this flow:
73
68
 
@@ -50,14 +50,7 @@ You work within **exactly ONE context at a time** — the one named in `.build-k
50
50
 
51
51
  ## Escalating Ambiguity
52
52
 
53
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need
54
- in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the
55
- specific question; it posts the question as a comment on the slice and marks it `Blocked` on the
56
- board (overriding the `InProgress` set earlier), then stop this iteration without finishing the
57
- build — reply `<promise>DONE</promise>` as if the iteration's work was to raise the question, not to
58
- implement the slice. This is an escalation path, not a routine step — read the slice.json and the
59
- matching build skill's own instructions fully first; most slices are fully specified and need none of
60
- this.
53
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
61
54
 
62
55
  ## Progress Report Format
63
56
 
@@ -48,13 +48,13 @@ Inspect the `sliceStatus` in the payload:
48
48
 
49
49
  This is the build trigger. Setting `InProgress` and building are one atomic step:
50
50
 
51
- **If the slice's requirements are genuinely ambiguous, contradictory, or missing a decision you need in order to proceed — do not guess, and do not build anyway.** Invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked` (superseding the `InProgress` set in step 1), then drop this task without finishing the build. This is an escalation path, not a routine step — most slices are fully specified.
51
+ **Default: make a reasonable assumption and build the slice.** If a detail is unclear or missing (an example value, a field type, a status mapping, a referenced event that isn't modeled yet, ...), pick the most sensible interpretation from `slice.json`, its specifications and the surrounding model, build it, and record each assumption in one line in `progress.txt` and as a code comment. Ambiguity alone is never a reason to stop. **Only if the slice literally cannot be built** — nothing runnable can be produced even with sensible assumptions — invoke `/request-feedback` with the specific question; it posts the question as a comment on the slice and marks it `Blocked`. That must be the absolute exception; a `Blocked` slice should mean "impossible without a human", never "the agent preferred to ask".
52
52
 
53
53
  1. Immediately call `/update-slice-status` to set the slice to `InProgress` on the board.
54
54
 
55
55
  **Claim conflict**: if this call reports the slice is already in `InProgress` (or any status other than `Planned`), another agent already claimed it first — this is expected, not an error. Log it in `progress.txt`, drop this task without building, and continue the loop (the next task will naturally cover the next slice). Do not retry.
56
56
 
57
- 2. Read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json` (written by `/load-slice`).
57
+ 2. Run `/load-slice sliceId=<payload.sliceId>` to fetch the full slice, then read the slice definition from `.build-kit/.slices/<contextSlug>/<sliceFolder>/slice.json`. The runner keeps a `slice.json` fresh from a summary endpoint, so a file holding only `id`, `title`, `status` and `sliceType` is that stub, not an empty slice — never conclude a slice has no elements from it. Only if the full fetch itself returns no commands/events/read models/fields is the slice actually empty.
58
58
 
59
59
  3. Determine the **slice type** from the slice.json:
60
60
  - **Translation** — `sliceType === "TRANSLATION"` → read `description` and `notes` from slice.json for hints; default to `/build-automation` if nothing else is specified