Skip to content

Commit f9550c5

Browse files
authored
Merge pull request #755 from objectstack-ai/copilot/complete-roadmap-development
2 parents da3bdf3 + 9b3e479 commit f9550c5

14 files changed

Lines changed: 2705 additions & 29 deletions

ROADMAP.md

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ This strategy ensures rapid iteration while maintaining a clear path to producti
9898
| `.describe()` Annotations | 7,111+ |
9999
| Service Contracts | 25 |
100100
| Contracts Implemented | 13 (52%) |
101-
| Test Files | 199 |
102-
| Tests Passing | 5,805 / 5,805 |
101+
| Test Files | 208 |
102+
| Tests Passing | 5,921 / 5,921 |
103103
| `@deprecated` Items | 3 |
104104
| Protocol Domains | 15 (Data, UI, AI, API, Automation, Cloud, Contracts, Identity, Integration, Kernel, QA, Security, Shared, Studio, System) |
105105

@@ -127,10 +127,10 @@ Support record comments, @mention, activity feed, and changelog for the ObjectUI
127127
| Real-time collaboration (OT/CRDT) || `system/collaboration.zod.ts` |
128128
| `IFeedService` contract || `contracts/feed-service.ts` |
129129
| `service-feed` in-memory implementation || `@objectstack/service-feed` (40 tests) |
130-
| Pin/star comments | 🔴 | Not yet specified |
130+
| Pin/star comments | | `data/feed.zod.ts``pinned`/`starred`, `api/feed-api.zod.ts` → Pin/Star endpoints |
131131
| Comment notification integration with `INotificationService` | 🔴 | `service-notification` not implemented |
132-
| Activity feed search/filter endpoint | 🔴 | Not yet specified |
133-
| Changelog (field-level audit trail) endpoint | 🟡 | `FieldChangeSchema` exists; dedicated API pending |
132+
| Activity feed search/filter endpoint | | `api/feed-api.zod.ts``SearchFeedRequestSchema` |
133+
| Changelog (field-level audit trail) endpoint | | `api/feed-api.zod.ts``GetChangelogRequestSchema`, `ChangelogEntrySchema` |
134134

135135
### 2. Automation Persistence & Scheduling Specs
136136

@@ -146,11 +146,11 @@ Multi-stage triggers, action pipelines, execution logs, and cron scheduling stan
146146
| Retry policies with exponential backoff || `automation/webhook.zod.ts` |
147147
| `IAutomationService` contract || `contracts/automation-service.ts` |
148148
| `service-automation` DAG engine (MVP) || `@objectstack/service-automation` (27 tests) |
149-
| Execution log/history storage protocol | 🔴 | No dedicated `ExecutionLogSchema`; only string arrays in results |
150-
| Execution error tracking & diagnostics | 🔴 | Not yet specified |
151-
| Conflict resolution for concurrent executions | 🔴 | Not yet specified |
152-
| Checkpointing/resume for interrupted flows | 🔴 | Not yet specified |
153-
| Scheduled execution persistence (next-run, pause/resume) | 🔴 | Limited to cron strings; no runtime state tracking |
149+
| Execution log/history storage protocol | | `automation/execution.zod.ts` `ExecutionLogSchema`, `ExecutionStepLogSchema` |
150+
| Execution error tracking & diagnostics | | `automation/execution.zod.ts``ExecutionErrorSchema`, `ExecutionErrorSeverity` |
151+
| Conflict resolution for concurrent executions | | `automation/execution.zod.ts``ConcurrencyPolicySchema` |
152+
| Checkpointing/resume for interrupted flows | | `automation/execution.zod.ts``CheckpointSchema` |
153+
| Scheduled execution persistence (next-run, pause/resume) | | `automation/execution.zod.ts``ScheduleStateSchema` |
154154

155155
### 3. File Direct Upload & Resumable Upload Protocol
156156

