Skip to content

Commit a7984ec

Browse files
authored
Merge pull request #543 from objectstack-ai/copilot/update-development-plan
2 parents 8d561d0 + 2ef0db1 commit a7984ec

9 files changed

Lines changed: 105 additions & 102 deletions

File tree

packages/spec/DEVELOPMENT_PLAN.md

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ Based on the full audit of 139 `.zod.ts` files (43,746 LOC, 1,089 schemas), the
1313

1414
### Key Metrics Baseline
1515

16-
| Metric | Current | Phase 2 Target | Phase 4 Target |
16+
| Metric | Original | Current | Phase 4 Target |
1717
|---|---|---|---|
18-
| `z.any()` usages | 397 | < 100 | < 30 |
19-
| `z.unknown()` usages | 8 | > 200 | > 350 |
20-
| `z.infer` coverage | 93% (1,011/1,089) | 98% | 100% |
21-
| `.describe()` annotations | 5,026 | 5,300 | 5,600 |
22-
| Schema duplications | 13+ pairs | 3 | 0 |
23-
| Runtime logic violations | 2 files | 0 | 0 |
18+
| `z.any()` usages | 397 | 8 | 8 (filter operators only) |
19+
| `z.unknown()` usages | 8 | 340 | > 350 |
20+
| `z.infer` coverage | 93% (1,011/1,089) | ~98% | 100% |
21+
| `.describe()` annotations | 5,026 | 5,300+ | 5,600 |
22+
| Schema duplications | 13+ pairs | 1 | 0 |
23+
| Runtime logic violations | 2 files | 2 files | 0 |
2424
| Naming violations | 3 | 0 | 0 |
2525

