@wrongstack/core 0.301.0 → 0.302.2

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 (44) hide show
  1. package/dist/agent-status-tracker.d.ts +6 -2
  2. package/dist/chronicle/index.js +1836 -1645
  3. package/dist/chronicle/metrics-store.d.ts +14 -0
  4. package/dist/chronicle/project-server-protocol.d.ts +13 -0
  5. package/dist/chronicle/project-server.js +1759 -1583
  6. package/dist/chronicle/rollup-adapter.d.ts +2 -0
  7. package/dist/chronicle/sqlite-journal.d.ts +59 -0
  8. package/dist/coordination/index.js +791 -249
  9. package/dist/coordination/mail-tools.d.ts +2 -2
  10. package/dist/core/continue-intent.d.ts +2 -0
  11. package/dist/core/conversation-state.d.ts +5 -0
  12. package/dist/core/index.js +120 -19
  13. package/dist/defaults/index.js +928 -374
  14. package/dist/execution/index.js +28 -11
  15. package/dist/index.d.ts +3 -1
  16. package/dist/index.js +8763 -6781
  17. package/dist/infrastructure/index.js +722 -672
  18. package/dist/kernel/events/memory-events.d.ts +62 -0
  19. package/dist/plugin/index.js +2154 -1979
  20. package/dist/session-catalog/client.d.ts +62 -0
  21. package/dist/session-catalog/endpoint.d.ts +6 -0
  22. package/dist/session-catalog/index.d.ts +6 -0
  23. package/dist/session-catalog/index.js +1978 -0
  24. package/dist/session-catalog/project-server.d.ts +3 -0
  25. package/dist/session-catalog/project-server.js +1838 -0
  26. package/dist/session-catalog/protocol.d.ts +275 -0
  27. package/dist/session-catalog/registry.d.ts +59 -0
  28. package/dist/session-catalog/store.d.ts +55 -0
  29. package/dist/session-registry-types.d.ts +17 -0
  30. package/dist/session-registry.d.ts +1 -1
  31. package/dist/storage/index.d.ts +42 -38
  32. package/dist/storage/index.js +14279 -13393
  33. package/dist/storage/session-event-bridge.d.ts +2 -2
  34. package/dist/storage/session-store.d.ts +6 -0
  35. package/dist/tools/index.js +8 -2
  36. package/dist/types/context-evidence.d.ts +2 -0
  37. package/dist/types/messages.d.ts +8 -0
  38. package/dist/types/session.d.ts +19 -0
  39. package/dist/utils/context-evidence.d.ts +13 -1
  40. package/dist/utils/index.js +26 -2
  41. package/instructions/system-lite.md +11 -2
  42. package/instructions/system-pro.md +14 -0
  43. package/instructions/system.md +14 -0
  44. package/package.json +7 -3
@@ -45,12 +45,12 @@ export interface SessionEventBridge {
45
45
  allows(type: SessionEvent['type']): boolean;
46
46
  }
47
47
  /** Core events that are always written regardless of auditLevel. */
48
- declare const CORE_RECONSTRUCT_EVENTS: Set<"agent_error" | "agent_spawned" | "agent_stopped" | "checkpoint" | "compaction" | "context_snapshot" | "error" | "file_event" | "file_observation" | "file_snapshot" | "in_flight_end" | "in_flight_start" | "llm_request" | "llm_response" | "message_appended" | "message_truncated" | "message_updated" | "messages_replaced" | "mode_changed" | "provider_error" | "provider_retry" | "rewound" | "session_end" | "session_forked" | "session_resumed" | "session_start" | "side_effect" | "skill_activated" | "skill_deactivated" | "spec_analyzed" | "spec_parsed" | "task_completed" | "task_created" | "task_failed" | "task_updated" | "tool_call_end" | "tool_call_start" | "tool_progress" | "tool_result" | "tool_use" | "user_input">;
48
+ declare const CORE_RECONSTRUCT_EVENTS: Set<"agent_error" | "agent_spawned" | "agent_stopped" | "checkpoint" | "compaction" | "context_snapshot" | "error" | "file_event" | "file_observation" | "file_snapshot" | "in_flight_end" | "in_flight_start" | "llm_request" | "llm_response" | "message_appended" | "message_truncated" | "message_updated" | "messages_dropped" | "messages_replaced" | "mode_changed" | "provider_error" | "provider_retry" | "rewound" | "session_end" | "session_forked" | "session_resumed" | "session_start" | "side_effect" | "skill_activated" | "skill_deactivated" | "spec_analyzed" | "spec_parsed" | "task_completed" | "task_created" | "task_failed" | "task_updated" | "tool_call_end" | "tool_call_start" | "tool_progress" | "tool_result" | "tool_use" | "user_input">;
49
49
  /**
50
50
  * Events that are considered "standard" audit detail.
51
51
  * These are lightweight and high-value for forensics.
52
52
  */
