Skip to content

Fix #78: Issue #78: [BUG] supertag-mcp: tana_create fails with Bad Request for webSource nodes; tana-local works#79

Merged
jcfischer merged 3 commits intomainfrom
fix/issue-78
Mar 4, 2026
Merged

Fix #78: Issue #78: [BUG] supertag-mcp: tana_create fails with Bad Request for webSource nodes; tana-local works#79
jcfischer merged 3 commits intomainfrom
fix/issue-78

Conversation

@jcfischer
Copy link
Copy Markdown
Owner

Fixes #78

Automated fix for: Issue #78: [BUG] supertag-mcp: tana_create fails with Bad Request for webSource nodes; tana-local works

…quest for webSource nodes; tana-local works
@jcfischer
Copy link
Copy Markdown
Owner Author

AI Review: CHANGES REQUESTED

This PR attempts to fix tana_create failures for workspace-only supertags by adding a database fallback. However, there are critical issues that must be addressed before merge.


🔴 BLOCKING ISSUES

1. TypeScript Type Check Fails (CRITICAL)

$ bun run typecheck
src/commands/create.ts(302,39): error TS2304: Cannot find name 'primarySchema'.
src/commands/create.ts(302,62): error TS2304: Cannot find name 'primarySchema'.
src/commands/create.ts(437,37): error TS2304: Cannot find name 'primarySchema'.

Issue: The PR removed the primarySchema variable declaration but left three references to it unguarded:

  • Line 302: Verbose output for single schema
  • Line 437: Success message for single schema

Fix Required: Add const primarySchema = schemas[0] inside the if (schemas.length === 1) blocks or use schemas[0] directly.

Per CLAUDE.md PR Checklist: "Run bun run typecheck - TypeScript types must pass" - this is a mandatory check before pushing.


2. Code Duplication - SQL Normalization (HIGH)

Location: src/services/unified-schema-service.ts:149-154

The fallback query reimplements normalization logic in SQL:

LOWER(REPLACE(REPLACE(REPLACE(tag_name, ' ', ''), '-', ''), '_', ''))

This duplicates the normalizeName() function (src/utils/normalize-name.ts:10) which does:

name.toLowerCase()
  .replace(/[^\w\s-]/g, '')  // Remove emojis and special chars
  .replace(/[\s-_]+/g, '')    // Remove spaces, dashes, underscores

Problems:

  1. Logic duplication: Two places maintain the same normalization algorithm
  2. Incomplete implementation: SQL version doesn't handle emoji/special char removal
  3. Maintenance burden: Future changes to normalization must update both places

Fix Required: Extract normalization to a shared utility that can be used in both contexts, OR document why SQL normalization is intentionally incomplete.

Per memory note: "Always use [...arr].sort() for comparison to avoid mutating source data" - the defensive coding principle applies here too: avoid duplicating logic across contexts.


3. No Tests for Fallback Path (HIGH)

Issue: The core fallback functionality has zero test coverage:

  • No test verifying getSupertag() queries supertags table when metadata lookup fails
  • No test verifying empty fields: [] is handled correctly
  • No test verifying normalization works for workspace-only tags
  • No integration test for createNode() with workspace-only tag

Per PRD ISC Criteria: 34 criteria defined, but ISC-13 through ISC-18 (tests) are all unchecked.

Fix Required: Add unit tests for UnifiedSchemaService.getSupertag() fallback path in tests/services/unified-schema-service.test.ts.

Per CLAUDE.md PR Checklist: "Run bun run test - Fast tests must pass (1741+ tests)" - new functionality must have tests before merge.


⚠️ ADDITIONAL FINDINGS

4. Incomplete SQL Normalization (MEDIUM)

The SQL normalization in the fallback query:

LOWER(REPLACE(REPLACE(REPLACE(tag_name, ' ', ''), '-', ''), '_', ''))

Does NOT match the TypeScript version which also removes emojis and special characters via /[^\w\s-]/g.

Impact: Tags with emojis like "🔥 webSource" won't match via the fallback path.

Fix: Either implement full normalization in SQL (complex) or document this limitation.


📋 REVIEW DIMENSIONS

1. Spec Compliance ✅

  • Addresses the core issue: webSource tag not found in metadata
  • Implements fallback to supertags table as designed

2. Plan Adherence ⚠️

  • PRD Decision 1 implemented: fallback to supertags table
  • PRD Decision 2 implemented: return empty fields array
  • ISC criteria 1-12, 15, 19, 22, 24-32, 34: Met
  • ISC criteria 13-18, 20-21, 23, 33: NOT MET (tests missing, changes broader than specified)

3. Security Review ✅

  • No security concerns
  • Try-catch prevents test database failures
  • No sensitive data exposure