2626
---
@@ -120,14 +120,14 @@ error: z.object({
120120

121121
| # | Task | File(s) | Status |
122122
|---|---|---|---|
123-
| 1.1 | Fix z.any() in handler union | `data/hook.zod.ts` | |
124-
| 1.2 | Fix ValidationRuleSchema type-safety | `data/validation.zod.ts` | |
125-
| 1.3 | Fix invalid computed key syntax | `system/auth-config.zod.ts` | |
126-
| 1.4 | Fix Mongo capabilities key names | `data/driver/mongo.zod.ts` | |
127-
| 1.5 | Fix DatasourceConfig alias | `data/datasource.zod.ts` | |
128-
| 1.6 | Replace z.instanceof(Error) | `kernel/plugin-lifecycle-events.zod.ts`, `kernel/startup-orchestrator.zod.ts` | |
129-
| 1.7 | Fix `$exist``$exists` typo | `data/filter.zod.ts` | |
130-
| 1.8 | Run full test suite, verify build || |
123+
| 1.1 | Fix z.any() in handler union | `data/hook.zod.ts` | |
124+
| 1.2 | Fix ValidationRuleSchema type-safety | `data/validation.zod.ts` | |
125+
| 1.3 | Fix invalid computed key syntax | `system/auth-config.zod.ts` | |
126+
| 1.4 | Fix Mongo capabilities key names | `data/driver/mongo.zod.ts` | |
127+
| 1.5 | Fix DatasourceConfig alias | `data/datasource.zod.ts` | |
128+
| 1.6 | Replace z.instanceof(Error) | `kernel/plugin-lifecycle-events.zod.ts`, `kernel/startup-orchestrator.zod.ts` | |
129+
| 1.7 | Fix `$exist``$exists` typo | `data/filter.zod.ts` | |
130+
| 1.8 | Run full test suite, verify build || |
131131

132132
---
133133

@@ -272,17 +272,17 @@ Replace `z.date()` with `z.string().datetime()` in serializable schemas:
272272

273273
| # | Task | File(s) | Status |
274274
|---|---|---|---|
275-
| 2.1 | Create `shared/enums.zod.ts` + update consumers | 8+ files | |
276-
| 2.2 | Create `shared/metadata-types.zod.ts` | 3 files | |
277-
| 2.3 | Deduplicate security schemas | 4 files | |
278-
| 2.4 | Rename `MetricType` in license.zod.ts | `hub/license.zod.ts` | |
279-
| 2.5 | Unify SnakeCaseIdentifierSchema usage | 4 UI files | |
280-
| 2.6 | Fix snake_case property keys | `system/metadata-persistence.zod.ts` | |
281-
| 2.7 | Replace z.date() with z.string().datetime() | 6 files | |
282-
| 2.8 | Unify isolation level enum | `data/driver.zod.ts` | |
283-
| 2.9 | Rename system/service-registry.zod.ts | 1 file + index | |
284-
| 2.10 | Deduplicate Presence schemas (realtime/websocket) | 2 files | |
285-
| 2.11 | Run full test suite, update index.ts re-exports || |
275+
| 2.1 | Create `shared/enums.zod.ts` + update consumers | 8+ files | |
276+
| 2.2 | Create `shared/metadata-types.zod.ts` | 3 files | |
277+
| 2.3 | Deduplicate security schemas | 4 files | ✅ (Kernel uses Kernel-prefixed variants) |
278+
| 2.4 | Rename `MetricType` in license.zod.ts | `hub/license.zod.ts` | |
279+
| 2.5 | Unify SnakeCaseIdentifierSchema usage | 4 UI files | |
280+
| 2.6 | Fix snake_case property keys | `system/metadata-persistence.zod.ts` | |
281+
| 2.7 | Replace z.date() with z.string().datetime() | 6 files | |
282+
| 2.8 | Unify isolation level enum | `data/driver.zod.ts` | |
283+
| 2.9 | Rename system/service-registry.zod.ts | 1 file + index | |
284+
| 2.10 | Deduplicate Presence schemas (realtime/websocket) | 2 files | |
285+
| 2.11 | Run full test suite, update index.ts re-exports || |
286286

287287
---
288288

@@ -387,18 +387,20 @@ Already replaced by `locations` array. Remove the deprecated field.
387387

388388
| # | Task | Scope | z.any() Reduction | Status |
389389
|---|---|---|---|---|
390-
| 3.1 | Bulk metadata/config z.any() → z.unknown() | ~88 files | -140 ||
391-
| 3.2 | Tighten id fields | 2 files | -2 ||
392-
| 3.3a | Harden kernel/plugin.zod.ts | 1 file | -20 ||
393-
| 3.3b | Harden data/driver.zod.ts | 1 file | -10 ||
394-
| 3.3c | Harden data/data-engine.zod.ts | 1 file | -8 ||
395-
| 3.3d | Harden kernel/events.zod.ts | 1 file | -8 ||
396-
| 3.3e | Harden kernel/manifest.zod.ts | 1 file | -7 ||
397-
| 3.4 | Fix UI z.any() with proper imports | 4 files | -6 ||
398-
| 3.5 | Remove deprecated action.location | 1 file | -1 ||
399-
| 3.6 | Run full test suite ||||
400-
401-
**Expected total reduction:** 397 → ~95 (`z.any()`)
390+
| 3.1 | Bulk metadata/config z.any() → z.unknown() | ~88 files | -140 ||
391+
| 3.2 | Tighten id fields | 2 files | -2 ||
392+
| 3.3a | Harden kernel/plugin.zod.ts | 1 file | -23 ||
393+
| 3.3b | Harden data/driver.zod.ts | 1 file | -10 ||
394+
| 3.3c | Harden data/data-engine.zod.ts | 1 file | -8 ||
395+
| 3.3d | Harden kernel/events.zod.ts | 1 file | -8 ||
396+
| 3.3e | Harden kernel/manifest.zod.ts | 1 file | -7 ||
397+
| 3.4 | Fix UI z.any() with proper imports | 4 files | -6 ||
398+
| 3.5 | Remove deprecated action.location | 1 file | -1 ||
399+
| 3.6 | Replace z.any() in api/protocol.zod.ts | 1 file | -28 ||
400+
| 3.7 | Replace z.any() in hook, core-services, widget | 3 files | -3 ||
401+
| 3.8 | Run full test suite ||||
402+
403+
**Actual total reduction:** 397 → 8 (`z.any()`) — remaining 8 are legitimate filter operators ($eq/$ne/$in/$nin)
402404

403405
---
404406

@@ -513,7 +515,7 @@ formula: z.string().optional()
513515

514516
| # | Task | Scope | Status |
515517
|---|---|---|---|
516-
| 4.1 | Add missing z.infer exports | 14 files | |
518+
| 4.1 | Add missing z.infer exports | 14 files | ✅ (1 remaining: WidgetSource added) |
517519
| 4.2 | Add z.input<> exports for transform schemas | ~20 files ||
518520
| 4.3 | Improve .describe() coverage | 9 files ||
519521
| 4.4 | Move runtime logic to core/runtime | 3 files ||

packages/spec/src/api/protocol.zod.ts

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -444,92 +444,92 @@ export {
444444
*/
445445
export const ObjectStackProtocolSchema = z.object({
446446
// Discovery & Metadata
447-
getDiscovery: z.any()
447+
getDiscovery: z.function()
448448
.describe('Get API discovery information'),
449449

450-
getMetaTypes: z.any()
450+
getMetaTypes: z.function()
451451
.describe('Get available metadata types'),
452452

453-
getMetaItems: z.any()
453+
getMetaItems: z.function()
454454
.describe('Get all items of a metadata type'),
455455

456-
getMetaItem: z.any()
456+
getMetaItem: z.function()
457457
.describe('Get a specific metadata item'),
458-
saveMetaItem: z.any()
458+
saveMetaItem: z.function()
459459
.describe('Save metadata item'),
460-
getMetaItemCached: z.any()
460+
getMetaItemCached: z.function()
461461
.describe('Get a metadata item with cache validation'),
462462

463-
getUiView: z.any()
463+
getUiView: z.function()
464464
.describe('Get UI view definition'),
465465

466466
// Analytics Operations
467-
analyticsQuery: z.any()
467+
analyticsQuery: z.function()
468468
.describe('Execute analytics query'),
469469

470-
getAnalyticsMeta: z.any()
470+
getAnalyticsMeta: z.function()
471471
.describe('Get analytics metadata (cubes)'),
472472

473473
// Automation Operations
474-
triggerAutomation: z.any()
474+
triggerAutomation: z.function()
475475
.describe('Trigger an automation flow or script'),
476476

477477
// Hub Operations
478-
listSpaces: z.any()
478+
listSpaces: z.function()
479479
.describe('List Hub Spaces'),
480480

481-
createSpace: z.any()
481+
createSpace: z.function()
482482
.describe('Create Hub Space'),
483483

484-
installPlugin: z.any()
484+
installPlugin: z.function()
485485
.describe('Install Plugin into Space'),
486486

487487
// Package Management Operations
488-
listPackages: z.any()
488+
listPackages: z.function()
489489
.describe('List installed packages with optional filters'),
490490

491-
getPackage: z.any()
491+
getPackage: z.function()
492492
.describe('Get a specific installed package by ID'),
493493

494-
installPackage: z.any()
494+
installPackage: z.function()
495495
.describe('Install a new package from manifest'),
496496

497-
uninstallPackage: z.any()
497+
uninstallPackage: z.function()
498498
.describe('Uninstall a package by ID'),
499499

500-
enablePackage: z.any()
500+
enablePackage: z.function()
501501
.describe('Enable a disabled package'),
502502

503-
disablePackage: z.any()
503+
disablePackage: z.function()
504504
.describe('Disable an installed package'),
505505

506506
// Data Operations
507-
findData: z.any()
507+
findData: z.function()
508508
.describe('Find data records'),
509509

510-
getData: z.any()
510+
getData: z.function()
511511
.describe('Get single data record'),
512512

513-
createData: z.any()
513+
createData: z.function()
514514
.describe('Create a data record'),
515515

516-
updateData: z.any()
516+
updateData: z.function()
517517
.describe('Update a data record'),
518518

519-
deleteData: z.any()
519+
deleteData: z.function()
520520
.describe('Delete a data record'),
521521

522522
// Batch Operations
523-
batchData: z.any()
523+
batchData: z.function()
524524
.describe('Perform batch operations'),
525525

526-
createManyData: z.any()
526+
createManyData: z.function()
527527
.describe('Create multiple records'),
528528

529-
updateManyData: z.any()
529+
updateManyData: z.function()
530530
.describe('Update multiple records'),
531531

532-
deleteManyData: z.any()
532+
deleteManyData: z.function()
533533
.describe('Delete multiple records'),
534534
});
535535

packages/spec/src/api/realtime.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,10 +198,13 @@ describe('RealtimePresenceStatus', () => {
198198
});
199199

200200
it('should reject invalid presence statuses', () => {
201-
expect(() => RealtimePresenceStatus.parse('busy')).toThrow();
202201
expect(() => RealtimePresenceStatus.parse('idle')).toThrow();
203202
expect(() => RealtimePresenceStatus.parse('')).toThrow();
204203
});
204+
205+
it('should accept busy status', () => {
206+
expect(() => RealtimePresenceStatus.parse('busy')).not.toThrow();
207+
});
205208
});
206209

207210
describe('RealtimePresenceSchema', () => {

packages/spec/src/api/realtime.zod.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export type Subscription = z.infer<typeof SubscriptionSchema>;
5555
export const RealtimePresenceStatus = z.enum([
5656
'online',
5757
'away',
58+
'busy',
5859
'offline',
5960
]);
6061

packages/spec/src/api/websocket.zod.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { z } from 'zod';
22
import { EventNameSchema } from '../shared/identifiers.zod';
3+
import { RealtimePresenceStatus } from './realtime.zod';
34

45
/**
56
* WebSocket Event Protocol
@@ -133,14 +134,9 @@ export type UnsubscribeRequest = z.infer<typeof UnsubscribeRequestSchema>;
133134

134135
/**
135136
* Presence Status Enum
136-
* User availability status for presence tracking
137+
* Re-exported from realtime.zod.ts for backward compatibility
137138
*/
138-
export const WebSocketPresenceStatus = z.enum([
139-
'online', // User is actively online
140-
'away', // User is idle/away
141-
'busy', // User is busy (do not disturb)
142-
'offline', // User is offline
143-
]);
139+
export const WebSocketPresenceStatus = RealtimePresenceStatus;
144140

145141
export type WebSocketPresenceStatus = z.infer<typeof WebSocketPresenceStatus>;
146142

packages/spec/src/data/hook.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ export const HookContextSchema = z.object({
157157
* Engine Access
158158
* Reference to the ObjectQL engine for performing side effects.
159159
*/
160-
ql: z.any().describe('ObjectQL Engine Reference'),
160+
ql: z.unknown().describe('ObjectQL Engine Reference'),
161161
});
162162

163163
export type Hook = z.input<typeof HookSchema>;

packages/spec/src/kernel/plugin.zod.ts

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,65 @@
11
import { z } from 'zod';
22

3-
// We use z.any() for service methods that are interfaces with function signatures,
4-
// as Zod cannot easily validate function signatures at runtime.
3+
// Service method interfaces use z.function() instead of z.any() for type safety.
54
// Generic data fields use z.unknown() for type safety.
65
export const PluginContextSchema = z.object({
76
ql: z.object({
8-
object: z.any(), // Return any to allow method chaining
9-
query: z.any(),
7+
object: z.function().describe('Get object handle for method chaining'),
8+
query: z.function().describe('Execute a query'),
109
}).passthrough().describe('ObjectQL Engine Interface'),
1110

1211
os: z.object({
13-
getCurrentUser: z.any(),
14-
getConfig: z.any(),
12+
getCurrentUser: z.function().describe('Get the current authenticated user'),
13+
getConfig: z.function().describe('Get platform configuration'),
1514
}).passthrough().describe('ObjectStack Kernel Interface'),
1615

1716
logger: z.object({
18-
debug: z.any(),
19-
info: z.any(),
20-
warn: z.any(),
21-
error: z.any(),
17+
debug: z.function().describe('Log debug message'),
18+
info: z.function().describe('Log info message'),
19+
warn: z.function().describe('Log warning message'),
20+
error: z.function().describe('Log error message'),
2221
}).passthrough().describe('Logger Interface'),
2322

2423
storage: z.object({
25-
get: z.any(),
26-
set: z.any(),
27-
delete: z.any(),
24+
get: z.function().describe('Get a value from storage'),
25+
set: z.function().describe('Set a value in storage'),
26+
delete: z.function().describe('Delete a value from storage'),
2827
}).passthrough().describe('Storage Interface'),
2928

3029
i18n: z.object({
31-
t: z.any(),
32-
getLocale: z.any(),
30+
t: z.function().describe('Translate a key'),
31+
getLocale: z.function().describe('Get current locale'),
3332
}).passthrough().describe('Internationalization Interface'),
3433

3534
metadata: z.record(z.string(), z.unknown()),
3635
events: z.record(z.string(), z.unknown()),
3736

3837
app: z.object({
3938
router: z.object({
40-
get: z.any(),
41-
post: z.any(),
42-
use: z.any(),
39+
get: z.function().describe('Register GET route handler'),
40+
post: z.function().describe('Register POST route handler'),
41+
use: z.function().describe('Register middleware'),
4342
}).passthrough()
4443
}).passthrough().describe('App Framework Interface'),
4544

4645
drivers: z.object({
47-
register: z.any(),
46+
register: z.function().describe('Register a driver'),
4847
}).passthrough().describe('Driver Registry'),
4948
});
5049

5150
export type PluginContextData = z.infer<typeof PluginContextSchema>;
5251
export type PluginContext = PluginContextData;
5352

5453
export const PluginLifecycleSchema = z.object({
55-
onInstall: z.any().optional(),
54+
onInstall: z.function().optional().describe('Called when plugin is installed'),
5655

57-
onEnable: z.any().optional(),
56+
onEnable: z.function().optional().describe('Called when plugin is enabled'),
5857

59-
onDisable: z.any().optional(),
58+
onDisable: z.function().optional().describe('Called when plugin is disabled'),
6059

61-
onUninstall: z.any().optional(),
60+
onUninstall: z.function().optional().describe('Called when plugin is uninstalled'),
6261

63-
onUpgrade: z.any().optional(),
62+
onUpgrade: z.function().optional().describe('Called when plugin is upgraded'),
6463
});
6564

6665
export type PluginLifecycleHooks = z.infer<typeof PluginLifecycleSchema>;

packages/spec/src/system/core-services.zod.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export const ServiceStatusSchema = z.object({
9595
*/
9696
export const KernelServiceMapSchema = z.record(
9797
CoreServiceName,
98-
z.any().describe('Service Instance implementing the protocol interface')
98+
z.unknown().describe('Service Instance implementing the protocol interface')
9999
);
100100

101101
// ==========================================

0 commit comments

Comments
 (0)