|
| 1 | +# Package-Aware Metadata Management Implementation |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +This document describes the implementation of package-aware metadata management in ObjectStack, ensuring that: |
| 6 | +1. Every metadata item belongs to a package |
| 7 | +2. Code-loaded packages are read-only |
| 8 | +3. Database packages are mutable |
| 9 | + |
| 10 | +## Architecture |
| 11 | + |
| 12 | +### Core Principles |
| 13 | + |
| 14 | +1. **Package Ownership**: All metadata (objects, views, flows, etc.) must belong to a package |
| 15 | +2. **Source-Based Mutability**: |
| 16 | + - **Filesystem/Code packages** → Read-only (scope='system', source='filesystem') |
| 17 | + - **Database packages** → Mutable (scope='platform'/'user', source='database') |
| 18 | +3. **Conversation Context**: AI tools track active package per conversation |
| 19 | +4. **Overlay Pattern**: Code metadata can have database overlays for customization |
| 20 | + |
| 21 | +### Schema Support (Already Exists) |
| 22 | + |
| 23 | +The `MetadataRecordSchema` in `packages/spec/src/system/metadata-persistence.zod.ts` already includes: |
| 24 | + |
| 25 | +```typescript |
| 26 | +{ |
| 27 | + packageId: string | undefined; // Package ownership |
| 28 | + managedBy: 'package' | 'platform' | 'user'; // Lifecycle management |
| 29 | + scope: 'system' | 'platform' | 'user'; // Mutability scope |
| 30 | + source: 'filesystem' | 'database' | 'api' | 'migration'; // Origin |
| 31 | +} |
| 32 | +``` |
| 33 | + |
| 34 | +## Implementation Progress |
| 35 | + |
| 36 | +### Phase 1: Package Management Tools ✅ COMPLETE |
| 37 | + |
| 38 | +Created 5 new AI tools for package management: |
| 39 | + |
| 40 | +1. **`list_packages`** (`list-packages.tool.ts`) |
| 41 | + - Lists all installed packages |
| 42 | + - Supports filtering by status and enabled state |
| 43 | + - Returns package metadata (id, name, version, type, status) |
| 44 | + |
| 45 | +2. **`get_package`** (`get-package.tool.ts`) |
| 46 | + - Gets detailed information about a specific package |
| 47 | + - Returns full manifest, dependencies, namespaces |
| 48 | + |
| 49 | +3. **`create_package`** (`create-package.tool.ts`) |
| 50 | + - Creates a new package with manifest |
| 51 | + - Validates reverse domain notation for package ID |
| 52 | + - Auto-derives namespace from package ID |
| 53 | + - Automatically sets as active package in conversation |
| 54 | + |
| 55 | +4. **`get_active_package`** (`get-active-package.tool.ts`) |
| 56 | + - Retrieves the currently active package from conversation context |
| 57 | + - Returns null if no active package is set |
| 58 | + |
| 59 | +5. **`set_active_package`** (`set-active-package.tool.ts`) |
| 60 | + - Sets the active package for the conversation |
| 61 | + - All subsequent metadata operations use this package |
| 62 | + |
| 63 | +**Handler Implementation**: `package-tools.ts` |
| 64 | +- Implements `IPackageRegistry` interface for package CRUD |
| 65 | +- Implements `IConversationService` interface for context tracking |
| 66 | +- Validates package IDs (reverse domain notation) |
| 67 | +- Validates namespaces (snake_case) |
| 68 | +- Validates versions (semver) |
| 69 | + |
| 70 | +### Phase 2: Enhanced Metadata Tools ⏳ IN PROGRESS |
| 71 | + |
| 72 | +**Completed:** |
| 73 | +- Updated `MetadataToolContext` interface to include: |
| 74 | + - `conversationService` - for tracking active package |
| 75 | + - `conversationId` - current conversation context |
| 76 | + - `packageRegistry` - for validating packages and checking read-only status |
| 77 | + |
| 78 | +- Added `packageId` parameter to `create_object` tool |
| 79 | + - Optional parameter |
| 80 | + - Falls back to active package from conversation |
| 81 | + - Provides clear error message if no package context available |
| 82 | + |
| 83 | +**Remaining Work:** |
| 84 | +- Update `createObjectHandler` to: |
| 85 | + - Resolve package ID (explicit > active > error) |
| 86 | + - Check if package is read-only |
| 87 | + - Attach package metadata to object definition |
| 88 | + - Return package info in success response |
| 89 | + |
| 90 | +- Update other metadata tools (`add_field`, `modify_field`, `delete_field`) |
| 91 | + - Add `packageId` parameter where appropriate |
| 92 | + - Implement read-only validation |
| 93 | + |
| 94 | +### Phase 3: Conversation Context Management (TODO) |
| 95 | + |
| 96 | +**Objectives:** |
| 97 | +- Store `activePackageId` in conversation metadata |
| 98 | +- Persist across conversation turns |
| 99 | +- Clear on conversation end |
| 100 | + |
| 101 | +**Implementation Plan:** |
| 102 | +```typescript |
| 103 | +// In conversation service |
| 104 | +interface ConversationMetadata { |
| 105 | + activePackageId?: string; |
| 106 | + lastPackageOperation?: string; |
| 107 | + createdAt?: string; |
| 108 | +} |
| 109 | + |
| 110 | +// Store in database table: ai_conversation_metadata |
| 111 | +{ |
| 112 | + conversation_id: string; |
| 113 | + metadata: JSON; // Contains activePackageId |
| 114 | + updated_at: timestamp; |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +### Phase 4: Metadata Service Write Protection (TODO) |
| 119 | + |
| 120 | +**Objectives:** |
| 121 | +- Prevent modification of code-based metadata |
| 122 | +- Allow database metadata modifications |
| 123 | +- Support customization overlays for code metadata |
| 124 | + |
| 125 | +**Implementation Plan:** |
| 126 | + |
| 127 | +1. **Add source tracking to metadata registration:** |
| 128 | +```typescript |
| 129 | +// In metadata service |
| 130 | +async register(type: string, name: string, data: unknown, options?: { |
| 131 | + packageId?: string; |
| 132 | + scope?: 'system' | 'platform' | 'user'; |
| 133 | + source?: 'filesystem' | 'database' | 'api'; |
| 134 | +}): Promise<void> |
| 135 | +``` |
| 136 | + |
| 137 | +2. **Implement read-only check:** |
| 138 | +```typescript |
| 139 | +async register(type: string, name: string, data: unknown, options) { |
| 140 | + const existing = await this.get(type, name); |
| 141 | + |
| 142 | + if (existing) { |
| 143 | + const metadata = existing as MetadataRecord; |
| 144 | + |
| 145 | + // Block if trying to modify code-based metadata |
| 146 | + if (metadata.scope === 'system' || metadata.source === 'filesystem') { |
| 147 | + throw new Error( |
| 148 | + `Cannot modify ${type} "${name}" - it is code-based metadata. ` + |
| 149 | + `Use overlay customization instead via saveOverlay().` |
| 150 | + ); |
| 151 | + } |
| 152 | + } |
| 153 | + |
| 154 | + // Proceed with registration for database metadata |
| 155 | + await this.storage.save(type, name, { ...data, ...options }); |
| 156 | +} |
| 157 | +``` |
| 158 | + |
| 159 | +3. **Support overlay pattern:** |
| 160 | +```typescript |
| 161 | +// Allow customization of code metadata via overlays |
| 162 | +await metadataService.saveOverlay({ |
| 163 | + type: 'object', |
| 164 | + name: 'account', |
| 165 | + scope: 'platform', // or 'user' |
| 166 | + overlay: { |
| 167 | + fields: { |
| 168 | + custom_field: { type: 'text', label: 'Custom Field' } |
| 169 | + } |
| 170 | + } |
| 171 | +}); |
| 172 | + |
| 173 | +// Runtime serves merged result: |
| 174 | +// base (from code) + platform overlay + user overlay |
| 175 | +const effective = await metadataService.getEffective('object', 'account', context); |
| 176 | +``` |
| 177 | + |
| 178 | +### Phase 5: Testing & Documentation (TODO) |
| 179 | + |
| 180 | +**Unit Tests Needed:** |
| 181 | +- Package tool validation (reverse domain, semver, snake_case) |
| 182 | +- Package CRUD operations |
| 183 | +- Active package resolution logic |
| 184 | +- Read-only package detection |
| 185 | +- Metadata service write protection |
| 186 | + |
| 187 | +**Integration Tests Needed:** |
| 188 | +- End-to-end package creation workflow |
| 189 | +- Metadata creation with package context |
| 190 | +- Read-only enforcement for code packages |
| 191 | +- Overlay application and merging |
| 192 | + |
| 193 | +**Documentation Needed:** |
| 194 | +- Package-first development workflow guide |
| 195 | +- AI agent integration examples |
| 196 | +- Package naming conventions |
| 197 | +- Customization overlay patterns |
| 198 | +- Migration guide for existing metadata |
| 199 | + |
| 200 | +## Usage Examples |
| 201 | + |
| 202 | +### Creating a Package and Objects via AI |
| 203 | + |
| 204 | +```typescript |
| 205 | +// User: "Create a new CRM application" |
| 206 | +// AI uses: create_package |
| 207 | +{ |
| 208 | + id: "com.acme.crm", |
| 209 | + name: "CRM Application", |
| 210 | + version: "1.0.0", |
| 211 | + type: "application" |
| 212 | +} |
| 213 | + |
| 214 | +// AI automatically sets as active package |
| 215 | +// Now all metadata creation uses this package |
| 216 | + |
| 217 | +// User: "Create an Account object with name and email fields" |
| 218 | +// AI uses: create_object (packageId is implicit from active package) |
| 219 | +{ |
| 220 | + name: "account", |
| 221 | + label: "Account", |
| 222 | + fields: [ |
| 223 | + { name: "account_name", type: "text", label: "Account Name" }, |
| 224 | + { name: "email", type: "text", label: "Email" } |
| 225 | + ] |
| 226 | +} |
| 227 | + |
| 228 | +// Object is created with packageId="com.acme.crm" |
| 229 | +``` |
| 230 | + |
| 231 | +### Handling Read-Only Packages |
| 232 | + |
| 233 | +```typescript |
| 234 | +// Code-based package (loaded from filesystem) |
| 235 | +// packages/my-plugin/metadata/objects/user.object.ts |
| 236 | +export default defineObject({ |
| 237 | + name: 'user', |
| 238 | + label: 'User', |
| 239 | + fields: { ... } |
| 240 | +}); |
| 241 | + |
| 242 | +// At runtime, this is registered with: |
| 243 | +// scope='system', source='filesystem', packageId='com.example.myplugin' |
| 244 | + |
| 245 | +// User tries: "Add a custom_field to the user object" |
| 246 | +// AI uses: add_field |
| 247 | +{ |
| 248 | + objectName: "user", |
| 249 | + name: "custom_field", |
| 250 | + type: "text" |
| 251 | +} |
| 252 | + |
| 253 | +// Metadata service blocks: |
| 254 | +// "Cannot modify object 'user' - it is code-based metadata. |
| 255 | +// Use overlay customization instead." |
| 256 | + |
| 257 | +// AI suggests alternative: |
| 258 | +// "I see 'user' is a system object. I can create a customization overlay instead. |
| 259 | +// Would you like me to add the field as a platform-level customization?" |
| 260 | +``` |
| 261 | + |
| 262 | +## Best Practices |
| 263 | + |
| 264 | +1. **Package Naming**: |
| 265 | + - Use reverse domain notation: `com.company.product` |
| 266 | + - Examples: `com.acme.crm`, `org.nonprofit.fundraising` |
| 267 | + |
| 268 | +2. **Namespace Derivation**: |
| 269 | + - Auto-derived from last part of package ID |
| 270 | + - `com.acme.crm` → namespace: `crm` |
| 271 | + - Can be explicitly overridden if needed |
| 272 | + |
| 273 | +3. **Scope Selection**: |
| 274 | + - `system`: Platform/framework code (read-only) |
| 275 | + - `platform`: Admin-configured (mutable, applies to all users) |
| 276 | + - `user`: User-configured (mutable, personal customizations) |
| 277 | + |
| 278 | +4. **Source Tracking**: |
| 279 | + - `filesystem`: Loaded from code files (read-only) |
| 280 | + - `database`: Stored in database (mutable) |
| 281 | + - `api`: Loaded from external API |
| 282 | + - `migration`: Created during migration |
| 283 | + |
| 284 | +## Next Steps |
| 285 | + |
| 286 | +1. Complete Phase 2: Finish enhancing all metadata tool handlers |
| 287 | +2. Implement Phase 3: Conversation context persistence |
| 288 | +3. Implement Phase 4: Metadata service write protection |
| 289 | +4. Write comprehensive tests (Phase 5) |
| 290 | +5. Update AI agent system prompts with package-first instructions |
| 291 | +6. Create user documentation and migration guide |
| 292 | + |
| 293 | +## Related Files |
| 294 | + |
| 295 | +### New Files Created |
| 296 | +- `packages/services/service-ai/src/tools/list-packages.tool.ts` |
| 297 | +- `packages/services/service-ai/src/tools/get-package.tool.ts` |
| 298 | +- `packages/services/service-ai/src/tools/create-package.tool.ts` |
| 299 | +- `packages/services/service-ai/src/tools/get-active-package.tool.ts` |
| 300 | +- `packages/services/service-ai/src/tools/set-active-package.tool.ts` |
| 301 | +- `packages/services/service-ai/src/tools/package-tools.ts` |
| 302 | + |
| 303 | +### Modified Files |
| 304 | +- `packages/services/service-ai/src/index.ts` - Added package tool exports |
| 305 | +- `packages/services/service-ai/src/tools/create-object.tool.ts` - Added packageId parameter |
| 306 | +- `packages/services/service-ai/src/tools/metadata-tools.ts` - Enhanced context interface |
| 307 | + |
| 308 | +### Existing Schema Files (Used) |
| 309 | +- `packages/spec/src/system/metadata-persistence.zod.ts` - MetadataRecordSchema |
| 310 | +- `packages/spec/src/kernel/package-registry.zod.ts` - InstalledPackageSchema |
| 311 | +- `packages/spec/src/kernel/manifest.zod.ts` - ManifestSchema |
| 312 | +- `packages/spec/src/api/package-api.zod.ts` - Package API contracts |
| 313 | +- `packages/spec/src/contracts/metadata-service.ts` - IMetadataService interface |
| 314 | + |
| 315 | +## Conclusion |
| 316 | + |
| 317 | +The foundation for package-aware metadata management has been established. The package management tools are complete and ready for use. The next phases will complete the integration with metadata tools and enforce read-only protection for code-based packages. |
| 318 | + |
| 319 | +This implementation aligns with industry best practices from Salesforce, ServiceNow, and other enterprise low-code platforms, ensuring metadata governance, version control compatibility, and safe upgrade paths. |
0 commit comments