@danypops/papyrus 0.11.3 → 0.12.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 +16 -2
- package/extension/src/active-task-continuation.ts +6 -0
- package/extension/src/domain-tools.ts +108 -52
- package/extension/src/index.ts +90 -37
- package/extension/src/notes.ts +14 -1
- package/extension/src/task-focus-events.ts +57 -0
- package/extension/src/tasks.ts +51 -15
- package/extension/src/tool-rendering/artifact-card.ts +117 -0
- package/extension/src/tool-rendering/artifact-list.ts +179 -0
- package/extension/src/tool-rendering/index.ts +107 -0
- package/extension/src/tool-rendering/render-model.ts +406 -0
- package/package.json +4 -2
- package/src/adapters/in-memory-conversation-journal-store.ts +48 -0
- package/src/adapters/sqlite-artifact-scope-store.ts +36 -0
- package/src/adapters/sqlite-artifact-store.ts +20 -11
- package/src/adapters/sqlite-discourse-store.ts +325 -0
- package/src/adapters/sqlite-graph-projection-store.ts +41 -0
- package/src/adapters/sqlite-task-focus-store.ts +34 -15
- package/src/authority-registry.ts +115 -0
- package/src/cli.ts +904 -124
- package/src/constants.ts +38 -5
- package/src/conversation-journal-service.ts +87 -0
- package/src/db.ts +336 -8
- package/src/domain/artifact-event.ts +99 -0
- package/src/domain/conversation-journal.ts +168 -0
- package/src/domain/discourse-store.ts +142 -0
- package/src/domain/graph-projection.ts +74 -0
- package/src/domain/task-event.ts +4 -0
- package/src/domain-services.ts +133 -38
- package/src/graph-projection-service.ts +103 -0
- package/src/id-migration.ts +200 -0
- package/src/module-registry.ts +53 -0
- package/src/modules/docs.ts +77 -0
- package/src/modules/graph-projection.ts +82 -0
- package/src/modules/notes.ts +76 -0
- package/src/modules/rules.ts +81 -0
- package/src/modules/skills.ts +113 -0
- package/src/modules/tasks.ts +164 -0
- package/src/ops.ts +142 -15
- package/src/ports/artifact-scope-store.ts +20 -0
- package/src/ports/artifact-store.ts +10 -5
- package/src/ports/conversation-journal-store.ts +17 -0
- package/src/ports/graph-projection-store.ts +15 -0
- package/src/ports/task-focus-store.ts +62 -20
- package/src/service.ts +218 -223
- package/src/task-service.ts +70 -38
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* module-registry.ts — Branch-by-Abstraction step 1 of the modules/projections refactor
|
|
3
|
+
* (see docs reducing-papyrus-consumer-change-amplification-with-modules--pvdo and
|
|
4
|
+
* papyrus-full-context-mesh-and-domain-storage-ownership-bound-qhzp).
|
|
5
|
+
*
|
|
6
|
+
* A statically registered operation descriptor replaces one entry of the central
|
|
7
|
+
* operation switch in src/service.ts. This is intentionally minimal for this slice:
|
|
8
|
+
* one registry, one contract, no dynamic loading, no migration/authority/CLI descriptors
|
|
9
|
+
* yet — those are separate follow-up steps. The goal is to prove the shape end-to-end
|
|
10
|
+
* for a real module (Notes) without changing any observable behavior.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface OperationDefinition<Input = unknown, Output = unknown> {
|
|
14
|
+
/** Dotted operation name, e.g. "notes.capture". Must be unique across every registered module. */
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/** Owning module id, e.g. "notes". Used for boot diagnostics and future authority/migration scoping. */
|
|
17
|
+
readonly moduleId: string;
|
|
18
|
+
execute(input: Input): Output | Promise<Output>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* O(1) name -> descriptor lookup. Boot-time registration is O(N) and rejects duplicate
|
|
23
|
+
* names immediately rather than silently letting the last registration win, so a module
|
|
24
|
+
* collision fails fast instead of producing quiet cross-module dispatch bugs.
|
|
25
|
+
*/
|
|
26
|
+
export class OperationRegistry {
|
|
27
|
+
private readonly operations = new Map<string, OperationDefinition>();
|
|
28
|
+
|
|
29
|
+
register(operation: OperationDefinition): void {
|
|
30
|
+
const existing = this.operations.get(operation.name);
|
|
31
|
+
if (existing) {
|
|
32
|
+
throw new Error(`operation "${operation.name}" is already registered by module "${existing.moduleId}"`);
|
|
33
|
+
}
|
|
34
|
+
this.operations.set(operation.name, operation);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
registerAll(operations: readonly OperationDefinition[]): void {
|
|
38
|
+
for (const operation of operations) this.register(operation);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
get(name: string): OperationDefinition | undefined {
|
|
42
|
+
return this.operations.get(name);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
has(name: string): boolean {
|
|
46
|
+
return this.operations.has(name);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Bounded by registration count, not a runtime query — safe to call freely for diagnostics/CLI listing. */
|
|
50
|
+
list(): string[] {
|
|
51
|
+
return [...this.operations.keys()].sort();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modules/docs.ts — Docs as a Papyrus-native registered module
|
|
3
|
+
* (step 5, continued, of the incremental refactor in
|
|
4
|
+
* reducing-papyrus-consumer-change-amplification-with-modules--pvdo).
|
|
5
|
+
*
|
|
6
|
+
* Imports only src/domain-services.ts's Doc functions, which are already generic
|
|
7
|
+
* ArtifactStore-based with no other module's concrete class dependency.
|
|
8
|
+
*/
|
|
9
|
+
import type { AuthorityRegistry } from "../authority-registry.ts";
|
|
10
|
+
import { assignDocumentProject, createDocument, linkDocument, listDocuments, showDocument, transitionDocument, type DocumentRelation } from "../domain-services.ts";
|
|
11
|
+
import type { OperationDefinition } from "../module-registry.ts";
|
|
12
|
+
import type { ArtifactScopeStore } from "../ports/artifact-scope-store.ts";
|
|
13
|
+
import type { ArtifactStore } from "../ports/artifact-store.ts";
|
|
14
|
+
|
|
15
|
+
const MODULE_ID = "docs";
|
|
16
|
+
|
|
17
|
+
type OperationInput = Record<string, unknown>;
|
|
18
|
+
|
|
19
|
+
function string(input: OperationInput, key: string): string {
|
|
20
|
+
const value = input[key];
|
|
21
|
+
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function optionalString(input: OperationInput, key: string): string | undefined {
|
|
26
|
+
const value = input[key];
|
|
27
|
+
if (value === undefined) return undefined;
|
|
28
|
+
if (typeof value !== "string") throw new Error(`${key} must be a string`);
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function optionalNumber(input: OperationInput, key: string): number | undefined {
|
|
33
|
+
const value = input[key];
|
|
34
|
+
if (value === undefined) return undefined;
|
|
35
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} must be a number`);
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const eventContext = (input: OperationInput) => ({
|
|
40
|
+
actor: optionalString(input, "actor"),
|
|
41
|
+
source: optionalString(input, "source"),
|
|
42
|
+
sessionId: optionalString(input, "session_id") ?? optionalString(input, "sessionId"),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const artifactFilter = (input: OperationInput) => ({
|
|
46
|
+
status: optionalString(input, "status"),
|
|
47
|
+
text: optionalString(input, "text"),
|
|
48
|
+
limit: optionalNumber(input, "limit"),
|
|
49
|
+
projectRoot: optionalString(input, "project_root"),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/** Registers every docs.* operation against the shared ArtifactStore port. Behavior is unchanged from the prior inline handlers in src/service.ts. */
|
|
53
|
+
/** This module's own operation names, the single source of truth src/service.ts's EXPECTED_OPERATION_NAMES spreads in rather than re-listing by hand. */
|
|
54
|
+
export const DOCS_OPERATION_NAMES = [
|
|
55
|
+
"docs.create", "docs.list", "docs.show", "docs.activate", "docs.archive", "docs.reopen", "docs.link", "docs.assign_project",
|
|
56
|
+
] as const;
|
|
57
|
+
|
|
58
|
+
export function docsOperations(artifacts: ArtifactStore, scopes: ArtifactScopeStore, authority: AuthorityRegistry): OperationDefinition[] {
|
|
59
|
+
const define = <Input, Output>(name: string, execute: (input: Input) => Output): OperationDefinition<Input, Output> => ({
|
|
60
|
+
name, moduleId: MODULE_ID, execute,
|
|
61
|
+
});
|
|
62
|
+
return [
|
|
63
|
+
define("docs.create", (input: OperationInput) => createDocument(artifacts, scopes, {
|
|
64
|
+
title: string(input, "title"), body: optionalString(input, "body"), subtype: optionalString(input, "subtype"),
|
|
65
|
+
labels: input["labels"] as string[] | undefined, extra: input["extra"] as Record<string, unknown> | undefined,
|
|
66
|
+
templateId: optionalString(input, "template_id") ?? optionalString(input, "templateId"),
|
|
67
|
+
projectRoot: optionalString(input, "project_root"),
|
|
68
|
+
}, authority, eventContext(input))),
|
|
69
|
+
define("docs.list", (input: OperationInput) => listDocuments(artifacts, scopes, artifactFilter(input))),
|
|
70
|
+
define("docs.show", (input: OperationInput) => showDocument(artifacts, string(input, "id"))),
|
|
71
|
+
define("docs.activate", (input: OperationInput) => transitionDocument(artifacts, string(input, "id"), "activate", authority, eventContext(input))),
|
|
72
|
+
define("docs.archive", (input: OperationInput) => transitionDocument(artifacts, string(input, "id"), "archive", authority, eventContext(input))),
|
|
73
|
+
define("docs.reopen", (input: OperationInput) => transitionDocument(artifacts, string(input, "id"), "reopen", authority, eventContext(input))),
|
|
74
|
+
define("docs.link", (input: OperationInput) => linkDocument(artifacts, string(input, "id"), string(input, "relation") as DocumentRelation, string(input, "target_id"), authority, eventContext(input))),
|
|
75
|
+
define("docs.assign_project", (input: OperationInput) => assignDocumentProject(artifacts, scopes, string(input, "id"), optionalString(input, "project_root"))),
|
|
76
|
+
];
|
|
77
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modules/graph-projection.ts — the generic graph projection protocol as a registered
|
|
3
|
+
* Papyrus-native module (step 6 of the incremental refactor in
|
|
4
|
+
* reducing-papyrus-consumer-change-amplification-with-modules--pvdo). See
|
|
5
|
+
* src/domain/graph-projection.ts and src/graph-projection-service.ts for the protocol
|
|
6
|
+
* itself; this file only parses/validates raw operation input into that typed shape.
|
|
7
|
+
*/
|
|
8
|
+
import type { AuthorityRegistry } from "../authority-registry.ts";
|
|
9
|
+
import { GRAPH_PROJECTION_SCHEMA_VERSION, type GraphProjectionBatch, type ProjectedArtifact, type ProjectedEdge } from "../domain/graph-projection.ts";
|
|
10
|
+
import { GraphProjection } from "../graph-projection-service.ts";
|
|
11
|
+
import type { OperationDefinition } from "../module-registry.ts";
|
|
12
|
+
import type { ArtifactStore } from "../ports/artifact-store.ts";
|
|
13
|
+
import type { GraphProjectionStore } from "../ports/graph-projection-store.ts";
|
|
14
|
+
|
|
15
|
+
const MODULE_ID = "graph_projection";
|
|
16
|
+
|
|
17
|
+
type OperationInput = Record<string, unknown>;
|
|
18
|
+
|
|
19
|
+
function string(input: OperationInput, key: string): string {
|
|
20
|
+
const value = input[key];
|
|
21
|
+
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function number(input: OperationInput, key: string): number {
|
|
26
|
+
const value = input[key];
|
|
27
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} is required and must be a number`);
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
32
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function parseArtifact(raw: unknown, index: number): ProjectedArtifact {
|
|
36
|
+
if (!isRecord(raw)) throw new Error(`artifacts[${index}] must be an object`);
|
|
37
|
+
return {
|
|
38
|
+
externalId: string(raw, "external_id"),
|
|
39
|
+
kind: string(raw, "kind"),
|
|
40
|
+
...(typeof raw["subtype"] === "string" ? { subtype: raw["subtype"] } : {}),
|
|
41
|
+
title: string(raw, "title"),
|
|
42
|
+
...(typeof raw["body"] === "string" ? { body: raw["body"] } : {}),
|
|
43
|
+
...(Array.isArray(raw["labels"]) ? { labels: raw["labels"] as string[] } : {}),
|
|
44
|
+
...(isRecord(raw["extra"]) ? { extra: raw["extra"] } : {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function parseEdge(raw: unknown, index: number): ProjectedEdge {
|
|
49
|
+
if (!isRecord(raw)) throw new Error(`edges[${index}] must be an object`);
|
|
50
|
+
return { from: string(raw, "from"), relation: string(raw, "relation"), to: string(raw, "to") };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parseBatch(input: OperationInput): GraphProjectionBatch {
|
|
54
|
+
const schemaVersion = string(input, "schema_version");
|
|
55
|
+
const rawArtifacts = input["artifacts"];
|
|
56
|
+
const rawEdges = input["edges"] ?? [];
|
|
57
|
+
if (!Array.isArray(rawArtifacts)) throw new Error("artifacts must be an array");
|
|
58
|
+
if (!Array.isArray(rawEdges)) throw new Error("edges must be an array");
|
|
59
|
+
return {
|
|
60
|
+
schemaVersion: schemaVersion as typeof GRAPH_PROJECTION_SCHEMA_VERSION,
|
|
61
|
+
producerId: string(input, "producer_id"),
|
|
62
|
+
batchId: string(input, "batch_id"),
|
|
63
|
+
sequence: number(input, "sequence"),
|
|
64
|
+
artifacts: rawArtifacts.map(parseArtifact),
|
|
65
|
+
edges: rawEdges.map(parseEdge),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Registers graph_projection.apply and graph_projection.checkpoint against one GraphProjection instance. */
|
|
70
|
+
/** This module's own operation names, the single source of truth src/service.ts's EXPECTED_OPERATION_NAMES spreads in rather than re-listing by hand. */
|
|
71
|
+
export const GRAPH_PROJECTION_OPERATION_NAMES = ["graph_projection.apply", "graph_projection.checkpoint"] as const;
|
|
72
|
+
|
|
73
|
+
export function graphProjectionOperations(artifacts: ArtifactStore, store: GraphProjectionStore, authority: AuthorityRegistry): OperationDefinition[] {
|
|
74
|
+
const projection = new GraphProjection(artifacts, store, authority);
|
|
75
|
+
const define = <Input, Output>(name: string, execute: (input: Input) => Output): OperationDefinition<Input, Output> => ({
|
|
76
|
+
name, moduleId: MODULE_ID, execute,
|
|
77
|
+
});
|
|
78
|
+
return [
|
|
79
|
+
define("graph_projection.apply", (input: OperationInput) => projection.apply(parseBatch(input))),
|
|
80
|
+
define("graph_projection.checkpoint", (input: OperationInput) => projection.checkpoint(string(input, "producer_id"))),
|
|
81
|
+
];
|
|
82
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modules/notes.ts — Notes as the first Papyrus-native registered module
|
|
3
|
+
* (step 5 of the incremental refactor in reducing-papyrus-consumer-change-amplification-with-modules--pvdo,
|
|
4
|
+
* combined with step 1 for a real proof-of-shape rather than an empty abstraction).
|
|
5
|
+
*
|
|
6
|
+
* Notes was chosen first because it owns no bespoke schema (it reuses the generic doc
|
|
7
|
+
* table via NOTE_SUBTYPE) and has exactly six operations — the smallest real module to
|
|
8
|
+
* prove the OperationRegistry shape against before extracting Tasks or Docs.
|
|
9
|
+
*
|
|
10
|
+
* This module does not import another module's infrastructure (src/task-service.ts,
|
|
11
|
+
* src/domain-services.ts, etc.) — only its own src/note-service.ts and the shared
|
|
12
|
+
* OperationInput parsing helpers, matching the "module code does not import another
|
|
13
|
+
* module's infrastructure" constraint.
|
|
14
|
+
*/
|
|
15
|
+
import type { OperationDefinition } from "../module-registry.ts";
|
|
16
|
+
import { Notes, type NoteDisposition } from "../note-service.ts";
|
|
17
|
+
|
|
18
|
+
const MODULE_ID = "notes";
|
|
19
|
+
|
|
20
|
+
type OperationInput = Record<string, unknown>;
|
|
21
|
+
|
|
22
|
+
function string(input: OperationInput, key: string): string {
|
|
23
|
+
const value = input[key];
|
|
24
|
+
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function optionalString(input: OperationInput, key: string): string | undefined {
|
|
29
|
+
const value = input[key];
|
|
30
|
+
if (value === undefined) return undefined;
|
|
31
|
+
if (typeof value !== "string") throw new Error(`${key} must be a string`);
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function optionalNumber(input: OperationInput, key: string): number | undefined {
|
|
36
|
+
const value = input[key];
|
|
37
|
+
if (value === undefined) return undefined;
|
|
38
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} must be a number`);
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** This module's own operation names, the single source of truth src/service.ts's EXPECTED_OPERATION_NAMES spreads in rather than re-listing by hand. */
|
|
43
|
+
export const NOTES_OPERATION_NAMES = [
|
|
44
|
+
"notes.capture", "notes.list", "notes.show", "notes.consume", "notes.promote", "notes.archive",
|
|
45
|
+
] as const;
|
|
46
|
+
|
|
47
|
+
/** Registers every notes.* operation against one Notes instance. Behavior is unchanged from the prior inline handlers in src/service.ts. */
|
|
48
|
+
export function notesOperations(notes: Notes): OperationDefinition[] {
|
|
49
|
+
const define = <Input, Output>(name: string, execute: (input: Input) => Output): OperationDefinition<Input, Output> => ({
|
|
50
|
+
name, moduleId: MODULE_ID, execute,
|
|
51
|
+
});
|
|
52
|
+
return [
|
|
53
|
+
define("notes.capture", (input: OperationInput) => notes.capture({
|
|
54
|
+
body: string(input, "body"), title: optionalString(input, "title"), projectRoot: string(input, "project_root"),
|
|
55
|
+
actor: optionalString(input, "actor"), source: optionalString(input, "source"), sessionId: optionalString(input, "session_id"),
|
|
56
|
+
})),
|
|
57
|
+
define("notes.list", (input: OperationInput) => notes.list({
|
|
58
|
+
projectRoot: string(input, "project_root"), status: optionalString(input, "status") as "draft" | "active" | "archived" | undefined,
|
|
59
|
+
text: optionalString(input, "text"), limit: optionalNumber(input, "limit"),
|
|
60
|
+
})),
|
|
61
|
+
define("notes.show", (input: OperationInput) => notes.show(string(input, "id"), string(input, "project_root"))),
|
|
62
|
+
define("notes.consume", (input: OperationInput) => notes.consume(string(input, "id"), {
|
|
63
|
+
projectRoot: string(input, "project_root"), actor: optionalString(input, "actor"), source: optionalString(input, "source"),
|
|
64
|
+
sessionId: optionalString(input, "session_id"), reason: optionalString(input, "reason"),
|
|
65
|
+
})),
|
|
66
|
+
define("notes.promote", (input: OperationInput) => notes.promote(string(input, "id"), string(input, "target_id"), {
|
|
67
|
+
projectRoot: string(input, "project_root"), actor: optionalString(input, "actor"), source: optionalString(input, "source"),
|
|
68
|
+
sessionId: optionalString(input, "session_id"), reason: optionalString(input, "reason"),
|
|
69
|
+
})),
|
|
70
|
+
define("notes.archive", (input: OperationInput) => notes.archive(string(input, "id"), {
|
|
71
|
+
projectRoot: string(input, "project_root"), disposition: string(input, "disposition") as NoteDisposition,
|
|
72
|
+
actor: optionalString(input, "actor"), source: optionalString(input, "source"), sessionId: optionalString(input, "session_id"),
|
|
73
|
+
reason: optionalString(input, "reason"),
|
|
74
|
+
})),
|
|
75
|
+
];
|
|
76
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modules/rules.ts — Rules as a Papyrus-native registered module
|
|
3
|
+
* (step 5, continued, of the incremental refactor in
|
|
4
|
+
* reducing-papyrus-consumer-change-amplification-with-modules--pvdo).
|
|
5
|
+
*
|
|
6
|
+
* rules.injectable is intentionally NOT registered here even though its operation name
|
|
7
|
+
* starts with "rules.": its implementation requires tasks.active() (the current Task
|
|
8
|
+
* Focus) to decide which scoped rules apply, a genuine cross-module concern. It stays a
|
|
9
|
+
* composition-root operation in src/service.ts rather than importing Tasks internals
|
|
10
|
+
* into this module or introducing a premature "modules call each other through the
|
|
11
|
+
* registry" convention.
|
|
12
|
+
*/
|
|
13
|
+
import { assignRuleProject, createRule, gateTaskWithRule, listRules, previewRule, showRule, transitionRule } from "../domain-services.ts";
|
|
14
|
+
import type { OperationDefinition } from "../module-registry.ts";
|
|
15
|
+
import type { ArtifactScopeStore } from "../ports/artifact-scope-store.ts";
|
|
16
|
+
import type { ArtifactStore } from "../ports/artifact-store.ts";
|
|
17
|
+
|
|
18
|
+
const MODULE_ID = "rules";
|
|
19
|
+
|
|
20
|
+
type OperationInput = Record<string, unknown>;
|
|
21
|
+
|
|
22
|
+
function string(input: OperationInput, key: string): string {
|
|
23
|
+
const value = input[key];
|
|
24
|
+
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function optionalString(input: OperationInput, key: string): string | undefined {
|
|
29
|
+
const value = input[key];
|
|
30
|
+
if (value === undefined) return undefined;
|
|
31
|
+
if (typeof value !== "string") throw new Error(`${key} must be a string`);
|
|
32
|
+
return value;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function optionalNumber(input: OperationInput, key: string): number | undefined {
|
|
36
|
+
const value = input[key];
|
|
37
|
+
if (value === undefined) return undefined;
|
|
38
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} must be a number`);
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const eventContext = (input: OperationInput) => ({
|
|
43
|
+
actor: optionalString(input, "actor"),
|
|
44
|
+
source: optionalString(input, "source"),
|
|
45
|
+
sessionId: optionalString(input, "session_id") ?? optionalString(input, "sessionId"),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const artifactFilter = (input: OperationInput) => ({
|
|
49
|
+
status: optionalString(input, "status"),
|
|
50
|
+
text: optionalString(input, "text"),
|
|
51
|
+
limit: optionalNumber(input, "limit"),
|
|
52
|
+
projectRoot: optionalString(input, "project_root"),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/** Registers every rules.* operation except rules.injectable (see module comment). Behavior is unchanged from the prior inline handlers in src/service.ts. */
|
|
56
|
+
/** This module's own operation names, the single source of truth src/service.ts's EXPECTED_OPERATION_NAMES spreads in rather than re-listing by hand. rules.injectable is deliberately absent -- see the module comment above. */
|
|
57
|
+
export const RULES_OPERATION_NAMES = [
|
|
58
|
+
"rules.create", "rules.list", "rules.show", "rules.preview", "rules.enable", "rules.disable", "rules.gate", "rules.assign_project",
|
|
59
|
+
] as const;
|
|
60
|
+
|
|
61
|
+
export function rulesOperations(artifacts: ArtifactStore, scopes: ArtifactScopeStore): OperationDefinition[] {
|
|
62
|
+
const define = <Input, Output>(name: string, execute: (input: Input) => Output): OperationDefinition<Input, Output> => ({
|
|
63
|
+
name, moduleId: MODULE_ID, execute,
|
|
64
|
+
});
|
|
65
|
+
return [
|
|
66
|
+
define("rules.create", (input: OperationInput) => createRule(artifacts, scopes, {
|
|
67
|
+
title: string(input, "title"), body: optionalString(input, "body"), condition: optionalString(input, "condition"),
|
|
68
|
+
action: optionalString(input, "rule_action") ?? optionalString(input, "governance_action"),
|
|
69
|
+
severity: optionalString(input, "severity") as "block" | "warn" | "info" | undefined,
|
|
70
|
+
labels: input["labels"] as string[] | undefined, extra: input["extra"] as Record<string, unknown> | undefined,
|
|
71
|
+
projectRoot: optionalString(input, "project_root"),
|
|
72
|
+
}, eventContext(input))),
|
|
73
|
+
define("rules.list", (input: OperationInput) => listRules(artifacts, scopes, artifactFilter(input))),
|
|
74
|
+
define("rules.show", (input: OperationInput) => showRule(artifacts, string(input, "id"))),
|
|
75
|
+
define("rules.preview", (input: OperationInput) => previewRule(artifacts, string(input, "id"))),
|
|
76
|
+
define("rules.enable", (input: OperationInput) => transitionRule(artifacts, string(input, "id"), "enable", eventContext(input))),
|
|
77
|
+
define("rules.disable", (input: OperationInput) => transitionRule(artifacts, string(input, "id"), "disable", eventContext(input))),
|
|
78
|
+
define("rules.gate", (input: OperationInput) => gateTaskWithRule(artifacts, string(input, "id"), string(input, "task_id"), eventContext(input))),
|
|
79
|
+
define("rules.assign_project", (input: OperationInput) => assignRuleProject(artifacts, scopes, string(input, "id"), optionalString(input, "project_root"))),
|
|
80
|
+
];
|
|
81
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modules/skills.ts — Skills as a Papyrus-native registered module
|
|
3
|
+
* (step 5, continued, of the incremental refactor in
|
|
4
|
+
* reducing-papyrus-consumer-change-amplification-with-modules--pvdo).
|
|
5
|
+
*
|
|
6
|
+
* skills.instantiate is intentionally NOT registered here even though its operation name
|
|
7
|
+
* starts with "skills.": when the target template's targetKind is "task" it calls
|
|
8
|
+
* tasks.create() directly instead of the generic instantiateTemplate path — a genuine
|
|
9
|
+
* cross-module concern, same category as rules.injectable (see modules/rules.ts). It
|
|
10
|
+
* stays a composition-root operation in src/service.ts.
|
|
11
|
+
*
|
|
12
|
+
* skills.run depends on the Task-domain ports (TaskEventStore, TaskScopeStore) as
|
|
13
|
+
* constructor parameters. These are shared port contracts every module may depend on,
|
|
14
|
+
* the same way every module already depends on ArtifactStore — not "another module's
|
|
15
|
+
* infrastructure" in the sense of a concrete class. skill-execution.ts already has this
|
|
16
|
+
* port dependency pre-existing; untangling it is a separate, larger concern than this
|
|
17
|
+
* extraction.
|
|
18
|
+
*/
|
|
19
|
+
import type { AuthorityRegistry } from "../authority-registry.ts";
|
|
20
|
+
import { assignSkillProject, createArtifactTemplate, createSkill, listSkills, showSkill, skillInvocation, transitionSkill } from "../domain-services.ts";
|
|
21
|
+
import type { OperationDefinition } from "../module-registry.ts";
|
|
22
|
+
import type { ArtifactScopeStore } from "../ports/artifact-scope-store.ts";
|
|
23
|
+
import type { ArtifactStore } from "../ports/artifact-store.ts";
|
|
24
|
+
import type { TaskEventStore } from "../ports/task-event-store.ts";
|
|
25
|
+
import type { TaskScopeStore } from "../ports/task-scope-store.ts";
|
|
26
|
+
import { instantiateSkillWorkflow } from "../skill-execution.ts";
|
|
27
|
+
|
|
28
|
+
const MODULE_ID = "skills";
|
|
29
|
+
|
|
30
|
+
type OperationInput = Record<string, unknown>;
|
|
31
|
+
|
|
32
|
+
function string(input: OperationInput, key: string): string {
|
|
33
|
+
const value = input[key];
|
|
34
|
+
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function optionalString(input: OperationInput, key: string): string | undefined {
|
|
39
|
+
const value = input[key];
|
|
40
|
+
if (value === undefined) return undefined;
|
|
41
|
+
if (typeof value !== "string") throw new Error(`${key} must be a string`);
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function optionalNumber(input: OperationInput, key: string): number | undefined {
|
|
46
|
+
const value = input[key];
|
|
47
|
+
if (value === undefined) return undefined;
|
|
48
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} must be a number`);
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const eventContext = (input: OperationInput) => ({
|
|
53
|
+
actor: optionalString(input, "actor"),
|
|
54
|
+
source: optionalString(input, "source"),
|
|
55
|
+
sessionId: optionalString(input, "session_id") ?? optionalString(input, "sessionId"),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
const eventContextFor = (input: OperationInput, source: string) => {
|
|
59
|
+
const context = eventContext(input);
|
|
60
|
+
return { ...context, source: context.source ?? source };
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const artifactFilter = (input: OperationInput) => ({
|
|
64
|
+
status: optionalString(input, "status"),
|
|
65
|
+
text: optionalString(input, "text"),
|
|
66
|
+
limit: optionalNumber(input, "limit"),
|
|
67
|
+
projectRoot: optionalString(input, "project_root"),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
export interface SkillsModuleDeps {
|
|
71
|
+
artifacts: ArtifactStore;
|
|
72
|
+
events: TaskEventStore;
|
|
73
|
+
scopes: TaskScopeStore;
|
|
74
|
+
/** Docs/Rules/Skills project scoping (distinct from `scopes`, which is Task-run project scoping for skills.run's materialized blueprint tasks). */
|
|
75
|
+
artifactScopes: ArtifactScopeStore;
|
|
76
|
+
authority: AuthorityRegistry;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Registers every skills.* operation except skills.instantiate (see module comment). Behavior is unchanged from the prior inline handlers in src/service.ts. */
|
|
80
|
+
/** This module's own operation names, the single source of truth src/service.ts's EXPECTED_OPERATION_NAMES spreads in rather than re-listing by hand. skills.instantiate is deliberately absent -- see the module comment above. */
|
|
81
|
+
export const SKILLS_OPERATION_NAMES = [
|
|
82
|
+
"skills.create", "skills.create_template", "skills.list", "skills.show", "skills.invoke", "skills.run", "skills.enable", "skills.disable", "skills.assign_project",
|
|
83
|
+
] as const;
|
|
84
|
+
|
|
85
|
+
export function skillsOperations({ artifacts, events, scopes, artifactScopes, authority }: SkillsModuleDeps): OperationDefinition[] {
|
|
86
|
+
const define = <Input, Output>(name: string, execute: (input: Input) => Output): OperationDefinition<Input, Output> => ({
|
|
87
|
+
name, moduleId: MODULE_ID, execute,
|
|
88
|
+
});
|
|
89
|
+
return [
|
|
90
|
+
define("skills.create", (input: OperationInput) => createSkill(artifacts, artifactScopes, {
|
|
91
|
+
title: string(input, "title"), body: optionalString(input, "body"), trigger: optionalString(input, "trigger"),
|
|
92
|
+
steps: input["steps"] as string[] | undefined, tools: input["tools"] as string[] | undefined,
|
|
93
|
+
definition: input["definition"],
|
|
94
|
+
labels: input["labels"] as string[] | undefined, extra: input["extra"] as Record<string, unknown> | undefined,
|
|
95
|
+
projectRoot: optionalString(input, "project_root"),
|
|
96
|
+
}, authority, eventContext(input))),
|
|
97
|
+
define("skills.create_template", (input: OperationInput) => createArtifactTemplate(artifacts, artifactScopes, {
|
|
98
|
+
title: string(input, "title"), targetKind: string(input, "target_kind"), defaults: input["defaults"] as Record<string, unknown> | undefined,
|
|
99
|
+
required: input["required"] as string[] | undefined, body: optionalString(input, "body"), labels: input["labels"] as string[] | undefined,
|
|
100
|
+
projectRoot: optionalString(input, "project_root"),
|
|
101
|
+
}, authority, eventContext(input))),
|
|
102
|
+
define("skills.list", (input: OperationInput) => listSkills(artifacts, artifactScopes, artifactFilter(input))),
|
|
103
|
+
define("skills.show", (input: OperationInput) => showSkill(artifacts, string(input, "id"))),
|
|
104
|
+
define("skills.invoke", (input: OperationInput) => skillInvocation(artifacts, string(input, "id"))),
|
|
105
|
+
define("skills.run", (input: OperationInput) => instantiateSkillWorkflow(artifacts, string(input, "id"), {
|
|
106
|
+
runId: optionalString(input, "run_id") ?? optionalString(input, "runId"),
|
|
107
|
+
arguments: input["arguments"] as Record<string, unknown> | undefined,
|
|
108
|
+
}, { events, scopes, projectRoot: string(input, "project_root"), context: eventContextFor(input, "skill-run") })),
|
|
109
|
+
define("skills.enable", (input: OperationInput) => transitionSkill(artifacts, string(input, "id"), "enable", eventContext(input))),
|
|
110
|
+
define("skills.disable", (input: OperationInput) => transitionSkill(artifacts, string(input, "id"), "disable", eventContext(input))),
|
|
111
|
+
define("skills.assign_project", (input: OperationInput) => assignSkillProject(artifacts, artifactScopes, string(input, "id"), optionalString(input, "project_root"))),
|
|
112
|
+
];
|
|
113
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* modules/tasks.ts — Tasks as the second Papyrus-native registered module
|
|
3
|
+
* (step 5, continued, of the incremental refactor in
|
|
4
|
+
* reducing-papyrus-consumer-change-amplification-with-modules--pvdo).
|
|
5
|
+
*
|
|
6
|
+
* Deliberately more representative than modules/notes.ts: Tasks owns real schema
|
|
7
|
+
* (task_events, task_focus, task_scopes and their migrations, still in src/db.ts for
|
|
8
|
+
* this slice — module-owned migrations are a separate follow-up,
|
|
9
|
+
* add-a-module-migration-ledger-keyed-by-moduleid-version-with-3e7k), a much larger
|
|
10
|
+
* operation surface, and cross-module edges (rules.gate links a rule to a task;
|
|
11
|
+
* graph.link routes depends_on through Tasks.depend for cycle safety — those two
|
|
12
|
+
* remain in src/service.ts since they are graph.* / rules.* operations, not tasks.*).
|
|
13
|
+
*
|
|
14
|
+
* Task-domain-internal files (task-context.ts, task-execution.ts, domain/task-event.ts,
|
|
15
|
+
* domain/task-scope.ts) are imported directly — they belong to this bounded context,
|
|
16
|
+
* unlike a different module's infrastructure. Generic input-parsing helpers are
|
|
17
|
+
* duplicated locally rather than imported from src/service.ts, matching the precedent
|
|
18
|
+
* set by modules/notes.ts: a module does not import another module's infrastructure,
|
|
19
|
+
* including the composition root's own helpers.
|
|
20
|
+
*/
|
|
21
|
+
import type { Checklist } from "../domain/checklist.ts";
|
|
22
|
+
import type { TaskEventContext, TaskEventDirection } from "../domain/task-event.ts";
|
|
23
|
+
import type { TaskViewMode } from "../domain/task-scope.ts";
|
|
24
|
+
import type { OperationDefinition } from "../module-registry.ts";
|
|
25
|
+
import type { ArtifactStore } from "../ports/artifact-store.ts";
|
|
26
|
+
import { taskContext } from "../task-context.ts";
|
|
27
|
+
import { projectTaskExecution } from "../task-execution.ts";
|
|
28
|
+
import { Tasks, type TaskStatus } from "../task-service.ts";
|
|
29
|
+
|
|
30
|
+
const MODULE_ID = "tasks";
|
|
31
|
+
|
|
32
|
+
type OperationInput = Record<string, unknown>;
|
|
33
|
+
|
|
34
|
+
function string(input: OperationInput, key: string): string {
|
|
35
|
+
const value = input[key];
|
|
36
|
+
if (typeof value !== "string" || value.length === 0) throw new Error(`${key} is required`);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function optionalString(input: OperationInput, key: string): string | undefined {
|
|
41
|
+
const value = input[key];
|
|
42
|
+
if (value === undefined) return undefined;
|
|
43
|
+
if (typeof value !== "string") throw new Error(`${key} must be a string`);
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function optionalStringArray(input: OperationInput, key: string): string[] | undefined {
|
|
48
|
+
const value = input[key];
|
|
49
|
+
if (value === undefined) return undefined;
|
|
50
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) throw new Error(`${key} must be an array of strings`);
|
|
51
|
+
return value as string[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function optionalNumber(input: OperationInput, key: string): number | undefined {
|
|
55
|
+
const value = input[key];
|
|
56
|
+
if (value === undefined) return undefined;
|
|
57
|
+
if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`${key} must be a number`);
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const eventContext = (input: OperationInput): TaskEventContext => ({
|
|
62
|
+
actor: optionalString(input, "actor"),
|
|
63
|
+
source: optionalString(input, "source"),
|
|
64
|
+
sessionId: optionalString(input, "session_id") ?? optionalString(input, "sessionId"),
|
|
65
|
+
reason: optionalString(input, "reason"),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const taskFilter = (input: OperationInput) => ({
|
|
69
|
+
status: optionalString(input, "status"),
|
|
70
|
+
text: optionalString(input, "text"),
|
|
71
|
+
limit: optionalNumber(input, "limit"),
|
|
72
|
+
projectRoot: string(input, "project_root"),
|
|
73
|
+
scope: optionalString(input, "scope") as TaskViewMode | undefined,
|
|
74
|
+
rootTaskId: optionalString(input, "root_task_id"),
|
|
75
|
+
sessionId: optionalString(input, "session_id") ?? optionalString(input, "sessionId"),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Registers every tasks.* operation against one Tasks instance. Behavior is unchanged from
|
|
80
|
+
* the prior inline handlers in src/service.ts. tasks.context needs the raw ArtifactStore
|
|
81
|
+
* port directly (taskContext is a plain-artifact query, not a Tasks method), so the
|
|
82
|
+
* composition root passes the same artifacts port it already constructs Tasks with —
|
|
83
|
+
* this is not "another module's infrastructure", it is the shared port every module writes
|
|
84
|
+
* through.
|
|
85
|
+
*/
|
|
86
|
+
/** This module's own operation names, the single source of truth src/service.ts's EXPECTED_OPERATION_NAMES spreads in rather than re-listing by hand. */
|
|
87
|
+
export const TASKS_OPERATION_NAMES = [
|
|
88
|
+
"tasks.create", "tasks.update", "tasks.list", "tasks.graph", "tasks.plan", "tasks.show", "tasks.history",
|
|
89
|
+
"tasks.scope", "tasks.set_scope", "tasks.assign_project", "tasks.active", "tasks.focused", "tasks.focus",
|
|
90
|
+
"tasks.pause", "tasks.unpause", "tasks.clear_focus", "tasks.start", "tasks.submit", "tasks.complete",
|
|
91
|
+
"tasks.run_gates", "tasks.set_checklist", "tasks.context", "tasks.reject", "tasks.retry", "tasks.cancel",
|
|
92
|
+
"tasks.depend", "tasks.undepend", "tasks.contain", "tasks.uncontain",
|
|
93
|
+
] as const;
|
|
94
|
+
|
|
95
|
+
export function tasksOperations(tasks: Tasks, artifacts: ArtifactStore): OperationDefinition[] {
|
|
96
|
+
const define = <Input, Output>(name: string, execute: (input: Input) => Output): OperationDefinition<Input, Output> => ({
|
|
97
|
+
name, moduleId: MODULE_ID, execute,
|
|
98
|
+
});
|
|
99
|
+
return [
|
|
100
|
+
define("tasks.create", (input: OperationInput) => tasks.create({
|
|
101
|
+
title: string(input, "title"),
|
|
102
|
+
body: optionalString(input, "body"),
|
|
103
|
+
status: optionalString(input, "status") as TaskStatus | undefined,
|
|
104
|
+
labels: input["labels"] as string[] | undefined,
|
|
105
|
+
extra: input["extra"] as Record<string, unknown> | undefined,
|
|
106
|
+
gates: input["gates"] as Parameters<Tasks["create"]>[0]["gates"],
|
|
107
|
+
checklist: input["checklist"] as Checklist | undefined,
|
|
108
|
+
templateId: optionalString(input, "template_id") ?? optionalString(input, "templateId"),
|
|
109
|
+
parentId: optionalString(input, "parent_id") ?? optionalString(input, "parentId"),
|
|
110
|
+
dependsOn: (input["depends_on"] ?? input["dependsOn"]) as string[] | undefined,
|
|
111
|
+
projectRoot: string(input, "project_root"),
|
|
112
|
+
projectSource: "cwd",
|
|
113
|
+
}, eventContext(input))),
|
|
114
|
+
define("tasks.update", (input: OperationInput) => tasks.update(string(input, "id"), {
|
|
115
|
+
...(input["title"] !== undefined ? { title: optionalString(input, "title")! } : {}),
|
|
116
|
+
...(input["body"] !== undefined ? { body: optionalString(input, "body")! } : {}),
|
|
117
|
+
...(input["labels"] !== undefined ? { labels: optionalStringArray(input, "labels")! } : {}),
|
|
118
|
+
...(input["status"] !== undefined ? { status: string(input, "status") as "todo" } : {}),
|
|
119
|
+
}, eventContext(input))),
|
|
120
|
+
define("tasks.list", (input: OperationInput) => tasks.list(taskFilter(input))),
|
|
121
|
+
define("tasks.graph", (input: OperationInput) => tasks.graph(taskFilter(input))),
|
|
122
|
+
define("tasks.plan", (input: OperationInput) => projectTaskExecution(tasks.graph(taskFilter(input)))),
|
|
123
|
+
define("tasks.show", (input: OperationInput) => tasks.show(string(input, "id"))),
|
|
124
|
+
define("tasks.history", (input: OperationInput) => tasks.history(string(input, "id"), {
|
|
125
|
+
limit: optionalNumber(input, "limit"),
|
|
126
|
+
cursor: optionalNumber(input, "cursor"),
|
|
127
|
+
direction: optionalString(input, "direction") as TaskEventDirection | undefined,
|
|
128
|
+
})),
|
|
129
|
+
define("tasks.scope", (input: OperationInput) => tasks.scopeSelection(string(input, "project_root"))),
|
|
130
|
+
define("tasks.set_scope", (input: OperationInput) => tasks.setView(
|
|
131
|
+
string(input, "project_root"),
|
|
132
|
+
string(input, "scope") as TaskViewMode,
|
|
133
|
+
optionalString(input, "root_task_id"),
|
|
134
|
+
)),
|
|
135
|
+
define("tasks.assign_project", (input: OperationInput) => tasks.assignProject(
|
|
136
|
+
string(input, "id"),
|
|
137
|
+
string(input, "project_root"),
|
|
138
|
+
eventContext(input),
|
|
139
|
+
)),
|
|
140
|
+
define("tasks.active", (input: OperationInput) => tasks.active(taskFilter(input))),
|
|
141
|
+
define("tasks.focused", (input: OperationInput) => tasks.focused(taskFilter(input))),
|
|
142
|
+
define("tasks.focus", (input: OperationInput) => tasks.focus(string(input, "id"), eventContext(input))),
|
|
143
|
+
define("tasks.pause", (input: OperationInput) => tasks.pauseFocus(eventContext(input))),
|
|
144
|
+
define("tasks.unpause", (input: OperationInput) => tasks.unpauseFocus(eventContext(input))),
|
|
145
|
+
define("tasks.clear_focus", (input: OperationInput) => tasks.clearFocus(eventContext(input))),
|
|
146
|
+
define("tasks.start", (input: OperationInput) => tasks.transition(string(input, "id"), "start", eventContext(input))),
|
|
147
|
+
define("tasks.submit", (input: OperationInput) => tasks.transition(string(input, "id"), "submit", eventContext(input))),
|
|
148
|
+
define("tasks.complete", (input: OperationInput) => tasks.completeAsync(string(input, "id"), eventContext(input))),
|
|
149
|
+
define("tasks.run_gates", (input: OperationInput) => tasks.runGates(string(input, "id"), eventContext(input))),
|
|
150
|
+
define("tasks.set_checklist", (input: OperationInput) => tasks.setChecklist(string(input, "id"), input["checklist"] as Checklist)),
|
|
151
|
+
define("tasks.context", (input: OperationInput) => taskContext(
|
|
152
|
+
artifacts,
|
|
153
|
+
tasks.active(taskFilter(input))?.id,
|
|
154
|
+
new Set(tasks.list(taskFilter(input)).map((task) => task.id)),
|
|
155
|
+
)),
|
|
156
|
+
define("tasks.reject", (input: OperationInput) => tasks.transition(string(input, "id"), "reject", eventContext(input))),
|
|
157
|
+
define("tasks.retry", (input: OperationInput) => tasks.transition(string(input, "id"), "retry", eventContext(input))),
|
|
158
|
+
define("tasks.cancel", (input: OperationInput) => tasks.transition(string(input, "id"), "cancel", eventContext(input))),
|
|
159
|
+
define("tasks.depend", (input: OperationInput) => tasks.depend(string(input, "id"), string(input, "dependency_id"), eventContext(input))),
|
|
160
|
+
define("tasks.undepend", (input: OperationInput) => tasks.undepend(string(input, "id"), string(input, "dependency_id"), eventContext(input))),
|
|
161
|
+
define("tasks.contain", (input: OperationInput) => tasks.contain(string(input, "parent_id"), string(input, "child_id"), eventContext(input))),
|
|
162
|
+
define("tasks.uncontain", (input: OperationInput) => tasks.uncontain(string(input, "parent_id"), string(input, "child_id"), eventContext(input))),
|
|
163
|
+
];
|
|
164
|
+
}
|