@kynetic-ai/spec 0.1.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/README.md +263 -0
- package/dist/acp/client.d.ts +159 -0
- package/dist/acp/client.d.ts.map +1 -0
- package/dist/acp/client.js +255 -0
- package/dist/acp/client.js.map +1 -0
- package/dist/acp/framing.d.ts +119 -0
- package/dist/acp/framing.d.ts.map +1 -0
- package/dist/acp/framing.js +302 -0
- package/dist/acp/framing.js.map +1 -0
- package/dist/acp/index.d.ts +14 -0
- package/dist/acp/index.d.ts.map +1 -0
- package/dist/acp/index.js +13 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/acp/types.d.ts +89 -0
- package/dist/acp/types.d.ts.map +1 -0
- package/dist/acp/types.js +99 -0
- package/dist/acp/types.js.map +1 -0
- package/dist/agents/adapters.d.ts +55 -0
- package/dist/agents/adapters.d.ts.map +1 -0
- package/dist/agents/adapters.js +84 -0
- package/dist/agents/adapters.js.map +1 -0
- package/dist/agents/index.d.ts +8 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +10 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/spawner.d.ts +53 -0
- package/dist/agents/spawner.d.ts.map +1 -0
- package/dist/agents/spawner.js +83 -0
- package/dist/agents/spawner.js.map +1 -0
- package/dist/cli/batch.d.ts +82 -0
- package/dist/cli/batch.d.ts.map +1 -0
- package/dist/cli/batch.js +162 -0
- package/dist/cli/batch.js.map +1 -0
- package/dist/cli/commands/clone-for-testing.d.ts +6 -0
- package/dist/cli/commands/clone-for-testing.d.ts.map +1 -0
- package/dist/cli/commands/clone-for-testing.js +176 -0
- package/dist/cli/commands/clone-for-testing.js.map +1 -0
- package/dist/cli/commands/derive.d.ts +6 -0
- package/dist/cli/commands/derive.d.ts.map +1 -0
- package/dist/cli/commands/derive.js +450 -0
- package/dist/cli/commands/derive.js.map +1 -0
- package/dist/cli/commands/help.d.ts +6 -0
- package/dist/cli/commands/help.d.ts.map +1 -0
- package/dist/cli/commands/help.js +196 -0
- package/dist/cli/commands/help.js.map +1 -0
- package/dist/cli/commands/inbox.d.ts +6 -0
- package/dist/cli/commands/inbox.d.ts.map +1 -0
- package/dist/cli/commands/inbox.js +235 -0
- package/dist/cli/commands/inbox.js.map +1 -0
- package/dist/cli/commands/index.d.ts +20 -0
- package/dist/cli/commands/index.d.ts.map +1 -0
- package/dist/cli/commands/index.js +21 -0
- package/dist/cli/commands/index.js.map +1 -0
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +245 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/item.d.ts +6 -0
- package/dist/cli/commands/item.d.ts.map +1 -0
- package/dist/cli/commands/item.js +1311 -0
- package/dist/cli/commands/item.js.map +1 -0
- package/dist/cli/commands/link.d.ts +6 -0
- package/dist/cli/commands/link.d.ts.map +1 -0
- package/dist/cli/commands/link.js +288 -0
- package/dist/cli/commands/link.js.map +1 -0
- package/dist/cli/commands/log.d.ts +16 -0
- package/dist/cli/commands/log.d.ts.map +1 -0
- package/dist/cli/commands/log.js +291 -0
- package/dist/cli/commands/log.js.map +1 -0
- package/dist/cli/commands/meta.d.ts +15 -0
- package/dist/cli/commands/meta.d.ts.map +1 -0
- package/dist/cli/commands/meta.js +1378 -0
- package/dist/cli/commands/meta.js.map +1 -0
- package/dist/cli/commands/module.d.ts +6 -0
- package/dist/cli/commands/module.d.ts.map +1 -0
- package/dist/cli/commands/module.js +102 -0
- package/dist/cli/commands/module.js.map +1 -0
- package/dist/cli/commands/ralph.d.ts +9 -0
- package/dist/cli/commands/ralph.d.ts.map +1 -0
- package/dist/cli/commands/ralph.js +465 -0
- package/dist/cli/commands/ralph.js.map +1 -0
- package/dist/cli/commands/search.d.ts +6 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +134 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/session.d.ts +164 -0
- package/dist/cli/commands/session.d.ts.map +1 -0
- package/dist/cli/commands/session.js +745 -0
- package/dist/cli/commands/session.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +26 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +586 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/shadow.d.ts +6 -0
- package/dist/cli/commands/shadow.d.ts.map +1 -0
- package/dist/cli/commands/shadow.js +299 -0
- package/dist/cli/commands/shadow.js.map +1 -0
- package/dist/cli/commands/task.d.ts +6 -0
- package/dist/cli/commands/task.d.ts.map +1 -0
- package/dist/cli/commands/task.js +1514 -0
- package/dist/cli/commands/task.js.map +1 -0
- package/dist/cli/commands/tasks.d.ts +6 -0
- package/dist/cli/commands/tasks.d.ts.map +1 -0
- package/dist/cli/commands/tasks.js +347 -0
- package/dist/cli/commands/tasks.js.map +1 -0
- package/dist/cli/commands/trait.d.ts +10 -0
- package/dist/cli/commands/trait.d.ts.map +1 -0
- package/dist/cli/commands/trait.js +295 -0
- package/dist/cli/commands/trait.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +6 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +626 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/exit-codes.d.ts +62 -0
- package/dist/cli/exit-codes.d.ts.map +1 -0
- package/dist/cli/exit-codes.js +65 -0
- package/dist/cli/exit-codes.js.map +1 -0
- package/dist/cli/help/content.d.ts +35 -0
- package/dist/cli/help/content.d.ts.map +1 -0
- package/dist/cli/help/content.js +312 -0
- package/dist/cli/help/content.js.map +1 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +85 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/introspection.d.ts +87 -0
- package/dist/cli/introspection.d.ts.map +1 -0
- package/dist/cli/introspection.js +127 -0
- package/dist/cli/introspection.js.map +1 -0
- package/dist/cli/output.d.ts +56 -0
- package/dist/cli/output.d.ts.map +1 -0
- package/dist/cli/output.js +467 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/suggest.d.ts +16 -0
- package/dist/cli/suggest.d.ts.map +1 -0
- package/dist/cli/suggest.js +72 -0
- package/dist/cli/suggest.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/alignment.d.ts +113 -0
- package/dist/parser/alignment.d.ts.map +1 -0
- package/dist/parser/alignment.js +261 -0
- package/dist/parser/alignment.js.map +1 -0
- package/dist/parser/assess.d.ts +81 -0
- package/dist/parser/assess.d.ts.map +1 -0
- package/dist/parser/assess.js +197 -0
- package/dist/parser/assess.js.map +1 -0
- package/dist/parser/convention-validation.d.ts +48 -0
- package/dist/parser/convention-validation.d.ts.map +1 -0
- package/dist/parser/convention-validation.js +167 -0
- package/dist/parser/convention-validation.js.map +1 -0
- package/dist/parser/fix.d.ts +38 -0
- package/dist/parser/fix.d.ts.map +1 -0
- package/dist/parser/fix.js +185 -0
- package/dist/parser/fix.js.map +1 -0
- package/dist/parser/index.d.ts +12 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +13 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/items.d.ts +138 -0
- package/dist/parser/items.d.ts.map +1 -0
- package/dist/parser/items.js +321 -0
- package/dist/parser/items.js.map +1 -0
- package/dist/parser/meta.d.ts +120 -0
- package/dist/parser/meta.d.ts.map +1 -0
- package/dist/parser/meta.js +441 -0
- package/dist/parser/meta.js.map +1 -0
- package/dist/parser/refs.d.ts +185 -0
- package/dist/parser/refs.d.ts.map +1 -0
- package/dist/parser/refs.js +404 -0
- package/dist/parser/refs.js.map +1 -0
- package/dist/parser/shadow.d.ts +253 -0
- package/dist/parser/shadow.d.ts.map +1 -0
- package/dist/parser/shadow.js +1053 -0
- package/dist/parser/shadow.js.map +1 -0
- package/dist/parser/traits.d.ts +72 -0
- package/dist/parser/traits.d.ts.map +1 -0
- package/dist/parser/traits.js +120 -0
- package/dist/parser/traits.js.map +1 -0
- package/dist/parser/validate.d.ts +89 -0
- package/dist/parser/validate.d.ts.map +1 -0
- package/dist/parser/validate.js +817 -0
- package/dist/parser/validate.js.map +1 -0
- package/dist/parser/yaml.d.ts +326 -0
- package/dist/parser/yaml.d.ts.map +1 -0
- package/dist/parser/yaml.js +1383 -0
- package/dist/parser/yaml.js.map +1 -0
- package/dist/ralph/cli-renderer.d.ts +20 -0
- package/dist/ralph/cli-renderer.d.ts.map +1 -0
- package/dist/ralph/cli-renderer.js +179 -0
- package/dist/ralph/cli-renderer.js.map +1 -0
- package/dist/ralph/events.d.ts +65 -0
- package/dist/ralph/events.d.ts.map +1 -0
- package/dist/ralph/events.js +397 -0
- package/dist/ralph/events.js.map +1 -0
- package/dist/ralph/index.d.ts +8 -0
- package/dist/ralph/index.d.ts.map +1 -0
- package/dist/ralph/index.js +10 -0
- package/dist/ralph/index.js.map +1 -0
- package/dist/schema/common.d.ts +46 -0
- package/dist/schema/common.d.ts.map +1 -0
- package/dist/schema/common.js +71 -0
- package/dist/schema/common.js.map +1 -0
- package/dist/schema/inbox.d.ts +90 -0
- package/dist/schema/inbox.d.ts.map +1 -0
- package/dist/schema/inbox.js +30 -0
- package/dist/schema/inbox.js.map +1 -0
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +7 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/meta.d.ts +762 -0
- package/dist/schema/meta.d.ts.map +1 -0
- package/dist/schema/meta.js +144 -0
- package/dist/schema/meta.js.map +1 -0
- package/dist/schema/spec.d.ts +912 -0
- package/dist/schema/spec.d.ts.map +1 -0
- package/dist/schema/spec.js +104 -0
- package/dist/schema/spec.js.map +1 -0
- package/dist/schema/task.d.ts +664 -0
- package/dist/schema/task.d.ts.map +1 -0
- package/dist/schema/task.js +130 -0
- package/dist/schema/task.js.map +1 -0
- package/dist/sessions/index.d.ts +11 -0
- package/dist/sessions/index.d.ts.map +1 -0
- package/dist/sessions/index.js +13 -0
- package/dist/sessions/index.js.map +1 -0
- package/dist/sessions/store.d.ts +144 -0
- package/dist/sessions/store.d.ts.map +1 -0
- package/dist/sessions/store.js +325 -0
- package/dist/sessions/store.js.map +1 -0
- package/dist/sessions/types.d.ts +157 -0
- package/dist/sessions/types.d.ts.map +1 -0
- package/dist/sessions/types.js +90 -0
- package/dist/sessions/types.js.map +1 -0
- package/dist/strings/errors.d.ts +420 -0
- package/dist/strings/errors.d.ts.map +1 -0
- package/dist/strings/errors.js +282 -0
- package/dist/strings/errors.js.map +1 -0
- package/dist/strings/guidance.d.ts +65 -0
- package/dist/strings/guidance.d.ts.map +1 -0
- package/dist/strings/guidance.js +66 -0
- package/dist/strings/guidance.js.map +1 -0
- package/dist/strings/index.d.ts +12 -0
- package/dist/strings/index.d.ts.map +1 -0
- package/dist/strings/index.js +12 -0
- package/dist/strings/index.js.map +1 -0
- package/dist/strings/labels.d.ts +74 -0
- package/dist/strings/labels.d.ts.map +1 -0
- package/dist/strings/labels.js +75 -0
- package/dist/strings/labels.js.map +1 -0
- package/dist/strings/validation.d.ts +126 -0
- package/dist/strings/validation.d.ts.map +1 -0
- package/dist/strings/validation.js +135 -0
- package/dist/strings/validation.js.map +1 -0
- package/dist/utils/commit.d.ts +23 -0
- package/dist/utils/commit.d.ts.map +1 -0
- package/dist/utils/commit.js +67 -0
- package/dist/utils/commit.js.map +1 -0
- package/dist/utils/git.d.ts +57 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +192 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/grep.d.ts +28 -0
- package/dist/utils/grep.d.ts.map +1 -0
- package/dist/utils/grep.js +86 -0
- package/dist/utils/grep.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/time.d.ts +18 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +61 -0
- package/dist/utils/time.js.map +1 -0
- package/package.json +62 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alignment tracking between specs and tasks.
|
|
3
|
+
*
|
|
4
|
+
* Provides bidirectional mapping from spec items to implementing tasks,
|
|
5
|
+
* and detects alignment issues like orphaned specs or stale implementation status.
|
|
6
|
+
*/
|
|
7
|
+
import type { LoadedSpecItem, LoadedTask, KspecContext } from './yaml.js';
|
|
8
|
+
import type { ReferenceIndex } from './refs.js';
|
|
9
|
+
import type { ImplementationStatus } from '../schema/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* Summary of a spec item's implementation status based on linked tasks
|
|
12
|
+
*/
|
|
13
|
+
export interface SpecImplementationSummary {
|
|
14
|
+
specUlid: string;
|
|
15
|
+
specTitle: string;
|
|
16
|
+
currentStatus: ImplementationStatus;
|
|
17
|
+
expectedStatus: ImplementationStatus;
|
|
18
|
+
linkedTasks: LinkedTaskSummary[];
|
|
19
|
+
isAligned: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Summary of a task linked to a spec item
|
|
23
|
+
*/
|
|
24
|
+
export interface LinkedTaskSummary {
|
|
25
|
+
taskUlid: string;
|
|
26
|
+
taskTitle: string;
|
|
27
|
+
taskStatus: string;
|
|
28
|
+
hasNotes: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Alignment warning
|
|
32
|
+
*/
|
|
33
|
+
export interface AlignmentWarning {
|
|
34
|
+
type: 'orphaned_spec' | 'status_mismatch' | 'stale_implementation';
|
|
35
|
+
specUlid?: string;
|
|
36
|
+
specTitle?: string;
|
|
37
|
+
taskUlid?: string;
|
|
38
|
+
message: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Index for tracking spec-task alignment.
|
|
42
|
+
* Build once when loading, then query for alignment issues.
|
|
43
|
+
*/
|
|
44
|
+
export declare class AlignmentIndex {
|
|
45
|
+
/** spec ULID → task ULIDs that reference it */
|
|
46
|
+
private specToTasks;
|
|
47
|
+
/** task ULID → spec ULID it references */
|
|
48
|
+
private taskToSpec;
|
|
49
|
+
/** All spec items by ULID */
|
|
50
|
+
private specItems;
|
|
51
|
+
/** All tasks by ULID */
|
|
52
|
+
private tasks;
|
|
53
|
+
/**
|
|
54
|
+
* Build index from loaded items
|
|
55
|
+
*/
|
|
56
|
+
constructor(tasks: LoadedTask[], items: LoadedSpecItem[]);
|
|
57
|
+
/**
|
|
58
|
+
* Resolve task spec_refs and build the bidirectional index.
|
|
59
|
+
* Must be called with a ReferenceIndex to resolve @refs.
|
|
60
|
+
*/
|
|
61
|
+
buildLinks(refIndex: ReferenceIndex): void;
|
|
62
|
+
/**
|
|
63
|
+
* Get tasks that implement a spec item
|
|
64
|
+
*/
|
|
65
|
+
getTasksForSpec(specUlid: string): LoadedTask[];
|
|
66
|
+
/**
|
|
67
|
+
* Get the spec item a task implements
|
|
68
|
+
*/
|
|
69
|
+
getSpecForTask(taskUlid: string, refIndex: ReferenceIndex): LoadedSpecItem | undefined;
|
|
70
|
+
/**
|
|
71
|
+
* Calculate expected implementation status based on linked task statuses
|
|
72
|
+
*/
|
|
73
|
+
calculateExpectedStatus(specUlid: string): ImplementationStatus;
|
|
74
|
+
/**
|
|
75
|
+
* Get implementation summary for a spec item
|
|
76
|
+
*/
|
|
77
|
+
getImplementationSummary(specUlid: string): SpecImplementationSummary | undefined;
|
|
78
|
+
/**
|
|
79
|
+
* Find all alignment issues
|
|
80
|
+
*/
|
|
81
|
+
findAlignmentWarnings(): AlignmentWarning[];
|
|
82
|
+
/**
|
|
83
|
+
* Get all spec items with their implementation summary
|
|
84
|
+
*/
|
|
85
|
+
getAllImplementationSummaries(): SpecImplementationSummary[];
|
|
86
|
+
/**
|
|
87
|
+
* Get stats about alignment
|
|
88
|
+
*/
|
|
89
|
+
getStats(): {
|
|
90
|
+
totalSpecs: number;
|
|
91
|
+
specsWithTasks: number;
|
|
92
|
+
alignedSpecs: number;
|
|
93
|
+
orphanedSpecs: number;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Result of syncing spec implementation status
|
|
98
|
+
*/
|
|
99
|
+
export interface SyncResult {
|
|
100
|
+
synced: boolean;
|
|
101
|
+
specUlid: string;
|
|
102
|
+
specTitle: string;
|
|
103
|
+
previousStatus: ImplementationStatus;
|
|
104
|
+
newStatus: ImplementationStatus;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Sync a spec item's implementation status based on its linked tasks.
|
|
108
|
+
* Called after task state changes (start, complete, etc.).
|
|
109
|
+
*
|
|
110
|
+
* @returns SyncResult if status changed, null if no change needed or no spec_ref
|
|
111
|
+
*/
|
|
112
|
+
export declare function syncSpecImplementationStatus(ctx: KspecContext, task: LoadedTask, allTasks: LoadedTask[], allItems: LoadedSpecItem[], refIndex: ReferenceIndex): Promise<SyncResult | null>;
|
|
113
|
+
//# sourceMappingURL=alignment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alignment.d.ts","sourceRoot":"","sources":["../../src/parser/alignment.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAChD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAM/D;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,oBAAoB,CAAC;IACpC,cAAc,EAAE,oBAAoB,CAAC;IACrC,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,eAAe,GAAG,iBAAiB,GAAG,sBAAsB,CAAC;IACnE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD;;;GAGG;AACH,qBAAa,cAAc;IACzB,+CAA+C;IAC/C,OAAO,CAAC,WAAW,CAA+B;IAElD,0CAA0C;IAC1C,OAAO,CAAC,UAAU,CAA6B;IAE/C,6BAA6B;IAC7B,OAAO,CAAC,SAAS,CAAqC;IAEtD,wBAAwB;IACxB,OAAO,CAAC,KAAK,CAAiC;IAE9C;;OAEG;gBACS,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE;IAkBxD;;;OAGG;IACH,UAAU,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI;IAa1C;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,EAAE;IAO/C;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,cAAc,GAAG,SAAS;IAWtF;;OAEG;IACH,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB;IA4B/D;;OAEG;IACH,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,yBAAyB,GAAG,SAAS;IA4BjF;;OAEG;IACH,qBAAqB,IAAI,gBAAgB,EAAE;IA4C3C;;OAEG;IACH,6BAA6B,IAAI,yBAAyB,EAAE;IAW5D;;OAEG;IACH,QAAQ,IAAI;QACV,UAAU,EAAE,MAAM,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;KACvB;CAyBF;AAMD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,oBAAoB,CAAC;IACrC,SAAS,EAAE,oBAAoB,CAAC;CACjC;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAChD,GAAG,EAAE,YAAY,EACjB,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,UAAU,EAAE,EACtB,QAAQ,EAAE,cAAc,EAAE,EAC1B,QAAQ,EAAE,cAAc,GACvB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CA6C5B"}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alignment tracking between specs and tasks.
|
|
3
|
+
*
|
|
4
|
+
* Provides bidirectional mapping from spec items to implementing tasks,
|
|
5
|
+
* and detects alignment issues like orphaned specs or stale implementation status.
|
|
6
|
+
*/
|
|
7
|
+
import { updateSpecItem } from './yaml.js';
|
|
8
|
+
// ============================================================
|
|
9
|
+
// ALIGNMENT INDEX
|
|
10
|
+
// ============================================================
|
|
11
|
+
/**
|
|
12
|
+
* Index for tracking spec-task alignment.
|
|
13
|
+
* Build once when loading, then query for alignment issues.
|
|
14
|
+
*/
|
|
15
|
+
export class AlignmentIndex {
|
|
16
|
+
/** spec ULID → task ULIDs that reference it */
|
|
17
|
+
specToTasks = new Map();
|
|
18
|
+
/** task ULID → spec ULID it references */
|
|
19
|
+
taskToSpec = new Map();
|
|
20
|
+
/** All spec items by ULID */
|
|
21
|
+
specItems = new Map();
|
|
22
|
+
/** All tasks by ULID */
|
|
23
|
+
tasks = new Map();
|
|
24
|
+
/**
|
|
25
|
+
* Build index from loaded items
|
|
26
|
+
*/
|
|
27
|
+
constructor(tasks, items) {
|
|
28
|
+
// Index spec items
|
|
29
|
+
for (const item of items) {
|
|
30
|
+
this.specItems.set(item._ulid, item);
|
|
31
|
+
this.specToTasks.set(item._ulid, []);
|
|
32
|
+
}
|
|
33
|
+
// Index tasks and build reverse mapping
|
|
34
|
+
for (const task of tasks) {
|
|
35
|
+
this.tasks.set(task._ulid, task);
|
|
36
|
+
if (task.spec_ref) {
|
|
37
|
+
// Store the raw ref - we'll resolve it when needed
|
|
38
|
+
this.taskToSpec.set(task._ulid, task.spec_ref);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve task spec_refs and build the bidirectional index.
|
|
44
|
+
* Must be called with a ReferenceIndex to resolve @refs.
|
|
45
|
+
*/
|
|
46
|
+
buildLinks(refIndex) {
|
|
47
|
+
for (const [taskUlid, specRef] of this.taskToSpec) {
|
|
48
|
+
const result = refIndex.resolve(specRef);
|
|
49
|
+
if (result.ok) {
|
|
50
|
+
const specUlid = result.ulid;
|
|
51
|
+
const existing = this.specToTasks.get(specUlid);
|
|
52
|
+
if (existing) {
|
|
53
|
+
existing.push(taskUlid);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get tasks that implement a spec item
|
|
60
|
+
*/
|
|
61
|
+
getTasksForSpec(specUlid) {
|
|
62
|
+
const taskUlids = this.specToTasks.get(specUlid) || [];
|
|
63
|
+
return taskUlids
|
|
64
|
+
.map(ulid => this.tasks.get(ulid))
|
|
65
|
+
.filter((t) => t !== undefined);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get the spec item a task implements
|
|
69
|
+
*/
|
|
70
|
+
getSpecForTask(taskUlid, refIndex) {
|
|
71
|
+
const specRef = this.taskToSpec.get(taskUlid);
|
|
72
|
+
if (!specRef)
|
|
73
|
+
return undefined;
|
|
74
|
+
const result = refIndex.resolve(specRef);
|
|
75
|
+
if (result.ok) {
|
|
76
|
+
return this.specItems.get(result.ulid);
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Calculate expected implementation status based on linked task statuses
|
|
82
|
+
*/
|
|
83
|
+
calculateExpectedStatus(specUlid) {
|
|
84
|
+
const taskUlids = this.specToTasks.get(specUlid) || [];
|
|
85
|
+
if (taskUlids.length === 0) {
|
|
86
|
+
return 'not_started';
|
|
87
|
+
}
|
|
88
|
+
const tasks = taskUlids
|
|
89
|
+
.map(ulid => this.tasks.get(ulid))
|
|
90
|
+
.filter((t) => t !== undefined);
|
|
91
|
+
if (tasks.length === 0) {
|
|
92
|
+
return 'not_started';
|
|
93
|
+
}
|
|
94
|
+
// Check task statuses
|
|
95
|
+
const hasInProgress = tasks.some(t => t.status === 'in_progress');
|
|
96
|
+
const allCompleted = tasks.every(t => t.status === 'completed');
|
|
97
|
+
const someCompleted = tasks.some(t => t.status === 'completed');
|
|
98
|
+
if (allCompleted) {
|
|
99
|
+
return 'implemented';
|
|
100
|
+
}
|
|
101
|
+
if (hasInProgress || someCompleted) {
|
|
102
|
+
return 'in_progress';
|
|
103
|
+
}
|
|
104
|
+
return 'not_started';
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get implementation summary for a spec item
|
|
108
|
+
*/
|
|
109
|
+
getImplementationSummary(specUlid) {
|
|
110
|
+
const spec = this.specItems.get(specUlid);
|
|
111
|
+
if (!spec)
|
|
112
|
+
return undefined;
|
|
113
|
+
const taskUlids = this.specToTasks.get(specUlid) || [];
|
|
114
|
+
const linkedTasks = taskUlids
|
|
115
|
+
.map(ulid => this.tasks.get(ulid))
|
|
116
|
+
.filter((t) => t !== undefined)
|
|
117
|
+
.map(t => ({
|
|
118
|
+
taskUlid: t._ulid,
|
|
119
|
+
taskTitle: t.title,
|
|
120
|
+
taskStatus: t.status,
|
|
121
|
+
hasNotes: t.notes.length > 0,
|
|
122
|
+
}));
|
|
123
|
+
const currentStatus = spec.status?.implementation || 'not_started';
|
|
124
|
+
const expectedStatus = this.calculateExpectedStatus(specUlid);
|
|
125
|
+
return {
|
|
126
|
+
specUlid,
|
|
127
|
+
specTitle: spec.title,
|
|
128
|
+
currentStatus,
|
|
129
|
+
expectedStatus,
|
|
130
|
+
linkedTasks,
|
|
131
|
+
isAligned: currentStatus === expectedStatus,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Find all alignment issues
|
|
136
|
+
*/
|
|
137
|
+
findAlignmentWarnings() {
|
|
138
|
+
const warnings = [];
|
|
139
|
+
// Check each spec item
|
|
140
|
+
for (const [specUlid, spec] of this.specItems) {
|
|
141
|
+
const taskUlids = this.specToTasks.get(specUlid) || [];
|
|
142
|
+
const currentStatus = spec.status?.implementation || 'not_started';
|
|
143
|
+
const expectedStatus = this.calculateExpectedStatus(specUlid);
|
|
144
|
+
// Orphaned spec (no tasks)
|
|
145
|
+
if (taskUlids.length === 0 && currentStatus === 'not_started') {
|
|
146
|
+
warnings.push({
|
|
147
|
+
type: 'orphaned_spec',
|
|
148
|
+
specUlid,
|
|
149
|
+
specTitle: spec.title,
|
|
150
|
+
message: `Spec item "${spec.title}" has no implementing tasks`,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// Status mismatch
|
|
154
|
+
if (currentStatus !== expectedStatus) {
|
|
155
|
+
warnings.push({
|
|
156
|
+
type: 'status_mismatch',
|
|
157
|
+
specUlid,
|
|
158
|
+
specTitle: spec.title,
|
|
159
|
+
message: `Spec "${spec.title}" status is "${currentStatus}" but should be "${expectedStatus}" based on task progress`,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Check completed tasks with stale spec status
|
|
164
|
+
for (const [taskUlid, task] of this.tasks) {
|
|
165
|
+
if (task.status === 'completed' && task.spec_ref) {
|
|
166
|
+
const specRef = this.taskToSpec.get(taskUlid);
|
|
167
|
+
if (specRef) {
|
|
168
|
+
// Note: We already checked this via spec iteration above
|
|
169
|
+
// But this provides task-centric context
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return warnings;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Get all spec items with their implementation summary
|
|
177
|
+
*/
|
|
178
|
+
getAllImplementationSummaries() {
|
|
179
|
+
const summaries = [];
|
|
180
|
+
for (const specUlid of this.specItems.keys()) {
|
|
181
|
+
const summary = this.getImplementationSummary(specUlid);
|
|
182
|
+
if (summary) {
|
|
183
|
+
summaries.push(summary);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return summaries;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get stats about alignment
|
|
190
|
+
*/
|
|
191
|
+
getStats() {
|
|
192
|
+
let specsWithTasks = 0;
|
|
193
|
+
let alignedSpecs = 0;
|
|
194
|
+
let orphanedSpecs = 0;
|
|
195
|
+
for (const specUlid of this.specItems.keys()) {
|
|
196
|
+
const taskUlids = this.specToTasks.get(specUlid) || [];
|
|
197
|
+
if (taskUlids.length > 0) {
|
|
198
|
+
specsWithTasks++;
|
|
199
|
+
const summary = this.getImplementationSummary(specUlid);
|
|
200
|
+
if (summary?.isAligned) {
|
|
201
|
+
alignedSpecs++;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
orphanedSpecs++;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
totalSpecs: this.specItems.size,
|
|
210
|
+
specsWithTasks,
|
|
211
|
+
alignedSpecs,
|
|
212
|
+
orphanedSpecs,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Sync a spec item's implementation status based on its linked tasks.
|
|
218
|
+
* Called after task state changes (start, complete, etc.).
|
|
219
|
+
*
|
|
220
|
+
* @returns SyncResult if status changed, null if no change needed or no spec_ref
|
|
221
|
+
*/
|
|
222
|
+
export async function syncSpecImplementationStatus(ctx, task, allTasks, allItems, refIndex) {
|
|
223
|
+
// Skip if task has no spec_ref
|
|
224
|
+
if (!task.spec_ref) {
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
// Resolve the spec reference
|
|
228
|
+
const result = refIndex.resolve(task.spec_ref);
|
|
229
|
+
if (!result.ok) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
// Find the spec item
|
|
233
|
+
const specItem = allItems.find(item => item._ulid === result.ulid);
|
|
234
|
+
if (!specItem) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
// Build alignment index to calculate expected status
|
|
238
|
+
const alignmentIndex = new AlignmentIndex(allTasks, allItems);
|
|
239
|
+
alignmentIndex.buildLinks(refIndex);
|
|
240
|
+
const expectedStatus = alignmentIndex.calculateExpectedStatus(specItem._ulid);
|
|
241
|
+
const currentStatus = specItem.status?.implementation || 'not_started';
|
|
242
|
+
// No change needed
|
|
243
|
+
if (currentStatus === expectedStatus) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
// Update the spec item
|
|
247
|
+
await updateSpecItem(ctx, specItem, {
|
|
248
|
+
status: {
|
|
249
|
+
maturity: specItem.status?.maturity || 'draft',
|
|
250
|
+
implementation: expectedStatus,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
return {
|
|
254
|
+
synced: true,
|
|
255
|
+
specUlid: specItem._ulid,
|
|
256
|
+
specTitle: specItem.title,
|
|
257
|
+
previousStatus: currentStatus,
|
|
258
|
+
newStatus: expectedStatus,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
//# sourceMappingURL=alignment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alignment.js","sourceRoot":"","sources":["../../src/parser/alignment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task automation assessment logic.
|
|
3
|
+
*
|
|
4
|
+
* Provides criteria checking for task automation eligibility.
|
|
5
|
+
* AC: @tasks-assess-automation
|
|
6
|
+
*/
|
|
7
|
+
import type { LoadedTask, LoadedSpecItem } from './yaml.js';
|
|
8
|
+
import type { ReferenceIndex } from './refs.js';
|
|
9
|
+
/**
|
|
10
|
+
* Criterion check result
|
|
11
|
+
*/
|
|
12
|
+
export interface CriterionResult {
|
|
13
|
+
pass: boolean;
|
|
14
|
+
/** For skipped criteria (neutral) */
|
|
15
|
+
skipped?: boolean;
|
|
16
|
+
/** Additional context */
|
|
17
|
+
detail?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Full assessment result for a task
|
|
21
|
+
* AC: @tasks-assess-automation ac-3, ac-4
|
|
22
|
+
*/
|
|
23
|
+
export interface TaskAssessment {
|
|
24
|
+
taskRef: string;
|
|
25
|
+
taskUlid: string;
|
|
26
|
+
taskTitle: string;
|
|
27
|
+
taskType: string;
|
|
28
|
+
criteria: {
|
|
29
|
+
has_spec_ref: CriterionResult & {
|
|
30
|
+
spec_ref?: string;
|
|
31
|
+
};
|
|
32
|
+
spec_has_acs: CriterionResult & {
|
|
33
|
+
ac_count?: number;
|
|
34
|
+
};
|
|
35
|
+
not_spike: CriterionResult;
|
|
36
|
+
};
|
|
37
|
+
recommendation: 'review_for_eligible' | 'needs_review' | 'manual_only';
|
|
38
|
+
reason: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Assessment summary counts
|
|
42
|
+
* AC: @tasks-assess-automation ac-5, ac-25
|
|
43
|
+
*/
|
|
44
|
+
export interface AssessmentSummary {
|
|
45
|
+
review_for_eligible: number;
|
|
46
|
+
needs_review: number;
|
|
47
|
+
manual_only: number;
|
|
48
|
+
total: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Assess a single task's automation eligibility
|
|
52
|
+
* AC: @tasks-assess-automation ac-8 through ac-16
|
|
53
|
+
*/
|
|
54
|
+
export declare function assessTask(task: LoadedTask, index: ReferenceIndex, items: LoadedSpecItem[]): TaskAssessment;
|
|
55
|
+
/**
|
|
56
|
+
* Filter tasks for assessment
|
|
57
|
+
* AC: @tasks-assess-automation ac-1, ac-2, ac-27, ac-28
|
|
58
|
+
*/
|
|
59
|
+
export declare function filterTasksForAssessment(tasks: LoadedTask[], options: {
|
|
60
|
+
all?: boolean;
|
|
61
|
+
taskRef?: string;
|
|
62
|
+
}, index: ReferenceIndex): LoadedTask[];
|
|
63
|
+
/**
|
|
64
|
+
* Compute summary counts from assessments
|
|
65
|
+
* AC: @tasks-assess-automation ac-5, ac-25
|
|
66
|
+
*/
|
|
67
|
+
export declare function computeSummary(assessments: TaskAssessment[]): AssessmentSummary;
|
|
68
|
+
/**
|
|
69
|
+
* Determine what changes auto mode would make
|
|
70
|
+
* AC: @tasks-assess-automation ac-17, ac-18, ac-21
|
|
71
|
+
*/
|
|
72
|
+
export interface AutoModeChange {
|
|
73
|
+
taskRef: string;
|
|
74
|
+
taskUlid: string;
|
|
75
|
+
taskTitle: string;
|
|
76
|
+
action: 'set_manual_only' | 'set_needs_review' | 'no_change';
|
|
77
|
+
newStatus?: 'manual_only' | 'needs_review';
|
|
78
|
+
reason: string;
|
|
79
|
+
}
|
|
80
|
+
export declare function computeAutoModeChanges(assessments: TaskAssessment[]): AutoModeChange[];
|
|
81
|
+
//# sourceMappingURL=assess.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assess.d.ts","sourceRoot":"","sources":["../../src/parser/assess.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAMhD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,qCAAqC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE;QACR,YAAY,EAAE,eAAe,GAAG;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,YAAY,EAAE,eAAe,GAAG;YAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACtD,SAAS,EAAE,eAAe,CAAC;KAC5B,CAAC;IACF,cAAc,EAAE,qBAAqB,GAAG,cAAc,GAAG,aAAa,CAAC;IACvE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACf;AAMD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,cAAc,EAAE,GACtB,cAAc,CAmChB;AA8GD;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAC5C,KAAK,EAAE,cAAc,GACpB,UAAU,EAAE,CAsBd;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,iBAAiB,CAa/E;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,iBAAiB,GAAG,kBAAkB,GAAG,WAAW,CAAC;IAC7D,SAAS,CAAC,EAAE,aAAa,GAAG,cAAc,CAAC;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAmCtF"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task automation assessment logic.
|
|
3
|
+
*
|
|
4
|
+
* Provides criteria checking for task automation eligibility.
|
|
5
|
+
* AC: @tasks-assess-automation
|
|
6
|
+
*/
|
|
7
|
+
// ============================================================
|
|
8
|
+
// ASSESSMENT LOGIC
|
|
9
|
+
// ============================================================
|
|
10
|
+
/**
|
|
11
|
+
* Assess a single task's automation eligibility
|
|
12
|
+
* AC: @tasks-assess-automation ac-8 through ac-16
|
|
13
|
+
*/
|
|
14
|
+
export function assessTask(task, index, items) {
|
|
15
|
+
const taskRef = task.slugs.length > 0 ? `@${task.slugs[0]}` : `@${task._ulid.slice(0, 8)}`;
|
|
16
|
+
const taskType = task.type || 'task';
|
|
17
|
+
// AC: @tasks-assess-automation ac-8, ac-9 - Check has_spec_ref
|
|
18
|
+
const hasSpecRefResult = checkHasSpecRef(task, index);
|
|
19
|
+
// AC: @tasks-assess-automation ac-10, ac-11 - Check spec_has_acs
|
|
20
|
+
const specHasAcsResult = checkSpecHasAcs(task, index, items);
|
|
21
|
+
// AC: @tasks-assess-automation ac-12, ac-13 - Check not_spike
|
|
22
|
+
const notSpikeResult = checkNotSpike(task);
|
|
23
|
+
// Compute recommendation
|
|
24
|
+
// AC: @tasks-assess-automation ac-14, ac-15, ac-16
|
|
25
|
+
const { recommendation, reason } = computeRecommendation(hasSpecRefResult, specHasAcsResult, notSpikeResult, taskType);
|
|
26
|
+
return {
|
|
27
|
+
taskRef,
|
|
28
|
+
taskUlid: task._ulid,
|
|
29
|
+
taskTitle: task.title,
|
|
30
|
+
taskType,
|
|
31
|
+
criteria: {
|
|
32
|
+
has_spec_ref: hasSpecRefResult,
|
|
33
|
+
spec_has_acs: specHasAcsResult,
|
|
34
|
+
not_spike: notSpikeResult,
|
|
35
|
+
},
|
|
36
|
+
recommendation,
|
|
37
|
+
reason,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Check if task has spec_ref pointing to resolvable spec
|
|
42
|
+
* AC: @tasks-assess-automation ac-8, ac-9
|
|
43
|
+
*/
|
|
44
|
+
function checkHasSpecRef(task, index) {
|
|
45
|
+
if (!task.spec_ref) {
|
|
46
|
+
return { pass: false, detail: 'missing' };
|
|
47
|
+
}
|
|
48
|
+
const resolved = index.resolve(task.spec_ref);
|
|
49
|
+
if (!resolved.ok) {
|
|
50
|
+
return { pass: false, detail: 'unresolvable', spec_ref: task.spec_ref };
|
|
51
|
+
}
|
|
52
|
+
return { pass: true, spec_ref: task.spec_ref };
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check if linked spec has acceptance criteria
|
|
56
|
+
* AC: @tasks-assess-automation ac-10, ac-11
|
|
57
|
+
*/
|
|
58
|
+
function checkSpecHasAcs(task, index, items) {
|
|
59
|
+
// AC: @tasks-assess-automation ac-11 - skipped if no spec_ref
|
|
60
|
+
if (!task.spec_ref) {
|
|
61
|
+
return { pass: false, skipped: true, detail: 'no spec to check' };
|
|
62
|
+
}
|
|
63
|
+
const resolved = index.resolve(task.spec_ref);
|
|
64
|
+
if (!resolved.ok) {
|
|
65
|
+
return { pass: false, skipped: true, detail: 'spec not resolvable' };
|
|
66
|
+
}
|
|
67
|
+
// Find the spec item
|
|
68
|
+
const specItem = items.find(i => i._ulid === resolved.ulid);
|
|
69
|
+
if (!specItem) {
|
|
70
|
+
return { pass: false, skipped: true, detail: 'spec not found in items' };
|
|
71
|
+
}
|
|
72
|
+
const acCount = specItem.acceptance_criteria?.length || 0;
|
|
73
|
+
if (acCount === 0) {
|
|
74
|
+
return { pass: false, ac_count: 0, detail: 'spec has no acceptance criteria' };
|
|
75
|
+
}
|
|
76
|
+
return { pass: true, ac_count: acCount };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Check if task type is not spike
|
|
80
|
+
* AC: @tasks-assess-automation ac-12, ac-13
|
|
81
|
+
*/
|
|
82
|
+
function checkNotSpike(task) {
|
|
83
|
+
const taskType = task.type || 'task';
|
|
84
|
+
if (taskType === 'spike') {
|
|
85
|
+
return { pass: false, detail: 'type: spike' };
|
|
86
|
+
}
|
|
87
|
+
return { pass: true, detail: `type: ${taskType}` };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Compute recommendation based on criteria results
|
|
91
|
+
* AC: @tasks-assess-automation ac-14, ac-15, ac-16
|
|
92
|
+
*/
|
|
93
|
+
function computeRecommendation(hasSpecRef, specHasAcs, notSpike, taskType) {
|
|
94
|
+
// AC: @tasks-assess-automation ac-14 - Spikes are always manual_only
|
|
95
|
+
if (!notSpike.pass) {
|
|
96
|
+
return {
|
|
97
|
+
recommendation: 'manual_only',
|
|
98
|
+
reason: 'Spikes output knowledge, not automatable code',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// AC: @tasks-assess-automation ac-15 - Missing spec or no ACs → needs_review
|
|
102
|
+
const reasons = [];
|
|
103
|
+
if (!hasSpecRef.pass) {
|
|
104
|
+
reasons.push('missing spec_ref');
|
|
105
|
+
}
|
|
106
|
+
if (!specHasAcs.pass && !specHasAcs.skipped) {
|
|
107
|
+
reasons.push('spec has no acceptance criteria');
|
|
108
|
+
}
|
|
109
|
+
else if (specHasAcs.skipped && !hasSpecRef.pass) {
|
|
110
|
+
// Only add this if spec is missing (not if spec is unresolvable)
|
|
111
|
+
}
|
|
112
|
+
if (reasons.length > 0) {
|
|
113
|
+
return {
|
|
114
|
+
recommendation: 'needs_review',
|
|
115
|
+
reason: reasons.join(', '),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// AC: @tasks-assess-automation ac-16 - All criteria pass → review_for_eligible
|
|
119
|
+
return {
|
|
120
|
+
recommendation: 'review_for_eligible',
|
|
121
|
+
reason: 'Criteria pass - verify spec is appropriate and ACs are adequate',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Filter tasks for assessment
|
|
126
|
+
* AC: @tasks-assess-automation ac-1, ac-2, ac-27, ac-28
|
|
127
|
+
*/
|
|
128
|
+
export function filterTasksForAssessment(tasks, options, index) {
|
|
129
|
+
let filtered = tasks;
|
|
130
|
+
// AC: @tasks-assess-automation ac-28 - Exclude non-pending tasks
|
|
131
|
+
filtered = filtered.filter(t => t.status === 'pending');
|
|
132
|
+
// AC: @tasks-assess-automation ac-6 - Single task assessment
|
|
133
|
+
if (options.taskRef) {
|
|
134
|
+
const resolved = index.resolve(options.taskRef);
|
|
135
|
+
if (!resolved.ok) {
|
|
136
|
+
return []; // Will be handled by caller
|
|
137
|
+
}
|
|
138
|
+
filtered = filtered.filter(t => t._ulid === resolved.ulid);
|
|
139
|
+
return filtered;
|
|
140
|
+
}
|
|
141
|
+
// AC: @tasks-assess-automation ac-1, ac-27 - Filter by unassessed unless --all
|
|
142
|
+
if (!options.all) {
|
|
143
|
+
filtered = filtered.filter(t => !t.automation);
|
|
144
|
+
}
|
|
145
|
+
return filtered;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Compute summary counts from assessments
|
|
149
|
+
* AC: @tasks-assess-automation ac-5, ac-25
|
|
150
|
+
*/
|
|
151
|
+
export function computeSummary(assessments) {
|
|
152
|
+
const summary = {
|
|
153
|
+
review_for_eligible: 0,
|
|
154
|
+
needs_review: 0,
|
|
155
|
+
manual_only: 0,
|
|
156
|
+
total: assessments.length,
|
|
157
|
+
};
|
|
158
|
+
for (const assessment of assessments) {
|
|
159
|
+
summary[assessment.recommendation]++;
|
|
160
|
+
}
|
|
161
|
+
return summary;
|
|
162
|
+
}
|
|
163
|
+
export function computeAutoModeChanges(assessments) {
|
|
164
|
+
return assessments.map(assessment => {
|
|
165
|
+
// AC: @tasks-assess-automation ac-17 - Spikes → manual_only
|
|
166
|
+
if (assessment.recommendation === 'manual_only') {
|
|
167
|
+
return {
|
|
168
|
+
taskRef: assessment.taskRef,
|
|
169
|
+
taskUlid: assessment.taskUlid,
|
|
170
|
+
taskTitle: assessment.taskTitle,
|
|
171
|
+
action: 'set_manual_only',
|
|
172
|
+
newStatus: 'manual_only',
|
|
173
|
+
reason: assessment.reason,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// AC: @tasks-assess-automation ac-17 - Missing criteria → needs_review
|
|
177
|
+
if (assessment.recommendation === 'needs_review') {
|
|
178
|
+
return {
|
|
179
|
+
taskRef: assessment.taskRef,
|
|
180
|
+
taskUlid: assessment.taskUlid,
|
|
181
|
+
taskTitle: assessment.taskTitle,
|
|
182
|
+
action: 'set_needs_review',
|
|
183
|
+
newStatus: 'needs_review',
|
|
184
|
+
reason: assessment.reason,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
// AC: @tasks-assess-automation ac-18, ac-21 - review_for_eligible → no change
|
|
188
|
+
return {
|
|
189
|
+
taskRef: assessment.taskRef,
|
|
190
|
+
taskUlid: assessment.taskUlid,
|
|
191
|
+
taskTitle: assessment.taskTitle,
|
|
192
|
+
action: 'no_change',
|
|
193
|
+
reason: 'Passes criteria - requires agent/human review to mark eligible',
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=assess.js.map
|