Single source of truth for Context Economics + Learning Loop. Spec language: MUST / SHOULD / MUST NOT. Test IDs map to plan Wave A/B/C. Related: PROMPT-ENGINEERING.md, TOOL-SYSTEM.md, MEMORY-SYSTEM.md, LEARNING-LOOP.md, STATE-MACHINES.md.
Formula: Agent = LLM + Harness + Learning Loop.
Hard constraint: the Context Surface keeps cold-start fixed prefix cheap while skills/experience grow unbounded.
MUST: Every LLM call's fixed prefix systemTokens + toolDefTokens MUST fit the active scenario pack budget (see §3) before history is considered.
MUST: When promptAffordTokens is known and
systemTokens + toolDefTokens + PROMPT_AFFORD_OUTPUT_RESERVE + safetyMargin > promptAffordTokens,
the runtime MUST downgrade once to the reflex pack+profile and re-pack; if still over, it MUST NOT call the provider (prompt_pack_rejected).
MUST (§Afford.S1): The same afford gate MUST run on every LLM entry that calls
prepareMessages before the provider — including handleMessage and
handleMessageStream (and task streams that share prepare). Shared helper
ensureAffordablePromptPack MUST be used so stream/non-stream cannot drift.
MUST NOT: Shrink only messageBudget while shipping a fixed prefix that already exceeds afford.
Test IDs: A-afford-downgrade, A-afford-heartbeat-fail, A-budget-contract-converse,
A-budget-contract-reflex, S-stream-afford-reject, S-stream-afford-downgrade.
MUST: Skill full bodies and deferred tool schemas MUST enter context only via discover_tools (or equivalent L1 load). Cold start exposes L0 catalog (name + short description) only.
MUST NOT: Inject full SKILL.md bodies at spawn into the system prompt.
Test IDs: A-skill-l0-only, A-tooldef-budget.
MUST: Task execution MUST NOT write new team skills. Distillation runs after completed
(including rejection→revision→approval) via the Learning Loop hook with scenario: distillation
(LEARNING-LOOP.md §2) — MUST NOT reuse Dream's memory_consolidation.
MUST NOT: Distill on failed.
Test IDs: B-hook-skip-trivial, B-hook-fire-complex, B-hook-skip-failed,
B-distill-uses-distillation-scenario.
MUST: High-impact (or impact-omitted) skill installs require human approve via HITL
(LEARNING-LOOP.md §8.3). Low-impact skill installs MAY proceed without HITL.
Optional .pending/ staging (§3) remains a helper path.
MUST NOT: Couple skill usage/success metrics to trust score.
Test IDs: B-approve-install, B-reject-feedback, B-stats-reject-feedback.
| Pack | Scenarios | ToolDef budget (tokens) | Prompt profile |
|---|---|---|---|
reflex |
heartbeat, memory_consolidation (Dream), memory_flush, distillation |
3_000 | reflex |
converse |
chat, a2a, group_chat, comment_response, requirement_action |
8_000 | converse |
execute |
task_execution |
10_000 | execute |
govern |
review, deliberation |
8_000 | govern |
MUST: selectTools / allowlists MUST resolve through the pack for the active scenario.
MUST: reflex tool set MUST be a subset of:
task_list, task_get, memory_save, memory_search, notify_user, request_user_input,
schedule_wakeup, cancel_wakeup, set_heartbeat_interval, discover_tools,
check_mailbox, file_read, agent_send_message, update_notebook,
and for managers additionally team_status.
MUST NOT: Include package_install, package_list, goal_create, goal_update, goal_status,
spawn_subagent, spawn_subagents, deliverable_create, requirement_propose,
memory_update_longterm in the default reflex allowlist (reachable only via discover_tools).
MUST: When scenario: distillation, the runtime MAY widen the allowlist with:
memory_update, memory_update_longterm, file_write, file_edit,
package_list, package_install (plus reflex core, which already includes request_user_input).
MUST NOT: Add hub_install on the distillation turn.
MUST: Skill package_install follows Learning Habits impact/HITL
(LEARNING-LOOP.md §2.2 / §8.3) — high/omitted impact asks first.
MUST: Inject Learning Habits for distillation; MUST NOT inject them for memory_consolidation.
Test IDs: A-pack-reflex-tools, B-hb-no-evolution-essay, B-distill-package-install-allowed,
B-distill-habits-injected, B-dream-no-habits.
MUST: Default converse MUST NOT include spawn_subagents or deliverable_create (discover only).
MUST: execute MUST include code/shell/coding groups (or equivalent) needed for task work.
MUST: Keyword matching and recentToolNames MAY accelerate loading but MUST NOT exceed the pack toolDefTokens budget.
Test IDs: A-pack-converse-no-spawn, A-pack-execute-has-code, A-tooldef-sticky-capped.
| Constant | Value | Purpose |
|---|---|---|
TOOL_DEF_BUDGET_REFLEX |
3_000 | Max tool schema tokens |
TOOL_DEF_BUDGET_CONVERSE |
8_000 | Max tool schema tokens |
TOOL_DEF_BUDGET_EXECUTE |
10_000 | Max tool schema tokens |
TOOL_DEF_BUDGET_GOVERN |
8_000 | Max tool schema tokens |
ROLE_PROMPT_MAX_TOKENS |
6_000 | ROLE soft size metric (warn only; never truncate) |
KNOWLEDGE_PROMPT_MAX_TOKENS |
1_500 | knowledge.md injection (execute/govern) |
KNOWLEDGE_PROMPT_MAX_TOKENS_CONVERSE |
1_200 | knowledge.md injection (converse) |
KNOWLEDGE_PROMPT_MAX_TOKENS_REFLEX |
0 | reflex: no full knowledge dump |
STATE_PROMPT_MAX_LINES_REFLEX |
5 | state.md lines in reflex |
STATE_TTL_DAYS |
7 | state.md entry expiry |
COLD_CONVERSE_FIXED_MAX |
28_000 | Acceptance: system+tools |
COLD_REFLEX_FIXED_MAX |
8_000 | Acceptance: system+tools |
SYSTEM_PROMPT_BUDGET_CONVERSE |
16_000 | Soft size metric for converse system (observe/warn; never truncate ROLE/L0) |
SYSTEM_ANNOUNCEMENTS_CHARS_CONVERSE |
400 | Team announcements body cap (converse) |
SYSTEM_NORMS_CHARS_CONVERSE |
400 | Team norms body cap (converse) |
SYSTEM_ANNOUNCEMENTS_CHARS |
2_000 | Team announcements body cap (execute/govern) |
SYSTEM_NORMS_CHARS |
2_000 | Team norms body cap (execute/govern) |
SYSTEM_WORKFLOWS_MAX_CONVERSE |
3 | Available-workflow lines in converse |
SYSTEM_DYNAMIC_CONTEXT_CHARS_CONVERSE |
800 | Caller dynamicContext blob cap (converse) |
DEFERRED_CATALOG_MAX_CHARS |
1_500 | Tier-3 rediscovery catalog hard cap |
DEEP_SLEEP_IDLE_HEARTBEATS |
3 | Consecutive idle before skip LLM |
SUBTASK_SOFT_CAP |
8 | Warn at/above this count |
Constants live in @markus/shared limits.ts.
| Section | reflex | converse | execute/govern |
|---|---|---|---|
| ROLE (full; soft warn metric) | yes | yes | yes |
| L0 + Collaboration Rules | yes (complete, concise) | yes (complete, concise) | yes (complete, concise) |
| Identity (roster) | manager + ≤3 active | capped (existing max) | capped |
| knowledge.md | no | capped (KNOWLEDGE_PROMPT_MAX_TOKENS_CONVERSE) |
capped (KNOWLEDGE_PROMPT_MAX_TOKENS) |
| state.md | ≤5 lines | short/optional | short/optional |
| Skill L0 catalog | yes | yes | yes |
| Skill full bodies | discover only | discover only | discover only |
| L3 checklists (quality/git/error recovery) | no | no (incl. comment_response) |
yes |
| Team announcements / norms | capped | capped (400 chars) | capped (2000 chars) |
| Available workflows | as needed | ≤3 short lines | full list |
| Channel history / shared deliverables | no | optional short | as needed |
| Task board detail | counts + top blocked/failed | only when non-empty | existing caps (empty stub ok) |
| Date / locale / Interaction Mode | yes | always (never trimmed) | yes |
MUST: buildSystemPrompt MUST accept promptProfile derived from scenario pack.
MUST: Size control is progressive disclosure, not truncation of always-on text:
- Always-on (never truncated): ROLE.md (persona only — not HANDBOOK.md), L0 capability
rules,
## Markus Collaboration Rules(distilled from HANDBOOK.md), Interaction Mode, date/locale. - Collaboration Rules MUST encode Conversation-first + Task-when-needed: chat may
pair-build; tasks for async/delegation/formal review; STOP only after
task_createfor that work. Managers are player-coach (ROLE may build; Position routes when useful). - Progressive at assemble: knowledge digest (demote
##→###, deprioritize stale fault/transcript sections), announcements/norms, workflows, CONTEXT.md (full content on disk / via tools). HANDBOOK.md long-form is the AGENT HANDBOOK — a single source of truth (templates/roles/HANDBOOK.md) that ships with the build and upgrades on rebuild/release; it is NOT injected and NOT copied per-agent. The## Platform Handbook (on demand)section injects the resolved absolute path (viaAgentManager.resolveHandbookPath), so agentsfile_readthe handbook directly without searching it. - Progressive at tool layer: Markus core schemas LIVE; skill/MCP schemas only after
discover_tools(boot MUST register without activate; sticky/recent MUST NOT re-activate skill/MCP). Activated skill/MCP MAY LRU-defer under toolDef budget; CORE_KEEP + HITL + discover never defer. - Heartbeat MUST skip/defer LLM turns while
human_chatis focused or queued. - Team Status SHOULD label stopped agents as
stopped(not scaryoffline); when all listed teammates are stopped, hintagent_startand that Owner chat work may continue. - Work-context-bound tools (
task_submit_review, subtasks,task_note, …) MUST appear LIVE only in entity-bound sessions:execute/govern, or converse scenarioscomment_response/requirement_action/workflow_action. MUST NOT sticky into freechat/a2a/group_chat/ reflex.task_submit_reviewresolvestask_idfrom ALS/activeTaskswhen present; otherwise the agent MUST passtask_id(no board guessing).
MUST (§Afford.S3): SYSTEM_PROMPT_BUDGET_CONVERSE is an observe/warn metric only.
MUST NOT: Runtime-truncate ROLE, L0, or Collaboration Rules to fit that metric.
Provider afford guard handles hard limits (downgrade/reject).
Test IDs: A-profile-reflex-omits, A-profile-role-full, A-collab-rules-always-on,
A-role-no-shared-append, A-knowledge-cap, A-profile-converse-no-l3,
S-converse-keeps-essentials, S-converse-keeps-date, S-tool-mcp-progressive.
MUST: Skill/MCP tool schemas (feishu_*, *__*, chrome-devtools*) MUST NOT enter
the LIVE tool list except via explicit discover_tools activation.
MUST: Markus core tools (TOOL_DEF_CORE_KEEP: shell/file/task/memory/…) +
discover_tools + HITL MUST stay LIVE for converse/execute/govern.
MUST: When estimated tool-definition tokens still exceed the pack budget, evict
optional extras / activated skill-MCP (LRU) only — never CORE_KEEP or HITL/discover.
Prefer catalog (Tier 3 / inactive namespace list) over schema dump.
Test IDs: S-tool-mcp-progressive, S-tooldef-evict-mcp-before-core, S-activated-mcp-lru.
MUST (§Afford.S2): Evicted tool rediscovery catalog MUST be injected into the volatile
[Live context] tail (never a system segment — byte-stable prefix must be preserved), as a
short name-only (or name + ≤40 chars) list, total ≤ DEFERRED_CATALOG_MAX_CHARS.
MUST NOT: Append the eviction catalog to discover_tools.description (that re-inflates
toolDefTokens and defeats the budget).
MUST: After discover_tools activates tool names, the runtime MUST rebuild the LLM
tools schema list before the next provider call (stream / chat / task / session
continuations). Core + HITL stay immune; skill/MCP activations may later LRU-defer.
MUST NOT: Reuse a stale pre-discover llmTools array across tool-loop iterations —
that makes activation a no-op and causes discover_tools death spirals.
MUST: Consecutive discover_tools calls (any args) ≥ 5 MUST be treated as a
critical loop and break the tool loop.
Test IDs: A-tooldef-budget, A-tooldef-sticky-capped, S-catalog-not-in-tooldef,
S-discover-activated-protected, S-discover-tools-spin.
MUST: On OpenRouter reservation 402 (requested up to N … can only afford M), retry with
max_tokens = min(M, max(512, M - safety)).
MUST: When lastPromptAffordTokens is known, first request MUST also clamp
max_tokens ≤ promptAfford - estimatedPrompt - margin so the client does not send a
doomed high reservation (e.g. 13156) before failing.
Test IDs: S-max-tokens-clamp-remaining.
MUST: Persistent semantic storage MUST use:
| File | Role |
|---|---|
knowledge.md |
Permanent curated knowledge |
state.md |
Time-bounded snapshots (TTL) |
NOTEBOOK.md |
Situational workspace |
| observations buffer | Raw insights; never fully injected |
MUST: On first load, migrate legacy MEMORY.md (heuristic: dated/silent/current → state; else knowledge).
Details: MEMORY-SYSTEM.md.
Test IDs: A-knowledge-cap, C-dream-state-ttl.
State machine: task completed → DistillationHook → Habits encode (memory / skill+HITL) → stats → DreamLibrarian → fanout.
Also: platform Learning Habits L0 (look-back / encode-where / skill impact) and user-initiated Remember-from-message → child evolution session (user↔agent DM only).
Details and MUST rules: LEARNING-LOOP.md §2–§9.
Test IDs (habits / remember): B-prompt-learning-habits-*, B-skill-install-*,
B-self-evolution-skill-retired, B-evolve-*, B-recall-chat-session-*, B-ui-remember-*.
- Review verdicts:
approved|approved_with_notes|rejected task_contextattached on assign- Deliverable
version+changelog - Deep sleep after idle heartbeats
- Subtask soft cap warning
Details: STATE-MACHINES.md and TOOL/MEMORY docs.
Test IDs: C-review-notes, C-task-context-inject, C-deliv-version, C-subtask-soft-cap, A-deep-sleep-skip, A-deep-sleep-wake.
| Metric | Target | Verify |
|---|---|---|
| Cold converse fixed | ≤ 28_000 | A-budget-contract-converse + live |
| Cold reflex fixed | ≤ 8_000 | A-budget-contract-reflex + live |
| Deep sleep quiet period | 0 LLM calls | A-deep-sleep-skip + live |
| Afford over fixed | 0 provider calls | A-afford-downgrade |
| Skill library growth | Does not increase fixed L0 cost beyond catalog | A-skill-l0-only |
| Evolution rates API | reuse / first-pass / distill rate | C-metrics-api |
- Spec merged (this doc + linked specs).
- Failing tests with Test IDs above.
- Minimal implementation to green.
- Verify checklist against MUST rows; live cold-start logs for Wave D.
If Spec is wrong: fix Spec → fix tests → fix code (never hack around Spec).
Automated (must be green):
-
packages/core/test/capability-packs.test.ts(A-pack-, A-tooldef-) -
packages/core/test/afford-guard.test.ts(A-afford-*) -
packages/core/test/deep-sleep.test.ts(A-deep-sleep-*) -
packages/core/test/prompt-profiles.test.ts(A-profile-*, B-hb-no-evolution-essay) -
packages/core/test/prompt-budget.contract.test.ts(A-budget-contract-*) -
packages/core/test/learning-loop.test.ts(B-hook-, B-stats-) -
packages/core/test/memory-taxonomy.test.ts(A-knowledge / C-dream-state-ttl) -
packages/core/test/skill-fanout.test.ts(C-fanout-*, C-task-context-inject) -
packages/core/test/evolution-metrics.test.ts+GET /api/evolution/metrics(C-metrics-api) -
packages/core/test/governance-runtime.test.ts(C-review-notes / C-subtask-soft-cap)
Live (operator):
- Cold chat: log
systemTokens+toolDefTokens≤ 12k - Cold heartbeat / deep sleep: ≤ 8k fixed; idle skips LLM
- Low afford:
prompt_pack_rejectedwithout OR call - Complex task complete → distillation (Habits encode; skill install via §8.3 HITL)
- Deliverable update returns
versionbump acceptTask(..., notes)storesapproved_with_notes