@elevasis/core 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +428 -42
- package/dist/index.js +596 -47
- package/dist/organization-model/index.d.ts +428 -42
- package/dist/organization-model/index.js +596 -47
- package/package.json +4 -3
- package/src/__tests__/template-foundations-compatibility.test.ts +2 -2
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +1131 -0
- package/src/_gen/__tests__/scaffold-contracts.test.ts +53 -0
- package/src/_gen/scaffold-contracts.ts +45 -0
- package/src/business/acquisition/types.ts +2 -0
- package/src/commands/queue/types/task.ts +3 -3
- package/src/execution/engine/index.ts +8 -0
- package/src/execution/engine/tools/registry.ts +26 -24
- package/src/execution/engine/tools/tool-maps.ts +13 -9
- package/src/execution/engine/workflow/types.ts +2 -3
- package/src/index.ts +10 -0
- package/src/organization-model/README.md +16 -12
- package/src/organization-model/__tests__/defaults.test.ts +175 -0
- package/src/organization-model/__tests__/domains/customers.test.ts +295 -0
- package/src/organization-model/__tests__/domains/goals.test.ts +479 -0
- package/src/organization-model/__tests__/domains/identity.test.ts +279 -0
- package/src/organization-model/__tests__/domains/navigation.test.ts +212 -0
- package/src/organization-model/__tests__/domains/offerings.test.ts +419 -0
- package/src/organization-model/__tests__/domains/operations.test.ts +203 -0
- package/src/organization-model/__tests__/domains/resource-mappings.test.ts +362 -0
- package/src/organization-model/__tests__/domains/roles.test.ts +347 -0
- package/src/organization-model/__tests__/domains/statuses.test.ts +243 -0
- package/src/organization-model/__tests__/foundation.test.ts +3 -3
- package/src/organization-model/__tests__/resolve.test.ts +447 -3
- package/src/organization-model/__tests__/schema.test.ts +407 -0
- package/src/organization-model/contracts.ts +5 -5
- package/src/organization-model/defaults.ts +39 -16
- package/src/organization-model/domains/customers.ts +75 -0
- package/src/organization-model/domains/goals.ts +80 -0
- package/src/organization-model/domains/identity.ts +94 -0
- package/src/organization-model/domains/navigation.ts +43 -4
- package/src/organization-model/domains/offerings.ts +66 -0
- package/src/organization-model/domains/operations.ts +85 -0
- package/src/organization-model/domains/{delivery.ts → projects.ts} +6 -6
- package/src/organization-model/domains/{lead-gen.ts → prospecting.ts} +5 -5
- package/src/organization-model/domains/roles.ts +55 -0
- package/src/organization-model/domains/sales.ts +94 -0
- package/src/organization-model/domains/shared.ts +30 -1
- package/src/organization-model/domains/statuses.ts +130 -0
- package/src/organization-model/index.ts +3 -3
- package/src/organization-model/organization-graph.mdx +1 -0
- package/src/organization-model/organization-model.mdx +84 -19
- package/src/organization-model/published.ts +53 -8
- package/src/organization-model/schema.ts +67 -7
- package/src/organization-model/types.ts +31 -7
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/types.ts +1 -1
- package/src/projects/api-schemas.ts +1 -0
- package/src/reference/_generated/contracts.md +116 -8
- package/src/reference/glossary.md +25 -4
- package/src/requests/__tests__/api-schemas.test.ts +277 -0
- package/src/requests/api-schemas.ts +83 -0
- package/src/requests/index.ts +1 -0
- package/src/scaffold-registry/__tests__/schema.test.ts +280 -0
- package/src/scaffold-registry/index.ts +194 -0
- package/src/scaffold-registry/schema.ts +144 -0
- package/src/supabase/database.types.ts +158 -6
- package/src/organization-model/domains/crm.ts +0 -46
- /package/src/business/{delivery → projects}/index.ts +0 -0
- /package/src/business/{delivery → projects}/types.ts +0 -0
- /package/src/business/{crm → sales}/api-schemas.ts +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapshot test for the scaffold-contracts generator wrapper.
|
|
3
|
+
*
|
|
4
|
+
* Calls generate() (which runs the real script) and asserts:
|
|
5
|
+
* 1. The output file exists and has content.
|
|
6
|
+
* 2. The content matches the stored snapshot (drift detection).
|
|
7
|
+
*
|
|
8
|
+
* If the snapshot is missing it is created on first run. Subsequent runs fail
|
|
9
|
+
* if the output changes without an intentional snapshot update.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { readFileSync } from 'node:fs'
|
|
13
|
+
import { resolve } from 'node:path'
|
|
14
|
+
import { describe, it, expect } from 'vitest'
|
|
15
|
+
|
|
16
|
+
/** Monorepo root relative to packages/core/src/_gen/__tests__/ */
|
|
17
|
+
const ROOT = resolve(import.meta.dirname, '..', '..', '..', '..', '..')
|
|
18
|
+
|
|
19
|
+
const OUTPUT_PATH = resolve(ROOT, 'packages/core/src/reference/_generated/contracts.md')
|
|
20
|
+
|
|
21
|
+
describe('scaffold-contracts generator', () => {
|
|
22
|
+
it('output file exists and has content', () => {
|
|
23
|
+
// The generator must have been run (either manually or by CI gen step).
|
|
24
|
+
// This test validates the committed artifact — it does NOT re-run the generator
|
|
25
|
+
// so the test suite stays fast and deterministic.
|
|
26
|
+
let content: string
|
|
27
|
+
try {
|
|
28
|
+
content = readFileSync(OUTPUT_PATH, 'utf8')
|
|
29
|
+
} catch {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Generated file not found: ${OUTPUT_PATH}\n` +
|
|
32
|
+
`Run "pnpm scaffold:generate" or "node scripts/monorepo/generate-scaffold-contracts.js" first.`
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
expect(content.length).toBeGreaterThan(0)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('output file matches stored snapshot', async () => {
|
|
40
|
+
let content: string
|
|
41
|
+
try {
|
|
42
|
+
content = readFileSync(OUTPUT_PATH, 'utf8')
|
|
43
|
+
} catch {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Generated file not found: ${OUTPUT_PATH}\n` +
|
|
46
|
+
`Run "pnpm scaffold:generate" first to produce the artifact before snapshotting.`
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Snapshot stored alongside test file in __snapshots__/
|
|
51
|
+
await expect(content).toMatchFileSnapshot(resolve(import.meta.dirname, '__snapshots__', 'contracts.md.snap'))
|
|
52
|
+
})
|
|
53
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin _gen/ wrapper for the scaffold-contracts generator.
|
|
3
|
+
*
|
|
4
|
+
* The actual generation logic lives in scripts/monorepo/generate-scaffold-contracts.js.
|
|
5
|
+
* This wrapper provides the canonical generate() entry point so the generator
|
|
6
|
+
* participates in the @repo/gen-utils pattern (snapshot tests, drift detection,
|
|
7
|
+
* Turbo gen task).
|
|
8
|
+
*
|
|
9
|
+
* DO NOT move logic here — edit the script instead, then re-run generate().
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { spawnSync } from 'node:child_process'
|
|
13
|
+
import { fileURLToPath } from 'node:url'
|
|
14
|
+
import { resolve, dirname } from 'node:path'
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
17
|
+
const __dirname = dirname(__filename)
|
|
18
|
+
|
|
19
|
+
/** Monorepo root — four levels up from packages/core/src/_gen/ */
|
|
20
|
+
const ROOT = resolve(__dirname, '..', '..', '..', '..')
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Run the scaffold-contracts generator.
|
|
24
|
+
* Delegates to scripts/monorepo/generate-scaffold-contracts.js via child process
|
|
25
|
+
* so the existing script is the single source of implementation.
|
|
26
|
+
*/
|
|
27
|
+
export function generate(): void {
|
|
28
|
+
const result = spawnSync('node', ['scripts/monorepo/generate-scaffold-contracts.js'], {
|
|
29
|
+
stdio: 'inherit',
|
|
30
|
+
cwd: ROOT
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
if (result.error) {
|
|
34
|
+
throw new Error(`scaffold-contracts generator failed to spawn: ${result.error.message}`)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (result.status !== 0) {
|
|
38
|
+
throw new Error(`scaffold-contracts generator exited with status ${result.status}`)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Allow direct invocation: node packages/core/src/_gen/scaffold-contracts.ts
|
|
43
|
+
if (process.argv[1] && resolve(process.argv[1]) === __filename) {
|
|
44
|
+
generate()
|
|
45
|
+
}
|
|
@@ -25,7 +25,7 @@ export interface Task extends OriginTracking {
|
|
|
25
25
|
humanCheckpoint?: string
|
|
26
26
|
|
|
27
27
|
// Status (updated to include 'completed')
|
|
28
|
-
status:
|
|
28
|
+
status: QueueTaskStatus
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Target resource tracking — mirrors origin columns.
|
|
@@ -59,7 +59,7 @@ export interface Task extends OriginTracking {
|
|
|
59
59
|
* - failed: execution failed, task can be retried
|
|
60
60
|
* - expired: timed out before action
|
|
61
61
|
*/
|
|
62
|
-
export type
|
|
62
|
+
export type QueueTaskStatus = 'pending' | 'processing' | 'completed' | 'failed' | 'expired'
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* Parameters for creating a new HITL task
|
|
@@ -106,7 +106,7 @@ export interface PatchTaskParams {
|
|
|
106
106
|
*/
|
|
107
107
|
export interface ListTasksFilters {
|
|
108
108
|
organizationId: string
|
|
109
|
-
status?:
|
|
109
|
+
status?: QueueTaskStatus
|
|
110
110
|
humanCheckpoint?: string
|
|
111
111
|
timeRange?: TimeRange
|
|
112
112
|
priorityMin?: number
|
|
@@ -278,12 +278,20 @@ export {
|
|
|
278
278
|
type AddContactsToListResult,
|
|
279
279
|
type BulkImportParams,
|
|
280
280
|
type BulkImportResult,
|
|
281
|
+
type BulkImportCompanyEntry,
|
|
282
|
+
type BulkImportCompaniesParams,
|
|
283
|
+
type BulkImportCompaniesResult,
|
|
281
284
|
type DeactivateContactsByCompanyParams,
|
|
282
285
|
type DeactivateContactsByCompanyResult,
|
|
283
286
|
// Deal types (acq_deals table)
|
|
284
287
|
type UpsertDealParams,
|
|
285
288
|
type AcqDeal,
|
|
286
289
|
type DealActivityEntry,
|
|
290
|
+
// Deal analytics types
|
|
291
|
+
type DealStageSummary,
|
|
292
|
+
type StaleDeal,
|
|
293
|
+
type DealPipelineAnalytics,
|
|
294
|
+
type DealAnalyticsParams,
|
|
287
295
|
// Deal-sync param types
|
|
288
296
|
type UpdateDiscoveryDataParams,
|
|
289
297
|
type UpdateProposalDataParams,
|
|
@@ -21,14 +21,9 @@ import type {
|
|
|
21
21
|
MilestoneRow,
|
|
22
22
|
TaskRow,
|
|
23
23
|
NoteRow
|
|
24
|
-
} from '../../../business/
|
|
25
|
-
import type {
|
|
26
|
-
|
|
27
|
-
DealDetail,
|
|
28
|
-
DealStage,
|
|
29
|
-
ListDealsQuery
|
|
30
|
-
} from '../../../business/acquisition'
|
|
31
|
-
import type { RecentActivityEntry } from '../../../business/crm/api-schemas'
|
|
24
|
+
} from '../../../business/projects'
|
|
25
|
+
import type { DealListItem, DealDetail, DealStage, ListDealsQuery } from '../../../business/acquisition'
|
|
26
|
+
import type { RecentActivityEntry } from '../../../business/sales/api-schemas'
|
|
32
27
|
import type { Database } from '../../../supabase/database.types'
|
|
33
28
|
|
|
34
29
|
/**
|
|
@@ -148,12 +143,24 @@ export interface ICrmService {
|
|
|
148
143
|
getDeal(dealId: string, organizationId: string): Promise<DealDetail | null>
|
|
149
144
|
getDealByEmail(email: string, organizationId: string): Promise<DealDetail | null>
|
|
150
145
|
updateDealStage(dealId: string, organizationId: string, stage: DealStage): Promise<void>
|
|
151
|
-
createDealNote(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
146
|
+
createDealNote(
|
|
147
|
+
params: import('./lead-service-types').CreateDealNoteParams
|
|
148
|
+
): Promise<import('./lead-service-types').AcqDealNote>
|
|
149
|
+
listDealNotes(
|
|
150
|
+
params: import('./lead-service-types').ListDealNotesParams
|
|
151
|
+
): Promise<import('./lead-service-types').AcqDealNote[]>
|
|
152
|
+
createDealTask(
|
|
153
|
+
params: import('./lead-service-types').CreateDealTaskParams
|
|
154
|
+
): Promise<import('./lead-service-types').AcqDealTask>
|
|
155
|
+
listDealTasks(
|
|
156
|
+
params: import('./lead-service-types').ListDealTasksParams
|
|
157
|
+
): Promise<import('./lead-service-types').AcqDealTask[]>
|
|
158
|
+
listDealTasksDue(
|
|
159
|
+
params: import('./lead-service-types').ListDealTasksDueParams
|
|
160
|
+
): Promise<import('./lead-service-types').AcqDealTask[]>
|
|
161
|
+
completeDealTask(
|
|
162
|
+
params: import('./lead-service-types').CompleteDealTaskParams
|
|
163
|
+
): Promise<import('./lead-service-types').AcqDealTask>
|
|
157
164
|
recordActivity(params: import('./lead-service-types').RecordDealActivityParams): Promise<void>
|
|
158
165
|
deleteDeal(params: import('./lead-service-types').DeleteDealParams): Promise<void>
|
|
159
166
|
}
|
|
@@ -208,11 +215,7 @@ export interface IProjectsService {
|
|
|
208
215
|
): Promise<TaskRow[]>
|
|
209
216
|
getTask(id: string, organizationId: string): Promise<TaskRow | null>
|
|
210
217
|
createTask(input: Omit<TaskInsert, 'organization_id'> & { organizationId: string }): Promise<TaskRow>
|
|
211
|
-
updateTask(
|
|
212
|
-
id: string,
|
|
213
|
-
organizationId: string,
|
|
214
|
-
input: Omit<TaskUpdate, 'organization_id'>
|
|
215
|
-
): Promise<TaskRow | null>
|
|
218
|
+
updateTask(id: string, organizationId: string, input: Omit<TaskUpdate, 'organization_id'>): Promise<TaskRow | null>
|
|
216
219
|
deleteTask(id: string, organizationId: string): Promise<boolean>
|
|
217
220
|
mergeTaskResumeContext(
|
|
218
221
|
id: string,
|
|
@@ -221,11 +224,7 @@ export interface IProjectsService {
|
|
|
221
224
|
): Promise<TaskResumeContextRow | null>
|
|
222
225
|
listNotes(projectId: string, organizationId: string): Promise<NoteRow[]>
|
|
223
226
|
createNote(input: Omit<NoteInsert, 'organization_id'> & { organizationId: string }): Promise<NoteRow>
|
|
224
|
-
updateNote(
|
|
225
|
-
id: string,
|
|
226
|
-
organizationId: string,
|
|
227
|
-
input: Omit<NoteUpdate, 'organization_id'>
|
|
228
|
-
): Promise<NoteRow | null>
|
|
227
|
+
updateNote(id: string, organizationId: string, input: Omit<NoteUpdate, 'organization_id'>): Promise<NoteRow | null>
|
|
229
228
|
deleteNote(id: string, organizationId: string): Promise<boolean>
|
|
230
229
|
}
|
|
231
230
|
|
|
@@ -548,6 +547,9 @@ export {
|
|
|
548
547
|
type AddContactsToListResult,
|
|
549
548
|
type BulkImportParams,
|
|
550
549
|
type BulkImportResult,
|
|
550
|
+
type BulkImportCompanyEntry,
|
|
551
|
+
type BulkImportCompaniesParams,
|
|
552
|
+
type BulkImportCompaniesResult,
|
|
551
553
|
type DeactivateContactsByCompanyParams,
|
|
552
554
|
type DeactivateContactsByCompanyResult,
|
|
553
555
|
type ILeadService,
|
|
@@ -261,8 +261,8 @@ import type {
|
|
|
261
261
|
} from './lead-service-types'
|
|
262
262
|
|
|
263
263
|
import type { DealListItem, DealDetail } from '../../../business/acquisition'
|
|
264
|
-
import { CrmSchemas } from '../../../business/
|
|
265
|
-
import type { RecentActivityEntry } from '../../../business/
|
|
264
|
+
import { CrmSchemas } from '../../../business/sales/api-schemas'
|
|
265
|
+
import type { RecentActivityEntry } from '../../../business/sales/api-schemas'
|
|
266
266
|
import { DealSchemas } from '../../../business/acquisition'
|
|
267
267
|
import { ProjectSchemas } from '../../../projects/api-schemas'
|
|
268
268
|
import type {
|
|
@@ -272,7 +272,7 @@ import type {
|
|
|
272
272
|
NoteRow,
|
|
273
273
|
ProjectWithCounts,
|
|
274
274
|
ProjectDetail
|
|
275
|
-
} from '../../../business/
|
|
275
|
+
} from '../../../business/projects'
|
|
276
276
|
import type { z } from 'zod'
|
|
277
277
|
|
|
278
278
|
// ---------------------------------------------------------------------------
|
|
@@ -357,14 +357,14 @@ export type NotificationToolMap = {
|
|
|
357
357
|
type ProjectsListParams = z.infer<typeof ProjectSchemas.GetProjectsQuery>
|
|
358
358
|
type ProjectCreateParams = z.infer<typeof ProjectSchemas.CreateProjectRequest>
|
|
359
359
|
type ProjectUpdateParams = z.infer<typeof ProjectSchemas.UpdateProjectRequest>
|
|
360
|
-
type MilestoneCreateParams =
|
|
361
|
-
z.infer<typeof ProjectSchemas.
|
|
360
|
+
type MilestoneCreateParams = z.infer<typeof ProjectSchemas.ProjectIdPathParams> &
|
|
361
|
+
z.infer<typeof ProjectSchemas.CreateMilestoneRequest>
|
|
362
362
|
type MilestoneUpdateParams = z.infer<typeof ProjectSchemas.UpdateMilestoneRequest>
|
|
363
363
|
type TaskListParams = z.infer<typeof ProjectSchemas.ProjectIdPathParams> & z.infer<typeof ProjectSchemas.GetTasksQuery>
|
|
364
364
|
type TaskCreateParams = z.infer<typeof ProjectSchemas.CreateTaskRequest>
|
|
365
365
|
type TaskUpdateParams = z.infer<typeof ProjectSchemas.UpdateTaskRequest>
|
|
366
|
-
type TaskResumeContextParams =
|
|
367
|
-
z.infer<typeof ProjectSchemas.
|
|
366
|
+
type TaskResumeContextParams = z.infer<typeof ProjectSchemas.TaskIdParams> &
|
|
367
|
+
z.infer<typeof ProjectSchemas.MergeResumeContextRequest>
|
|
368
368
|
type NoteListParams = z.infer<typeof ProjectSchemas.ProjectIdPathParams>
|
|
369
369
|
type NoteCreateParams = z.infer<typeof ProjectSchemas.CreateNoteRequest>
|
|
370
370
|
type NoteUpdateParams = z.infer<typeof ProjectSchemas.UpdateNoteRequest>
|
|
@@ -378,7 +378,10 @@ export type ProjectsToolMap = {
|
|
|
378
378
|
deleteProject: { params: z.infer<typeof ProjectSchemas.ProjectIdParams>; result: void }
|
|
379
379
|
listMilestones: { params: z.infer<typeof ProjectSchemas.ProjectIdPathParams>; result: MilestoneRow[] }
|
|
380
380
|
createMilestone: { params: MilestoneCreateParams; result: MilestoneRow }
|
|
381
|
-
updateMilestone: {
|
|
381
|
+
updateMilestone: {
|
|
382
|
+
params: z.infer<typeof ProjectSchemas.MilestoneIdParams> & MilestoneUpdateParams
|
|
383
|
+
result: MilestoneRow
|
|
384
|
+
}
|
|
382
385
|
deleteMilestone: { params: z.infer<typeof ProjectSchemas.MilestoneIdParams>; result: void }
|
|
383
386
|
listTasks: { params: TaskListParams; result: TaskRow[] }
|
|
384
387
|
getTask: { params: z.infer<typeof ProjectSchemas.TaskIdParams>; result: TaskRow }
|
|
@@ -399,7 +402,8 @@ export type ProjectsToolMap = {
|
|
|
399
402
|
type CrmRecentActivityParams = Partial<z.infer<typeof CrmSchemas.GetRecentActivityQuery>>
|
|
400
403
|
type CrmListDealsParams = Partial<z.infer<typeof DealSchemas.ListDealsQuery>>
|
|
401
404
|
type CrmGetDealParams = z.infer<typeof DealSchemas.DealIdParams>
|
|
402
|
-
type CrmUpdateDealStageParams = z.infer<typeof DealSchemas.DealIdParams> &
|
|
405
|
+
type CrmUpdateDealStageParams = z.infer<typeof DealSchemas.DealIdParams> &
|
|
406
|
+
z.infer<typeof DealSchemas.SyncDealStageRequest>
|
|
403
407
|
type CrmGetDealByEmailParams = { email: string }
|
|
404
408
|
type CrmTaskDueParams = Partial<z.infer<typeof DealSchemas.ListDealTasksDueQuery>>
|
|
405
409
|
type CrmDealTaskParams = Omit<CreateDealTaskParams, 'organizationId'>
|
|
@@ -28,10 +28,9 @@ export type StepHandler = (input: unknown, context: ExecutionContext) => Promise
|
|
|
28
28
|
// Step type for flow control
|
|
29
29
|
export const StepType = {
|
|
30
30
|
LINEAR: 'linear',
|
|
31
|
-
CONDITIONAL: 'conditional'
|
|
31
|
+
CONDITIONAL: 'conditional'
|
|
32
32
|
} as const
|
|
33
|
-
|
|
34
|
-
export type StepType = typeof StepType[keyof typeof StepType]
|
|
33
|
+
export type StepType = (typeof StepType)[keyof typeof StepType]
|
|
35
34
|
|
|
36
35
|
// Next step configuration types
|
|
37
36
|
export interface LinearNext {
|
package/src/index.ts
CHANGED
|
@@ -52,3 +52,13 @@ export * from './integrations/oauth/index'
|
|
|
52
52
|
|
|
53
53
|
// Credential types and utilities
|
|
54
54
|
export * from './integrations/credentials/index'
|
|
55
|
+
|
|
56
|
+
// Scaffold registry — browser-safe schema types and Zod schemas only.
|
|
57
|
+
// For server-side loader functions, use: import { ... } from '@repo/core/scaffold-registry'
|
|
58
|
+
export { ScaffoldRegistrySchema, ScaffoldEntryKindSchema } from './scaffold-registry/schema'
|
|
59
|
+
export type {
|
|
60
|
+
ScaffoldRegistry,
|
|
61
|
+
ScaffoldRegistryEntry,
|
|
62
|
+
ScaffoldRef,
|
|
63
|
+
ScaffoldEntryKind
|
|
64
|
+
} from './scaffold-registry/schema'
|
|
@@ -14,7 +14,7 @@ The public entry point exposes:
|
|
|
14
14
|
- `resolveOrganizationModel`
|
|
15
15
|
- `PROJECTS_FEATURE_ID`
|
|
16
16
|
- `PROJECTS_INDEX_SURFACE_ID`
|
|
17
|
-
- `
|
|
17
|
+
- `PROJECTS_VIEW_CAPABILITY_ID`
|
|
18
18
|
- `OrganizationModel` and the supporting domain types
|
|
19
19
|
|
|
20
20
|
Import it from the published subpath:
|
|
@@ -22,7 +22,7 @@ Import it from the published subpath:
|
|
|
22
22
|
```ts
|
|
23
23
|
import {
|
|
24
24
|
DEFAULT_ORGANIZATION_MODEL,
|
|
25
|
-
|
|
25
|
+
PROJECTS_VIEW_CAPABILITY_ID,
|
|
26
26
|
defineOrganizationModel,
|
|
27
27
|
PROJECTS_FEATURE_ID,
|
|
28
28
|
PROJECTS_INDEX_SURFACE_ID,
|
|
@@ -41,30 +41,34 @@ Top-level fields:
|
|
|
41
41
|
- `branding` - organization branding defaults and overrides.
|
|
42
42
|
- `features` - unified feature entries that combine enablement, labels, and semantic references.
|
|
43
43
|
- `navigation` - navigation model for the product shell.
|
|
44
|
-
- `
|
|
45
|
-
- `
|
|
46
|
-
- `
|
|
47
|
-
- `
|
|
44
|
+
- `sales` - sales pipeline and relationship management.
|
|
45
|
+
- `prospecting` - lists, companies, and contacts.
|
|
46
|
+
- `projects` - projects, milestones, and tasks.
|
|
47
|
+
- `identity`, `customers`, `offerings`, `roles`, `goals` - reality domains (2026-04 expansion).
|
|
48
|
+
- `statuses` - unified status registry across delivery / hitl / execution / request / schedule.
|
|
49
|
+
- `operations` - catalog of stateful runtime entities (HITL queue, sessions, executions, notifications, schedules).
|
|
50
|
+
- `resourceMappings` - cross-surface resource mappings (includes techStack subsection).
|
|
48
51
|
|
|
49
52
|
## Default Feature Set
|
|
50
53
|
|
|
51
|
-
The default model includes
|
|
54
|
+
The default model includes eight feature IDs:
|
|
52
55
|
|
|
53
|
-
- `
|
|
54
|
-
- `
|
|
56
|
+
- `sales` - deal pipeline and relationship management.
|
|
57
|
+
- `prospecting` - lists, companies, and contacts.
|
|
55
58
|
- `projects` - projects, milestones, and tasks.
|
|
56
59
|
- `operations` - organization graph and command-view surfaces.
|
|
57
60
|
- `monitoring` - monitoring surfaces.
|
|
58
61
|
- `settings` - organization settings.
|
|
59
62
|
- `seo` - SEO surfaces (disabled by default).
|
|
63
|
+
- `configure` - `/configure` skill entry point (external projects).
|
|
60
64
|
|
|
61
65
|
## Project Bridge Constants
|
|
62
66
|
|
|
63
|
-
The organization-model surface
|
|
67
|
+
The organization-model surface exports a narrow set of canonical IDs for the shared Projects bridge:
|
|
64
68
|
|
|
65
69
|
- `PROJECTS_FEATURE_ID` -> `projects`
|
|
66
70
|
- `PROJECTS_INDEX_SURFACE_ID` -> `projects.index`
|
|
67
|
-
- `
|
|
71
|
+
- `PROJECTS_VIEW_CAPABILITY_ID` -> `delivery.projects.view`
|
|
68
72
|
|
|
69
73
|
Use these when wiring shared UI manifests, template adapters, or other consumers that need to agree on the same Projects contract without repeating raw literals.
|
|
70
74
|
|
|
@@ -89,5 +93,5 @@ Use these when wiring shared UI manifests, template adapters, or other consumers
|
|
|
89
93
|
|
|
90
94
|
- Use `resolveOrganizationModel()` when you need a runtime-safe model for rendering or policy checks.
|
|
91
95
|
- Use `defineOrganizationModel()` when authoring a static partial model in source.
|
|
92
|
-
- Treat feature IDs such as `
|
|
96
|
+
- Treat feature IDs such as `sales`, `prospecting`, and `projects` as the canonical shell-level contract in new organization-model authoring.
|
|
93
97
|
- Keep feature IDs, surface IDs, and capability IDs stable because downstream UI and policy code depend on them.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { DEFAULT_ORGANIZATION_MODEL } from '../defaults'
|
|
3
|
+
import { DEFAULT_ORGANIZATION_MODEL_BRANDING } from '../domains/branding'
|
|
4
|
+
import { OrganizationModelBrandingSchema } from '../domains/branding'
|
|
5
|
+
import { DEFAULT_ORGANIZATION_MODEL_SALES } from '../domains/sales'
|
|
6
|
+
import { OrganizationModelSalesSchema } from '../domains/sales'
|
|
7
|
+
import { DEFAULT_ORGANIZATION_MODEL_PROJECTS } from '../domains/projects'
|
|
8
|
+
import { OrganizationModelProjectsSchema } from '../domains/projects'
|
|
9
|
+
import { DEFAULT_ORGANIZATION_MODEL_PROSPECTING } from '../domains/prospecting'
|
|
10
|
+
import { OrganizationModelProspectingSchema } from '../domains/prospecting'
|
|
11
|
+
import { DEFAULT_ORGANIZATION_MODEL_NAVIGATION } from '../domains/navigation'
|
|
12
|
+
import { OrganizationModelNavigationSchema } from '../domains/navigation'
|
|
13
|
+
import { DEFAULT_ORGANIZATION_MODEL_OPERATIONS } from '../domains/operations'
|
|
14
|
+
import { OperationsDomainSchema } from '../domains/operations'
|
|
15
|
+
import { DEFAULT_ORGANIZATION_MODEL_STATUSES } from '../domains/statuses'
|
|
16
|
+
import { StatusesDomainSchema } from '../domains/statuses'
|
|
17
|
+
import { resolveOrganizationModel } from '../resolve'
|
|
18
|
+
import { OrganizationModelSchema } from '../schema'
|
|
19
|
+
|
|
20
|
+
// All DEFAULT_ORGANIZATION_MODEL* constants and the schemas used to validate them.
|
|
21
|
+
// The composite constant lives in defaults.ts; domain sub-constants live in their
|
|
22
|
+
// respective domain files. All are covered here for roundtrip integrity.
|
|
23
|
+
const domainCases = [
|
|
24
|
+
{
|
|
25
|
+
name: 'DEFAULT_ORGANIZATION_MODEL_BRANDING',
|
|
26
|
+
constant: DEFAULT_ORGANIZATION_MODEL_BRANDING,
|
|
27
|
+
schema: OrganizationModelBrandingSchema
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'DEFAULT_ORGANIZATION_MODEL_SALES',
|
|
31
|
+
constant: DEFAULT_ORGANIZATION_MODEL_SALES,
|
|
32
|
+
schema: OrganizationModelSalesSchema
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'DEFAULT_ORGANIZATION_MODEL_PROJECTS',
|
|
36
|
+
constant: DEFAULT_ORGANIZATION_MODEL_PROJECTS,
|
|
37
|
+
schema: OrganizationModelProjectsSchema
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'DEFAULT_ORGANIZATION_MODEL_PROSPECTING',
|
|
41
|
+
constant: DEFAULT_ORGANIZATION_MODEL_PROSPECTING,
|
|
42
|
+
schema: OrganizationModelProspectingSchema
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: 'DEFAULT_ORGANIZATION_MODEL_NAVIGATION',
|
|
46
|
+
constant: DEFAULT_ORGANIZATION_MODEL_NAVIGATION,
|
|
47
|
+
schema: OrganizationModelNavigationSchema
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'DEFAULT_ORGANIZATION_MODEL_STATUSES',
|
|
51
|
+
constant: DEFAULT_ORGANIZATION_MODEL_STATUSES,
|
|
52
|
+
schema: StatusesDomainSchema
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'DEFAULT_ORGANIZATION_MODEL_OPERATIONS',
|
|
56
|
+
constant: DEFAULT_ORGANIZATION_MODEL_OPERATIONS,
|
|
57
|
+
schema: OperationsDomainSchema
|
|
58
|
+
}
|
|
59
|
+
] as const
|
|
60
|
+
|
|
61
|
+
describe('organization-model defaults', () => {
|
|
62
|
+
describe('DEFAULT_ORGANIZATION_MODEL (composite)', () => {
|
|
63
|
+
it('passes OrganizationModelSchema.parse without throwing', () => {
|
|
64
|
+
expect(() => OrganizationModelSchema.parse(DEFAULT_ORGANIZATION_MODEL)).not.toThrow()
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('resolveOrganizationModel(DEFAULT_ORGANIZATION_MODEL) returns a schema-valid model', () => {
|
|
68
|
+
const result = resolveOrganizationModel(DEFAULT_ORGANIZATION_MODEL)
|
|
69
|
+
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('resolveOrganizationModel with no override equals resolveOrganizationModel with DEFAULT_ORGANIZATION_MODEL', () => {
|
|
73
|
+
const fromUndefined = resolveOrganizationModel(undefined)
|
|
74
|
+
const fromDefault = resolveOrganizationModel(DEFAULT_ORGANIZATION_MODEL)
|
|
75
|
+
expect(fromDefault).toEqual(fromUndefined)
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
describe.each(domainCases)('$name (domain sub-constant)', ({ name: _name, constant, schema }) => {
|
|
80
|
+
it('passes its domain schema parse without throwing', () => {
|
|
81
|
+
expect(() => schema.parse(constant)).not.toThrow()
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('DEFAULT_ORGANIZATION_MODEL_BRANDING via resolveOrganizationModel', () => {
|
|
86
|
+
it('resolving with branding override produces a schema-valid model', () => {
|
|
87
|
+
const result = resolveOrganizationModel({ branding: DEFAULT_ORGANIZATION_MODEL_BRANDING })
|
|
88
|
+
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
describe('DEFAULT_ORGANIZATION_MODEL_SALES via resolveOrganizationModel', () => {
|
|
93
|
+
it('resolving with sales override produces a schema-valid model', () => {
|
|
94
|
+
const result = resolveOrganizationModel({ sales: DEFAULT_ORGANIZATION_MODEL_SALES })
|
|
95
|
+
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
describe('DEFAULT_ORGANIZATION_MODEL_PROJECTS via resolveOrganizationModel', () => {
|
|
100
|
+
it('resolving with projects override produces a schema-valid model', () => {
|
|
101
|
+
const result = resolveOrganizationModel({ projects: DEFAULT_ORGANIZATION_MODEL_PROJECTS })
|
|
102
|
+
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
describe('DEFAULT_ORGANIZATION_MODEL_PROSPECTING via resolveOrganizationModel', () => {
|
|
107
|
+
it('resolving with prospecting override produces a schema-valid model', () => {
|
|
108
|
+
const result = resolveOrganizationModel({ prospecting: DEFAULT_ORGANIZATION_MODEL_PROSPECTING })
|
|
109
|
+
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
110
|
+
})
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
describe('DEFAULT_ORGANIZATION_MODEL_NAVIGATION via resolveOrganizationModel', () => {
|
|
114
|
+
it('resolving with navigation override produces a schema-valid model', () => {
|
|
115
|
+
const result = resolveOrganizationModel({ navigation: DEFAULT_ORGANIZATION_MODEL_NAVIGATION })
|
|
116
|
+
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
describe('DEFAULT_ORGANIZATION_MODEL_OPERATIONS via resolveOrganizationModel', () => {
|
|
121
|
+
it('resolving with operations override produces a schema-valid model', () => {
|
|
122
|
+
const result = resolveOrganizationModel({ operations: DEFAULT_ORGANIZATION_MODEL_OPERATIONS })
|
|
123
|
+
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('default operations seed covers all 5 entity categories', () => {
|
|
127
|
+
expect(DEFAULT_ORGANIZATION_MODEL_OPERATIONS.entries).toHaveLength(5)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('every entry has a non-empty id, label, and semanticClass', () => {
|
|
131
|
+
for (const entry of DEFAULT_ORGANIZATION_MODEL_OPERATIONS.entries) {
|
|
132
|
+
expect(entry.id.length).toBeGreaterThan(0)
|
|
133
|
+
expect(entry.label.length).toBeGreaterThan(0)
|
|
134
|
+
expect(entry.semanticClass.length).toBeGreaterThan(0)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('all entry ids are unique', () => {
|
|
139
|
+
const ids = DEFAULT_ORGANIZATION_MODEL_OPERATIONS.entries.map((e) => e.id)
|
|
140
|
+
const uniqueIds = new Set(ids)
|
|
141
|
+
expect(uniqueIds.size).toBe(ids.length)
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
describe('DEFAULT_ORGANIZATION_MODEL_STATUSES via resolveOrganizationModel', () => {
|
|
146
|
+
it('resolving with statuses override produces a schema-valid model', () => {
|
|
147
|
+
const result = resolveOrganizationModel({ statuses: DEFAULT_ORGANIZATION_MODEL_STATUSES })
|
|
148
|
+
expect(() => OrganizationModelSchema.parse(result)).not.toThrow()
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('default statuses seed covers all 9 delivery.task values', () => {
|
|
152
|
+
const taskEntries = DEFAULT_ORGANIZATION_MODEL_STATUSES.entries.filter((e) => e.semanticClass === 'delivery.task')
|
|
153
|
+
expect(taskEntries).toHaveLength(9)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
it('default statuses seed covers all 5 queue values', () => {
|
|
157
|
+
const queueEntries = DEFAULT_ORGANIZATION_MODEL_STATUSES.entries.filter((e) => e.semanticClass === 'queue')
|
|
158
|
+
expect(queueEntries).toHaveLength(5)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
it('every entry has a non-empty id, label, and semanticClass', () => {
|
|
162
|
+
for (const entry of DEFAULT_ORGANIZATION_MODEL_STATUSES.entries) {
|
|
163
|
+
expect(entry.id.length).toBeGreaterThan(0)
|
|
164
|
+
expect(entry.label.length).toBeGreaterThan(0)
|
|
165
|
+
expect(entry.semanticClass.length).toBeGreaterThan(0)
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('all entry ids are unique', () => {
|
|
170
|
+
const ids = DEFAULT_ORGANIZATION_MODEL_STATUSES.entries.map((e) => e.id)
|
|
171
|
+
const uniqueIds = new Set(ids)
|
|
172
|
+
expect(uniqueIds.size).toBe(ids.length)
|
|
173
|
+
})
|
|
174
|
+
})
|
|
175
|
+
})
|