akm-cli 0.9.17-alpha.6 → 0.9.17-alpha.7
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/CHANGELOG.md +146 -0
- package/STABILITY.md +2 -2
- package/dist/akm +7 -7
- package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
- package/dist/commands/improve/consolidate.js +11 -0
- package/dist/commands/improve/improve-cli.js +27 -7
- package/dist/commands/improve/ledger.js +7 -3
- package/dist/commands/improve/preparation.js +40 -10
- package/dist/commands/improve/reflect.js +46 -22
- package/dist/commands/improve/retrieval-gate.js +127 -0
- package/dist/commands/improve/retrieval-scope.js +77 -0
- package/dist/commands/read/curate.js +1 -17
- package/dist/commands/tasks/tasks-cli.js +10 -12
- package/dist/commands/tasks/tasks.js +57 -56
- package/dist/commands/tasks/validate.js +27 -46
- package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
- package/dist/core/improve-result.js +4 -1
- package/dist/core/non-task-input.js +20 -0
- package/dist/core/paths.js +0 -4
- package/dist/indexer/indexer.js +1 -3
- package/dist/indexer/usage/usage-events.js +34 -0
- package/dist/scripts/akm-migrate-node.js +5822 -5833
- package/dist/scripts/akm-migrate.js +6301 -6312
- package/dist/storage/repositories/proposals-repository.js +4 -0
- package/dist/tasks/backends/cron.js +80 -43
- package/dist/tasks/backends/launchd.js +28 -15
- package/dist/tasks/backends/schtasks.js +25 -10
- package/dist/tasks/run/load-task.js +1 -1
- package/dist/tasks/scheduler-binding.js +4 -2
- package/dist/tasks/scheduler-invocation.js +127 -235
- package/dist/tasks/scheduler-sync.js +13 -8
- package/dist/tasks/source/parse-task-source.js +22 -126
- package/dist/tasks/source/task-to-v3.js +1 -55
- package/dist/tasks/source/task-to-v4.js +1 -13
- package/docs/migration/v0.9.1-to-v0.9.2.md +7 -3
- package/docs/reference/cli.md +4 -3
- package/docs/reference/tasks.md +58 -40
- package/package.json +1 -1
|
@@ -5,59 +5,32 @@
|
|
|
5
5
|
* The task source version router.
|
|
6
6
|
*
|
|
7
7
|
* Runs the bounded YAML front end ONCE (`readBoundedTaskSourceYaml`), reads
|
|
8
|
-
* `root.version`, and
|
|
9
|
-
*
|
|
10
|
-
* grammar itself, no re-serialization back to disk, no synthetic document
|
|
11
|
-
* (the shim adds a pure bytes-in/bytes-out detour, never a disk write).
|
|
12
|
-
*
|
|
13
|
-
* The terminal routing table:
|
|
8
|
+
* `root.version`, and dispatches into `parseTaskSourceV4Document`. The
|
|
9
|
+
* runtime reads only task source v4 (#987):
|
|
14
10
|
*
|
|
15
11
|
* | root `version` | outcome |
|
|
16
12
|
* |------------------------|-----------------------------------------------------------------|
|
|
17
|
-
* | `4
|
|
18
|
-
* | `4`, some `schedule[]` entry carries `enabled`
|
|
19
|
-
* | `2` or `3` |
|
|
13
|
+
* | `4` | `parseTaskSourceV4Document` — the current grammar |
|
|
14
|
+
* | `4`, some `schedule[]` entry carries `enabled` (0.9.15's grammar) | `TASK_SOURCE_INVALID` naming `akm migrate apply`, which removes the key |
|
|
15
|
+
* | `2` or `3` | `TASK_SCHEMA_VERSION_UNSUPPORTED` naming `akm migrate apply`, which converts the file |
|
|
20
16
|
* | any other number | `TASK_SCHEMA_VERSION_UNSUPPORTED`, naming the migrator |
|
|
21
17
|
* | absent / not a number | `parseTaskSourceV4Document` — its own `TASK_SOURCE_INVALID` "version is required and must be 4" / "must be exactly 4" wording |
|
|
22
18
|
*
|
|
19
|
+
* `akm migrate apply` (`scripts/akm-migrate/`) is the one place a v2/v3
|
|
20
|
+
* document, or a v4 document carrying the retired `schedule[].enabled`, is
|
|
21
|
+
* converted: it rewrites the file once, under a backup. Nothing here converts
|
|
22
|
+
* in memory. Every caller reports the refusal for that one file — `akm task
|
|
23
|
+
* sync` keeps reconciling every other task (#867).
|
|
24
|
+
*
|
|
23
25
|
* A missing or non-numeric `version:` is a MALFORMED v4 document, not a
|
|
24
26
|
* legacy one — it routes into the v4 parser so the field error names the
|
|
25
|
-
* one grammar `src`
|
|
26
|
-
* message that would send the user to the migrator for a document that was
|
|
27
|
-
* never task v2 or v3 in the first place.
|
|
28
|
-
*
|
|
29
|
-
* task v2 and task v3 sources are no longer read as their own standing
|
|
30
|
-
* grammar anywhere else in `src` — the only readers of that grammar are the
|
|
31
|
-
* pure, byte-producing planners (`./task-to-v3.ts`, `./task-to-v4.ts`, and
|
|
32
|
-
* the frozen v3 reader `./task-source-v3-frozen.ts`), reached either through
|
|
33
|
-
* this shim (bytes in, bytes out, never touches disk) or through
|
|
34
|
-
* `akm migrate apply` / `akm-migrate` (`scripts/akm-migrate`, which
|
|
35
|
-
* additionally rewrites the file on disk once the user asks for that).
|
|
36
|
-
* Policy: a deterministic byte transform is the tool's job, not the user's —
|
|
37
|
-
* upgrading past a schema bump must not silently break a scheduled task, so
|
|
38
|
-
* v2/v3 files keep reading successfully at the cost of a one-line
|
|
39
|
-
* deprecation warning, and `akm migrate apply` remains available to rewrite
|
|
40
|
-
* the file and silence it. Activation itself is host-local (scheduler
|
|
41
|
-
* config, `src/core/activation-policy.ts`) and this shim never touches it:
|
|
42
|
-
* the v3->v4 planner never hoists the retired `akm.enabled` field to v4's
|
|
43
|
-
* top level and never carries a schedule entry's `enabled` key, so the
|
|
44
|
-
* document this shim hands back carries no enablement at all — the same
|
|
45
|
-
* shape a native v4 document has. A declared `version: 4` document that
|
|
46
|
-
* still carries a `schedule[].enabled` key (0.9.15's v4 grammar accepted
|
|
47
|
-
* it; this release's does not) gets the identical treatment: routed
|
|
48
|
-
* through `planTaskToV4File`'s own `version === 4` branch, which strips
|
|
49
|
-
* every `schedule[].enabled` key without reading its value and reports
|
|
50
|
-
* `source-enablement-removed`, then re-parsed and returned with the same
|
|
51
|
-
* one-line deprecation warning. The front end's own pre-version failures
|
|
27
|
+
* one grammar `src` accepts. The front end's own pre-version failures
|
|
52
28
|
* (source not a string, source too large, YAML parse/warning/expansion)
|
|
53
29
|
* render with the label `task source`.
|
|
54
30
|
*/
|
|
55
31
|
import { UsageError } from "../../core/errors.js";
|
|
56
|
-
import { warnOnce } from "../../core/warn.js";
|
|
57
32
|
import { readBoundedTaskSourceYaml } from "./bounded-document.js";
|
|
58
|
-
import {
|
|
59
|
-
import { planTaskToV3File } from "./task-to-v3.js";
|
|
60
|
-
import { planTaskToV4File } from "./task-to-v4.js";
|
|
33
|
+
import { parseTaskSourceV4Document, TASK_SOURCE_V4_VERSION } from "./task-source-v4.js";
|
|
61
34
|
/** Read the root `version` field without over-accepting non-number values (e.g. the string `"4"`). */
|
|
62
35
|
export function peekTaskSourceVersion(root) {
|
|
63
36
|
if (root === null || typeof root !== "object" || Array.isArray(root))
|
|
@@ -66,71 +39,19 @@ export function peekTaskSourceVersion(root) {
|
|
|
66
39
|
return typeof value === "number" ? value : undefined;
|
|
67
40
|
}
|
|
68
41
|
const TASK_MIGRATE_HINT = "Run `akm migrate apply --dry-run` to preview the task-v3 to task-source-v4 conversion, then run `akm migrate apply`.";
|
|
69
|
-
/**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
* behavior (e.g. an ambiguous shell command), not one the migrator can just
|
|
73
|
-
* be re-run to fix. `reason`/`detail` are the SAME blocked outcome
|
|
74
|
-
* `akm migrate status`/`apply` reports for this file, so the message names
|
|
75
|
-
* the actual decision instead of pointing at a command that will report the
|
|
76
|
-
* identical block.
|
|
77
|
-
*/
|
|
78
|
-
function unmigratableVersionError(filePath, version, reason, detail) {
|
|
79
|
-
return new UsageError(`TASK_SCHEMA_VERSION_UNSUPPORTED: Task at ${filePath} uses task schema version ${version} and needs a human decision before it can run — the deterministic migrator cannot convert it automatically (${reason}${detail ? `: ${detail}` : ""}).`, "TASK_SCHEMA_VERSION_UNSUPPORTED", "Review the file and resolve the ambiguity by hand, then it will convert normally; `akm migrate status` reports the same reason.");
|
|
42
|
+
/** A v2/v3 document: `akm migrate apply` converts it; this release reads only v4. */
|
|
43
|
+
function legacyVersionError(filePath, version) {
|
|
44
|
+
return new UsageError(`TASK_SCHEMA_VERSION_UNSUPPORTED: Task at ${filePath} uses task schema version ${version}; this release reads only version 4. Run \`akm migrate apply\` to convert it.`, "TASK_SCHEMA_VERSION_UNSUPPORTED", TASK_MIGRATE_HINT);
|
|
80
45
|
}
|
|
81
46
|
function unsupportedVersionError(filePath, version) {
|
|
82
47
|
return new UsageError(`TASK_SCHEMA_VERSION_UNSUPPORTED: Task at ${filePath} uses task schema version ${version}, which this release does not accept.`, "TASK_SCHEMA_VERSION_UNSUPPORTED", TASK_MIGRATE_HINT);
|
|
83
48
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Plan the SAME bytes already in hand through the pure v3->v4 (and, for v2,
|
|
86
|
-
* chained v2->v3->v4) migration planner(s) — never touches disk, never
|
|
87
|
-
* writes the file, never re-reads it from disk. `version === 4` runs the
|
|
88
|
-
* identical bytes straight through `planTaskToV4File`'s own `version === 4`
|
|
89
|
-
* branch instead (the retired `schedule[].enabled` case, `./task-to-v4.ts`),
|
|
90
|
-
* so there is exactly one helper for every version this shim reads, not a
|
|
91
|
-
* second one for the v4-only case. Returns the produced v4 YAML text, or the
|
|
92
|
-
* blocked reason/detail when the deterministic conversion cannot proceed
|
|
93
|
-
* (an unmigratable v2/v3 shape, or a v4 document `planTaskToV4File` cannot
|
|
94
|
-
* revalidate once `schedule[].enabled` is stripped) — the caller falls back
|
|
95
|
-
* to the same hard error this gate threw before the shim existed, now
|
|
96
|
-
* naming that reason.
|
|
97
|
-
*/
|
|
98
|
-
function planInMemoryV4Bytes(version, yaml, filePath, workspaceRoot) {
|
|
99
|
-
const bytes = Buffer.from(yaml, "utf8");
|
|
100
|
-
// `writable`/`onDiskWritable` gate the DISK apply path's "don't touch a
|
|
101
|
-
// read-only file" check inside the planners; this shim never writes
|
|
102
|
-
// anything to disk, so that check does not apply here and must not block
|
|
103
|
-
// an otherwise-legal read of a task file that happens to be read-only.
|
|
104
|
-
const baseInput = {
|
|
105
|
-
filePath,
|
|
106
|
-
bytes,
|
|
107
|
-
mode: 0o644,
|
|
108
|
-
writable: true,
|
|
109
|
-
onDiskWritable: true,
|
|
110
|
-
...(workspaceRoot ? { containmentRoot: workspaceRoot } : {}),
|
|
111
|
-
};
|
|
112
|
-
let v3Bytes;
|
|
113
|
-
if (version === 3 || version === 4) {
|
|
114
|
-
v3Bytes = bytes;
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
const v3Outcome = planTaskToV3File(baseInput);
|
|
118
|
-
if (v3Outcome.status !== "changed")
|
|
119
|
-
return { reason: v3Outcome.reason, detail: v3Outcome.detail };
|
|
120
|
-
v3Bytes = v3Outcome.after;
|
|
121
|
-
}
|
|
122
|
-
const v4Outcome = planTaskToV4File({ ...baseInput, bytes: v3Bytes });
|
|
123
|
-
if (v4Outcome.status !== "changed")
|
|
124
|
-
return { reason: v4Outcome.reason, detail: v4Outcome.detail };
|
|
125
|
-
return v4Outcome.after.toString("utf8");
|
|
126
|
-
}
|
|
127
49
|
/**
|
|
128
50
|
* True when `root`'s `schedule:` is a sequence with at least one mapping
|
|
129
51
|
* entry that carries an `enabled` key, regardless of that key's value or
|
|
130
52
|
* type — the shape 0.9.15's v4 grammar accepted and this release's does
|
|
131
|
-
* not (`schedule[].enabled`).
|
|
132
|
-
*
|
|
133
|
-
* decides whether to route through the in-memory shim.
|
|
53
|
+
* not (`schedule[].enabled`). Activation is host-local `scheduler.enabled`,
|
|
54
|
+
* so the value is never read; `akm migrate apply` removes the key.
|
|
134
55
|
*/
|
|
135
56
|
export function v4ScheduleHasRetiredEnabledKey(root) {
|
|
136
57
|
if (root === null || typeof root !== "object" || Array.isArray(root))
|
|
@@ -140,42 +61,17 @@ export function v4ScheduleHasRetiredEnabledKey(root) {
|
|
|
140
61
|
return false;
|
|
141
62
|
return schedule.some((entry) => entry !== null && typeof entry === "object" && !Array.isArray(entry) && Object.hasOwn(entry, "enabled"));
|
|
142
63
|
}
|
|
143
|
-
/** Parse task source YAML, routing per the
|
|
64
|
+
/** Parse task source YAML, routing per the table above. */
|
|
144
65
|
export function parseTaskSource(input) {
|
|
145
66
|
const { root, lineAt } = readBoundedTaskSourceYaml(input, { sourceLabel: "task source" });
|
|
146
67
|
const version = peekTaskSourceVersion(root);
|
|
147
68
|
if (version !== undefined && version !== TASK_SOURCE_V4_VERSION) {
|
|
148
|
-
if (version === 2 || version === 3)
|
|
149
|
-
|
|
150
|
-
if (typeof shimmed === "string") {
|
|
151
|
-
const v4 = parseTaskSourceV4({
|
|
152
|
-
yaml: shimmed,
|
|
153
|
-
filePath: input.filePath,
|
|
154
|
-
...(input.workspaceRoot ? { workspaceRoot: input.workspaceRoot } : {}),
|
|
155
|
-
});
|
|
156
|
-
warnOnce(`task-source:v${version}-shim:${input.filePath}`, `akm: task ${input.filePath} uses schema v${version} — auto-read as v4; run \`akm migrate apply\` to rewrite it and silence this`);
|
|
157
|
-
return Object.freeze({ version: 4, v4 });
|
|
158
|
-
}
|
|
159
|
-
throw unmigratableVersionError(input.filePath, version, shimmed.reason, shimmed.detail);
|
|
160
|
-
}
|
|
69
|
+
if (version === 2 || version === 3)
|
|
70
|
+
throw legacyVersionError(input.filePath, version);
|
|
161
71
|
throw unsupportedVersionError(input.filePath, version);
|
|
162
72
|
}
|
|
163
73
|
if (version === TASK_SOURCE_V4_VERSION && v4ScheduleHasRetiredEnabledKey(root)) {
|
|
164
|
-
|
|
165
|
-
if (typeof shimmed === "string") {
|
|
166
|
-
const v4 = parseTaskSourceV4({
|
|
167
|
-
yaml: shimmed,
|
|
168
|
-
filePath: input.filePath,
|
|
169
|
-
...(input.workspaceRoot ? { workspaceRoot: input.workspaceRoot } : {}),
|
|
170
|
-
});
|
|
171
|
-
warnOnce(`task-source:v4-schedule-enabled-shim:${input.filePath}`, `akm: task ${input.filePath} uses the retired schedule[].enabled field — auto-read with it ignored; run \`akm migrate apply\` to rewrite it and silence this`);
|
|
172
|
-
return Object.freeze({ version: 4, v4 });
|
|
173
|
-
}
|
|
174
|
-
// Version 4 is supported; a blocked outcome here means the file itself
|
|
175
|
-
// is malformed (`generated-v4-validation-failed`), not that this
|
|
176
|
-
// version needs a human decision — throw the v4 grammar error, not
|
|
177
|
-
// `unmigratableVersionError`.
|
|
178
|
-
throw new UsageError(shimmed.detail ?? shimmed.reason, "TASK_SOURCE_INVALID");
|
|
74
|
+
throw new UsageError(`Invalid task source v4 at ${input.filePath}: schedule[].enabled was removed (scheduler activation is host-local config). Run \`akm migrate apply\` to rewrite it.`, "TASK_SOURCE_INVALID", TASK_MIGRATE_HINT);
|
|
179
75
|
}
|
|
180
76
|
const documentOptions = {
|
|
181
77
|
filePath: input.filePath,
|
|
@@ -374,7 +374,7 @@ function isReason(value) {
|
|
|
374
374
|
return typeof value === "string";
|
|
375
375
|
}
|
|
376
376
|
/** Convert one already-normalized legacy record directly to final task v3. */
|
|
377
|
-
|
|
377
|
+
function planLegacyTaskDataToV3(input, data) {
|
|
378
378
|
if (data.version !== 2) {
|
|
379
379
|
return blocked(input, "unsupported-task-version", `expected normalized legacy version 2, got ${String(data.version)}`);
|
|
380
380
|
}
|
|
@@ -451,57 +451,3 @@ export function planTaskToV3File(input) {
|
|
|
451
451
|
}
|
|
452
452
|
return planLegacyTaskDataToV3(input, data);
|
|
453
453
|
}
|
|
454
|
-
function generationFor(files) {
|
|
455
|
-
const digest = crypto.createHash("sha256");
|
|
456
|
-
digest.update("akm-task-to-v3-plan-v1\0");
|
|
457
|
-
for (const file of files) {
|
|
458
|
-
digest.update(file.filePath);
|
|
459
|
-
digest.update("\0");
|
|
460
|
-
digest.update(file.status);
|
|
461
|
-
digest.update("\0");
|
|
462
|
-
digest.update(file.reason);
|
|
463
|
-
digest.update("\0");
|
|
464
|
-
digest.update(String(file.mode));
|
|
465
|
-
digest.update("\0");
|
|
466
|
-
digest.update(file.writable ? "writable" : "read-only");
|
|
467
|
-
digest.update("\0");
|
|
468
|
-
digest.update(file.onDiskWritable === false ? "disk-read-only" : "disk-writable-or-unspecified");
|
|
469
|
-
digest.update("\0");
|
|
470
|
-
if (file.containmentRoot)
|
|
471
|
-
digest.update(file.containmentRoot);
|
|
472
|
-
digest.update("\0");
|
|
473
|
-
digest.update(file.beforeHash);
|
|
474
|
-
digest.update("\0");
|
|
475
|
-
if (file.status === "changed")
|
|
476
|
-
digest.update(file.afterHash);
|
|
477
|
-
digest.update("\0");
|
|
478
|
-
if (file.detail)
|
|
479
|
-
digest.update(file.detail);
|
|
480
|
-
digest.update("\0");
|
|
481
|
-
}
|
|
482
|
-
return digest.digest("hex");
|
|
483
|
-
}
|
|
484
|
-
/** Build/fingerprint a plan from already-derived immutable outcomes. */
|
|
485
|
-
export function taskToV3PlanFromOutcomes(outcomes) {
|
|
486
|
-
const files = [...outcomes].sort((left, right) => left.filePath < right.filePath ? -1 : left.filePath > right.filePath ? 1 : 0);
|
|
487
|
-
for (let index = 1; index < files.length; index += 1) {
|
|
488
|
-
const previous = files[index - 1];
|
|
489
|
-
const current = files[index];
|
|
490
|
-
if (previous && current && path.resolve(previous.filePath) === path.resolve(current.filePath)) {
|
|
491
|
-
throw new Error(`duplicate task migration file path: ${current.filePath}`);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
return Object.freeze({ schemaVersion: 1, generation: generationFor(files), files: Object.freeze(files) });
|
|
495
|
-
}
|
|
496
|
-
/** Plan a complete, stable file set. Input order cannot change the result. */
|
|
497
|
-
export function planTaskToV3Migration(inputs) {
|
|
498
|
-
const sorted = [...inputs].sort((left, right) => left.filePath < right.filePath ? -1 : left.filePath > right.filePath ? 1 : 0);
|
|
499
|
-
let previous;
|
|
500
|
-
for (const current of sorted) {
|
|
501
|
-
if (previous && path.resolve(previous.filePath) === path.resolve(current.filePath)) {
|
|
502
|
-
throw new Error(`duplicate task migration file path: ${current.filePath}`);
|
|
503
|
-
}
|
|
504
|
-
previous = current;
|
|
505
|
-
}
|
|
506
|
-
return taskToV3PlanFromOutcomes(sorted.map(planTaskToV3File));
|
|
507
|
-
}
|
|
@@ -318,7 +318,7 @@ function planV3DataToV4(input, data) {
|
|
|
318
318
|
// `uses: workflows/` it was equally inert there — nothing ever consumed
|
|
319
319
|
// it. Hoisting it unconditionally would therefore emit bytes the real
|
|
320
320
|
// parseTaskSourceV4 below rejects, blocking a valid, previously-runnable
|
|
321
|
-
// v3 file from
|
|
321
|
+
// v3 file from `akm migrate apply`
|
|
322
322
|
// (scripts/akm-migrate/migrate/task-files.ts). Dropping an
|
|
323
323
|
// already-inert field and SAYING SO is the faithful translation, and
|
|
324
324
|
// keeps spec row B-66 / §5.3's `changed` guarantee intact.
|
|
@@ -473,15 +473,3 @@ export function taskToV4PlanFromOutcomes(outcomes) {
|
|
|
473
473
|
}
|
|
474
474
|
return Object.freeze({ schemaVersion: 1, generation: generationFor(files), files: Object.freeze(files) });
|
|
475
475
|
}
|
|
476
|
-
/** Plan a complete, stable file set. Input order cannot change the result. */
|
|
477
|
-
export function planTaskToV4Migration(inputs) {
|
|
478
|
-
const sorted = [...inputs].sort((left, right) => left.filePath < right.filePath ? -1 : left.filePath > right.filePath ? 1 : 0);
|
|
479
|
-
let previous;
|
|
480
|
-
for (const current of sorted) {
|
|
481
|
-
if (previous && path.resolve(previous.filePath) === path.resolve(current.filePath)) {
|
|
482
|
-
throw new Error(`duplicate task migration file path: ${current.filePath}`);
|
|
483
|
-
}
|
|
484
|
-
previous = current;
|
|
485
|
-
}
|
|
486
|
-
return taskToV4PlanFromOutcomes(sorted.map(planTaskToV4File));
|
|
487
|
-
}
|
|
@@ -57,7 +57,9 @@ pace: an unmigrated v2/v3 task source keeps reading and running through the
|
|
|
57
57
|
in-memory read shim, with a one-line deprecation warning, until you migrate
|
|
58
58
|
it (nothing silently breaks or half-runs, and only a genuinely unmigratable
|
|
59
59
|
shape fails closed with an actionable hint), and pre-`irVersion`-5 runs stay
|
|
60
|
-
inspectable indefinitely.
|
|
60
|
+
inspectable indefinitely. (0.9.17-alpha.7 removed that shim: a v2/v3 task
|
|
61
|
+
source now fails on its own, naming `akm migrate apply`, which `akm upgrade`
|
|
62
|
+
runs after an install.)
|
|
61
63
|
|
|
62
64
|
## Task sources
|
|
63
65
|
|
|
@@ -72,7 +74,9 @@ apply` remains the way to rewrite the file on disk and silence the warning.
|
|
|
72
74
|
current grammar — extended the same kind of shim to a `version: 4` document
|
|
73
75
|
whose `schedule[]` still carries a per-entry `enabled` key, retired from
|
|
74
76
|
task source v4's own grammar since; the field is stripped without being
|
|
75
|
-
read, never re-added.
|
|
77
|
+
read, never re-added. 0.9.17-alpha.7 removed both shims: akm now reads only
|
|
78
|
+
task source v4, and each older file fails on its own, naming
|
|
79
|
+
`akm migrate apply`.)
|
|
76
80
|
|
|
77
81
|
**Before — 0.9.1 task v2:**
|
|
78
82
|
|
|
@@ -679,7 +683,7 @@ phase-specific code:
|
|
|
679
683
|
| Code | Domain |
|
|
680
684
|
|---|---|
|
|
681
685
|
| `TASK_SOURCE_INVALID` | A task document's field- or semantic-level validation failure, or a malformed/oversized/too-deep YAML front end failure. |
|
|
682
|
-
| `TASK_SCHEMA_VERSION_UNSUPPORTED` | A task document's `version:` is `3` or `2` and the in-memory read shim's deterministic conversion cannot resolve it — a human decision is needed (see [Task sources](#task-sources)); a convertible v2/v3 document reads through the shim instead of failing. |
|
|
686
|
+
| `TASK_SCHEMA_VERSION_UNSUPPORTED` | A task document's `version:` is `3` or `2` and the in-memory read shim's deterministic conversion cannot resolve it — a human decision is needed (see [Task sources](#task-sources)); a convertible v2/v3 document reads through the shim instead of failing. From 0.9.17-alpha.7, every v2/v3 document fails this way, naming `akm migrate apply`. |
|
|
683
687
|
| `TARGET_REF_INVALID` | A value is not a canonical `commands/`, `scripts/`, `tasks/`, or `workflows/` asset ref (malformed shapes, GitHub locators, other asset families). |
|
|
684
688
|
| `WORKFLOW_SOURCE_INVALID` | A workflow-source compile failure other than the one below. |
|
|
685
689
|
| `COMPOSITION_INVALID` | A composition-policy rejection: a rejected `with:`, a multi-job document, a composition cycle/depth/size violation. |
|
package/docs/reference/cli.md
CHANGED
|
@@ -2980,9 +2980,10 @@ crontab whose akm markers are malformed is refused unmodified, and another
|
|
|
2980
2980
|
akm process holding the scheduler lock makes sync exit 75 (retry shortly).
|
|
2981
2981
|
|
|
2982
2982
|
`akm task prune` reclaims installed scheduler entries that `sync` can never
|
|
2983
|
-
clean up on its own: entries
|
|
2984
|
-
|
|
2985
|
-
|
|
2983
|
+
clean up on its own: entries that no longer resolve to a live bundle (a row
|
|
2984
|
+
whose `AKM_BUNDLE_DIR` names a directory that is gone, or a row written
|
|
2985
|
+
before 0.9.17-alpha.7 whose `--scheduler-context` descriptor cannot be
|
|
2986
|
+
read). It never touches an entry that
|
|
2986
2987
|
still resolves to a live bundle — that's `sync`'s job. Like `sync
|
|
2987
2988
|
--dry-run`, the default is a dry-run preview (zero scheduler writes) that
|
|
2988
2989
|
exits non-zero when there are candidates to remove; `--yes` executes the
|
package/docs/reference/tasks.md
CHANGED
|
@@ -5,27 +5,22 @@ Task assets are strict, local automation sources. They live at
|
|
|
5
5
|
launchd, or Windows Task Scheduler with `akm task sync`. The task file is
|
|
6
6
|
authored source; scheduler entries are derived OS state.
|
|
7
7
|
|
|
8
|
-
**Task source v4 (`version: 4`) is the
|
|
9
|
-
document with `version: 3` or `version: 2`
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
stripped without ever being read (activation is host-local, below) and the
|
|
20
|
-
same one-line deprecation warning is printed. Task source v4 adds typed
|
|
21
|
-
`inputs:` and a single bounded `output:` schema (command targets only), and
|
|
22
|
-
makes scheduling OPTIONAL rather than mandatory. `akm task add` authors
|
|
23
|
-
task source v4 directly.
|
|
8
|
+
**Task source v4 (`version: 4`) is the only task source grammar akm reads.**
|
|
9
|
+
A document with `version: 3` or `version: 2` fails to load with `UsageError`
|
|
10
|
+
code `TASK_SCHEMA_VERSION_UNSUPPORTED`, naming `akm migrate apply`, which
|
|
11
|
+
converts it. A declared `version: 4` document whose `schedule[]` still
|
|
12
|
+
carries a per-entry `enabled` key — 0.9.15's v4 grammar accepted it, this
|
|
13
|
+
release's does not — fails the same way with `TASK_SOURCE_INVALID`
|
|
14
|
+
(activation is host-local, below). Each such file fails on its own:
|
|
15
|
+
`akm task sync` reports it and keeps reconciling every other task. Task
|
|
16
|
+
source v4 adds typed `inputs:` and a single bounded `output:` schema
|
|
17
|
+
(command targets only), and makes scheduling OPTIONAL rather than
|
|
18
|
+
mandatory. `akm task add` authors task source v4 directly.
|
|
24
19
|
|
|
25
20
|
If you have `version: 3` or `version: 2` files on disk (from an earlier
|
|
26
21
|
akm release), see [Migrating to task source v4](#migrating-to-task-source-v4)
|
|
27
22
|
below — `akm migrate apply` converts both generations in one pass and
|
|
28
|
-
rewrites the file on disk
|
|
23
|
+
rewrites the file on disk (`akm upgrade` runs it after an install). The
|
|
29
24
|
retired v3 grammar itself is documented at the bottom of this page
|
|
30
25
|
([Task v3 (retired): grammar reference for migration](#task-v3-retired-grammar-reference-for-migration))
|
|
31
26
|
purely so you can read an old file while migrating it; it is no longer
|
|
@@ -38,10 +33,9 @@ indexed, scheduled, or run. Every task should declare `version: 4`; a
|
|
|
38
33
|
document with no `version:` key, or a `version:` that is not a number,
|
|
39
34
|
fails with `TASK_SOURCE_INVALID` (`must be exactly 4.` / `is required and
|
|
40
35
|
must be exactly 4.`) — a genuinely malformed v4 document, not a legacy one.
|
|
41
|
-
`version: 3` and `version: 2`
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
(see [Migrating to task source v4](#migrating-to-task-source-v4)).
|
|
36
|
+
`version: 3` and `version: 2` fail with `TASK_SCHEMA_VERSION_UNSUPPORTED`
|
|
37
|
+
naming `akm migrate apply` (see
|
|
38
|
+
[Migrating to task source v4](#migrating-to-task-source-v4)).
|
|
45
39
|
The published [task schema](../../schemas/akm-task.json) describes the
|
|
46
40
|
hand-authored contract; `src/tasks/source/task-source-v4.ts` is the
|
|
47
41
|
authoritative bounded parser.
|
|
@@ -414,17 +408,14 @@ for full before/after examples and recovery guidance.
|
|
|
414
408
|
anything — see [`akm task explain`](#akm-task-explain) above.
|
|
415
409
|
- `akm task validate <path>` parses one task file by filesystem path (the
|
|
416
410
|
file need not live in a configured bundle) and reports the same
|
|
417
|
-
`valid`/`
|
|
411
|
+
`valid`/`blocked`/`invalid`/`not-a-task` diagnostic
|
|
418
412
|
`akm task sync` would produce for it — including sync's own cron-dialect
|
|
419
413
|
check and its per-schedule-entry input-contract check — without touching
|
|
420
414
|
the scheduler and without requiring a configured engine, even for a
|
|
421
415
|
command-kind task. The envelope's own `sourceVersion` field names the
|
|
422
|
-
file's declared schema version. A version 2/3 file
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
decision it needs. A `version: 4` file whose only defect is a retired
|
|
426
|
-
`schedule[].enabled` also reports `converts` (`sourceVersion` still `4`)
|
|
427
|
-
— it read through the shim too, not the direct v4 path.
|
|
416
|
+
file's declared schema version. A version 2/3 file, and a `version: 4`
|
|
417
|
+
file still carrying a retired `schedule[].enabled`, report `blocked`
|
|
418
|
+
(exit 1) naming `akm migrate apply`, which converts them.
|
|
428
419
|
- `akm task add` validates a task source v4 document, writes it, adds its ref
|
|
429
420
|
to local scheduler activation, and syncs its bundle. `--params` renders
|
|
430
421
|
typed `inputs:` declarations instead of a `with:` bag; `--schedule` is
|
|
@@ -443,8 +434,8 @@ for full before/after examples and recovery guidance.
|
|
|
443
434
|
A row that fails to install or remove is reported in `failures` and every
|
|
444
435
|
other row still applies. A source that fails to parse is reported the same
|
|
445
436
|
way, and its installed row is left exactly as it is. Rows akm cannot attribute to a bundle this sync covers —
|
|
446
|
-
another bundle's, another installation's (the row's
|
|
447
|
-
different
|
|
437
|
+
another bundle's, another installation's (the row's `AKM_BUNDLE_DIR`
|
|
438
|
+
names a different working stash), or anything outside akm's `# akm:task` markers,
|
|
448
439
|
`com.akm.task.` labels, or `\akm\` task folder — are never touched. A
|
|
449
440
|
Task Scheduler row is compared by the fingerprint akm writes into its
|
|
450
441
|
`<Source>` plus its enabled state, so an edit made in Task Scheduler that
|
|
@@ -457,9 +448,10 @@ for full before/after examples and recovery guidance.
|
|
|
457
448
|
removals annotated with their owning bundle) without writing to the
|
|
458
449
|
scheduler; exits non-zero when removals are pending.
|
|
459
450
|
- `akm task prune` removes installed scheduler entries `sync` cannot reach
|
|
460
|
-
because
|
|
461
|
-
|
|
462
|
-
|
|
451
|
+
because they no longer resolve to a live bundle: a row whose
|
|
452
|
+
`AKM_BUNDLE_DIR` names a directory that is gone, or a row written before
|
|
453
|
+
0.9.17-alpha.7 whose `--scheduler-context` descriptor cannot be read. It
|
|
454
|
+
never touches an entry that still resolves to a live bundle.
|
|
463
455
|
Defaults to a dry-run preview (zero writes); `--yes` executes it; `--id
|
|
464
456
|
<id1,id2,...>` scopes to specific ids and refuses any id that isn't a
|
|
465
457
|
current orphan candidate.
|
|
@@ -473,13 +465,39 @@ for full before/after examples and recovery guidance.
|
|
|
473
465
|
section directly above the first akm task block in the crontab (on macOS,
|
|
474
466
|
an `EnvironmentVariables` entry in each plist). It is the PATH of the shell
|
|
475
467
|
that ran the sync, rewritten on every crontab write and removed with the
|
|
476
|
-
last akm block; cron applies it to every row below it.
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
468
|
+
last akm block; cron applies it to every row below it.
|
|
469
|
+
- A task's row is its command plus its schedule:
|
|
470
|
+
`<launcher> task run <id> --bundle <bundle> --scheduled`, and it sets its
|
|
471
|
+
own environment. Every row sets `AKM_BUNDLE_DIR` to the working stash of
|
|
472
|
+
the shell that ran the sync (its `AKM_BUNDLE_DIR`, or the default bundle),
|
|
473
|
+
so the scheduled run uses the same working stash, `--bundle` finds a stash
|
|
474
|
+
no config names, and sync tells rows of other installations sharing the
|
|
475
|
+
scheduler apart (#846). Rows synced from a shell that set
|
|
476
|
+
`AKM_CONFIG_DIR`, `AKM_DATA_DIR`, `AKM_CACHE_DIR` or `AKM_STATE_DIR`
|
|
477
|
+
explicitly set those too. Each backend does it its own way: a
|
|
478
|
+
`VAR=value` prefix in the crontab, an `EnvironmentVariables` entry in the
|
|
479
|
+
plist, a `$env:VAR='value';` assignment ahead of the command in Task
|
|
480
|
+
Scheduler. Other defaults resolve at fire time, so a scheduled run uses
|
|
481
|
+
the same state, data and cache directories an interactive command does.
|
|
482
|
+
Run the sync from a shell whose environment you would want scheduled.
|
|
483
|
+
|
|
484
|
+
```text
|
|
485
|
+
15 2 * * * AKM_BUNDLE_DIR=/home/u/akm /home/u/.bun/bin/bun /home/u/.bun/lib/node_modules/akm-cli/dist/akm task run nightly --bundle work --scheduled > /home/u/.cache/akm/tasks/logs/nightly.log 2>&1
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
A row whose command is over 1,000 bytes runs a wrapper script under the
|
|
489
|
+
log directory instead (`sh <log dir>/.akm-cron-wrapper-<id>-<hash>.sh`);
|
|
490
|
+
sync reads the script to tell which task the row runs.
|
|
491
|
+
|
|
492
|
+
Releases 0.9.0 through 0.9.17-alpha.6 wrote a `--scheduler-context
|
|
493
|
+
<file>` argument into each row instead, naming a descriptor file under
|
|
494
|
+
`$DATA/tasks/context/` that held the same values. akm still applies that
|
|
495
|
+
file when such a row fires, and the first `akm task sync` after upgrading
|
|
496
|
+
rewrites each row in place: it shows as an update, keeps the row's
|
|
497
|
+
launcher and schedule, and sets the values inline. A row the sync leaves as it is (its task file failed to load, or
|
|
498
|
+
a `--bundle` sync did not cover it) still names its file; once
|
|
499
|
+
`akm task doctor` lists no binding with a `contextPath`, the old descriptor
|
|
500
|
+
files are not read and can be deleted.
|
|
483
501
|
|
|
484
502
|
Scheduler execution is at least once. Backends provide a stable invocation
|
|
485
503
|
identity and AKM fences stale attempts, but an ambiguous process crash can be
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "akm-cli",
|
|
3
|
-
"version": "0.9.17-alpha.
|
|
3
|
+
"version": "0.9.17-alpha.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "akm (Agent Knowledge Manager) — a portable, local-first capability library for AI agents. Discover, load, share, and improve reusable skills, scripts, workflows, and knowledge across any shell-capable coding agent, including Claude Code, OpenCode, and Cursor.",
|
|
6
6
|
"keywords": [
|