@astrosheep/keiyaku 4.6.8 → 4.6.9
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 +40 -14
- package/build/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/build/plugins/claude/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/claude/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/claude/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/claude/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/claude/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/claude/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/claude/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/build/plugins/codex/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/codex/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/codex/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/codex/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/codex/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/codex/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/opencode/index.js +1 -1
- package/build/plugins/opencode/skills/keiyaku/SKILL.md +5 -6
- package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/plugins/opencode/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/plugins/opencode/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/plugins/opencode/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/plugins/opencode/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/plugins/opencode/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/skills/keiyaku/SKILL.md +5 -6
- package/build/skills/keiyaku-akuma/SKILL.md +1 -1
- package/build/skills/keiyaku-akuma/references/automation.md +8 -8
- package/build/skills/keiyaku-babysit/SKILL.md +1 -1
- package/build/skills/keiyaku-bind/SKILL.md +9 -9
- package/build/skills/keiyaku-debt/SKILL.md +91 -0
- package/build/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/src/akuma/akuma-instance.d.ts +2 -0
- package/build/src/akuma/akuma-instance.js +10 -0
- package/build/src/akuma/akuma-observe.d.ts +33 -8
- package/build/src/akuma/akuma-observe.js +88 -64
- package/build/src/akuma/akuma-owner.d.ts +11 -2
- package/build/src/akuma/akuma-owner.js +45 -35
- package/build/src/akuma/akuma-selection-execution.js +9 -1
- package/build/src/akuma/akuma-stream.d.ts +22 -0
- package/build/src/akuma/akuma-stream.js +116 -0
- package/build/src/akuma/akuma-wait.d.ts +1 -40
- package/build/src/akuma/akuma-wait.js +23 -84
- package/build/src/akuma/akuma.d.ts +17 -11
- package/build/src/akuma/akuma.js +2 -0
- package/build/src/akuma/allowed.d.ts +7 -12
- package/build/src/akuma/allowed.js +13 -4
- package/build/src/akuma/body-supervisor.d.ts +5 -5
- package/build/src/akuma/body-supervisor.js +22 -20
- package/build/src/akuma/body.js +28 -1
- package/build/src/akuma/call-request.d.ts +12 -22
- package/build/src/akuma/heart/activity-schema.d.ts +12 -0
- package/build/src/akuma/heart/activity-schema.js +6 -0
- package/build/src/akuma/heart/index.d.ts +59 -2
- package/build/src/akuma/heart/index.js +87 -8
- package/build/src/akuma/heart/rows.js +5 -3
- package/build/src/akuma/heart/soul.d.ts +9 -0
- package/build/src/akuma/heart/soul.js +29 -0
- package/build/src/akuma/heart/storage.d.ts +15 -0
- package/build/src/akuma/heart/storage.js +55 -0
- package/build/src/akuma/heart/timeline.d.ts +7 -0
- package/build/src/akuma/heart/timeline.js +10 -0
- package/build/src/akuma/index.d.ts +1 -0
- package/build/src/akuma/projection.d.ts +25 -0
- package/build/src/akuma/projection.js +24 -7
- package/build/src/akuma/provider-recipe.d.ts +11 -0
- package/build/src/akuma/provider-recipe.js +23 -0
- package/build/src/akuma/provider.d.ts +25 -0
- package/build/src/akuma/provider.js +99 -7
- package/build/src/akuma/providers/acp/events.js +25 -2
- package/build/src/akuma/providers/claude/events.js +76 -10
- package/build/src/akuma/providers/claude/index.js +2 -5
- package/build/src/akuma/providers/codex-app-server/events.js +17 -2
- package/build/src/akuma/providers/codex-app-server/index.js +15 -9
- package/build/src/akuma/providers/decode.d.ts +8 -0
- package/build/src/akuma/providers/decode.js +21 -0
- package/build/src/akuma/providers/grok-build/index.js +10 -26
- package/build/src/akuma/providers/opencode-sdk/event-stream.d.ts +3 -3
- package/build/src/akuma/providers/opencode-sdk/event-stream.js +2 -25
- package/build/src/akuma/providers/opencode-sdk/events.d.ts +7 -43
- package/build/src/akuma/providers/opencode-sdk/events.js +147 -284
- package/build/src/akuma/providers/opencode-sdk/index.js +267 -421
- package/build/src/akuma/providers/opencode-sdk/native-http.d.ts +9 -0
- package/build/src/akuma/providers/opencode-sdk/native-http.js +69 -0
- package/build/src/akuma/providers/opencode-sdk/session.d.ts +42 -36
- package/build/src/akuma/providers/opencode-sdk/session.js +75 -82
- package/build/src/akuma/providers/paseo/events.d.ts +8 -0
- package/build/src/akuma/providers/paseo/events.js +58 -11
- package/build/src/akuma/providers/paseo/index.js +11 -7
- package/build/src/akuma/providers/pi/events.d.ts +2 -0
- package/build/src/akuma/providers/pi/events.js +32 -24
- package/build/src/akuma/providers/pi/index.js +21 -35
- package/build/src/akuma/request-lifecycle.d.ts +1 -2
- package/build/src/akuma/request-lifecycle.js +2 -8
- package/build/src/akuma/request-rendezvous.js +38 -55
- package/build/src/akuma/request-serve.d.ts +1 -1
- package/build/src/akuma/request-serve.js +77 -113
- package/build/src/akuma/selection-observation.d.ts +57 -51
- package/build/src/akuma/selection-observation.js +3 -3
- package/build/src/akuma/turn-drive.d.ts +3 -0
- package/build/src/akuma/turn-drive.js +26 -5
- package/build/src/akuma-body.js +12 -22
- package/build/src/cli/akuma-caller.js +10 -0
- package/build/src/cli/commands/akuma-grammar.js +3 -4
- package/build/src/cli/commands/akuma.js +13 -51
- package/build/src/cli/commands/contract-grammar.d.ts +7 -6
- package/build/src/cli/commands/contract-grammar.js +27 -32
- package/build/src/cli/commands/contract-help.d.ts +16 -7
- package/build/src/cli/commands/contract-help.js +20 -11
- package/build/src/cli/commands/contract.js +11 -11
- package/build/src/cli/commands/debt-grammar.d.ts +19 -0
- package/build/src/cli/commands/debt-grammar.js +323 -0
- package/build/src/cli/commands/debt-query.d.ts +5 -0
- package/build/src/cli/commands/{task-query.js → debt-query.js} +9 -9
- package/build/src/cli/commands/{task.d.ts → debt.d.ts} +2 -2
- package/build/src/cli/commands/{task.js → debt.js} +83 -143
- package/build/src/cli/coordinates.d.ts +1 -1
- package/build/src/cli/coordinates.js +1 -1
- package/build/src/cli/parse.d.ts +4 -4
- package/build/src/cli/parse.js +17 -16
- package/build/src/cli/render/akuma-activity.d.ts +11 -83
- package/build/src/cli/render/akuma-activity.js +20 -382
- package/build/src/cli/render/catalog.d.ts +2 -2
- package/build/src/cli/render/catalog.js +5 -5
- package/build/src/cli/render/contract.js +2 -2
- package/build/src/cli/render/debt.d.ts +28 -0
- package/build/src/cli/render/{task.js → debt.js} +51 -112
- package/build/src/cli/render/kanshi.js +28 -28
- package/build/src/cli/render/marks.d.ts +5 -5
- package/build/src/cli/render/marks.js +4 -10
- package/build/src/cli/render/nuke.js +1 -1
- package/build/src/cli/runtime.js +3 -3
- package/build/src/cli/scan.d.ts +1 -1
- package/build/src/cli/usage.d.ts +1 -1
- package/build/src/cli/usage.js +4 -4
- package/build/src/contract-worktree.js +3 -3
- package/build/src/debt/board.d.ts +160 -0
- package/build/src/{task → debt}/board.js +89 -89
- package/build/src/debt/catalog.d.ts +11 -0
- package/build/src/debt/catalog.js +59 -0
- package/build/src/{task → debt}/context.d.ts +1 -1
- package/build/src/{task → debt}/context.js +1 -1
- package/build/src/{task → debt}/created-observation.d.ts +6 -10
- package/build/src/{task → debt}/created-observation.js +7 -7
- package/build/src/debt/document.d.ts +37 -0
- package/build/src/{task → debt}/document.js +37 -37
- package/build/src/debt/identity.d.ts +21 -0
- package/build/src/{task → debt}/identity.js +35 -35
- package/build/src/debt/index.d.ts +111 -0
- package/build/src/debt/index.js +203 -0
- package/build/src/{task → debt}/input.d.ts +8 -8
- package/build/src/{task → debt}/input.js +21 -21
- package/build/src/debt/lifecycle-operations.d.ts +6 -0
- package/build/src/{task → debt}/lifecycle-operations.js +27 -31
- package/build/src/debt/mutation-result.d.ts +981 -0
- package/build/src/debt/mutation-result.js +109 -0
- package/build/src/debt/mutation.d.ts +424 -0
- package/build/src/debt/mutation.js +223 -0
- package/build/src/debt/operation-types.d.ts +71 -0
- package/build/src/debt/operations.d.ts +28 -0
- package/build/src/{task → debt}/operations.js +59 -60
- package/build/src/debt/query.d.ts +76 -0
- package/build/src/{task → debt}/query.js +55 -55
- package/build/src/{task → debt}/store.d.ts +13 -13
- package/build/src/{task → debt}/store.js +58 -49
- package/build/src/debt/view.d.ts +4 -0
- package/build/src/debt/view.js +7 -0
- package/build/src/identity/coordinates.d.ts +1 -1
- package/build/src/index.d.ts +13 -13
- package/build/src/index.js +9 -9
- package/build/src/kanshi/index.d.ts +1 -1
- package/build/src/kanshi/read.js +33 -33
- package/build/src/kanshi/report.d.ts +7 -7
- package/build/src/kanshi/select.js +4 -4
- package/build/src/library/akumas.d.ts +2 -3
- package/build/src/library/bind.d.ts +3 -3
- package/build/src/library/bind.js +5 -5
- package/build/src/library/contract-creation.js +5 -5
- package/build/src/library/contract-execution.js +4 -4
- package/build/src/library/contract-types.d.ts +2 -2
- package/build/src/library/input.d.ts +2 -2
- package/build/src/library/input.js +5 -5
- package/build/src/library/invocation.js +2 -2
- package/build/src/library/nuke.js +3 -3
- package/build/src/library/outcome.d.ts +126 -126
- package/build/src/library/outcome.js +3 -3
- package/build/src/library/selection.d.ts +1 -4
- package/build/src/library/selection.js +11 -62
- package/build/src/plugin/public.d.ts +5 -0
- package/build/src/plugin/runtime.js +3 -1
- package/build/src/runtime/proc/windows-launch.exe +0 -0
- package/build/src/settlement/fence.d.ts +3 -3
- package/build/src/settlement/fence.js +4 -4
- package/build/src/settlement/holder.d.ts +16 -16
- package/build/src/settlement/holder.js +69 -63
- package/build/src/settlement/settle.d.ts +5 -5
- package/build/src/settlement/settle.js +53 -53
- package/package.json +12 -6
- package/plugins/paseo/plugin/index.server.ts +17 -0
- package/plugins/paseo/plugin/paseo-plugin.json +4 -0
- package/plugins/paseo/plugin/server/attach.ts +52 -0
- package/plugins/paseo/plugin/server/keiyaku.ts +44 -0
- package/plugins/paseo/plugin/server/message.ts +60 -0
- package/plugins/paseo/plugin/server/mirror.ts +193 -0
- package/plugins/paseo/plugin/server/model.ts +14 -0
- package/plugins/paseo/plugin/server/provider.ts +276 -0
- package/plugins/paseo/plugin/server/sdk-worker.ts +237 -0
- package/plugins/paseo/plugin/server/sdk.ts +289 -0
- package/plugins/paseo/plugin/server/stop.ts +26 -0
- package/plugins/paseo/plugin/server/transport.ts +35 -0
- package/plugins/paseo/plugin/shared/attach-rpc.ts +10 -0
- package/plugins/paseo/plugin/shared/refresh-rpc.ts +6 -0
- package/plugins/paseo/plugin/tsconfig.json +6 -0
- package/skills/keiyaku/SKILL.md +5 -6
- package/skills/keiyaku-akuma/SKILL.md +1 -1
- package/skills/keiyaku-akuma/references/automation.md +8 -8
- package/skills/keiyaku-babysit/SKILL.md +1 -1
- package/skills/keiyaku-bind/SKILL.md +9 -9
- package/skills/keiyaku-debt/SKILL.md +91 -0
- package/skills/keiyaku-workflow/SKILL.md +2 -2
- package/build/plugins/claude/skills/keiyaku-task/SKILL.md +0 -104
- package/build/plugins/codex/skills/keiyaku-task/SKILL.md +0 -104
- package/build/plugins/opencode/skills/keiyaku-task/SKILL.md +0 -104
- package/build/skills/keiyaku-task/SKILL.md +0 -104
- package/build/src/cli/commands/task-grammar.d.ts +0 -19
- package/build/src/cli/commands/task-grammar.js +0 -384
- package/build/src/cli/commands/task-query.d.ts +0 -5
- package/build/src/cli/render/task.d.ts +0 -39
- package/build/src/task/board.d.ts +0 -168
- package/build/src/task/catalog.d.ts +0 -11
- package/build/src/task/catalog.js +0 -59
- package/build/src/task/compose-language.d.ts +0 -57
- package/build/src/task/compose-language.js +0 -364
- package/build/src/task/compose-parser.d.ts +0 -32
- package/build/src/task/compose-parser.js +0 -180
- package/build/src/task/compose.d.ts +0 -16
- package/build/src/task/compose.js +0 -189
- package/build/src/task/document.d.ts +0 -39
- package/build/src/task/identity.d.ts +0 -21
- package/build/src/task/index.d.ts +0 -138
- package/build/src/task/index.js +0 -269
- package/build/src/task/lifecycle-operations.d.ts +0 -6
- package/build/src/task/mutation-result.d.ts +0 -1726
- package/build/src/task/mutation-result.js +0 -198
- package/build/src/task/mutation.d.ts +0 -590
- package/build/src/task/mutation.js +0 -262
- package/build/src/task/operation-types.d.ts +0 -71
- package/build/src/task/operations.d.ts +0 -28
- package/build/src/task/query.d.ts +0 -76
- package/build/src/task/view.d.ts +0 -4
- package/build/src/task/view.js +0 -7
- package/skills/keiyaku-task/SKILL.md +0 -104
- /package/build/src/{task → debt}/operation-types.js +0 -0
package/build/src/cli/usage.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type CliUsageGuide = Readonly<{
|
|
|
6
6
|
hideDiagnostic?: boolean;
|
|
7
7
|
}>;
|
|
8
8
|
export declare const ROOT_USAGE_GUIDE: CliUsageGuide;
|
|
9
|
-
export declare const
|
|
9
|
+
export declare const DEBT_FAMILY_USAGE_GUIDE: CliUsageGuide;
|
|
10
10
|
export declare function acceptsFromUsage(usage: string): string;
|
|
11
11
|
export declare function commandGuide(commandPath: string, usage: string): CliUsageGuide;
|
|
12
12
|
export declare function unknownCommandGuide(guide: CliUsageGuide, given: string): CliUsageGuide;
|
package/build/src/cli/usage.js
CHANGED
|
@@ -3,10 +3,10 @@ export const ROOT_USAGE_GUIDE = {
|
|
|
3
3
|
accepts: "keiyaku <command> [options]",
|
|
4
4
|
help: "keiyaku --help",
|
|
5
5
|
};
|
|
6
|
-
export const
|
|
7
|
-
scope: "keiyaku
|
|
8
|
-
accepts: "keiyaku
|
|
9
|
-
help: "keiyaku
|
|
6
|
+
export const DEBT_FAMILY_USAGE_GUIDE = {
|
|
7
|
+
scope: "keiyaku debt",
|
|
8
|
+
accepts: "keiyaku debt <command> ...",
|
|
9
|
+
help: "keiyaku debt --help",
|
|
10
10
|
};
|
|
11
11
|
export function acceptsFromUsage(usage) {
|
|
12
12
|
return usage
|
|
@@ -6,10 +6,10 @@ import { CONTRACT_DELIVERER_SKILL, CONTRACT_REVIEWER_SKILL, CONTRACT_ONBOARDING_
|
|
|
6
6
|
import { executeGit } from "./git/process.js";
|
|
7
7
|
import { worktreePath } from "./git/workspace.js";
|
|
8
8
|
import { appointmentFor, placeRegisterPath } from "./workspace-place.js";
|
|
9
|
-
import { repairNamespaceContext } from "./
|
|
10
|
-
import { contractNamespace } from "./
|
|
9
|
+
import { repairNamespaceContext } from "./debt/context.js";
|
|
10
|
+
import { contractNamespace } from "./debt/identity.js";
|
|
11
11
|
const IGNORE_BYTES = ".gitignore\nKEIYAKU.md\n";
|
|
12
|
-
const PRIMARY_IGNORE_BYTES = "*\n!settings.json\n!
|
|
12
|
+
const PRIMARY_IGNORE_BYTES = "*\n!settings.json\n!debts/\n!debts/**\n";
|
|
13
13
|
const SEAT_IGNORE_BYTES = ".gitignore\nSKILL.md\n";
|
|
14
14
|
const SEAT_SKILLS = [
|
|
15
15
|
["keiyaku-onboarding", CONTRACT_ONBOARDING_SKILL],
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { type DebtDocument, type DebtPriority, type DebtState } from "./document.js";
|
|
2
|
+
import { type DebtId } from "./identity.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { type BoundedList } from "../bounded-list.js";
|
|
5
|
+
export type DebtRef = Readonly<{
|
|
6
|
+
id: DebtId;
|
|
7
|
+
title: string | null;
|
|
8
|
+
state: DebtState | "missing";
|
|
9
|
+
}>;
|
|
10
|
+
export declare const debtRowSchema: z.ZodObject<{
|
|
11
|
+
id: z.ZodPipe<z.ZodString, z.ZodTransform<`debt/${string}`, string>>;
|
|
12
|
+
title: z.ZodString;
|
|
13
|
+
state: z.ZodEnum<{
|
|
14
|
+
open: "open";
|
|
15
|
+
done: "done";
|
|
16
|
+
drop: "drop";
|
|
17
|
+
}>;
|
|
18
|
+
priority: z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>;
|
|
19
|
+
disposition: z.ZodEnum<{
|
|
20
|
+
done: "done";
|
|
21
|
+
drop: "drop";
|
|
22
|
+
ready: "ready";
|
|
23
|
+
blocked: "blocked";
|
|
24
|
+
}>;
|
|
25
|
+
updatedAt: z.ZodString;
|
|
26
|
+
bodyPresent: z.ZodBoolean;
|
|
27
|
+
children: z.ZodOptional<z.ZodObject<{
|
|
28
|
+
live: z.ZodNumber;
|
|
29
|
+
total: z.ZodNumber;
|
|
30
|
+
}, z.core.$strict>>;
|
|
31
|
+
}, z.core.$strict>;
|
|
32
|
+
export declare const debtRowsSchema: z.ZodReadonly<z.ZodArray<z.ZodObject<{
|
|
33
|
+
id: z.ZodPipe<z.ZodString, z.ZodTransform<`debt/${string}`, string>>;
|
|
34
|
+
title: z.ZodString;
|
|
35
|
+
state: z.ZodEnum<{
|
|
36
|
+
open: "open";
|
|
37
|
+
done: "done";
|
|
38
|
+
drop: "drop";
|
|
39
|
+
}>;
|
|
40
|
+
priority: z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>;
|
|
41
|
+
disposition: z.ZodEnum<{
|
|
42
|
+
done: "done";
|
|
43
|
+
drop: "drop";
|
|
44
|
+
ready: "ready";
|
|
45
|
+
blocked: "blocked";
|
|
46
|
+
}>;
|
|
47
|
+
updatedAt: z.ZodString;
|
|
48
|
+
bodyPresent: z.ZodBoolean;
|
|
49
|
+
children: z.ZodOptional<z.ZodObject<{
|
|
50
|
+
live: z.ZodNumber;
|
|
51
|
+
total: z.ZodNumber;
|
|
52
|
+
}, z.core.$strict>>;
|
|
53
|
+
}, z.core.$strict>>>;
|
|
54
|
+
export type DebtDisposition = z.infer<typeof debtRowSchema>["disposition"];
|
|
55
|
+
export type DebtRow = z.infer<typeof debtRowSchema>;
|
|
56
|
+
export type BlockedDebtRow = DebtRow & Readonly<{
|
|
57
|
+
blockers: readonly DebtRef[];
|
|
58
|
+
}>;
|
|
59
|
+
export type DebtDetailFacts = Readonly<{
|
|
60
|
+
debt: DebtDocument;
|
|
61
|
+
needs: readonly (DebtRef & Readonly<{
|
|
62
|
+
released: boolean;
|
|
63
|
+
}>)[];
|
|
64
|
+
blockers: readonly DebtRef[];
|
|
65
|
+
blocks: readonly DebtRef[];
|
|
66
|
+
parent: DebtRef | null;
|
|
67
|
+
children: readonly DebtRef[];
|
|
68
|
+
supersedes: readonly DebtRef[];
|
|
69
|
+
supersededBy: readonly DebtRef[];
|
|
70
|
+
related: readonly DebtRef[];
|
|
71
|
+
}>;
|
|
72
|
+
export type DebtTreeNode = Readonly<{
|
|
73
|
+
debt: DebtRef & Readonly<{
|
|
74
|
+
priority: DebtPriority | null;
|
|
75
|
+
}>;
|
|
76
|
+
cycle?: true;
|
|
77
|
+
children: readonly DebtTreeNode[];
|
|
78
|
+
}>;
|
|
79
|
+
export type DebtBoard = Readonly<{
|
|
80
|
+
debts: ReadonlyMap<DebtId, DebtDocument>;
|
|
81
|
+
}>;
|
|
82
|
+
/** The local ids occupied across one namespace of the current board. */
|
|
83
|
+
export declare function occupiedLocalIds(board: DebtBoard, namespace: readonly string[]): Set<string>;
|
|
84
|
+
type DebtRelation = "needs" | "parent" | "supersedes" | "relates";
|
|
85
|
+
export type DebtDoctorIssue = Readonly<{
|
|
86
|
+
kind: "missing-target";
|
|
87
|
+
debtId: DebtId;
|
|
88
|
+
relation: DebtRelation;
|
|
89
|
+
target: DebtId;
|
|
90
|
+
}> | Readonly<{
|
|
91
|
+
kind: "self-relation";
|
|
92
|
+
debtId: DebtId;
|
|
93
|
+
relation: DebtRelation;
|
|
94
|
+
}> | Readonly<{
|
|
95
|
+
kind: "cycle";
|
|
96
|
+
relation: "needs" | "parent" | "supersedes";
|
|
97
|
+
debts: readonly DebtId[];
|
|
98
|
+
}>;
|
|
99
|
+
export declare function debtRef(board: DebtBoard, id: DebtId): DebtRef;
|
|
100
|
+
export type DebtRelationProjection = Readonly<{
|
|
101
|
+
children(id: DebtId): readonly DebtRef[];
|
|
102
|
+
blocks(id: DebtId): readonly DebtRef[];
|
|
103
|
+
supersededBy(id: DebtId): readonly DebtRef[];
|
|
104
|
+
related(id: DebtId): readonly DebtRef[];
|
|
105
|
+
}>;
|
|
106
|
+
export declare function createDebtRelations(board: DebtBoard): DebtRelationProjection;
|
|
107
|
+
export declare function debtDisposition(board: DebtBoard, debt: DebtDocument): DebtDisposition;
|
|
108
|
+
export declare function debtBlocked(board: DebtBoard, debt: DebtDocument): boolean;
|
|
109
|
+
export declare function projectOwnedDetailFacts(board: DebtBoard, id: DebtId): DebtDetailFacts | null;
|
|
110
|
+
export declare function projectDetailFacts(board: DebtBoard, id: DebtId, relations: DebtRelationProjection): DebtDetailFacts | null;
|
|
111
|
+
export declare function projectDebtRow(board: DebtBoard, relations: DebtRelationProjection, debt: DebtDocument): DebtRow;
|
|
112
|
+
export declare function projectRows(board: DebtBoard, relations: DebtRelationProjection, scope: readonly string[] | null, selection: "active" | "closed" | "all"): readonly DebtRow[];
|
|
113
|
+
/**
|
|
114
|
+
* Rows for the identities that created work, grouped by creator. Only documents a
|
|
115
|
+
* requested identity created reach row projection, and each group keeps the
|
|
116
|
+
* established row order.
|
|
117
|
+
*/
|
|
118
|
+
export declare function projectCreatedByRows(board: DebtBoard, relations: DebtRelationProjection, createdByIds: readonly string[]): ReadonlyMap<string, readonly DebtRow[]>;
|
|
119
|
+
export declare function projectBlocked(board: DebtBoard, scope: readonly string[] | null, relations: DebtRelationProjection): readonly BlockedDebtRow[];
|
|
120
|
+
export declare function projectStatusRows(board: DebtBoard, relations: DebtRelationProjection, scope: readonly string[] | null): ({
|
|
121
|
+
id: `debt/${string}`;
|
|
122
|
+
title: string;
|
|
123
|
+
state: "open" | "done" | "drop";
|
|
124
|
+
priority: 0 | 1 | 2 | 3;
|
|
125
|
+
disposition: "done" | "drop" | "ready" | "blocked";
|
|
126
|
+
updatedAt: string;
|
|
127
|
+
bodyPresent: boolean;
|
|
128
|
+
children?: {
|
|
129
|
+
live: number;
|
|
130
|
+
total: number;
|
|
131
|
+
} | undefined;
|
|
132
|
+
} | {
|
|
133
|
+
blockers: readonly Readonly<{
|
|
134
|
+
id: DebtId;
|
|
135
|
+
title: string | null;
|
|
136
|
+
state: DebtState | "missing";
|
|
137
|
+
}>[];
|
|
138
|
+
id: `debt/${string}`;
|
|
139
|
+
title: string;
|
|
140
|
+
state: "open" | "done" | "drop";
|
|
141
|
+
priority: 0 | 1 | 2 | 3;
|
|
142
|
+
disposition: "done" | "drop" | "ready" | "blocked";
|
|
143
|
+
updatedAt: string;
|
|
144
|
+
bodyPresent: boolean;
|
|
145
|
+
children?: {
|
|
146
|
+
live: number;
|
|
147
|
+
total: number;
|
|
148
|
+
} | undefined;
|
|
149
|
+
})[];
|
|
150
|
+
export type DebtBoardObservation = Readonly<{
|
|
151
|
+
statusRows: ReturnType<typeof projectStatusRows>;
|
|
152
|
+
selectRecentStatus(limit?: number): BoundedList<ReturnType<typeof projectStatusRows>[number]>;
|
|
153
|
+
selectNamespace(namespace: readonly string[]): readonly DebtRow[];
|
|
154
|
+
selectCreatedBy(createdBy: string): readonly DebtRow[];
|
|
155
|
+
}>;
|
|
156
|
+
export declare function projectDebtBoardObservation(board: DebtBoard): DebtBoardObservation;
|
|
157
|
+
export declare function relationProblem(board: DebtBoard, before: DebtDocument | null, after: DebtDocument): string | null;
|
|
158
|
+
export declare function diagnoseBoard(board: DebtBoard): readonly DebtDoctorIssue[];
|
|
159
|
+
export declare function buildTree(board: DebtBoard, root: DebtId, relations: DebtRelationProjection): DebtTreeNode | null;
|
|
160
|
+
export {};
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { debtNonblankTextSchema, debtStateSchema, debtPrioritySchema, } from "./document.js";
|
|
2
|
+
import { debtIdSchema, parseDebtId, sameNamespace } from "./identity.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { boundedListLimit, projectBoundedList } from "../bounded-list.js";
|
|
5
|
-
const
|
|
6
|
-
export const
|
|
5
|
+
const debtRowTimestampSchema = z.string().refine((value) => Number.isFinite(Date.parse(value)), "expected timestamp");
|
|
6
|
+
export const debtRowSchema = z
|
|
7
7
|
.object({
|
|
8
|
-
id:
|
|
9
|
-
title:
|
|
10
|
-
state:
|
|
11
|
-
priority:
|
|
12
|
-
disposition: z.enum(["ready", "blocked", "
|
|
13
|
-
updatedAt:
|
|
8
|
+
id: debtIdSchema,
|
|
9
|
+
title: debtNonblankTextSchema,
|
|
10
|
+
state: debtStateSchema,
|
|
11
|
+
priority: debtPrioritySchema,
|
|
12
|
+
disposition: z.enum(["ready", "blocked", "done", "drop"]),
|
|
13
|
+
updatedAt: debtRowTimestampSchema,
|
|
14
14
|
bodyPresent: z.boolean(),
|
|
15
15
|
children: z
|
|
16
16
|
.object({ live: z.number().int().nonnegative(), total: z.number().int().nonnegative() })
|
|
@@ -18,20 +18,20 @@ export const taskRowSchema = z
|
|
|
18
18
|
.optional(),
|
|
19
19
|
})
|
|
20
20
|
.strict();
|
|
21
|
-
export const
|
|
21
|
+
export const debtRowsSchema = z.array(debtRowSchema).readonly();
|
|
22
22
|
/** The local ids occupied across one namespace of the current board. */
|
|
23
23
|
export function occupiedLocalIds(board, namespace) {
|
|
24
|
-
return new Set([...board.
|
|
25
|
-
const coordinate =
|
|
24
|
+
return new Set([...board.debts.values()].flatMap((debt) => {
|
|
25
|
+
const coordinate = parseDebtId(debt.id);
|
|
26
26
|
return sameNamespace(coordinate.namespace, namespace) ? [coordinate.localId] : [];
|
|
27
27
|
}));
|
|
28
28
|
}
|
|
29
29
|
const terminal = (state) => state === "done" || state === "drop";
|
|
30
30
|
const none = Object.freeze([]);
|
|
31
31
|
const sorted = (values) => [...values].sort((a, b) => Buffer.compare(Buffer.from(a.id), Buffer.from(b.id)));
|
|
32
|
-
export function
|
|
33
|
-
const
|
|
34
|
-
return
|
|
32
|
+
export function debtRef(board, id) {
|
|
33
|
+
const debt = board.debts.get(id);
|
|
34
|
+
return debt === undefined ? { id, title: null, state: "missing" } : { id, title: debt.title, state: debt.state };
|
|
35
35
|
}
|
|
36
36
|
function append(index, key, value) {
|
|
37
37
|
const values = index.get(key);
|
|
@@ -41,22 +41,22 @@ function append(index, key, value) {
|
|
|
41
41
|
values.push(value);
|
|
42
42
|
}
|
|
43
43
|
function freezeRefs(board, index) {
|
|
44
|
-
return new Map([...index].map(([id, targets]) => [id, sorted(targets.map((target) =>
|
|
44
|
+
return new Map([...index].map(([id, targets]) => [id, sorted(targets.map((target) => debtRef(board, target)))]));
|
|
45
45
|
}
|
|
46
|
-
export function
|
|
46
|
+
export function createDebtRelations(board) {
|
|
47
47
|
const children = new Map();
|
|
48
48
|
const blocks = new Map();
|
|
49
49
|
const supersededBy = new Map();
|
|
50
50
|
const incoming = new Map();
|
|
51
|
-
for (const
|
|
52
|
-
if (
|
|
53
|
-
append(children,
|
|
54
|
-
for (const need of
|
|
55
|
-
append(blocks, need,
|
|
56
|
-
for (const target of
|
|
57
|
-
append(supersededBy, target,
|
|
58
|
-
for (const target of
|
|
59
|
-
append(incoming, target,
|
|
51
|
+
for (const debt of board.debts.values()) {
|
|
52
|
+
if (debt.parent !== null)
|
|
53
|
+
append(children, debt.parent, debt.id);
|
|
54
|
+
for (const need of debt.needs)
|
|
55
|
+
append(blocks, need, debt.id);
|
|
56
|
+
for (const target of debt.supersedes)
|
|
57
|
+
append(supersededBy, target, debt.id);
|
|
58
|
+
for (const target of debt.relates)
|
|
59
|
+
append(incoming, target, debt.id);
|
|
60
60
|
}
|
|
61
61
|
const childRefs = freezeRefs(board, children);
|
|
62
62
|
const blockRefs = freezeRefs(board, blocks);
|
|
@@ -67,44 +67,44 @@ export function createTaskRelations(board) {
|
|
|
67
67
|
blocks: (id) => blockRefs.get(id) ?? none,
|
|
68
68
|
supersededBy: (id) => successorRefs.get(id) ?? none,
|
|
69
69
|
related: (id) => {
|
|
70
|
-
const
|
|
71
|
-
const ids = new Set(
|
|
70
|
+
const debt = board.debts.get(id);
|
|
71
|
+
const ids = new Set(debt?.relates ?? []);
|
|
72
72
|
for (const ref of incomingRefs.get(id) ?? none)
|
|
73
73
|
ids.add(ref.id);
|
|
74
|
-
return ids.size === 0 ? none : sorted([...ids].map((related) =>
|
|
74
|
+
return ids.size === 0 ? none : sorted([...ids].map((related) => debtRef(board, related)));
|
|
75
75
|
},
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
function needReleased(board, id) {
|
|
79
|
-
const target = board.
|
|
79
|
+
const target = board.debts.get(id);
|
|
80
80
|
return target !== undefined && terminal(target.state);
|
|
81
81
|
}
|
|
82
|
-
function hasUnresolvedNeeds(board,
|
|
83
|
-
return
|
|
82
|
+
function hasUnresolvedNeeds(board, debt) {
|
|
83
|
+
return debt.needs.some((id) => !needReleased(board, id));
|
|
84
84
|
}
|
|
85
|
-
export function
|
|
86
|
-
if (
|
|
87
|
-
return
|
|
88
|
-
return hasUnresolvedNeeds(board,
|
|
85
|
+
export function debtDisposition(board, debt) {
|
|
86
|
+
if (debt.state !== "open")
|
|
87
|
+
return debt.state;
|
|
88
|
+
return hasUnresolvedNeeds(board, debt) ? "blocked" : "ready";
|
|
89
89
|
}
|
|
90
|
-
export function
|
|
91
|
-
return
|
|
90
|
+
export function debtBlocked(board, debt) {
|
|
91
|
+
return debt.state === "open" && hasUnresolvedNeeds(board, debt);
|
|
92
92
|
}
|
|
93
93
|
export function projectOwnedDetailFacts(board, id) {
|
|
94
|
-
const
|
|
95
|
-
if (
|
|
94
|
+
const debt = board.debts.get(id);
|
|
95
|
+
if (debt === undefined)
|
|
96
96
|
return null;
|
|
97
|
-
const needs =
|
|
97
|
+
const needs = debt.needs.map((need) => ({ ...debtRef(board, need), released: needReleased(board, need) }));
|
|
98
98
|
return {
|
|
99
|
-
|
|
99
|
+
debt,
|
|
100
100
|
needs,
|
|
101
101
|
blockers: needs.filter((need) => !need.released).map(({ released: _released, ...ref }) => ref),
|
|
102
102
|
blocks: none,
|
|
103
|
-
parent:
|
|
103
|
+
parent: debt.parent === null ? null : debtRef(board, debt.parent),
|
|
104
104
|
children: none,
|
|
105
|
-
supersedes:
|
|
105
|
+
supersedes: debt.supersedes.map((target) => debtRef(board, target)),
|
|
106
106
|
supersededBy: none,
|
|
107
|
-
related:
|
|
107
|
+
related: debt.relates.length === 0 ? none : sorted(debt.relates.map((target) => debtRef(board, target))),
|
|
108
108
|
};
|
|
109
109
|
}
|
|
110
110
|
export function projectDetailFacts(board, id, relations) {
|
|
@@ -119,33 +119,33 @@ export function projectDetailFacts(board, id, relations) {
|
|
|
119
119
|
related: relations.related(id),
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
|
-
function inScope(
|
|
123
|
-
return scope === null || sameNamespace(
|
|
122
|
+
function inScope(debt, scope) {
|
|
123
|
+
return scope === null || sameNamespace(parseDebtId(debt.id).namespace, scope);
|
|
124
124
|
}
|
|
125
|
-
export function
|
|
126
|
-
const children = relations.children(
|
|
125
|
+
export function projectDebtRow(board, relations, debt) {
|
|
126
|
+
const children = relations.children(debt.id);
|
|
127
127
|
const total = children.length;
|
|
128
128
|
const live = children.filter((child) => child.state !== "done" && child.state !== "drop").length;
|
|
129
129
|
return {
|
|
130
|
-
id:
|
|
131
|
-
title:
|
|
132
|
-
state:
|
|
133
|
-
priority:
|
|
134
|
-
disposition:
|
|
135
|
-
updatedAt:
|
|
136
|
-
bodyPresent:
|
|
130
|
+
id: debt.id,
|
|
131
|
+
title: debt.title,
|
|
132
|
+
state: debt.state,
|
|
133
|
+
priority: debt.priority,
|
|
134
|
+
disposition: debtDisposition(board, debt),
|
|
135
|
+
updatedAt: debt.updatedAt,
|
|
136
|
+
bodyPresent: debt.body.length > 0,
|
|
137
137
|
...(total === 0 ? {} : { children: { live, total } }),
|
|
138
138
|
};
|
|
139
139
|
}
|
|
140
140
|
export function projectRows(board, relations, scope, selection) {
|
|
141
|
-
return [...board.
|
|
142
|
-
.filter((
|
|
143
|
-
.filter((
|
|
144
|
-
.map((
|
|
145
|
-
.sort(
|
|
141
|
+
return [...board.debts.values()]
|
|
142
|
+
.filter((debt) => inScope(debt, scope))
|
|
143
|
+
.filter((debt) => selection === "all" || (selection === "closed") === terminal(debt.state))
|
|
144
|
+
.map((debt) => projectDebtRow(board, relations, debt))
|
|
145
|
+
.sort(compareDebtRows);
|
|
146
146
|
}
|
|
147
|
-
/** The established
|
|
148
|
-
function
|
|
147
|
+
/** The established Debt row order: priority first, then canonical identity bytes. */
|
|
148
|
+
function compareDebtRows(left, right) {
|
|
149
149
|
return left.priority - right.priority || Buffer.compare(Buffer.from(left.id), Buffer.from(right.id));
|
|
150
150
|
}
|
|
151
151
|
/**
|
|
@@ -156,13 +156,13 @@ function compareTaskRows(left, right) {
|
|
|
156
156
|
export function projectCreatedByRows(board, relations, createdByIds) {
|
|
157
157
|
const requested = new Set(createdByIds);
|
|
158
158
|
const attributed = [];
|
|
159
|
-
for (const
|
|
160
|
-
const createdBy =
|
|
159
|
+
for (const debt of board.debts.values()) {
|
|
160
|
+
const createdBy = debt.createdBy;
|
|
161
161
|
if (createdBy === undefined || !requested.has(createdBy))
|
|
162
162
|
continue;
|
|
163
|
-
attributed.push({ createdBy, row:
|
|
163
|
+
attributed.push({ createdBy, row: projectDebtRow(board, relations, debt) });
|
|
164
164
|
}
|
|
165
|
-
attributed.sort((left, right) =>
|
|
165
|
+
attributed.sort((left, right) => compareDebtRows(left.row, right.row));
|
|
166
166
|
const grouped = new Map();
|
|
167
167
|
for (const { createdBy, row } of attributed) {
|
|
168
168
|
const rows = grouped.get(createdBy);
|
|
@@ -175,7 +175,7 @@ export function projectCreatedByRows(board, relations, createdByIds) {
|
|
|
175
175
|
}
|
|
176
176
|
export function projectBlocked(board, scope, relations) {
|
|
177
177
|
return projectRows(board, relations, scope, "active").flatMap((row) => {
|
|
178
|
-
if (!
|
|
178
|
+
if (!debtBlocked(board, board.debts.get(row.id)))
|
|
179
179
|
return [];
|
|
180
180
|
return [{ ...row, blockers: projectDetailFacts(board, row.id, relations).blockers }];
|
|
181
181
|
});
|
|
@@ -187,8 +187,8 @@ export function projectStatusRows(board, relations, scope) {
|
|
|
187
187
|
return unresolved === undefined ? row : { ...row, blockers: unresolved };
|
|
188
188
|
});
|
|
189
189
|
}
|
|
190
|
-
export function
|
|
191
|
-
const relations =
|
|
190
|
+
export function projectDebtBoardObservation(board) {
|
|
191
|
+
const relations = createDebtRelations(board);
|
|
192
192
|
const rows = projectRows(board, relations, null, "all");
|
|
193
193
|
const statusRows = projectStatusRows(board, relations, null);
|
|
194
194
|
return {
|
|
@@ -198,11 +198,11 @@ export function projectTaskBoardObservation(board) {
|
|
|
198
198
|
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt) ||
|
|
199
199
|
Buffer.compare(Buffer.from(left.id), Buffer.from(right.id))), boundedListLimit(limit)),
|
|
200
200
|
selectNamespace: (namespace) => projectRows(board, relations, namespace, "all"),
|
|
201
|
-
selectCreatedBy: (createdBy) => rows.filter((row) => board.
|
|
201
|
+
selectCreatedBy: (createdBy) => rows.filter((row) => board.debts.get(row.id)?.createdBy === createdBy),
|
|
202
202
|
};
|
|
203
203
|
}
|
|
204
|
-
function relationIds(
|
|
205
|
-
return relation === "parent" ? (
|
|
204
|
+
function relationIds(debt, relation) {
|
|
205
|
+
return relation === "parent" ? (debt.parent === null ? [] : [debt.parent]) : debt[relation];
|
|
206
206
|
}
|
|
207
207
|
export function relationProblem(board, before, after) {
|
|
208
208
|
for (const relation of ["needs", "parent", "supersedes", "relates"]) {
|
|
@@ -212,15 +212,15 @@ export function relationProblem(board, before, after) {
|
|
|
212
212
|
continue;
|
|
213
213
|
if (target === after.id)
|
|
214
214
|
return `${after.id} has a self ${relation} relation`;
|
|
215
|
-
if (!board.
|
|
215
|
+
if (!board.debts.has(target))
|
|
216
216
|
return `${after.id} ${relation} target does not exist: ${target}`;
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
return null;
|
|
220
220
|
}
|
|
221
221
|
function cycleComponents(board, relation) {
|
|
222
|
-
const ids = sorted([...board.
|
|
223
|
-
const edges = new Map(ids.map((id) => [id, relationIds(board.
|
|
222
|
+
const ids = sorted([...board.debts.keys()].map((id) => ({ id }))).map(({ id }) => id);
|
|
223
|
+
const edges = new Map(ids.map((id) => [id, relationIds(board.debts.get(id), relation).filter((target) => board.debts.has(target))]));
|
|
224
224
|
const reverse = new Map(ids.map((id) => [id, []]));
|
|
225
225
|
for (const [id, targets] of edges)
|
|
226
226
|
for (const target of targets)
|
|
@@ -268,28 +268,28 @@ function cycleComponents(board, relation) {
|
|
|
268
268
|
}
|
|
269
269
|
export function diagnoseBoard(board) {
|
|
270
270
|
const issues = [];
|
|
271
|
-
for (const
|
|
271
|
+
for (const debt of sorted(board.debts.values()))
|
|
272
272
|
for (const relation of ["needs", "parent", "supersedes", "relates"]) {
|
|
273
|
-
for (const target of relationIds(
|
|
274
|
-
if (target ===
|
|
275
|
-
issues.push({ kind: "self-relation",
|
|
276
|
-
else if (!board.
|
|
277
|
-
issues.push({ kind: "missing-target",
|
|
273
|
+
for (const target of relationIds(debt, relation)) {
|
|
274
|
+
if (target === debt.id)
|
|
275
|
+
issues.push({ kind: "self-relation", debtId: debt.id, relation });
|
|
276
|
+
else if (!board.debts.has(target))
|
|
277
|
+
issues.push({ kind: "missing-target", debtId: debt.id, relation, target });
|
|
278
278
|
}
|
|
279
279
|
}
|
|
280
280
|
for (const relation of ["needs", "parent", "supersedes"]) {
|
|
281
|
-
for (const
|
|
282
|
-
issues.push({ kind: "cycle", relation,
|
|
281
|
+
for (const debts of cycleComponents(board, relation))
|
|
282
|
+
issues.push({ kind: "cycle", relation, debts });
|
|
283
283
|
}
|
|
284
284
|
return issues;
|
|
285
285
|
}
|
|
286
286
|
export function buildTree(board, root, relations) {
|
|
287
|
-
if (!board.
|
|
287
|
+
if (!board.debts.has(root))
|
|
288
288
|
return null;
|
|
289
289
|
const walk = (id, path) => {
|
|
290
|
-
const ref =
|
|
291
|
-
const
|
|
292
|
-
const node = {
|
|
290
|
+
const ref = debtRef(board, id);
|
|
291
|
+
const debt = board.debts.get(id);
|
|
292
|
+
const node = { debt: { ...ref, priority: debt?.priority ?? null } };
|
|
293
293
|
if (path.includes(id))
|
|
294
294
|
return { ...node, cycle: true, children: [] };
|
|
295
295
|
return { ...node, children: relations.children(id).map((child) => walk(child.id, [...path, id])) };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BoundedList } from "../bounded-list.js";
|
|
2
|
+
import type { DebtRef, DebtRow } from "./board.js";
|
|
3
|
+
import type { WorldRoot } from "../world.js";
|
|
4
|
+
export declare function parseDebtNamespaceSelector(value: string): readonly string[];
|
|
5
|
+
export type RecentDebtStatusRow = DebtRow & Readonly<{
|
|
6
|
+
blockers?: readonly DebtRef[];
|
|
7
|
+
}>;
|
|
8
|
+
export declare function observeRecentDebtStatus(world: WorldRoot, input?: Readonly<{
|
|
9
|
+
namespace?: readonly string[];
|
|
10
|
+
limit?: number;
|
|
11
|
+
}>): Promise<BoundedList<RecentDebtStatusRow>>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { isDebtSegment, parseDebtId } from "./identity.js";
|
|
2
|
+
import { readRecentDebtDocuments, readDebtDocument } from "./store.js";
|
|
3
|
+
export function parseDebtNamespaceSelector(value) {
|
|
4
|
+
if (value === "debt/")
|
|
5
|
+
return [];
|
|
6
|
+
if (!value.startsWith("debt/") || !value.endsWith("/")) {
|
|
7
|
+
throw new TypeError(`invalid Debt namespace selector: ${value}`);
|
|
8
|
+
}
|
|
9
|
+
const body = value.slice("debt/".length, -1);
|
|
10
|
+
const segments = body.split("/");
|
|
11
|
+
if (body.length === 0 || !segments.every(isDebtSegment)) {
|
|
12
|
+
throw new TypeError(`invalid Debt namespace selector: ${value}`);
|
|
13
|
+
}
|
|
14
|
+
return parseDebtId(`debt/${body}/placeholder`).namespace;
|
|
15
|
+
}
|
|
16
|
+
function terminal(debt) {
|
|
17
|
+
return debt?.state === "done" || debt?.state === "drop";
|
|
18
|
+
}
|
|
19
|
+
async function catalogRow(world, debt, documents) {
|
|
20
|
+
const needs = await Promise.all(debt.needs.map(async (id) => documents.get(id) ?? (await readDebtDocument(world, id))));
|
|
21
|
+
const blockers = [];
|
|
22
|
+
if (debt.state === "open") {
|
|
23
|
+
for (const [index, need] of needs.entries()) {
|
|
24
|
+
if (terminal(need))
|
|
25
|
+
continue;
|
|
26
|
+
blockers.push({ id: debt.needs[index], title: need?.title ?? null, state: need?.state ?? "missing" });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
id: debt.id,
|
|
31
|
+
title: debt.title,
|
|
32
|
+
state: debt.state,
|
|
33
|
+
priority: debt.priority,
|
|
34
|
+
disposition: debt.state === "open" && needs.some((need) => !terminal(need))
|
|
35
|
+
? "blocked"
|
|
36
|
+
: debt.state === "open"
|
|
37
|
+
? "ready"
|
|
38
|
+
: debt.state,
|
|
39
|
+
updatedAt: debt.updatedAt,
|
|
40
|
+
bodyPresent: debt.body.length > 0,
|
|
41
|
+
...(blockers.length === 0 ? {} : { blockers }),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export async function observeRecentDebtStatus(world, input = {}) {
|
|
45
|
+
const { namespace } = input;
|
|
46
|
+
if (namespace !== undefined && !namespace.every(isDebtSegment)) {
|
|
47
|
+
throw new TypeError("namespace must contain canonical segments");
|
|
48
|
+
}
|
|
49
|
+
const documents = await readRecentDebtDocuments(world, {
|
|
50
|
+
...(namespace === undefined ? {} : { namespace }),
|
|
51
|
+
selection: "active",
|
|
52
|
+
...(input.limit === undefined ? {} : { limit: input.limit }),
|
|
53
|
+
});
|
|
54
|
+
const known = new Map(documents.rows.map((document) => [document.id, document]));
|
|
55
|
+
return {
|
|
56
|
+
rows: await Promise.all(documents.rows.map((document) => catalogRow(world, document, known))),
|
|
57
|
+
hasMore: documents.hasMore,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -15,7 +15,7 @@ export type ResolvedNamespaceContext = Readonly<{
|
|
|
15
15
|
export declare function readNamespaceContext(coordinates: NamespaceContextInput): Promise<NamespaceContextRead>;
|
|
16
16
|
export declare function installNamespaceContext(root: string, segments: readonly string[]): Promise<void>;
|
|
17
17
|
export declare function repairNamespaceContext(root: string, segments: readonly string[]): Promise<"kept" | "installed">;
|
|
18
|
-
export declare function
|
|
18
|
+
export declare function resolveDebtNamespaceContext(input: NamespaceContextInput): Promise<ResolvedNamespaceContext | Readonly<{
|
|
19
19
|
kind: "invalid-namespace-context";
|
|
20
20
|
path: string;
|
|
21
21
|
}>>;
|
|
@@ -80,7 +80,7 @@ export async function repairNamespaceContext(root, segments) {
|
|
|
80
80
|
await installNamespaceContext(root, segments);
|
|
81
81
|
return "installed";
|
|
82
82
|
}
|
|
83
|
-
export async function
|
|
83
|
+
export async function resolveDebtNamespaceContext(input) {
|
|
84
84
|
let current = input.directory;
|
|
85
85
|
for (;;) {
|
|
86
86
|
const selected = await readAtWithPath(current);
|
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
import type { WorldRoot } from "../world.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
export declare const
|
|
3
|
+
export declare const createdDebtObservationSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
4
4
|
kind: z.ZodLiteral<"present">;
|
|
5
5
|
rows: z.ZodReadonly<z.ZodArray<z.ZodObject<{
|
|
6
|
-
id: z.ZodPipe<z.ZodString, z.ZodTransform<`
|
|
6
|
+
id: z.ZodPipe<z.ZodString, z.ZodTransform<`debt/${string}`, string>>;
|
|
7
7
|
title: z.ZodString;
|
|
8
8
|
state: z.ZodEnum<{
|
|
9
9
|
open: "open";
|
|
10
|
-
in_progress: "in_progress";
|
|
11
|
-
on_hold: "on_hold";
|
|
12
10
|
done: "done";
|
|
13
11
|
drop: "drop";
|
|
14
12
|
}>;
|
|
15
13
|
priority: z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>, z.ZodLiteral<2>, z.ZodLiteral<3>]>;
|
|
16
14
|
disposition: z.ZodEnum<{
|
|
17
|
-
in_progress: "in_progress";
|
|
18
|
-
on_hold: "on_hold";
|
|
19
15
|
done: "done";
|
|
20
16
|
drop: "drop";
|
|
21
17
|
ready: "ready";
|
|
@@ -32,11 +28,11 @@ export declare const createdTaskObservationSchema: z.ZodUnion<readonly [z.ZodObj
|
|
|
32
28
|
kind: z.ZodLiteral<"failed">;
|
|
33
29
|
diagnostic: z.ZodString;
|
|
34
30
|
}, z.core.$strict>]>;
|
|
35
|
-
export type
|
|
36
|
-
export declare const
|
|
31
|
+
export type CreatedDebtObservation = z.infer<typeof createdDebtObservationSchema>;
|
|
32
|
+
export declare const EMPTY_CREATED_DEBT_OBSERVATION: CreatedDebtObservation;
|
|
37
33
|
/**
|
|
38
|
-
* Project
|
|
34
|
+
* Project Debt board rows created by each identity, preserving input order. One complete
|
|
39
35
|
* board and one relationship projection answer every requested identity, and only the
|
|
40
36
|
* documents those identities created reach row projection.
|
|
41
37
|
*/
|
|
42
|
-
export declare function
|
|
38
|
+
export declare function observeCreatedDebtObservations(world: WorldRoot, createdByIds: readonly string[]): Promise<readonly CreatedDebtObservation[]>;
|