@workflow-cannon/workspace-kit 0.15.0 → 0.16.1
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 +55 -106
- package/dist/cli/doctor-planning-issues.d.ts +6 -0
- package/dist/cli/doctor-planning-issues.js +37 -0
- package/dist/cli.js +3 -0
- package/dist/core/config-metadata.js +56 -1
- package/dist/modules/approvals/review-runtime.js +3 -11
- package/dist/modules/improvement/generate-recommendations-runtime.js +3 -11
- package/dist/modules/task-engine/doctor-planning-persistence.d.ts +9 -0
- package/dist/modules/task-engine/doctor-planning-persistence.js +77 -0
- package/dist/modules/task-engine/index.d.ts +2 -0
- package/dist/modules/task-engine/index.js +44 -46
- package/dist/modules/task-engine/migrate-task-persistence-runtime.d.ts +2 -0
- package/dist/modules/task-engine/migrate-task-persistence-runtime.js +192 -0
- package/dist/modules/task-engine/planning-config.d.ts +10 -0
- package/dist/modules/task-engine/planning-config.js +37 -0
- package/dist/modules/task-engine/planning-open.d.ts +16 -0
- package/dist/modules/task-engine/planning-open.js +34 -0
- package/dist/modules/task-engine/sqlite-dual-planning.d.ts +21 -0
- package/dist/modules/task-engine/sqlite-dual-planning.js +137 -0
- package/dist/modules/task-engine/store.d.ts +12 -3
- package/dist/modules/task-engine/store.js +62 -38
- package/dist/modules/task-engine/wishlist-store.d.ts +12 -3
- package/dist/modules/task-engine/wishlist-store.js +62 -40
- package/package.json +11 -2
- package/src/modules/documentation/README.md +1 -0
- package/src/modules/documentation/instructions/document-project.md +1 -0
- package/src/modules/documentation/instructions/generate-document.md +1 -0
- package/src/modules/documentation/templates/README.md +89 -0
package/README.md
CHANGED
|
@@ -1,136 +1,85 @@
|
|
|
1
1
|
AI agents: `./.ai/` is the authoritative source of truth; read all files there before any analysis, planning, or code changes, and follow `./.ai/` over `README.md`, `./docs/`, and conflicting existing code patterns.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
It is built for developers using VS Code who want safe, reproducible, package-first workflow automation with clear release evidence.
|
|
8
|
-
|
|
9
|
-
## Table of Contents
|
|
10
|
-
|
|
11
|
-
- [What This Repository Is](#what-this-repository-is)
|
|
12
|
-
- [Big-Picture Vision](#big-picture-vision)
|
|
13
|
-
- [Current Status](#current-status)
|
|
14
|
-
- [Goals](#goals)
|
|
15
|
-
- [Package](#package)
|
|
16
|
-
- [Repository Map](#repository-map)
|
|
17
|
-
- [Documentation Index](#documentation-index)
|
|
18
|
-
- [License](#license)
|
|
19
|
-
|
|
20
|
-
## What This Repository Is
|
|
3
|
+
<div align="center">
|
|
4
|
+
<img src="title_image.png" alt="Workflow Cannon" width="720" />
|
|
5
|
+
</div>
|
|
21
6
|
|
|
22
|
-
Workflow Cannon
|
|
23
|
-
|
|
24
|
-
- The `@workflow-cannon/workspace-kit` package
|
|
25
|
-
- Maintainer planning and execution artifacts
|
|
26
|
-
- Consumer validation and release-readiness evidence
|
|
27
|
-
|
|
28
|
-
Guiding characteristics:
|
|
7
|
+
# Workflow Cannon
|
|
29
8
|
|
|
30
|
-
-
|
|
31
|
-
- Deterministic, auditable workflows
|
|
32
|
-
- Safe-by-default operations (validation, traceability, rollback-friendly changes)
|
|
9
|
+
**[`@workflow-cannon/workspace-kit`](https://www.npmjs.com/package/@workflow-cannon/workspace-kit)** — CLI, task engine, and workflow contracts for repos that want deterministic, policy-governed automation with clear evidence.
|
|
33
10
|
|
|
34
|
-
##
|
|
11
|
+
## Quick start (clone this repo)
|
|
35
12
|
|
|
36
|
-
|
|
13
|
+
**Needs:** Node.js **22+** (see CI), **pnpm 10** (see `packageManager` in `package.json`).
|
|
37
14
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
-
|
|
15
|
+
```bash
|
|
16
|
+
git clone https://github.com/NJLaPrell/workflow-cannon.git
|
|
17
|
+
cd workflow-cannon
|
|
18
|
+
pnpm install
|
|
19
|
+
pnpm run build
|
|
20
|
+
```
|
|
41
21
|
|
|
42
|
-
|
|
22
|
+
Verify the kit sees your workspace:
|
|
43
23
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
24
|
+
```bash
|
|
25
|
+
node dist/cli.js doctor
|
|
26
|
+
node dist/cli.js --help
|
|
27
|
+
```
|
|
47
28
|
|
|
48
|
-
|
|
29
|
+
Try **read-only** task-engine queries:
|
|
49
30
|
|
|
50
|
-
|
|
31
|
+
```bash
|
|
32
|
+
node dist/cli.js run list-tasks '{}'
|
|
33
|
+
node dist/cli.js run get-next-actions '{}'
|
|
34
|
+
```
|
|
51
35
|
|
|
52
|
-
|
|
36
|
+
**Developing:** after edits, `pnpm run build` then `pnpm test` (or `pnpm run phase5-gates` before larger changes). If `workspace-kit` is not on your `PATH`, use `node dist/cli.js …` from the repo root (same as above).
|
|
53
37
|
|
|
54
|
-
|
|
55
|
-
- emit recommendation items with confidence, deduping, and provenance
|
|
56
|
-
- route recommendations through an approval queue (`accept`, `decline`, `accept edited`)
|
|
57
|
-
- apply approved changes through guarded automation (dry-run, diff, rollback-ready)
|
|
58
|
-
- measure post-change outcomes so future recommendations improve over time
|
|
38
|
+
## Quick start (use the package in another project)
|
|
59
39
|
|
|
60
|
-
|
|
40
|
+
```bash
|
|
41
|
+
npm install @workflow-cannon/workspace-kit
|
|
42
|
+
npx workspace-kit --help
|
|
43
|
+
```
|
|
61
44
|
|
|
62
|
-
|
|
45
|
+
Or with pnpm: `pnpm add @workflow-cannon/workspace-kit` then `pnpm exec workspace-kit --help`.
|
|
63
46
|
|
|
64
|
-
|
|
47
|
+
## What this repo contains
|
|
65
48
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
49
|
+
| Area | What |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| **CLI** | `workspace-kit` — `doctor`, `config`, `run <module-command>` (see `workspace-kit run` with no args for the list). |
|
|
52
|
+
| **Task engine** | Canonical queue in `.workspace-kit/tasks/state.json`; lifecycle via `run-transition`. Wishlist ideation uses ids `W###` (see [`docs/maintainers/runbooks/wishlist-workflow.md`](docs/maintainers/runbooks/wishlist-workflow.md)). |
|
|
53
|
+
| **Docs** | Maintainer process, roadmap, and changelog under `docs/maintainers/`. |
|
|
54
|
+
| **Cursor extension** (optional) | Thin UI in `extensions/cursor-workflow-cannon/` — build with `pnpm run ui:prepare`. |
|
|
72
55
|
|
|
73
|
-
|
|
56
|
+
There is **no** built-in IDE slash command like `/qt` from this package; editor integrations are **your** config (e.g. `.cursor/commands/`), while **`workspace-kit`** is the supported CLI.
|
|
74
57
|
|
|
75
|
-
##
|
|
58
|
+
## Policy and approvals (read this before mutating state)
|
|
76
59
|
|
|
77
|
-
-
|
|
78
|
-
- Preserve independent consumer validation and update cadence.
|
|
79
|
-
- Grow modular capabilities for planning, tasking, configuration, policy, and improvement.
|
|
80
|
-
- Build a human-governed enhancement loop that learns from usage and recommends better workflows/rules.
|
|
81
|
-
- Maintain deterministic and auditable behavior as system complexity increases.
|
|
60
|
+
Sensitive `workspace-kit run` commands require JSON **`policyApproval`** in the third CLI argument. Chat approval is not enough. Env-based approval applies to `init` / `upgrade` / `config`, not the `run` path.
|
|
82
61
|
|
|
83
|
-
|
|
62
|
+
- **Human guide:** [`docs/maintainers/POLICY-APPROVAL.md`](docs/maintainers/POLICY-APPROVAL.md)
|
|
63
|
+
- **Copy-paste table:** [`docs/maintainers/AGENT-CLI-MAP.md`](docs/maintainers/AGENT-CLI-MAP.md)
|
|
84
64
|
|
|
85
|
-
|
|
65
|
+
## Project status and roadmap
|
|
86
66
|
|
|
87
|
-
|
|
88
|
-
npm install @workflow-cannon/workspace-kit
|
|
89
|
-
```
|
|
67
|
+
Release cadence, phase history, and strategic decisions: [`docs/maintainers/ROADMAP.md`](docs/maintainers/ROADMAP.md). **Live execution queue:** `.workspace-kit/tasks/state.json` (`status` and `id` are authoritative — not this README’s milestone bullets).
|
|
90
68
|
|
|
91
|
-
|
|
69
|
+
Snapshot: [`docs/maintainers/data/workspace-kit-status.yaml`](docs/maintainers/data/workspace-kit-status.yaml).
|
|
92
70
|
|
|
93
|
-
|
|
71
|
+
## Where to go next
|
|
94
72
|
|
|
95
|
-
|
|
|
73
|
+
| Goal | Start here |
|
|
96
74
|
| --- | --- |
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
- `README.md` - project entry point
|
|
106
|
-
- `.ai/PRINCIPLES.md` - project goals and decision principles (canonical AI)
|
|
107
|
-
- `docs/maintainers/ROADMAP.md` - roadmap and decision log
|
|
108
|
-
- `.workspace-kit/tasks/state.json` - execution tracking
|
|
109
|
-
- `docs/maintainers/ARCHITECTURE.md` - architecture direction
|
|
110
|
-
- `docs/maintainers/DECISIONS.md` - focused design/decision notes
|
|
111
|
-
- `docs/maintainers/RELEASING.md` - release checklist and validation expectations
|
|
112
|
-
- `.ai/module-build.md` - canonical AI module build guidance
|
|
113
|
-
- `docs/maintainers/` - maintainer process and boundary docs
|
|
114
|
-
- `docs/maintainers/module-build-guide.md` - human-readable module build guidance
|
|
115
|
-
- `docs/adr/` - ADR templates and records
|
|
116
|
-
|
|
117
|
-
## Documentation Index
|
|
118
|
-
|
|
119
|
-
- Project goals and decision principles: `.ai/PRINCIPLES.md`
|
|
120
|
-
- Strategy and long-range direction: `docs/maintainers/ROADMAP.md`
|
|
121
|
-
- Active execution tasks: `.workspace-kit/tasks/state.json`
|
|
122
|
-
- Glossary and agent-guidance terms: `docs/maintainers/TERMS.md`
|
|
123
|
-
- Architecture direction: `docs/maintainers/ARCHITECTURE.md`
|
|
124
|
-
- Project decisions: `docs/maintainers/DECISIONS.md`
|
|
125
|
-
- Governance policy surface: `docs/maintainers/GOVERNANCE.md`
|
|
126
|
-
- Release process and gates: `docs/maintainers/RELEASING.md`
|
|
127
|
-
- Policy / approval surfaces: `docs/maintainers/POLICY-APPROVAL.md`
|
|
128
|
-
- Canonical changelog: `docs/maintainers/CHANGELOG.md` (`CHANGELOG.md` at repo root is pointer-only)
|
|
129
|
-
- Canonical AI module build guidance: `.ai/module-build.md`
|
|
130
|
-
- Human module build guide: `docs/maintainers/module-build-guide.md`
|
|
131
|
-
- Security, support, and governance: `docs/maintainers/SECURITY.md`, `docs/maintainers/SUPPORT.md`, `docs/maintainers/GOVERNANCE.md`
|
|
132
|
-
- AI behavior rules and command wrappers: `.cursor/rules/`, `.cursor/commands/`
|
|
75
|
+
| Goals, trade-offs, gates | [`.ai/PRINCIPLES.md`](.ai/PRINCIPLES.md) |
|
|
76
|
+
| Roadmap & versions | [`docs/maintainers/ROADMAP.md`](docs/maintainers/ROADMAP.md) |
|
|
77
|
+
| Changelog | [`docs/maintainers/CHANGELOG.md`](docs/maintainers/CHANGELOG.md) |
|
|
78
|
+
| Release process | [`docs/maintainers/RELEASING.md`](docs/maintainers/RELEASING.md) |
|
|
79
|
+
| Glossary | [`docs/maintainers/TERMS.md`](docs/maintainers/TERMS.md) |
|
|
80
|
+
| Architecture | [`docs/maintainers/ARCHITECTURE.md`](docs/maintainers/ARCHITECTURE.md) |
|
|
81
|
+
| Agent/CLI execution | [`docs/maintainers/AGENTS.md`](docs/maintainers/AGENTS.md) |
|
|
133
82
|
|
|
134
83
|
## License
|
|
135
84
|
|
|
136
|
-
|
|
85
|
+
MIT. See [`LICENSE`](LICENSE).
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type DoctorPlanningIssue = {
|
|
2
|
+
path: string;
|
|
3
|
+
reason: string;
|
|
4
|
+
};
|
|
5
|
+
/** Resolve layered config and run SQLite planning persistence checks for `workspace-kit doctor`. */
|
|
6
|
+
export declare function collectDoctorPlanningPersistenceIssues(cwd: string): Promise<DoctorPlanningIssue[]>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ModuleRegistry } from "../core/module-registry.js";
|
|
2
|
+
import { resolveWorkspaceConfigWithLayers } from "../core/workspace-kit-config.js";
|
|
3
|
+
import { validatePlanningPersistenceForDoctor } from "../modules/task-engine/doctor-planning-persistence.js";
|
|
4
|
+
import { documentationModule } from "../modules/documentation/index.js";
|
|
5
|
+
import { taskEngineModule } from "../modules/task-engine/index.js";
|
|
6
|
+
import { approvalsModule } from "../modules/approvals/index.js";
|
|
7
|
+
import { planningModule } from "../modules/planning/index.js";
|
|
8
|
+
import { improvementModule } from "../modules/improvement/index.js";
|
|
9
|
+
import { workspaceConfigModule } from "../modules/workspace-config/index.js";
|
|
10
|
+
const defaultRegistryModules = [
|
|
11
|
+
workspaceConfigModule,
|
|
12
|
+
documentationModule,
|
|
13
|
+
taskEngineModule,
|
|
14
|
+
approvalsModule,
|
|
15
|
+
planningModule,
|
|
16
|
+
improvementModule
|
|
17
|
+
];
|
|
18
|
+
/** Resolve layered config and run SQLite planning persistence checks for `workspace-kit doctor`. */
|
|
19
|
+
export async function collectDoctorPlanningPersistenceIssues(cwd) {
|
|
20
|
+
try {
|
|
21
|
+
const registry = new ModuleRegistry(defaultRegistryModules);
|
|
22
|
+
const { effective } = await resolveWorkspaceConfigWithLayers({
|
|
23
|
+
workspacePath: cwd,
|
|
24
|
+
registry,
|
|
25
|
+
invocationConfig: {}
|
|
26
|
+
});
|
|
27
|
+
return validatePlanningPersistenceForDoctor(cwd, effective);
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
return [
|
|
31
|
+
{
|
|
32
|
+
path: "workspace-config",
|
|
33
|
+
reason: `config-resolution-failed: ${err.message}`
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ import { pathToFileURL } from "node:url";
|
|
|
5
5
|
import { AGENT_CLI_MAP_HUMAN_DOC, appendPolicyTrace, parsePolicyApprovalFromEnv, resolveActorWithFallback } from "./core/policy.js";
|
|
6
6
|
import { runWorkspaceConfigCli } from "./core/config-cli.js";
|
|
7
7
|
import { handleRunCommand } from "./cli/run-command.js";
|
|
8
|
+
import { collectDoctorPlanningPersistenceIssues } from "./cli/doctor-planning-issues.js";
|
|
8
9
|
const EXIT_SUCCESS = 0;
|
|
9
10
|
const EXIT_VALIDATION_FAILURE = 1;
|
|
10
11
|
const EXIT_USAGE_ERROR = 2;
|
|
@@ -606,6 +607,7 @@ export async function runCli(args, options = {}) {
|
|
|
606
607
|
});
|
|
607
608
|
}
|
|
608
609
|
}
|
|
610
|
+
issues.push(...(await collectDoctorPlanningPersistenceIssues(cwd)));
|
|
609
611
|
if (issues.length > 0) {
|
|
610
612
|
writeError("workspace-kit doctor failed validation.");
|
|
611
613
|
for (const issue of issues) {
|
|
@@ -615,6 +617,7 @@ export async function runCli(args, options = {}) {
|
|
|
615
617
|
}
|
|
616
618
|
writeLine("workspace-kit doctor passed.");
|
|
617
619
|
writeLine("All canonical workspace-kit contract files are present and parseable JSON.");
|
|
620
|
+
writeLine("Effective workspace config resolved; task planning persistence checks passed (including SQLite when configured).");
|
|
618
621
|
writeLine(`Next: workspace-kit run — list module commands; see ${AGENT_CLI_MAP_HUMAN_DOC} for tier/policy copy-paste.`);
|
|
619
622
|
return EXIT_SUCCESS;
|
|
620
623
|
}
|
|
@@ -16,6 +16,46 @@ const REGISTRY = {
|
|
|
16
16
|
exposure: "public",
|
|
17
17
|
writableLayers: ["project", "user"]
|
|
18
18
|
},
|
|
19
|
+
"tasks.wishlistStoreRelativePath": {
|
|
20
|
+
key: "tasks.wishlistStoreRelativePath",
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Relative path (from workspace root) to the Wishlist JSON store when persistenceBackend is json.",
|
|
23
|
+
default: ".workspace-kit/wishlist/state.json",
|
|
24
|
+
domainScope: "project",
|
|
25
|
+
owningModule: "task-engine",
|
|
26
|
+
sensitive: false,
|
|
27
|
+
requiresRestart: false,
|
|
28
|
+
requiresApproval: false,
|
|
29
|
+
exposure: "public",
|
|
30
|
+
writableLayers: ["project", "user"]
|
|
31
|
+
},
|
|
32
|
+
"tasks.persistenceBackend": {
|
|
33
|
+
key: "tasks.persistenceBackend",
|
|
34
|
+
type: "string",
|
|
35
|
+
description: "Task + wishlist persistence: json (default) or sqlite.",
|
|
36
|
+
default: "json",
|
|
37
|
+
allowedValues: ["json", "sqlite"],
|
|
38
|
+
domainScope: "project",
|
|
39
|
+
owningModule: "task-engine",
|
|
40
|
+
sensitive: false,
|
|
41
|
+
requiresRestart: false,
|
|
42
|
+
requiresApproval: false,
|
|
43
|
+
exposure: "public",
|
|
44
|
+
writableLayers: ["project", "user"]
|
|
45
|
+
},
|
|
46
|
+
"tasks.sqliteDatabaseRelativePath": {
|
|
47
|
+
key: "tasks.sqliteDatabaseRelativePath",
|
|
48
|
+
type: "string",
|
|
49
|
+
description: "Relative path (from workspace root) to the SQLite file when persistenceBackend is sqlite.",
|
|
50
|
+
default: ".workspace-kit/tasks/workspace-kit.db",
|
|
51
|
+
domainScope: "project",
|
|
52
|
+
owningModule: "task-engine",
|
|
53
|
+
sensitive: false,
|
|
54
|
+
requiresRestart: false,
|
|
55
|
+
requiresApproval: false,
|
|
56
|
+
exposure: "public",
|
|
57
|
+
writableLayers: ["project", "user"]
|
|
58
|
+
},
|
|
19
59
|
"policy.extraSensitiveModuleCommands": {
|
|
20
60
|
key: "policy.extraSensitiveModuleCommands",
|
|
21
61
|
type: "array",
|
|
@@ -315,10 +355,25 @@ export function validatePersistedConfigDocument(data, label) {
|
|
|
315
355
|
}
|
|
316
356
|
const t = tasks;
|
|
317
357
|
for (const k of Object.keys(t)) {
|
|
318
|
-
if (k !== "storeRelativePath"
|
|
358
|
+
if (k !== "storeRelativePath" &&
|
|
359
|
+
k !== "wishlistStoreRelativePath" &&
|
|
360
|
+
k !== "persistenceBackend" &&
|
|
361
|
+
k !== "sqliteDatabaseRelativePath") {
|
|
319
362
|
throw new Error(`config-invalid(${label}): unknown tasks.${k}`);
|
|
320
363
|
}
|
|
321
364
|
}
|
|
365
|
+
if (t.storeRelativePath !== undefined) {
|
|
366
|
+
validateValueForMetadata(REGISTRY["tasks.storeRelativePath"], t.storeRelativePath);
|
|
367
|
+
}
|
|
368
|
+
if (t.wishlistStoreRelativePath !== undefined) {
|
|
369
|
+
validateValueForMetadata(REGISTRY["tasks.wishlistStoreRelativePath"], t.wishlistStoreRelativePath);
|
|
370
|
+
}
|
|
371
|
+
if (t.persistenceBackend !== undefined) {
|
|
372
|
+
validateValueForMetadata(REGISTRY["tasks.persistenceBackend"], t.persistenceBackend);
|
|
373
|
+
}
|
|
374
|
+
if (t.sqliteDatabaseRelativePath !== undefined) {
|
|
375
|
+
validateValueForMetadata(REGISTRY["tasks.sqliteDatabaseRelativePath"], t.sqliteDatabaseRelativePath);
|
|
376
|
+
}
|
|
322
377
|
}
|
|
323
378
|
const policy = data.policy;
|
|
324
379
|
if (policy !== undefined) {
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { appendLineageEvent } from "../../core/lineage-store.js";
|
|
2
|
-
import {
|
|
2
|
+
import { openPlanningStores } from "../task-engine/planning-open.js";
|
|
3
3
|
import { TransitionService } from "../task-engine/service.js";
|
|
4
4
|
import { appendDecisionRecord, computeDecisionFingerprint, readDecisionFingerprints } from "./decisions-store.js";
|
|
5
|
-
function taskStoreRelativePath(ctx) {
|
|
6
|
-
const tasks = ctx.effectiveConfig?.tasks;
|
|
7
|
-
if (!tasks || typeof tasks !== "object" || Array.isArray(tasks)) {
|
|
8
|
-
return undefined;
|
|
9
|
-
}
|
|
10
|
-
const p = tasks.storeRelativePath;
|
|
11
|
-
return typeof p === "string" && p.trim().length > 0 ? p.trim() : undefined;
|
|
12
|
-
}
|
|
13
5
|
function getEvidenceKey(task) {
|
|
14
6
|
const m = task.metadata;
|
|
15
7
|
const k = m && typeof m.evidenceKey === "string" ? m.evidenceKey : "";
|
|
@@ -24,8 +16,8 @@ export async function runReviewItem(ctx, args, actor) {
|
|
|
24
16
|
if (decision === "accept_edited" && !(typeof args.editedSummary === "string" && args.editedSummary.trim())) {
|
|
25
17
|
return { ok: false, code: "invalid-args", message: "accept_edited requires non-empty editedSummary" };
|
|
26
18
|
}
|
|
27
|
-
const
|
|
28
|
-
|
|
19
|
+
const planning = await openPlanningStores(ctx);
|
|
20
|
+
const store = planning.taskStore;
|
|
29
21
|
const task = store.getTask(taskId);
|
|
30
22
|
if (!task) {
|
|
31
23
|
return { ok: false, code: "task-not-found", message: `Task '${taskId}' not found` };
|
|
@@ -1,17 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { openPlanningStores } from "../task-engine/planning-open.js";
|
|
3
3
|
import { appendLineageEvent } from "../../core/lineage-store.js";
|
|
4
4
|
import { loadImprovementState, saveImprovementState } from "./improvement-state.js";
|
|
5
5
|
import { ingestAgentTranscripts, ingestConfigMutations, ingestGitDiffBetweenTags, ingestPolicyDenials, ingestTaskTransitionFriction, taskIdForEvidenceKey } from "./ingest.js";
|
|
6
6
|
import { priorityForTier } from "./confidence.js";
|
|
7
|
-
function taskStoreRelativePath(ctx) {
|
|
8
|
-
const tasks = ctx.effectiveConfig?.tasks;
|
|
9
|
-
if (!tasks || typeof tasks !== "object" || Array.isArray(tasks)) {
|
|
10
|
-
return undefined;
|
|
11
|
-
}
|
|
12
|
-
const p = tasks.storeRelativePath;
|
|
13
|
-
return typeof p === "string" && p.trim().length > 0 ? p.trim() : undefined;
|
|
14
|
-
}
|
|
15
7
|
function hasEvidenceKey(tasks, key) {
|
|
16
8
|
return tasks.some((t) => {
|
|
17
9
|
const m = t.metadata;
|
|
@@ -48,8 +40,8 @@ export function getMaxRecommendationCandidatesPerRun(ctx) {
|
|
|
48
40
|
}
|
|
49
41
|
export async function runGenerateRecommendations(ctx, args) {
|
|
50
42
|
const runId = randomUUID();
|
|
51
|
-
const
|
|
52
|
-
|
|
43
|
+
const planning = await openPlanningStores(ctx);
|
|
44
|
+
const store = planning.taskStore;
|
|
53
45
|
const state = await loadImprovementState(ctx.workspacePath);
|
|
54
46
|
const transcriptsRoot = resolveTranscriptArchivePath(ctx, args);
|
|
55
47
|
const fromTag = typeof args.fromTag === "string" ? args.fromTag.trim() : undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type DoctorPlanningIssue = {
|
|
2
|
+
path: string;
|
|
3
|
+
reason: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* When effective config selects SQLite for task/wishlist persistence, verify the DB file exists
|
|
7
|
+
* and can be opened read-only; if a planning row is present, validate embedded JSON schemaVersion.
|
|
8
|
+
*/
|
|
9
|
+
export declare function validatePlanningPersistenceForDoctor(workspacePath: string, effectiveConfig: Record<string, unknown>): DoctorPlanningIssue[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import Database from "better-sqlite3";
|
|
4
|
+
import { getTaskPersistenceBackend, planningSqliteDatabaseRelativePath } from "./planning-config.js";
|
|
5
|
+
/**
|
|
6
|
+
* When effective config selects SQLite for task/wishlist persistence, verify the DB file exists
|
|
7
|
+
* and can be opened read-only; if a planning row is present, validate embedded JSON schemaVersion.
|
|
8
|
+
*/
|
|
9
|
+
export function validatePlanningPersistenceForDoctor(workspacePath, effectiveConfig) {
|
|
10
|
+
const issues = [];
|
|
11
|
+
if (getTaskPersistenceBackend(effectiveConfig) !== "sqlite") {
|
|
12
|
+
return issues;
|
|
13
|
+
}
|
|
14
|
+
const ctx = { workspacePath, effectiveConfig };
|
|
15
|
+
const dbRel = planningSqliteDatabaseRelativePath(ctx);
|
|
16
|
+
const dbAbs = path.resolve(workspacePath, dbRel);
|
|
17
|
+
const relDisplay = path.relative(workspacePath, dbAbs) || dbAbs;
|
|
18
|
+
if (!fs.existsSync(dbAbs)) {
|
|
19
|
+
issues.push({
|
|
20
|
+
path: relDisplay,
|
|
21
|
+
reason: "sqlite-planning-db-missing (tasks.persistenceBackend is sqlite; run migrate-task-persistence json-to-sqlite or fix tasks.sqliteDatabaseRelativePath)"
|
|
22
|
+
});
|
|
23
|
+
return issues;
|
|
24
|
+
}
|
|
25
|
+
let db;
|
|
26
|
+
try {
|
|
27
|
+
db = new Database(dbAbs, { readonly: true });
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
issues.push({
|
|
31
|
+
path: relDisplay,
|
|
32
|
+
reason: `sqlite-open-failed: ${err.message}`
|
|
33
|
+
});
|
|
34
|
+
return issues;
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const row = db
|
|
38
|
+
.prepare("SELECT task_store_json, wishlist_store_json FROM workspace_planning_state WHERE id = 1")
|
|
39
|
+
.get();
|
|
40
|
+
if (row) {
|
|
41
|
+
try {
|
|
42
|
+
const taskDoc = JSON.parse(row.task_store_json);
|
|
43
|
+
if (taskDoc.schemaVersion !== 1) {
|
|
44
|
+
issues.push({
|
|
45
|
+
path: relDisplay,
|
|
46
|
+
reason: `sqlite-task_store_json: unsupported schemaVersion (expected 1, got ${taskDoc.schemaVersion})`
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
issues.push({ path: relDisplay, reason: "sqlite-task_store_json: invalid JSON" });
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const wishDoc = JSON.parse(row.wishlist_store_json);
|
|
55
|
+
if (wishDoc.schemaVersion !== 1) {
|
|
56
|
+
issues.push({
|
|
57
|
+
path: relDisplay,
|
|
58
|
+
reason: `sqlite-wishlist_store_json: unsupported schemaVersion (expected 1, got ${wishDoc.schemaVersion})`
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
issues.push({ path: relDisplay, reason: "sqlite-wishlist_store_json: invalid JSON" });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
issues.push({
|
|
69
|
+
path: relDisplay,
|
|
70
|
+
reason: `sqlite-schema-invalid: ${err.message}`
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
db.close();
|
|
75
|
+
}
|
|
76
|
+
return issues;
|
|
77
|
+
}
|
|
@@ -8,4 +8,6 @@ export { readWorkspaceStatusSnapshot } from "./dashboard-status.js";
|
|
|
8
8
|
export { WishlistStore } from "./wishlist-store.js";
|
|
9
9
|
export type { WishlistItem, WishlistStatus, WishlistStoreDocument } from "./wishlist-types.js";
|
|
10
10
|
export { validateWishlistIntakePayload, validateWishlistUpdatePayload, buildWishlistItemFromIntake, WISHLIST_ID_RE } from "./wishlist-validation.js";
|
|
11
|
+
export { openPlanningStores } from "./planning-open.js";
|
|
12
|
+
export { getTaskPersistenceBackend, planningSqliteDatabaseRelativePath, planningTaskStoreRelativePath, planningWishlistStoreRelativePath } from "./planning-config.js";
|
|
11
13
|
export declare const taskEngineModule: WorkflowModule;
|