Repository navigation
Default new node content to "Untitled {Type}" for name-as-content core types (project, skill) #1995
Description
Overview
project and skill are Core hardcoded node types whose content field is the node's name, not body text — their NodeBehavior::validate() requires it to be non-empty. Every frontend node-creation path that could reach these types (sidebar "+New", slash commands) always creates instances with content: '', so creation always fails validation. This does not affect primitive/body-content types (text, header, code-block, task, etc.) — empty content is correct and expected for those; this issue is scoped only to types where content is the identity.
Problem Statement
project and skill are Core hardcoded types whose behaviors require non-empty content:
packages/core/src/behaviors/mod.rs:804—ProjectNodeBehavior:"content (project name)"packages/core/src/behaviors/mod.rs:1909—SkillNodeBehavior:"Skill name (content) cannot be empty"
Reproduced directly against the daemon:
$ nodespace node create --type skill --content "" --json
Error: ... InvalidArgument, message: "Validation failed: Missing required field: Skill name (content) cannot be empty"
$ nodespace node create --type project --content "" --json
Error: ... InvalidArgument, message: "Validation failed: Missing required field: content (project name)"
Both types have no working creation path as a result:
- Neither is registered in
packages/desktop-app/src/lib/plugins/core-plugins.ts, so there is no/projector/skillslash command (confirmed viagit log -S— neither was ever added; this is a gap, not a regression/removal). - The only remaining entry point is the sidebar "+New" button (
QueryNodeViewer.handleCreateInstance→createSchemaInstance()inpackages/desktop-app/src/lib/services/schema-authoring.ts:24-41), which always creates withcontent: ''and therefore always fails validation for these two types.
The same content-required pattern exists for three more Core behaviors — no working creation path is confirmed broken for these (they either aren't user-creatable yet or have their own dedicated creation UI), but any future generic creation path for them hits the identical wall unless this fix accounts for them too:
collection—packages/core/src/behaviors/mod.rs:1655("Collection name (content) cannot be empty") — has its own dedicated creation UI already, unaffected in practiceagent-guidance—packages/core/src/behaviors/mod.rs:1859("Agent-guidance title (content) cannot be empty")tool—packages/core/src/behaviors/mod.rs:2019("Tool display name (content) cannot be empty")
Explicitly not in scope: primitive/body-content types (text, header, code-block, quote-block, ordered-list, task, etc.) correctly create with empty content today — that's the expected "start typing" state, not a bug. This issue must not touch their creation defaults.
Proposed Solution
For the specific set of Core types whose NodeBehavior requires non-empty content (project, skill, and — for consistency, since they share the identical constraint — collection, agent-guidance, tool), default content to "Untitled {Display Name}" at the point(s) where the frontend currently hardcodes content: '' for them:
createSchemaInstance()(packages/desktop-app/src/lib/services/schema-authoring.ts:24-41) — the sidebar "+New" path; needs to special-case (or look up) this set of types rather than blindly defaulting every type- Any future
core-plugins.tsslash command entries added forproject/skill(out of scope to add here, see Non-Goals, but the content-default mechanism should be reusable if/when they're added)
This must be scoped narrowly — by an explicit list, a shared marker, or a lookup against NodeBehavior — so it never applies to primitive body-content types or to titleTemplate-driven custom entities (see Architecture Impact).
Architecture Impact
../nodespace-docs/components/schema-management.md:302 states: "The content field is always empty ('') — never \"Untitled\". Content is not the name." — this line is scoped specifically to the titleTemplate case (custom schema types whose display title is computed from properties via compute_title(), where content is deliberately kept empty as a hidden field, not shown to the user). It sits directly under the doc's "With titleTemplate" behavior section (lines 299-304).
This issue does not reverse that rule and must not apply its default to titleTemplate types (see Non-Goals). No other ADR or architecture doc describes a content-default convention for name-as-content Core types; this issue establishes that convention for the first time, narrowly scoped to that category. No companion nodespace-docs change is required unless the implementer judges the new convention worth documenting alongside the existing titleTemplate section.
Acceptance Criteria
- Sidebar "+New" (
createSchemaInstance) createsprojectandskillinstances successfully (previously failed withInvalidArgument) -
projectandskillinstances created via "+New" default to non-empty, type-identifying content (e.g."Untitled Project"/"Untitled Skill"— exact wording is an implementation choice) - Primitive/body-content types (
text,header,code-block,task, etc.) are unaffected — verify they still create with empty content, not "Untitled" - Custom schema types with a
titleTemplateare unaffected — theircontentremains''perschema-management.md:302 - Regression test: creating a
project/skillinstance via the sidebar "+New" flow (or the equivalent service-level call) succeeds and round-trips a non-empty content value - Regression test: creating a primitive type (e.g.
textortask) via the same code path still results in empty content - No new test failures (
bun run test:all) - Code passes
bun run quality:fix
Technical Specifications
Reference Files
packages/desktop-app/src/lib/services/schema-authoring.ts(lines 24-41) —createSchemaInstance(), the sidebar "+New" creation call; currently hardcodescontent: ''for every typepackages/desktop-app/src/lib/components/viewers/query-node-viewer.svelte(line 510-546) —handleCreateInstance(), callscreateSchemaInstance()and surfacescreateErrorfrom a failed create; useful to confirm the error disappears once the fix landspackages/core/src/behaviors/mod.rs— validation source of truth:ProjectNodeBehavior::validate(~line 804),SkillNodeBehavior::validate(~line 1909),CollectionNodeBehavior::validate(~line 1655),AgentGuidanceNodeBehavior::validate(~line 1859),ToolNodeBehavior::validate(~line 2019), vs.CustomNodeBehavior::validate(line 2199-2209, explicitly permits empty content) and primitive-type behaviors (no content check)
Implementation Notes
- A display name for the "Untitled {Type}" string can be sourced the same way
createPluginFromSchema()derives one:schema.content || humanizeSchemaId(schemaId)(packages/desktop-app/src/lib/plugins/schema-plugin-loader.ts:118). - Do not touch backend validation (
CustomNodeBehavior::validate()or anyNodeBehaviorimpl) — this is purely a frontend default-content fix. The backend behaviors correctly enforce their own rules; the bug is that the frontend never supplies a value that satisfies them for this specific subset of types. - Prefer an explicit, named list (or a small lookup table) of "name-as-content" types over inferring it from schema shape — there is no existing frontend signal (schema field,
isCoreflag, etc.) that distinguishesproject/skillfromtask/text; encoding it implicitly (e.g. "core types with no schema fields") would be fragile and likely to silently drift as new Core types are added.
Non-Goals
- Does not implement
ai-chat's "Untitled" default or its async LLM title-generation — that is Auto-generate ai-chat conversation titles via idle-gated background LLM task #1698's scope in full. - Does not change
titleTemplatecustom-entity behavior —contentstays''for those, perschema-management.md:302. - Does not change any primitive/body-content type's creation default (
text,header,code-block,task, etc.) — empty content is correct for these. - Does not add a
/projector/skillslash command — this issue only fixes the existing "+New" creation path. Adding those slash commands (if desired) is separate, additive scope.
Related Issues
- Auto-generate ai-chat conversation titles via idle-gated background LLM task #1698 (ai-chat's own "Untitled" default + title-generation, explicitly out of scope here)
- Slash command creates custom entity nodes inline #971 (established the
titleTemplate"content stays empty as sentinel" precedent this issue must not disturb)
Activity
[-]Default new node content to "Untitled" for types with no template-driven title[/-][+]Default new node content to "Untitled {Type}" for name-as-content core types (project, skill)[/+]mstomar125 commentedon Aug 10, 2026
Resolved by PR #2018 (merged, main
c4218cec).createSchemaInstancenow seeds "Untitled {Type}" for the name-as-content Core types (project, skill, collection, agent-guidance, tool), so the sidebar "+New" path succeeds for them instead of failing non-empty-content validation; body-content and titleTemplate/custom types keep empty content. Verified against the actualbehavior.type_name()keys; +2 unit tests; independent review APPROVE.