53
- declare const STANDARD_AUDIT_EVENTS: Set<"agent_error" | "agent_spawned" | "agent_stopped" | "checkpoint" | "compaction" | "context_snapshot" | "error" | "file_event" | "file_observation" | "file_snapshot" | "in_flight_end" | "in_flight_start" | "llm_request" | "llm_response" | "message_appended" | "message_truncated" | "message_updated" | "messages_replaced" | "mode_changed" | "provider_error" | "provider_retry" | "rewound" | "session_end" | "session_forked" | "session_resumed" | "session_start" | "side_effect" | "skill_activated" | "skill_deactivated" | "spec_analyzed" | "spec_parsed" | "task_completed" | "task_created" | "task_failed" | "task_updated" | "tool_call_end" | "tool_call_start" | "tool_progress" | "tool_result" | "tool_use" | "user_input">;
53
+ declare const STANDARD_AUDIT_EVENTS: Set<"agent_error" | "agent_spawned" | "agent_stopped" | "checkpoint" | "compaction" | "context_snapshot" | "error" | "file_event" | "file_observation" | "file_snapshot" | "in_flight_end" | "in_flight_start" | "llm_request" | "llm_response" | "message_appended" | "message_truncated" | "message_updated" | "messages_dropped" | "messages_replaced" | "mode_changed" | "provider_error" | "provider_retry" | "rewound" | "session_end" | "session_forked" | "session_resumed" | "session_start" | "side_effect" | "skill_activated" | "skill_deactivated" | "spec_analyzed" | "spec_parsed" | "task_completed" | "task_created" | "task_failed" | "task_updated" | "tool_call_end" | "tool_call_start" | "tool_progress" | "tool_result" | "tool_use" | "user_input">;
54
54
  /**
55
55
  * Create a safe, audit-level-aware bridge around a SessionWriter.
56
56
  *
@@ -11,6 +11,9 @@ export declare class DefaultSessionStore implements SessionStore {
11
11
  private readonly logger;
12
12
  private readonly onAppend?;
13
13
  private readonly onAppendBatch?;
14
+ /** Present in built production output; source-only tests retain the local compatibility path. */
15
+ private readonly catalogClient;
16
+ private readonly maintenanceHolderId;
14
17
  /**
15
18
  * In-memory cache for load() results, keyed by session ID. The cache is
16
19
  * invalidated when the file's mtimeMs or size changes (indicating the
@@ -38,6 +41,7 @@ export declare class DefaultSessionStore implements SessionStore {
38
41
  * the file has changed externally (e.g., another process wrote to it).
39
42
  */
40
43
  clearLoadCache(sessionId?: string): void;
44
+ dispose(): Promise<void>;
41
45
  /** Absolute path to the session index file. */
42
46
  private get indexFile();
43
47
  /** Join session ID to its absolute path within the store directory. */
