Skip to content

feat(templates): implement Templates as Tiles system#206

Closed
Diplow wants to merge 6 commits into
mainfrom
feature-template-tiles
Closed

feat(templates): implement Templates as Tiles system#206
Diplow wants to merge 6 commits into
mainfrom
feature-template-tiles

Conversation

@Diplow
Copy link
Copy Markdown
Owner

@Diplow Diplow commented Jan 5, 2026

Complete implementation of tile-based template storage with 6 subtasks:

  1. Extend itemType to accept string values

    • Add type guards: isBuiltInItemType(), isReservedItemType(), isCustomItemType()
    • Add RESERVED_ITEM_TYPES constant
    • 33 unit tests
  2. Add templateName column

    • Migration 0015_add_template_name_column.sql
    • Update types across layers (domain → infrastructure → API)
    • 19 integration tests
  3. Implement Template Resolver Service

    • TemplateResolverService for tile-based template lookup
    • TemplateData and TemplateWithChildren types
    • TemplateNotFoundError for clear error handling
    • 25 unit tests
  4. Migrate Built-in Templates to Tile Storage

    • Seed script: drizzle/seeds/templates.seed.ts (idempotent)
    • Well-known coordinates under D1i4gEqbi01JWS2F6I7GUN8ekRiU2mjK,0:1,2,*
    • 33 unit tests
  5. Update buildPrompt to Use Tile-Based Templates

    • Tile-based template lookup with TypeScript fallback
    • {{@ChildTemplateName}} pre-processor syntax for sub-templates
    • 43 unit tests
  6. Add User Template Allowlist Enforcement

    • TemplateAllowlistService for security validation
    • Built-in templates always allowed
    • Case-insensitive matching
    • Visibility validation (public tiles require public templates)
    • 56 unit tests

Architecture improvements (Rule-of-6 compliance):

  • services/_templates/ subfolder for template services
  • services/_context/ subfolder for context builders
  • templates/_internals/ for shared utilities

Total: 209+ new tests

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Templates-as-tiles (create/reuse prompt templates), Prompt Provenance UI, per-user template allowlists, and broader support for custom item types.
  • Infrastructure

    • Store template provenance on tiles, seed built-in templates, DB migrations to add/adjust template_name and user allowlist storage, and repository for allowlists.
  • Documentation

    • Expanded guides: item type system, templates-as-tiles, prompt provenance, and rendering/priorities.
  • Tests

    • Extensive new unit & integration tests covering templates, compiler, resolver, allowlists, item-type utilities, and migrations.
  • Behavior

    • UI: composition view now available for user tiles when expanded; type handling accepts custom strings.

✏️ Tip: You can customize this high-level summary in your review settings.

Complete implementation of tile-based template storage with 6 subtasks:

1. Extend itemType to accept string values
   - Add type guards: isBuiltInItemType(), isReservedItemType(), isCustomItemType()
   - Add RESERVED_ITEM_TYPES constant
   - 33 unit tests

2. Add templateName column
   - Migration 0015_add_template_name_column.sql
   - Update types across layers (domain → infrastructure → API)
   - 19 integration tests

3. Implement Template Resolver Service
   - TemplateResolverService for tile-based template lookup
   - TemplateData and TemplateWithChildren types
   - TemplateNotFoundError for clear error handling
   - 25 unit tests

4. Migrate Built-in Templates to Tile Storage
   - Seed script: drizzle/seeds/templates.seed.ts (idempotent)
   - Well-known coordinates under D1i4gEqbi01JWS2F6I7GUN8ekRiU2mjK,0:1,2,*
   - 33 unit tests

5. Update buildPrompt to Use Tile-Based Templates
   - Tile-based template lookup with TypeScript fallback
   - {{@ChildTemplateName}} pre-processor syntax for sub-templates
   - 43 unit tests

6. Add User Template Allowlist Enforcement
   - TemplateAllowlistService for security validation
   - Built-in templates always allowed
   - Case-insensitive matching
   - Visibility validation (public tiles require public templates)
   - 56 unit tests

Architecture improvements (Rule-of-6 compliance):
- services/_templates/ subfolder for template services
- services/_context/ subfolder for context builders
- templates/_internals/ for shared utilities

Total: 209+ new tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented Jan 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
hexframe Ready Ready Preview, Comment Jan 7, 2026 2:23pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 5, 2026

📝 Walkthrough

Walkthrough

Adds "Templates as Tiles" and prompt-provenance features: DB template_name column, template seeding and migrations, per-user template allowlists, TemplateResolver and TemplateAllowlist services with Drizzle repo, a two‑phase template compiler/pool/pre‑processor, itemType extension to ItemTypeValue (built‑in/custom/reserved) and many type/export/UI adjustments.

Changes

Cohort / File(s) Summary
Docs & Specs
CLAUDE.md, docs/features/TEMPLATES_AS_TILES.md, docs/features/PROMPT_PROVENANCE_UI.md, src/lib/domains/mapping/README.md, src/lib/domains/mapping/_objects/README.md, src/lib/domains/agentic/services/README.md, src/lib/domains/agentic/templates/README.md, src/lib/domains/mapping/infrastructure/map-item/README.md
New and expanded documentation for Templates-as-Tiles, prompt provenance UI, item-type taxonomy, template services, DB schema and migration notes.
DB Migrations / Schema / Seeds / Tests
drizzle/migrations/0015_add_template_name_column.sql, drizzle/migrations/0016_fix_template_name_constraints.sql, drizzle/migrations/0017_add_user_template_allowlist.sql, drizzle/seeds/templates.seed.ts, src/server/db/schema/_tables/mapping/map-items.ts, src/server/db/schema/_tables/auth/user-template-allowlist.ts, src/server/db/schema/index.ts, src/server/db/schema/__tests__/template-name-column.integration.test.ts
Add template_name column (VARCHAR(100) + UNIQUE), create user_template_allowlist table, seed built-in templates, and integration tests.
Template Services & Repos
src/lib/domains/agentic/services/_templates/*, src/lib/domains/agentic/infrastructure/template-allowlist/*, src/lib/domains/agentic/infrastructure/index.ts
New TemplateResolverService, TemplateAllowlistService, errors/types, DrizzleTemplateAllowlistRepository and barrel exports.
Template Compiler / Pool / Pre-processor / Internals
src/lib/domains/agentic/templates/_compiler/*, _pool/*, _pre-processor/*, _internals/*, _templates/*
Two‑phase compiler (compile → Mustache render), template pool builder, variable prefixer, RenderChildren/RenderAncestors expansion, path parser/resolver, Mustache data builder, section builders and utils.
Prompt Builder Integration & Tests
src/lib/domains/agentic/templates/_prompt-builder.ts, src/lib/domains/agentic/templates/__tests__/*, src/lib/domains/agentic/templates/_templates/*
Centralize PromptData types, adapt prompt builder to ItemTypeValue semantics, add comprehensive template/compiler tests.
Item Type System & Mapping API
src/lib/domains/mapping/_objects/map-item.ts, src/lib/domains/mapping/_objects/index.ts, src/lib/domains/mapping/infrastructure/map-item/item-type-utils.ts, src/lib/domains/mapping/utils/index.ts, src/lib/domains/mapping/_objects/README.md
Introduce ItemTypeValue = MapItemType
Mapping DB / Queries / Mappers / Types
src/lib/domains/mapping/infrastructure/map-item/types.ts, mappers.ts, db.ts, queries/specialized-queries.ts, queries/write-queries.ts
Propagate templateName into DB types/mappers/queries; refactor ContextQueryConfig to nested include flags; include templateName in fetches and updates.
Mapping Services & Signature Updates
many files under src/lib/domains/mapping/*, src/app/map/*, src/server/api/routers/map/*, src/lib/domains/mapping/services/_item-services/*
Widen itemType usage: replace constrained NonUserMapItemTypeString/MapItemType usages with ItemTypeValue or string across services, schemas, routers, UI types; move reserved "user" validation to schema.
Agentic Reorg & Exports
src/lib/domains/agentic/services/_context/*, src/lib/domains/agentic/services/index.ts, agentic.factory.ts, agentic.service.ts
Move context-related services into _context subdir and update re-exports/imports; adjust tests.
Stream Event Extractors
src/lib/domains/agentic/repositories/_helpers/stream-event-extractors.ts, src/lib/domains/agentic/repositories/claude-agent-sdk.repository.ts
New shared extractors for Claude SDK stream events; repository now imports these helpers.
Session Store Factory
src/lib/domains/agentic/services/sandbox-session/session-store-factory.ts, redis-session-store.ts, index.ts
Move createSessionStore to factory; select Redis vs Memory based on env vars; redis import becomes type-only in redis store.
UI: Tile Form / Type Selector / TileWidget
src/app/map/Chat/Timeline/Widgets/TileWidget/*, _TypeSelectorField.tsx, __tests__/type-selector.test.tsx
Relax itemType typing to string/ItemTypeValue; TypeSelector queries getEffectiveAllowlist and builds dynamic options (filters reserved "user"); updated tests.
Mutation, Cache & Handlers Type Adjustments
src/app/map/Cache/*, MutationCoordinator, handlers, types/*
Replace NonUserMapItemTypeString with string/ItemTypeValue across mutation/caching APIs and optimistic update typings.
Rule Exceptions & Test Wiring
src/.ruleof6-exceptions, scripts/run-tests.sh
Add rule-of-6 exceptions and move template-name integration tests to Phase 2 execution.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant UI as Frontend (Tile UI / Prompt Builder)
    participant Agentic as Agentic Service
    participant Resolver as Template Resolver
    participant Allowlist as Template Allowlist Service
    participant Repo as DB Repository
    participant Compiler as Template Compiler

    User->>UI: request build/render (templateName, userId, itemType)
    UI->>Agentic: buildPrompt(input)
    Agentic->>Resolver: getTemplateByName(templateName)
    Resolver->>Repo: findByTemplateName(templateName)
    Repo-->>Resolver: TemplateData
    Resolver-->>Agentic: TemplateData

    Agentic->>Allowlist: validateAllowlist(userId, templateName)
    Allowlist->>Repo: getUserAllowlist(userId)
    Repo-->>Allowlist: UserAllowlist | null
    Allowlist-->>Agentic: allowed | throws

    Agentic->>Allowlist: validateVisibility(templateName, tileVisibility, templateVisibility)
    Allowlist->>Repo: getTemplateVisibility(templateName)
    Repo-->>Allowlist: Visibility
    Allowlist-->>Agentic: ok | throws

    Agentic->>Compiler: twoPhaseRender(templateTile, taskTile, fallback)
    Compiler->>Compiler: compileTemplate (phase 1)
    Compiler->>Compiler: buildMustacheData & render (phase 2)
    Compiler-->>Agentic: rendered prompt
    Agentic-->>UI: rendered prompt
    UI-->>User: display rendered prompt
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • Develop #198 — Strong overlap: item-type system changes, templateName DB propagation, and template subsystem work.
  • Develop #152 — Related: Claude stream-event extraction and repository integration changes overlapping with new extractors.
  • Develop #182 — Related changes to mapping/map-item infrastructure, queries and types that touch the same map-item and template fields.

Poem

🐰
I hop through tiles where templates now hide,
Seeds and names tucked safely inside.
Allowlist doors and provenance light,
Resolver, compiler — prompts take flight.
Hoppy hops — templates bloom bright. 🥕✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(templates): implement Templates as Tiles system' accurately and concisely summarizes the primary change—implementing a tile-based template storage system with comprehensive supporting services, type guards, migrations, and security measures.
Docstring Coverage ✅ Passed Docstring coverage is 83.51% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Fix all issues with AI Agents 🤖
In @docs/features/PROMPT_PROVENANCE_UI.md:
- Around line 1-294: The doc incorrectly lives in the shipped/features area and
implies implemented APIs; move the design doc out of the public "features"
listing into the planned/features area (or otherwise mark it as "Planned") and
update the document text to clearly state it's a design proposal (not
implemented). Remove or clarify any language that presents APIs as existing
(references to AnnotatedPrompt, PromptSection, buildAnnotatedPrompt,
PromptViewer) so it doesn't read like shipped code, and if there is a FEATURES
index or sidebar update include this doc under "Planned" rather than active
features.

In @drizzle/seeds/templates.seed.ts:
- Around line 141-168: The _createTemplate function performs two separate
inserts (into vde_base_items and vde_map_items) without a transaction, risking
orphaned vde_base_items if the second insert fails; wrap both INSERTs in a
single database transaction (begin, run first INSERT capturing
baseResult/baseItemId, then second INSERT using parentId, SYSTEM_USER_ID,
TEMPLATE_ITEM_TYPE, spec.templateName, etc.), commit on success and rollback on
any error, and rethrow the error so callers can handle failures.

In
@src/lib/domains/agentic/services/__tests__/template-allowlist.service.test.ts:
- Around line 270-280: The test claims "allow built-in templates for any
visibility" but only asserts public/public cases; update the test in
template-allowlist.service.test.ts (the it block that calls
service.validateVisibility) to also assert that built-in templates (e.g.,
template types like 'system', 'user', 'organizational', 'context') do not throw
when passed private visibility combinations (for example 'private' or other
non-public visibility strings used in your codebase) by adding
expect(...validateVisibility(..., 'private', ...)).resolves.not.toThrow() checks
alongside the existing public checks so both public and private visibility cases
are covered.

In @src/lib/domains/agentic/templates/README.md:
- Around line 115-137: The fenced code blocks showing the directory tree and
template diagram in README.md (the blocks beginning with the templates/ listing
and the "Template \"my-agent-template\" (parent)" section) lack language
specifiers; update those triple-backtick fences to use a plain text specifier
such as ```text or ```plaintext to improve rendering and syntax highlighting for
the diagram blocks referenced in the README examples (the templates/ tree block
and the Template "my-agent-template" block).
🧹 Nitpick comments (17)
src/lib/domains/agentic/services/sandbox-session/session-store-factory.ts (1)

15-30: Consider error handling for Redis instantiation.

The Redis client is instantiated without error handling. If the connection fails (e.g., invalid credentials, network issues), the error will propagate to the caller. Consider whether fail-fast is the desired behavior or if fallback to MemorySessionStore would be more resilient.

🔎 Example: Add try-catch with fallback
  if (redisUrl && redisToken) {
-   console.log('[SessionStore] Using Redis-backed session store')
-   const redis = new Redis({
-     url: redisUrl,
-     token: redisToken
-   })
-   return new RedisSessionStore(redis, defaultTtlSeconds)
+   try {
+     console.log('[SessionStore] Using Redis-backed session store')
+     const redis = new Redis({
+       url: redisUrl,
+       token: redisToken
+     })
+     return new RedisSessionStore(redis, defaultTtlSeconds)
+   } catch (error) {
+     console.error('[SessionStore] Failed to initialize Redis, falling back to memory store', error)
+   }
  }

  console.log('[SessionStore] Redis not configured, using in-memory store (sessions will not persist across cold starts)')
  return new MemorySessionStore()
drizzle/migrations/0015_add_template_name_column.sql (1)

12-13: Consider uncommenting the index for template-based queries.

The commented-out index on template_name would benefit queries filtering tiles by template (e.g., "show all tiles created from template X"). If template-based lookups are part of the feature set, the index should be created.

🔎 Considerations for index creation

When to create the index:

  • If the application queries tiles by template_name (e.g., WHERE template_name = ?)
  • If template-based reporting/analytics are planned
  • If the "created from template X" feature mentioned in comments will be exposed in the UI

When to defer:

  • If template-based queries are rare
  • If most queries are by primary key or other indexed columns
  • If table size is small enough that full scans are acceptable

Since the PR objectives mention "provenance tracking" and features like "created-from-template tracking," the index is likely needed. Consider uncommenting line 13.

src/server/db/schema/_tables/mapping/map-items.ts (1)

54-54: LGTM! Template provenance column added correctly.

The templateName column is properly configured:

  • Nullable for backward compatibility with existing items
  • Appropriate length (255) for template names
  • Aligns with the tile-based template system objectives
Optional: Consider adding an index if template-based queries are planned

If you anticipate queries that filter or group by template name (e.g., "find all items created with template X"), consider adding an index:

  refItemIdx: index("map_item_ref_item_idx").on(table.refItemId),
+ templateNameIdx: index("map_item_template_name_idx").on(table.templateName),
  uniqueCoords: uniqueIndex("map_item_unique_coords_idx").on(

This can be deferred until query patterns emerge.

src/lib/domains/mapping/infrastructure/map-item/types.ts (1)

39-39: Consider simplifying the optional nullable type.

The templateName?: string | null declaration creates three possible states (undefined, null, or string). While this may be intentional, it's typically clearer to use either:

  • templateName?: string (optional, can be undefined)
  • templateName: string | null (required but nullable)

Since line 13 uses string | null and the field should be explicitly set during creation (even if to null), consider:

-  templateName?: string | null;
+  templateName: string | null;

Alternatively, if undefined should be allowed during creation with a default of null applied at the DB layer, the current form is acceptable but should be documented.

src/lib/domains/agentic/templates/__tests__/builtin-templates.test.ts (1)

78-161: Replace specification tests with integration tests once seed implementation exists.

Many tests in this suite are tautological and don't verify actual runtime behavior:

  • Lines 80-91: Verifies a constant array equals itself
  • Lines 137-148: Checks two hardcoded values are equal
  • Lines 150-159: Compares the same constant to itself

While these serve as TDD specifications (as noted in the file header), they should be replaced with actual integration tests that verify the seed script's behavior against a real or test database once drizzle/seeds/templates.seed.ts is implemented.

Consider replacing these with tests that:

  • Actually call the seed function with a test database
  • Verify template tiles are created at the correct coordinates
  • Validate idempotency by running the seed twice and checking for duplicates
  • Confirm content synchronization when TypeScript templates change
src/lib/domains/mapping/infrastructure/map-item/README.md (1)

138-155: Consider clarifying item_type semantic types.

The documentation states item_type contains "Semantic tile type (USER, ORGANIZATIONAL, CONTEXT, SYSTEM)", but according to the PR objectives (subtask 1), itemType has been extended to accept string values beyond built-in types, including custom types.

Consider updating the description to:

-| `item_type` | varchar(50) | Semantic tile type (USER, ORGANIZATIONAL, CONTEXT, SYSTEM) |
+| `item_type` | varchar(50) | Semantic tile type (built-in: USER, ORGANIZATIONAL, CONTEXT, SYSTEM; supports custom types) |

This clarifies that the field accepts both built-in and custom type values.

src/lib/domains/agentic/templates/_pre-processor/index.ts (1)

123-146: Consider removing the unreachable default case.

Line 144's default case is unreachable since _validateTemplateExists ensures the template exists before _invokeTemplate is called. While this is defensive programming, it creates dead code.

Options:

  1. Keep it as defensive programming (current approach)
  2. Remove it and rely on TypeScript exhaustiveness checking
  3. Add a comment explaining it's intentionally defensive
src/server/db/schema/__tests__/template-name-column.integration.test.ts (1)

1-7: Consider importing Drizzle operators at the module level.

Lines 314, 342, 505, 532, and 576 use dynamic imports for eq, and, and isNull from drizzle-orm. For better performance and readability, import these at the top of the file alongside other imports.

🔎 Proposed fix
 import { describe, beforeEach, it, expect } from "vitest";
+import { eq, and, isNull } from "drizzle-orm";
 import { db } from "~/server/db";
 import { schema } from "~/server/db";

Then update the dynamic imports throughout the file:

-      const { eq } = await import("drizzle-orm");
       const [updatedItem] = await db
src/lib/domains/mapping/infrastructure/map-item/__tests__/item-type-extension.test.ts (1)

4-10: Unnecessary eslint-disable comment.

The @typescript-eslint/no-unused-vars disable is not needed since all imported items (isBuiltInItemType, isReservedItemType, isCustomItemType, RESERVED_ITEM_TYPES) are used throughout the test file.

🔎 Suggested fix
-// eslint-disable-next-line @typescript-eslint/no-unused-vars
 import {
   isBuiltInItemType,
   isReservedItemType,
   isCustomItemType,
   RESERVED_ITEM_TYPES,
 } from "~/lib/domains/mapping/infrastructure/map-item/item-type-utils";
src/lib/domains/agentic/templates/_internals/types.ts (1)

22-28: Consider unifying ancestor type with PromptDataTile.

The ancestors array type is nearly identical to PromptDataTile (minus preview and children). For consistency and potential future extensibility, consider reusing or extending PromptDataTile:

-  /** Ancestors from root to parent - content flows top-down */
-  ancestors: Array<{
-    title: string
-    content: string | undefined
-    coords: string
-    itemType?: MapItemType
-  }>
+  /** Ancestors from root to parent - content flows top-down */
+  ancestors: Array<Omit<PromptDataTile, 'preview' | 'children'>>

This reduces type duplication and makes it clearer that ancestors follow the same shape as tiles.

src/lib/domains/agentic/services/__tests__/template-allowlist.service.test.ts (1)

400-413: Performance test lacks timing assertion.

The test claims to verify large allowlists are handled "efficiently" but only checks correctness, not performance. Consider adding a timing bound or documenting that this is a correctness-under-load test.

     it('should handle user with large allowlist efficiently', async () => {
       const largeAllowlist: UserAllowlist = {
         userId: testUserId,
         allowedTemplates: Array.from({ length: 1000 }, (_, i) => `template-${i}`)
       }
       vi.mocked(mockRepository.getUserAllowlist).mockResolvedValue(largeAllowlist)

-      // Should still perform efficiently
+      // Verify correctness with large dataset (performance is O(n) with Set lookup)
       await expect(service.validateAllowlist(testUserId, 'template-999'))
         .resolves.not.toThrow()
       await expect(service.validateAllowlist(testUserId, 'template-not-in-list'))
         .rejects
         .toThrow(TemplateNotAllowedError)
     })
src/lib/domains/mapping/infrastructure/map-item/queries/specialized-queries.ts (1)

390-435: Type annotations could be more precise.

The childrenResults and grandchildrenResults arrays are typed as Array<{ map_items: unknown; base_items: unknown }>, requiring type guards and casts later. Consider defining a more precise intermediate type to improve type safety.

// Define a result type matching the select shape
type ContextQueryResult = {
  map_items: {
    id: number;
    coord_user_id: string;
    coord_group_id: number;
    path: string;
    item_type: string;
    visibility: string;
    parentId: number | null;
    refItemId: number;
    templateName: string | null;
    createdAt: Date;
    updatedAt: Date;
  };
  base_items: {
    id: number;
    title: string;
    content: string;
    preview: string | null;
    link: string | null;
    originId: string | null;
    createdAt: Date;
    updatedAt: Date;
  } | null;
};