@@ -165,10 +165,10 @@ CloudFile / PresignedUrl schema supporting S3/Azure/GCS direct-to-cloud file upl
165165
| Bucket encryption & CORS || `system/object-storage.zod.ts` |
166166
| `IStorageService` contract || `contracts/storage-service.ts` |
167167
| `service-storage` local FS + S3 skeleton || `@objectstack/service-storage` (8 tests) |
168-
| Chunked upload with resume token | 🔴 | No resume token for interrupted uploads |
169-
| Upload progress tracking protocol | 🔴 | Not yet specified |
170-
| Mobile / file picker / browser fallback | 🔴 | Not yet specified |
171-
| File type whitelist/blacklist validation | 🔴 | Not yet specified |
168+
| Chunked upload with resume token | | `api/storage.zod.ts``InitiateChunkedUploadRequestSchema`, `resumeToken` |
169+
| Upload progress tracking protocol | | `api/storage.zod.ts``UploadProgressSchema` |
170+
| Mobile / file picker / browser fallback | 🔴 | Not yet specified (runtime concern) |
171+
| File type whitelist/blacklist validation | | `api/storage.zod.ts``FileTypeValidationSchema` |
172172

173173
### 4. Streaming Data Export & Batch Operation Optimization
174174

@@ -182,11 +182,11 @@ Cursor/Pagination protocol for large-scale data import/export with template-base
182182
| Import mapping configuration || `data/mapping.zod.ts` |
183183
| Dataset import mode || `data/dataset.zod.ts` |
184184
| Full query & filter language || `data/filter.zod.ts` |
185-
| Streaming/chunked export endpoint (CSV/JSON/Excel) | 🔴 | Not yet specified |
186-
| Import validation & deduplication | 🔴 | Not yet specified |
187-
| Template-based field mapping for import/export | 🔴 | Mapping schema exists; no template registry |
188-
| Scheduled export jobs & status query | 🔴 | Not yet specified |
189-
| Export job progress & download URL | 🔴 | Not yet specified |
185+
| Streaming/chunked export endpoint (CSV/JSON/Excel) | | `api/export.zod.ts``CreateExportJobRequestSchema`, `ExportFormat` |
186+
| Import validation & deduplication | | `api/export.zod.ts``ImportValidationConfigSchema`, `DeduplicationStrategy` |
187+
| Template-based field mapping for import/export | | `api/export.zod.ts``ExportImportTemplateSchema`, `FieldMappingEntrySchema` |
188+
| Scheduled export jobs & status query | | `api/export.zod.ts``ScheduledExportSchema` |
189+
| Export job progress & download URL | | `api/export.zod.ts``ExportJobProgressSchema` |
190190

191191
### 5. API Capability Declaration & Service Discovery
192192

@@ -198,10 +198,10 @@ Strengthen discovery capabilities for frontend intelligent adaptation.
198198
| Dynamic API route mapping || `api/discovery.zod.ts``ApiRoutesSchema` |
199199
| Localization info (locale, timezone) || `api/discovery.zod.ts` |
200200
| Custom metadata extensions || `api/discovery.zod.ts` |
201-
| Capabilities declaration (comments, automation, search, cron, files, analytics) | 🔴 | No hierarchical capability descriptors |
202-
| Per-service version info | 🔴 | Not yet specified |
203-
| Rate limit & quota disclosure | 🔴 | Not yet specified |
204-
| OpenAPI/GraphQL schema discovery endpoint | 🔴 | Not yet specified |
201+
| Capabilities declaration (comments, automation, search, cron, files, analytics) | | `api/discovery.zod.ts``capabilities` with hierarchical descriptors |
202+
| Per-service version info | | `api/discovery.zod.ts``ServiceInfoSchema.version` |
203+
| Rate limit & quota disclosure | | `api/discovery.zod.ts``ServiceInfoSchema.rateLimit` |
204+
| OpenAPI/GraphQL schema discovery endpoint | | `api/discovery.zod.ts``DiscoverySchema.schemaDiscovery` |
205205

