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.
Files changed (108) hide show
  1. package/.claude/settings.local.json +6 -1
  2. package/README.md +81 -54
  3. package/agentic/orchestrator/defaults/policy.defaults.yaml +212 -0
  4. package/agentic/orchestrator/schemas/adapters.schema.json +17 -4
  5. package/agentic/orchestrator/schemas/agents.schema.json +58 -13
  6. package/agentic/orchestrator/schemas/gates.schema.json +88 -17
  7. package/agentic/orchestrator/schemas/index.schema.json +172 -37
  8. package/agentic/orchestrator/schemas/multi-project.schema.json +85 -17
  9. package/agentic/orchestrator/schemas/plan.schema.json +135 -30
  10. package/agentic/orchestrator/schemas/policy.schema.json +198 -69
  11. package/agentic/orchestrator/schemas/policy.user.schema.json +165 -0
  12. package/agentic/orchestrator/schemas/qa_test_index.schema.json +71 -14
  13. package/agentic/orchestrator/schemas/state.schema.json +196 -39
  14. package/apps/control-plane/scripts/validate-architecture-rules.mjs +5 -3
  15. package/apps/control-plane/scripts/validate-docker-mcp-contract.mjs +0 -3
  16. package/apps/control-plane/scripts/validate-mcp-contracts.ts +2 -6
  17. package/apps/control-plane/src/application/services/policy-loader-service.ts +115 -0
  18. package/apps/control-plane/src/cli/aop.ts +35 -1
  19. package/apps/control-plane/src/cli/cli-argument-parser.ts +4 -0
  20. package/apps/control-plane/src/cli/help-command-handler.ts +2 -1
  21. package/apps/control-plane/src/cli/init-command-handler.ts +136 -27
  22. package/apps/control-plane/src/cli/retry-command-handler.ts +3 -6
  23. package/apps/control-plane/src/cli/types.ts +1 -0
  24. package/apps/control-plane/src/core/git.ts +1 -3
  25. package/apps/control-plane/src/core/kernel.ts +3 -6
  26. package/apps/control-plane/src/core/schemas.ts +36 -1
  27. package/apps/control-plane/src/interfaces/cli/bootstrap.ts +1 -1
  28. package/apps/control-plane/src/mcp/tool-registry-loader.ts +37 -1
  29. package/apps/control-plane/src/supervisor/qa-wave-executor.ts +1 -1
  30. package/apps/control-plane/test/aop.spec.ts +37 -0
  31. package/apps/control-plane/test/batch-operations.spec.ts +5 -3
  32. package/apps/control-plane/test/bootstrap-attach.spec.ts +28 -18
  33. package/apps/control-plane/test/bootstrap-edge-cases.spec.ts +25 -15
  34. package/apps/control-plane/test/bootstrap.spec.ts +30 -22
  35. package/apps/control-plane/test/cli.unit.spec.ts +55 -10
  36. package/apps/control-plane/test/core-utils.spec.ts +12 -3
  37. package/apps/control-plane/test/feature-deletion-service.spec.ts +2 -1
  38. package/apps/control-plane/test/feature-lifecycle.spec.ts +1 -1
  39. package/apps/control-plane/test/helpers.ts +22 -2
  40. package/apps/control-plane/test/init-wizard.spec.ts +160 -7
  41. package/apps/control-plane/test/issue-tracker.spec.ts +3 -3
  42. package/apps/control-plane/test/kernel.spec.ts +62 -0
  43. package/apps/control-plane/test/lock-service.spec.ts +4 -4
  44. package/apps/control-plane/test/policy-loader-service.spec.ts +346 -0
  45. package/apps/control-plane/test/reactions.spec.ts +8 -6
  46. package/apps/control-plane/test/resume-command.spec.ts +31 -15
  47. package/apps/control-plane/test/supervisor.unit.spec.ts +2 -2
  48. package/apps/control-plane/test/tool-registry-loader.spec.ts +12 -0
  49. package/apps/control-plane/vitest.config.ts +1 -1
  50. package/dist/apps/control-plane/application/services/policy-loader-service.d.ts +24 -0
  51. package/dist/apps/control-plane/application/services/policy-loader-service.js +90 -0
  52. package/dist/apps/control-plane/application/services/policy-loader-service.js.map +1 -0
  53. package/dist/apps/control-plane/application/tools/tool-metadata.js +1 -0
  54. package/dist/apps/control-plane/application/tools/tool-metadata.js.map +1 -1
  55. package/dist/apps/control-plane/cli/aop.js +33 -1
  56. package/dist/apps/control-plane/cli/aop.js.map +1 -1
  57. package/dist/apps/control-plane/cli/cli-argument-parser.js +4 -0
  58. package/dist/apps/control-plane/cli/cli-argument-parser.js.map +1 -1
  59. package/dist/apps/control-plane/cli/dashboard-command-handler.js +11 -5
  60. package/dist/apps/control-plane/cli/dashboard-command-handler.js.map +1 -1
  61. package/dist/apps/control-plane/cli/help-command-handler.js +5 -4
  62. package/dist/apps/control-plane/cli/help-command-handler.js.map +1 -1
  63. package/dist/apps/control-plane/cli/init-command-handler.d.ts +1 -0
  64. package/dist/apps/control-plane/cli/init-command-handler.js +110 -26
  65. package/dist/apps/control-plane/cli/init-command-handler.js.map +1 -1
  66. package/dist/apps/control-plane/cli/retry-command-handler.js +3 -6
  67. package/dist/apps/control-plane/cli/retry-command-handler.js.map +1 -1
  68. package/dist/apps/control-plane/cli/types.d.ts +1 -0
  69. package/dist/apps/control-plane/core/git.js +1 -3
  70. package/dist/apps/control-plane/core/git.js.map +1 -1
  71. package/dist/apps/control-plane/core/kernel.js +3 -5
  72. package/dist/apps/control-plane/core/kernel.js.map +1 -1
  73. package/dist/apps/control-plane/core/schemas.d.ts +2 -0
  74. package/dist/apps/control-plane/core/schemas.js +31 -1
  75. package/dist/apps/control-plane/core/schemas.js.map +1 -1
  76. package/dist/apps/control-plane/interfaces/cli/bootstrap.js +1 -1
  77. package/dist/apps/control-plane/interfaces/cli/bootstrap.js.map +1 -1
  78. package/dist/apps/control-plane/mcp/tool-registry-loader.d.ts +2 -0
  79. package/dist/apps/control-plane/mcp/tool-registry-loader.js +31 -1
  80. package/dist/apps/control-plane/mcp/tool-registry-loader.js.map +1 -1
  81. package/dist/apps/control-plane/supervisor/build-wave-executor.js +1 -1
  82. package/dist/apps/control-plane/supervisor/build-wave-executor.js.map +1 -1
  83. package/dist/apps/control-plane/supervisor/qa-wave-executor.js +1 -1
  84. package/dist/apps/control-plane/supervisor/qa-wave-executor.js.map +1 -1
  85. package/package.json +18 -17
  86. package/packages/web-dashboard/package.json +1 -1
  87. package/packages/web-dashboard/src/app/api/actions/route.ts +2 -2
  88. package/packages/web-dashboard/src/app/api/events/route.ts +1 -1
  89. package/packages/web-dashboard/src/app/api/features/[id]/checkout/route.ts +1 -1
  90. package/packages/web-dashboard/src/app/api/features/[id]/diff/route.ts +1 -1
  91. package/packages/web-dashboard/src/app/api/features/[id]/evidence/[artifact]/route.ts +1 -1
  92. package/packages/web-dashboard/src/app/api/features/[id]/review/route.ts +2 -2
  93. package/packages/web-dashboard/src/app/api/features/[id]/route.ts +1 -1
  94. package/packages/web-dashboard/src/app/api/projects/route.ts +2 -2
  95. package/packages/web-dashboard/src/app/api/status/route.ts +1 -1
  96. package/packages/web-dashboard/src/app/page.tsx +1 -1
  97. package/packages/web-dashboard/src/lib/aop-client.ts +1 -1
  98. package/packages/web-dashboard/src/lib/orchestrator-tools.ts +1 -1
  99. package/packages/web-dashboard/tsconfig.json +1 -0
  100. package/spec-files/completed/agentic_orchestrator_init_policy_ux_simplification_spec.md +481 -0
  101. package/spec-files/outstanding/agentic_orchestrator_artifact_database_publishing_spec.md +500 -0
  102. package/spec-files/outstanding/agentic_orchestrator_observability_integrity_diagnostics_spec.md +500 -195
  103. package/spec-files/outstanding/agentic_orchestrator_performance_improvements_spec.md +471 -162
  104. package/spec-files/outstanding/agentic_orchestrator_planning_review_quality_spec.md +532 -268
  105. package/spec-files/outstanding/agentic_orchestrator_productization_commercial_spec.md +645 -0
  106. package/spec-files/progress.md +99 -2
  107. package/tsconfig.base.json +4 -0
  108. /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
- expect(policyContent).toContain('framework: vitest');
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
- expect(content).toContain('framework: vitest');
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 import('../src/core/fs.js')>();
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
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
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 };