// Then use: let childrenResults: ContextQueryResult[] = [];
src/lib/domains/agentic/templates/__tests__/prompt-builder-tile-templates.test.ts (1)

241-251: Circular reference test is a placeholder.

This test only verifies that a string equals itself, which always passes. When implementing the actual circular detection, ensure this test exercises the real detection logic.

     it('should detect circular template references and throw error', async () => {
       // GIVEN template A references B and B references A
       // A: <a>{{@TemplateB}}</a>
       // B: <b>{{@TemplateA}}</b>

       // WHEN the template is pre-processed
       // THEN it should detect the cycle and throw a clear error

-      const expectedErrorType = 'CircularTemplateError'
-      expect(expectedErrorType).toBe('CircularTemplateError')
+      // TODO: Replace with actual implementation test
+      // Example:
+      // await expect(preprocessTemplate(circularTemplateA, mockStore))
+      //   .rejects.toThrow(CircularTemplateError)
+      expect(true).toBe(true) // Placeholder until implementation
     })
src/lib/domains/agentic/services/__tests__/template-resolver.service.test.ts (1)

167-214: Test name is misleading - tests sibling count, not nesting depth.

The test is named "should handle deeply nested sub-template structures" but actually tests 6 sibling sub-templates at the same level, not nested depth. Consider renaming to clarify intent.