206206
> **Recommendation:** Sync this roadmap with ObjectUI / client / runner / console and prioritize v3.1 protocol to fill core platform gaps.
207207

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

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,3 +536,150 @@ describe('DiscoverySchema with services', () => {
536536
expect(discovery.services['ai'].enabled).toBe(false);
537537
});
538538
});
539+
540+
// ==========================================
541+
// ServiceInfoSchema — version & rateLimit
542+
// ==========================================
543+
544+
describe('ServiceInfoSchema (version field)', () => {
545+
it('should accept a service with version', () => {
546+
const info = ServiceInfoSchema.parse({
547+
enabled: true,
548+
status: 'available',
549+
route: '/api/v1/data',
550+
provider: 'objectql',
551+
version: '3.0.6',
552+
});
553+
expect(info.version).toBe('3.0.6');
554+
});
555+
556+
it('should allow version to be omitted', () => {
557+
const info = ServiceInfoSchema.parse({
558+
enabled: true,
559+
status: 'available',
560+
});
561+
expect(info.version).toBeUndefined();
562+
});
563+
});
564+
565+
describe('ServiceInfoSchema (rateLimit field)', () => {
566+
it('should accept a service with rateLimit', () => {
567+
const info = ServiceInfoSchema.parse({
568+
enabled: true,
569+
status: 'available',
570+
rateLimit: {
571+
requestsPerMinute: 60,
572+
requestsPerHour: 1000,
573+
burstLimit: 10,
574+
retryAfterMs: 5000,
575+
},
576+
});
577+
expect(info.rateLimit?.requestsPerMinute).toBe(60);
578+
expect(info.rateLimit?.burstLimit).toBe(10);
579+
});
580+
581+
it('should allow rateLimit to be omitted', () => {
582+
const info = ServiceInfoSchema.parse({
583+
enabled: true,
584+
status: 'available',
585+
});
586+
expect(info.rateLimit).toBeUndefined();
587+
});
588+
589+
it('should allow partial rateLimit fields', () => {
590+
const info = ServiceInfoSchema.parse({
591+
enabled: true,
592+
status: 'available',
593+
rateLimit: {
594+
requestsPerMinute: 100,
595+
},
596+
});
597+
expect(info.rateLimit?.requestsPerMinute).toBe(100);
598+
expect(info.rateLimit?.requestsPerHour).toBeUndefined();
599+
});
600+
});
601+
602+
// ==========================================
603+
// DiscoverySchema — capabilities
604+
// ==========================================
605+
606+
describe('DiscoverySchema (capabilities field)', () => {
607+
const fixture = {
608+
name: 'ObjectStack',
609+
version: '1.0.0',
610+
environment: 'production' as const,
611+
routes: { data: '/api/v1/data', metadata: '/api/v1/meta' },
612+
services: minimalServices,
613+
locale: { default: 'en-US', supported: ['en-US'], timezone: 'UTC' },
614+
};
615+
616+
it('should accept discovery with capabilities', () => {
617+
const discovery = DiscoverySchema.parse({
618+
...fixture,
619+
capabilities: {
620+
comments: {
621+
enabled: true,
622+
features: { threaded: true, reactions: true, mentions: true },
623+
description: 'Feed and comments support',
624+
},
625+
automation: {
626+
enabled: false,
627+
description: 'Flow automation engine',
628+
},
629+
},
630+
});
631+
expect(discovery.capabilities?.comments.enabled).toBe(true);
632+
expect(discovery.capabilities?.comments.features?.threaded).toBe(true);
633+
expect(discovery.capabilities?.automation.enabled).toBe(false);
634+
});
635+
636+
it('should allow capabilities to be omitted', () => {
637+
const discovery = DiscoverySchema.parse(fixture);
638+
expect(discovery.capabilities).toBeUndefined();
639+
});
640+
});
641+
642+
// ==========================================
643+
// DiscoverySchema — schemaDiscovery
644+
// ==========================================
645+
646+
describe('DiscoverySchema (schemaDiscovery field)', () => {
647+
const fixture = {
648+
name: 'ObjectStack',
649+
version: '1.0.0',
650+
environment: 'production' as const,
651+
routes: { data: '/api/v1/data', metadata: '/api/v1/meta' },
652+
services: minimalServices,
653+
locale: { default: 'en-US', supported: ['en-US'], timezone: 'UTC' },
654+
};
655+
656+
it('should accept discovery with schemaDiscovery', () => {
657+
const discovery = DiscoverySchema.parse({
658+
...fixture,
659+
schemaDiscovery: {
660+
openapi: '/api/v1/openapi.json',
661+
graphql: '/graphql',
662+
jsonSchema: '/api/v1/schemas',
663+
},
664+
});
665+
expect(discovery.schemaDiscovery?.openapi).toBe('/api/v1/openapi.json');
666+
expect(discovery.schemaDiscovery?.graphql).toBe('/graphql');
667+
expect(discovery.schemaDiscovery?.jsonSchema).toBe('/api/v1/schemas');
668+
});
669+
670+
it('should allow schemaDiscovery to be omitted', () => {
671+
const discovery = DiscoverySchema.parse(fixture);
672+
expect(discovery.schemaDiscovery).toBeUndefined();
673+
});
674+
675+
it('should allow partial schemaDiscovery fields', () => {
676+
const discovery = DiscoverySchema.parse({
677+
...fixture,
678+
schemaDiscovery: {
679+
openapi: '/api/v1/openapi.json',
680+
},
681+
});
682+
expect(discovery.schemaDiscovery?.openapi).toBe('/api/v1/openapi.json');
683+
expect(discovery.schemaDiscovery?.graphql).toBeUndefined();
684+
});
685+
});

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,17 @@ export const ServiceInfoSchema = z.object({
1717
route: z.string().optional().describe('e.g. /api/v1/analytics'),
1818
/** Implementation provider name */
1919
provider: z.string().optional().describe('e.g. "objectql", "plugin-redis", "driver-memory"'),
20+
/** Service version */
21+
version: z.string().optional().describe('Semantic version of the service implementation (e.g. "3.0.6")'),
2022
/** Human-readable reason if unavailable */
2123
message: z.string().optional().describe('e.g. "Install plugin-workflow to enable"'),
24+
/** Rate limit configuration for this service */
25+
rateLimit: z.object({
26+
requestsPerMinute: z.number().int().optional().describe('Maximum requests per minute'),
27+
requestsPerHour: z.number().int().optional().describe('Maximum requests per hour'),
28+
burstLimit: z.number().int().optional().describe('Maximum burst request count'),
29+
retryAfterMs: z.number().int().optional().describe('Suggested retry-after delay in milliseconds when rate-limited'),
30+
}).optional().describe('Rate limit and quota info for this service'),
2231
});
2332