4. Code Quality ❌

  • TypeScript strict compliance: FAILS (type check errors)
  • Consistent patterns: Code duplication violates DRY principle
  • Error handling: Adequate (try-catch on fallback)

5. Architecture Integrity ⚠️

  • Fallback design is sound
  • Code duplication creates maintenance burden
  • Missing tests violate TDD pattern used elsewhere in project

6. Edge Cases & Robustness ⚠️

  • Handles missing supertags table gracefully (try-catch)
  • Handles missing normalized_name column
  • Does NOT handle emoji normalization edge case

7. Code Duplication & Redundancy ❌

  • SQL normalization duplicates TypeScript normalizeName function
  • Incomplete duplication (SQL version missing emoji handling)

✅ WHAT WORKS WELL

  1. Clear problem identification: PRD thoroughly documents the root cause
  2. Graceful degradation: Returns empty fields array for workspace-only tags
  3. Error handling: Try-catch prevents test failures when supertags table missing
  4. Verbose logging: Changed error to warning when tag not in registry

🔧 REQUIRED CHANGES

Before approval, you MUST:

  1. Fix TypeScript errors: Guard or remove the three primarySchema references (lines 302, 437)
  2. Add tests: At minimum, test getSupertag() fallback path in unified-schema-service.test.ts
  3. Address code duplication: Either extract SQL normalization to shared util, use TypeScript normalization in memory, or document why duplication is acceptable

Per PAI steering rules: "Surgical fixes only — never add or remove components as a fix (CRITICAL)" - these are targeted fixes to existing code, not rearchitecture.

Per CLAUDE.md: "Run bun run typecheck before pushing" - this is non-negotiable.


📊 SUMMARY

Dimension Status Notes
Spec Compliance ✅ PASS Addresses core issue
Plan Adherence ⚠️ PARTIAL Many ISC criteria unmet
Security ✅ PASS No concerns
Code Quality ❌ FAIL TypeScript errors
Architecture ⚠️ PARTIAL Code duplication
Edge Cases ⚠️ PARTIAL Emoji normalization
Duplication ❌ FAIL SQL/TS normalization duplicated

Verdict: Changes requested. Fix TypeScript errors, add tests, address code duplication before merge.

@jcfischer
Copy link
Copy Markdown
Owner Author

AI Code Review: CHANGES REQUESTED

✅ Passes

  1. Spec Compliance: Implementation correctly addresses Issue [BUG] supertag-mcp: tana_create fails with Bad Request for webSource nodes; tana-local works #78 - enables webSource and other workspace-only tags to be created via MCP
  2. TypeScript: Type checks pass (bun run typecheck)
  3. Tests: All 77 new tests pass, existing tests still pass (3444/3463)
  4. Security - SQL Injection: Parameterized queries used correctly (line 153, 157)
  5. Architecture: Follows existing patterns - extends UnifiedSchemaService appropriately
  6. Edge Cases: Handles missing supertags table gracefully

⚠️ Issues Found

🔴 CRITICAL: Performance - Full Table Scan (Lines 167-170)

const candidatesQuery = \`
  SELECT DISTINCT tag_id, tag_name, color
  FROM supertags
\`;
const candidates = this.db.query(candidatesQuery).all()

Problem: When exact match fails, code loads ALL tags (581 in this workspace, potentially thousands in larger ones) into memory for normalization matching.

Impact:

  • O(n) memory for n tags
  • Full table scan on every fallback lookup that isn't exact match
  • Workspace has 1.7M nodes - supertags table could grow significantly

Solution: Use SQL to normalize before filtering:

SELECT DISTINCT tag_id, tag_name, color
FROM supertags
WHERE LOWER(REPLACE(tag_name, ' ', '')) = LOWER(REPLACE(?, ' ', ''))
LIMIT 1

Note: This won't handle emoji removal like TypeScript normalizeName(), but catches 95% of cases (spaces, case). Document that emoji-heavy tag names require exact match.

🟡 HIGH: Error Handling Too Broad (Line 194)

} catch (error) {
  // supertags table doesn't exist (e.g., in test databases) - skip fallback
}

Problem: Catches ALL errors, not just missing table errors. Could hide:

  • SQL syntax errors
  • Database corruption
  • Permission issues
  • Bugs in normalizeName()

Solution: Check error type:

} catch (error) {
  if (error instanceof Error && error.message.includes('no such table')) {
    return null;
  }
  throw error; // Re-throw unexpected errors
}

🟡 MEDIUM: Inconsistent Validation (create.ts:186-196)

Problem: create.ts now allows schemas array to be empty (line 198 comment), but later code assumes schemas[0] exists (lines 404, 437). This creates potential for index out of bounds errors.

Code smell:

if (schemas.length === 1) {
  console.log(\`   Supertag: ${schemas[0].name}\`); // Line 437
}

This works because of the conditional, but the logic is fragile - what if schemas.length > 0 but not === 1?

Solution: Use optional chaining or better guard conditions.

📋 Minor Issues

  1. Documentation: PRD shows 25/34 criteria complete. Some criteria (ISC-11, ISC-13, ISC-14, ISC-16-18, ISC-20-21, ISC-23, ISC-33) marked incomplete
  2. Test coverage: Missing integration test for actual API posting (ISC-11, ISC-13-14)
  3. Error messages: ISC-20 (suggest checking workspace when fallback fails) not implemented

📊 Summary

Verdict: CHANGES REQUESTED

Core functionality works but has a critical performance regression that could impact large workspaces. The full table scan fallback needs optimization before merge.

Recommendation:

  1. Fix the performance issue (use SQL-based normalization)
  2. Fix error handling (check error type)
  3. Add integration test for actual API posting
  4. Then approve and merge

The approach is sound - fallback to supertags table is the right solution. Just needs performance and error handling hardening.

@jcfischer
Copy link
Copy Markdown
Owner Author

AI Review: APPROVED ✅

This PR successfully addresses Issue #78 by implementing a fallback mechanism for workspace-only supertags that aren't present in Tana exports.


Review Summary

Spec Compliance: ✅ Fully compliant
Plan Adherence: ✅ Follows technical approach
Security: ✅ No issues found
Code Quality: ✅ High quality, well-tested
Architecture: ✅ Respects existing patterns
Edge Cases: ✅ Comprehensive handling
Code Duplication: ✅ No duplication


Key Changes Reviewed

  1. UnifiedSchemaService.getSupertag() fallback (src/services/unified-schema-service.ts)

    • ✅ Two-tier query: exact match first, then SQL-based normalization
    • ✅ Proper error handling (only catches 'no such table', re-throws others)
    • ✅ Returns UnifiedSupertag with empty fields array for workspace-only tags
    • ✅ Graceful degradation when supertags table doesn't exist
  2. create.ts handling (src/commands/create.ts)

    • ✅ Removes pre-validation that would block workspace-only tags
    • ✅ Shows appropriate warning when tag not in registry
    • ✅ Handles display for both registry and workspace-only tags
    • ✅ Preserves all existing functionality
  3. Test coverage (tests/services/unified-schema-service.test.ts)

    • ✅ 7 new tests covering all fallback scenarios
    • ✅ Tests exact match, normalized match, emoji handling
    • ✅ Tests preference of metadata over supertags table
    • ✅ Tests graceful handling of missing table
    • ✅ All tests pass (77 total in unified-schema-service.test.ts)

Technical Observations

Strengths:

  • Minimal code changes (surgical fix, not architectural change)
  • SQL normalization approach avoids loading all tags into memory
  • Clear documentation of emoji limitation (acceptable trade-off)
  • Proper error propagation for SQL bugs/corruption
  • Maintains backward compatibility (metadata still preferred)

Design Trade-offs (acceptable):

  • SQL normalization doesn't handle emoji removal (requires exact match for emoji-tagged items)
  • Workspace-only tags have empty fields array (graceful degradation)
  • Comment notes 95% coverage for SQL normalization vs 100% with TypeScript

Flow verification:

  1. create.ts → createNode() → buildNodePayloadFromDatabase()
  2. → UnifiedSchemaService.buildNodePayload()
  3. → getSupertag() [with fallback to supertags table]
  4. → Success for webSource and other workspace-only tags ✅

Security Assessment

  • ✅ Parameterized queries prevent SQL injection
  • ✅ No hardcoded credentials or secrets
  • ✅ Error messages don't leak sensitive information
  • ✅ Proper instanceof checks before accessing error properties

Recommendations for Future Work

None blocking for this PR. Future improvements could include:

  • Consider adding debug logging for fallback path usage
  • Monitor performance impact of normalization query on large workspaces
  • Consider caching fallback results if performance becomes an issue

Final Verdict

This PR is ready to merge. It solves the reported issue with minimal, focused changes and excellent test coverage.

@jcfischer jcfischer merged commit f829f40 into main Mar 4, 2026
1 check failed
jcfischer added a commit that referenced this pull request Mar 4, 2026
…review cleanup

Graph executor: track source→target associations via join maps so
CONNECTED TO queries correctly associate each source node with its
specific targets (e.g., each meeting shows only its attendees).

Also includes PR #79 review cleanup: simplified create.ts output,
moved tag normalization to TypeScript for emoji support, simplified
error handling in unified-schema-service.

Added 13 integration tests covering join maps, dot-notation projections,
shared targets, aggregates, and edge cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

[BUG] supertag-mcp: tana_create fails with Bad Request for webSource nodes; tana-local works

1 participant