@cat-factory/orchestration 0.6.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/LICENSE +21 -0
- package/dist/container.d.ts +460 -0
- package/dist/container.d.ts.map +1 -0
- package/dist/container.js +657 -0
- package/dist/container.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/modules/board/BoardService.d.ts +125 -0
- package/dist/modules/board/BoardService.d.ts.map +1 -0
- package/dist/modules/board/BoardService.js +496 -0
- package/dist/modules/board/BoardService.js.map +1 -0
- package/dist/modules/board/board.logic.d.ts +17 -0
- package/dist/modules/board/board.logic.d.ts.map +1 -0
- package/dist/modules/board/board.logic.js +51 -0
- package/dist/modules/board/board.logic.js.map +1 -0
- package/dist/modules/boardScan/BoardScanService.d.ts +35 -0
- package/dist/modules/boardScan/BoardScanService.d.ts.map +1 -0
- package/dist/modules/boardScan/BoardScanService.js +91 -0
- package/dist/modules/boardScan/BoardScanService.js.map +1 -0
- package/dist/modules/boardScan/board-scan.logic.d.ts +10 -0
- package/dist/modules/boardScan/board-scan.logic.d.ts.map +1 -0
- package/dist/modules/boardScan/board-scan.logic.js +26 -0
- package/dist/modules/boardScan/board-scan.logic.js.map +1 -0
- package/dist/modules/bootstrap/BootstrapService.d.ts +114 -0
- package/dist/modules/bootstrap/BootstrapService.d.ts.map +1 -0
- package/dist/modules/bootstrap/BootstrapService.js +516 -0
- package/dist/modules/bootstrap/BootstrapService.js.map +1 -0
- package/dist/modules/clarity/ClarityReviewService.d.ts +48 -0
- package/dist/modules/clarity/ClarityReviewService.d.ts.map +1 -0
- package/dist/modules/clarity/ClarityReviewService.js +63 -0
- package/dist/modules/clarity/ClarityReviewService.js.map +1 -0
- package/dist/modules/clarity/clarity.logic.d.ts +36 -0
- package/dist/modules/clarity/clarity.logic.d.ts.map +1 -0
- package/dist/modules/clarity/clarity.logic.js +98 -0
- package/dist/modules/clarity/clarity.logic.js.map +1 -0
- package/dist/modules/estimation/estimate.logic.d.ts +11 -0
- package/dist/modules/estimation/estimate.logic.d.ts.map +1 -0
- package/dist/modules/estimation/estimate.logic.js +37 -0
- package/dist/modules/estimation/estimate.logic.js.map +1 -0
- package/dist/modules/execution/AgentContextBuilder.d.ts +114 -0
- package/dist/modules/execution/AgentContextBuilder.d.ts.map +1 -0
- package/dist/modules/execution/AgentContextBuilder.js +316 -0
- package/dist/modules/execution/AgentContextBuilder.js.map +1 -0
- package/dist/modules/execution/CompanionController.d.ts +60 -0
- package/dist/modules/execution/CompanionController.d.ts.map +1 -0
- package/dist/modules/execution/CompanionController.js +216 -0
- package/dist/modules/execution/CompanionController.js.map +1 -0
- package/dist/modules/execution/ExecutionService.d.ts +874 -0
- package/dist/modules/execution/ExecutionService.d.ts.map +1 -0
- package/dist/modules/execution/ExecutionService.js +2921 -0
- package/dist/modules/execution/ExecutionService.js.map +1 -0
- package/dist/modules/execution/MergeResolver.d.ts +34 -0
- package/dist/modules/execution/MergeResolver.d.ts.map +1 -0
- package/dist/modules/execution/MergeResolver.js +81 -0
- package/dist/modules/execution/MergeResolver.js.map +1 -0
- package/dist/modules/execution/ReviewGateController.d.ts +163 -0
- package/dist/modules/execution/ReviewGateController.d.ts.map +1 -0
- package/dist/modules/execution/ReviewGateController.js +251 -0
- package/dist/modules/execution/ReviewGateController.js.map +1 -0
- package/dist/modules/execution/TesterController.d.ts +61 -0
- package/dist/modules/execution/TesterController.d.ts.map +1 -0
- package/dist/modules/execution/TesterController.js +215 -0
- package/dist/modules/execution/TesterController.js.map +1 -0
- package/dist/modules/execution/advance.d.ts +84 -0
- package/dist/modules/execution/advance.d.ts.map +1 -0
- package/dist/modules/execution/advance.js +2 -0
- package/dist/modules/execution/advance.js.map +1 -0
- package/dist/modules/execution/artifact-review.logic.d.ts +25 -0
- package/dist/modules/execution/artifact-review.logic.d.ts.map +1 -0
- package/dist/modules/execution/artifact-review.logic.js +39 -0
- package/dist/modules/execution/artifact-review.logic.js.map +1 -0
- package/dist/modules/execution/ci.logic.d.ts +101 -0
- package/dist/modules/execution/ci.logic.d.ts.map +1 -0
- package/dist/modules/execution/ci.logic.js +117 -0
- package/dist/modules/execution/ci.logic.js.map +1 -0
- package/dist/modules/execution/drive.d.ts +47 -0
- package/dist/modules/execution/drive.d.ts.map +1 -0
- package/dist/modules/execution/drive.js +112 -0
- package/dist/modules/execution/drive.js.map +1 -0
- package/dist/modules/execution/gates.d.ts +97 -0
- package/dist/modules/execution/gates.d.ts.map +1 -0
- package/dist/modules/execution/gates.js +2 -0
- package/dist/modules/execution/gates.js.map +1 -0
- package/dist/modules/execution/individualVendors.logic.d.ts +22 -0
- package/dist/modules/execution/individualVendors.logic.d.ts.map +1 -0
- package/dist/modules/execution/individualVendors.logic.js +33 -0
- package/dist/modules/execution/individualVendors.logic.js.map +1 -0
- package/dist/modules/execution/job.logic.d.ts +52 -0
- package/dist/modules/execution/job.logic.d.ts.map +1 -0
- package/dist/modules/execution/job.logic.js +56 -0
- package/dist/modules/execution/job.logic.js.map +1 -0
- package/dist/modules/execution/release.logic.d.ts +43 -0
- package/dist/modules/execution/release.logic.d.ts.map +1 -0
- package/dist/modules/execution/release.logic.js +49 -0
- package/dist/modules/execution/release.logic.js.map +1 -0
- package/dist/modules/execution/retry.logic.d.ts +40 -0
- package/dist/modules/execution/retry.logic.d.ts.map +1 -0
- package/dist/modules/execution/retry.logic.js +83 -0
- package/dist/modules/execution/retry.logic.js.map +1 -0
- package/dist/modules/execution/stepGating.logic.d.ts +15 -0
- package/dist/modules/execution/stepGating.logic.d.ts.map +1 -0
- package/dist/modules/execution/stepGating.logic.js +29 -0
- package/dist/modules/execution/stepGating.logic.js.map +1 -0
- package/dist/modules/execution/stepResolvers.d.ts +41 -0
- package/dist/modules/execution/stepResolvers.d.ts.map +1 -0
- package/dist/modules/execution/stepResolvers.js +2 -0
- package/dist/modules/execution/stepResolvers.js.map +1 -0
- package/dist/modules/execution/tester-infra.logic.d.ts +42 -0
- package/dist/modules/execution/tester-infra.logic.d.ts.map +1 -0
- package/dist/modules/execution/tester-infra.logic.js +46 -0
- package/dist/modules/execution/tester-infra.logic.js.map +1 -0
- package/dist/modules/merge/MergePresetService.d.ts +32 -0
- package/dist/modules/merge/MergePresetService.d.ts.map +1 -0
- package/dist/modules/merge/MergePresetService.js +109 -0
- package/dist/modules/merge/MergePresetService.js.map +1 -0
- package/dist/modules/modelDefaults/ModelDefaultsService.d.ts +22 -0
- package/dist/modules/modelDefaults/ModelDefaultsService.d.ts.map +1 -0
- package/dist/modules/modelDefaults/ModelDefaultsService.js +28 -0
- package/dist/modules/modelDefaults/ModelDefaultsService.js.map +1 -0
- package/dist/modules/notifications/NotificationService.d.ts +74 -0
- package/dist/modules/notifications/NotificationService.d.ts.map +1 -0
- package/dist/modules/notifications/NotificationService.js +131 -0
- package/dist/modules/notifications/NotificationService.js.map +1 -0
- package/dist/modules/observability/LlmObservabilityService.d.ts +121 -0
- package/dist/modules/observability/LlmObservabilityService.d.ts.map +1 -0
- package/dist/modules/observability/LlmObservabilityService.js +140 -0
- package/dist/modules/observability/LlmObservabilityService.js.map +1 -0
- package/dist/modules/observability/observability.logic.d.ts +57 -0
- package/dist/modules/observability/observability.logic.d.ts.map +1 -0
- package/dist/modules/observability/observability.logic.js +186 -0
- package/dist/modules/observability/observability.logic.js.map +1 -0
- package/dist/modules/pipelines/PipelineService.d.ts +54 -0
- package/dist/modules/pipelines/PipelineService.d.ts.map +1 -0
- package/dist/modules/pipelines/PipelineService.js +226 -0
- package/dist/modules/pipelines/PipelineService.js.map +1 -0
- package/dist/modules/pipelines/pipelineShape.d.ts +53 -0
- package/dist/modules/pipelines/pipelineShape.d.ts.map +1 -0
- package/dist/modules/pipelines/pipelineShape.js +74 -0
- package/dist/modules/pipelines/pipelineShape.js.map +1 -0
- package/dist/modules/recurring/RecurringPipelineService.d.ts +76 -0
- package/dist/modules/recurring/RecurringPipelineService.d.ts.map +1 -0
- package/dist/modules/recurring/RecurringPipelineService.js +295 -0
- package/dist/modules/recurring/RecurringPipelineService.js.map +1 -0
- package/dist/modules/recurring/TrackerSettingsService.d.ts +16 -0
- package/dist/modules/recurring/TrackerSettingsService.d.ts.map +1 -0
- package/dist/modules/recurring/TrackerSettingsService.js +30 -0
- package/dist/modules/recurring/TrackerSettingsService.js.map +1 -0
- package/dist/modules/recurring/schedule.logic.d.ts +14 -0
- package/dist/modules/recurring/schedule.logic.d.ts.map +1 -0
- package/dist/modules/recurring/schedule.logic.js +85 -0
- package/dist/modules/recurring/schedule.logic.js.map +1 -0
- package/dist/modules/releaseHealth/ReleaseHealthService.d.ts +38 -0
- package/dist/modules/releaseHealth/ReleaseHealthService.d.ts.map +1 -0
- package/dist/modules/releaseHealth/ReleaseHealthService.js +96 -0
- package/dist/modules/releaseHealth/ReleaseHealthService.js.map +1 -0
- package/dist/modules/requirements/RequirementReviewService.d.ts +48 -0
- package/dist/modules/requirements/RequirementReviewService.d.ts.map +1 -0
- package/dist/modules/requirements/RequirementReviewService.js +83 -0
- package/dist/modules/requirements/RequirementReviewService.js.map +1 -0
- package/dist/modules/requirements/requirements.logic.d.ts +93 -0
- package/dist/modules/requirements/requirements.logic.d.ts.map +1 -0
- package/dist/modules/requirements/requirements.logic.js +203 -0
- package/dist/modules/requirements/requirements.logic.js.map +1 -0
- package/dist/modules/review/IterativeReviewService.d.ts +175 -0
- package/dist/modules/review/IterativeReviewService.d.ts.map +1 -0
- package/dist/modules/review/IterativeReviewService.js +327 -0
- package/dist/modules/review/IterativeReviewService.js.map +1 -0
- package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.d.ts +20 -0
- package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.d.ts.map +1 -0
- package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.js +26 -0
- package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.js.map +1 -0
- package/dist/modules/services/ServiceMountService.d.ts +48 -0
- package/dist/modules/services/ServiceMountService.d.ts.map +1 -0
- package/dist/modules/services/ServiceMountService.js +90 -0
- package/dist/modules/services/ServiceMountService.js.map +1 -0
- package/dist/modules/settings/WorkspaceSettingsService.d.ts +22 -0
- package/dist/modules/settings/WorkspaceSettingsService.d.ts.map +1 -0
- package/dist/modules/settings/WorkspaceSettingsService.js +50 -0
- package/dist/modules/settings/WorkspaceSettingsService.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Block, RequirementReview, RequirementReviewItem } from '@cat-factory/kernel';
|
|
2
|
+
import type { DocumentRepository, TaskRepository } from '@cat-factory/kernel';
|
|
3
|
+
import type { RequirementReviewRepository } from '@cat-factory/kernel';
|
|
4
|
+
import { type IterativeReviewDeps, IterativeReviewService, type ReviewCommon, type ReviewRepository } from '../review/IterativeReviewService.js';
|
|
5
|
+
import { type RequirementsContext } from './requirements.logic.js';
|
|
6
|
+
export interface RequirementReviewServiceDependencies extends IterativeReviewDeps {
|
|
7
|
+
requirementReviewRepository: RequirementReviewRepository;
|
|
8
|
+
/** Linked PRD/RFC documents (optional; only when the documents integration is on). */
|
|
9
|
+
documentRepository?: DocumentRepository;
|
|
10
|
+
/** Linked tracker issues (optional; only when the task-source integration is on). */
|
|
11
|
+
taskRepository?: TaskRepository;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The requirements-review agent: a single LLM call reviews a block's collected requirements
|
|
15
|
+
* (description + linked PRD/RFC docs + tracker issues) and raises questions/challenges,
|
|
16
|
+
* humans answer them, and a second LLM call folds the answers into a standardized
|
|
17
|
+
* requirements document. The whole iterative loop lives in {@link IterativeReviewService};
|
|
18
|
+
* this class supplies only the requirements-specific subject, prompts and document field.
|
|
19
|
+
*/
|
|
20
|
+
export declare class RequirementReviewService extends IterativeReviewService<RequirementReview, RequirementsContext> {
|
|
21
|
+
protected readonly repository: ReviewRepository<RequirementReview>;
|
|
22
|
+
private readonly documentRepository?;
|
|
23
|
+
private readonly taskRepository?;
|
|
24
|
+
constructor(deps: RequirementReviewServiceDependencies);
|
|
25
|
+
protected readonly entityName = "Requirement review";
|
|
26
|
+
protected readonly reviewerLabel = "requirements reviewer";
|
|
27
|
+
protected readonly reviewAgentKind = "requirements-review";
|
|
28
|
+
protected readonly reworkAgentKind = "requirements-rework";
|
|
29
|
+
protected readonly reviewSystemPrompt: string;
|
|
30
|
+
protected readonly reworkSystemPrompt: string;
|
|
31
|
+
protected readonly reviewIdPrefix = "rrv";
|
|
32
|
+
protected readonly itemIdPrefix = "rri";
|
|
33
|
+
protected readonly revisedNoun = "revised requirements";
|
|
34
|
+
protected readonly truncationMessage: string;
|
|
35
|
+
protected readonly notificationType: 'requirement_review';
|
|
36
|
+
protected readonly notificationSubject = "The reviewer";
|
|
37
|
+
protected notificationTitle(block: Block): string;
|
|
38
|
+
protected buildReviewPrompt(ctx: RequirementsContext): string;
|
|
39
|
+
protected buildReworkPrompt(ctx: RequirementsContext, items: RequirementReviewItem[]): string;
|
|
40
|
+
protected applyIncorporatedDoc(ctx: RequirementsContext, doc: string): void;
|
|
41
|
+
protected applyFeedback(ctx: RequirementsContext, feedback: string): void;
|
|
42
|
+
protected readDoc(review: RequirementReview): string | null;
|
|
43
|
+
protected withDoc(review: RequirementReview, doc: string): RequirementReview;
|
|
44
|
+
protected newReview(common: ReviewCommon): RequirementReview;
|
|
45
|
+
/** Assemble the block's collected requirements + any linked docs/issues. */
|
|
46
|
+
protected gatherContext(workspaceId: string, block: Block): Promise<RequirementsContext>;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=RequirementReviewService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequirementReviewService.d.ts","sourceRoot":"","sources":["../../../src/modules/requirements/RequirementReviewService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAC1F,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC7E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAA;AAEtE,OAAO,EACL,KAAK,mBAAmB,EACxB,sBAAsB,EACtB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACtB,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EACL,KAAK,mBAAmB,EAGzB,MAAM,yBAAyB,CAAA;AAEhC,MAAM,WAAW,oCAAqC,SAAQ,mBAAmB;IAC/E,2BAA2B,EAAE,2BAA2B,CAAA;IACxD,sFAAsF;IACtF,kBAAkB,CAAC,EAAE,kBAAkB,CAAA;IACvC,qFAAqF;IACrF,cAAc,CAAC,EAAE,cAAc,CAAA;CAChC;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,SAAQ,sBAAsB,CAClE,iBAAiB,EACjB,mBAAmB,CACpB;IACC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,iBAAiB,CAAC,CAAA;IAClE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAoB;IACxD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAgB;IAEhD,YAAY,IAAI,EAAE,oCAAoC,EAKrD;IAED,SAAS,CAAC,QAAQ,CAAC,UAAU,wBAAuB;IACpD,SAAS,CAAC,QAAQ,CAAC,aAAa,2BAA0B;IAC1D,SAAS,CAAC,QAAQ,CAAC,eAAe,yBAAwB;IAC1D,SAAS,CAAC,QAAQ,CAAC,eAAe,yBAAwB;IAC1D,SAAS,CAAC,QAAQ,CAAC,kBAAkB,SAAuB;IAC5D,SAAS,CAAC,QAAQ,CAAC,kBAAkB,SAAuB;IAC5D,SAAS,CAAC,QAAQ,CAAC,cAAc,SAAQ;IACzC,SAAS,CAAC,QAAQ,CAAC,YAAY,SAAQ;IACvC,SAAS,CAAC,QAAQ,CAAC,WAAW,0BAAyB;IACvD,SAAS,CAAC,QAAQ,CAAC,iBAAiB,SAEwC;IAC5E,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAG,oBAAoB,CAAS;IACnE,SAAS,CAAC,QAAQ,CAAC,mBAAmB,kBAAiB;IAEvD,SAAS,CAAC,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAEhD;IAED,SAAS,CAAC,iBAAiB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAE5D;IAED,SAAS,CAAC,iBAAiB,CAAC,GAAG,EAAE,mBAAmB,EAAE,KAAK,EAAE,qBAAqB,EAAE,GAAG,MAAM,CAE5F;IAED,SAAS,CAAC,oBAAoB,CAAC,GAAG,EAAE,mBAAmB,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAE1E;IAED,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,mBAAmB,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAExE;IAED,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAE1D;IAED,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAE3E;IAED,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,iBAAiB,CAE3D;IAED,4EAA4E;IAC5E,UAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAsB7F;CACF"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { REVIEW_SYSTEM_PROMPT, REWORK_SYSTEM_PROMPT } from '@cat-factory/agents';
|
|
2
|
+
import { IterativeReviewService, } from '../review/IterativeReviewService.js';
|
|
3
|
+
import { buildReviewPrompt, buildReworkPrompt, } from './requirements.logic.js';
|
|
4
|
+
/**
|
|
5
|
+
* The requirements-review agent: a single LLM call reviews a block's collected requirements
|
|
6
|
+
* (description + linked PRD/RFC docs + tracker issues) and raises questions/challenges,
|
|
7
|
+
* humans answer them, and a second LLM call folds the answers into a standardized
|
|
8
|
+
* requirements document. The whole iterative loop lives in {@link IterativeReviewService};
|
|
9
|
+
* this class supplies only the requirements-specific subject, prompts and document field.
|
|
10
|
+
*/
|
|
11
|
+
export class RequirementReviewService extends IterativeReviewService {
|
|
12
|
+
repository;
|
|
13
|
+
documentRepository;
|
|
14
|
+
taskRepository;
|
|
15
|
+
constructor(deps) {
|
|
16
|
+
super(deps);
|
|
17
|
+
this.repository = deps.requirementReviewRepository;
|
|
18
|
+
this.documentRepository = deps.documentRepository;
|
|
19
|
+
this.taskRepository = deps.taskRepository;
|
|
20
|
+
}
|
|
21
|
+
entityName = 'Requirement review';
|
|
22
|
+
reviewerLabel = 'requirements reviewer';
|
|
23
|
+
reviewAgentKind = 'requirements-review';
|
|
24
|
+
reworkAgentKind = 'requirements-rework';
|
|
25
|
+
reviewSystemPrompt = REVIEW_SYSTEM_PROMPT;
|
|
26
|
+
reworkSystemPrompt = REWORK_SYSTEM_PROMPT;
|
|
27
|
+
reviewIdPrefix = 'rrv';
|
|
28
|
+
itemIdPrefix = 'rri';
|
|
29
|
+
revisedNoun = 'revised requirements';
|
|
30
|
+
truncationMessage = 'The reworked requirements were cut off before completion (model output limit ' +
|
|
31
|
+
'reached). Try splitting this work into smaller tasks, then rework again.';
|
|
32
|
+
notificationType = 'requirement_review';
|
|
33
|
+
notificationSubject = 'The reviewer';
|
|
34
|
+
notificationTitle(block) {
|
|
35
|
+
return `Requirements review: ${block.title}`;
|
|
36
|
+
}
|
|
37
|
+
buildReviewPrompt(ctx) {
|
|
38
|
+
return buildReviewPrompt(ctx);
|
|
39
|
+
}
|
|
40
|
+
buildReworkPrompt(ctx, items) {
|
|
41
|
+
return buildReworkPrompt(ctx, items);
|
|
42
|
+
}
|
|
43
|
+
applyIncorporatedDoc(ctx, doc) {
|
|
44
|
+
ctx.incorporatedDoc = doc;
|
|
45
|
+
}
|
|
46
|
+
applyFeedback(ctx, feedback) {
|
|
47
|
+
ctx.reworkFeedback = feedback;
|
|
48
|
+
}
|
|
49
|
+
readDoc(review) {
|
|
50
|
+
return review.incorporatedRequirements;
|
|
51
|
+
}
|
|
52
|
+
withDoc(review, doc) {
|
|
53
|
+
return { ...review, incorporatedRequirements: doc };
|
|
54
|
+
}
|
|
55
|
+
newReview(common) {
|
|
56
|
+
return { ...common, incorporatedRequirements: null };
|
|
57
|
+
}
|
|
58
|
+
/** Assemble the block's collected requirements + any linked docs/issues. */
|
|
59
|
+
async gatherContext(workspaceId, block) {
|
|
60
|
+
const docs = this.documentRepository
|
|
61
|
+
? (await this.documentRepository.listByBlock(workspaceId, block.id)).map((d) => ({
|
|
62
|
+
title: d.title,
|
|
63
|
+
url: d.url,
|
|
64
|
+
excerpt: d.excerpt,
|
|
65
|
+
}))
|
|
66
|
+
: [];
|
|
67
|
+
const tasks = this.taskRepository
|
|
68
|
+
? (await this.taskRepository.listByBlock(workspaceId, block.id)).map((t) => ({
|
|
69
|
+
key: t.externalId,
|
|
70
|
+
title: t.title,
|
|
71
|
+
status: t.status,
|
|
72
|
+
type: t.type,
|
|
73
|
+
description: t.description,
|
|
74
|
+
}))
|
|
75
|
+
: [];
|
|
76
|
+
return {
|
|
77
|
+
block: { title: block.title, type: block.type, description: block.description },
|
|
78
|
+
docs,
|
|
79
|
+
tasks,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=RequirementReviewService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RequirementReviewService.js","sourceRoot":"","sources":["../../../src/modules/requirements/RequirementReviewService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Block, RequirementConcernLevel, RequirementReviewItem } from '@cat-factory/kernel';
|
|
2
|
+
/** A requirements/PRD/RFC document linked to the block as context. */
|
|
3
|
+
export interface ReviewContextDoc {
|
|
4
|
+
title: string;
|
|
5
|
+
url: string;
|
|
6
|
+
excerpt: string;
|
|
7
|
+
}
|
|
8
|
+
/** A tracker issue linked to the block as context. */
|
|
9
|
+
export interface ReviewContextTask {
|
|
10
|
+
key: string;
|
|
11
|
+
title: string;
|
|
12
|
+
status: string;
|
|
13
|
+
type: string;
|
|
14
|
+
description: string;
|
|
15
|
+
}
|
|
16
|
+
/** Everything the reviewer reasons over: the block plus its linked context. */
|
|
17
|
+
export interface RequirementsContext {
|
|
18
|
+
block: Pick<Block, 'title' | 'type' | 'description'>;
|
|
19
|
+
docs: ReviewContextDoc[];
|
|
20
|
+
tasks: ReviewContextTask[];
|
|
21
|
+
/**
|
|
22
|
+
* The standardized requirements document produced by a prior incorporation. When
|
|
23
|
+
* present (a re-review or a redo), it is the authoritative requirements text the
|
|
24
|
+
* reviewer/rework reasons over — the original description + linked context become
|
|
25
|
+
* background reference. Absent on the first pass.
|
|
26
|
+
*/
|
|
27
|
+
incorporatedDoc?: string;
|
|
28
|
+
/**
|
|
29
|
+
* The human's freeform "do it differently" comment when redoing a merge they were
|
|
30
|
+
* unhappy with — folded into the next rework so it corrects course. Absent otherwise.
|
|
31
|
+
*/
|
|
32
|
+
reworkFeedback?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Render the block's "collected requirements" as a single Markdown document — the
|
|
36
|
+
* standardized incorporated document when one exists (a later review/rework cycle),
|
|
37
|
+
* else the block description, plus any linked PRD/RFC pages and tracker issues. Used
|
|
38
|
+
* both as the reviewer's input and as the base the incorporate step rewrites.
|
|
39
|
+
*/
|
|
40
|
+
export declare function renderRequirements(ctx: RequirementsContext): string;
|
|
41
|
+
export declare function buildReviewPrompt(ctx: RequirementsContext): string;
|
|
42
|
+
/** Pull the first JSON object out of a model response (tolerates code fences). */
|
|
43
|
+
export declare function extractJson(text: string): unknown;
|
|
44
|
+
/**
|
|
45
|
+
* Coerce the model's parsed JSON into review items. Tolerant: unknown
|
|
46
|
+
* categories/severities fall back to sensible defaults, items missing both a
|
|
47
|
+
* title and detail are dropped, and the result is sorted high-severity first and
|
|
48
|
+
* capped so a runaway response can't flood the board.
|
|
49
|
+
*/
|
|
50
|
+
export declare function coerceReviewItems(raw: unknown, newId: () => string, now: number): RequirementReviewItem[];
|
|
51
|
+
/**
|
|
52
|
+
* Build the user prompt for the requirements-rework step: the gathered context plus
|
|
53
|
+
* the human's answers (resolved items, folded in) and dismissals (kept out). Works
|
|
54
|
+
* with an empty item list too — the "no challenges" path simply restates the
|
|
55
|
+
* requirements in the standard structure. The {@link REWORK_SYSTEM_PROMPT} (in
|
|
56
|
+
* `@cat-factory/agents`) defines the required output structure.
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildReworkPrompt(ctx: RequirementsContext, items: RequirementReviewItem[]): string;
|
|
59
|
+
/**
|
|
60
|
+
* Whether an incorporation pass has anything to fold in: at least one finding the human
|
|
61
|
+
* answered/resolved with a non-empty reply, or a freeform "do it differently" feedback.
|
|
62
|
+
* When false, the rework + re-review LLM calls would add no new facts — the only thing
|
|
63
|
+
* {@link buildReworkPrompt} could still emit is dismissed items as negative "do NOT add"
|
|
64
|
+
* guidance, never new content — so the engine skips them and settles the review directly
|
|
65
|
+
* (the parallel of a polling gate's "precheck passed, don't spin up the agent" skip).
|
|
66
|
+
* Matches the `answered` filter {@link buildReworkPrompt} uses to decide what gets folded
|
|
67
|
+
* in. Note this changes the all-dismissed case: it no longer produces an LLM-restated
|
|
68
|
+
* (reformatted-but-fact-identical) document; downstream consumes the last incorporated
|
|
69
|
+
* doc if an earlier iteration produced one, else the original description.
|
|
70
|
+
*/
|
|
71
|
+
export declare function hasNotesToIncorporate(items: RequirementReviewItem[], feedback?: string): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* What the engine should do with a reviewer pass's findings:
|
|
74
|
+
* - `auto-pass`: no outstanding findings, or every outstanding finding's severity is at
|
|
75
|
+
* or below the task's tolerated level — record them but advance without a human.
|
|
76
|
+
* - `awaiting`: outstanding findings above the tolerated level and the iteration budget
|
|
77
|
+
* has room — pause for the human to answer/dismiss.
|
|
78
|
+
* - `exceeded`: outstanding findings above the tolerated level but the iteration budget
|
|
79
|
+
* is spent — pause for the human to pick how to proceed.
|
|
80
|
+
*/
|
|
81
|
+
export type ReviewDisposition = 'auto-pass' | 'awaiting' | 'exceeded';
|
|
82
|
+
/**
|
|
83
|
+
* Decide a reviewer pass's disposition from its findings, the task's tolerated concern
|
|
84
|
+
* level and the iteration budget. Only OUTSTANDING items (not yet resolved/dismissed)
|
|
85
|
+
* gate the run, so a pass whose findings the human later dismisses converges. Pure so
|
|
86
|
+
* the engine + tests share one rule.
|
|
87
|
+
*/
|
|
88
|
+
export declare function disposeReview(items: RequirementReviewItem[], opts: {
|
|
89
|
+
iteration: number;
|
|
90
|
+
maxIterations: number;
|
|
91
|
+
concernThreshold: RequirementConcernLevel;
|
|
92
|
+
}): ReviewDisposition;
|
|
93
|
+
//# sourceMappingURL=requirements.logic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requirements.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/requirements/requirements.logic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,KAAK,EACL,uBAAuB,EACvB,qBAAqB,EAGtB,MAAM,qBAAqB,CAAA;AAa5B,sEAAsE;AACtE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,+EAA+E;AAC/E,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,aAAa,CAAC,CAAA;IACpD,IAAI,EAAE,gBAAgB,EAAE,CAAA;IACxB,KAAK,EAAE,iBAAiB,EAAE,CAAA;IAC1B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAyBnE;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAwBlE;AAED,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CASjD;AAgBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,MAAM,MAAM,EACnB,GAAG,EAAE,MAAM,GACV,qBAAqB,EAAE,CA2BzB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,mBAAmB,EACxB,KAAK,EAAE,qBAAqB,EAAE,GAC7B,MAAM,CA6CR;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,qBAAqB,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAKhG;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAAA;AAErE;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,qBAAqB,EAAE,EAC9B,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,uBAAuB,CAAA;CAAE,GAC5F,iBAAiB,CAOnB"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { REQUIREMENT_CONCERN_RANK } from '@cat-factory/contracts';
|
|
2
|
+
// Pure logic for the requirements-review agent: assembling the "collected
|
|
3
|
+
// requirements" text from a block + its linked context, building the review and
|
|
4
|
+
// incorporate prompts, and coercing the model's JSON response into review items.
|
|
5
|
+
// Kept side-effect-free so the integration tests can exercise the prompt/parse
|
|
6
|
+
// paths directly and the service stays a thin orchestrator.
|
|
7
|
+
const CATEGORIES = ['gap', 'clarification', 'assumption', 'risk', 'question'];
|
|
8
|
+
const SEVERITIES = ['low', 'medium', 'high'];
|
|
9
|
+
const SEVERITY_RANK = { high: 0, medium: 1, low: 2 };
|
|
10
|
+
/**
|
|
11
|
+
* Render the block's "collected requirements" as a single Markdown document — the
|
|
12
|
+
* standardized incorporated document when one exists (a later review/rework cycle),
|
|
13
|
+
* else the block description, plus any linked PRD/RFC pages and tracker issues. Used
|
|
14
|
+
* both as the reviewer's input and as the base the incorporate step rewrites.
|
|
15
|
+
*/
|
|
16
|
+
export function renderRequirements(ctx) {
|
|
17
|
+
const lines = ctx.incorporatedDoc?.trim()
|
|
18
|
+
? [
|
|
19
|
+
`# ${ctx.block.title} (${ctx.block.type})`,
|
|
20
|
+
'',
|
|
21
|
+
'## Current standardized requirements (under review)',
|
|
22
|
+
ctx.incorporatedDoc.trim(),
|
|
23
|
+
]
|
|
24
|
+
: [
|
|
25
|
+
`# ${ctx.block.title} (${ctx.block.type})`,
|
|
26
|
+
'',
|
|
27
|
+
'## Description',
|
|
28
|
+
ctx.block.description?.trim() || '(no description provided)',
|
|
29
|
+
];
|
|
30
|
+
if (ctx.docs.length) {
|
|
31
|
+
lines.push('', '## Linked requirement / PRD / RFC documents');
|
|
32
|
+
for (const d of ctx.docs)
|
|
33
|
+
lines.push('', `### ${d.title} (${d.url})`, d.excerpt);
|
|
34
|
+
}
|
|
35
|
+
if (ctx.tasks.length) {
|
|
36
|
+
lines.push('', '## Linked tracker issues');
|
|
37
|
+
for (const t of ctx.tasks) {
|
|
38
|
+
lines.push('', `### ${t.key} — ${t.title} [${t.type} / ${t.status}]`, t.description);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return lines.join('\n');
|
|
42
|
+
}
|
|
43
|
+
export function buildReviewPrompt(ctx) {
|
|
44
|
+
return [
|
|
45
|
+
'Here are the collected requirements to review:',
|
|
46
|
+
'',
|
|
47
|
+
renderRequirements(ctx),
|
|
48
|
+
'',
|
|
49
|
+
'Produce a JSON object of this exact shape:',
|
|
50
|
+
'{',
|
|
51
|
+
' "items": [',
|
|
52
|
+
' {',
|
|
53
|
+
' "category": "gap|clarification|assumption|risk|question",',
|
|
54
|
+
' "severity": "low|medium|high",',
|
|
55
|
+
' "title": "short headline of the concern",',
|
|
56
|
+
' "detail": "the full question / gap / challenge, phrased for a product owner"',
|
|
57
|
+
' }',
|
|
58
|
+
' ]',
|
|
59
|
+
'}',
|
|
60
|
+
'',
|
|
61
|
+
'Assign a severity to EVERY item — no item may omit it. Use `high` for a gap or ' +
|
|
62
|
+
'ambiguity that would block correct implementation, `medium` for one that risks ' +
|
|
63
|
+
'rework or a wrong assumption, and `low` for a minor clarification or nice-to-have. ' +
|
|
64
|
+
'Raise between 0 and 20 items, ordered by severity (high first). If the requirements ' +
|
|
65
|
+
'are genuinely complete and unambiguous, return an empty items array. Output JSON only.',
|
|
66
|
+
].join('\n');
|
|
67
|
+
}
|
|
68
|
+
/** Pull the first JSON object out of a model response (tolerates code fences). */
|
|
69
|
+
export function extractJson(text) {
|
|
70
|
+
const start = text.indexOf('{');
|
|
71
|
+
const end = text.lastIndexOf('}');
|
|
72
|
+
if (start === -1 || end <= start)
|
|
73
|
+
return null;
|
|
74
|
+
try {
|
|
75
|
+
return JSON.parse(text.slice(start, end + 1));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function asString(value) {
|
|
82
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
83
|
+
}
|
|
84
|
+
function coerceCategory(value) {
|
|
85
|
+
return CATEGORIES.includes(value)
|
|
86
|
+
? value
|
|
87
|
+
: 'question';
|
|
88
|
+
}
|
|
89
|
+
function coerceSeverity(value) {
|
|
90
|
+
return SEVERITIES.includes(value) ? value : 'medium';
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Coerce the model's parsed JSON into review items. Tolerant: unknown
|
|
94
|
+
* categories/severities fall back to sensible defaults, items missing both a
|
|
95
|
+
* title and detail are dropped, and the result is sorted high-severity first and
|
|
96
|
+
* capped so a runaway response can't flood the board.
|
|
97
|
+
*/
|
|
98
|
+
export function coerceReviewItems(raw, newId, now) {
|
|
99
|
+
const list = Array.isArray(raw?.items)
|
|
100
|
+
? raw.items
|
|
101
|
+
: Array.isArray(raw)
|
|
102
|
+
? raw
|
|
103
|
+
: [];
|
|
104
|
+
const items = [];
|
|
105
|
+
for (const entry of list) {
|
|
106
|
+
if (!entry || typeof entry !== 'object')
|
|
107
|
+
continue;
|
|
108
|
+
const obj = entry;
|
|
109
|
+
const title = asString(obj.title);
|
|
110
|
+
const detail = asString(obj.detail) || asString(obj.question);
|
|
111
|
+
if (!title && !detail)
|
|
112
|
+
continue;
|
|
113
|
+
items.push({
|
|
114
|
+
id: newId(),
|
|
115
|
+
category: coerceCategory(obj.category),
|
|
116
|
+
severity: coerceSeverity(obj.severity),
|
|
117
|
+
title: title || detail.slice(0, 80),
|
|
118
|
+
detail: detail || title,
|
|
119
|
+
status: 'open',
|
|
120
|
+
reply: null,
|
|
121
|
+
createdAt: now,
|
|
122
|
+
updatedAt: now,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
items.sort((a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity]);
|
|
126
|
+
return items.slice(0, 20);
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Build the user prompt for the requirements-rework step: the gathered context plus
|
|
130
|
+
* the human's answers (resolved items, folded in) and dismissals (kept out). Works
|
|
131
|
+
* with an empty item list too — the "no challenges" path simply restates the
|
|
132
|
+
* requirements in the standard structure. The {@link REWORK_SYSTEM_PROMPT} (in
|
|
133
|
+
* `@cat-factory/agents`) defines the required output structure.
|
|
134
|
+
*/
|
|
135
|
+
export function buildReworkPrompt(ctx, items) {
|
|
136
|
+
const lines = ['Current collected requirements:', '', renderRequirements(ctx), ''];
|
|
137
|
+
// An answered item (the human recorded a reply) is folded in; a resolved one too.
|
|
138
|
+
const answered = items.filter((i) => (i.status === 'answered' || i.status === 'resolved') && i.reply?.trim());
|
|
139
|
+
const dismissed = items.filter((i) => i.status === 'dismissed');
|
|
140
|
+
if (answered.length) {
|
|
141
|
+
lines.push('Clarifications the product owner provided (fold these in):', '');
|
|
142
|
+
for (const i of answered) {
|
|
143
|
+
lines.push(`- Q (${i.category}): ${i.title} — ${i.detail}`);
|
|
144
|
+
lines.push(` A: ${i.reply?.trim() || '(no answer recorded)'}`);
|
|
145
|
+
}
|
|
146
|
+
lines.push('');
|
|
147
|
+
}
|
|
148
|
+
if (dismissed.length) {
|
|
149
|
+
lines.push('Items the product owner dismissed as out of scope (do NOT add these):', '');
|
|
150
|
+
for (const i of dismissed)
|
|
151
|
+
lines.push(`- ${i.title}`);
|
|
152
|
+
lines.push('');
|
|
153
|
+
}
|
|
154
|
+
if (!answered.length && !dismissed.length) {
|
|
155
|
+
lines.push('The reviewer raised no open questions — restate the requirements cleanly in the ' +
|
|
156
|
+
'standard structure without inventing new facts.', '');
|
|
157
|
+
}
|
|
158
|
+
// When the human was unhappy with a previous merge and asked to redo it, fold their
|
|
159
|
+
// freeform direction in so this attempt corrects course rather than repeating it.
|
|
160
|
+
if (ctx.reworkFeedback?.trim()) {
|
|
161
|
+
lines.push('', 'The reviewer was UNHAPPY with your previous reworked document and asked you to ' +
|
|
162
|
+
'redo it with this specific direction — follow it closely:', '', ctx.reworkFeedback.trim(), '');
|
|
163
|
+
}
|
|
164
|
+
lines.push('Rewrite the requirements as a single self-contained Markdown document in the standard ' +
|
|
165
|
+
'structure described in your instructions, folding in every answer above. Output the ' +
|
|
166
|
+
'revised requirements only.');
|
|
167
|
+
return lines.join('\n');
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Whether an incorporation pass has anything to fold in: at least one finding the human
|
|
171
|
+
* answered/resolved with a non-empty reply, or a freeform "do it differently" feedback.
|
|
172
|
+
* When false, the rework + re-review LLM calls would add no new facts — the only thing
|
|
173
|
+
* {@link buildReworkPrompt} could still emit is dismissed items as negative "do NOT add"
|
|
174
|
+
* guidance, never new content — so the engine skips them and settles the review directly
|
|
175
|
+
* (the parallel of a polling gate's "precheck passed, don't spin up the agent" skip).
|
|
176
|
+
* Matches the `answered` filter {@link buildReworkPrompt} uses to decide what gets folded
|
|
177
|
+
* in. Note this changes the all-dismissed case: it no longer produces an LLM-restated
|
|
178
|
+
* (reformatted-but-fact-identical) document; downstream consumes the last incorporated
|
|
179
|
+
* doc if an earlier iteration produced one, else the original description.
|
|
180
|
+
*/
|
|
181
|
+
export function hasNotesToIncorporate(items, feedback) {
|
|
182
|
+
if (feedback?.trim())
|
|
183
|
+
return true;
|
|
184
|
+
return items.some((i) => (i.status === 'answered' || i.status === 'resolved') && !!i.reply?.trim());
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Decide a reviewer pass's disposition from its findings, the task's tolerated concern
|
|
188
|
+
* level and the iteration budget. Only OUTSTANDING items (not yet resolved/dismissed)
|
|
189
|
+
* gate the run, so a pass whose findings the human later dismisses converges. Pure so
|
|
190
|
+
* the engine + tests share one rule.
|
|
191
|
+
*/
|
|
192
|
+
export function disposeReview(items, opts) {
|
|
193
|
+
const outstanding = items.filter((i) => i.status !== 'dismissed' && i.status !== 'resolved');
|
|
194
|
+
if (outstanding.length === 0)
|
|
195
|
+
return 'auto-pass';
|
|
196
|
+
const maxRank = Math.max(...outstanding.map((i) => REQUIREMENT_CONCERN_RANK[i.severity]));
|
|
197
|
+
if (maxRank <= REQUIREMENT_CONCERN_RANK[opts.concernThreshold])
|
|
198
|
+
return 'auto-pass';
|
|
199
|
+
if (opts.iteration >= opts.maxIterations)
|
|
200
|
+
return 'exceeded';
|
|
201
|
+
return 'awaiting';
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=requirements.logic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requirements.logic.js","sourceRoot":"","sources":["../../../src/modules/requirements/requirements.logic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { Block, BlockRepository, Clock, IdGenerator, ModelProvider, ModelProviderResolver, ModelRef, NotificationType, RequirementConcernLevel, RequirementReviewItem, RequirementReviewStatus, ReviewItemStatus } from '@cat-factory/kernel';
|
|
2
|
+
import type { NotificationService } from '../notifications/NotificationService.js';
|
|
3
|
+
/** The fields every review (requirements or clarity) shares; the doc field is per-kind. */
|
|
4
|
+
export interface ReviewCommon {
|
|
5
|
+
id: string;
|
|
6
|
+
blockId: string;
|
|
7
|
+
status: RequirementReviewStatus;
|
|
8
|
+
items: RequirementReviewItem[];
|
|
9
|
+
model: string | null;
|
|
10
|
+
iteration: number;
|
|
11
|
+
maxIterations: number;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
updatedAt: number;
|
|
14
|
+
}
|
|
15
|
+
/** The structural persistence port both review repositories satisfy. */
|
|
16
|
+
export interface ReviewRepository<TReview> {
|
|
17
|
+
getByBlock(workspaceId: string, blockId: string): Promise<TReview | null>;
|
|
18
|
+
get(workspaceId: string, id: string): Promise<TReview | null>;
|
|
19
|
+
upsert(workspaceId: string, review: TReview): Promise<void>;
|
|
20
|
+
deleteByBlock(workspaceId: string, blockId: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
/** The runtime dependencies shared by every iterative-review service. */
|
|
23
|
+
export interface IterativeReviewDeps {
|
|
24
|
+
blockRepository: BlockRepository;
|
|
25
|
+
idGenerator: IdGenerator;
|
|
26
|
+
clock: Clock;
|
|
27
|
+
/** Resolve a {@link ModelProvider} for a workspace's credential scope. Preferred. */
|
|
28
|
+
modelProviderResolver?: ModelProviderResolver;
|
|
29
|
+
/** Static reviewer model provider (e.g. a fake in tests). Used when no resolver is set. */
|
|
30
|
+
modelProvider?: ModelProvider;
|
|
31
|
+
/** Default model ref when the block pins none — the agents' routing default. */
|
|
32
|
+
modelRef?: ModelRef;
|
|
33
|
+
/** Resolve a block's selected model id to a ref (the deployment-aware resolver). */
|
|
34
|
+
resolveBlockModel?: (modelId: string | undefined) => ModelRef | undefined;
|
|
35
|
+
/** Resolve the workspace's per-agent-kind default model id (consulted when the block pins none). */
|
|
36
|
+
resolveWorkspaceModelDefault?: (workspaceId: string, agentKind: string) => Promise<string | undefined>;
|
|
37
|
+
/** Raises a notification when a review yields findings. Optional. */
|
|
38
|
+
notificationService?: NotificationService;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Stateless, synchronous iterative reviewer (no container, no durable driver). The LLM is
|
|
42
|
+
* reached through the provider-agnostic {@link ModelProvider} port — the same one the
|
|
43
|
+
* document planner uses — so this service never imports a provider SDK or an API key. The
|
|
44
|
+
* model is resolved exactly like an agent step: a model pinned on the block wins, else the
|
|
45
|
+
* workspace's per-kind default, else the routing default (which falls back to Cloudflare
|
|
46
|
+
* Workers AI when no direct provider key is set). Reads of an existing review work
|
|
47
|
+
* regardless.
|
|
48
|
+
*
|
|
49
|
+
* @typeParam TReview The persisted review type (adds a kind-specific document field).
|
|
50
|
+
* @typeParam TContext The reviewer's per-kind context (the subject under review).
|
|
51
|
+
* @typeParam TContextInput Extra per-call inputs threaded into context gathering (e.g. an
|
|
52
|
+
* investigation report for clarity); `{}` when a kind needs none.
|
|
53
|
+
*/
|
|
54
|
+
export declare abstract class IterativeReviewService<TReview extends ReviewCommon, TContext, TContextInput = unknown> {
|
|
55
|
+
protected readonly deps: IterativeReviewDeps;
|
|
56
|
+
constructor(deps: IterativeReviewDeps);
|
|
57
|
+
protected abstract readonly repository: ReviewRepository<TReview>;
|
|
58
|
+
/** Label for `assertFound` (e.g. 'Requirement review' / 'Clarity review'). */
|
|
59
|
+
protected abstract readonly entityName: string;
|
|
60
|
+
/** Human label for error messages (e.g. 'requirements reviewer' / 'clarity reviewer'). */
|
|
61
|
+
protected abstract readonly reviewerLabel: string;
|
|
62
|
+
/** The agent kind keying the workspace default model + observability (e.g. 'requirements-review'). */
|
|
63
|
+
protected abstract readonly reviewAgentKind: string;
|
|
64
|
+
/** The rework agent kind for observability (e.g. 'requirements-rework'). */
|
|
65
|
+
protected abstract readonly reworkAgentKind: string;
|
|
66
|
+
protected abstract readonly reviewSystemPrompt: string;
|
|
67
|
+
protected abstract readonly reworkSystemPrompt: string;
|
|
68
|
+
/** Id prefix for fresh reviews / items (e.g. 'rrv' / 'rri'). */
|
|
69
|
+
protected abstract readonly reviewIdPrefix: string;
|
|
70
|
+
protected abstract readonly itemIdPrefix: string;
|
|
71
|
+
/** Noun for the "no revised X produced" error (e.g. 'revised requirements'). */
|
|
72
|
+
protected abstract readonly revisedNoun: string;
|
|
73
|
+
/** The full error message when the rework output is length-truncated. */
|
|
74
|
+
protected abstract readonly truncationMessage: string;
|
|
75
|
+
protected abstract readonly notificationType: NotificationType;
|
|
76
|
+
/** Notification title for a findings notification (e.g. `Requirements review: ${title}`). */
|
|
77
|
+
protected abstract notificationTitle(block: Block): string;
|
|
78
|
+
/** Notification body lead-in noun (e.g. 'The reviewer' / 'The clarity reviewer'). */
|
|
79
|
+
protected abstract readonly notificationSubject: string;
|
|
80
|
+
/** Assemble the subject under review (block + any kind-specific context). */
|
|
81
|
+
protected abstract gatherContext(workspaceId: string, block: Block, input: TContextInput): Promise<TContext>;
|
|
82
|
+
protected abstract buildReviewPrompt(ctx: TContext): string;
|
|
83
|
+
protected abstract buildReworkPrompt(ctx: TContext, items: RequirementReviewItem[]): string;
|
|
84
|
+
/** Apply a prior incorporated document to the context (a re-review / redo base). */
|
|
85
|
+
protected abstract applyIncorporatedDoc(ctx: TContext, doc: string): void;
|
|
86
|
+
/** Apply the human's freeform "do it differently" feedback to the context. */
|
|
87
|
+
protected abstract applyFeedback(ctx: TContext, feedback: string): void;
|
|
88
|
+
/** Read the kind-specific document field off a review. */
|
|
89
|
+
protected abstract readDoc(review: TReview): string | null;
|
|
90
|
+
/** Return a copy of the review with its document field set. */
|
|
91
|
+
protected abstract withDoc(review: TReview, doc: string): TReview;
|
|
92
|
+
/** Build a fresh review from the common fields, initialising the document field to null. */
|
|
93
|
+
protected abstract newReview(common: ReviewCommon): TReview;
|
|
94
|
+
/** Whether the LLM-backed review path is available. */
|
|
95
|
+
get enabled(): boolean;
|
|
96
|
+
/** The current review for a block, or null if none has been run. */
|
|
97
|
+
getForBlock(workspaceId: string, blockId: string): Promise<TReview | null>;
|
|
98
|
+
/**
|
|
99
|
+
* Run a fresh review of a block (iteration 1). Replaces any prior review for the block
|
|
100
|
+
* (answers from a stale run don't carry over). The returned review's `status` encodes the
|
|
101
|
+
* disposition: `incorporated` (auto-pass — advance), `ready` (findings to answer) or
|
|
102
|
+
* `exceeded` (findings but the iteration budget is already 1).
|
|
103
|
+
*/
|
|
104
|
+
review(workspaceId: string, blockId: string, opts?: {
|
|
105
|
+
maxIterations?: number;
|
|
106
|
+
concernThreshold?: RequirementConcernLevel;
|
|
107
|
+
} & TContextInput): Promise<TReview>;
|
|
108
|
+
/**
|
|
109
|
+
* Re-review the block against its current incorporated document (one more reviewer pass;
|
|
110
|
+
* `iteration` increments). Keeps the review id + the document; replaces the items with the
|
|
111
|
+
* fresh findings and re-encodes the disposition into `status`. Called after an
|
|
112
|
+
* incorporation so the loop can converge (`incorporated`), continue (`ready`) or stop for a
|
|
113
|
+
* human (`exceeded`).
|
|
114
|
+
*/
|
|
115
|
+
reReview(workspaceId: string, reviewId: string, opts?: {
|
|
116
|
+
concernThreshold?: RequirementConcernLevel;
|
|
117
|
+
}): Promise<TReview>;
|
|
118
|
+
/** Record a human's answer to one item (and flip it to `answered`). */
|
|
119
|
+
replyToItem(workspaceId: string, reviewId: string, itemId: string, reply: string): Promise<TReview>;
|
|
120
|
+
/** Set an item's status (resolve / dismiss / reopen). */
|
|
121
|
+
setItemStatus(workspaceId: string, reviewId: string, itemId: string, status: ReviewItemStatus): Promise<TReview>;
|
|
122
|
+
/**
|
|
123
|
+
* Incorporate the human's answers (and dismissals) into one self-contained, standard-format
|
|
124
|
+
* document. Requires every finding to be answered or dismissed (no `open` items). The
|
|
125
|
+
* optional `feedback` is the human's "do it differently" direction when redoing a merge they
|
|
126
|
+
* were unhappy with, folded into the prompt alongside the prior document. Stores the document
|
|
127
|
+
* on the review and parks it `merged` for the human to re-review or redo.
|
|
128
|
+
*/
|
|
129
|
+
incorporate(workspaceId: string, reviewId: string, opts?: {
|
|
130
|
+
feedback?: string;
|
|
131
|
+
} & TContextInput): Promise<{
|
|
132
|
+
review: TReview;
|
|
133
|
+
}>;
|
|
134
|
+
/**
|
|
135
|
+
* Mark the review settled (`incorporated`) — the phase is done and the last incorporated
|
|
136
|
+
* document (if any) becomes what downstream agents consume.
|
|
137
|
+
*/
|
|
138
|
+
markIncorporated(workspaceId: string, reviewId: string): Promise<TReview>;
|
|
139
|
+
/** Grant one more reviewer pass after the cap was hit, reopening the loop (`ready`). */
|
|
140
|
+
grantExtraRound(workspaceId: string, reviewId: string): Promise<TReview>;
|
|
141
|
+
/** Flag a review as `incorporating` (the durable driver is about to fold + re-review). */
|
|
142
|
+
markIncorporating(workspaceId: string, reviewId: string): Promise<TReview>;
|
|
143
|
+
/** Flag a review as `reviewing` (the second async stage — re-reviewing the folded document). */
|
|
144
|
+
markReReviewing(workspaceId: string, reviewId: string): Promise<TReview>;
|
|
145
|
+
/** The model provider for a workspace's scope (per-scope DB pool, else the static one). */
|
|
146
|
+
protected providerFor(workspaceId: string): Promise<ModelProvider | undefined>;
|
|
147
|
+
/**
|
|
148
|
+
* The model to run for a block, with the same precedence as a pipeline step: the block's
|
|
149
|
+
* pinned selection wins, else the workspace's per-kind default, else the routing default.
|
|
150
|
+
* A pinned subscription model (Claude Code / Codex) is degraded to the routing default
|
|
151
|
+
* because the reviewer is an INLINE LLM call with no provider key for the container harness
|
|
152
|
+
* — the same seam the inline agent executor uses, so the two can't drift.
|
|
153
|
+
*/
|
|
154
|
+
protected modelFor(workspaceId: string, block: Block): Promise<ModelRef | undefined>;
|
|
155
|
+
/** Resolve the provider + ref, throwing the kind's "no model configured" error if unavailable. */
|
|
156
|
+
protected resolveModel(workspaceId: string, block: Block): Promise<{
|
|
157
|
+
modelProvider: ModelProvider;
|
|
158
|
+
ref: ModelRef;
|
|
159
|
+
}>;
|
|
160
|
+
private reviewerFailed;
|
|
161
|
+
/** Run the reviewer LLM over the prepared context and coerce the JSON into review items. */
|
|
162
|
+
protected runReviewer(workspaceId: string, block: Block, context: TContext): Promise<{
|
|
163
|
+
ref: ModelRef;
|
|
164
|
+
items: RequirementReviewItem[];
|
|
165
|
+
}>;
|
|
166
|
+
/**
|
|
167
|
+
* Tell people to react to a review's findings. Best-effort and only when there ARE findings
|
|
168
|
+
* — a clean review pings no one. Never lets a notification failure break the awaited review.
|
|
169
|
+
*/
|
|
170
|
+
protected notifyFindings(workspaceId: string, block: Block, findingCount: number): Promise<void>;
|
|
171
|
+
private load;
|
|
172
|
+
protected patchReview(workspaceId: string, reviewId: string, patch: (review: TReview) => TReview): Promise<TReview>;
|
|
173
|
+
private mutateItem;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=IterativeReviewService.d.ts.map
|