Repository navigation
Closed
Labels
Description
Overview
Every built-in seeded node (agent-guidance, skills, tools, core plays, bundled playbook plays) gets a fixed literal UUID and is defined in one Rust seed table per kind. Long text bodies move out of Rust string literals into plain .md files (no frontmatter). The local agent's resident system prompt is assembled from the agent-guidance seeds in table order, instead of in the order the database happens to return them.
Problem Statement
- The resident prompt is assembled in reverse.
PromptAssembler::fetch_prompt_overrides()(packages/agent/src/prompt_assembler.rs) gets the guidance nodes throughnode_ops::query_nodes, which always sortsOrderBy::CreatedDesc(packages/core/src/ops/node_ops.rs,filter.with_order_by(OrderBy::CreatedDesc)).- The five seeds are created in list order about 1 ms apart. So the live prompt starts with "Tool Call Formatting" and ends with "Core Identity", the reverse of the order the seed list defines.
- The order is also fragile:
- a timestamp tie falls back to
id ASC, and the ids are random UUIDs; - a replaced or user-created root node jumps to the front;
- the 50-node cap drops the oldest node first.
- a timestamp tie falls back to
- The golden test doesn't catch this.
packages/agent/tests/it/prompt_assembly_snapshot.rs(assemble_resident_system_prompt) iteratesseed_agent_guidance_nodes()in its defined order, so the golden pins an order production never produces. - The guidance set is open. Any root
agent-guidancenode a user creates is silently added to the system prompt. - Seeded nodes have random ids. They're matched on re-seed by
_seed.key(their title), not by id. Two devices that each seed the same built-in under different random UUIDs produce two distinct nodes. - Plays use slug ids, breaking the UUID rule. Examples:
play-core-parent-task-completion(packages/core/src/playbook/core_plays.rs), andspec-driven-plan-approval-gateand its siblings from the bundled spec-driven playbook.create_noderequires a UUID for every type except date and schema (packages/core/src/services/node_service/crud.rs, "Production nodes must use UUID format"). Mentions only resolve UUID and date ids (is_valid_node_id). - Seed content is Rust string literals. That includes
markdown_contentinseed_agent_guidance_nodes(),seed_skill_nodes()andseed_tool_nodes()(packages/agent/src/{prompt_assembler,skill_pipeline}.rs). The bundled playbook skills already use.mdfiles viainclude_str!, but with a hand-parsed frontmatter header (packages/core/src/methodology/skills.rs). The result is two seed formats.
Proposed Solution
Seed tables
- One
consttable per seed kind, holding the node's fixed literal UUID, its title and any metadata, plusbody: include_str!("…/name.md")where the node has a body. A comment names each id. Array order is the defined order. - Bodies are plain Markdown: no frontmatter and no Rust string assembly. What the file says is what the node holds.
- Shared rule text. The constants in
packages/agent/src/skill_rules.rsbecome.mdfragments. The guidance bodies, skill bodies, tool descriptions (packages/agent/src/local_agent/tools.rs) and the generatedSKILL.md(packages/cli/examples/gen_skill_md.rs) all include the same fragment files, so each rule still has one source. Fragments are resolved when the seeds are built, through a small include marker. - The bundled playbook skills (
packages/core/src/methodology/skills/*) drop their frontmatter and move into tables too, so the repo has one seed format. - Tools stay defined in Rust, because each is bound to a handler with a typed parameter schema. Their table only gains the fixed UUID, and their description text comes from the shared fragments.
Identity and reconciliation
- Seeding matches existing nodes by id.
_seedkeeps the ADR-072 durability fields:tier, the version hashes,config_modifiedandguidance_modified. _seed.keystays as the human-readable handle fornodespace skill reset <key>, so the CLI surface doesn't change, but it is no longer identity.- A deleted seed is recreated on the next launch (ADR-072's "absent → create" row). A user turns a built-in off by archiving it.
- No migration: change the seeding and reset the database (CLAUDE.md).
Agent guidance
- The assembler reads the agent-guidance seed table and fetches each node by its fixed id, in table order. It no longer queries by type.
- Only the built-in sections are used. A user's own standing instructions belong in skills, which the agent finds with
search_skills. - An archived guidance node is skipped. The emergency fallback stays for the case where no section resolves.
Plays
- Core plays and bundled playbook plays get fixed literal UUIDs in their tables.
- ADR-060 §5 orders rules across plays by play node id. Fixed UUIDs keep the order the same on every device, but the order itself changes from slug order to UUID order. Check that no play relies on running before another. If one does, raise it on this issue rather than encoding it in UUID order.
Skills link to the schemas they are about (applies_to)
- Today:
SkillNode.node_types(packages/nodespace-types/src/skill.rs) is an undeclaredstring[]of schema ids.find_skills(packages/core/src/ops/skill_ops.rs) reads it to choose which schemas'schema_metadataa matched skill carries. No seeded or bundled skill sets it, and the decoder also reads it from a flat key (a compatibility fallback). - Replace it with a relationship declared on the
skillschema:applies_to, from skill to schema node, Many/Many, optional, with reverse nameskills. Declare it the same way task'sblocksand project'stasksare declared incore_schemas.rs. It applies to every skill: built-in, playbook-installed, or authored by a user or agent. find_skillsscope:- A matched skill's schema scope is its
applies_totargets plus their subtypes, found by followingextends(subtypes inherit their base type). - Linked core schemas such as
taskare honoured too; the unlinked fallback stays custom-only. - Read the edges for all candidates in one batched query, not one query per skill.
- With no links, keep today's fallback: the type the query names, otherwise the first five custom schemas.
- A matched skill's schema scope is its
- Delete
SkillNode.node_types,with_node_types, and the flat-key fallback infrom_properties. - The bundled playbook skills create their
applies_toedges at install. For example, spec-driven's "Writing a spec" links tospec, and Linear's "Creating an issue" links toissue. - Skill guidance: the skill-authoring guidance (SKILL.md and the CLI references) tells agents to link a new skill to the schemas it is about, using the existing relationship commands.
Architecture Impact
- ADR-072 (
decisions/072-per-aspect-seed-durability-with-explicit-reset.md) says reconciliation is "keyed by a per-node_seedblock (key, …)". This issue extends it: identity moves to the fixed node id, and the per-aspect guard and explicit reset are unchanged. Companion docs change: amend ADR-072's reconciliation keying. - ADR-060 §5 and
architecture/playbook-system.md: cross-play ordering keys on play node id. Unchanged in principle; the ids become UUIDs. architecture/prompt-skill-system.mdis stale. It still calls the typepromptand describespriority,template_syntaxandsourcefields that ADR-057 says never shipped. Rewrite it to describeagent-guidanceas a closed, table-ordered set and the seed-table pattern. This is a companion docs change; nodespace-docs is committed straight tomain.- ADR-057 / ADR-030: consistent. Skill bodies remain Markdown children under the skill root.
- ADR-081 §8: the new
.mdfiles sit underpackages/, soscripts/check-pro-boundary.tsscans them. They must add no Pro markers. The Pro-only skill guidance leaves core in [Core release blocker] Strip all Pro code, edition branching and sync/ACL state from core in one pass (ADR-081 steps 0 and 2) #3412.
Acceptance Criteria
- agent-guidance, skills, tools, core plays and bundled playbook plays are each defined by one
constseed table with fixed literal UUIDs, and a test asserts every id is a valid UUID and unique across all tables - Every seeded node's id passes
is_valid_node_id; no play id is a slug - Guidance and skill bodies, and shared rule fragments, are plain
.mdfiles with no frontmatter; no seed body is a Rust string literal - Each
skill_rulesconstant is replaced by one.mdfragment, used by guidance, skills, tool descriptions andgen_skill_md; the generatedSKILL.mdis unchanged unless a rule's text intentionally changes - The bundled playbook skill files drop their frontmatter, and the frontmatter parser in
methodology/skills.rsis deleted - The
skillschema declaresapplies_to(skill → schema, Many/Many, reverseskills); a test creates the edge from a skill to a schema node and resolves both directions -
find_skillsscopesschema_metadatato a skill'sapplies_totargets plus theirextendssubtypes, including linked core schemas, read in one batched query; tests cover a linked custom type, a linked core type, a subtype of a linked type, and the unlinked fallback -
SkillNode.node_types,with_node_typesand the flat-key fallback are deleted - The bundled playbook skills create
applies_toedges at install (test per playbook) - Skill-authoring guidance teaches linking a skill to its schemas; the generated
SKILL.mdand its drift check are updated - Seeding reconciles by id: re-seeding an unchanged database creates no nodes, and a changed template replaces the node with the same id, preserving ADR-072's per-aspect guard
- A deleted seeded node is recreated on the next seed run; an archived one stays archived and is not re-activated
- The resident prompt is assembled from the guidance table in table order: a test seeds the nodes in reverse creation order and asserts the prompt's section order matches the table
- A root
agent-guidancenode not in the table is not included in the prompt (test) - An archived guidance section is omitted from the prompt (test)
-
prompt_assembly_snapshot.rsbuilds the resident prompt through the same path as production, and the golden is regenerated (UPDATE_GOLDEN=1) with the diff reviewed - ADR-060 §5 cross-play ordering checked against the new ids; any order dependency is raised on this issue
- Companion nodespace-docs changes committed: ADR-072 amendment, and
architecture/prompt-skill-system.mdrewritten -
bun run test:changedpasses;bun run quality:fixclean; Pro-boundary ratchet unchanged
Technical Specifications
Reference Files
- Guidance seeds:
packages/agent/src/prompt_assembler.rs(seed_agent_guidance_nodes,fetch_prompt_overrides,fetch_prompt_body,assemble_static) - Skill and tool seeds:
packages/agent/src/skill_pipeline.rs(seed_skill_nodes,seed_tool_nodes) - Shared rules:
packages/agent/src/skill_rules.rs,packages/agent/src/agent_guidance.rs,packages/agent/src/local_agent/tools.rs,packages/cli/examples/gen_skill_md.rs,packages/cli/tests/it/skill_md_generation.rs - Seeding and reconciliation:
packages/daemon/src/services/assembly.rs(seed_agent_nodes),packages/core/src/services/node_service/mod.rs(seed_nodes_from_templates),packages/core/src/markdown/mod.rs(NodeTemplate,prepare_nodes_from_template) - Plays:
packages/core/src/playbook/core_plays.rs,packages/core/src/methodology/{spec_driven,jira,linear,skills,install}.rs - Golden test:
packages/agent/tests/it/prompt_assembly_snapshot.rs - UUID rule:
packages/core/src/services/node_service/crud.rs(provided-id validation),is_valid_node_idinnode_service/mod.rs
Implementation Notes
- Order is the array order of the guidance table. There is no separate order field.
- Keep
NodeTemplate, or replace it with the table row types, whichever deletes more code. Don't keep both. - Model-facing text edits fail
prompt_assembly_snapshot; regenerate withUPDATE_GOLDEN=1and review the diff. - Run
bun run build:sidecars --debugandbun run build:skillin a fresh worktree before workspace-wide cargo commands.
Dependencies
- [Core release blocker] Strip all Pro code, edition branching and sync/ACL state from core in one pass (ADR-081 steps 0 and 2) #3412 (rewrites the same seeding and schema files, and removes the Pro-only skill guidance)
Non-Goals
- Typed wire shapes for skill, tool or play (separate type-architecture work)
- Play's
enabledswitch - Making core schemas reject undeclared properties such as
_seed
Activity
[-]Seed built-in nodes from fixed-UUID tables with plain-Markdown bodies, and assemble agent guidance in table order[/-][+]Seed built-ins from fixed-UUID tables with Markdown bodies, order agent guidance, and link skills to their schemas[/+]malibio commentedon Oct 2, 2026
Baseline — Frontend: 309 test files passed (5606 tests)
malibio commentedon Oct 2, 2026
ADR-060 §5 check against the fixed play ids: no play relies on running before another. Every shipped invariant rule's only action is reject, so order among them only decides which rejection message is reported. The three rules that write (the core parent-task rollup, Linear's scheduled cycle rollover, Jira's sprint completion stamp) each have a trigger no other writing rule shares. Nothing to encode in id order. PR: #3495
Seed built-ins from fixed-UUID tables with Markdown bodies, order age…
Seed built-ins from fixed-UUID tables with Markdown bodies, order age…