🔎 Suggested rename
-    it('should handle deeply nested sub-template structures', async () => {
+    it('should handle maximum structural children (6 sub-templates)', async () => {
docs/features/TEMPLATES_AS_TILES.md (2)

69-74: Add language specifier to fenced code block.

Per markdownlint, fenced code blocks should have a language specified. This appears to be a plain text hierarchical structure.

🔎 Proposed fix
-```
+```text
 Template "my-agent-template"
 ├── [1] Sub-template "HeaderSection"
 ├── [2] Sub-template "ContextBlock"
 └── [3] Sub-template "TaskSection"
 ```

185-205: Add language specifier to fenced code block.

This workflow description should have a language specifier for markdown consistency.

🔎 Proposed fix
-```
+```text
 1. User creates tile:
    - type: "template"
    ...
src/lib/domains/agentic/services/_templates/template-allowlist.service.ts (1)

45-68: Consider adding prototype chain fix for Error subclasses.

Same as TemplateNotFoundError, these error classes may have instanceof issues in transpiled code.

🔎 Proposed fix for both error classes
 export class TemplateNotAllowedError extends Error {
   constructor(
     public readonly templateName: string,
     public readonly userId: string | null,
     public readonly allowedTemplates: string[]
   ) {
     super(`Template "${templateName}" is not allowed for user "${userId}"`)
     this.name = 'TemplateNotAllowedError'
+    Object.setPrototypeOf(this, TemplateNotAllowedError.prototype)
   }
 }

 export class TemplateVisibilityError extends Error {
   constructor(
     public readonly templateName: string,
     public readonly tileVisibility: Visibility,
     public readonly templateVisibility: Visibility
   ) {
     super(`Cannot use private template "${templateName}" for public tile`)
     this.name = 'TemplateVisibilityError'
+    Object.setPrototypeOf(this, TemplateVisibilityError.prototype)
   }
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 189562e and cdb9e4c.

📒 Files selected for processing (57)
  • CLAUDE.md
  • docs/features/PROMPT_PROVENANCE_UI.md
  • docs/features/TEMPLATES_AS_TILES.md
  • drizzle/migrations/0015_add_template_name_column.sql
  • drizzle/seeds/templates.seed.ts
  • src/.ruleof6-exceptions
  • src/lib/domains/agentic/index.ts
  • src/lib/domains/agentic/repositories/_helpers/stream-event-extractors.ts
  • src/lib/domains/agentic/repositories/claude-agent-sdk.repository.ts
  • src/lib/domains/agentic/services/README.md
  • src/lib/domains/agentic/services/__tests__/agentic.service.test.ts
  • src/lib/domains/agentic/services/__tests__/canvas-context-builder.test.ts
  • src/lib/domains/agentic/services/__tests__/chat-context-builder.test.ts
  • src/lib/domains/agentic/services/__tests__/context-composition.test.ts
  • src/lib/domains/agentic/services/__tests__/context-serializer.test.ts
  • src/lib/domains/agentic/services/__tests__/template-allowlist.service.test.ts
  • src/lib/domains/agentic/services/__tests__/template-resolver.service.test.ts
  • src/lib/domains/agentic/services/_context/canvas-context-builder.service.ts
  • src/lib/domains/agentic/services/_context/chat-context-builder.service.ts
  • src/lib/domains/agentic/services/_context/context-composition.service.ts
  • src/lib/domains/agentic/services/_context/context-serializer.service.ts
  • src/lib/domains/agentic/services/_context/tokenizer.service.ts
  • src/lib/domains/agentic/services/_templates/index.ts
  • src/lib/domains/agentic/services/_templates/prompt-template.service.ts
  • src/lib/domains/agentic/services/_templates/template-allowlist.service.ts
  • src/lib/domains/agentic/services/_templates/template-resolver.service.ts
  • src/lib/domains/agentic/services/agentic.factory.ts
  • src/lib/domains/agentic/services/agentic.service.ts
  • src/lib/domains/agentic/services/index.ts
  • src/lib/domains/agentic/services/preview-generator.service.ts
  • src/lib/domains/agentic/services/sandbox-session/index.ts
  • src/lib/domains/agentic/services/sandbox-session/redis-session-store.ts
  • src/lib/domains/agentic/services/sandbox-session/session-store-factory.ts
  • src/lib/domains/agentic/templates/README.md
  • src/lib/domains/agentic/templates/__tests__/builtin-templates.test.ts
  • src/lib/domains/agentic/templates/__tests__/prompt-builder-tile-templates.test.ts
  • src/lib/domains/agentic/templates/_internals/section-builders.ts
  • src/lib/domains/agentic/templates/_internals/types.ts
  • src/lib/domains/agentic/templates/_internals/utils.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
  • src/lib/domains/agentic/templates/_templates/_folder.ts
  • src/lib/domains/agentic/templates/_templates/_generic-tile.ts
  • src/lib/domains/agentic/templates/_templates/_hexplan.ts
  • src/lib/domains/mapping/README.md
  • src/lib/domains/mapping/_objects/README.md
  • src/lib/domains/mapping/_objects/map-item.ts
  • src/lib/domains/mapping/infrastructure/map-item/README.md
  • src/lib/domains/mapping/infrastructure/map-item/__tests__/item-type-extension.test.ts
  • src/lib/domains/mapping/infrastructure/map-item/db.ts
  • src/lib/domains/mapping/infrastructure/map-item/item-type-utils.ts
  • src/lib/domains/mapping/infrastructure/map-item/mappers.ts
  • src/lib/domains/mapping/infrastructure/map-item/queries/specialized-queries.ts
  • src/lib/domains/mapping/infrastructure/map-item/queries/write-queries.ts
  • src/lib/domains/mapping/infrastructure/map-item/types.ts
  • src/server/db/schema/__tests__/template-name-column.integration.test.ts
  • src/server/db/schema/_tables/mapping/map-items.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use absolute imports with ~/ prefix instead of relative imports (./ or ../), as enforced by ESLint no-restricted-imports rule

Files:

  • src/lib/domains/mapping/infrastructure/map-item/queries/write-queries.ts
  • src/lib/domains/agentic/templates/_internals/utils.ts
  • src/lib/domains/agentic/services/agentic.factory.ts
  • src/lib/domains/mapping/_objects/map-item.ts
  • src/lib/domains/agentic/templates/__tests__/builtin-templates.test.ts
  • src/lib/domains/agentic/services/__tests__/template-allowlist.service.test.ts
  • src/server/db/schema/__tests__/template-name-column.integration.test.ts
  • src/lib/domains/agentic/templates/_templates/_generic-tile.ts
  • src/lib/domains/agentic/repositories/claude-agent-sdk.repository.ts
  • src/lib/domains/mapping/infrastructure/map-item/queries/specialized-queries.ts
  • src/lib/domains/agentic/services/__tests__/context-serializer.test.ts
  • src/lib/domains/agentic/services/__tests__/template-resolver.service.test.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
  • src/lib/domains/mapping/infrastructure/map-item/item-type-utils.ts
  • src/lib/domains/agentic/templates/_templates/_folder.ts
  • src/lib/domains/agentic/services/sandbox-session/session-store-factory.ts
  • src/lib/domains/agentic/services/_templates/index.ts
  • src/lib/domains/agentic/templates/_templates/_hexplan.ts
  • src/lib/domains/agentic/repositories/_helpers/stream-event-extractors.ts
  • src/lib/domains/agentic/services/__tests__/chat-context-builder.test.ts
  • src/lib/domains/agentic/services/_templates/template-resolver.service.ts
  • src/lib/domains/agentic/services/sandbox-session/index.ts
  • src/lib/domains/agentic/services/__tests__/agentic.service.test.ts
  • src/lib/domains/agentic/services/preview-generator.service.ts
  • src/lib/domains/agentic/templates/_internals/section-builders.ts
  • src/lib/domains/mapping/infrastructure/map-item/mappers.ts
  • src/lib/domains/agentic/services/__tests__/canvas-context-builder.test.ts
  • src/lib/domains/agentic/templates/_internals/types.ts
  • src/lib/domains/agentic/services/index.ts
  • src/lib/domains/agentic/index.ts
  • src/lib/domains/mapping/infrastructure/map-item/types.ts
  • src/lib/domains/agentic/services/__tests__/context-composition.test.ts
  • src/server/db/schema/_tables/mapping/map-items.ts
  • src/lib/domains/agentic/templates/__tests__/prompt-builder-tile-templates.test.ts
  • drizzle/seeds/templates.seed.ts
  • src/lib/domains/agentic/services/_context/context-composition.service.ts
  • src/lib/domains/agentic/services/agentic.service.ts
  • src/lib/domains/mapping/infrastructure/map-item/db.ts
  • src/lib/domains/agentic/services/sandbox-session/redis-session-store.ts
  • src/lib/domains/mapping/infrastructure/map-item/__tests__/item-type-extension.test.ts
  • src/lib/domains/agentic/services/_templates/template-allowlist.service.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
src/lib/domains/agentic/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use buildPrompt() function from src/lib/domains/agentic/services/prompt-executor.service.ts to deterministically generate XML prompts from tile coordinates

Files:

  • src/lib/domains/agentic/templates/_internals/utils.ts
  • src/lib/domains/agentic/services/agentic.factory.ts
  • src/lib/domains/agentic/templates/__tests__/builtin-templates.test.ts
  • src/lib/domains/agentic/services/__tests__/template-allowlist.service.test.ts
  • src/lib/domains/agentic/templates/_templates/_generic-tile.ts
  • src/lib/domains/agentic/repositories/claude-agent-sdk.repository.ts
  • src/lib/domains/agentic/services/__tests__/context-serializer.test.ts
  • src/lib/domains/agentic/services/__tests__/template-resolver.service.test.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
  • src/lib/domains/agentic/templates/_templates/_folder.ts
  • src/lib/domains/agentic/services/sandbox-session/session-store-factory.ts
  • src/lib/domains/agentic/services/_templates/index.ts
  • src/lib/domains/agentic/templates/_templates/_hexplan.ts
  • src/lib/domains/agentic/repositories/_helpers/stream-event-extractors.ts
  • src/lib/domains/agentic/services/__tests__/chat-context-builder.test.ts
  • src/lib/domains/agentic/services/_templates/template-resolver.service.ts
  • src/lib/domains/agentic/services/sandbox-session/index.ts
  • src/lib/domains/agentic/services/__tests__/agentic.service.test.ts
  • src/lib/domains/agentic/services/preview-generator.service.ts
  • src/lib/domains/agentic/templates/_internals/section-builders.ts
  • src/lib/domains/agentic/services/__tests__/canvas-context-builder.test.ts
  • src/lib/domains/agentic/templates/_internals/types.ts
  • src/lib/domains/agentic/services/index.ts
  • src/lib/domains/agentic/index.ts
  • src/lib/domains/agentic/services/__tests__/context-composition.test.ts
  • src/lib/domains/agentic/templates/__tests__/prompt-builder-tile-templates.test.ts
  • src/lib/domains/agentic/services/_context/context-composition.service.ts
  • src/lib/domains/agentic/services/agentic.service.ts
  • src/lib/domains/agentic/services/sandbox-session/redis-session-store.ts
  • src/lib/domains/agentic/services/_templates/template-allowlist.service.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Vitest for tests (not Jest)

Files:

  • src/lib/domains/agentic/templates/__tests__/builtin-templates.test.ts
  • src/lib/domains/agentic/services/__tests__/template-allowlist.service.test.ts
  • src/server/db/schema/__tests__/template-name-column.integration.test.ts
  • src/lib/domains/agentic/services/__tests__/context-serializer.test.ts
  • src/lib/domains/agentic/services/__tests__/template-resolver.service.test.ts
  • src/lib/domains/agentic/services/__tests__/chat-context-builder.test.ts
  • src/lib/domains/agentic/services/__tests__/agentic.service.test.ts
  • src/lib/domains/agentic/services/__tests__/canvas-context-builder.test.ts
  • src/lib/domains/agentic/services/__tests__/context-composition.test.ts
  • src/lib/domains/agentic/templates/__tests__/prompt-builder-tile-templates.test.ts
  • src/lib/domains/mapping/infrastructure/map-item/__tests__/item-type-extension.test.ts
src/server/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use tRPC for type-safe API in backend implementations

Files:

  • src/server/db/schema/__tests__/template-name-column.integration.test.ts
  • src/server/db/schema/_tables/mapping/map-items.ts
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Applies to src/lib/domains/agentic/**/*.{ts,tsx} : Use `buildPrompt()` function from `src/lib/domains/agentic/services/prompt-executor.service.ts` to deterministically generate XML prompts from tile coordinates
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Applies to src/lib/domains/agentic/**/*.{ts,tsx} : Use `buildPrompt()` function from `src/lib/domains/agentic/services/prompt-executor.service.ts` to deterministically generate XML prompts from tile coordinates

Applied to files:

  • src/lib/domains/agentic/templates/_internals/utils.ts
  • src/lib/domains/agentic/services/agentic.factory.ts
  • docs/features/PROMPT_PROVENANCE_UI.md
  • src/lib/domains/agentic/templates/__tests__/builtin-templates.test.ts
  • src/lib/domains/agentic/services/README.md
  • src/lib/domains/agentic/templates/_templates/_generic-tile.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
  • src/lib/domains/agentic/templates/_templates/_folder.ts
  • src/lib/domains/agentic/services/_templates/index.ts
  • src/lib/domains/agentic/templates/_templates/_hexplan.ts
  • src/lib/domains/agentic/services/__tests__/chat-context-builder.test.ts
  • src/lib/domains/agentic/services/_templates/template-resolver.service.ts
  • src/lib/domains/agentic/services/__tests__/agentic.service.test.ts
  • src/lib/domains/agentic/services/preview-generator.service.ts
  • src/lib/domains/agentic/templates/_internals/section-builders.ts
  • src/lib/domains/agentic/services/__tests__/canvas-context-builder.test.ts
  • src/lib/domains/agentic/templates/_internals/types.ts
  • src/lib/domains/agentic/services/index.ts
  • src/lib/domains/agentic/index.ts
  • docs/features/TEMPLATES_AS_TILES.md
  • src/lib/domains/agentic/templates/__tests__/prompt-builder-tile-templates.test.ts
  • src/lib/domains/agentic/templates/README.md
  • src/lib/domains/agentic/services/_context/context-composition.service.ts
  • src/lib/domains/agentic/services/agentic.service.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Every tile has a semantic type (USER, ORGANIZATIONAL, CONTEXT, SYSTEM) that guides agent behavior; treat unclassified legacy tiles with null itemType as undefined

Applied to files:

  • src/lib/domains/mapping/_objects/map-item.ts
  • CLAUDE.md
  • src/lib/domains/mapping/README.md
  • src/lib/domains/mapping/_objects/README.md
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Use Vitest for tests (not Jest)

Applied to files:

  • src/lib/domains/agentic/services/__tests__/context-serializer.test.ts
  • src/lib/domains/agentic/services/__tests__/chat-context-builder.test.ts
  • src/lib/domains/agentic/services/__tests__/agentic.service.test.ts
  • src/lib/domains/agentic/services/__tests__/canvas-context-builder.test.ts
  • src/lib/domains/agentic/services/__tests__/context-composition.test.ts
  • src/lib/domains/mapping/infrastructure/map-item/__tests__/item-type-extension.test.ts
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Tiles can have both subtask children (positive directions 1-6) and context children (negative directions -1 to -6) simultaneously; context children are reference materials not preloaded eagerly

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Use `updateItem` calls for agents to update hexplan tiles with status markers (🟡, ✅, 🔴)

Applied to files:

  • CLAUDE.md
  • src/lib/domains/agentic/templates/_templates/_hexplan.ts
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Hexplan tiles at direction-0 track execution state with status markers (🟡 STARTED, ✅ COMPLETED, 🔴 BLOCKED) and steps without prefixes are considered pending (📋)

Applied to files:

  • src/lib/domains/agentic/templates/_templates/_hexplan.ts
🧬 Code graph analysis (12)
src/lib/domains/agentic/templates/__tests__/builtin-templates.test.ts (4)
src/lib/domains/agentic/templates/_system-template.ts (1)
  • SYSTEM_TEMPLATE (37-58)
src/lib/domains/agentic/templates/_user-template.ts (1)
  • USER_TEMPLATE (59-91)
src/lib/domains/agentic/services/_templates/index.ts (3)
  • TemplateRepository (19-19)
  • TemplateResolverService (13-13)
  • TemplateData (17-17)
src/lib/domains/agentic/services/_templates/template-resolver.service.ts (3)
  • TemplateRepository (45-48)
  • TemplateResolverService (59-125)
  • TemplateData (13-18)
src/server/db/schema/__tests__/template-name-column.integration.test.ts (2)
src/lib/domains/mapping/services/__tests__/helpers/_test-utilities.ts (1)
  • _createUniqueTestParams (176-191)
src/server/db/schema/_tables/mapping/map-items.ts (1)
  • mapItems (38-91)
src/lib/domains/mapping/infrastructure/map-item/queries/specialized-queries.ts (4)
src/lib/domains/mapping/utils/index.ts (1)
  • Direction (10-10)
src/app/map/constants.ts (1)
  • Direction (4-4)
src/lib/domains/mapping/infrastructure/map-item/mappers.ts (1)
  • pathToString (33-35)
src/server/db/schema/_tables/mapping/map-items.ts (1)
  • mapItems (38-91)
src/lib/domains/agentic/services/__tests__/template-resolver.service.test.ts (1)
src/lib/domains/agentic/services/_templates/template-resolver.service.ts (5)
  • TemplateRepository (45-48)
  • TemplateResolverService (59-125)
  • TemplateData (13-18)
  • TemplateWithChildren (23-25)
  • TemplateNotFoundError (32-37)
src/lib/domains/agentic/templates/_pre-processor/index.ts (3)
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (2)
  • resolveItemReference (56-88)
  • TemplateContext (20-41)
src/lib/domains/agentic/templates/_pre-processor/_parser.ts (1)
  • ParsedParams (16-22)
src/lib/domains/agentic/templates/_templates/index.ts (1)
  • TemplateRegistry (15-41)
src/lib/domains/agentic/services/sandbox-session/session-store-factory.ts (1)
src/lib/domains/agentic/services/sandbox-session/redis-session-store.ts (2)
  • ISessionStore (8-13)
  • MemorySessionStore (19-37)
src/lib/domains/agentic/templates/_internals/section-builders.ts (4)
src/lib/domains/agentic/templates/_internals/types.ts (2)
  • PromptDataTile (7-14)
  • PromptData (16-42)
src/lib/domains/agentic/templates/_internals/utils.ts (2)
  • _hasContent (22-24)
  • _escapeXML (10-17)
src/lib/domains/agentic/templates/_templates/_generic-tile.ts (1)
  • GenericTile (53-95)
src/lib/domains/agentic/templates/_system-template.ts (1)
  • ANCESTOR_INTRO (73-73)
src/lib/domains/agentic/templates/_internals/types.ts (1)
src/lib/domains/agentic/templates/_prompt-builder.ts (2)
  • PromptDataTile (29-29)
  • PromptData (29-29)
src/lib/domains/agentic/templates/__tests__/prompt-builder-tile-templates.test.ts (3)
src/lib/domains/agentic/templates/_internals/types.ts (1)
  • PromptData (16-42)
src/lib/domains/agentic/templates/_system-template.ts (1)
  • SYSTEM_TEMPLATE (37-58)
src/lib/domains/agentic/templates/_user-template.ts (1)
  • USER_TEMPLATE (59-91)
drizzle/seeds/templates.seed.ts (2)
src/lib/domains/agentic/templates/_system-template.ts (1)
  • SYSTEM_TEMPLATE (37-58)
src/lib/domains/agentic/templates/_user-template.ts (1)
  • USER_TEMPLATE (59-91)
src/lib/domains/mapping/infrastructure/map-item/__tests__/item-type-extension.test.ts (1)
src/lib/domains/mapping/infrastructure/map-item/item-type-utils.ts (4)
  • RESERVED_ITEM_TYPES (21-21)
  • isBuiltInItemType (34-39)
  • isReservedItemType (47-52)
  • isCustomItemType (61-66)
src/lib/domains/agentic/services/_templates/template-allowlist.service.ts (1)
src/lib/domains/agentic/services/_templates/index.ts (7)
  • BUILT_IN_TEMPLATES (26-26)
  • Visibility (29-29)
  • UserAllowlist (30-30)
  • TemplateNotAllowedError (24-24)
  • TemplateVisibilityError (25-25)
  • TemplateAllowlistRepository (31-31)
  • TemplateAllowlistService (23-23)
🪛 markdownlint-cli2 (0.18.1)
docs/features/TEMPLATES_AS_TILES.md

69-69: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


185-185: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

src/lib/domains/agentic/templates/README.md

115-115: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


161-161: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Tests - Phase 2 (React Components)
  • GitHub Check: Build
  • GitHub Check: ESLint
  • GitHub Check: Tests - Phase 1 (Main Suite)

Comment on lines +1 to +294
# Prompt Provenance UI

## Summary

Show users exactly which tiles and templates contributed to each part of a generated prompt, enabling transparency and debuggability.

## Motivation

**Current state:** The Chat can display the generated prompt, but it's an opaque text blob. Users cannot see:
- Which tile contributed which section
- Which template rendered it
- How to modify a specific part of the output

**Problem:**
- Debugging prompts requires mental reconstruction
- Users can't learn how their tile structure affects prompts
- No easy path from "I don't like this part" to "here's where to change it"

**Hexframe principle:** Transparency. Users should understand exactly how their system produces outputs.

## Relationship to Templates as Tiles

This feature builds on [Templates as Tiles](./TEMPLATES_AS_TILES.md):

| Templates as Tiles provides | This feature uses it for |
|----------------------------|--------------------------|
| Templates have coordinates | Link prompt sections to template source |
| Sub-templates are children | Show template composition hierarchy |
| Dynamic tile types | Show which type produced each section |

## Core Design

### 1. Annotated Prompt Structure

The prompt builder returns both:
- **Raw prompt**: The text sent to the LLM (unchanged)
- **Annotated prompt**: Structured representation with provenance

```typescript
interface AnnotatedPrompt {
raw: string;
sections: PromptSection[];
}

interface PromptSection {
content: string;
source: {
tileCoords: string; // e.g., "userId,0:1,3"
tileTitle: string;
templateCoords?: string; // Template that rendered this tile
templateName?: string;
};
children?: PromptSection[]; // Nested sections (sub-templates)
}
```

### 2. Section Boundaries

Each discrete prompt section tracks its origin:

```
┌─────────────────────────────────────────────────────────────┐
│ <hexrun-intro> │
│ This prompt was generated from Hexframe tiles... │
│ │
│ Source: Template "system" → HexrunIntro section │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ <context title="API Reference" coords="user,0:1,-2"> │
│ The API uses REST conventions... │
│ </context> │
│ │
│ Source: Tile "API Reference" at user,0:1,-2 │
│ Rendered by: GenericTile primitive │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ <task> │
│ <goal>Implement authentication</goal> │
│ Add JWT-based auth to the API endpoints... │
│ </task> │
│ │
│ Source: Tile "Implement authentication" at user,0:1,3 │
│ Rendered by: Template "system" → TaskSection │
└─────────────────────────────────────────────────────────────┘
```

### 3. UI Presentation Options

#### Option A: Highlighted Blocks

Prompt displayed as text with colored/bordered blocks. Hovering shows source info.

```
┌─ Template: system ──────────────────────────────────────────┐
│ <hexrun-intro> │
│ This prompt was generated from Hexframe tiles... │
│ </hexrun-intro> │
└─────────────────────────────────────────────────────────────┘

┌─ Tile: API Reference (user,0:1,-2) ─────────────────────────┐
│ <context title="API Reference"> │
│ The API uses REST conventions... │
│ </context> │
└─────────────────────────────────────────────────────────────┘
```

**Interactions:**
- Hover: Show source tile/template info
- Click: Navigate to source tile in map
- Right-click: "Edit this tile" / "View template"

#### Option B: Side Panel

Prompt text on left, source tree on right. Selecting text highlights corresponding source.

```
┌────────────────────────────┬─────────────────────────────┐
│ <hexrun-intro> │ ▼ Prompt Structure │
│ This prompt was generated │ ├─ HexrunIntro (template) │
│ from Hexframe tiles... │ ├─ Context │
│ </hexrun-intro> │ │ └─ API Reference │
│ │ ├─ Subtasks │
│ <context title="API Ref"> │ │ ├─ Task 1 │
│ The API uses REST... │ │ └─ Task 2 │
│ </context> │ ├─ Task │
│ │ │ └─ Implement auth │
│ <subtasks> │ └─ HexPlan (template) │
│ ... │ │
└────────────────────────────┴─────────────────────────────┘
```

#### Option C: Inline Annotations (Collapsible)

Each section has a small annotation bar that expands on click.

```
▸ Template: system/hexrun-intro ─────────────────────────────
<hexrun-intro>
This prompt was generated from Hexframe tiles...
</hexrun-intro>

▸ Tile: API Reference ───────────────────────────────────────
<context title="API Reference">
The API uses REST conventions...
</context>
```

### 4. Implementation Approach

#### Phase 1: Track Provenance in Prompt Builder

Modify `buildPrompt()` to return `AnnotatedPrompt`:

```typescript
// Before
function buildPrompt(data: PromptData): string

// After
function buildPrompt(data: PromptData): AnnotatedPrompt
// or
function buildAnnotatedPrompt(data: PromptData): AnnotatedPrompt
```

Each section builder tracks its source:

```typescript
function _buildContextSection(composedChildren, ...): PromptSection[] {
return composedChildren.map(child => ({
content: GenericTile(child, ['title', 'content'], 'context'),
source: {
tileCoords: child.coords,
tileTitle: child.title,
// templateCoords if rendered by a tile-based template
}
}));
}
```

#### Phase 2: Store Provenance with Chat Messages

The annotated prompt is stored alongside messages:

```typescript
interface ChatMessage {
role: 'user' | 'assistant' | 'system';
content: string;
promptProvenance?: AnnotatedPrompt; // For system messages
}
```

#### Phase 3: Render in Chat UI

Add prompt viewer component with provenance display:

```typescript
<PromptViewer
prompt={message.promptProvenance}
onTileClick={(coords) => navigateToTile(coords)}
onTemplateClick={(coords) => navigateToTemplate(coords)}
/>
```

## Data Flow

```
Tile Hierarchy
┌─────────────────┐
│ buildPrompt() │──────────────────────────────────────────┐
│ │ │
│ For each section: │
│ - Render content │
│ - Track source tile coords │
│ - Track template coords (if applicable) │
└─────────────────┘ │
│ │
▼ ▼
┌─────────────────┐ ┌───────────────────┐
│ Raw Prompt │ │ Annotated Prompt │
│ (sent to LLM) │ │ (for UI display) │
└─────────────────┘ └───────────────────┘
┌───────────────────┐
│ Chat UI │
│ Prompt Viewer │
└───────────────────┘
```

## Rendering Primitive Provenance

For code-based primitives (`GenericTile`, `Folder`, etc.), track which primitive rendered the content:

```typescript
interface PromptSection {
source: {
tileCoords: string;
tileTitle: string;
templateCoords?: string; // If tile-based template
templateName?: string;
primitive?: string; // "GenericTile", "Folder", etc.
};
}
```

This enables:
- "This was rendered by the GenericTile primitive with fields: title, content"
- Future: link to primitive documentation

## Interactive Features

### 1. Click to Navigate
Click any section → navigate to source tile in the map view.

### 2. Edit in Place
"Edit this tile" button → opens tile editor, changes reflect in next prompt.

### 3. Template Inspection
"View template" → shows the template tile that rendered this section.

### 4. Diff View (Future)
Compare two prompt versions to see what changed after tile edits.

## Migration Considerations

### Backward Compatibility
- Existing `buildPrompt()` API continues to work (returns raw string)
- New `buildAnnotatedPrompt()` for provenance-aware callers
- Or: `buildPrompt()` returns `AnnotatedPrompt`, with `.raw` for the string

### Chat History
- Existing messages don't have provenance data
- New messages store provenance
- UI gracefully handles missing provenance (shows raw text only)

## Open Questions

1. **Granularity:** How fine-grained should sections be? Per-tile? Per XML tag? Per line?

2. **Performance:** Does tracking provenance add significant overhead? Benchmark needed.

3. **Storage:** Store full annotated prompt, or reconstruct from tile versions on demand?

4. **Nesting display:** How to show deeply nested template composition without overwhelming the UI?

## Non-Goals (For This Feature)

- Real-time prompt preview as you edit tiles (separate feature)
- Prompt diffing between versions
- Template debugging/stepping
- Prompt optimization suggestions
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check for AnnotatedPrompt and PromptSection implementations

echo "=== Searching for AnnotatedPrompt interface/type ==="
rg -n "interface AnnotatedPrompt|type AnnotatedPrompt" src/ --type ts

echo -e "\n=== Searching for PromptSection interface/type ==="
rg -n "interface PromptSection|type PromptSection" src/ --type ts

echo -e "\n=== Searching for buildAnnotatedPrompt function ==="
rg -n "function buildAnnotatedPrompt|buildAnnotatedPrompt.*=" src/ --type ts

echo -e "\n=== Searching for PromptViewer component ==="
fd "PromptViewer" src/ --type f

echo -e "\n=== Checking buildPrompt return type changes ==="
rg -n "buildPrompt.*AnnotatedPrompt" src/ --type ts

Repository: Diplow/hexframe

Length of output: 312


🏁 Script executed:

#!/bin/bash
echo "=== Repository structure ==="
ls -la
echo -e "\n=== src/ directory ==="
ls -la src/ 2>&1 | head -20
echo -e "\n=== TypeScript files in repository ==="
fd --type f -e ts -e tsx | head -20

Repository: Diplow/hexframe

Length of output: 4797


Move this design document to docs/features/planned/ to clarify it describes future work, not an implemented feature.

This comprehensive design document is not yet implemented. No traces of AnnotatedPrompt, PromptSection, buildAnnotatedPrompt(), or PromptViewer component exist in the codebase. Placing it in docs/features/ incorrectly suggests the feature is complete; it should be repositioned to a planned features directory to distinguish design proposals from shipped functionality.

🤖 Prompt for AI Agents
In @docs/features/PROMPT_PROVENANCE_UI.md around lines 1-294, The doc
incorrectly lives in the shipped/features area and implies implemented APIs;
move the design doc out of the public "features" listing into the
planned/features area (or otherwise mark it as "Planned") and update the
document text to clearly state it's a design proposal (not implemented). Remove
or clarify any language that presents APIs as existing (references to
AnnotatedPrompt, PromptSection, buildAnnotatedPrompt, PromptViewer) so it
doesn't read like shipped code, and if there is a FEATURES index or sidebar
update include this doc under "Planned" rather than active features.

Comment on lines +270 to +280
it('should allow built-in templates for any visibility', async () => {
// Built-in templates are always considered public/available
await expect(service.validateVisibility('system', 'public', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('user', 'public', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('organizational', 'public', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('context', 'public', 'public'))
.resolves.not.toThrow()
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Test does not fully verify "any visibility" claim.

The test description states "should allow built-in templates for any visibility" but only tests 'public', 'public' combinations. To fully verify the claim, also test private tiles with built-in templates.

🔎 Suggested fix
     it('should allow built-in templates for any visibility', async () => {
       // Built-in templates are always considered public/available
       await expect(service.validateVisibility('system', 'public', 'public'))
         .resolves.not.toThrow()
       await expect(service.validateVisibility('user', 'public', 'public'))
         .resolves.not.toThrow()
       await expect(service.validateVisibility('organizational', 'public', 'public'))
         .resolves.not.toThrow()
       await expect(service.validateVisibility('context', 'public', 'public'))
         .resolves.not.toThrow()
+      // Also verify built-in templates work with private tiles
+      await expect(service.validateVisibility('system', 'private', 'public'))
+        .resolves.not.toThrow()
+      await expect(service.validateVisibility('user', 'private', 'public'))
+        .resolves.not.toThrow()
     })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should allow built-in templates for any visibility', async () => {
// Built-in templates are always considered public/available
await expect(service.validateVisibility('system', 'public', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('user', 'public', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('organizational', 'public', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('context', 'public', 'public'))
.resolves.not.toThrow()
})
it('should allow built-in templates for any visibility', async () => {
// Built-in templates are always considered public/available
await expect(service.validateVisibility('system', 'public', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('user', 'public', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('organizational', 'public', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('context', 'public', 'public'))
.resolves.not.toThrow()
// Also verify built-in templates work with private tiles
await expect(service.validateVisibility('system', 'private', 'public'))
.resolves.not.toThrow()
await expect(service.validateVisibility('user', 'private', 'public'))
.resolves.not.toThrow()
})
🤖 Prompt for AI Agents
In
@src/lib/domains/agentic/services/__tests__/template-allowlist.service.test.ts
around lines 270-280, The test claims "allow built-in templates for any
visibility" but only asserts public/public cases; update the test in
template-allowlist.service.test.ts (the it block that calls
service.validateVisibility) to also assert that built-in templates (e.g.,
template types like 'system', 'user', 'organizational', 'context') do not throw
when passed private visibility combinations (for example 'private' or other
non-public visibility strings used in your codebase) by adding
expect(...validateVisibility(..., 'private', ...)).resolves.not.toThrow() checks
alongside the existing public checks so both public and private visibility cases
are covered.

Comment on lines +115 to +137
```
templates/
├── index.ts # Public API exports
├── _prompt-builder.ts # Core implementation (template lookup, rendering)
├── _system-template.ts # SYSTEM tile template and data types
├── _user-template.ts # USER tile template and data types
├── _hexrun-orchestrator-template.ts # @-mention orchestration template
├── _pre-processor/ # {{@Template}} tag expansion
│ ├── index.ts
│ ├── _parser.ts
│ └── _resolver.ts
├── _templates/ # Rendering primitive functions
│ ├── index.ts # Template registry
│ ├── _generic-tile.ts # GenericTile() renderer
│ ├── _folder.ts # Folder() renderer
│ ├── _tile-or-folder.ts # TileOrFolder() renderer
│ └── _hexplan.ts # HexPlan() renderer
├── _internals/ # Shared utilities
│ ├── types.ts # PromptData and related types
│ ├── utils.ts # XML escaping, content checks
│ └── section-builders.ts # Context/subtask/ancestor section builders
└── __tests__/ # Test files
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language specifiers to fenced code blocks.

Lines 115 and 161 have fenced code blocks without language specifiers. For text diagrams and directory trees, use ```text or ```plaintext.

🔎 Proposed fix

Line 115:

-```
+```text
 templates/
 ├── index.ts                  # Public API exports

Line 161:

-```
+```text
 Template "my-agent-template" (parent)
 ├── [1] Sub-template "HeaderSection"

Also applies to: 161-175

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

115-115: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In @src/lib/domains/agentic/templates/README.md around lines 115-137, The fenced
code blocks showing the directory tree and template diagram in README.md (the
blocks beginning with the templates/ listing and the "Template
\"my-agent-template\" (parent)" section) lack language specifiers; update those
triple-backtick fences to use a plain text specifier such as ```text or
```plaintext to improve rendering and syntax highlighting for the diagram blocks
referenced in the README examples (the templates/ tree block and the Template
"my-agent-template" block).

…e column

The templateName column was created with VARCHAR(255) and no unique
constraint, but the spec in TEMPLATES_AS_TILES.md requires:
- VARCHAR(100) length limit
- UNIQUE constraint for global template name uniqueness

Changes:
- Add migration 0016 to add UNIQUE constraint and change column to VARCHAR(100)
- Update schema definition to match (length: 100, uniqueIndex)

This fixes 4 failing integration tests that validate these constraints.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add user_template_allowlist table for per-user allowed templates
- Create DrizzleTemplateAllowlistRepository for database operations
- Add tRPC endpoints: getEffectiveAllowlist, addToAllowlist, removeFromAllowlist
- Update _TypeSelectorField to fetch allowed types from API dynamically
- Change itemType from MapItemType enum to ItemTypeValue (MapItemType | string)
- Update entire type chain: domain → infrastructure → services → API → cache
- Add type assertions for enum comparisons to fix ESLint errors
- Isolate flaky template-name-column tests in test runner
- Fix migration to reference "users" table (not "user")

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/app/map/Cache/Lifecycle/MutationCoordinator/mutation-coordinator.ts (2)

1084-1111: Hardcoded MapItemType.CONTEXT may not preserve custom item types.

_createOptimisticItem (line 1106) hardcodes itemType: MapItemType.CONTEXT regardless of what type is passed to createItem. Since the createItem method now accepts itemType: string, this should be passed through to the optimistic item.

🔎 Suggested fix
 private _createOptimisticItem(
   coordId: string,
   coords: ReturnType<typeof CoordSystem.parseId>,
   data: {
     title?: string;
     name?: string;
     description?: string;
     content?: string;
     preview?: string;
     link?: string;
+    itemType?: string;
   },
   parentId: string | null
 ): MapItemAPIContract {
   return {
     id: `temp_${Date.now()}`,
     coordinates: coordId,
     title: data.title ?? "New Item",
     content: data.content ?? "",
     preview: data.preview,
     link: data.link ?? "",
     depth: coords.path.length,
     parentId,
-    itemType: MapItemType.CONTEXT,
+    itemType: data.itemType ?? MapItemType.CONTEXT,
     ownerId: this.config.mapContext?.userId ?? "unknown",
     originId: null,
     visibility: Visibility.PRIVATE,
   };
 }

1254-1269: _reconstructApiData loses actual item type instead of preserving it from tile data.

Line 1264 hardcodes itemType: MapItemType.CONTEXT instead of using tile.data.itemType. Since TileData.data.itemType is populated during the adapt function and contains the actual item type, this causes item types to be lost during optimistic updates and rollbacks. The method already reads other fields from tile.data (title, content, preview, link, visibility), so itemType should be preserved consistently.

🔎 Suggested fix
 private _reconstructApiData(tile: TileData): MapItemAPIContract {
   return {
     id: String(tile.metadata.dbId),
     coordinates: tile.metadata.coordId,
     depth: tile.metadata.depth,
     title: tile.data.title,
     content: tile.data.content,
     preview: tile.data.preview,
     link: tile.data.link,
     parentId: null,
-    itemType: MapItemType.CONTEXT,
+    itemType: tile.data.itemType ?? MapItemType.CONTEXT,
     ownerId: tile.metadata.ownerId ?? this.config.mapContext?.userId ?? "unknown",
     originId: null,
     visibility: tile.data.visibility ?? Visibility.PRIVATE,
   };
 }
🤖 Fix all issues with AI Agents
In @drizzle/seeds/templates.seed.ts:
- Around line 239-266: The _createTemplate function performs two INSERTs (into
vde_base_items and vde_map_items) without a transaction, risking orphaned
vde_base_items if the second INSERT fails; modify _createTemplate to run both
INSERTs inside a single DB transaction (use your SQL client's transaction helper
or explicit BEGIN/COMMIT/ROLLBACK), so that on any error you ROLLBACK and
rethrow; ensure the transaction scope includes creating baseResult (INSERT INTO
vde_base_items) and the subsequent INSERT INTO vde_map_items, and handle errors
to always rollback before throwing.
- Around line 125-149: Both _ensureGrandparentExists and
_ensureTemplatesParentExists perform two separate INSERTs (see
baseResult/mapResult and the subsequent id checks) without transactional
protection, which can leave the DB in a partial state; refactor each function to
execute both INSERTs inside a single transaction (use your SQL client's
transaction API or sql.begin/transaction) so that you RETURN the map item id
only after committing and rollback on any error, and remove the separate
non-atomic insert pattern by using the transaction-scoped queries and error
handling.

In @src/lib/domains/agentic/templates/_hexrun-orchestrator-template.ts:
- Around line 109-110: Replace the string literal comparison in
shouldUseOrchestrator with the MapItemType enum: change the check from itemType
=== 'system' to itemType === MapItemType.SYSTEM, and add an import for
MapItemType from ~/lib/domains/mapping/_objects/map-item so the function uses
the enum for type safety and consistency with other templates.

In @src/server/api/routers/map/map-schemas.ts:
- Around line 57-60: itemTypeSchema currently only bans "user" and allows any
non-empty string; update itemTypeSchema to trim input, enforce a max length of
50, reject strings that become empty after trimming, restrict characters to only
alphanumeric, hyphen and underscore (e.g. /^[A-Za-z0-9_-]+$/), and keep the
case-insensitive "user" prohibition; implement this by replacing the existing
z.string().min(1).refine(...) chain with a pipeline that trims then checks
length <= 50, validates the regex, and refines against val.toLowerCase() ===
"user" (or equivalent) so all checks run in order.
🧹 Nitpick comments (12)
src/lib/domains/mapping/_objects/map-item.ts (1)

135-135: Consider extracting repeated type cast to a constant.

The pattern (MapItemType.USER as ItemTypeValue) appears twice in this file and potentially across other files. Consider extracting it to improve maintainability:

const USER_ITEM_TYPE_VALUE: ItemTypeValue = MapItemType.USER as ItemTypeValue;

Then use: if (attrs.itemType === USER_ITEM_TYPE_VALUE)

Also applies to: 178-178

src/lib/domains/mapping/types/parameters.ts (1)

19-26: Consider adding format validation and normalization for custom item types.

While the case-insensitive "user" exclusion is good, the schema lacks:

  1. Format validation - could allow leading/trailing spaces, special characters
  2. Case normalization - "MyType" and "mytype" would be treated as different types

Consider:

const ItemTypeSchema = z.string()
  .min(1)
  .trim()
  .regex(/^[a-z][a-z0-9_-]*$/i, { 
    message: "Item type must start with a letter and contain only letters, numbers, hyphens, or underscores" 
  })
  .transform(val => val.toLowerCase())
  .refine(
    (val) => val !== "user",
    { message: "The 'user' item type is reserved for system-created root tiles" }
  );

This ensures consistent matching and prevents problematic type names across the allowlist, templates, and tile creation flows.

src/server/api/routers/map/map-schemas.ts (1)

43-56: Consider documenting the reserved types more explicitly.

The comment mentions "user" is reserved but doesn't explain the full context of reserved vs. built-in vs. custom types. Based on the PR objectives mentioning RESERVED_ITEM_TYPES, consider enhancing the documentation to clarify:

  • Built-in types: "organizational", "context", "system" (from MapItemType enum)
  • Reserved types: "user" (system-controlled, cannot be set via API)
  • Custom types: Any other valid string (e.g., "template")

This would help API consumers understand the type hierarchy.

src/app/map/Chat/Timeline/Widgets/TileWidget/__tests__/type-selector.test.tsx (3)

6-28: Mock data doesn't reflect the custom type support added in this PR.

The mock returns allowedTypes: ['organizational', 'context', 'system'], but the PR introduces support for custom item types (e.g., "template"). Consider adding test cases that verify:

  1. TypeSelector handles custom types from the allowlist
  2. The UI properly displays custom types alongside built-in types
  3. Validation works correctly when selecting custom types
🔎 Example test case for custom types

Add a test that mocks a custom type in the allowlist:

it("should display custom types from allowlist", async () => {
  // Mock with custom type
  vi.mocked(api.agentic.getEffectiveAllowlist.useQuery).mockReturnValue({
    data: { allowedTypes: ['organizational', 'context', 'system', 'template'] },
    isLoading: false,
  } as any);

  const { _TypeSelectorField } = await import(
    "~/app/map/Chat/Timeline/Widgets/TileWidget/_internals/form/_TypeSelectorField"
  );

  render(
    <_TypeSelectorField
      value="template"
      onChange={vi.fn()}
    />
  );

  const select = screen.getByRole("combobox");
  await userEvent.click(select);

  expect(screen.getByText("Template")).toBeInTheDocument();
});

170-170: Remove self-evident comment.

This comment doesn't add meaningful information since the mock structure is already clear and used by previous tests without explanation.

-  // Uses the mock already defined at the top of the file
-
   it("should include TypeSelectorField in the form", async () => {

310-328: Test verifies initial state but not preservation across updates.

The test name says "should preserve itemType when prop is user type" but only checks the initial state. Consider adding a follow-up test that verifies the user type cannot be changed through state updates (if that's the intended behavior).

Example additional test:

it("should maintain user type even when setItemType is attempted", async () => {
  const { renderHook, act } = await import("@testing-library/react");
  const { useTileState } = await import(
    "~/app/map/Chat/Timeline/Widgets/TileWidget/useTileState"
  );

  const { result } = renderHook(() =>
    useTileState({
      title: "Root Tile",
      preview: "",
      content: "",
      itemType: "user",
      tileId: "root",
    })
  );

  // Attempt to change type
  act(() => {
    result.current.editing.setItemType("context");
  });

  // Verify user type is protected (or allowed to change, depending on requirements)
  expect(result.current.editing.itemType).toBe(/* expected behavior */);
});
src/lib/domains/mapping/_repositories/map-item.ts (1)

257-269: User type restrictions are already enforced at the service layer; infrastructure layer could add defensive validation.

The service layer validates before calling batchUpdateItemTypeWithStructuralDescendants:

  • Prevents setting items to USER type
  • Prevents changing USER-typed root items
  • Only calls batch for SYSTEM or CONTEXT types

Additionally, USER items are architecturally constrained to always be root items with no descendants (validated in MapItemValidation.validateParentChildRelationship), so they cannot be updated by a method that only updates descendants. However, the infrastructure layer itself (write-queries.ts) lacks defensive validation. Consider adding a check to reject USER type at the query layer as well.

src/server/db/schema/_tables/auth/user-template-allowlist.ts (1)

33-34: Remove redundant index on userId.

The uniqueIndex on userId (line 33) already provides index functionality. The additional regular index on the same column (line 34) is redundant since PostgreSQL automatically creates an index for unique constraints.

🔎 Proposed fix to remove redundant index
 }, (table) => ({
   uniqueUserAllowlist: uniqueIndex("unique_user_allowlist").on(table.userId),
-  userIdIdx: index("user_template_allowlist_user_id_idx").on(table.userId),
 }));
src/server/api/routers/agentic/agentic.ts (1)

607-608: Consider extracting repository/service instantiation to a higher scope.

Each endpoint creates new DrizzleTemplateAllowlistRepository and TemplateAllowlistService instances. For better performance and consistency, consider:

  1. Creating the repository once in a middleware (similar to mappingServiceMiddleware and agenticServiceMiddleware)
  2. Passing it through the tRPC context
  3. Reusing the same instance across all three endpoints

This reduces object creation overhead and aligns with the existing service injection pattern used elsewhere in this router.

💡 Example refactor pattern

Create a middleware similar to existing ones:

// In middleware or router setup
const templateAllowlistMiddleware = t.procedure.use(({ ctx, next }) => {
  const repository = new DrizzleTemplateAllowlistRepository(ctx.db)
  const templateAllowlistService = new TemplateAllowlistService(repository)
  
  return next({
    ctx: {
      ...ctx,
      templateAllowlistService
    }
  })
})

Then use in endpoints:

getEffectiveAllowlist: protectedProcedure
  .use(templateAllowlistMiddleware)
  .query(async ({ ctx }) => {
    const allowlist = await ctx.templateAllowlistService.getEffectiveAllowlist(
      ctx.session?.userId ?? null
    )
    return { allowedTypes: allowlist }
  })

Also applies to: 620-621, 656-657

src/lib/domains/agentic/infrastructure/template-allowlist/drizzle-allowlist-repository.ts (1)

34-52: Consider using Drizzle's conflict handling for upsert.

The current implementation calls getUserAllowlist (line 35), which adds an extra database round-trip. Drizzle ORM supports PostgreSQL's ON CONFLICT for atomic upserts:

🔎 Proposed refactor using Drizzle's onConflictDoUpdate
 async saveUserAllowlist(allowlist: UserAllowlist): Promise<void> {
-  const existing = await this.getUserAllowlist(allowlist.userId);
-
-  if (existing) {
-    await this.db
-      .update(userTemplateAllowlist)
-      .set({
-        allowedTemplates: allowlist.allowedTemplates,
-        updatedAt: new Date(),
-      })
-      .where(eq(userTemplateAllowlist.userId, allowlist.userId));
-  } else {
-    await this.db.insert(userTemplateAllowlist).values({
-      id: nanoid(),
-      userId: allowlist.userId,
-      allowedTemplates: allowlist.allowedTemplates,
-    });
-  }
+  await this.db
+    .insert(userTemplateAllowlist)
+    .values({
+      id: nanoid(),
+      userId: allowlist.userId,
+      allowedTemplates: allowlist.allowedTemplates,
+    })
+    .onConflictDoUpdate({
+      target: userTemplateAllowlist.userId,
+      set: {
+        allowedTemplates: allowlist.allowedTemplates,
+        updatedAt: new Date(),
+      },
+    });
 }

This reduces two queries to one atomic operation and handles race conditions better.

src/lib/domains/mapping/services/_item-services/_item-crud.service.ts (2)

563-592: Switch falls through silently for custom parent types.

When parentItemType is a custom type (e.g., "template"), none of the switch cases match, and the function exits without validation. This is likely correct behavior (custom parents don't constrain children), but consider adding an early return for clarity:

🔎 Optional: Add early return for custom parent types
   const parentItemType = parent.attrs.itemType;

+  // Custom parent types don't constrain child itemTypes
+  if (!isBuiltInItemType(parentItemType)) {
+    return;
+  }
+
   // Rule: ORGANIZATIONAL tiles can only be under USER or ORGANIZATIONAL
   if (newItemType === (MapItemType.ORGANIZATIONAL as ItemTypeValue)) {

100-100: Consider a helper to reduce cast verbosity.

Multiple lines use the (MapItemType.X as ItemTypeValue) pattern. A simple helper could improve readability:

🔎 Optional refactor
// At module level or in infrastructure
const asItemType = (type: MapItemType): ItemTypeValue => type;

// Usage
if (itemType === asItemType(MapItemType.USER)) { ... }

Also applies to: 195-195, 199-199, 207-207

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6efb5bc and 3fecbeb.

📒 Files selected for processing (44)
  • drizzle/migrations/0017_add_user_template_allowlist.sql
  • drizzle/seeds/templates.seed.ts
  • scripts/run-tests.sh
  • src/app/map/Cache/Handlers/NavigationHandler/_helpers/_loading/region-loader.ts
  • src/app/map/Cache/Handlers/NavigationHandler/_helpers/_validation/tile-converter.ts
  • src/app/map/Cache/Lifecycle/MutationCoordinator/_mutation-wrappers.ts
  • src/app/map/Cache/Lifecycle/MutationCoordinator/mutation-coordinator.ts
  • src/app/map/Cache/Lifecycle/_callbacks/mutation-callbacks.ts
  • src/app/map/Cache/Services/types.ts
  • src/app/map/Cache/types/handlers.ts
  • src/app/map/Cache/types/index.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/TileForm.tsx
  • src/app/map/Chat/Timeline/Widgets/TileWidget/__tests__/type-selector.test.tsx
  • src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/_handlers.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/form/_TypeSelectorField.tsx
  • src/app/map/Chat/Timeline/Widgets/TileWidget/tile-widget.tsx
  • src/app/map/Chat/Timeline/Widgets/TileWidget/useTileState.tsx
  • src/app/map/Chat/Timeline/_components/_renderers/widget-renderers.tsx
  • src/app/map/Chat/Timeline/_utils/creation-handlers.ts
  • src/app/map/Chat/Timeline/_utils/tile-handlers.ts
  • src/lib/domains/agentic/infrastructure/index.ts
  • src/lib/domains/agentic/infrastructure/template-allowlist/drizzle-allowlist-repository.ts
  • src/lib/domains/agentic/infrastructure/template-allowlist/index.ts
  • src/lib/domains/agentic/services/task-execution.service.ts
  • src/lib/domains/agentic/templates/_hexrun-orchestrator-template.ts
  • src/lib/domains/agentic/templates/_internals/types.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
  • src/lib/domains/mapping/_actions/_map-item-copy-helpers.ts
  • src/lib/domains/mapping/_actions/_map-item-creation-helpers.ts
  • src/lib/domains/mapping/_objects/index.ts
  • src/lib/domains/mapping/_objects/map-item.ts
  • src/lib/domains/mapping/_repositories/map-item.ts
  • src/lib/domains/mapping/infrastructure/index.ts
  • src/lib/domains/mapping/infrastructure/map-item/types.ts
  • src/lib/domains/mapping/services/_item-services/_item-crud.service.ts
  • src/lib/domains/mapping/types/item-attributes.ts
  • src/lib/domains/mapping/types/parameters.ts
  • src/lib/domains/mapping/utils/index.ts
  • src/server/api/routers/agentic/agentic.ts
  • src/server/api/routers/map/map-items.ts
  • src/server/api/routers/map/map-schemas.ts
  • src/server/db/schema/_tables/auth/user-template-allowlist.ts
  • src/server/db/schema/_tables/mapping/map-items.ts
  • src/server/db/schema/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/lib/domains/agentic/templates/_internals/types.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use absolute imports with ~/ prefix instead of relative imports (./ or ../), as enforced by ESLint no-restricted-imports rule

Files:

  • src/lib/domains/agentic/infrastructure/index.ts
  • src/lib/domains/agentic/services/task-execution.service.ts
  • src/lib/domains/agentic/infrastructure/template-allowlist/index.ts
  • src/lib/domains/mapping/utils/index.ts
  • src/server/api/routers/agentic/agentic.ts
  • src/lib/domains/mapping/types/item-attributes.ts
  • src/lib/domains/mapping/infrastructure/index.ts
  • src/server/db/schema/_tables/mapping/map-items.ts
  • src/lib/domains/mapping/_objects/index.ts
  • src/app/map/Cache/types/handlers.ts
  • src/server/db/schema/_tables/auth/user-template-allowlist.ts
  • src/app/map/Cache/types/index.ts
  • src/lib/domains/agentic/templates/_hexrun-orchestrator-template.ts
  • src/lib/domains/agentic/infrastructure/template-allowlist/drizzle-allowlist-repository.ts
  • src/server/db/schema/index.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/TileForm.tsx
  • src/lib/domains/mapping/infrastructure/map-item/types.ts
  • src/app/map/Chat/Timeline/_utils/tile-handlers.ts
  • src/app/map/Chat/Timeline/_components/_renderers/widget-renderers.tsx
  • src/server/api/routers/map/map-schemas.ts
  • src/lib/domains/mapping/_actions/_map-item-copy-helpers.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
  • src/app/map/Chat/Timeline/_utils/creation-handlers.ts
  • src/lib/domains/mapping/_objects/map-item.ts
  • src/app/map/Cache/Lifecycle/_callbacks/mutation-callbacks.ts
  • src/app/map/Cache/Handlers/NavigationHandler/_helpers/_validation/tile-converter.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/useTileState.tsx
  • src/lib/domains/mapping/_actions/_map-item-creation-helpers.ts
  • src/app/map/Cache/Services/types.ts
  • drizzle/seeds/templates.seed.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/tile-widget.tsx
  • src/server/api/routers/map/map-items.ts
  • src/app/map/Cache/Handlers/NavigationHandler/_helpers/_loading/region-loader.ts
  • src/app/map/Cache/Lifecycle/MutationCoordinator/_mutation-wrappers.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/form/_TypeSelectorField.tsx
  • src/app/map/Cache/Lifecycle/MutationCoordinator/mutation-coordinator.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/__tests__/type-selector.test.tsx
  • src/lib/domains/mapping/services/_item-services/_item-crud.service.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/_handlers.ts
  • src/lib/domains/mapping/types/parameters.ts
  • src/lib/domains/mapping/_repositories/map-item.ts
src/lib/domains/agentic/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use buildPrompt() function from src/lib/domains/agentic/services/prompt-executor.service.ts to deterministically generate XML prompts from tile coordinates

Files:

  • src/lib/domains/agentic/infrastructure/index.ts
  • src/lib/domains/agentic/services/task-execution.service.ts
  • src/lib/domains/agentic/infrastructure/template-allowlist/index.ts
  • src/lib/domains/agentic/templates/_hexrun-orchestrator-template.ts
  • src/lib/domains/agentic/infrastructure/template-allowlist/drizzle-allowlist-repository.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
src/server/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use tRPC for type-safe API in backend implementations

Files:

  • src/server/api/routers/agentic/agentic.ts
  • src/server/db/schema/_tables/mapping/map-items.ts
  • src/server/db/schema/_tables/auth/user-template-allowlist.ts
  • src/server/db/schema/index.ts
  • src/server/api/routers/map/map-schemas.ts
  • src/server/api/routers/map/map-items.ts
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/app/**/*.{ts,tsx}: Use Next.js 15 App Router with progressive enhancement, following Static → Progressive → Dynamic component patterns
Use localStorage caching for performance optimization in frontend code

Files:

  • src/app/map/Cache/types/handlers.ts
  • src/app/map/Cache/types/index.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/TileForm.tsx
  • src/app/map/Chat/Timeline/_utils/tile-handlers.ts
  • src/app/map/Chat/Timeline/_components/_renderers/widget-renderers.tsx
  • src/app/map/Chat/Timeline/_utils/creation-handlers.ts
  • src/app/map/Cache/Lifecycle/_callbacks/mutation-callbacks.ts
  • src/app/map/Cache/Handlers/NavigationHandler/_helpers/_validation/tile-converter.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/useTileState.tsx
  • src/app/map/Cache/Services/types.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/tile-widget.tsx
  • src/app/map/Cache/Handlers/NavigationHandler/_helpers/_loading/region-loader.ts
  • src/app/map/Cache/Lifecycle/MutationCoordinator/_mutation-wrappers.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/form/_TypeSelectorField.tsx
  • src/app/map/Cache/Lifecycle/MutationCoordinator/mutation-coordinator.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/__tests__/type-selector.test.tsx
  • src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/_handlers.ts
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Vitest for tests (not Jest)

Files:

  • src/app/map/Chat/Timeline/Widgets/TileWidget/__tests__/type-selector.test.tsx
🧠 Learnings (7)
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Every tile has a semantic type (USER, ORGANIZATIONAL, CONTEXT, SYSTEM) that guides agent behavior; treat unclassified legacy tiles with null itemType as undefined

Applied to files:

  • src/lib/domains/agentic/services/task-execution.service.ts
  • src/lib/domains/mapping/types/item-attributes.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/TileForm.tsx
  • src/app/map/Chat/Timeline/_utils/tile-handlers.ts
  • src/server/api/routers/map/map-schemas.ts
  • src/lib/domains/mapping/_objects/map-item.ts
  • src/app/map/Cache/Handlers/NavigationHandler/_helpers/_validation/tile-converter.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/useTileState.tsx
  • src/app/map/Chat/Timeline/Widgets/TileWidget/tile-widget.tsx
  • src/lib/domains/mapping/services/_item-services/_item-crud.service.ts
  • src/lib/domains/mapping/types/parameters.ts
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Applies to src/lib/domains/agentic/**/*.{ts,tsx} : Use `buildPrompt()` function from `src/lib/domains/agentic/services/prompt-executor.service.ts` to deterministically generate XML prompts from tile coordinates

Applied to files:

  • src/server/api/routers/agentic/agentic.ts
  • src/lib/domains/agentic/templates/_hexrun-orchestrator-template.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Applies to **/*.{test,spec}.{js,jsx,ts,tsx} : Use Vitest for tests (not Jest)

Applied to files:

  • scripts/run-tests.sh
  • src/app/map/Chat/Timeline/Widgets/TileWidget/__tests__/type-selector.test.tsx
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Applies to drizzle/migrations/**/*.ts : Use Drizzle ORM with PostgreSQL and store migrations in `/drizzle/migrations/`

Applied to files:

  • src/server/db/schema/_tables/mapping/map-items.ts
  • src/server/db/schema/_tables/auth/user-template-allowlist.ts
  • src/lib/domains/agentic/infrastructure/template-allowlist/drizzle-allowlist-repository.ts
📚 Learning: 2025-09-16T20:38:01.707Z
Learnt from: Diplow
Repo: Diplow/hexframe PR: 123
File: src/app/map/Chat/Timeline/Widgets/DebugLogsWidget.tsx:20-33
Timestamp: 2025-09-16T20:38:01.707Z
Learning: DebugLogsWidget in src/app/map/Chat/Timeline/Widgets/DebugLogsWidget.tsx must not use debugLogger for its own logging operations to avoid infinite loops, since render operations are logged by the debug system.

Applied to files:

  • src/app/map/Chat/Timeline/_components/_renderers/widget-renderers.tsx
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Use `updateItem` calls for agents to update hexplan tiles with status markers (🟡, ✅, 🔴)

Applied to files:

  • src/app/map/Chat/Timeline/_components/_renderers/widget-renderers.tsx
  • src/lib/domains/mapping/services/_item-services/_item-crud.service.ts
  • src/lib/domains/mapping/_repositories/map-item.ts
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Applies to src/server/**/*.{ts,tsx} : Use tRPC for type-safe API in backend implementations

Applied to files:

  • src/server/api/routers/map/map-items.ts
  • src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/form/_TypeSelectorField.tsx
🧬 Code graph analysis (18)
src/lib/domains/agentic/services/task-execution.service.ts (3)
src/lib/domains/mapping/_objects/index.ts (1)
  • ItemTypeValue (9-9)
src/lib/domains/mapping/_objects/map-item.ts (1)
  • ItemTypeValue (69-69)
src/lib/domains/mapping/utils/index.ts (1)
  • ItemTypeValue (32-32)
src/server/api/routers/agentic/agentic.ts (4)
src/server/api/trpc.ts (2)
  • protectedProcedure (351-366)
  • t (172-184)
src/lib/domains/agentic/infrastructure/index.ts (1)
  • DrizzleTemplateAllowlistRepository (14-14)
src/lib/domains/agentic/infrastructure/template-allowlist/drizzle-allowlist-repository.ts (1)
  • DrizzleTemplateAllowlistRepository (16-59)
src/server/db/index.ts (1)
  • db (78-78)
src/app/map/Cache/types/handlers.ts (2)
src/lib/domains/mapping/types/item-attributes.ts (1)
  • MapItemCreateAttributes (45-51)
src/app/map/Cache/Lifecycle/MutationCoordinator/mutation-coordinator.ts (1)
  • MutationResult (78-81)
src/server/db/schema/_tables/auth/user-template-allowlist.ts (1)
src/server/db/schema/_tables/auth/users.ts (1)
  • users (19-31)
src/lib/domains/agentic/templates/_hexrun-orchestrator-template.ts (5)
src/lib/domains/mapping/_objects/index.ts (1)
  • ItemTypeValue (9-9)
src/lib/domains/mapping/_objects/map-item.ts (1)
  • ItemTypeValue (69-69)
src/lib/domains/mapping/utils/index.ts (1)
  • ItemTypeValue (32-32)
src/lib/domains/agentic/templates/index.ts (1)
  • shouldUseOrchestrator (15-15)
src/lib/domains/agentic/templates/_internals/utils.ts (1)
  • _hasContent (22-24)
src/lib/domains/agentic/infrastructure/template-allowlist/drizzle-allowlist-repository.ts (6)
src/server/db/index.ts (2)
  • schema (81-81)
  • db (78-78)
src/lib/domains/agentic/infrastructure/index.ts (1)
  • DrizzleTemplateAllowlistRepository (14-14)
src/lib/domains/agentic/infrastructure/template-allowlist/index.ts (1)
  • DrizzleTemplateAllowlistRepository (1-1)
src/server/db/schema/_tables/auth/user-template-allowlist.ts (1)
  • userTemplateAllowlist (20-35)
src/lib/domains/mapping/_objects/index.ts (1)
  • Visibility (10-10)
src/lib/domains/mapping/utils/index.ts (1)
  • Visibility (33-33)
src/lib/domains/mapping/infrastructure/map-item/types.ts (5)
src/lib/domains/mapping/_objects/index.ts (2)
  • ItemTypeValue (9-9)
  • Visibility (10-10)
src/lib/domains/mapping/_objects/map-item.ts (1)
  • ItemTypeValue (69-69)
src/lib/domains/mapping/utils/index.ts (2)
  • ItemTypeValue (32-32)
  • Visibility (33-33)
src/lib/domains/agentic/services/_templates/template-allowlist.service.ts (1)
  • Visibility (30-30)
src/lib/domains/mapping/types/contracts.ts (1)
  • Visibility (30-30)
src/lib/domains/mapping/_actions/_map-item-copy-helpers.ts (3)
src/lib/domains/mapping/_objects/index.ts (1)
  • ItemTypeValue (9-9)
src/lib/domains/mapping/_objects/map-item.ts (1)
  • ItemTypeValue (69-69)
src/lib/domains/mapping/utils/index.ts (1)
  • ItemTypeValue (32-32)
src/lib/domains/mapping/_objects/map-item.ts (6)
src/lib/domains/mapping/_objects/index.ts (5)
  • ItemTypeValue (9-9)
  • MapItemType (6-6)
  • Attrs (3-3)
  • Attrs (18-18)
  • Visibility (10-10)
src/lib/domains/mapping/utils/index.ts (4)
  • ItemTypeValue (32-32)
  • MapItemType (29-29)
  • Coord (9-9)
  • Visibility (33-33)
src/lib/domains/mapping/types/contracts.ts (2)
  • MapItemType (30-30)
  • Visibility (30-30)
src/lib/domains/mapping/_objects/base-item.ts (1)
  • Attrs (8-8)
src/lib/domains/mapping/utils/hex-coordinates.ts (1)
  • Coord (22-29)
src/lib/domains/agentic/services/_templates/template-allowlist.service.ts (1)
  • Visibility (30-30)
src/app/map/Cache/Handlers/NavigationHandler/_helpers/_validation/tile-converter.ts (3)
src/lib/domains/mapping/_objects/index.ts (1)
  • ItemTypeValue (9-9)
src/lib/domains/mapping/_objects/map-item.ts (1)
  • ItemTypeValue (69-69)
src/lib/domains/mapping/utils/index.ts (1)
  • ItemTypeValue (32-32)
src/lib/domains/mapping/_actions/_map-item-creation-helpers.ts (5)
src/lib/domains/mapping/_objects/index.ts (3)
  • ItemTypeValue (9-9)
  • MapItemWithId (13-13)
  • MapItemType (6-6)
src/lib/domains/mapping/_objects/map-item.ts (2)
  • ItemTypeValue (69-69)
  • MapItemWithId (183-183)
src/lib/domains/mapping/utils/index.ts (3)
  • ItemTypeValue (32-32)
  • Coord (9-9)
  • MapItemType (29-29)
src/lib/domains/mapping/utils/hex-coordinates.ts (1)
  • Coord (22-29)
src/lib/domains/mapping/types/contracts.ts (1)
  • MapItemType (30-30)
src/app/map/Cache/Services/types.ts (3)
src/lib/domains/mapping/_objects/index.ts (1)
  • ItemTypeValue (9-9)
src/lib/domains/mapping/_objects/map-item.ts (1)
  • ItemTypeValue (69-69)
src/lib/domains/mapping/utils/index.ts (1)
  • ItemTypeValue (32-32)
drizzle/seeds/templates.seed.ts (2)
src/lib/domains/agentic/templates/_system-template.ts (1)
  • SYSTEM_TEMPLATE (37-58)
src/lib/domains/agentic/templates/_user-template.ts (1)
  • USER_TEMPLATE (59-91)
src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/form/_TypeSelectorField.tsx (1)
src/commons/trpc/react.tsx (1)
  • api (74-74)
src/app/map/Cache/Lifecycle/MutationCoordinator/mutation-coordinator.ts (2)
src/lib/domains/mapping/types/item-attributes.ts (1)
  • MapItemCreateAttributes (45-51)
src/app/map/Cache/types/handlers.ts (1)
  • MutationResult (62-67)
src/lib/domains/mapping/services/_item-services/_item-crud.service.ts (5)
src/lib/domains/mapping/_objects/index.ts (2)
  • ItemTypeValue (9-9)
  • MapItemType (6-6)
src/lib/domains/mapping/_objects/map-item.ts (1)
  • ItemTypeValue (69-69)
src/lib/domains/mapping/utils/index.ts (2)
  • ItemTypeValue (32-32)
  • MapItemType (29-29)
src/lib/domains/mapping/types/contracts.ts (1)
  • MapItemType (30-30)
src/lib/domains/mapping/infrastructure/index.ts (1)
  • isBuiltInItemType (18-18)
src/lib/domains/mapping/types/parameters.ts (5)
src/lib/domains/mapping/_objects/index.ts (1)
  • MapItemType (6-6)
src/lib/domains/mapping/utils/index.ts (1)
  • MapItemType (29-29)
src/lib/domains/mapping/services/__tests__/helpers/_test-utilities.ts (1)
  • MapItemType (13-13)
src/lib/domains/mapping/types/contracts.ts (1)
  • MapItemType (30-30)
src/lib/domains/mapping/types/index.ts (2)
  • CreateMapItemParamsSchema (30-30)
  • UpdateMapItemAttrsSchema (31-31)
src/lib/domains/mapping/_repositories/map-item.ts (3)
src/lib/domains/mapping/_objects/index.ts (2)
  • ItemTypeValue (9-9)
  • MapItemWithId (13-13)
src/lib/domains/mapping/_objects/map-item.ts (2)
  • ItemTypeValue (69-69)
  • MapItemWithId (183-183)
src/lib/domains/mapping/utils/index.ts (1)
  • ItemTypeValue (32-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build
  • GitHub Check: Tests - Phase 1 (Main Suite)
  • GitHub Check: Tests - Phase 2 (React Components)
🔇 Additional comments (55)
src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/_handlers.ts (1)

16-29: No issues found. The type change from EditableItemType to string | undefined is correctly implemented with proper validation layering:

  1. Type validation exists in services: ItemCrudService (line 479) and PromptBuilder (line 49) use isBuiltInItemType() to enforce constraints. Built-in types and reserved types ("user") are protected at the service layer where domain logic resides.

  2. Handler design is correct: _handleSave() is properly designed as a thin delegation layer. It passes editItemType to the callback without validation—validation belongs in the service layer, not the UI handler.

  3. All callers updated: tile-widget.tsx (line 126) correctly invokes _handleSave() with the new signature. The WidgetHandlers interface (widget-renderers.tsx line 16) and callback contracts are aligned.

  4. Type change is intentional: Supporting string | undefined enables custom item types ("template", "project", "workflow") as per PR objectives while maintaining type safety through service-layer validation.

scripts/run-tests.sh (1)

87-87: LGTM: Appropriate test isolation for database integration test.

Moving the template-name-column integration test to Phase 2 ensures proper isolation with single-fork execution, which is consistent with other database schema tests.

Also applies to: 141-142

src/lib/domains/mapping/_objects/map-item.ts (2)

64-69: LGTM: Well-documented type extension for custom item types.

The ItemTypeValue type union cleanly extends the built-in MapItemType enum to support custom strings, enabling template-based types while preserving system semantics for the "user" type.


75-77: LGTM: Interface changes support template provenance tracking.

The changes to accept ItemTypeValue for itemType and the addition of optional templateName properly support the Templates as Tiles feature for tracking tile origins.

src/lib/domains/mapping/_actions/_map-item-creation-helpers.ts (1)

12-12: LGTM: Consistent adoption of ItemTypeValue throughout the helper.

All method signatures properly updated to use ItemTypeValue, maintaining consistency with the domain object changes. The type safety is preserved while enabling custom item types.

Also applies to: 34-34, 61-61, 78-78, 82-82, 110-110

src/lib/domains/mapping/types/parameters.ts (1)

28-35: LGTM: Clean schema composition with backward compatibility.

The union approach in CreateItemTypeSchema maintains backward compatibility with the MapItemType enum while supporting custom types. The consistent application across create and update schemas is well-structured.

Also applies to: 43-43, 58-68

src/server/db/schema/index.ts (1)

25-25: LGTM: Consistent barrel export for new auth table.

The export for the user-template-allowlist table follows the established pattern and is properly placed with other auth table exports.

src/server/db/schema/_tables/mapping/map-items.ts (3)

45-46: LGTM! Item type constraint removal aligns with PR objectives.

The removal of .$type<MapItemType>() correctly enables the item type system extension to accept custom string values (ItemTypeValue), as specified in the PR objectives. The existing check constraints (lines 67-74) continue to work with string-based comparisons.


53-53: LGTM! Template name column addition is correct.

The new templateName column with varchar(100) nullable constraint aligns with migration 0015_add_template_name_column.sql mentioned in the PR objectives and enables template provenance tracking for tile-based templates.


88-90: Global uniqueness of template names is intentional and tested.

The unique index on templateName is correctly implemented as global. Test coverage in src/server/db/schema/__tests__/template-name-column.integration.test.ts explicitly verifies this behavior:

  • Line 17: Requirements document that "templateName has a unique constraint (globally unique)"
  • Lines 162-202: Test explicitly verifies that the same templateName is rejected across different users

The constraint is part of the "templates as tiles" feature design, where template names must be globally unique for consistent prompt rendering across the system.

src/lib/domains/agentic/infrastructure/index.ts (1)

11-14: LGTM! Clean addition of template allowlist repository export.

The new DrizzleTemplateAllowlistRepository export follows the project's absolute import convention and correctly exposes the repository implementation for the template allowlist feature introduced in PR objective #6.

src/lib/domains/mapping/_objects/index.ts (1)

9-9: LGTM! ItemTypeValue type export enables flexible item typing.

The addition of ItemTypeValue to the public exports correctly exposes the new flexible item type alias (MapItemType | string) introduced in PR objective #1, enabling consumers to work with both built-in and custom item types.

src/lib/domains/mapping/infrastructure/index.ts (1)

15-18: LGTM! Item type utility functions correctly exposed.

The addition of isBuiltInItemType, isReservedItemType, and isCustomItemType predicates correctly exposes the type guard functions introduced in PR objective #1, enabling consumers to distinguish between built-in, reserved, and custom item types.

src/lib/domains/agentic/infrastructure/template-allowlist/index.ts (1)

1-1: LGTM! Clean barrel export for template allowlist infrastructure.

The new barrel export file follows the project's infrastructure organization pattern and correctly re-exports DrizzleTemplateAllowlistRepository using absolute imports, enabling consolidated access to the template allowlist repository implementation.

src/app/map/Chat/Timeline/_components/_renderers/widget-renderers.tsx (1)

6-6: LGTM: Interface types updated consistently.

The WidgetHandlers interface correctly updates the handleTileSave signature to accept itemType?: string, maintaining consistency with the implementation in tile-handlers.ts.

Also applies to: 16-16

src/lib/domains/mapping/utils/index.ts (1)

32-32: LGTM: Public API type export added.

The ItemTypeValue type is correctly re-exported to provide public access to the unified type system (MapItemType | string) that supports both built-in and custom item types.

src/app/map/Cache/Handlers/NavigationHandler/_helpers/_validation/tile-converter.ts (1)

4-4: LGTM: Type widening maintains compatibility.

The change from MapItemType to ItemTypeValue correctly widens the accepted types to support both built-in enum values and custom string types. Since ItemTypeValue = MapItemType | string, this maintains full backwards compatibility while enabling the new flexible item type system.

Also applies to: 20-20, 33-33

src/app/map/Chat/Timeline/_utils/tile-handlers.ts (1)

4-4: Backend accepts string-based itemType values—type migration is correct.

The changes appropriately widen itemType from NonUserMapItemTypeString to string, aligning with the ItemTypeValue system that supports custom template types. Backend mutation operations (createItem accepts { itemType: string }; updateItem uses MapItemUpdateAttributes) confirm string-based values are supported. The optional parameter maintains backwards compatibility.

src/app/map/Cache/Lifecycle/_callbacks/mutation-callbacks.ts (1)

26-26: Backend correctly accepts string-based itemType.

Verification confirms the change is properly implemented. The MutationOperations interface and server router both accept itemType as a string directly. The server-side toItemTypeValue() function is a pass-through, confirming the backend requires no enum transformation. The asymmetry noted (retaining toVisibilityEnum while removing toItemTypeEnum) reflects the actual API contract where visibility requires enum conversion but itemType does not.

src/lib/domains/agentic/services/task-execution.service.ts (2)

20-20: LGTM: Type contract upgraded to support custom item types.

The import change from MapItemType to ItemTypeValue aligns with the broader refactor to support both built-in and custom template types while maintaining type-only imports (no runtime dependency on mapping domain).


30-42: LGTM: TaskTile interface updated consistently.

The itemType field correctly uses the new ItemTypeValue type, maintaining compatibility with MapItemContract while supporting both built-in and custom item types.

src/app/map/Chat/Timeline/Widgets/TileWidget/TileForm.tsx (1)

16-28: LGTM: Props updated to accept string-based item types.

The change from EditableItemType to string for both itemType and onItemTypeChange aligns with the broader migration to a flexible type system that supports custom templates alongside built-in types.

src/app/map/Cache/types/index.ts (1)

90-107: LGTM: Mutation operation types updated to accept flexible item types.

The change from NonUserMapItemTypeString to string for itemType in both createItemOptimistic and updateItemOptimistic operations correctly aligns with the new type system that supports custom templates.

src/lib/domains/mapping/types/item-attributes.ts (1)

27-40: LGTM: Well-documented type migration to support custom item types.

The updated documentation clearly explains the distinction between built-in types, custom types, and the reserved "user" type. The change from NonUserMapItemTypeString to string properly supports the new flexible type system.

drizzle/migrations/0017_add_user_template_allowlist.sql (2)

9-19: LGTM: Well-structured idempotent migration.

The migration follows best practices:

  • Idempotent statements (IF NOT EXISTS) allow safe re-execution
  • Proper constraints (primary key, foreign key with cascade, unique constraint)
  • Performance optimization via index on user_id
  • Appropriate timestamps for audit trail

The TEXT[] array type and '{}' default are correct PostgreSQL syntax.


9-16: The migration dependency is correctly ordered. The users table is created in migration 0000_sweet_firebird.sql, which executes before migration 0017_add_user_template_allowlist.sql. The foreign key constraint will not cause any dependency errors.

src/app/map/Cache/Handlers/NavigationHandler/_helpers/_loading/region-loader.ts (1)

5-5: Type migration is consistent and safe for read operations.

The change from MapItemType to ItemTypeValue is appropriate here since this function loads existing items that have already been validated at creation time. The broader type accommodates both built-in and custom item types introduced in this PR.

Also applies to: 22-22

src/app/map/Cache/Lifecycle/MutationCoordinator/_mutation-wrappers.ts (1)

1-1: Type safety reduction is properly mitigated by API-layer validation and integration tests.

The change from NonUserMapItemTypeString to string removes compile-time guarantees, but this is acceptable given:

  1. API validation: map-schemas.ts (lines 57-60) validates that itemType cannot be "user" via Zod schema with case-insensitive check and clear error message.
  2. Integration test coverage: Tests in map-items-itemtype.integration.test.ts (lines 156–168) explicitly verify that attempting to change itemType to or from "user" is rejected.

The runtime protection is solid and the risk is well-managed. No further action required.

src/lib/domains/mapping/_repositories/map-item.ts (1)

234-242: Type signature mismatch: interface accepts ItemTypeValue but implementation expects MapItemType.

The repository interface signature changed to accept ItemTypeValue (which includes all strings), while the implementation in db.ts still uses MapItemType. The "Cannot change to/from USER type" restriction is already validated at the service layer in _item-crud.service.ts (lines 195 and 199) before the repository method is called, so the business rule is enforced despite the broader type signature. However, the type mismatch should be resolved—either update the implementation signature to match the interface, or update the interface to match the implementation.

src/app/map/Chat/Timeline/_utils/creation-handlers.ts (1)

12-12: LGTM! Consistent type broadening for custom template support.

The itemType parameter correctly broadens to string while preserving the default value "context". This aligns with the PR's goal of supporting custom template types through the tile system.

Also applies to: 26-26

src/lib/domains/mapping/infrastructure/map-item/types.ts (2)

1-1: LGTM! Core type infrastructure correctly updated.

The transition from MapItemType to ItemTypeValue is consistent across all DB attribute types (DbMapItemSelect, CreateMapItemDbAttrs, UpdateMapItemDbAttrs), properly supporting both built-in and custom item types.

Also applies to: 10-10, 36-36, 47-47


13-13: The database migration for templateName column exists and is properly implemented.

The migration 0015_add_template_name_column.sql adds the template_name column to vde_map_items as expected. A follow-up migration (0016) adds a UNIQUE constraint and adjusts the column length to VARCHAR(100). The type definitions correctly reflect the database schema with appropriate nullability across all three types (DbMapItemSelect, CreateMapItemDbAttrs, and UpdateMapItemDbAttrs).

src/server/db/schema/_tables/auth/user-template-allowlist.ts (2)

37-38: LGTM! Type exports follow Drizzle ORM conventions.

The type exports correctly use Drizzle's type inference for select and insert operations.


21-21: ID generation is properly implemented in the repository layer.

The id field correctly has no database default. The DrizzleTemplateAllowlistRepository generates IDs using nanoid() on line 47 of the insert operation before persisting to the database.

src/app/map/Chat/Timeline/Widgets/TileWidget/tile-widget.tsx (1)

33-33: LGTM! Widget properly supports flexible item types.

The itemType prop and onSave callback correctly accept string | null to support both built-in and custom template types. The component logic appropriately handles null values throughout.

Also applies to: 48-48

src/app/map/Cache/types/handlers.ts (1)

95-95: Validate itemType values at runtime.

The type was broadened from NonUserMapItemTypeString to string, removing compile-time type constraints. Ensure downstream validation exists in the mutation coordinator or repository layer to enforce:

  • Reserved type restrictions
  • Allowlist of valid itemType values
  • Security rules preventing injection of arbitrary types

Without runtime validation, invalid or malicious itemType values could bypass security checks.

src/lib/domains/mapping/_actions/_map-item-copy-helpers.ts (1)

5-5: LGTM! Type migration to ItemTypeValue is correct.

The type updates correctly propagate ItemTypeValue (which is MapItemType | string) through the copy helper functions, maintaining type safety while supporting the extended item-type system.

Also applies to: 50-50, 142-142, 151-151

src/server/api/routers/agentic/agentic.ts (3)

603-614: LGTM! Endpoint correctly retrieves effective allowlist.

The implementation properly combines built-in templates with user-specific allowed templates using the service layer.


652-684: LGTM! Removal logic correctly prevents removing built-in templates.

The implementation properly validates that built-in templates cannot be removed (line 665-667) and performs case-insensitive filtering (line 672-674).


616-650: Case-insensitive duplicate prevention is correctly implemented.

The logic properly handles the flow: filters out built-in templates from the current allowlist, performs case-insensitive duplicate checking, and saves the new template with its original casing. Comprehensive test coverage exists in template-allowlist.service.test.ts (lines 333-354), including case-insensitive matching scenarios. The implementation is sound and no changes are needed.

src/lib/domains/agentic/infrastructure/template-allowlist/drizzle-allowlist-repository.ts (2)

19-32: LGTM! Clean implementation of getUserAllowlist.

The method correctly queries the database and handles the null case when no allowlist exists for the user.


54-58: Placeholder implementation noted.

The comment clearly indicates this is a placeholder that will look up template tile visibility in the future. This is acceptable for the current PR scope.

src/app/map/Cache/Services/types.ts (1)

2-2: LGTM! Type definitions correctly updated to ItemTypeValue.

All ServerService method return types have been consistently updated to use ItemTypeValue instead of MapItemType, maintaining type safety across the cache service layer.

Also applies to: 19-19, 35-35, 49-49, 63-63, 77-77, 94-94

src/app/map/Chat/Timeline/Widgets/TileWidget/useTileState.tsx (1)

9-9: LGTM! Type widening aligns with ItemTypeValue migration.

The change from strict union type to string | null is consistent with the broader PR's migration to ItemTypeValue. The default fallback to 'context' appropriately handles legacy tiles with null itemType, as noted in the learnings.

Also applies to: 31-33, 64-64

drizzle/seeds/templates.seed.ts (1)

68-81: Well-structured template specifications.

The BUILTIN_TEMPLATE_SPECS array cleanly defines the built-in templates with proper coordinates. The idempotent upsert logic in _seedTemplate handles both creation and updates correctly.

src/app/map/Cache/Lifecycle/MutationCoordinator/mutation-coordinator.ts (1)

30-30: Type signature updates are consistent.

The widening of itemType from NonUserMapItemTypeString to string in the config (line 30), createItem (line 292), and _prepareOptimisticUpdate (line 1180) aligns with the PR's ItemTypeValue migration.

Also applies to: 292-292, 1180-1180

src/app/map/Chat/Timeline/Widgets/TileWidget/_internals/form/_TypeSelectorField.tsx (2)

35-43: Good defensive handling of allowlist data.

The fallback to default types (line 38) and ensuring the current value is always displayed (lines 42-43) provide good UX resilience. This handles edge cases where the user has a custom type that's no longer in their allowlist.


21-28: Clean label formatting helper.

_formatTypeLabel correctly prioritizes built-in labels and falls back to capitalization for custom types.

src/lib/domains/agentic/templates/_prompt-builder.ts (2)

42-71: Custom type handling looks correct but exhaustive check is now unreachable.

The early return for non-built-in types (lines 49-51) means the default case with exhaustiveCheck: never (lines 66-69) is now unreachable for custom types. This is fine since isBuiltInItemType guarantees only MapItemType values reach the switch, but consider adding a comment for clarity.

🔎 Optional: Add clarifying comment
+  // At this point, itemType is guaranteed to be a built-in MapItemType
+  // due to the isBuiltInItemType check above
   switch (itemType) {
     case MapItemType.SYSTEM:

187-189: Type cast is appropriate for template data selection.

The cast (MapItemType.USER as ItemTypeValue) is necessary because data.itemType could be a custom string. Only built-in USER tiles get the user template data; all others (including custom types) get system template data.

src/server/api/routers/map/map-items.ts (3)

171-191: Update handling is correct with optional itemType.

The updateParams type (line 178) correctly marks itemType as optional, and line 191 only sets it when explicitly provided in the input.


30-37: Schema validation for reserved "user" type is correctly implemented.

The itemTypeSchema at lines 57–60 of map-schemas.ts uses refine() to reject the "user" type (case-insensitive), and both itemCreationSchema and itemUpdateSchema apply this validation. The toItemTypeValue() pass-through function is appropriate here because tRPC enforces schema validation before the function executes, providing proper separation of concerns.


134-134: The code is correct as written. itemType is required in itemCreationSchema (line 77 of map-schemas.ts shows no .optional() modifier), so input.itemType is guaranteed to be a non-undefined string when passed to toItemTypeValue(). Zod validation ensures the schema constraint is enforced before the code reaches line 134.

src/lib/domains/mapping/services/_item-services/_item-crud.service.ts (2)

477-481: Good: Custom types skip hierarchy validation.

The early return for non-built-in types (lines 479-481) correctly allows custom types like "template" to bypass the strict SYSTEM/CONTEXT/ORGANIZATIONAL hierarchy rules.


61-61: Type signature updates are consistent with PR migration.

The ItemTypeValue type in addItemToMap (line 61) and updateItem (line 171) properly supports both built-in and custom item types while maintaining the USER type restriction.

Also applies to: 171-171

Comment on lines +125 to +149
const baseResult = await sql<Array<{ id: number }>>`
INSERT INTO vde_base_items (title, content, created_at, updated_at)
VALUES ('System', 'System-level organizational tiles', NOW(), NOW())
RETURNING id
`
const baseItemId = baseResult[0]?.id
if (!baseItemId) throw new Error('Failed to create grandparent base item')

const mapResult = await sql<Array<{ id: number }>>`
INSERT INTO vde_map_items (
coord_user_id, coord_group_id, path, item_type, visibility,
parent_id, ref_item_id, created_at, updated_at
)
VALUES (
${SYSTEM_USER_ID}, 0, ${grandparentPath}, 'organizational', 'public',
${rootId}, ${baseItemId}, NOW(), NOW()
)
RETURNING id
`

const grandparentId = mapResult[0]?.id
if (!grandparentId) throw new Error('Failed to create grandparent map item')

console.log(` Created grandparent organizational tile at path: ${grandparentPath}`)
return grandparentId
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Same atomicity issue in hierarchy tile creation.

Both _ensureGrandparentExists (lines 125-143) and _ensureTemplatesParentExists (lines 175-193) have the same two-phase insert pattern without transaction wrapping. Consider wrapping all creation functions in transactions for consistency.

🔎 Example fix pattern
+  await sql.begin(async (tx) => {
-  const baseResult = await sql<Array<{ id: number }>>`
+    const baseResult = await tx<Array<{ id: number }>>`
       INSERT INTO vde_base_items (title, content, created_at, updated_at)
       VALUES ('System', 'System-level organizational tiles', NOW(), NOW())
       RETURNING id
     `
     // ... validation ...
-  const mapResult = await sql<Array<{ id: number }>>`
+    const mapResult = await tx<Array<{ id: number }>>`
       INSERT INTO vde_map_items (...)
       VALUES (...)
       RETURNING id
     `
+  })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const baseResult = await sql<Array<{ id: number }>>`
INSERT INTO vde_base_items (title, content, created_at, updated_at)
VALUES ('System', 'System-level organizational tiles', NOW(), NOW())
RETURNING id
`
const baseItemId = baseResult[0]?.id
if (!baseItemId) throw new Error('Failed to create grandparent base item')
const mapResult = await sql<Array<{ id: number }>>`
INSERT INTO vde_map_items (
coord_user_id, coord_group_id, path, item_type, visibility,
parent_id, ref_item_id, created_at, updated_at
)
VALUES (
${SYSTEM_USER_ID}, 0, ${grandparentPath}, 'organizational', 'public',
${rootId}, ${baseItemId}, NOW(), NOW()
)
RETURNING id
`
const grandparentId = mapResult[0]?.id
if (!grandparentId) throw new Error('Failed to create grandparent map item')
console.log(` Created grandparent organizational tile at path: ${grandparentPath}`)
return grandparentId
await sql.begin(async (tx) => {
const baseResult = await tx<Array<{ id: number }>>`
INSERT INTO vde_base_items (title, content, created_at, updated_at)
VALUES ('System', 'System-level organizational tiles', NOW(), NOW())
RETURNING id
`
const baseItemId = baseResult[0]?.id
if (!baseItemId) throw new Error('Failed to create grandparent base item')
const mapResult = await tx<Array<{ id: number }>>`
INSERT INTO vde_map_items (
coord_user_id, coord_group_id, path, item_type, visibility,
parent_id, ref_item_id, created_at, updated_at
)
VALUES (
${SYSTEM_USER_ID}, 0, ${grandparentPath}, 'organizational', 'public',
${rootId}, ${baseItemId}, NOW(), NOW()
)
RETURNING id
`
const grandparentId = mapResult[0]?.id
if (!grandparentId) throw new Error('Failed to create grandparent map item')
console.log(` Created grandparent organizational tile at path: ${grandparentPath}`)
return grandparentId
})
🤖 Prompt for AI Agents
In @drizzle/seeds/templates.seed.ts around lines 125 - 149, Both
_ensureGrandparentExists and _ensureTemplatesParentExists perform two separate
INSERTs (see baseResult/mapResult and the subsequent id checks) without
transactional protection, which can leave the DB in a partial state; refactor
each function to execute both INSERTs inside a single transaction (use your SQL
client's transaction API or sql.begin/transaction) so that you RETURN the map
item id only after committing and rollback on any error, and remove the separate
non-atomic insert pattern by using the transaction-scoped queries and error
handling.

Comment on lines +239 to +266
async function _createTemplate(
spec: BuiltinTemplateSpec,
parentId: number
): Promise<void> {
const pathString = [...TEMPLATES_PARENT_PATH, spec.direction].join(',')

const baseResult = await sql<Array<{ id: number }>>`
INSERT INTO vde_base_items (title, content, created_at, updated_at)
VALUES (${spec.title}, ${spec.content}, NOW(), NOW())
RETURNING id
`

const baseItemId = baseResult[0]?.id
if (baseItemId === undefined) {
throw new Error(`Failed to create base item for template: ${spec.templateName}`)
}

await sql`
INSERT INTO vde_map_items (
coord_user_id, coord_group_id, path, item_type, visibility,
parent_id, ref_item_id, template_name, created_at, updated_at
)
VALUES (
${SYSTEM_USER_ID}, 0, ${pathString}, ${TEMPLATE_ITEM_TYPE}, 'public',
${parentId}, ${baseItemId}, ${spec.templateName}, NOW(), NOW()
)
`
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing transaction for atomic template creation.

This issue was flagged in a previous review and remains unaddressed. The _createTemplate function executes two separate INSERT statements without transaction wrapping. If the second insert fails, an orphan vde_base_items record will remain.

🤖 Prompt for AI Agents
In @drizzle/seeds/templates.seed.ts around lines 239 - 266, The _createTemplate
function performs two INSERTs (into vde_base_items and vde_map_items) without a
transaction, risking orphaned vde_base_items if the second INSERT fails; modify
_createTemplate to run both INSERTs inside a single DB transaction (use your SQL
client's transaction helper or explicit BEGIN/COMMIT/ROLLBACK), so that on any
error you ROLLBACK and rethrow; ensure the transaction scope includes creating
baseResult (INSERT INTO vde_base_items) and the subsequent INSERT INTO
vde_map_items, and handle errors to always rollback before throwing.

Comment on lines +109 to +110
export function shouldUseOrchestrator(itemType: ItemTypeValue, userMessage: string | undefined): boolean {
return itemType === 'system' && _hasContent(userMessage)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find MapItemType enum definition
echo "=== Searching for MapItemType enum definition ==="
rg -n "enum MapItemType|type MapItemType" --type=ts -A 10

echo ""
echo "=== Searching for MapItemType.SYSTEM usages and 'system' literals ==="
rg -n "MapItemType\.SYSTEM|itemType.*['\"]system['\"]" --type=ts -B 2 -A 2

echo ""
echo "=== Searching for ItemTypeValue type definition ==="
rg -n "type ItemTypeValue|interface ItemTypeValue" --type=ts -A 5

Repository: Diplow/hexframe

Length of output: 50371


🏁 Script executed:

#!/bin/bash
# Examine the full context of the _hexrun-orchestrator-template.ts file
echo "=== Checking _hexrun-orchestrator-template.ts imports and usage patterns ==="
head -120 src/lib/domains/agentic/templates/_hexrun-orchestrator-template.ts

echo ""
echo "=== Check ItemTypeValue definition and usage context ==="
rg -n "type ItemTypeValue" src/lib/domains/mapping/_objects/map-item.ts -A 5

Repository: Diplow/hexframe

Length of output: 4176


🏁 Script executed:

#!/bin/bash
# Check where shouldUseOrchestrator is used
echo "=== Usages of shouldUseOrchestrator function ==="
rg -n "shouldUseOrchestrator" --type=ts -B 2 -A 2

echo ""
echo "=== Check if MapItemType is imported in _hexrun-orchestrator-template.ts ==="
head -15 src/lib/domains/agentic/templates/_hexrun-orchestrator-template.ts | grep -E "import|from"

Repository: Diplow/hexframe

Length of output: 2782


Use MapItemType.SYSTEM enum instead of string literal for type safety and consistency.

The string 'system' is factually the correct runtime value of MapItemType.SYSTEM, but using a hardcoded string literal breaks the pattern established throughout the codebase. Other agentic template files (e.g., _prompt-builder.ts, _internals/section-builders.ts) use the MapItemType.SYSTEM enum for comparisons. Replace itemType === 'system' with itemType === MapItemType.SYSTEM and add the required import from ~/lib/domains/mapping/_objects/map-item to maintain consistency, type safety, and catch potential enum changes at compile time.

🤖 Prompt for AI Agents
In @src/lib/domains/agentic/templates/_hexrun-orchestrator-template.ts around
lines 109 - 110, Replace the string literal comparison in shouldUseOrchestrator
with the MapItemType enum: change the check from itemType === 'system' to
itemType === MapItemType.SYSTEM, and add an import for MapItemType from
~/lib/domains/mapping/_objects/map-item so the function uses the enum for type
safety and consistency with other templates.

Comment on lines +57 to +60
export const itemTypeSchema = z.string().min(1).refine(
(val) => val.toLowerCase() !== "user",
{ message: "The 'user' item type is reserved for system-created root tiles" }
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add string length limits and stricter validation for custom item types.

The current validation allows any non-empty string except "user", which has several risks:

  1. No maximum length: Very long strings could cause storage or display issues
  2. No whitespace handling: Strings like " " or " organizational " would pass validation
  3. No character restrictions: Special characters, line breaks, or control characters could cause issues
🔎 Proposed validation improvements
-export const itemTypeSchema = z.string().min(1).refine(
-  (val) => val.toLowerCase() !== "user",
-  { message: "The 'user' item type is reserved for system-created root tiles" }
-);
+export const itemTypeSchema = z
+  .string()
+  .min(1)
+  .max(50)
+  .trim()
+  .regex(/^[a-z0-9_-]+$/i, {
+    message: "Item type must contain only letters, numbers, hyphens, and underscores"
+  })
+  .refine(
+    (val) => val.toLowerCase() !== "user",
+    { message: "The 'user' item type is reserved for system-created root tiles" }
+  );

This ensures:

  • Maximum 50 characters (reasonable for type names)
  • Whitespace is trimmed before validation
  • Only alphanumeric, hyphen, and underscore characters allowed
  • Case-insensitive "user" check remains
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const itemTypeSchema = z.string().min(1).refine(
(val) => val.toLowerCase() !== "user",
{ message: "The 'user' item type is reserved for system-created root tiles" }
);
export const itemTypeSchema = z
.string()
.min(1)
.max(50)
.trim()
.regex(/^[a-z0-9_-]+$/i, {
message: "Item type must contain only letters, numbers, hyphens, and underscores"
})
.refine(
(val) => val.toLowerCase() !== "user",
{ message: "The 'user' item type is reserved for system-created root tiles" }
);
🤖 Prompt for AI Agents
In @src/server/api/routers/map/map-schemas.ts around lines 57 - 60,
itemTypeSchema currently only bans "user" and allows any non-empty string;
update itemTypeSchema to trim input, enforce a max length of 50, reject strings
that become empty after trimming, restrict characters to only alphanumeric,
hyphen and underscore (e.g. /^[A-Za-z0-9_-]+$/), and keep the case-insensitive
"user" prohibition; implement this by replacing the existing
z.string().min(1).refine(...) chain with a pipeline that trims then checks
length <= 50, validates the regex, and refines against val.toLowerCase() ===
"user" (or equivalent) so all checks run in order.

Implement two-phase template rendering with child path syntax:
- child[-3].title, child[-2,-1].content for direct field access
- child[-6..-1], child[1..6] for range iteration
- {{@RenderChildren range=[-6..-1] fallback='generic'}} for pool dispatch

New modules:
- _pre-processor/_path-parser.ts: Parse child path expressions
- _pool/: Template pool types and builder for itemType matching
- _compiler/: Two-phase compile (structural template) + render (Mustache)

Key features:
- Templates match tiles by templateName (title) to itemType
- Recursive depth control via nested template pools
- Variable prefixing: {{title}} → {{child[-3].title}}
- Inspectable compiled templates showing exact prompt structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI Agents
In @src/lib/domains/agentic/templates/_internals/types.ts:
- Around line 7-15: The PromptDataTile interface duplicates TileData; replace
the duplicate by importing/re-exporting or aliasing TileData: remove
PromptDataTile from src/lib/domains/agentic/templates/_internals/types.ts and
update any usages to reference TileData (from
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts), or alternatively
export TileData from the pre-processor module and import it here so both modules
share the single TileData definition; ensure imports/exports and all references
to PromptDataTile are updated to the TileData symbol to keep types consistent.
🧹 Nitpick comments (5)
src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts (1)

16-16: Consider enforcing brace symmetry in the regex pattern.

The current pattern /(\{\{\{?)(\w+)(\}\}\}?)/g doesn't enforce that opening and closing braces match (both 2 or both 3). For example, {{{title}} would be matched even though it's malformed Mustache syntax.

Consider using separate patterns or add validation to ensure brace counts match:

🔎 Suggested improvement
-const MUSTACHE_VAR_PATTERN = /(\{\{\{?)(\w+)(\}\}\}?)/g
+// Match either {{var}} or {{{var}}} but not mixed
+const MUSTACHE_VAR_PATTERN = /(\{\{\{)(\w+)(\}\}\})|(\{\{)(\w+)(\}\})/g

Or add validation in the replace callback to verify symmetry.

src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts (1)

119-127: Consider simplifying the direction validation.

The null check followed by non-null assertion works but could be cleaner using a type guard pattern.

🔎 Suggested simplification
-  // Validate all directions parsed successfully
-  if (directions.some(d => d === null)) {
-    return null
-  }
-
-  // Validate directions are in valid range (-6 to 6, excluding 0)
-  if (directions.some(d => d === 0 || d! < -6 || d! > 6)) {
-    return null
-  }
+  // Validate all directions parsed successfully and are in valid range
+  const isValidDirection = (d: number | null): d is number =>
+    d !== null && d !== 0 && d >= -6 && d <= 6
+
+  if (!directions.every(isValidDirection)) {
+    return null
+  }
src/lib/domains/agentic/templates/_pool/builder.ts (1)

101-125: Consider extracting duplicate normalization logic.

The title normalization pattern .toLowerCase().replace(/\s+/g, '-') is duplicated on lines 111 and 121.

🔎 Extract helper function
+const normalizeTemplateName = (name: string): string =>
+  name.toLowerCase().replace(/\s+/g, '-')
+
 function _getTemplateName(tile: TileData): string | undefined {
   // Check for templateName in extended tile data
   const extendedTile = tile as TileData & { templateName?: string }
   if (extendedTile.templateName) {
     return extendedTile.templateName
   }

   // For template tiles, use title as templateName
   // (templates have itemType="template" but title indicates what they match)
   if (tile.itemType === 'template' && tile.title) {
-    return tile.title.toLowerCase().replace(/\s+/g, '-')
+    return normalizeTemplateName(tile.title)
   }

   // For non-template tiles, use itemType
   if (tile.itemType && tile.itemType !== 'template') {
     return String(tile.itemType)
   }

   // Last resort: derive from title
   if (tile.title) {
-    return tile.title.toLowerCase().replace(/\s+/g, '-')
+    return normalizeTemplateName(tile.title)
   }

   return undefined
 }
src/lib/domains/agentic/templates/_pre-processor/index.ts (1)

138-161: Consider keeping switch in sync with registry.

The _validateTemplateExists function checks if templateName exists in registry, but _invokeTemplate has a hardcoded switch. If new templates are added to the registry, the switch must also be updated.

This could be simplified by making registry functions have a uniform signature, allowing direct invocation without a switch. Alternatively, add a comment noting the coupling.

🔎 Minimal fix: Add comment noting the coupling
 function _invokeTemplate(
   templateName: string,
   resolvedParams: ResolvedParams,
   context: TemplateContext,
   registry: TemplateRegistry,
   originalParams: ParsedParams
 ): string {
+  // NOTE: This switch must stay in sync with TemplateRegistry keys.
+  // Adding a new template to the registry requires adding a case here.
   switch (templateName) {
src/lib/domains/agentic/templates/_compiler/index.ts (1)

63-77: Consider making maxDepth configurable.

The maxDepth is hardcoded to 10. Consider adding an optional parameter to allow callers to customize the recursion limit based on their specific needs.

🔎 Proposed enhancement to make maxDepth configurable
 export function compileTemplate(
   template: string,
   taskTile: TileData,
-  templatePool: TemplatePool
+  templatePool: TemplatePool,
+  maxDepth: number = 10
 ): string {
   const context: CompileContext = {
     currentPath: [],
     taskTile,
     templatePool,
-    maxDepth: 10,
+    maxDepth,
     currentDepth: 0
   }

   return _compileWithContext(template, context)
 }
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3fecbeb and 28dfed6.

📒 Files selected for processing (14)
  • src/lib/domains/agentic/templates/__tests__/compiler.test.ts
  • src/lib/domains/agentic/templates/_compiler/_data-builder.ts
  • src/lib/domains/agentic/templates/_compiler/_render-children.ts
  • src/lib/domains/agentic/templates/_compiler/_two-phase-render.ts
  • src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts
  • src/lib/domains/agentic/templates/_compiler/index.ts
  • src/lib/domains/agentic/templates/_compiler/types.ts
  • src/lib/domains/agentic/templates/_internals/types.ts
  • src/lib/domains/agentic/templates/_pool/builder.ts
  • src/lib/domains/agentic/templates/_pool/index.ts
  • src/lib/domains/agentic/templates/_pool/types.ts
  • src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts
  • src/lib/domains/agentic/templates/_pre-processor/_resolver.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
✅ Files skipped from review due to trivial changes (1)
  • src/lib/domains/agentic/templates/tests/compiler.test.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use absolute imports with ~/ prefix instead of relative imports (./ or ../), as enforced by ESLint no-restricted-imports rule

Files:

  • src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts
  • src/lib/domains/agentic/templates/_compiler/_render-children.ts
  • src/lib/domains/agentic/templates/_compiler/_two-phase-render.ts
  • src/lib/domains/agentic/templates/_pool/builder.ts
  • src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts
  • src/lib/domains/agentic/templates/_pre-processor/_resolver.ts
  • src/lib/domains/agentic/templates/_compiler/types.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
  • src/lib/domains/agentic/templates/_compiler/_data-builder.ts
  • src/lib/domains/agentic/templates/_pool/types.ts
  • src/lib/domains/agentic/templates/_internals/types.ts
  • src/lib/domains/agentic/templates/_compiler/index.ts
  • src/lib/domains/agentic/templates/_pool/index.ts
src/lib/domains/agentic/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use buildPrompt() function from src/lib/domains/agentic/services/prompt-executor.service.ts to deterministically generate XML prompts from tile coordinates

Files:

  • src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts
  • src/lib/domains/agentic/templates/_compiler/_render-children.ts
  • src/lib/domains/agentic/templates/_compiler/_two-phase-render.ts
  • src/lib/domains/agentic/templates/_pool/builder.ts
  • src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts
  • src/lib/domains/agentic/templates/_pre-processor/_resolver.ts
  • src/lib/domains/agentic/templates/_compiler/types.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
  • src/lib/domains/agentic/templates/_compiler/_data-builder.ts
  • src/lib/domains/agentic/templates/_pool/types.ts
  • src/lib/domains/agentic/templates/_internals/types.ts
  • src/lib/domains/agentic/templates/_compiler/index.ts
  • src/lib/domains/agentic/templates/_pool/index.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Every tile has a semantic type (USER, ORGANIZATIONAL, CONTEXT, SYSTEM) that guides agent behavior; treat unclassified legacy tiles with null itemType as undefined
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Applies to src/lib/domains/agentic/**/*.{ts,tsx} : Use `buildPrompt()` function from `src/lib/domains/agentic/services/prompt-executor.service.ts` to deterministically generate XML prompts from tile coordinates

Applied to files:

  • src/lib/domains/agentic/templates/_compiler/_two-phase-render.ts
  • src/lib/domains/agentic/templates/_pool/builder.ts
  • src/lib/domains/agentic/templates/_pre-processor/_resolver.ts
  • src/lib/domains/agentic/templates/_compiler/types.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
  • src/lib/domains/agentic/templates/_compiler/_data-builder.ts
  • src/lib/domains/agentic/templates/_internals/types.ts
  • src/lib/domains/agentic/templates/_compiler/index.ts
  • src/lib/domains/agentic/templates/_pool/index.ts
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Tiles can have both subtask children (positive directions 1-6) and context children (negative directions -1 to -6) simultaneously; context children are reference materials not preloaded eagerly

Applied to files:

  • src/lib/domains/agentic/templates/_pre-processor/_resolver.ts
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Every tile has a semantic type (USER, ORGANIZATIONAL, CONTEXT, SYSTEM) that guides agent behavior; treat unclassified legacy tiles with null itemType as undefined

Applied to files:

  • src/lib/domains/agentic/templates/_pre-processor/_resolver.ts
🧬 Code graph analysis (9)
src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts (1)
src/lib/domains/agentic/templates/_compiler/index.ts (3)
  • prefixVariables (14-14)
  • formatPath (14-14)
  • formatPathWithField (14-14)
src/lib/domains/agentic/templates/_compiler/_render-children.ts (7)
src/lib/domains/agentic/templates/_compiler/index.ts (3)
  • CompileContext (13-13)
  • compileTemplate (63-77)
  • prefixVariables (14-14)
src/lib/domains/agentic/templates/_compiler/types.ts (1)
  • CompileContext (13-24)
src/lib/domains/agentic/templates/_pre-processor/index.ts (4)
  • getChildrenInRange (15-15)
  • TileData (17-17)
  • ChildPath (26-26)
  • resolveChildPath (13-13)
src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts (1)
  • ChildPath (15-18)
src/lib/domains/agentic/templates/_pool/builder.ts (2)
  • findTemplateForItemType (80-89)
  • buildTemplatePool (19-54)
src/lib/domains/agentic/templates/_pool/index.ts (2)
  • findTemplateForItemType (12-12)
  • buildTemplatePool (10-10)
src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts (1)
  • prefixVariables (41-67)
src/lib/domains/agentic/templates/_pool/builder.ts (2)
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (1)
  • TileData (12-20)
src/lib/domains/agentic/templates/_pool/types.ts (2)
  • TemplatePool (27-32)
  • PooledTemplate (15-22)
src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts (1)
src/lib/domains/agentic/templates/_pre-processor/index.ts (8)
  • ChildPath (26-26)
  • ChildRange (26-26)
  • ParsedChildRef (26-26)
  • parseChildRef (20-20)
  • parseChildPath (21-21)
  • parseChildRange (22-22)
  • formatChildPath (23-23)
  • isChildRef (24-24)
src/lib/domains/agentic/templates/_compiler/types.ts (3)
src/lib/domains/agentic/templates/_compiler/index.ts (2)
  • CompileContext (13-13)
  • CompileResult (13-13)
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (1)
  • TileData (12-20)
src/lib/domains/agentic/templates/_pool/types.ts (1)
  • TemplatePool (27-32)
src/lib/domains/agentic/templates/_compiler/_data-builder.ts (2)
src/lib/domains/agentic/templates/_internals/types.ts (2)
  • PromptData (17-43)
  • PromptDataTile (7-15)
src/lib/domains/agentic/templates/_pre-processor/index.ts (1)
  • TileData (17-17)
src/lib/domains/agentic/templates/_pool/types.ts (1)
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (1)
  • TileData (12-20)
src/lib/domains/agentic/templates/_internals/types.ts (4)
src/lib/domains/agentic/templates/_prompt-builder.ts (2)
  • PromptDataTile (30-30)
  • PromptData (30-30)
src/lib/domains/mapping/_objects/index.ts (1)
  • ItemTypeValue (9-9)
src/lib/domains/mapping/_objects/map-item.ts (1)
  • ItemTypeValue (69-69)
src/lib/domains/mapping/utils/index.ts (1)
  • ItemTypeValue (32-32)
src/lib/domains/agentic/templates/_compiler/index.ts (4)
src/lib/domains/agentic/templates/_pre-processor/index.ts (1)
  • TileData (17-17)
src/lib/domains/agentic/templates/_pool/index.ts (1)
  • TemplatePool (7-7)
src/lib/domains/agentic/templates/_compiler/types.ts (1)
  • CompileContext (13-24)
src/lib/domains/agentic/templates/_compiler/_render-children.ts (1)
  • expandRenderChildren (26-55)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build
  • GitHub Check: Tests - Phase 1 (Main Suite)
  • GitHub Check: ESLint
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (29)
src/lib/domains/agentic/templates/_compiler/types.ts (1)

1-34: LGTM!

The type definitions are clean, well-documented, and use absolute imports correctly. The interfaces appropriately capture the context and results for the two-phase template compilation pipeline.

src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts (2)

41-67: LGTM!

The prefixing logic correctly:

  • Skips empty path prefixes (early return)
  • Preserves reserved variables unchanged
  • Only prefixes tile-specific fields (title, content, preview, coords, itemType)
  • Maintains Mustache syntax with correct brace handling

75-88: LGTM!

The path formatting helpers are clean and straightforward. They produce consistent child reference syntax for the template system.

src/lib/domains/agentic/templates/_internals/types.ts (1)

17-43: LGTM!

The PromptData interface is well-structured with clear documentation. The required itemType field and optional USER-specific fields (discussion, userMessage) appropriately reflect the prompt building requirements.

src/lib/domains/agentic/templates/_pool/types.ts (1)

1-32: LGTM!

The template pool types are clean and well-documented:

  • PooledTemplate appropriately captures template metadata and nested children
  • TemplatePool provides a clear Map-based structure with optional fallback
  • Uses absolute imports correctly
src/lib/domains/agentic/templates/_pool/index.ts (1)

1-13: LGTM!

The barrel file cleanly consolidates the template pool public API:

  • Separates type exports from function exports
  • Uses absolute imports correctly
  • Provides a single entry point for consumers
src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts (2)

1-27: LGTM! Well-structured path parser module.

Clean separation of types and well-documented patterns. The union type ParsedChildRef provides good type discrimination for downstream consumers.


140-158: LGTM! Range parsing is correct.

The validation logic correctly handles:

  • NaN checks for parsed integers
  • Exclusion of 0 from valid directions
  • Boundary validation (-6 to 6)
src/lib/domains/agentic/templates/_compiler/_render-children.ts (3)

8-13: LGTM! Imports follow coding guidelines.

All imports correctly use absolute paths with the ~/lib prefix as required by the ESLint configuration.


26-55: LGTM! Clean implementation of child expansion.

The function correctly:

  • Limits recursion depth with a descriptive HTML comment marker
  • Handles missing tiles gracefully
  • Delegates child rendering with proper context propagation

74-114: LGTM! Child rendering logic is well-structured.

The implementation correctly:

  • Falls back gracefully when no template matches
  • Builds a fresh template pool for nested children
  • Increments recursion depth to prevent infinite loops
  • Prefixes variables after compilation to maintain correct scoping

One observation: the fallbackName parameter is used to build the next-level pool (line 98), ensuring consistent fallback behavior through nested renders.

src/lib/domains/agentic/templates/_pool/builder.ts (2)

7-8: LGTM! Imports follow coding guidelines.

Correctly uses absolute imports with ~/lib prefix.


19-54: LGTM! Pool building logic is correct.

The function properly:

  • Skips non-template children (those without content)
  • Derives template names using the helper function
  • Tracks the fallback template when matched
src/lib/domains/agentic/templates/_pre-processor/index.ts (2)

10-27: LGTM! Clean barrel exports.

The module correctly re-exports path-parser and resolver utilities, making them available from a single entry point for consumers.


34-44: LGTM! Well-designed error class.

The TemplateError includes all necessary context for debugging: template name, original parameters, and error chaining via cause.

src/lib/domains/agentic/templates/_compiler/_data-builder.ts (4)

8-9: LGTM! Imports follow coding guidelines.

Correctly uses absolute imports with ~/lib prefix.


17-30: LGTM! Well-typed Mustache data interface.

The interface correctly models the flat data structure with:

  • Typed task field
  • Index signature for dynamic child path keys

96-110: LGTM! Robust direction extraction.

The function correctly handles edge cases:

  • Missing path part (no : separator)
  • Empty direction string (returns undefined)
  • Non-numeric directions (returns undefined via isNaN check)

141-169: LGTM! Clean recursive flattening.

The implementation correctly:

  • Skips children without direction values
  • Builds path keys with proper comma-separated format
  • Handles missing optional fields with sensible defaults
  • Recursively processes all nested children
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (4)

7-8: LGTM! Imports follow coding guidelines.

Correctly uses absolute imports with ~/lib prefix for both mapping types and path parser.


12-20: LGTM! TileData interface updated correctly.

The changes align with the Templates as Tiles system:

  • itemType now uses ItemTypeValue for string-based custom types
  • direction field enables path-based child navigation

Based on learnings, tiles can have both subtask children (positive directions 1-6) and context children (negative directions -1 to -6).


107-124: LGTM! Path resolution is correctly implemented.

The function properly handles:

  • Multi-level nested paths via direction iteration
  • Missing children at any level (returns undefined)
  • Direction-based child lookup

154-169: No action needed. The function's behavior is correct and tested.

The test at line 131 of compiler.test.ts explicitly validates that getChildrenInRange(mockTile, -6, -1) returns children sorted in ascending order by direction ([-3, -2]), confirming that always sorting ascending regardless of parameter order is the intended design. The function documentation also states it returns results "sorted by direction."

src/lib/domains/agentic/templates/_compiler/_two-phase-render.ts (3)

8-12: LGTM! Imports are correct.

All imports use absolute paths with ~/lib prefix. Mustache is an external library import which is appropriate.


42-72: LGTM! Clean two-phase render implementation.

The function clearly separates:

  • Phase 1: Template compilation with pool-based template resolution
  • Phase 2: Mustache rendering with flattened tile data

Minor note: The g flag on line 65 (/\n\n$/g) is unnecessary since $ matches end-of-string only once, but it doesn't affect correctness.


82-99: LGTM! Utility functions are straightforward.

compileOnly provides a clean inspection API for debugging.

usesPoolBasedRendering is a simple heuristic - note it may match the tag in comments/strings, but this is acceptable for its intended use case.

src/lib/domains/agentic/templates/_compiler/index.ts (3)

8-11: LGTM: Imports follow absolute path guidelines.

All imports correctly use the ~/ prefix as required by the coding guidelines.


13-28: LGTM: Well-structured public API facade.

The re-exports provide a clean public interface for the compiler module, exposing types and utilities while keeping internal helpers private.


98-172: LGTM: Sound implementation with good defensive practices.

The implementation correctly:

  • Processes tags in reverse order to preserve string positions
  • Validates range parameters before proceeding
  • Uses optional chaining for optional fallback parameter
  • Resets regex lastIndex to prevent state issues

The non-null assertions (lines 109, 134, 143-144) are safe due to preceding validation checks.

Comment on lines +7 to +15
export interface PromptDataTile {
title: string
content?: string
preview?: string
coords: string
itemType?: ItemTypeValue
direction?: number // -6 to 6, for child lookup by direction
children?: PromptDataTile[]
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Consider unifying PromptDataTile with TileData.

The PromptDataTile interface has identical fields to TileData from src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (lines 11-19):

  • Both have: title, content?, preview?, coords, itemType?, direction?, children?

Having two identical type definitions creates maintenance overhead. Consider either:

  1. Re-exporting TileData from the pre-processor module, or
  2. Using TileData directly and removing PromptDataTile

This would ensure type consistency across the template system.

🤖 Prompt for AI Agents
In @src/lib/domains/agentic/templates/_internals/types.ts around lines 7 - 15,
The PromptDataTile interface duplicates TileData; replace the duplicate by
importing/re-exporting or aliasing TileData: remove PromptDataTile from
src/lib/domains/agentic/templates/_internals/types.ts and update any usages to
reference TileData (from
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts), or alternatively
export TileData from the pre-processor module and import it here so both modules
share the single TileData definition; ensure imports/exports and all references
to PromptDataTile are updated to the TileData symbol to keep types consistent.

…te context

- Add {{@RenderAncestors fallback='...'}} tag for ancestor pool dispatch
- Allow direction 0 in range for hexplan tiles (range=[0..0])
- Add template context support via template[-1], template[-2], etc.
- Add ancestor path syntax: ancestor[-1] (parent), ancestor[-2] (grandparent)
- Update System Task Template to use:
  - {{{template[-1].content}}} for hexrun-intro
  - {{@RenderAncestors fallback='ancestor'}} for ancestors
  - {{@RenderChildren range=[0..0] fallback='hexplan'}} for hexplan
- Add sub-templates: system (subtasks), ancestor, hexplan
- Add hexrun-intro as composed child of template tile
- Add plan for User Interlocutor Template migration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/lib/domains/agentic/templates/_prompt-builder.ts (1)

42-71: Type narrowing issue: exhaustive check may not work correctly.

After the !isBuiltInItemType(itemType) check on line 49, TypeScript doesn't narrow itemType to MapItemType for the switch statement. The type remains ItemTypeValue (which is MapItemType | string), making the exhaustive check on line 67 ineffective.

♻️ Proposed fix: Add type assertion after the type guard
 function _getTemplateByItemType(itemType: ItemTypeValue | null | undefined): string {
   if (itemType === null || itemType === undefined) {
     throw new Error(TEMPLATE_LOOKUP_ERRORS.noItemType)
   }
 
   // Handle custom (non-built-in) item types by using SYSTEM template
   // Custom types like "template" are treated as executable tiles
   if (!isBuiltInItemType(itemType)) {
     return SYSTEM_TEMPLATE
   }
+
+  // TypeScript now knows itemType is MapItemType
+  const builtInType: MapItemType = itemType
 
-  switch (itemType) {
+  switch (builtInType) {
     case MapItemType.SYSTEM:
       return SYSTEM_TEMPLATE
 
     case MapItemType.USER:
       return USER_TEMPLATE
 
     case MapItemType.ORGANIZATIONAL:
       throw new Error(TEMPLATE_LOOKUP_ERRORS.organizationalNotSupported)
 
     case MapItemType.CONTEXT:
       throw new Error(TEMPLATE_LOOKUP_ERRORS.contextNotImplemented)
 
     default: {
-      const exhaustiveCheck: never = itemType
+      const exhaustiveCheck: never = builtInType
       throw new Error(`Unknown itemType: ${String(exhaustiveCheck)}`)
     }
   }
 }
🤖 Fix all issues with AI agents
In @src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts:
- Around line 74-79: The ancestor compilation ignores recursion depth: when
compiling ancestor templates via compileTemplate and then calling
prefixAncestorVariables, increment and check the depth in the context (use
context.currentDepth and context.maxDepth) to prevent infinite recursion from
nested {{@RenderAncestors}}; e.g., create a shallow copy of context with
currentDepth: (context.currentDepth || 0) + 1, validate that newCurrentDepth <
context.maxDepth (or throw/skip compilation) before calling
compileTemplate(template.content, newContext), then pass the compiled result to
prefixAncestorVariables so the depth safeguard mirrors the RenderChildren
behavior.

In @src/lib/domains/agentic/templates/_compiler/_render-children.ts:
- Around line 80-87: The initial template lookup in _render-children.ts
currently calls findTemplateForItemType(context.templatePool, itemTypeStr) but
ignores fallbackName; update the code so the template is resolved with fallback
handling just like in _render-ancestors.ts — e.g., set template =
findTemplateForItemType(context.templatePool, itemTypeStr) ?? (fallbackName ?
findTemplateForItemType(context.templatePool, fallbackName) : undefined) —
ensuring you reference findTemplateForItemType, itemTypeStr, fallbackName and
context.templatePool when making the change.
🧹 Nitpick comments (9)
docs/plans/user-template-pool-dispatch.md (3)

17-22: Strengthen target state clarity with explicit benefits and constraints.

The target state correctly identifies the three key patterns (RenderChildren, template context, direction-0), but it would benefit from brief explanations of why each is important:

  • Why RenderChildren over hardcoded variables?
  • What does template context enable beyond current approach?
  • Why direction-0 for recent-history (parity with hexplan)?

This added clarity will help reviewers and implementers understand the rationale.


25-37: Task table is high-level; consider documenting interdependencies and success criteria.

Tasks 1–6 are clearly listed, but the table lacks:

  • Interdependencies (e.g., must task 1 complete before 2, or can they run in parallel?)
  • Success/acceptance criteria (e.g., "All sub-templates resolve correctly" or "Backward compatibility with existing userIntro calls")
  • Estimated effort or priority order
  • Reference to corresponding test coverage (the PR mentions 209+ tests; which ones apply here?)

This would help implementers prioritize and verify completion.


68-68: Document the design decision to keep discussion and userMessage as Mustache variables.

The note explains that these remain as Mustache variables but not why. Is this because:

  • They're session-level data, not persistently stored tiles?
  • They require special handling (e.g., escaping, formatting) that template tiles don't support?
  • It's a temporary backward-compatibility measure?

Documenting the rationale will help maintainers understand the boundary between tile-based and variable-based content.

src/lib/domains/agentic/templates/_system-template.ts (1)

30-34: Consider clarifying the relationship between these fields and the {{@HexPlan}} pre-processor tag.

The implementation follows existing patterns well. However, the documentation could be enhanced to explain:

  • How these fields relate to the {{@HexPlan}} tag expansion at line 63
  • What makes this HexPlan "simplified" compared to a full HexPlan
  • Whether the pre-processor uses hasHexplan to conditionally expand/remove the tag

This would help future maintainers understand the data flow from these fields → pre-processor → final template output.

📝 Example documentation enhancement
-  // Simplified HexPlan (for tile-based templates)
+  // Simplified HexPlan (for tile-based templates)
+  // The pre-processor uses these fields to expand the {{@HexPlan}} tag:
+  // - If hasHexplan is true, hexPlan content is injected at the {{@HexPlan}} location
+  // - hexplanCoords provides tile coordinates for the hexplan source
   hasHexplan: boolean
   hexplanCoords: string
   hexPlan: string
src/lib/domains/agentic/templates/_prompt-builder.ts (1)

192-194: Unnecessary type assertion.

The type assertion (MapItemType.USER as ItemTypeValue) on line 192 is unnecessary. MapItemType.USER is already assignable to ItemTypeValue since ItemTypeValue = MapItemType | string. The comparison should work directly: data.itemType === MapItemType.USER.

♻️ Simplify by removing the type assertion
   // Prepare template data based on item type
-  const templateData = data.itemType === (MapItemType.USER as ItemTypeValue)
+  const templateData = data.itemType === MapItemType.USER
     ? _prepareUserTemplateData(data)
     : _prepareSystemTemplateData(data)
src/lib/domains/agentic/templates/_pre-processor/index.ts (1)

147-170: Unreachable default case after template validation.

The _validateTemplateExists function already throws for unknown templates at line 97, making the default case at line 168 unreachable during normal execution. Consider either:

  1. Removing the default case and relying on TypeScript's exhaustiveness checking (add a type guard)
  2. Adding a comment explaining this is defensive programming

The current implementation is safe but the default case is dead code in practice.

♻️ Option: Use exhaustive switch with never type
 function _invokeTemplate(
   templateName: string,
   resolvedParams: ResolvedParams,
   context: TemplateContext,
   registry: TemplateRegistry,
   originalParams: ParsedParams
 ): string {
+  // Assert templateName is a valid registry key (already validated by _validateTemplateExists)
+  const validName = templateName as keyof TemplateRegistry
   switch (templateName) {
     case 'GenericTile':
       return registry.GenericTile(resolvedParams.item, resolvedParams.fields, resolvedParams.wrapper)
     case 'Folder':
       return registry.Folder(resolvedParams.item, resolvedParams.fields, resolvedParams.depth)
     case 'TileOrFolder':
       return registry.TileOrFolder(resolvedParams.item, resolvedParams.fields, resolvedParams.wrapper, resolvedParams.depth)
     case 'HexPlan':
       return registry.HexPlan(context.hexplanCoords, context.hexPlan, context.hexplanStatus, {
         mcpServerName: context.mcpServerName,
         isParentTile: context.isParentTile,
         taskCoords: context.task.coords,
       })
     default:
-      throw new TemplateError(`No handler for template "${templateName}"`, templateName, originalParams)
+      // This should be unreachable after _validateTemplateExists
+      const _exhaustiveCheck: never = validName
+      throw new TemplateError(`No handler for template "${templateName}"`, templateName, originalParams)
   }
 }
src/lib/domains/agentic/templates/_compiler/_render-children.ts (1)

89-93: Silently skipping children without direction may hide data issues.

Children without a direction field are silently skipped with an empty string. While this prevents errors, it could mask issues where children are missing required direction data. Consider logging a warning or adding observability for debugging template rendering issues.

src/lib/domains/agentic/templates/_compiler/index.ts (1)

157-164: Silent skip for malformed RenderChildren tags could mask template errors.

Tags matching {{@RenderChildren ...}} but lacking a valid range=[...] parameter are silently skipped via continue at line 163. This could hide authoring errors in templates.

Consider logging a warning or returning a visible placeholder (like the max-depth comment) to aid debugging.

♻️ Proposed enhancement for debugging
     // Parse range parameter
     const rangeMatch = RANGE_PATTERN.exec(params)
     if (!rangeMatch) {
+      // Could add: console.warn(`RenderChildren tag missing valid range parameter: ${match[0]}`)
       continue
     }
src/lib/domains/agentic/templates/_compiler/_data-builder.ts (1)

17-30: MustacheData type could be more precise.

The index signature [key: string]: {...} | MustacheData['task'] means TypeScript allows any string key, which loses type safety. The task property has a different shape (no preview or itemType) than dynamic entries.

Consider using a discriminated union or separate properties for better type safety, though this may require refactoring consumers.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28dfed6 and 8888ea6.

📒 Files selected for processing (13)
  • docs/plans/user-template-pool-dispatch.md
  • src/lib/domains/agentic/templates/__tests__/compiler.test.ts
  • src/lib/domains/agentic/templates/_compiler/_data-builder.ts
  • src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts
  • src/lib/domains/agentic/templates/_compiler/_render-children.ts
  • src/lib/domains/agentic/templates/_compiler/_two-phase-render.ts
  • src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts
  • src/lib/domains/agentic/templates/_compiler/index.ts
  • src/lib/domains/agentic/templates/_compiler/types.ts
  • src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
  • src/lib/domains/agentic/templates/_system-template.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/lib/domains/agentic/templates/tests/compiler.test.ts
  • src/lib/domains/agentic/templates/_compiler/_two-phase-render.ts
  • src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts
  • src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use absolute imports with ~/ prefix instead of relative imports (./ or ../), as enforced by ESLint no-restricted-imports rule

Files:

  • src/lib/domains/agentic/templates/_system-template.ts
  • src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts
  • src/lib/domains/agentic/templates/_compiler/_render-children.ts
  • src/lib/domains/agentic/templates/_compiler/types.ts
  • src/lib/domains/agentic/templates/_compiler/_data-builder.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
  • src/lib/domains/agentic/templates/_compiler/index.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
src/lib/domains/agentic/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use buildPrompt() function from src/lib/domains/agentic/services/prompt-executor.service.ts to deterministically generate XML prompts from tile coordinates

Files:

  • src/lib/domains/agentic/templates/_system-template.ts
  • src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts
  • src/lib/domains/agentic/templates/_compiler/_render-children.ts
  • src/lib/domains/agentic/templates/_compiler/types.ts
  • src/lib/domains/agentic/templates/_compiler/_data-builder.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
  • src/lib/domains/agentic/templates/_compiler/index.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
🧠 Learnings (2)
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Applies to src/lib/domains/agentic/**/*.{ts,tsx} : Use `buildPrompt()` function from `src/lib/domains/agentic/services/prompt-executor.service.ts` to deterministically generate XML prompts from tile coordinates

Applied to files:

  • src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts
  • src/lib/domains/agentic/templates/_compiler/types.ts
  • src/lib/domains/agentic/templates/_compiler/_data-builder.ts
  • src/lib/domains/agentic/templates/_prompt-builder.ts
  • src/lib/domains/agentic/templates/_compiler/index.ts
  • src/lib/domains/agentic/templates/_pre-processor/index.ts
📚 Learning: 2026-01-02T18:31:50.216Z
Learnt from: CR
Repo: Diplow/hexframe PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-02T18:31:50.216Z
Learning: Parent tiles have subtask children and orchestrate them; their content is context for human reviewers, not agent instructions

Applied to files:

  • docs/plans/user-template-pool-dispatch.md
🧬 Code graph analysis (7)
src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts (3)
src/lib/domains/agentic/templates/_compiler/index.ts (2)
  • CompileContext (14-14)
  • compileTemplate (71-87)
src/lib/domains/agentic/templates/_compiler/types.ts (1)
  • CompileContext (13-26)
src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts (1)
  • prefixAncestorVariables (102-124)
src/lib/domains/agentic/templates/_compiler/_render-children.ts (7)
src/lib/domains/agentic/templates/_compiler/index.ts (3)
  • CompileContext (14-14)
  • compileTemplate (71-87)
  • prefixVariables (15-15)
src/lib/domains/agentic/templates/_compiler/types.ts (1)
  • CompileContext (13-26)
src/lib/domains/agentic/templates/_pre-processor/index.ts (4)
  • getChildrenInRange (15-15)
  • TileData (17-17)
  • ChildPath (30-30)
  • resolveChildPath (13-13)
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (3)
  • getChildrenInRange (154-169)
  • TileData (12-20)
  • resolveChildPath (107-124)
src/lib/domains/agentic/templates/_pre-processor/_path-parser.ts (1)
  • ChildPath (17-20)
src/lib/domains/agentic/templates/_pool/builder.ts (2)
  • findTemplateForItemType (80-89)
  • buildTemplatePool (19-54)
src/lib/domains/agentic/templates/_compiler/_variable-prefixer.ts (1)
  • prefixVariables (42-68)
src/lib/domains/agentic/templates/_compiler/types.ts (3)
src/lib/domains/agentic/templates/_compiler/index.ts (2)
  • CompileContext (14-14)
  • CompileResult (14-14)
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (1)
  • TileData (12-20)
src/lib/domains/agentic/templates/_pool/types.ts (1)
  • TemplatePool (27-32)
src/lib/domains/agentic/templates/_compiler/_data-builder.ts (5)
src/lib/domains/agentic/templates/_compiler/index.ts (1)
  • MustacheData (20-20)
src/lib/domains/agentic/templates/_prompt-builder.ts (2)
  • PromptData (30-30)
  • PromptDataTile (30-30)
src/lib/domains/agentic/templates/_internals/types.ts (2)
  • PromptData (17-43)
  • PromptDataTile (7-15)
src/lib/domains/agentic/templates/_pre-processor/index.ts (1)
  • TileData (17-17)
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (1)
  • TileData (12-20)
src/lib/domains/agentic/templates/_prompt-builder.ts (4)
src/lib/domains/mapping/_objects/map-item.ts (1)
  • ItemTypeValue (69-69)
src/lib/domains/mapping/infrastructure/map-item/item-type-utils.ts (1)
  • isBuiltInItemType (34-39)
src/lib/domains/agentic/templates/_system-template.ts (1)
  • SYSTEM_TEMPLATE (42-63)
src/lib/domains/agentic/templates/_internals/utils.ts (1)
  • _hasContent (22-24)
src/lib/domains/agentic/templates/_compiler/index.ts (5)
src/lib/domains/agentic/templates/_pre-processor/index.ts (1)
  • TileData (17-17)
src/lib/domains/agentic/templates/_pool/types.ts (1)
  • TemplatePool (27-32)
src/lib/domains/agentic/templates/_compiler/types.ts (1)
  • CompileContext (13-26)
src/lib/domains/agentic/templates/_compiler/_render-children.ts (1)
  • expandRenderChildren (26-55)
src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts (1)
  • expandRenderAncestors (28-51)
src/lib/domains/agentic/templates/_pre-processor/index.ts (3)
src/lib/domains/agentic/templates/_pre-processor/_resolver.ts (2)
  • resolveItemReference (60-98)
  • TemplateContext (22-43)
src/lib/domains/agentic/templates/_pre-processor/_parser.ts (1)
  • ParsedParams (16-22)
src/lib/domains/agentic/templates/_templates/index.ts (1)
  • TemplateRegistry (15-41)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests - Phase 2 (React Components)
  • GitHub Check: Build
  • GitHub Check: Tests - Phase 1 (Main Suite)
🔇 Additional comments (18)
docs/plans/user-template-pool-dispatch.md (4)

1-4: Clear goal statement, but verify coordinate references with PR implementation.

The goal is well-articulated: mirror the System Task Template's pool-based dispatch pattern. However, ensure the coordinate references (User Template at D1i4gEqbi01JWS2F6I7GUN8ekRiU2mjK,0:1,2,2 and System Task Template at [1,2,1]) match the actual tile coordinates in the implementation. The notation mixing absolute (D1i...) and shorthand relative ([1,2,1]) could be confusing if not clarified.


40-68: Confirm range syntax and fallback semantics match implementation.

The template content uses RenderChildren with ranges ([-6..-1], [1..6], [0..0]) and fallbacks ('generic', 'section', 'recent-history'). Verify:

  • Are range bounds inclusive on both ends? (e.g., does [-6..-1] include both -6 and -1?)
  • How are fallback names resolved? (e.g., is 'generic' a tile coordinate or a template name?)
  • What happens if a child at a position does not exist—does fallback apply per-child, or only if the entire range is empty?

These details are critical for correct rendering behavior.


71-105: Verify sub-template coordinate notation and preview field availability.

Sub-templates are defined at relative coordinates [1][5]. Confirm:

  • Are these coordinates relative to the parent User Interlocutor Template, or absolute within the tile map?
  • In the section sub-template (line 93–95), preview is referenced as a field. Is this:
    • A built-in field on structural children?
    • A custom field that must be set during seed/migration?
    • Populated dynamically by the template resolver?
  • Consistency check: the generic sub-template (lines 86–89) uses title and coords, while others add preview or content. Are these fields always available, or template-specific?

This will prevent mismatches between template content and available data.


108-122: Clarify how user-intro composed child (-1) content is persisted and updated.

The user-intro content is provided as a reference. Confirm:

  • Is this content seeded into tile storage during migration (as a composed child -1)?
  • How are future updates to the user-intro text handled? (Semver bump, migration, hardcoded in compiler?)
  • Is this specific text meant to be customized per user, workspace, or always identical?

This affects how the document should guide the implementation team.

src/lib/domains/agentic/templates/_prompt-builder.ts (2)

29-31: LGTM: Backward compatibility maintained.

The re-export pattern for PromptDataTile and PromptData ensures backward compatibility while centralizing type definitions in _internals/types. This is good practice when refactoring internal implementations.


47-51: LGTM: Custom item types handled correctly.

Non-built-in item types (like "template") correctly fall back to SYSTEM_TEMPLATE, enabling custom item types to be executable tiles.

src/lib/domains/agentic/templates/_pre-processor/index.ts (2)

7-35: LGTM!

The expanded exports surface is well-organized, grouping resolver utilities, path-parser functions, and their corresponding types. The use of absolute imports with ~/ prefix follows the coding guidelines.


91-113: LGTM - Clean refactoring with improved error handling.

The refactored _expandTag function properly delegates to helper functions and wraps non-TemplateError exceptions into TemplateError with cause preservation.

src/lib/domains/agentic/templates/_compiler/types.ts (1)

1-36: LGTM!

Well-structured type definitions for the two-phase template compilation. The JSDoc comments clearly document each field's purpose, and the types properly reference TileData and TemplatePool from their respective modules.

src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts (1)

28-51: LGTM - Correct ancestor indexing implementation.

The negative index calculation correctly maps array position to ancestor distance:

  • arrayIndex 0 (root) with ancestors.length=3-3
  • arrayIndex 2 (parent) with ancestors.length=3-1

This aligns with the documented indexing convention.

src/lib/domains/agentic/templates/_compiler/_render-children.ts (1)

26-55: LGTM - Solid recursion protection and child rendering logic.

The depth check at lines 33-35 prevents runaway recursion, and the early returns for missing tiles/children are appropriate.

src/lib/domains/agentic/templates/_compiler/index.ts (3)

14-29: LGTM - Clean re-export surface.

The index properly consolidates and re-exports types, utilities, and functions from internal modules, providing a cohesive public API for the compiler.


71-87: LGTM - Well-designed public entry point.

The compileTemplate function provides a clean API with sensible defaults (empty ancestors, maxDepth of 10), and properly initializes the CompileContext.


108-133: LGTM - Correct reverse-order tag expansion.

The implementation correctly:

  1. Finds all tags of both types
  2. Sorts by position descending to preserve string indices during replacement
  3. Expands each tag and splices into the result

This is a robust approach for in-place template expansion.

src/lib/domains/agentic/templates/_compiler/_data-builder.ts (4)

40-62: LGTM - Clean PromptData to TileData conversion.

The function properly merges composed and structural children into a unified TileData structure, delegating direction inference to the helper.


113-127: Edge case: Empty path part returns undefined correctly.

The function handles edge cases well:

  • Missing colon → returns undefined
  • Empty directions → isNaN check catches it
  • Single direction → works correctly

One minor note: if pathPart is an empty string after the colon (e.g., "userId,groupId:"), the split produces [""] and parseInt("", 10) returns NaN, which is correctly handled.


158-187: LGTM - Correct recursive flattening with path tracking.

The recursive flattening properly:

  1. Builds composite path keys like child[-3,-1]
  2. Skips children without direction (consistent with rendering logic)
  3. Recursively processes nested children

193-214: LGTM - Correct ancestor negative indexing.

The ancestor flattening correctly mirrors the indexing convention used in _render-ancestors.ts, ensuring consistency between template compilation and data building.

Comment on lines +74 to +79

// Compile the sub-template (no recursion for ancestors - they're flat)
const compiledSubTemplate = compileTemplate(template.content, context)

// Prefix variables with the ancestor index
return prefixAncestorVariables(compiledSubTemplate, negativeIndex)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file in question to understand the compilation logic
cat -n src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts | head -100

Repository: Diplow/hexframe

Length of output: 3655


🏁 Script executed:

# Search for RenderAncestors usage across the codebase
rg -n "RenderAncestors" --type ts --type tsx -g '!*.test.ts' -A 3 -B 3 | head -80

Repository: Diplow/hexframe

Length of output: 86


🏁 Script executed:

# Look for the compileTemplate function to understand context handling
rg -n "compileTemplate|buildPrompt" --type ts --type tsx -g '!*.test.ts' | head -40

Repository: Diplow/hexframe

Length of output: 86


🏁 Script executed:

# Check if there are any depth limits or safeguards in the compilation process
rg -n "depth|recursion|loop|infinite" --type ts --type tsx src/lib/domains/agentic/templates/ -g '!*.test.ts' | head -30

Repository: Diplow/hexframe

Length of output: 86


🏁 Script executed:

# Find the compileTemplate function definition
fd -n "_compiler" --type f src/lib/domains/agentic/templates/ | head -20

Repository: Diplow/hexframe

Length of output: 289


🏁 Script executed:

# Look for the main compiler file
ls -la src/lib/domains/agentic/templates/_compiler/

Repository: Diplow/hexframe

Length of output: 632


🏁 Script executed:

# Search for how context is used in compilation
rg -n "context\.ancestors" src/lib/domains/agentic/templates/ --type ts | head -20

Repository: Diplow/hexframe

Length of output: 160


🏁 Script executed:

# Search for RenderAncestors tag parsing/handling
rg -n "RenderAncestors|@RenderAncestors" src/lib/domains/agentic/templates/ --type ts | head -40

Repository: Diplow/hexframe

Length of output: 2214


🏁 Script executed:

# Read the main compiler file to understand full compilation flow
cat -n src/lib/domains/agentic/templates/_compiler/index.ts

Repository: Diplow/hexframe

Length of output: 8333


🏁 Script executed:

# Look at the two-phase render file for context
cat -n src/lib/domains/agentic/templates/_compiler/_two-phase-render.ts

Repository: Diplow/hexframe

Length of output: 4391


🏁 Script executed:

# Check the types to understand CompileContext
cat -n src/lib/domains/agentic/templates/_compiler/types.ts

Repository: Diplow/hexframe

Length of output: 1315


🏁 Script executed:

# Check if currentDepth is being checked/incremented anywhere in the compilation
rg -n "currentDepth|maxDepth" src/lib/domains/agentic/templates/_compiler/ --type ts

Repository: Diplow/hexframe

Length of output: 690


🏁 Script executed:

# Look at _render-children.ts to see how it handles recursion
cat -n src/lib/domains/agentic/templates/_compiler/_render-children.ts

Repository: Diplow/hexframe

Length of output: 4711


🏁 Script executed:

# Search for any depth checking or safeguards
rg -n "depth|maxDepth|currentDepth" src/lib/domains/agentic/templates/ --type ts

Repository: Diplow/hexframe

Length of output: 2873


🏁 Script executed:

# Check if expandRenderAncestors increments currentDepth
cat -n src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts | grep -A 30 "function _renderAncestor"

Repository: Diplow/hexframe

Length of output: 1079


🏁 Script executed:

# Search for any tests that verify RenderAncestors behavior
fd -n "test" src/lib/domains/agentic/templates/ --type f | head -10

Repository: Diplow/hexframe

Length of output: 289


🏁 Script executed:

# Look for any tests specifically testing RenderAncestors with nested templates
rg -n "RenderAncestors" src/lib/domains/agentic/templates/__tests__/ --type ts -A 5 -B 5 | head -60

Repository: Diplow/hexframe

Length of output: 41


🏁 Script executed:

# Verify the context is truly unchanged - check what compileTemplate expects
cat -n src/lib/domains/agentic/templates/_compiler/index.ts | sed -n '71,87p'

Repository: Diplow/hexframe

Length of output: 520


🏁 Script executed:

# Double-check if currentDepth is ever incremented in ancestor rendering
rg -n "currentDepth" src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts

Repository: Diplow/hexframe

Length of output: 41


Apply depth-limiting safeguards to RenderAncestors tag compilation to prevent infinite recursion.

The ancestor template is compiled with an unchanged context object and currentDepth is never incremented (unlike RenderChildren at line 107 of _render-children.ts). If an ancestor template contains a nested {{@RenderAncestors}} tag, compilation will recurse infinitely since the depth check at context.currentDepth >= context.maxDepth is never evaluated for ancestors.

The comment "no recursion for ancestors - they're flat" (line 75) states the design intent but isn't enforced by code. Either increment currentDepth when compiling ancestor templates and add a depth check, or validate/restrict ancestor templates from containing RenderAncestors tags.

🤖 Prompt for AI Agents
In @src/lib/domains/agentic/templates/_compiler/_render-ancestors.ts around
lines 74 - 79, The ancestor compilation ignores recursion depth: when compiling
ancestor templates via compileTemplate and then calling prefixAncestorVariables,
increment and check the depth in the context (use context.currentDepth and
context.maxDepth) to prevent infinite recursion from nested
{{@RenderAncestors}}; e.g., create a shallow copy of context with currentDepth:
(context.currentDepth || 0) + 1, validate that newCurrentDepth <
context.maxDepth (or throw/skip compilation) before calling
compileTemplate(template.content, newContext), then pass the compiled result to
prefixAncestorVariables so the depth safeguard mirrors the RenderChildren
behavior.

Comment on lines +80 to +87
// Find matching template in pool
const itemTypeStr = child.itemType ? String(child.itemType) : undefined
const template = findTemplateForItemType(context.templatePool, itemTypeStr)

if (!template) {
// No template found, skip this child
return ''
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

The fallbackName parameter is not used for initial template lookup.

The findTemplateForItemType call at line 82 only passes itemTypeStr, but fallbackName is only used when building the next-level pool at line 98. If the intent is to use fallbackName as a fallback when no template matches the child's itemType, this lookup should also consider it.

Compare with _render-ancestors.ts lines 67-68 which explicitly checks the fallback:

const template = findTemplateForItemType(context.templatePool, itemTypeStr)
  ?? (fallbackName ? findTemplateForItemType(context.templatePool, fallbackName) : undefined)
🐛 Proposed fix for consistent fallback handling
   // Find matching template in pool
   const itemTypeStr = child.itemType ? String(child.itemType) : undefined
-  const template = findTemplateForItemType(context.templatePool, itemTypeStr)
+  const template = findTemplateForItemType(context.templatePool, itemTypeStr)
+    ?? (fallbackName ? findTemplateForItemType(context.templatePool, fallbackName) : undefined)

   if (!template) {
     // No template found, skip this child
     return ''
   }
🤖 Prompt for AI Agents
In @src/lib/domains/agentic/templates/_compiler/_render-children.ts around lines
80 - 87, The initial template lookup in _render-children.ts currently calls
findTemplateForItemType(context.templatePool, itemTypeStr) but ignores
fallbackName; update the code so the template is resolved with fallback handling
just like in _render-ancestors.ts — e.g., set template =
findTemplateForItemType(context.templatePool, itemTypeStr) ?? (fallbackName ?
findTemplateForItemType(context.templatePool, fallbackName) : undefined) —
ensuring you reference findTemplateForItemType, itemTypeStr, fallbackName and
context.templatePool when making the change.

Remove the restriction that prevented user tiles (tiles with empty path)
from being compose expanded. This enables users to add composed children
to their root tile, which is now relevant for the template system.

Changes:
- Remove isUserTile check from canShowComposition in MapUI.tsx
- Remove isUserTile check from DynamicFrameCore.tsx
- Remove isUserTile check from BaseFrame.tsx
- Update test to expect user tiles can now show composition

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @src/app/map/Canvas/DynamicFrameCore.tsx:
- Line 532: The DynamicFrameCore currently sets canShowComposition =
isCompositionExpanded && slotScale > 1 without checking for existing composition
children, causing behavior to differ from BaseFrame; update DynamicFrameCore to
compute hasComposition via _hasCompositionChild(center, mapItems) and change
canShowComposition to isCompositionExpanded && hasComposition && slotScale > 1
(and update or remove the "show direction 0 child (or empty)" comment to reflect
the aligned behavior). Ensure you reference the same helper
(_hasCompositionChild) and variables (isCompositionExpanded, slotScale, center,
mapItems) when making the change.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8888ea6 and cc4d2c1.

📒 Files selected for processing (4)
  • src/app/map/Canvas/DynamicFrameCore.tsx
  • src/app/map/Canvas/Tile/Base/BaseFrame.tsx
  • src/app/map/Canvas/__tests__/TileContextMenu-composition.test.tsx
  • src/app/map/_components/MapUI.tsx
💤 Files with no reviewable changes (1)
  • src/app/map/_components/MapUI.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always use absolute imports with ~/ prefix instead of relative imports (./ or ../), as enforced by ESLint no-restricted-imports rule

Files:

  • src/app/map/Canvas/__tests__/TileContextMenu-composition.test.tsx
  • src/app/map/Canvas/DynamicFrameCore.tsx
  • src/app/map/Canvas/Tile/Base/BaseFrame.tsx
**/*.{test,spec}.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use Vitest for tests (not Jest)

Files:

  • src/app/map/Canvas/__tests__/TileContextMenu-composition.test.tsx
src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

src/app/**/*.{ts,tsx}: Use Next.js 15 App Router with progressive enhancement, following Static → Progressive → Dynamic component patterns
Use localStorage caching for performance optimization in frontend code

Files:

  • src/app/map/Canvas/__tests__/TileContextMenu-composition.test.tsx
  • src/app/map/Canvas/DynamicFrameCore.tsx
  • src/app/map/Canvas/Tile/Base/BaseFrame.tsx
🧬 Code graph analysis (1)
src/app/map/Canvas/Tile/Base/BaseFrame.tsx (1)
src/lib/domains/mapping/services/_item-services/_item-query.service.ts (1)
  • hasComposition (62-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Tests - Phase 1 (Main Suite)
  • GitHub Check: Build
  • GitHub Check: Tests - Phase 2 (React Components)
  • GitHub Check: ESLint
🔇 Additional comments (2)
src/app/map/Canvas/Tile/Base/BaseFrame.tsx (1)

43-43: LGTM! Composition logic correctly enables user tiles.

The simplified condition properly removes the user tile restriction as intended by the PR objectives, while maintaining essential checks for expansion state, composition child existence, and sufficient scale.

src/app/map/Canvas/__tests__/TileContextMenu-composition.test.tsx (1)

189-220: LGTM! Test correctly validates user tile composition behavior.

The test update properly reflects the new capability for user tiles (empty path) to display composition when canShowComposition is true. The test data and expectations are clear and correctly structured.

const isCompositionExpanded = props.isCompositionExpanded ?? false;
const isUserTile = item.metadata.coordinates.path.length === 0;
const canShowComposition = isCompositionExpanded && slotScale > 1 && !isUserTile;
const canShowComposition = isCompositionExpanded && slotScale > 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for hasComposition usage patterns in composition-related code
rg -nP --type=tsx --type=ts -C3 '\bhasComposition\b' -g '!**/node_modules/**' -g '!**/*.test.*'

# Search for canShowComposition patterns to see consistency
rg -nP --type=tsx --type=ts -C2 '\bcanShowComposition\s*=' -g '!**/node_modules/**' -g '!**/*.test.*'

Repository: Diplow/hexframe

Length of output: 114


🏁 Script executed:

# First, check if the files exist and get their structure
fd -t f -e tsx -e ts | grep -E '(BaseFrame|DynamicFrameCore)' | head -20

# Get line counts to understand file sizes
wc -l src/app/map/Canvas/BaseFrame.tsx src/app/map/Canvas/DynamicFrameCore.tsx 2>/dev/null || echo "Files not found yet"

Repository: Diplow/hexframe

Length of output: 289


🏁 Script executed:

# Let's search more broadly for these files
fd -t f 'BaseFrame\.(tsx|ts)' 2>/dev/null
fd -t f 'DynamicFrameCore\.(tsx|ts)' 2>/dev/null

Repository: Diplow/hexframe

Length of output: 141


🏁 Script executed:

# Read BaseFrame.tsx around line 43 to see the composition logic
sed -n '35,55p' src/app/map/Canvas/Tile/Base/BaseFrame.tsx

Repository: Diplow/hexframe

Length of output: 720


🏁 Script executed:

# Read DynamicFrameCore.tsx around line 532 to see the composition logic
sed -n '525,540p' src/app/map/Canvas/DynamicFrameCore.tsx

Repository: Diplow/hexframe

Length of output: 755


🏁 Script executed:

# Search for hasComposition in both files
echo "=== hasComposition in BaseFrame.tsx ===" && rg 'hasComposition' src/app/map/Canvas/Tile/Base/BaseFrame.tsx -n -B2 -A2
echo "=== hasComposition in DynamicFrameCore.tsx ===" && rg 'hasComposition' src/app/map/Canvas/DynamicFrameCore.tsx -n -B2 -A2

Repository: Diplow/hexframe

Length of output: 646


🏁 Script executed:

# Search for _hasCompositionChild usage in DynamicFrameCore
rg '_hasCompositionChild' src/app/map/Canvas/DynamicFrameCore.tsx -n -B3 -A3

Repository: Diplow/hexframe

Length of output: 41


🏁 Script executed:

# Look at more context around line 532 in DynamicFrameCore to understand the composition handling
sed -n '520,560p' src/app/map/Canvas/DynamicFrameCore.tsx

Repository: Diplow/hexframe

Length of output: 1536


🏁 Script executed:

# Check if CompositionFrame is imported and how it's used
rg 'CompositionFrame' src/app/map/Canvas/DynamicFrameCore.tsx -n -B2 -A2

Repository: Diplow/hexframe

Length of output: 477


🏁 Script executed:

# Search for composition-related logic in DynamicFrameCore
rg 'composition|Composition' src/app/map/Canvas/DynamicFrameCore.tsx -n -i -C2

Repository: Diplow/hexframe

Length of output: 3273


Inconsistency confirmed: BaseFrame and DynamicFrameCore handle composition display differently.

BaseFrame.tsx (line 43) checks hasComposition before rendering composition:

const hasComposition = _hasCompositionChild(center, mapItems);
const canShowComposition = isExpanded && hasComposition && scale > 1;

DynamicFrameCore.tsx (line 532) omits this check:

const canShowComposition = isCompositionExpanded && slotScale > 1;

DynamicFrameCore intentionally renders empty composition frames (see comment "show direction 0 child (or empty)" at line 535), allowing users to populate composition slots. This is architecturally different from BaseFrame, which only renders composition when children exist.

Clarify whether this difference is intentional or should be aligned with BaseFrame's validation logic.

🤖 Prompt for AI Agents
In @src/app/map/Canvas/DynamicFrameCore.tsx at line 532, The DynamicFrameCore
currently sets canShowComposition = isCompositionExpanded && slotScale > 1
without checking for existing composition children, causing behavior to differ
from BaseFrame; update DynamicFrameCore to compute hasComposition via
_hasCompositionChild(center, mapItems) and change canShowComposition to
isCompositionExpanded && hasComposition && slotScale > 1 (and update or remove
the "show direction 0 child (or empty)" comment to reflect the aligned
behavior). Ensure you reference the same helper (_hasCompositionChild) and
variables (isCompositionExpanded, slotScale, center, mapItems) when making the
change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant