Commit Graph

106 Commits

Author SHA1 Message Date
xiaoju d328fbe6e4 feat: wrap put/get/has/hash/verify/list with {type,value} envelope (Phase 2)
Fixes #70
2026-05-31 15:29:49 +00:00
xiaoju 11bb6b3e32 Merge pull request 'feat: register 18 @output/* schemas, default templates, wrapEnvelope (Phase 1c)' (#78) from fix/75-output-schemas into main 2026-05-31 15:14:49 +00:00
xiaoju 0dca8a5521 feat: register 18 @output/* schemas, default templates, wrapEnvelope (Phase 1c)
Fixes #75
2026-05-31 15:14:28 +00:00
xiaoju 5aad956c83 Merge pull request 'feat: remove cat/schema commands, add list --type, enhance verify (Phase 1b)' (#77) from fix/74-remove-cat-schema into main 2026-05-31 14:52:36 +00:00
xiaoju 038c901f4b feat: remove cat/schema commands, add list --type, enhance verify (Phase 1b)
Fixes #74
2026-05-31 14:41:38 +00:00
xiaoju f4cf92e128 Merge pull request 'feat: auto-bootstrap CAS store on open (Phase 1a)' (#76) from fix/73-auto-bootstrap into main
feat: auto-bootstrap CAS store on open (Phase 1a)

Fixes #73
2026-05-31 13:40:43 +00:00
xiaoju c8bf38cb81 feat: auto-bootstrap CAS store on open (Phase 1a)
Changes:
1. openStore() now async, auto-creates directory and bootstraps
2. Removed shouldCreate parameter and validateStoreExists()
3. All openStore() calls now awaited
4. Deleted init and bootstrap commands
5. Removed Issue #55 store validation tests (superseded by auto-bootstrap)
6. Enhanced error handling in openStore() for permission/path issues
7. Updated e2e snapshots after fixing missed await in cmdRender

Bootstrap is idempotent. Core tests using createMemoryStore() unaffected.

Fixes #73

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 13:34:43 +00:00
xingyue b93d7b229a Merge pull request 'feat(cli): convert e2e-check scenarios to snapshot fixture tests' (#68) from fix/66-e2e-snapshot-tests into main 2026-05-31 11:43:32 +00:00
xingyue 9912013b0a fix(cli): use dot notation for GC result assertions (Biome lint)
Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
2026-05-31 19:30:31 +08:00
xingyue 2ed097e207 fix(cli): make e2e snapshots stable across machines and runs
- Strip volatile fields (timestamp, created, updated) from JSON before
  snapshotting using a stripVolatile helper
- Remove toMatchSnapshot() from test 2.1 to avoid embedding machine-
  specific tmp paths in the snapshot; use toContain assertions instead
- Replace GC count snapshot with structural shape assertions so counts
  don't need to match exact phase-history state

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
2026-05-31 19:29:25 +08:00
xingyue b0d5b05457 feat(cli): add e2e snapshot fixture tests for all CLI scenarios
Convert 46 e2e-check workflow scenarios to fast bun test snapshot tests.
7 describe phases share a single mkdtempSync store; hashes are deterministic
so cross-phase data dependencies work without re-creating data.

Closes #66

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
2026-05-31 19:24:17 +08:00
xiaomo de20cfde53 Merge pull request 'refactor: e2e-check workflow 拆分为 4 角色' (#64) from fix/e2e-check-role-split into main 2026-05-31 10:58:50 +00:00
xingyue 9948db77ea refactor: split e2e-check workflow into 4 roles
- preparer: Docker setup, install, build, lint, unit test, init store
- tester: pure CLI scenario testing (receives container + store path)
- reporter: file Gitea issues (triggered by bugs or setup failures)
- cleanup: stop and remove Docker container

Also fixes template variable prefix (payload.name vs name).
2026-05-31 18:23:34 +08:00
xiaomo c60f05f650 Merge pull request 'fix: validate store directory exists before CLI operations' (#63) from fix/55-store-path-validation-rebase into main 2026-05-31 09:20:41 +00:00
xiaoju 314b076c05 fix: validate store path before read operations (Fixes #55)
- Add validateStoreExists() helper to check store directory existence
- Modify openStore() to accept shouldCreate parameter (default: false)
- Update read commands to validate store exists first
- Update write commands (init, put, schema put, hash) to allow creation
- Add 10 E2E tests covering all affected commands
- Improve error message: "Store not found at <path>" vs "Node not found"

Commands that now validate:
- get, has, verify, refs, walk, cat
- schema get, schema list

Commands that still create:
- init, put, schema put, hash
- var commands (use openVarStore)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 09:19:23 +00:00
xiaomo 664409b94a Merge pull request 'fix: workflow frontmatter schema 加 type: object' (#56) from fix/workflow-frontmatter-schema into main 2026-05-31 09:16:53 +00:00
xingyue f3f13e6f35 fix: add type: object to all oneOf variants in workflow frontmatter schemas
ajv strict mode requires explicit type: object when properties/required
are used. All three workflow YAMLs had this missing, causing frontmatter
validation to fail with:
  strict mode: missing type "object" for keyword "required"

Also added frontmatter output example in e2e-check.yaml procedure to
prevent agents from outputting bugs as plain strings instead of objects.
2026-05-31 17:15:43 +08:00
xiaomo 10c5c8f98e Merge pull request 'fix: clean error message for invalid schema in schema put command' (#61) from fix/54-schema-put-invalid-error into main 2026-05-31 09:15:37 +00:00
xiaomo d28003779e Merge pull request 'fix: detect missing root hash in render command' (#60) from fix/53-render-missing-hash-error into main 2026-05-31 09:15:35 +00:00
xiaomo 885a8e1147 Merge pull request 'test: add E2E template variable rendering tests' (#59) from fix/52-template-variable-rendering into main 2026-05-31 09:15:33 +00:00
xiaomo f0ffe6b234 Merge pull request 'fix: validate payload against schema in put command' (#57) from fix/50-schema-validation into main 2026-05-31 09:15:28 +00:00
xiaoju fc869cfc99 fix: resolve test failures for issue #53
Applied tester feedback to fix 5 test failures:

1. Updated error message format from "Node not found" to "CAS node not found"
   for consistency with existing tests in variable-store.test.ts and var.test.ts

2. Fixed CLI tests R9 and R10 to use bootstrap() directly instead of
   non-existent "types" command. Added imports for bootstrap and createFsStore.

3. Fixed render test 6.5 to pass actual schema Hash instead of entire
   bootstrap object (Record<string, Hash>)

4. Updated test expectations in render.test.ts (tests 1.5, 10.1, 10.2) to
   match new error message format

All 390 tests now pass. Core functionality verified:
- Missing root hash detection working correctly
- CLI exits with code 1 on missing hash
- Error message includes hash: "CAS node not found: <hash>"
- Nested nodes still render as cas: references (preserved behavior)
- Resolution decay behavior preserved

Fixes #53

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 08:36:19 +00:00
xiaoju 7fd2013ef2 fix: validate payload against schema in put command
Add schema validation to the `json-cas put` command to ensure data
integrity. The CLI now validates the payload against the specified
schema before storing, and exits with a non-zero code and descriptive
error message if validation fails.

Changes:
- Add schema existence check in cmdPut()
- Add payload validation before storing
- Exit with error code 1 on validation failure
- Provide helpful error messages indicating the file and schema
- Add comprehensive test suite with 16 test scenarios covering:
  - Valid data (regression tests)
  - Type mismatches (new validation)
  - Schema errors (edge cases)
  - Integration with existing features
  - Error message quality

The hash command continues to work without validation (dry-run
consistency), and schema put continues to use its own validation.

Fixes #50

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 08:24:13 +00:00
xiaoju 0b72c9400f style: apply biome formatting fixes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 08:21:10 +00:00
xiaoju eb36c16420 fix: detect missing root hash in render command and exit with error
When rendering a non-existent hash, the CLI now exits with code 1 and
displays an error message instead of silently outputting "cas:<hash>"
with exit code 0.

Changes:
- Updated CasNodeNotFoundError constructor signature to store hash
- Added root hash existence check in render() and renderAsync()
- Updated CLI error handling to catch CasNodeNotFoundError
- Added comprehensive test suite for missing hash error handling
- Updated existing incorrect tests (R2 and test 1.5)

The fix distinguishes between:
- Root hash (user-requested): Must exist or throw error
- Nested hash (during traversal): Renders as cas: reference (existing behavior)
- Resolution below epsilon: Renders as cas: reference (existing behavior)

Fixes #53

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 08:18:15 +00:00
xiaoju 3c8b16d7b1 fix: clean error message for invalid schema in schema put command
Fixes #54

The `json-cas schema put` command now catches SchemaValidationError
and displays a clean error message instead of showing a raw stack trace.

Changes:
- Import SchemaValidationError from @uncaged/json-cas
- Wrap putSchema() call in try-catch in cmdSchemaPut
- Catch SchemaValidationError specifically and call die(e.message)
- Add 5 comprehensive tests for invalid schema error handling

Test cases:
1. Invalid JSON Schema type value
2. Unknown schema keys
3. Invalid nested schema
4. Non-object root schema
5. Valid schema regression test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 08:14:23 +00:00
xiaoju 51e81c7b99 test: add E2E template variable rendering tests for issue #52
Add comprehensive test suite (Suite 9 and 10) covering template variable rendering:
- Suite 9: E2E Template Variable Rendering (12 tests)
  - Tests correct {{ payload.* }} syntax vs incorrect direct property access
  - Tests primitive payloads (string, number)
  - Tests nested objects, arrays, null values, booleans
  - Tests edge cases: empty strings, zero values, special characters
  - Validates CLI integration flow
- Suite 10: Context Variable Completeness (2 tests)
  - Verifies context propagation through recursive renders
  - Tests context isolation between parent and child nodes

All tests pass. Confirms the renderNode function correctly passes
node.payload to template context. Issue #52 was user error - templates
require {{ payload.name }} syntax, not {{ name }}.

Fixes #52

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 08:13:36 +00:00
xiaomo 2932aa5980 Merge pull request 'feat: ucas render --pipe/-p for stdin { type, value } input' (#49) from feat/48-render-pipe into main 2026-05-31 07:47:01 +00:00
xiaoju a0d7b67923 fix: address PR #49 review feedback
- Convention: renderDirect uses Store | null, options | null (no ?:)
- Validation: hash format check (13-char Crockford Base32) on stdin input
- DRY: remove collectRefsFromSchema, import collectRefs from schema.ts
- DRY: extract validateAndExtractOptions shared by render/renderAsync/renderDirect
- stdin: use process.stdin async iteration instead of /dev/stdin
- UX: error on --pipe + hash conflict instead of silent ignore
- Tests: add 9.10 (store present, schema missing)
2026-05-31 07:43:25 +00:00
xiaoju 7b29fe777c chore: remove stale temp files 2026-05-31 07:34:16 +00:00
xiaoju 64b8a88bdc feat: add renderDirect() and ucas render --pipe/-p
In-memory rendering of { type, value } envelopes without store writes.
Store is optional and read-only (for expanding nested cas_ref references).

CLI: ucas render --pipe/-p reads JSON from stdin.
Core: renderDirect(typeHash, value, store?, options?) for programmatic use.

Fixes #48
2026-05-31 07:34:07 +00:00
xiaoju 4717024e9b Merge pull request 'chore: Phase 4 cleanup — dedupe types, remove unused params, fix tests' (#47) from fix/46-phase4-cleanup into main 2026-05-31 07:19:42 +00:00
xiaoju 1e5f4b7c46 chore: Phase 4 cleanup — dedupe RenderOptions, remove unused param, fix test numbering
1. Deduplicate RenderOptions type
   - Remove duplicate definition from liquid-render.ts
   - Import from render.ts instead (canonical location)

2. Remove unused _globalDecay parameter
   - Remove from renderNode function signature
   - Update all call sites

3. Fix test numbering gaps
   - Suite 4: Renumber 4.4→4.2, 4.5→4.3
   - Suite 7→6: Renumber 7.1→6.1, 7.2→6.2, 7.4→6.3
   - Suite 8→7: Renumber all 8.x→7.x tests
   - Suite 10→8: Renumber 10.1→8.1
   - Result: consecutive suite numbering (1-8)

4. CLI test status: All 95 tests pass (no pre-existing failures found)

Fixes #46

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 07:11:44 +00:00
xiaoju 0a761f5289 Merge pull request 'feat: LiquidJS template rendering integration (#40)' (#45) from fix/40-liquidjs-integration into main 2026-05-31 06:08:31 +00:00
xiaoju 07e08e3b38 feat: Add CLI integration for LiquidJS template rendering
Integrates LiquidJS template rendering into the CLI render command.
When a template is registered for a node's schema type via the variable
system (@ucas/template/text/<schema-hash>), the CLI will use the template
for rendering. Otherwise, it falls back to YAML output.

Changes:
- Modified cmdRender in index.ts to use renderAsync with variable store
- Added Suite 6: CLI Integration with Templates (5 comprehensive tests)
- Fixed template file format: templates must be JSON-encoded strings
- Removed unused render import from index.ts
- Renamed unused globalDecay parameter in liquid-render.ts

Test coverage increased from 336 to 341 tests, all passing.

Fixes #40

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 06:05:44 +00:00
xiaoju e0af351991 fix: resolve TypeScript strict mode and dynamic import issues
Fixes reviewer feedback:
1. Fixed TypeScript strict mode error where ctx.engine was possibly null
   - Refactored to pass store/varStore/globalDecay directly to createLiquidEngine
   - Eliminated RenderContext type that caused circular dependency
   - Engine is now properly typed as Liquid (non-nullable)

2. Removed dynamic imports from production code
   - Changed render.ts to use static import of renderWithTemplate
   - Changed hasTemplate to use static import of putSchema
   - Complies with CLAUDE.md convention against dynamic imports

All tests pass (336 tests), build succeeds with no TypeScript errors,
lint checks pass with only 1 minor warning about unused parameter
(which is actually used in recursive calls).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 06:05:44 +00:00
xiaoju 72f85c9077 feat: implement LiquidJS template rendering integration
Integrates LiquidJS as the template engine for CAS node rendering with
custom {% render %} tag supporting recursive rendering with resolution
decay. Templates are discovered via variables under
@ucas/template/text/<type-hash>. When ucas render <hash> is invoked,
the system queries for a registered template; if found, uses LiquidJS;
otherwise falls back to Phase 3's default YAML renderer.

Key features:
- Custom {% render %} tag with recursive CAS node rendering
- Decay priority chain: template decay > CLI --decay > default 0.5
- Context variables: resolution, epsilon, hash, payload, type, timestamp
- Graceful fallback: No template → YAML rendering (Phase 3)
- Zero breaking changes: All Phase 3 tests still pass
- Template discovery via @ucas/template/text/<type-hash> variables

Implementation:
- New file: packages/json-cas/src/liquid-render.ts — LiquidJS integration
- Modified: packages/json-cas/src/render.ts — Template lookup + YAML fallback
- New file: packages/json-cas/src/liquid-render.test.ts — 32 comprehensive tests
- Dependency: liquidjs npm package
- CLI: No changes needed (transparent integration)

All tests pass (336 tests), build succeeds, lint checks pass.

Closes #40

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 06:05:44 +00:00
xiaoju cccfca3137 Merge pull request 'feat: implement template CLI subcommands (set/get/list/delete)' (#44) from fix/38-template-cli into main 2026-05-31 05:29:12 +00:00
xiaoju 5f2906908c feat: implement template CLI subcommands (set/get/list/delete)
Implement ucas template subcommands for managing template storage:
- template set <schema-hash> <file> | --inline <text>: Store template text in CAS
- template get <schema-hash>: Retrieve template as raw text
- template list: List all templates with preview
- template delete <schema-hash>: Delete template variable binding

Templates are stored as plain text under @string schema and bound to
variables using the naming pattern @ucas/template/text/<schema-hash>.

Fixes #38

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 05:06:47 +00:00
xiaoju 077eaa6f6d Merge pull request 'feat: implement render engine with resolution decay (#39)' (#43) from fix/39-render-rebase into main 2026-05-31 04:51:17 +00:00
xiaoju 7e23d911a4 feat: implement render engine with resolution decay (#39)
Implement Phase 3: render core engine with resolution-based decay and
default YAML rendering.

Core Features:
- Resolution decay model: child nodes receive resolution = parent × decay
- Epsilon threshold: nodes with resolution ≤ epsilon render as cas:<hash>
- Default YAML output format with 2-space indentation
- Cycle detection via visited set
- Floating-point tolerance for epsilon comparisons

Implementation:
- packages/json-cas/src/render.ts: Core render function
- packages/json-cas/src/render.test.ts: 38 comprehensive tests
- packages/cli-json-cas: ucas render command with --resolution, --decay, --epsilon flags
- CLI integration tests for render command

Tests: All 276 tests pass (38 new render tests, 3 CLI tests)
Build: Clean compilation with tsc
Lint: Passes biome check

Fixes #39

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 04:50:55 +00:00
xiaoju 301b05c212 Merge pull request 'feat: add built-in schema aliases with @ prefix support' (#42) from fix/37-builtin-schema-aliases into main 2026-05-31 04:45:20 +00:00
xiaoju 22fce0ac66 feat: add built-in schema aliases with @ prefix support
Implements Phase 1 of issue #37:
- Extended variable name validation to allow @ prefix (system-reserved)
- Registered 6 built-in schemas with @ aliases during bootstrap
  - @schema → meta-schema (self-referential)
  - @string → { type: "string" }
  - @number → { type: "number" }
  - @object → { type: "object" }
  - @array → { type: "array" }
  - @bool → { type: "boolean" }
- Bootstrap now returns Record<string, Hash> instead of Hash
- Added CLI @ alias resolution for all commands accepting type-hash
  - ucas schema get @string
  - ucas put @string <file>
  - ucas hash @string <file>
- Added comprehensive test coverage for all features
  - Variable name validation with @ prefix
  - Built-in schema registration
  - CLI alias resolution
  - Integration tests

Fixes #37

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-31 04:18:46 +00:00
xiaoju fddbb1549e feat: RFC-31 Phase 3 — CLI var 子命令重写
- var set <name> <hash> (upsert, replaces create+update)
- var get <name> --schema <hash> (schema required)
- var delete <name> [--schema <hash>] (optional schema)
- var list [prefix] (replaces --scope)
- var tag <name> --schema <hash> ...
- 41 CLI tests, all passing

Fixes #34
Ref #31
2026-05-30 15:44:19 +00:00
xiaoju 109aaab9b8 feat: RFC-31 Phase 3 — rewrite CLI var subcommands for composite key model
Migrate CLI var subcommands from ULID ID model to (name, schema) composite key model.

- Replace var create/update with unified var set (upsert semantics)
- Update var get to require --schema parameter for precise query
- Enhance var delete with batch (no --schema) and precise (with --schema) modes
- Refactor var list to use positional prefix parameter
- Update var tag to target composite keys
- Add comprehensive test suite (41 tests, 100% coverage)
- Update Variable schema: remove id/scope, add name field

Fixes #34

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-30 14:29:33 +00:00
xiaoju 906a6dfd1c feat: RFC-31 Phase 1+2 — Variable model refactor with (name, schema) composite key
- Replace ULID id + scope with qualified name + schema composite PK
- Add set() upsert, remove() with optional schema, validateName()
- get(name, schema) with fixed return type (no polymorphic)
- Tags/labels adapted to composite foreign keys
- GC compatible

Fixes #32
Ref #31
2026-05-30 13:36:51 +00:00
xiaoju 5e7db0ef6b refactor: apply PR #33 Review Round 2 fixes
Addresses Review Round 2 feedback for variable model refactor:

1. Remove create() method - set() is now the unified entry point
2. Remove VariableDuplicateError class (only used by create())
3. Clean up dead code: Array.isArray(existing) checks in update()/remove()/tag()
4. Add tag/label conflict validation to set() update path
5. Migrate gc.test.ts from create() to set()

Changes:
- Delete create() method (lines 381-467) and VariableDuplicateError class
- Remove Array.isArray checks from 3 methods (always null, never array)
- Remove orphaned delete() JSDoc comment
- Add 3 new tests for set() update path tag/label conflict validation
- Replace 10 create() calls with set() in gc.test.ts

Test results: 193 pass (190 existing + 3 new)
Build: clean, Lint: clean

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-30 13:25:01 +00:00
xiaoju 31f84a7ab0 refactor: implement PR #33 review feedback - Variable API refinements
Closes #33

## Breaking Changes

### 1. get() signature - schema required
- **Before:** `get(name, schema?)` with polymorphic return `Variable | Variable[] | null`
- **After:** `get(name, schema)` with required schema, returns `Variable | null`
- **Migration:** Use `list({ exactName })` to query all schema variants

### 2. delete() method removed
- **Removed:** `delete(name, schema)` method
- **Use:** `remove(name, schema?)` as the sole deletion API

## New Features

### 3. list() enhanced with exactName parameter
- **Added:** `exactName` parameter for exact name matching
- **Use case:** Query all schema variants of an exact name
- **Example:** `list({ exactName: "config" })` returns all schemas for "config"
- **Validation:** `exactName` and `namePrefix` are mutually exclusive

### 4. Additional verification tests
- Added tests confirming set() schema extraction behavior
- Added comprehensive validateName() error message tests
- Verified detailed error messages for all validation violations

## Implementation Details

### Changes in variable-store.ts
- Simplified get() to single signature with required schema
- Removed deprecated delete() method
- Enhanced list() with exactName parameter and validation
- Updated remove() to use list({ exactName }) for multi-variant queries
- Fixed tag() method to remove redundant Array.isArray check

### Changes in tests
- Replaced get() without schema tests with new required-schema tests
- Added 8 comprehensive tests for list({ exactName }) functionality
- Added 5 validateName() error message verification tests
- Added 2 set() schema extraction verification tests
- Updated integration test to use list({ exactName }) instead of get(name)
- Updated gc.test.ts to use remove() instead of delete()

## Verification
-  190 tests pass (1 unrelated CLI test fails)
-  TypeScript build passes with no errors
-  Biome lint and format pass
-  All Variable model tests pass
-  GC integration tests pass

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-30 12:56:34 +00:00
xiaoju 793a5c619d feat: implement RFC #31 Phase 1 - variable model API improvements
Closes #33

## Changes

### 1. Enhanced Name Validation
- Added `validateName()` private method with comprehensive validation rules
- Updated `InvalidVariableNameError` to include specific `reason` field
- Validation rules:
  - Each segment must match [a-zA-Z0-9._-]+
  - Segments separated by /
  - No empty segments (e.g., a//b)
  - No leading/trailing slashes (e.g., /a or a/)
- Applied to all mutating operations: set(), create(), update(), tag()

### 2. New set() Upsert Method
- Implements upsert semantics: insert if not exists, update if exists
- Checks (name, schema) pair existence using extractSchema(value)
- On update: preserves created timestamp, updates value and updated timestamp
- Preserves existing tags/labels when called without options
- Replaces tags/labels when called with options
- Allows same name with different schemas

### 3. Optional Schema in get()
- Overloaded signature: get(name) and get(name, schema)
- get(name) without schema:
  - Returns null when no variables exist
  - Returns single Variable when one schema variant exists
  - Returns Variable[] when multiple schema variants exist
- get(name, schema) with schema:
  - Returns Variable | null for exact match
  - Includes complete tags and labels

### 4. Renamed delete() to remove() with Optional Schema
- Overloaded signature: remove(name) and remove(name, schema)
- remove(name) without schema:
  - Deletes all schema variants for the name
  - Returns Variable[] (all deleted variants)
  - Returns empty array [] when nothing found
- remove(name, schema) with schema:
  - Deletes specific (name, schema) variant
  - Returns single Variable
  - Throws VariableNotFoundError when not found
- Cascades deletion to tags and labels via foreign key constraints

### 5. Code Quality Improvements
- Fixed `any` type usage, replaced with `unknown` and proper type guards
- Fixed string concatenation to use template literals
- Updated all internal get() calls to handle new return types
- Applied strict null checks and array checks

### 6. Comprehensive Test Coverage
- 36 tests covering all new behaviors
- Test suites for:
  - set() upsert method (7 tests)
  - get() with optional schema (6 tests)
  - remove() with optional schema (6 tests)
  - Name validation (6 tests)
  - Integration workflows (2 tests)
  - Legacy methods (2 tests)
  - Database schema verification
  - List and tag operations
- All tests pass: bun test (151 pass, 0 fail)

## Verification
-  bun test - All 151 tests pass
-  bun run build - Clean TypeScript build
-  bunx biome check - No lint errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-30 11:11:00 +00:00
xiaoju b89e31f468 feat: refactor Variable model to use (name, schema) composite key
Implements RFC-31 Phase 1 - refactors the Variable model to use a composite
primary key of (name, schema) instead of the previous ULID id + scope approach.

Key changes:

1. **Type Model**:
   - Removed `id: VariableId` and `scope: string` fields
   - Added `name: string` as part of composite key with `schema: Hash`
   - Variables with same name but different schemas are now distinct entities

2. **Database Schema**:
   - Changed primary key from `id` to `(name, schema)`
   - Updated foreign keys in `variable_tags` and `variable_labels` tables
   - Replaced scope-based indexes with name-based indexes
   - Enabled foreign key constraints for proper cascade deletes

3. **CRUD Operations** - all methods updated to use `(name, schema)`:
   - `create(name, value, options)` - validates unique (name, schema)
   - `get(name, schema)` - retrieves by composite key
   - `update(name, schema, value)` - updates with schema validation
   - `delete(name, schema)` - deletes with cascade to tags/labels
   - `list({ namePrefix?, schema?, tags?, labels? })` - filters by name prefix and schema
   - `tag(name, schema, operations)` - manages tags/labels by composite key

4. **Error Types**:
   - New: `VariableDuplicateError` for duplicate `(name, schema)` pairs
   - New: `InvalidVariableNameError` for empty names
   - Removed: `InvalidScopeError` (no longer needed)
   - Updated: `VariableNotFoundError` to reference `(name, schema)`

5. **GC Adaptation**:
   - Garbage collection works correctly with refactored model
   - Preserves nodes referenced by variables across all schemas
   - Global collection across all variable names and schemas

6. **Tests**:
   - Added comprehensive test suite covering all new functionality
   - Database schema validation tests
   - CRUD operation tests with composite keys
   - Multi-schema scenarios (same name, different schemas)
   - Tag/label management tests
   - GC integration tests
   - End-to-end workflow tests

7. **Breaking Changes**:
   - This is a backward-incompatible change
   - CLI commands will need updates in a future phase (out of scope for Phase 1)
   - Data migration is out of scope for Phase 1

Fixes #32

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-30 10:26:30 +00:00