agentic-orchestrator 0.1.4 → 0.1.6
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/.claude/settings.local.json +6 -1
- package/README.md +81 -54
- package/agentic/orchestrator/defaults/policy.defaults.yaml +212 -0
- package/agentic/orchestrator/schemas/adapters.schema.json +17 -4
- package/agentic/orchestrator/schemas/agents.schema.json +58 -13
- package/agentic/orchestrator/schemas/gates.schema.json +88 -17
- package/agentic/orchestrator/schemas/index.schema.json +172 -37
- package/agentic/orchestrator/schemas/multi-project.schema.json +85 -17
- package/agentic/orchestrator/schemas/plan.schema.json +135 -30
- package/agentic/orchestrator/schemas/policy.schema.json +198 -69
- package/agentic/orchestrator/schemas/policy.user.schema.json +165 -0
- package/agentic/orchestrator/schemas/qa_test_index.schema.json +71 -14
- package/agentic/orchestrator/schemas/state.schema.json +196 -39
- package/apps/control-plane/scripts/validate-architecture-rules.mjs +5 -3
- package/apps/control-plane/scripts/validate-docker-mcp-contract.mjs +0 -3
- package/apps/control-plane/scripts/validate-mcp-contracts.ts +2 -6
- package/apps/control-plane/src/application/services/policy-loader-service.ts +115 -0
- package/apps/control-plane/src/cli/aop.ts +35 -1
- package/apps/control-plane/src/cli/cli-argument-parser.ts +4 -0
- package/apps/control-plane/src/cli/help-command-handler.ts +2 -1
- package/apps/control-plane/src/cli/init-command-handler.ts +136 -27
- package/apps/control-plane/src/cli/retry-command-handler.ts +3 -6
- package/apps/control-plane/src/cli/types.ts +1 -0
- package/apps/control-plane/src/core/git.ts +1 -3
- package/apps/control-plane/src/core/kernel.ts +3 -6
- package/apps/control-plane/src/core/schemas.ts +36 -1
- package/apps/control-plane/src/interfaces/cli/bootstrap.ts +1 -1
- package/apps/control-plane/src/mcp/tool-registry-loader.ts +37 -1
- package/apps/control-plane/src/supervisor/qa-wave-executor.ts +1 -1
- package/apps/control-plane/test/aop.spec.ts +37 -0
- package/apps/control-plane/test/batch-operations.spec.ts +5 -3
- package/apps/control-plane/test/bootstrap-attach.spec.ts +28 -18
- package/apps/control-plane/test/bootstrap-edge-cases.spec.ts +25 -15
- package/apps/control-plane/test/bootstrap.spec.ts +30 -22
- package/apps/control-plane/test/cli.unit.spec.ts +55 -10
- package/apps/control-plane/test/core-utils.spec.ts +12 -3
- package/apps/control-plane/test/feature-deletion-service.spec.ts +2 -1
- package/apps/control-plane/test/feature-lifecycle.spec.ts +1 -1
- package/apps/control-plane/test/helpers.ts +22 -2
- package/apps/control-plane/test/init-wizard.spec.ts +160 -7
- package/apps/control-plane/test/issue-tracker.spec.ts +3 -3
- package/apps/control-plane/test/kernel.spec.ts +62 -0
- package/apps/control-plane/test/lock-service.spec.ts +4 -4
- package/apps/control-plane/test/policy-loader-service.spec.ts +346 -0
- package/apps/control-plane/test/reactions.spec.ts +8 -6
- package/apps/control-plane/test/resume-command.spec.ts +31 -15
- package/apps/control-plane/test/supervisor.unit.spec.ts +2 -2
- package/apps/control-plane/test/tool-registry-loader.spec.ts +12 -0
- package/apps/control-plane/vitest.config.ts +1 -1
- package/dist/apps/control-plane/application/services/policy-loader-service.d.ts +24 -0
- package/dist/apps/control-plane/application/services/policy-loader-service.js +90 -0
- package/dist/apps/control-plane/application/services/policy-loader-service.js.map +1 -0
- package/dist/apps/control-plane/application/tools/tool-metadata.js +1 -0
- package/dist/apps/control-plane/application/tools/tool-metadata.js.map +1 -1
- package/dist/apps/control-plane/cli/aop.js +33 -1
- package/dist/apps/control-plane/cli/aop.js.map +1 -1
- package/dist/apps/control-plane/cli/cli-argument-parser.js +4 -0
- package/dist/apps/control-plane/cli/cli-argument-parser.js.map +1 -1
- package/dist/apps/control-plane/cli/dashboard-command-handler.js +11 -5
- package/dist/apps/control-plane/cli/dashboard-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/help-command-handler.js +5 -4
- package/dist/apps/control-plane/cli/help-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/init-command-handler.d.ts +1 -0
- package/dist/apps/control-plane/cli/init-command-handler.js +110 -26
- package/dist/apps/control-plane/cli/init-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/retry-command-handler.js +3 -6
- package/dist/apps/control-plane/cli/retry-command-handler.js.map +1 -1
- package/dist/apps/control-plane/cli/types.d.ts +1 -0
- package/dist/apps/control-plane/core/git.js +1 -3
- package/dist/apps/control-plane/core/git.js.map +1 -1
- package/dist/apps/control-plane/core/kernel.js +3 -5
- package/dist/apps/control-plane/core/kernel.js.map +1 -1
- package/dist/apps/control-plane/core/schemas.d.ts +2 -0
- package/dist/apps/control-plane/core/schemas.js +31 -1
- package/dist/apps/control-plane/core/schemas.js.map +1 -1
- package/dist/apps/control-plane/interfaces/cli/bootstrap.js +1 -1
- package/dist/apps/control-plane/interfaces/cli/bootstrap.js.map +1 -1
- package/dist/apps/control-plane/mcp/tool-registry-loader.d.ts +2 -0
- package/dist/apps/control-plane/mcp/tool-registry-loader.js +31 -1
- package/dist/apps/control-plane/mcp/tool-registry-loader.js.map +1 -1
- package/dist/apps/control-plane/supervisor/build-wave-executor.js +1 -1
- package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js +1 -1
- package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
- package/package.json +18 -17
- package/packages/web-dashboard/package.json +1 -1
- package/packages/web-dashboard/src/app/api/actions/route.ts +2 -2
- package/packages/web-dashboard/src/app/api/events/route.ts +1 -1
- package/packages/web-dashboard/src/app/api/features/[id]/checkout/route.ts +1 -1
- package/packages/web-dashboard/src/app/api/features/[id]/diff/route.ts +1 -1
- package/packages/web-dashboard/src/app/api/features/[id]/evidence/[artifact]/route.ts +1 -1
- package/packages/web-dashboard/src/app/api/features/[id]/review/route.ts +2 -2
- package/packages/web-dashboard/src/app/api/features/[id]/route.ts +1 -1
- package/packages/web-dashboard/src/app/api/projects/route.ts +2 -2
- package/packages/web-dashboard/src/app/api/status/route.ts +1 -1
- package/packages/web-dashboard/src/app/page.tsx +1 -1
- package/packages/web-dashboard/src/lib/aop-client.ts +1 -1
- package/packages/web-dashboard/src/lib/orchestrator-tools.ts +1 -1
- package/packages/web-dashboard/tsconfig.json +1 -0
- package/spec-files/completed/agentic_orchestrator_init_policy_ux_simplification_spec.md +481 -0
- package/spec-files/outstanding/agentic_orchestrator_artifact_database_publishing_spec.md +500 -0
- package/spec-files/outstanding/agentic_orchestrator_observability_integrity_diagnostics_spec.md +500 -195
- package/spec-files/outstanding/agentic_orchestrator_performance_improvements_spec.md +471 -162
- package/spec-files/outstanding/agentic_orchestrator_planning_review_quality_spec.md +532 -268
- package/spec-files/outstanding/agentic_orchestrator_productization_commercial_spec.md +645 -0
- package/spec-files/progress.md +99 -2
- package/tsconfig.base.json +4 -0
- /package/spec-files/{outstanding → completed}/agentic_orchestrator_validator_hardening_spec.md +0 -0
|
@@ -18,6 +18,7 @@ vi.mock('node:child_process', () => ({
|
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
20
|
import { InitCommandHandler } from '../src/cli/init-command-handler.js';
|
|
21
|
+
import { HelpCommandHandler } from '../src/cli/help-command-handler.js';
|
|
21
22
|
|
|
22
23
|
function makePromptFactory(responses: string[]) {
|
|
23
24
|
const question = vi.fn(async () => responses.shift() ?? '');
|
|
@@ -61,7 +62,6 @@ describe('InitCommandHandler', () => {
|
|
|
61
62
|
expect(result.data.created).toContain('agentic/orchestrator/agents.yaml');
|
|
62
63
|
expect(result.data.created).toContain('agentic/orchestrator/adapters.yaml');
|
|
63
64
|
expect(result.data.created).toContain('agentic/orchestrator/prompts/planner.system.md');
|
|
64
|
-
expect(result.data.created).toContain('agentic/orchestrator/schemas/state.schema.json');
|
|
65
65
|
expect(result.data.skipped).toHaveLength(0);
|
|
66
66
|
expect(result.data.updated).toHaveLength(0);
|
|
67
67
|
expect(result.data.validation_warnings).toHaveLength(0);
|
|
@@ -69,7 +69,8 @@ describe('InitCommandHandler', () => {
|
|
|
69
69
|
// Verify files were created
|
|
70
70
|
const policyContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'policy.yaml'), 'utf8');
|
|
71
71
|
expect(policyContent).toContain('base_branch: main');
|
|
72
|
-
|
|
72
|
+
// lean policy does not include advanced fields like testing.framework
|
|
73
|
+
expect(policyContent).not.toContain('framework: vitest');
|
|
73
74
|
expect(policyContent).toContain('notifications:');
|
|
74
75
|
|
|
75
76
|
const gatesContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'gates.yaml'), 'utf8');
|
|
@@ -79,13 +80,17 @@ describe('InitCommandHandler', () => {
|
|
|
79
80
|
const agentsContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'agents.yaml'), 'utf8');
|
|
80
81
|
expect(agentsContent).toContain('version: 1');
|
|
81
82
|
expect(agentsContent).toContain('roles:');
|
|
83
|
+
expect(agentsContent).toContain('default_provider: custom');
|
|
84
|
+
expect(agentsContent).toContain('default_model: local-default');
|
|
85
|
+
|
|
86
|
+
const adaptersContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'adapters.yaml'), 'utf8');
|
|
87
|
+
expect(adaptersContent).toContain('activity-detector: process-heuristic');
|
|
88
|
+
expect(adaptersContent).toContain('scm-provider: github');
|
|
82
89
|
|
|
83
90
|
await expect(
|
|
84
91
|
fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'prompts', 'planner.system.md'), 'utf8')
|
|
85
92
|
).resolves.toContain('planner');
|
|
86
|
-
await expect(
|
|
87
|
-
fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'schemas', 'state.schema.json'), 'utf8')
|
|
88
|
-
).resolves.toContain('"$schema"');
|
|
93
|
+
await expect(fs.stat(path.join(cwd, 'agentic', 'orchestrator', 'schemas'))).rejects.toThrow();
|
|
89
94
|
});
|
|
90
95
|
|
|
91
96
|
it('GIVEN_existing_config_WHEN_init_without_force_THEN_skips_existing_files', async () => {
|
|
@@ -124,10 +129,11 @@ describe('InitCommandHandler', () => {
|
|
|
124
129
|
expect(result.data.updated).toContain('agentic/orchestrator/policy.yaml');
|
|
125
130
|
expect(result.data.skipped).not.toContain('agentic/orchestrator/policy.yaml');
|
|
126
131
|
|
|
127
|
-
// Verify file WAS overwritten with generated content
|
|
132
|
+
// Verify file WAS overwritten with generated content (lean policy by default)
|
|
128
133
|
const content = await fs.readFile(path.join(orchestratorDir, 'policy.yaml'), 'utf8');
|
|
129
134
|
expect(content).not.toContain('# existing');
|
|
130
|
-
|
|
135
|
+
// lean policy does not include testing.framework
|
|
136
|
+
expect(content).not.toContain('framework: vitest');
|
|
131
137
|
});
|
|
132
138
|
|
|
133
139
|
it('GIVEN_non_git_directory_WHEN_init_THEN_uses_defaults', async () => {
|
|
@@ -248,6 +254,9 @@ describe('InitCommandHandler', () => {
|
|
|
248
254
|
|
|
249
255
|
const prompts = makePromptFactory([
|
|
250
256
|
'release',
|
|
257
|
+
'claude',
|
|
258
|
+
'sonnet-4.5',
|
|
259
|
+
'github',
|
|
251
260
|
'4',
|
|
252
261
|
'4500',
|
|
253
262
|
'desktop,slack',
|
|
@@ -272,6 +281,14 @@ describe('InitCommandHandler', () => {
|
|
|
272
281
|
const gatesContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'gates.yaml'), 'utf8');
|
|
273
282
|
expect(gatesContent).toContain('"npx"');
|
|
274
283
|
expect(gatesContent).toContain('"jest"');
|
|
284
|
+
|
|
285
|
+
const agentsContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'agents.yaml'), 'utf8');
|
|
286
|
+
expect(agentsContent).toContain('default_provider: claude');
|
|
287
|
+
expect(agentsContent).toContain('default_model: sonnet-4.5');
|
|
288
|
+
|
|
289
|
+
const adaptersContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'adapters.yaml'), 'utf8');
|
|
290
|
+
expect(adaptersContent).toContain('activity-detector: claude-jsonl');
|
|
291
|
+
expect(adaptersContent).toContain('scm-provider: github');
|
|
275
292
|
});
|
|
276
293
|
});
|
|
277
294
|
|
|
@@ -337,6 +354,61 @@ describe('InitCommandHandler validation branches', () => {
|
|
|
337
354
|
expect(result.data.validation_warnings[0]).toContain('Failed to validate');
|
|
338
355
|
validateSpy.mockRestore();
|
|
339
356
|
});
|
|
357
|
+
|
|
358
|
+
it('GIVEN_skipped_non_policy_files_WHEN_validation_loop_runs_THEN_skipped_rels_are_not_validated', async () => {
|
|
359
|
+
const orchestratorDir = path.join(cwd, 'agentic', 'orchestrator');
|
|
360
|
+
await fs.mkdir(orchestratorDir, { recursive: true });
|
|
361
|
+
// Pre-create gates.yaml and agents.yaml so they get skipped (no --force)
|
|
362
|
+
await fs.writeFile(path.join(orchestratorDir, 'gates.yaml'), 'version: 1\n# existing gates\n', 'utf8');
|
|
363
|
+
await fs.writeFile(path.join(orchestratorDir, 'agents.yaml'), 'version: 1\n# existing agents\n', 'utf8');
|
|
364
|
+
execFileMock.mockResolvedValue({ stdout: 'origin/main\n', stderr: '' });
|
|
365
|
+
|
|
366
|
+
const handler = new InitCommandHandler(cwd);
|
|
367
|
+
const result = await handler.execute({ auto: true });
|
|
368
|
+
|
|
369
|
+
expect(result.ok).toBe(true);
|
|
370
|
+
expect(result.data.skipped).toContain('agentic/orchestrator/gates.yaml');
|
|
371
|
+
expect(result.data.skipped).toContain('agentic/orchestrator/agents.yaml');
|
|
372
|
+
// Skipped files are not validated — no errors from their existing (partial) content
|
|
373
|
+
expect(result.data.validation_warnings).toHaveLength(0);
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
describe('InitCommandHandler codex activity detector branch', () => {
|
|
378
|
+
let cwd: string;
|
|
379
|
+
|
|
380
|
+
beforeEach(async () => {
|
|
381
|
+
cwd = await fs.mkdtemp(path.join(os.tmpdir(), 'aop-init-codex-'));
|
|
382
|
+
execFileMock.mockReset();
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
afterEach(async () => {
|
|
386
|
+
await fs.rm(cwd, { recursive: true, force: true });
|
|
387
|
+
vi.restoreAllMocks();
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it('GIVEN_codex_provider_WHEN_init_THEN_activity_detector_is_codex_rpc', async () => {
|
|
391
|
+
await fs.mkdir(path.join(cwd, '.git'), { recursive: true });
|
|
392
|
+
await fs.writeFile(path.join(cwd, '.git', 'config'), '[core]\n', 'utf8');
|
|
393
|
+
execFileMock.mockResolvedValue({ stdout: 'origin/main\n', stderr: '' });
|
|
394
|
+
|
|
395
|
+
const prompts = makePromptFactory([
|
|
396
|
+
'main',
|
|
397
|
+
'codex',
|
|
398
|
+
'codex-default',
|
|
399
|
+
'github',
|
|
400
|
+
'3',
|
|
401
|
+
'3000',
|
|
402
|
+
'none',
|
|
403
|
+
'vitest'
|
|
404
|
+
]);
|
|
405
|
+
const handler = new InitCommandHandler(cwd, prompts.factory);
|
|
406
|
+
const result = await handler.execute({ auto: false });
|
|
407
|
+
|
|
408
|
+
expect(result.ok).toBe(true);
|
|
409
|
+
const adaptersContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'adapters.yaml'), 'utf8');
|
|
410
|
+
expect(adaptersContent).toContain('activity-detector: codex-rpc');
|
|
411
|
+
});
|
|
340
412
|
});
|
|
341
413
|
|
|
342
414
|
describe('InitCommandHandler webhook channel branch', () => {
|
|
@@ -359,6 +431,9 @@ describe('InitCommandHandler webhook channel branch', () => {
|
|
|
359
431
|
|
|
360
432
|
const prompts = makePromptFactory([
|
|
361
433
|
'main',
|
|
434
|
+
'custom',
|
|
435
|
+
'local-default',
|
|
436
|
+
'github',
|
|
362
437
|
'3',
|
|
363
438
|
'3000',
|
|
364
439
|
'webhook',
|
|
@@ -394,6 +469,9 @@ describe('InitCommandHandler parseNotificationChannels none branch', () => {
|
|
|
394
469
|
|
|
395
470
|
const prompts = makePromptFactory([
|
|
396
471
|
'main',
|
|
472
|
+
'custom',
|
|
473
|
+
'local-default',
|
|
474
|
+
'github',
|
|
397
475
|
'3',
|
|
398
476
|
'3000',
|
|
399
477
|
'none',
|
|
@@ -472,6 +550,9 @@ describe('InitCommandHandler parseInteger out-of-range and parseFramework fallba
|
|
|
472
550
|
|
|
473
551
|
const prompts = makePromptFactory([
|
|
474
552
|
'main',
|
|
553
|
+
'custom',
|
|
554
|
+
'local-default',
|
|
555
|
+
'github',
|
|
475
556
|
'0', // out of range (min=1) → parseInteger returns fallback 3
|
|
476
557
|
'3000',
|
|
477
558
|
'none',
|
|
@@ -493,6 +574,9 @@ describe('InitCommandHandler parseInteger out-of-range and parseFramework fallba
|
|
|
493
574
|
|
|
494
575
|
const prompts = makePromptFactory([
|
|
495
576
|
'main',
|
|
577
|
+
'custom',
|
|
578
|
+
'local-default',
|
|
579
|
+
'github',
|
|
496
580
|
'3',
|
|
497
581
|
'3000',
|
|
498
582
|
'none',
|
|
@@ -504,3 +588,72 @@ describe('InitCommandHandler parseInteger out-of-range and parseFramework fallba
|
|
|
504
588
|
expect(result.ok).toBe(true);
|
|
505
589
|
});
|
|
506
590
|
});
|
|
591
|
+
|
|
592
|
+
describe('InitCommandHandler lean vs advanced policy', () => {
|
|
593
|
+
let cwd: string;
|
|
594
|
+
|
|
595
|
+
beforeEach(async () => {
|
|
596
|
+
cwd = await fs.mkdtemp(path.join(os.tmpdir(), 'aop-init-policy-'));
|
|
597
|
+
execFileMock.mockReset();
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
afterEach(async () => {
|
|
601
|
+
await fs.rm(cwd, { recursive: true, force: true });
|
|
602
|
+
vi.restoreAllMocks();
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it('GIVEN_default_init_WHEN_policy_generated_THEN_policy_is_lean_shape', async () => {
|
|
606
|
+
execFileMock.mockResolvedValue({ stdout: 'origin/main\n', stderr: '' });
|
|
607
|
+
|
|
608
|
+
const handler = new InitCommandHandler(cwd);
|
|
609
|
+
const result = await handler.execute({ auto: true });
|
|
610
|
+
|
|
611
|
+
expect(result.ok).toBe(true);
|
|
612
|
+
expect(result.data.validation_warnings).toHaveLength(0);
|
|
613
|
+
|
|
614
|
+
const policyContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'policy.yaml'), 'utf8');
|
|
615
|
+
// Lean policy contains common controls
|
|
616
|
+
expect(policyContent).toContain('worktree:');
|
|
617
|
+
expect(policyContent).toContain('base_branch:');
|
|
618
|
+
expect(policyContent).toContain('supervisor:');
|
|
619
|
+
expect(policyContent).toContain('max_parallel_gate_runs:');
|
|
620
|
+
expect(policyContent).toContain('dashboard:');
|
|
621
|
+
expect(policyContent).toContain('merge_policy:');
|
|
622
|
+
expect(policyContent).toContain('notifications:');
|
|
623
|
+
// Lean policy does NOT contain advanced fields
|
|
624
|
+
expect(policyContent).not.toContain('commit_policy:');
|
|
625
|
+
expect(policyContent).not.toContain('patch_policy:');
|
|
626
|
+
expect(policyContent).not.toContain('rbac:');
|
|
627
|
+
expect(policyContent).not.toContain('locks:');
|
|
628
|
+
expect(policyContent).not.toContain('implementation:');
|
|
629
|
+
expect(policyContent).not.toContain('testing:');
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
it('GIVEN_advanced_policy_flag_WHEN_policy_generated_THEN_policy_is_full_shape', async () => {
|
|
633
|
+
execFileMock.mockResolvedValue({ stdout: 'origin/main\n', stderr: '' });
|
|
634
|
+
|
|
635
|
+
const handler = new InitCommandHandler(cwd);
|
|
636
|
+
const result = await handler.execute({ auto: true, advanced_policy: true });
|
|
637
|
+
|
|
638
|
+
expect(result.ok).toBe(true);
|
|
639
|
+
expect(result.data.validation_warnings).toHaveLength(0);
|
|
640
|
+
|
|
641
|
+
const policyContent = await fs.readFile(path.join(cwd, 'agentic', 'orchestrator', 'policy.yaml'), 'utf8');
|
|
642
|
+
// Full policy contains advanced controls
|
|
643
|
+
expect(policyContent).toContain('commit_policy:');
|
|
644
|
+
expect(policyContent).toContain('patch_policy:');
|
|
645
|
+
expect(policyContent).toContain('rbac:');
|
|
646
|
+
expect(policyContent).toContain('locks:');
|
|
647
|
+
expect(policyContent).toContain('implementation:');
|
|
648
|
+
expect(policyContent).toContain('testing:');
|
|
649
|
+
expect(policyContent).toContain('framework: vitest');
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it('GIVEN_advanced_policy_flag_WHEN_help_rendered_THEN_flag_is_documented', () => {
|
|
653
|
+
const handler = new HelpCommandHandler();
|
|
654
|
+
const result = handler.execute('init');
|
|
655
|
+
|
|
656
|
+
expect(result.ok).toBe(true);
|
|
657
|
+
expect(result.data.help).toContain('--advanced-policy');
|
|
658
|
+
});
|
|
659
|
+
});
|
|
@@ -507,7 +507,7 @@ describe('issue-tracker-service additional branch coverage', () => {
|
|
|
507
507
|
it('GIVEN_linear_planning_status_WHEN_updateIssueStatus_THEN_maps_to_backlog', async () => {
|
|
508
508
|
const bodies: string[] = [];
|
|
509
509
|
const runner: HttpRunner = async (_, init) => {
|
|
510
|
-
bodies.push(init.body ?? '');
|
|
510
|
+
bodies.push((init.body as string) ?? '');
|
|
511
511
|
// First call: resolveIssueNode → return a valid issue
|
|
512
512
|
if (bodies.length === 1) {
|
|
513
513
|
return {
|
|
@@ -529,7 +529,7 @@ describe('issue-tracker-service additional branch coverage', () => {
|
|
|
529
529
|
it('GIVEN_linear_qa_status_WHEN_updateIssueStatus_THEN_maps_to_in_review', async () => {
|
|
530
530
|
const bodies: string[] = [];
|
|
531
531
|
const runner: HttpRunner = async (_, init) => {
|
|
532
|
-
bodies.push(init.body ?? '');
|
|
532
|
+
bodies.push((init.body as string) ?? '');
|
|
533
533
|
if (bodies.length === 1) {
|
|
534
534
|
return {
|
|
535
535
|
status: 200, ok: true,
|
|
@@ -642,7 +642,7 @@ describe('Issue tracker additional branch coverage', () => {
|
|
|
642
642
|
it('GIVEN_linear_with_unknown_status_WHEN_updateIssueStatus_THEN_uses_in_progress_default', async () => {
|
|
643
643
|
const bodies: string[] = [];
|
|
644
644
|
const runner: HttpRunner = async (_, init) => {
|
|
645
|
-
bodies.push(init.body ?? '');
|
|
645
|
+
bodies.push((init.body as string) ?? '');
|
|
646
646
|
if (bodies.length === 1) {
|
|
647
647
|
return {
|
|
648
648
|
status: 200, ok: true,
|
|
@@ -369,3 +369,65 @@ describe('AopKernel', () => {
|
|
|
369
369
|
expect(branchLookup.code).not.toBe(0);
|
|
370
370
|
});
|
|
371
371
|
});
|
|
372
|
+
|
|
373
|
+
describe('AopKernel policy composition', () => {
|
|
374
|
+
it('GIVEN_lean_policy_repo_WHEN_kernel_loads_THEN_runtime_boots_successfully', async () => {
|
|
375
|
+
const leanRoot = await makeTempRepo(process.cwd());
|
|
376
|
+
try {
|
|
377
|
+
// Replace the full policy.yaml with a lean one
|
|
378
|
+
const leanPolicy = `version: 1
|
|
379
|
+
worktree:
|
|
380
|
+
base_branch: main
|
|
381
|
+
supervisor:
|
|
382
|
+
max_parallel_gate_runs: 2
|
|
383
|
+
dashboard:
|
|
384
|
+
enabled: true
|
|
385
|
+
port: 3000
|
|
386
|
+
merge_policy:
|
|
387
|
+
require_user_approval: true
|
|
388
|
+
notifications:
|
|
389
|
+
enabled: false
|
|
390
|
+
channels:
|
|
391
|
+
desktop:
|
|
392
|
+
enabled: false
|
|
393
|
+
slack:
|
|
394
|
+
enabled: false
|
|
395
|
+
webhook: ""
|
|
396
|
+
channel: "#aop-alerts"
|
|
397
|
+
webhook:
|
|
398
|
+
enabled: false
|
|
399
|
+
url: ""
|
|
400
|
+
`;
|
|
401
|
+
await fs.writeFile(path.join(leanRoot, 'agentic', 'orchestrator', 'policy.yaml'), leanPolicy, 'utf8');
|
|
402
|
+
|
|
403
|
+
const kernel = new AopKernel(leanRoot);
|
|
404
|
+
await kernel.ensureLoaded();
|
|
405
|
+
|
|
406
|
+
// Kernel should have composed defaults — policy snapshot should contain merged values
|
|
407
|
+
const snapshot = kernel.getPolicySnapshot() as Record<string, unknown>;
|
|
408
|
+
expect(snapshot['worktree']).toBeDefined();
|
|
409
|
+
expect((snapshot['worktree'] as Record<string, unknown>)['base_branch']).toBe('main');
|
|
410
|
+
// implementation and rbac come from defaults
|
|
411
|
+
expect(snapshot['implementation']).toBeDefined();
|
|
412
|
+
expect(snapshot['rbac']).toBeDefined();
|
|
413
|
+
} finally {
|
|
414
|
+
await fs.rm(leanRoot, { recursive: true, force: true });
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it('GIVEN_full_policy_repo_WHEN_kernel_loads_THEN_runtime_boots_successfully', async () => {
|
|
419
|
+
// makeTempRepo copies the full policy.yaml from the AOP repo
|
|
420
|
+
const fullRoot = await makeTempRepo(process.cwd());
|
|
421
|
+
try {
|
|
422
|
+
const kernel = new AopKernel(fullRoot);
|
|
423
|
+
await kernel.ensureLoaded();
|
|
424
|
+
|
|
425
|
+
const snapshot = kernel.getPolicySnapshot() as Record<string, unknown>;
|
|
426
|
+
expect(snapshot['implementation']).toBeDefined();
|
|
427
|
+
expect(snapshot['rbac']).toBeDefined();
|
|
428
|
+
expect(snapshot['commit_policy']).toBeDefined();
|
|
429
|
+
} finally {
|
|
430
|
+
await fs.rm(fullRoot, { recursive: true, force: true });
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
});
|
|
@@ -3,9 +3,10 @@ import { LockService } from '../src/application/services/lock-service.js';
|
|
|
3
3
|
import type { LockServicePort } from '../src/application/services/lock-service.js';
|
|
4
4
|
import { ERROR_CODES } from '../src/core/error-codes.js';
|
|
5
5
|
import { pathExists } from '../src/core/fs.js';
|
|
6
|
+
import type * as FsModule from '../src/core/fs.js';
|
|
6
7
|
|
|
7
8
|
vi.mock('../src/core/fs.js', async (importOriginal) => {
|
|
8
|
-
const actual = await importOriginal<typeof
|
|
9
|
+
const actual = await importOriginal<typeof FsModule>();
|
|
9
10
|
return {
|
|
10
11
|
...actual,
|
|
11
12
|
pathExists: vi.fn(async () => false),
|
|
@@ -13,8 +14,7 @@ vi.mock('../src/core/fs.js', async (importOriginal) => {
|
|
|
13
14
|
};
|
|
14
15
|
});
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
type AnyRecord = Record<string, any>;
|
|
17
|
+
type AnyRecord = Record<string, unknown>;
|
|
18
18
|
|
|
19
19
|
function makeIndex(overrides: Partial<AnyRecord> = {}): AnyRecord {
|
|
20
20
|
return {
|
|
@@ -50,7 +50,7 @@ function makePolicy(overrides: Partial<AnyRecord> = {}): AnyRecord {
|
|
|
50
50
|
function makePort(indexData: AnyRecord, policyData: AnyRecord): LockServicePort {
|
|
51
51
|
let stored = { ...indexData };
|
|
52
52
|
return {
|
|
53
|
-
withIndexLock: vi.fn(async (fn: () => Promise<unknown>) => fn())
|
|
53
|
+
withIndexLock: vi.fn(async (fn: () => Promise<unknown>) => fn()) as unknown as <T>(operation: () => Promise<T>) => Promise<T>,
|
|
54
54
|
readIndex: vi.fn(async () => ({ ...stored })),
|
|
55
55
|
writeIndex: vi.fn(async (idx: AnyRecord) => {
|
|
56
56
|
stored = { ...idx };
|