Skip to content

Rex Level System Reference

Reference document for the item level hierarchy used across n-dx. Intent: refactor from hardcoded "epic" | "feature" | "task" | "subtask" to generic depth levels (L1/L2/L3/L4) with user-configurable labels.


Current Design

Four hardcoded string literals define the item hierarchy:

epic        (L1) — root-level container, holds features
  feature   (L2) — grouping of related tasks
    task    (L3) — a single unit of work
      subtask (L4) — subdivision of a task

Defined in packages/rex/src/schema/v1.ts:

typescript
type ItemLevel = "epic" | "feature" | "task" | "subtask";

const LEVEL_HIERARCHY: Record<ItemLevel, Array<ItemLevel | null>> = {
  epic:    [null],           // root only
  feature: ["epic"],         // under epic
  task:    ["feature", "epic"], // under feature or epic
  subtask: ["task"],         // under task only
};

const CHILD_LEVEL: Record<ItemLevel, ItemLevel | null> = {
  epic: "feature", feature: "task", task: "subtask", subtask: null,
};

These are the structural rules — they'd stay the same regardless of labels.


Where Levels Are Referenced

A. Schema & Type Definitions

FileWhatNotes
rex/src/schema/v1.ts:33ItemLevel typeCanonical definition
rex/src/schema/v1.ts:286-331LEVEL_HIERARCHY, VALID_LEVELS, CHILD_LEVELStructural rules
rex/src/schema/v1.ts:342isItemLevel() type guardValidation
rex/src/schema/v1.ts:136MergedProposalRecord.proposalKind: "epic" | "feature" | "task"Proposal provenance
rex/src/schema/validate.ts:17z.enum(["epic", "feature", "task", "subtask"])Zod schema
rex/src/schema/validate.ts:20z.enum(["epic", "feature", "task"])Proposal node kinds
web/src/viewer/components/prd-tree/types.ts:16Mirror of ItemLevelBrowser bundle (intentional duplication)

B. Hierarchy Enforcement (Structural)

These use LEVEL_HIERARCHY / CHILD_LEVEL generically — they don't hardcode level names:

FileFunctionWhat it does
rex/src/core/tree.ts:58-68insertChild()Validates parent-child level via LEVEL_HIERARCHY
rex/src/core/move.ts:71-105moveItem()Validates reparenting via LEVEL_HIERARCHY
rex/src/cli/validate-input.ts:9-20validateLevel()Checks against VALID_LEVELS set
rex/src/recommend/create-from-recommendations.ts:113-161validatePlacement()Level hierarchy + special-case: item.level === "subtask"

C. Level-Specific Conditionals

Code that branches on specific level strings — these are the hardest to generalize:

FileLine(s)PatternSemantic meaning
rex/src/cli/commands/status.ts201level === "epic"Show per-epic stats
rex/src/cli/commands/remove.ts73, 110level === "epic"Grammar ("an" epic), cascade warnings
rex/src/cli/commands/validate-interactive.ts62level === "epic"Filter to root items for validation
rex/src/core/analytics.ts28, 60level === "epic", level === "task" || "subtask"Epic-level stats, work-item filtering
rex/src/core/structural.ts329level === "feature"Feature-level structural analysis
rex/src/core/epic-correlation.ts236level === "epic"Epic correlation analysis
rex/src/core/notion-map.ts603, 646level === "epic"Notion database mapping
rex/src/recommend/create-from-recommendations.ts152level === "subtask"Subtask can't be root
hench/src/cli/commands/run.ts46, 63, 139level === "epic", level === "task" || "subtask"Epic listing, work-item selection
hench/src/agent/planning/brief.ts45level === "task" || "subtask"Work-item collection for agent briefs
sourcevision/src/generators/pr-markdown-template.ts46, 97level === "epic", level === "feature"PR markdown parent chain lookup
sourcevision/src/cli/commands/prd-epic-resolver.ts109, 122level === "epic", level === "feature"Resolve epic from file changes
sourcevision/src/analyzers/branch-work-classifier.ts146-163level === "epic", level === "feature"Change significance scoring
sourcevision/src/analyzers/branch-work-collector.ts307level === "epic"Epic-level collection
sourcevision/src/analyzers/completion-reader.ts335level === "task" || "subtask"Work-item detection
web/src/server/routes-rex.ts1088, 1103, 1127, 2225level === "task" || "subtask", level === "epic"Prune operations, epic listing
web/src/viewer/components/prd-tree/prd-tree.ts392level === "task" || "subtask"Leaf node rendering
web/src/viewer/components/prd-tree/task-detail.ts837, 1213level === "task" || "subtask"Task detail panel display
web/src/viewer/components/prd-tree/compute.ts26level === "task" || "subtask"Completion stats
web/src/viewer/components/prd-tree/smart-add-input.ts98-350level === "epic" || "feature"Parent picker for smart-add

Pattern summary: Most conditionals fall into two semantic categories:

  1. "Is this a root/container item?" → checks for epic (L1)
  2. "Is this a work item?" → checks for task || subtask (L3/L4)

D. Display Mappings (Labels, Emoji, Icons)

Four separate emoji/label mappings exist — these should be consolidated:

FileLinesMapping
rex/src/cli/commands/prune.ts620-623epic→📦 feature→✨ task→📋 subtask→🔹
web/src/viewer/components/search-overlay.ts92-97epic→🏰 feature→⭐ task→✅ subtask→🔹
web/src/viewer/components/prd-tree/prune-confirmation.ts82-93Labels: Epic/Feature/Task/Subtask, Icons: ■/◆/●/○
web/src/viewer/components/prd-tree/task-detail.ts773-776LEVEL_LABELS for detail panel
rex/src/cli/commands/prune.ts94-99formatLevelSummary() — pluralizes: "2 epics, 3 tasks"