@@ -131,6 +135,8 @@ export declare class DefaultSessionStore implements SessionStore {
131
135
  private appendToIndexStrict;
132
136
  /** Best-effort index append used by writer close. */
133
137
  private appendToIndex;
138
+ /** Final summary boundary: daemon is authoritative when available. */
139
+ private persistCatalogSummary;
134
140
  /** Append a tombstone entry for a deleted session. */
135
141
  private writeTombstone;
136
142
  /**
@@ -8352,22 +8352,27 @@ var OneShotOrchestrator = class {
8352
8352
  */
8353
8353
  resolveFallbackChain(input, config, target) {
8354
8354
  const mgr = this.opts.fallbackProfileManager;
8355
+ let fromExplicitSource = false;
8355
8356
  let selected = mgr.resolveEffective({
8356
8357
  fallbackAuto: false,
8357
8358
  exclude: target
8358
8359
  });
8359
8360
  if (input.fallbackModels && input.fallbackModels.length > 0) {
8360
- selected = mgr.resolveEffective({
8361
+ const resolved = mgr.resolveEffective({
8361
8362
  fallbackModels: input.fallbackModels,
8362
8363
  fallbackAuto: false,
8363
8364
  exclude: target
8364
8365
  });
8366
+ selected = resolved;
8367
+ fromExplicitSource = resolved.length > 0;
8365
8368
  } else if (config.fallbackModels && config.fallbackModels.length > 0) {
8366
- selected = mgr.resolveEffective({
8369
+ const resolved = mgr.resolveEffective({
8367
8370
  fallbackModels: config.fallbackModels,
8368
8371
  fallbackAuto: false,
8369
8372
  exclude: target
8370
8373
  });
8374
+ selected = resolved;
8375
+ fromExplicitSource = resolved.length > 0;
8371
8376
  } else if (config.fallbackAuto !== false) {
8372
8377
  selected = mgr.resolveEffective({
8373
8378
  fallbackAuto: true,
@@ -8375,6 +8380,7 @@ var OneShotOrchestrator = class {
8375
8380
  });
8376
8381
  }
8377
8382
  if (config.fallbackAuto === false) return selected;
8383
+ if (fromExplicitSource) return selected;
8378
8384
  const combined = [...selected, ...mgr.resolveAllConfigured(target)];
8379
8385
  const seen = /* @__PURE__ */ new Set();
8380
8386
  return Object.freeze(
@@ -54,6 +54,8 @@ export interface CompletedWorkEvidence {
54
54
  }
55
55
  export interface ContextEvidenceState {
56
56
  currentIntent?: ContextIntentEvidence | undefined;
57
+ /** Recent real human inputs, bounded and rendered as volatile continuity evidence. */
58
+ recentUserTurns?: ContextIntentEvidence[] | undefined;
57
59
  sessionGoals: string[];
58
60
  implicitFacts: string[];
59
61
  activeErrors: string[];
@@ -3,6 +3,14 @@ export type MessageRole = 'user' | 'assistant' | 'system';
3
3
  export interface Message {
4
4
  role: MessageRole;
5
5
  content: string | ContentBlock[];
6
+ /**
7
+ * Local provenance used by context management. `user_input` is reserved for
8
+ * text the human actually submitted; provider adapters intentionally ignore
9
+ * this field. Keeping it on the journaled message lets resumed sessions
10
+ * distinguish real conversation turns from tool-result/runtime `user`
11
+ * messages without relying on brittle text heuristics.
12
+ */
13
+ origin?: 'user_input' | 'runtime' | undefined;
6
14
  /**
7
15
  * ISO-8601 timestamp from the originating SessionEvent.
8
16
  * Populated by SessionStore.replay() during session load/resume
@@ -137,6 +137,23 @@ export type SessionEvent = {
137
137
  * payload had on disk. Absent on freshly emitted events.
138
138
  */
139
139
  messagesOmitted?: number;
140
+ } | {
141
+ /**
142
+ * The oldest `count` messages were evicted from the front of the history.
143
+ *
144
+ * A delta rather than a `messages_replaced` snapshot, because eviction is
145
+ * the one rewrite that repeats: once a long session reaches
146
+ * `Context.MAX_MESSAGES`, *every* subsequent append overflows by one and
147
+ * drops one. Emitting the surviving history each time made the journal
148
+ * quadratic in session length — measured at 2.1 GB for one session whose
149
+ * actual content was ~10 MB, and 17.9 GB across a 20 GB corpus. Replay
150
+ * splices the same prefix off, so the reconstructed conversation is
151
+ * identical to what the snapshot would have produced.
152
+ */
153
+ type: 'messages_dropped';
154
+ ts: string;
155
+ version: 1;
156
+ count: number;
140
157
  } | {
141
158
  /**
142
159
  * Exact post-rewrite conversation state. Replay replaces all messages
@@ -590,6 +607,8 @@ export interface SessionStore {
590
607
  * number of sessions indexed.
591
608
  */
592
609
  rebuildIndex?(): Promise<number>;
610
+ /** Release project-daemon connections owned by this store. */
611
+ dispose?(): Promise<void>;
593
612
  /**
594
613
  * Streaming event-level search. Walks the JSONL once without buffering
595
614
  * the whole file, calling `predicate(event, eventIndex, ts)` for each
@@ -1,5 +1,5 @@
1
1
  import type { Context } from '../core/context.js';
2
- import type { TextBlock } from '../types/blocks.js';
2
+ import { type TextBlock } from '../types/blocks.js';
3
3
  import type { CompactReport } from '../types/compactor.js';
4
4
  import type { CompletedWorkEvidence, CompletedWorkSource, ContextEvidenceState, ToolOutputMetadata } from '../types/context-evidence.js';
5
5
  export declare function createContextEvidenceState(): ContextEvidenceState;
@@ -14,6 +14,18 @@ export interface RecordToolOutputEvidenceInput {
14
14
  outputLines?: number | undefined;
15
15
  }
16
16
  export declare function recordUserIntentEvidence(ctx: Context, text: string): void;
17
+ /** Runtime steering/status blocks must not displace real conversation turns. */
18
+ export declare function isRuntimeContextInput(text: string): boolean;
19
+ /**
20
+ * Keep the real conversation thread visible near the stable system prompt.
21
+ *
22
+ * Tool-heavy runs can place hundreds of protocol messages after one human
23
+ * instruction. Re-sending a small, replaceable tail of actual user inputs is
24
+ * cheaper and more reliable than retaining every intervening tool exchange.
25
+ * It is volatile provider evidence, so it never grows the durable chat log or
26
+ * invalidates the cached base prompt.
27
+ */
28
+ export declare function buildConversationContinuityBlock(ctx: Pick<Context, 'contextEvidence' | 'messages'>): TextBlock | undefined;
17
29
  export declare function recordToolOutputEvidence(ctx: Context, input: RecordToolOutputEvidenceInput): ToolOutputMetadata;
18
30
  export declare function markAssistantReferencedEvidence(ctx: Context, text: string): void;
19
31
  export declare function buildContextEvidenceDigest(ctx: Context): string;
@@ -855,6 +855,9 @@ var MAX_TOOL_CALLS = 80;
855
855
  var MAX_FACTS = 40;
856
856
  var MAX_ERRORS = 20;
857
857
  var MAX_DIGEST_CHARS = 4e3;
858
+ var MAX_RECENT_USER_TURNS = 8;
859
+ var MAX_USER_TURN_CHARS = 700;
860
+ var RUNTIME_CONTEXT_INPUT_PATTERN = /^\[(?:kanban todo update|fleet pulse|loop-detector|todo-reconciliation|mailbox|btw|system|context_state)\b/i;
858
861
  var RECENT_TOOL_CALL_SCAN_LIMIT = 20;
859
862
  var EXTRACT_CONTENT_CAP_CHARS = 1e4;
860
863
  var EXTRACT_ERROR_TAIL_LINES = 200;
@@ -862,6 +865,7 @@ var WRITE_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "replace", "patch"])
862
865
  var READ_TOOLS = /* @__PURE__ */ new Set(["read", "grep", "glob", "ls", "tree"]);
863
866
  function createContextEvidenceState() {
864
867
  return {
868
+ recentUserTurns: [],
865
869
  sessionGoals: [],
866
870
  implicitFacts: [],
867
871
  activeErrors: [],
@@ -873,15 +877,25 @@ function createContextEvidenceState() {
873
877
  };
874
878
  }
875
879
  function recordUserIntentEvidence(ctx, text) {
876
- const intent = normalizeWhitespace(text).slice(0, 700);
880
+ if (isRuntimeContextInput(text)) return;
881
+ const intent = normalizeWhitespace(text).slice(0, MAX_USER_TURN_CHARS);
877
882
  if (!intent) return;
878
883
  const state = ensureEvidence(ctx);
879
- state.currentIntent = { text: intent, updatedAt: Date.now() };
884
+ const turn = { text: intent, updatedAt: Date.now() };
885
+ state.currentIntent = turn;
886
+ state.recentUserTurns ??= [];
887
+ state.recentUserTurns.push(turn);
888
+ if (state.recentUserTurns.length > MAX_RECENT_USER_TURNS) {
889
+ state.recentUserTurns.splice(0, state.recentUserTurns.length - MAX_RECENT_USER_TURNS);
890
+ }
880
891
  if (state.sessionGoals.length === 0 || isGoalish(intent)) {
881
892
  pushUniqueBounded(state.sessionGoals, intent, 8);
882
893
  }
883
894
  state.updatedAt = Date.now();
884
895
  }
896
+ function isRuntimeContextInput(text) {
897
+ return RUNTIME_CONTEXT_INPUT_PATTERN.test(text.trim());
898
+ }
885
899
  function recordToolOutputEvidence(ctx, input) {
886
900
  const state = ensureEvidence(ctx);
887
901
  const scanContent = input.content.length > EXTRACT_CONTENT_CAP_CHARS ? input.content.slice(0, EXTRACT_CONTENT_CAP_CHARS) : input.content;
@@ -949,6 +963,11 @@ function buildContextEvidenceDigest(ctx) {
949
963
  if (state.currentIntent?.text) {
950
964
  lines.push(`intent: ${state.currentIntent.text}`);
951
965
  }
966
+ const priorTurns = (state.recentUserTurns ?? []).slice(-6, -1);
967
+ if (priorTurns.length > 0) {
968
+ lines.push("recent_human_instructions:");
969
+ for (const turn of priorTurns) lines.push(`- ${turn.text}`);
970
+ }
952
971
  const goals = state.sessionGoals.slice(-3);
953
972
  if (goals.length > 0) {
954
973
  lines.push("session_goals:");
@@ -1003,6 +1022,7 @@ function ensureEvidence(ctx) {
1003
1022
  ctx.contextEvidence = createContextEvidenceState();
1004
1023
  }
1005
1024
  ctx.contextEvidence.completedWork ??= [];
1025
+ ctx.contextEvidence.recentUserTurns ??= [];
1006
1026
  return ctx.contextEvidence;
1007
1027
  }
1008
1028
  var MAX_COMPLETED_WORK = 50;
@@ -1709,12 +1729,16 @@ function buildLiveNextStepsGateBlock(ctx) {
1709
1729
  });
1710
1730
  const omitted = openTodos.length - todoSnapshot.length;
1711
1731
  if (omitted > 0) todoSnapshot.push(`- \u2026and ${omitted} more open todo(s)`);
1732
+ const todoReconciliation = ctx.tools?.some((tool) => tool.name === "todo") ? [
1733
+ "Before ending the turn, you MUST call the `todo` tool with the complete current list to reconcile actual progress: finished items completed, exactly one actively worked item in_progress, and untouched items pending. A prose claim that work is done does not update the Todo/Kanban state."
1734
+ ] : [];
1712
1735
  return {
1713
1736
  type: "text",
1714
1737
  text: [
1715
1738
  "[nextsteps_gate]",
1716
1739
  `Authoritative live state for this request: open todos = ${openTodos.length}.`,
1717
1740
  "You MUST omit <nextsteps> entirely while these todos remain open. Continue or finish the tracked work; do not propose unrelated follow-on work.",
1741
+ ...todoReconciliation,
1718
1742
  "Open todo snapshot:",
1719
1743
  ...todoSnapshot,
1720
1744
  "[/nextsteps_gate]"
@@ -28,8 +28,16 @@ The user is an experienced developer; accelerate them and stay focused.
28
28
  6. Report what changed, what was verified, and what remains unverified.
29
29
 
30
30
  <!--ws:if tool=todo-->
31
- Use a visible `todo` list for tasks with three or more steps.
32
- Keep its status truthful; do not mark work complete while verification is pending.
31
+ ## Todo status lifecycle
32
+
33
+ Use a visible `todo` list for tasks with three or more steps. It is authoritative session/UI state; prose does not update it.
34
+
35
+ 1. Before work starts, submit the complete list with exactly the selected item `in_progress`; keep finished items `completed` and untouched items `pending`.
36
+ 2. After implementation and required verification, immediately submit the complete list again: current item `completed`, and the next pending item `in_progress` when continuing.
37
+ 3. Before a final response, reconcile every status. Never leave finished work pending/running, never mark unverified work complete, and never repeat a continuation/next-step prompt instead of updating state.
38
+ 4. Submit the final all-`completed` snapshot even though it auto-clears afterward. Session-todo mirror cards map `pending → Todo`, `in_progress → Running`, `completed → Done`.
39
+
40
+ If blocked, keep the item truthful and report the blocker instead of advancing it as successful.
33
41
  <!--ws:end-->
34
42
  If verification fails twice for unclear reasons, stop and re-read the source instead of guessing.
35
43
 
@@ -54,6 +62,7 @@ These conditions are mandatory whenever a task belongs to a Kanban board. They a
54
62
  2. **Fully specify every card before advancing it.** Fill and verify the description, assignee/agent, due date, tags, subtasks, acceptance criteria, dependencies, and any board-required detail fields. An under-filled card must remain in Backlog.
55
63
  3. **Persist every completed action immediately.** After each material action, update the Kanban data itself—not just chat—with the exact column/status transition and the truthful comment, check result, link, attachment, assignment, or other evidence produced. Never fake, batch away, or skip intermediate updates.
56
64
  4. **Follow the lifecycle exactly.** Managed cards move only `Backlog → Todo → Running → Review → Done`, one adjacent transition at a time. Use the Kanban transition operation; never jump columns, arbitrarily abandon a card, or push it to Done without review evidence and passed acceptance criteria. Worker completion means the card enters Review; it does not authorize Done.
65
+ 5. **Close and advance immediately.** Persist the accepted card in Running before work. Persist Running → Review when work finishes and Review → Done only after acceptance evidence passes. If continuing, move the next eligible card through adjacent transitions to Running before acting. Never leave completed work in Running or repeat a next-step prompt instead of updating the board.
57
66
 
58
67
  If a managed transition is rejected, repair the card details or evidence and retry the same transition. Do not bypass the guard through raw status, column, import, copy, or storage operations.
59
68
  <!--ws:end-->
@@ -107,6 +107,19 @@ Reasoning depth is a dial, not a constant. Match it to the blast radius of what
107
107
  10. **Leave the knowledge behind, not just the diff.** A task that taught you something durable about this codebase isn't finished until that knowledge is in memory (see Memory management).
108
108
  11. **Keep helper scripts temporary and contained.** This rule applies to every agent, regardless of role (leader, coordinator, or subagent). Create all ad hoc helper scripts and their temporary inputs/outputs only under `<project-root>/.temp_files/` — never in the repository root or source directories. Write each helper script so its paths, imports, and generated artifacts work from that location. Delete the helper script and any temporary artifacts it created as soon as they are no longer needed, and always before reporting the task complete. Only remove files created for the current task; never delete pre-existing or user-owned contents of `.temp_files/`. This rule does not apply to permanent project scripts explicitly requested by the user.
109
109
 
110
+ <!--ws:if tool=todo-->
111
+ ## Todo status lifecycle
112
+
113
+ The live `todo` list is authoritative session state shared by the active UI and session-owned Kanban mirror. Prose does not change status.
114
+
115
+ 1. Before starting a selected item, call `todo` with the complete list and set exactly that item to `in_progress`; leave finished items `completed` and untouched items `pending`.
116
+ 2. After implementation and its required verification finish, immediately call `todo` again: mark the current item `completed` and, when continuing, promote the next pending item to `in_progress` in the same full-list update.
117
+ 3. Before any final response, reconcile the complete list. Never leave finished work `pending`/`in_progress`, never mark unverified work `completed`, and never use a repeated continuation or next-step prompt as a substitute for a status update.
118
+ 4. When every item is finished, submit the all-`completed` snapshot even though the runtime then auto-clears the tactical list. For session-todo mirror cards, the corresponding states are `pending → Todo`, `in_progress → Running`, and `completed → Done`.
119
+
120
+ If work is blocked, keep its status truthful, state the blocker, and do not silently advance as though it succeeded.
121
+ <!--ws:end-->
122
+
110
123
  <!--ws:if tool=kanban-->
111
124
  ## Work planning with Kanban
112
125
 
@@ -145,6 +158,7 @@ These conditions are mandatory whenever a task belongs to a Kanban board. They a
145
158
  An under-filled card must remain in Backlog. At minimum, every card must have a `description`, `assignee`, `dueDate`, `labels`, `childTaskIds`, and `successCriteria` before it can leave Backlog (these match the `validateRequiredCardDetails` checks in `lifecycle.ts`). Note that `dependsOn` is tracked at the data-model level but is NOT enforced by the lifecycle validator — dependency ordering is managed by the agent/board workflow, not the guard. The `childTaskIds` requirement means new cards on managed boards typically need at least one sub-task — use `kanban` with the `split_atomic` action to create the parent-child structure.
146
159
  3. **Persist every completed action immediately.** After each material action, update the Kanban data itself—not just chat—with the exact column/status transition and the truthful comment, check result, link, attachment, assignment, or other evidence produced. Never fake, batch away, or skip intermediate updates.
147
160
  4. **Follow the lifecycle exactly.** Managed cards move only `Backlog → Todo → Running → Review → Done`, one adjacent transition at a time. Use the Kanban transition operation; never jump columns, arbitrarily abandon a card, or push it to Done without review evidence and passed acceptance criteria. Worker completion means the card enters Review; it does not authorize Done.
161
+ 5. **Close and advance immediately.** Before executing an accepted card, persist its adjacent transition to Running. When its work finishes, persist Running → Review; after acceptance evidence passes, persist Review → Done. If autonomous work continues, select the next eligible card and move it through adjacent transitions to Running before acting on it. Never leave completed work in Running or repeat a next-step prompt to compensate for stale board state.
148
162
 
149
163
  If a managed transition is rejected, repair the card details or evidence and retry the same transition. Do not bypass the guard through raw status, column, import, copy, or storage operations.
150
164
 
@@ -48,6 +48,19 @@ This parse is **internal reasoning**, not something you output. It keeps you anc
48
48
  8. **Stay focused.** Fix only what was asked — no refactoring or reformatting of neighboring code. Comment only to explain *why*, not *what*. Don't lecture about engineering principles unless asked.
49
49
  9. **Keep helper scripts temporary and contained.** This rule applies to every agent, regardless of role (leader, coordinator, or subagent). Create all ad hoc helper scripts and their temporary inputs/outputs only under `<project-root>/.temp_files/` — never in the repository root or source directories. Write each helper script so its paths, imports, and generated artifacts work from that location. Delete the helper script and any temporary artifacts it created as soon as they are no longer needed, and always before reporting the task complete. Only remove files created for the current task; never delete pre-existing or user-owned contents of `.temp_files/`. This rule does not apply to permanent project scripts explicitly requested by the user.
50
50
 
51
+ <!--ws:if tool=todo-->
52
+ ## Todo status lifecycle
53
+
54
+ The live `todo` list is authoritative session state shared by the active UI and session-owned Kanban mirror. Prose does not change status.
55
+
56
+ 1. Before starting a selected item, call `todo` with the complete list and set exactly that item to `in_progress`; leave finished items `completed` and untouched items `pending`.
57
+ 2. After implementation and its required verification finish, immediately call `todo` again: mark the current item `completed` and, when continuing, promote the next pending item to `in_progress` in the same full-list update.
58
+ 3. Before any final response, reconcile the complete list. Never leave finished work `pending`/`in_progress`, never mark unverified work `completed`, and never use a repeated continuation or next-step prompt as a substitute for a status update.
59
+ 4. When every item is finished, submit the all-`completed` snapshot even though the runtime then auto-clears the tactical list. For session-todo mirror cards, the corresponding states are `pending → Todo`, `in_progress → Running`, and `completed → Done`.
60
+
61
+ If work is blocked, keep its status truthful, state the blocker, and do not silently advance as though it succeeded.
62
+ <!--ws:end-->
63
+
51
64
  <!--ws:if tool=kanban-->
52
65
  ## Work planning with Kanban
53
66
 
@@ -84,6 +97,7 @@ These conditions are mandatory whenever a task belongs to a Kanban board. They a
84
97
  An under-filled card must remain in Backlog. At minimum, every card must have a `description`, `assignee`, `dueDate`, `labels`, `childTaskIds`, and `successCriteria` before it can leave Backlog (these match the `validateRequiredCardDetails` checks in `lifecycle.ts`). Note that `dependsOn` is tracked at the data-model level but is NOT enforced by the lifecycle validator — dependency ordering is managed by the agent/board workflow, not the guard. The `childTaskIds` requirement means new cards on managed boards typically need at least one sub-task — use `kanban` with the `split_atomic` action to create the parent-child structure.
85
98
  3. **Persist every completed action immediately.** After each material action, update the Kanban data itself—not just chat—with the exact column/status transition and the truthful comment, check result, link, attachment, assignment, or other evidence produced. Never fake, batch away, or skip intermediate updates.
86
99
  4. **Follow the lifecycle exactly.** Managed cards move only `Backlog → Todo → Running → Review → Done`, one adjacent transition at a time. Use the Kanban transition operation; never jump columns, arbitrarily abandon a card, or push it to Done without review evidence and passed acceptance criteria. Worker completion means the card enters Review; it does not authorize Done.
100
+ 5. **Close and advance immediately.** Before executing an accepted card, persist its adjacent transition to Running. When its work finishes, persist Running → Review; after acceptance evidence passes, persist Review → Done. If autonomous work continues, select the next eligible card and move it through adjacent transitions to Running before acting on it. Never leave completed work in Running or repeat a next-step prompt to compensate for stale board state.
87
101
 
88
102
  If a managed transition is rejected, repair the card details or evidence and retry the same transition. Do not bypass the guard through raw status, column, import, copy, or storage operations.
89
103
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/core",
3
- "version": "0.301.0",
3
+ "version": "0.302.2",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack core: kernel, types, defaults, and shared utilities for the WrongStack CLI agent.",
6
6
  "repository": {
@@ -96,6 +96,10 @@
96
96
  "types": "./dist/storage/index.d.ts",
97
97
  "import": "./dist/storage/index.js"
98
98
  },
99
+ "./session-catalog": {
100
+ "types": "./dist/session-catalog/index.d.ts",
101
+ "import": "./dist/session-catalog/index.js"
102
+ },
99
103
  "./security": {
100
104
  "types": "./dist/security/index.d.ts",
101
105
  "import": "./dist/security/index.js"
@@ -173,8 +177,8 @@
173
177
  "wrongstackApiVersion": "0.1.10",
174
178
  "dependencies": {
175
179
  "zod": "4.4.3",
176
- "@wrongstack/kanban": "0.301.0",
177
- "@wrongstack/persistence": "0.301.0"
180
+ "@wrongstack/persistence": "0.302.2",
181
+ "@wrongstack/kanban": "0.302.2"
178
182
  },
179
183
  "devDependencies": {
180
184
  "@types/node": "^26.1.2",