2433
/**
@@ -108,6 +117,29 @@ export const DiscoverySchema = z.object({
108117
'Per-service availability map keyed by CoreServiceName'
109118
),
110119

120+
/**
121+
* Hierarchical capability descriptors.
122+
* Declares platform features so clients can adapt UI without probing individual services.
123+
* Each key is a capability domain (e.g., "comments", "automation", "search"),
124+
* and its value describes what sub-features are available.
125+
*/
126+
capabilities: z.record(z.string(), z.object({
127+
enabled: z.boolean().describe('Whether this capability is available'),
128+
features: z.record(z.string(), z.boolean()).optional()
129+
.describe('Sub-feature flags within this capability'),
130+
description: z.string().optional()
131+
.describe('Human-readable capability description'),
132+
})).optional().describe('Hierarchical capability descriptors for frontend intelligent adaptation'),
133+
134+
/**
135+
* Schema discovery URLs for cross-ecosystem interoperability.
136+
*/
137+
schemaDiscovery: z.object({
138+
openapi: z.string().optional().describe('URL to OpenAPI (Swagger) specification (e.g., "/api/v1/openapi.json")'),
139+
graphql: z.string().optional().describe('URL to GraphQL schema endpoint (e.g., "/graphql")'),
140+
jsonSchema: z.string().optional().describe('URL to JSON Schema definitions'),
141+
}).optional().describe('Schema discovery endpoints for API toolchain integration'),
142+
111143
/**
112144
* Custom metadata key-value pairs for extensibility
113145
*/

0 commit comments

Comments
 (0)