E. LLM Prompt Templates

Level names are baked into prompts sent to LLMs for PRD generation:

FileConstantContent
rex/src/analyze/reason.ts:889-891PRD_SCHEMAJSON schema describing "epic", "features", "tasks" structure
rex/src/analyze/reason.ts:928-952FEW_SHOT_EXAMPLEExample JSON with epic.title, features[].tasks[]
rex/src/analyze/reason.ts:898-904TASK_QUALITY_RULESReferences "task" in quality guidelines
rex/src/analyze/reason.ts:910-915ANTI_PATTERNSReferences "tasks", "features"
rex/src/analyze/reason.ts:1138Prompt text"Group related items into epics and features logically"

F. Proposal Structure (Hardcoded 3-tier)

The proposal system uses a fixed epic → features[] → tasks[] structure, not the generic PRDItem hierarchy:

FileWhat
rex/src/analyze/propose.ts:44-54ProposalEpic, ProposalFeature, ProposalTask interfaces
rex/src/analyze/propose.tsProposal = { epic, features[] } — no subtask tier
rex/src/cli/commands/smart-add.tsConverts proposal structure to PRDItems
rex/src/cli/commands/smart-add-duplicates.tsAccesses proposal.features, feature.tasks

G. CLI Commands & Help Text

FileWhat
rex/src/cli/commands/constants.ts:17"Add item manually (epic|feature|task|subtask)"
rex/src/cli/index.ts:153new Set(["epic", "feature", "task", "subtask"]) for CLI parsing
rex/src/cli/mcp.ts:73MCP tool schema: z.enum(["epic", "feature", "task", "subtask"])
hench/src/cli/help.ts:45--epic=<id|title> flag documentation
web/src/viewer/components/guide.ts:73"epics → features → tasks → subtasks"
web/src/viewer/components/faq.tsReferences epics, features, tasks in FAQ content

H. Cross-Package Gateway Re-exports

GatewayExports level-related symbols
hench/src/prd/rex-gateway.tsRex types (ItemLevel, etc.)
web/src/server/rex-gateway.tsLEVEL_HIERARCHY, CHILD_LEVEL, VALID_LEVELS, ItemLevel type

I. Test Files with Level Logic

These test files contain level-specific assertions that will need updating:

FileFocus
rex/tests/unit/core/move.test.tsLevel hierarchy validation
rex/tests/unit/core/structural.test.tsRoot/parent placement
rex/tests/unit/cli/validate-input.test.tsLevel string validation
rex/tests/unit/cli/commands/add.test.tsAdding items at each level
rex/tests/unit/core/next-task.test.tsTask selection
rex/tests/unit/recommend/create-from-recommendations.test.tsHierarchy validation
rex/tests/unit/recommend/conflict-detection.test.tsConflict detection
web/tests/unit/server/type-consistency.test.tsValidates VALID_LEVELS.size === 4
web/tests/unit/viewer/add-item-form.test.tsLevel button interaction
web/tests/unit/viewer/tree-utils-deletion.test.tsHierarchical deletion

Semantic Roles

Most level-specific code isn't really about "epic" vs "feature" — it's about depth-based roles. Two concepts cover nearly all conditionals:

ConceptCurrent checkMeaningGeneric equivalent
Root containerlevel === "epic"Top-level grouping, can be at rootdepth === 1 or isRootLevel(level)
Work itemlevel === "task" || level === "subtask"Leaf-level actionable itemisWorkItem(level) or isLeafLevel(level)
Grouping itemlevel === "epic" || level === "feature"Containers for work itemsisContainerLevel(level)
Deepest leaflevel === "subtask"Cannot have childrenCHILD_LEVEL[level] === null

Proposal System (Special Case)

The analyze/propose.ts system is the hardest to generalize because it uses a fixed 3-tier object shape, not the generic PRDItem tree:

typescript
interface Proposal {
  epic: { title: string };
  features: Array<{
    title: string;
    tasks: Array<{ title, description, acceptanceCriteria, ... }>;
  }>;
}

This is embedded in LLM prompts (PRD_SCHEMA, FEW_SHOT_EXAMPLE). The LLM returns JSON matching this shape, which is then converted to PRDItem objects. Changing this requires updating both the prompt and the response parser simultaneously, plus the few-shot example.


Migration Path

What stays the same

  • LEVEL_HIERARCHY rules (structural parent-child relationships)
  • CHILD_LEVEL mappings
  • 4-level max depth
  • PRDItem.level field in stored documents

What changes

  • ItemLevel type: "epic" | ..."L1" | "L2" | "L3" | "L4" (internal)
  • All display strings: lookup from config instead of hardcoded
  • All level === "epic" checks: use semantic helpers (isRootLevel(), isWorkItem())
  • LLM prompts: inject configured labels dynamically
  • Proposal structure: generate field names from config
  • Existing .rex/prd.json files: migration to remap old level strings

Configuration shape (in .n-dx.json)

jsonc
{
  "rex": {
    "levels": {
      "L1": { "label": "Epic",    "emoji": "📦" },
      "L2": { "label": "Feature", "emoji": "✨" },
      "L3": { "label": "Task",    "emoji": "📋" },
      "L4": { "label": "Subtask", "emoji": "🔹" }
    }
  }
}

Defaults match current behavior. Users can override to e.g. "Theme" / "Story" / "Task" / "Step" or "Initiative" / "Epic" / "Story" / "Task".

Released under the Elastic License 2.0.