@open-agent-toolkit/cli 0.2.76 → 0.2.79
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/assets/agents/recon-worker.md +12 -12
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/workflows/skills/recon.md +53 -27
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/explainer-kit/SKILL.md +1 -1
- package/assets/skills/explainer-kit/scripts/bundle.mjs +26 -2
- package/assets/skills/explainer-kit/scripts/record.mjs +26 -3
- package/assets/skills/explainer-kit/scripts/verify.mjs +26 -3
- package/assets/skills/oat-explainer-kit/SKILL.md +1 -1
- package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +25 -5
- package/assets/skills/oat-project-complete/SKILL.md +1 -1
- package/assets/skills/oat-project-complete/scripts/consume-persisted-recap-intent.mjs +26 -6
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +26 -6
- package/assets/skills/oat-project-complete/scripts/finalize-synced-archive.mjs +7 -0
- package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +26 -5
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +26 -5
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +26 -6
- package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +26 -6
- package/assets/skills/oat-project-complete/scripts/resolve-synced-archive-entry.mjs +26 -5
- package/assets/skills/oat-project-complete/scripts/validate-durable-archive-receipt.mjs +7 -0
- package/assets/skills/oat-project-complete/scripts/validate-nonarchive-lifecycle-receipt.mjs +126 -95
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/scripts/capture-dirty-tree.mjs +3 -0
- package/assets/skills/recon/SKILL.md +37 -19
- package/assets/skills/recon/references/packet-contract.md +33 -30
- package/assets/skills/recon/references/profiles.md +18 -15
- package/assets/skills/recon/references/worker-contract.md +87 -7
- package/assets/skills/recon/scripts/create-review-brief.mjs +2 -5
- package/assets/skills/recon/scripts/lib/cli-entry.mjs +21 -0
- package/assets/skills/recon/scripts/lib/contracts.mjs +144 -38
- package/assets/skills/recon/scripts/lib/routing.mjs +2 -5
- package/assets/skills/recon/scripts/lib/validated-run.mjs +1 -1
- package/assets/skills/recon/scripts/prepare-routing.mjs +2 -2
- package/assets/skills/recon/scripts/reconcile-ledger.mjs +269 -2
- package/assets/skills/recon/scripts/render-packet.mjs +2 -5
- package/assets/skills/recon/scripts/validate-artifact.mjs +2 -5
- package/assets/skills/recon/scripts/validate-packet.mjs +73 -23
- package/package.json +2 -2
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
3
|
+
import { basename, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
import { promisify } from 'node:util';
|
|
5
6
|
|
|
6
7
|
const execFileAsync = promisify(execFile);
|
|
@@ -692,10 +693,29 @@ async function main(argv) {
|
|
|
692
693
|
return recoverCompletionReceipts(options);
|
|
693
694
|
}
|
|
694
695
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
696
|
+
/**
|
|
697
|
+
* Direct invocation, compared as canonical paths on both sides. Comparing a raw
|
|
698
|
+
* `process.argv[1]` against `import.meta.url` makes this script a silent no-op
|
|
699
|
+
* that exits 0 whenever the skill is reached through a symlinked install root,
|
|
700
|
+
* and canonicalizing only one side has the same effect under
|
|
701
|
+
* `--preserve-symlinks-main`, which keeps the link in `import.meta.url`.
|
|
702
|
+
* A path that cannot be canonicalized is not a module Node loaded as the entry
|
|
703
|
+
* point, so a thrown `realpathSync` means "not invoked directly" and returns
|
|
704
|
+
* `false`; it never masks a direct run.
|
|
705
|
+
*/
|
|
706
|
+
function isDirectInvocation(invokedPath) {
|
|
707
|
+
if (!invokedPath) return false;
|
|
708
|
+
try {
|
|
709
|
+
return (
|
|
710
|
+
realpathSync(fileURLToPath(import.meta.url)) ===
|
|
711
|
+
realpathSync(resolve(invokedPath))
|
|
712
|
+
);
|
|
713
|
+
} catch {
|
|
714
|
+
return false;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
if (isDirectInvocation(process.argv[1])) {
|
|
699
719
|
main(process.argv.slice(2))
|
|
700
720
|
.then((result) => {
|
|
701
721
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { realpathSync } from 'node:fs';
|
|
1
2
|
import { lstat, readdir } from 'node:fs/promises';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { join, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
4
5
|
|
|
5
6
|
import {
|
|
6
7
|
detectCompletionReceiptCandidate,
|
|
@@ -116,10 +117,29 @@ async function main(argv) {
|
|
|
116
117
|
return resolveCompletionRetry(parseArguments(argv));
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
/**
|
|
121
|
+
* Direct invocation, compared as canonical paths on both sides. Comparing a raw
|
|
122
|
+
* `process.argv[1]` against `import.meta.url` makes this script a silent no-op
|
|
123
|
+
* that exits 0 whenever the skill is reached through a symlinked install root,
|
|
124
|
+
* and canonicalizing only one side has the same effect under
|
|
125
|
+
* `--preserve-symlinks-main`, which keeps the link in `import.meta.url`.
|
|
126
|
+
* A path that cannot be canonicalized is not a module Node loaded as the entry
|
|
127
|
+
* point, so a thrown `realpathSync` means "not invoked directly" and returns
|
|
128
|
+
* `false`; it never masks a direct run.
|
|
129
|
+
*/
|
|
130
|
+
function isDirectInvocation(invokedPath) {
|
|
131
|
+
if (!invokedPath) return false;
|
|
132
|
+
try {
|
|
133
|
+
return (
|
|
134
|
+
realpathSync(fileURLToPath(import.meta.url)) ===
|
|
135
|
+
realpathSync(resolve(invokedPath))
|
|
136
|
+
);
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (isDirectInvocation(process.argv[1])) {
|
|
123
143
|
main(process.argv.slice(2))
|
|
124
144
|
.then((result) => {
|
|
125
145
|
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { execFile as execFileCallback } from 'node:child_process';
|
|
2
|
+
import { realpathSync } from 'node:fs';
|
|
2
3
|
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import {
|
|
4
|
+
import { resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
4
6
|
import { promisify } from 'node:util';
|
|
5
7
|
|
|
6
8
|
const execFile = promisify(execFileCallback);
|
|
@@ -149,10 +151,29 @@ async function main(argv) {
|
|
|
149
151
|
return resolveSyncedArchiveEntry({ ...options, record });
|
|
150
152
|
}
|
|
151
153
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Direct invocation, compared as canonical paths on both sides. Comparing a raw
|
|
156
|
+
* `process.argv[1]` against `import.meta.url` makes this script a silent no-op
|
|
157
|
+
* that exits 0 whenever the skill is reached through a symlinked install root,
|
|
158
|
+
* and canonicalizing only one side has the same effect under
|
|
159
|
+
* `--preserve-symlinks-main`, which keeps the link in `import.meta.url`.
|
|
160
|
+
* A path that cannot be canonicalized is not a module Node loaded as the entry
|
|
161
|
+
* point, so a thrown `realpathSync` means "not invoked directly" and returns
|
|
162
|
+
* `false`; it never masks a direct run.
|
|
163
|
+
*/
|
|
164
|
+
function isDirectInvocation(invokedPath) {
|
|
165
|
+
if (!invokedPath) return false;
|
|
166
|
+
try {
|
|
167
|
+
return (
|
|
168
|
+
realpathSync(fileURLToPath(import.meta.url)) ===
|
|
169
|
+
realpathSync(resolve(invokedPath))
|
|
170
|
+
);
|
|
171
|
+
} catch {
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (isDirectInvocation(process.argv[1])) {
|
|
156
177
|
main(process.argv.slice(2))
|
|
157
178
|
.then((result) => process.stdout.write(`${JSON.stringify(result)}\n`))
|
|
158
179
|
.catch((error) => {
|
|
@@ -134,6 +134,10 @@ async function main(argv) {
|
|
|
134
134
|
if (options.mode === 'receipt') {
|
|
135
135
|
let report;
|
|
136
136
|
try {
|
|
137
|
+
// Stdin is read to EOF with no timeout. Every caller pipes the archive
|
|
138
|
+
// report in (`printf '%s\n' "$ARCHIVE_OUTPUT" | node ...`), so EOF
|
|
139
|
+
// always arrives; a timeout would turn a slow producer into a false
|
|
140
|
+
// failure, and a caller that leaves stdin open is a caller bug.
|
|
137
141
|
report = JSON.parse(readFileSync(0, 'utf8'));
|
|
138
142
|
} catch (error) {
|
|
139
143
|
throw receiptError(
|
|
@@ -158,6 +162,9 @@ async function main(argv) {
|
|
|
158
162
|
* `--preserve-symlinks-main`, which keeps the link in `import.meta.url`. Either
|
|
159
163
|
* way the caller reads "exited 0" as "verified" — the fail-open shape this
|
|
160
164
|
* validator exists to prevent.
|
|
165
|
+
* A path that cannot be canonicalized is not a module Node loaded as the entry
|
|
166
|
+
* point, so a thrown `realpathSync` means "not invoked directly" and returns
|
|
167
|
+
* `false`; it never masks a direct run.
|
|
161
168
|
*/
|
|
162
169
|
function isDirectInvocation(invokedPath) {
|
|
163
170
|
if (!invokedPath) return false;
|
package/assets/skills/oat-project-complete/scripts/validate-nonarchive-lifecycle-receipt.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
resolve,
|
|
12
12
|
sep,
|
|
13
13
|
} from 'node:path';
|
|
14
|
+
import { fileURLToPath } from 'node:url';
|
|
14
15
|
|
|
15
16
|
function fail(message) {
|
|
16
17
|
process.stderr.write(`Invalid non-archive lifecycle receipt: ${message}\n`);
|
|
@@ -57,104 +58,134 @@ function gitSucceeds(cwd, args) {
|
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
function main(argv) {
|
|
62
|
+
const [recordArgument, commit, projectName] = argv;
|
|
63
|
+
if (!recordArgument || !/^[0-9a-f]{40}$/.test(commit ?? '') || !projectName) {
|
|
64
|
+
fail('expected <record-path> <full-commit-sha> <project-name>.');
|
|
65
|
+
}
|
|
64
66
|
|
|
65
|
-
const unresolvedRecordPath = resolve(recordArgument);
|
|
66
|
-
if (!existsSync(unresolvedRecordPath)) {
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
const recordPath = realpathSync(unresolvedRecordPath);
|
|
70
|
-
const repoRoot = realpathSync(
|
|
71
|
-
|
|
72
|
-
);
|
|
73
|
-
const recordRelative = relative(repoRoot, recordPath);
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
) {
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
const gitRecordPath = recordRelative.split(sep).join('/');
|
|
83
|
-
if (basename(gitRecordPath, extname(gitRecordPath)) !== projectName) {
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
if (!gitSucceeds(repoRoot, ['merge-base', '--is-ancestor', commit, 'HEAD'])) {
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
const changedPaths = git(repoRoot, [
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
])
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (changedPaths.length !== 1 || changedPaths[0] !== gitRecordPath) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
67
|
+
const unresolvedRecordPath = resolve(recordArgument);
|
|
68
|
+
if (!existsSync(unresolvedRecordPath)) {
|
|
69
|
+
fail('record path does not exist; restore the synced discovery record.');
|
|
70
|
+
}
|
|
71
|
+
const recordPath = realpathSync(unresolvedRecordPath);
|
|
72
|
+
const repoRoot = realpathSync(
|
|
73
|
+
git(dirname(recordPath), ['rev-parse', '--show-toplevel']),
|
|
74
|
+
);
|
|
75
|
+
const recordRelative = relative(repoRoot, recordPath);
|
|
76
|
+
if (
|
|
77
|
+
recordRelative === '' ||
|
|
78
|
+
recordRelative === '..' ||
|
|
79
|
+
recordRelative.startsWith(`..${sep}`) ||
|
|
80
|
+
isAbsolute(recordRelative)
|
|
81
|
+
) {
|
|
82
|
+
fail('record path is outside the repository root.');
|
|
83
|
+
}
|
|
84
|
+
const gitRecordPath = recordRelative.split(sep).join('/');
|
|
85
|
+
if (basename(gitRecordPath, extname(gitRecordPath)) !== projectName) {
|
|
86
|
+
fail('record filename must match the project name.');
|
|
87
|
+
}
|
|
88
|
+
if (!gitSucceeds(repoRoot, ['merge-base', '--is-ancestor', commit, 'HEAD'])) {
|
|
89
|
+
fail('receipt commit must be an ancestor of the current repository HEAD.');
|
|
90
|
+
}
|
|
91
|
+
const changedPaths = git(repoRoot, [
|
|
92
|
+
'diff-tree',
|
|
93
|
+
'--root',
|
|
94
|
+
'--no-commit-id',
|
|
95
|
+
'--name-only',
|
|
96
|
+
'-r',
|
|
97
|
+
'--format=',
|
|
98
|
+
commit,
|
|
99
|
+
])
|
|
100
|
+
.split('\n')
|
|
101
|
+
.filter(Boolean);
|
|
102
|
+
if (changedPaths.length !== 1 || changedPaths[0] !== gitRecordPath) {
|
|
103
|
+
fail(
|
|
104
|
+
'receipt commit must change exactly the synced discovery record path.',
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
const expectedSubject = `chore(oat): complete synced project ${projectName}`;
|
|
108
|
+
const subject = git(repoRoot, ['show', '-s', '--format=%s', commit]);
|
|
109
|
+
if (subject !== expectedSubject) {
|
|
110
|
+
fail(`commit subject must be "${expectedSubject}".`);
|
|
111
|
+
}
|
|
108
112
|
|
|
109
|
-
let committedRecordContents;
|
|
110
|
-
let record;
|
|
111
|
-
try {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
let committedRecordContents;
|
|
114
|
+
let record;
|
|
115
|
+
try {
|
|
116
|
+
committedRecordContents = gitRaw(repoRoot, [
|
|
117
|
+
'show',
|
|
118
|
+
`${commit}:${gitRecordPath}`,
|
|
119
|
+
]);
|
|
120
|
+
record = JSON.parse(committedRecordContents);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
fail(
|
|
123
|
+
`committed record is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
if (readFileSync(recordPath, 'utf8') !== committedRecordContents) {
|
|
127
|
+
fail('working-tree record content must exactly match the receipt commit.');
|
|
128
|
+
}
|
|
129
|
+
const expectedKeys = new Set([
|
|
130
|
+
'archiveSnapshot',
|
|
131
|
+
'completedAt',
|
|
132
|
+
'createdAt',
|
|
133
|
+
'ref',
|
|
134
|
+
'remote',
|
|
135
|
+
'schemaVersion',
|
|
136
|
+
'scope',
|
|
137
|
+
'slug',
|
|
138
|
+
'status',
|
|
115
139
|
]);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
140
|
+
if (
|
|
141
|
+
!record ||
|
|
142
|
+
typeof record !== 'object' ||
|
|
143
|
+
Array.isArray(record) ||
|
|
144
|
+
Object.keys(record).some((key) => !expectedKeys.has(key)) ||
|
|
145
|
+
record.schemaVersion !== 1 ||
|
|
146
|
+
record.slug !== projectName ||
|
|
147
|
+
record.scope !== 'synced' ||
|
|
148
|
+
record.ref !== `refs/oat/projects/${projectName}` ||
|
|
149
|
+
record.remote !== 'origin' ||
|
|
150
|
+
record.status !== 'complete' ||
|
|
151
|
+
typeof record.createdAt !== 'string' ||
|
|
152
|
+
Number.isNaN(Date.parse(record.createdAt)) ||
|
|
153
|
+
typeof record.completedAt !== 'string' ||
|
|
154
|
+
Number.isNaN(Date.parse(record.completedAt)) ||
|
|
155
|
+
(record.archiveSnapshot !== undefined &&
|
|
156
|
+
(typeof record.archiveSnapshot !== 'string' ||
|
|
157
|
+
record.archiveSnapshot.length === 0))
|
|
158
|
+
) {
|
|
159
|
+
fail(
|
|
160
|
+
'committed record must be a complete synced record with the exact project identity and lifecycle content.',
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
process.stdout.write(`${commit}\n`);
|
|
124
165
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
!
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
record.status !== 'complete' ||
|
|
147
|
-
typeof record.createdAt !== 'string' ||
|
|
148
|
-
Number.isNaN(Date.parse(record.createdAt)) ||
|
|
149
|
-
typeof record.completedAt !== 'string' ||
|
|
150
|
-
Number.isNaN(Date.parse(record.completedAt)) ||
|
|
151
|
-
(record.archiveSnapshot !== undefined &&
|
|
152
|
-
(typeof record.archiveSnapshot !== 'string' ||
|
|
153
|
-
record.archiveSnapshot.length === 0))
|
|
154
|
-
) {
|
|
155
|
-
fail(
|
|
156
|
-
'committed record must be a complete synced record with the exact project identity and lifecycle content.',
|
|
157
|
-
);
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Direct invocation, compared as canonical paths on both sides. Comparing a raw
|
|
169
|
+
* `process.argv[1]` against `import.meta.url` makes this script a silent no-op
|
|
170
|
+
* that exits 0 whenever the skill is reached through a symlinked install root,
|
|
171
|
+
* and canonicalizing only one side has the same effect under
|
|
172
|
+
* `--preserve-symlinks-main`, which keeps the link in `import.meta.url`.
|
|
173
|
+
* A path that cannot be canonicalized is not a module Node loaded as the entry
|
|
174
|
+
* point, so a thrown `realpathSync` means "not invoked directly" and returns
|
|
175
|
+
* `false`; it never masks a direct run.
|
|
176
|
+
*/
|
|
177
|
+
function isDirectInvocation(invokedPath) {
|
|
178
|
+
if (!invokedPath) return false;
|
|
179
|
+
try {
|
|
180
|
+
return (
|
|
181
|
+
realpathSync(fileURLToPath(import.meta.url)) ===
|
|
182
|
+
realpathSync(resolve(invokedPath))
|
|
183
|
+
);
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
158
187
|
}
|
|
159
188
|
|
|
160
|
-
process.
|
|
189
|
+
if (isDirectInvocation(process.argv[1])) {
|
|
190
|
+
main(process.argv.slice(2));
|
|
191
|
+
}
|
|
@@ -1051,6 +1051,9 @@ async function main(argv) {
|
|
|
1051
1051
|
* `--preserve-symlinks-main`, which keeps the link in `import.meta.url`. Either
|
|
1052
1052
|
* way the caller reads "exited 0" as "verified" — the fail-open shape the
|
|
1053
1053
|
* contracts exist to prevent.
|
|
1054
|
+
* A path that cannot be canonicalized is not a module Node loaded as the entry
|
|
1055
|
+
* point, so a thrown `realpathSync` means "not invoked directly" and returns
|
|
1056
|
+
* `false`; it never masks a direct run.
|
|
1054
1057
|
*/
|
|
1055
1058
|
function isDirectInvocation(invokedPath) {
|
|
1056
1059
|
if (!invokedPath) return false;
|
|
@@ -6,7 +6,7 @@ disable-model-invocation: true
|
|
|
6
6
|
user-invocable: true
|
|
7
7
|
allowed-tools: Read, Write, Glob, Grep, Bash, AskUserQuestion, Agent, mcp__*
|
|
8
8
|
metadata:
|
|
9
|
-
version: 1.1.
|
|
9
|
+
version: 1.1.5
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
# Recon
|
|
@@ -127,8 +127,8 @@ Use the complete mode policy from `references/profiles.md` and
|
|
|
127
127
|
`scripts/lib/routing.mjs`. Map manifest wave modes to the worker's closed
|
|
128
128
|
assignment vocabulary: `redundant-gather` to `gather`;
|
|
129
129
|
`semantic-verification` and `redundant-verification` to `verify`; `adversarial`
|
|
130
|
-
and `contradiction-resolution` to `adversary
|
|
131
|
-
|
|
130
|
+
and `contradiction-resolution` to `adversary`. The remaining worker modes keep
|
|
131
|
+
their same-named assignment; reconciliation is controller-owned. All lanes
|
|
132
132
|
within one launch are a homogeneous wave. Select each wave independently; a
|
|
133
133
|
stronger wave never raises unrelated waves' targets or effort.
|
|
134
134
|
|
|
@@ -164,9 +164,14 @@ task class and class floor. Preserve provider, route, role, model, effort,
|
|
|
164
164
|
reasoning mode, and service tier as separate provider-native axes. A null axis
|
|
165
165
|
means the adapter exposes no independently requested control; never translate
|
|
166
166
|
effort between harnesses or normalize an opaque selector. Prefer the canonical
|
|
167
|
-
`recon-worker` role
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
`recon-worker` role only when the current live Task catalog exposes it. A
|
|
168
|
+
materialized `.cursor/agents/recon-worker.md` file proves installation, not that
|
|
169
|
+
the current Cursor Task catalog can launch the role. If the role is absent from
|
|
170
|
+
that observed catalog, plan the generic role with the complete worker contract
|
|
171
|
+
as a visible generic role fallback before approval. A fallback after approval
|
|
172
|
+
is forbidden. Every Cursor recon leaf, including that generic fallback, must be
|
|
173
|
+
launched as a background task so a new parent-chat message cannot turn parent
|
|
174
|
+
turn interruption into an apparent worker crash.
|
|
170
175
|
|
|
171
176
|
Prepare a `schemaVersion: 2` draft manifest. Its execution object contains a
|
|
172
177
|
complete inherited `target`, limits, waves, conditions, and later the approval.
|
|
@@ -218,7 +223,8 @@ result. If any approved axis cannot be satisfied, stop with a
|
|
|
218
223
|
launch nothing. Never substitute a different axis to make the launch fit.
|
|
219
224
|
|
|
220
225
|
Launch each wave through the dispatch dependency with exactly the approved
|
|
221
|
-
axes.
|
|
226
|
+
axes. On Cursor, launch both the custom worker role and the pre-approved generic
|
|
227
|
+
fallback in the background. Immediately before each launch, check the constructed target through the
|
|
222
228
|
same production helper used by preview:
|
|
223
229
|
|
|
224
230
|
```bash
|
|
@@ -235,13 +241,22 @@ approved `waveId` and `laneId`. This structured identity is required for
|
|
|
235
241
|
conditional and non-conditional lanes alike; prose is descriptive, not
|
|
236
242
|
identity.
|
|
237
243
|
|
|
244
|
+
Worker completion is decided from the approved-path artifact before interpreting
|
|
245
|
+
a later provider transport error. When the artifact's identity, closed schema,
|
|
246
|
+
bytes, and digest validate, the lane is complete; a subsequent stream-close or
|
|
247
|
+
RPC error is recorded as a non-material diagnostic and never triggers a
|
|
248
|
+
replacement launch. Missing or invalid output remains a terminal `PASS_FAILED`
|
|
249
|
+
outcome with no retry or replacement.
|
|
250
|
+
|
|
238
251
|
Run the passes in this order:
|
|
239
252
|
|
|
240
253
|
1. `map` and `gather` workers write unique dossiers under `raw/dossiers/`.
|
|
241
254
|
Thorough runs complete their independent `redundant-gather` wave here too.
|
|
242
255
|
2. `compile` writes a candidate canonical claim ledger. A thorough candidate
|
|
243
256
|
directly references a complete dossier from every approved primary and
|
|
244
|
-
redundant gather lane before any review brief is created.
|
|
257
|
+
redundant gather lane before any review brief is created. Every display
|
|
258
|
+
excerpt is an exact contiguous substring of its cited source, apart from the
|
|
259
|
+
declared `redacted-exact` representation; paraphrase is invalid evidence.
|
|
245
260
|
3. Source preflight: run `scripts/validate-artifact.mjs` on the candidate
|
|
246
261
|
manifest and ledger, then reopen every declared source and evidence locator
|
|
247
262
|
with the checks in `scripts/validate-packet.mjs`. Resolve source roots to
|
|
@@ -251,23 +266,26 @@ Run the passes in this order:
|
|
|
251
266
|
manifest and ledger pair validates.
|
|
252
267
|
4. Profile-required `verify`, `adversary`, and `coverage` workers consume only
|
|
253
268
|
immutable selectively blind briefs created by
|
|
254
|
-
`scripts/create-review-brief.mjs` at unique paths.
|
|
269
|
+
`scripts/create-review-brief.mjs` at unique paths. Each review-producing
|
|
270
|
+
wave has exactly one lane; partition source or question fan-out in the
|
|
271
|
+
applicable gather wave instead.
|
|
255
272
|
5. Evaluate each predeclared evidence condition only after its completed
|
|
256
273
|
predecessor artifacts exist. Record exactly one root-authored condition
|
|
257
274
|
outcome. A triggered `contradiction-resolution` wave runs once as an
|
|
258
275
|
`adversary` evidence assignment; a not-triggered or unresolved wave supplies
|
|
259
276
|
no artifact. Accepted failure, cancellation, timeout, or missing output never
|
|
260
277
|
authorizes replacement work.
|
|
261
|
-
6. Standard and thorough
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
278
|
+
6. Standard and thorough run one deterministic controller reconciliation after
|
|
279
|
+
required review evidence and any triggered contradiction investigation. The
|
|
280
|
+
controller invokes `scripts/reconcile-ledger.mjs` once using the manifest's
|
|
281
|
+
`execution.reconciliation` paths and literal producer
|
|
282
|
+
`controller:reconcile-ledger-v1`, validates both candidate outputs, copies
|
|
283
|
+
the exact ledger bytes to a packet-contained temporary file, and atomically
|
|
284
|
+
renames it to `claims.json`. No reconciliation worker is dispatched.
|
|
285
|
+
|
|
286
|
+
If reconciliation requires new semantic judgment, preserve completed evidence
|
|
287
|
+
and return an explicit unresolved caller-owned gap. Never mutate the target or
|
|
288
|
+
substitute a
|
|
271
289
|
contradiction search for synthesis.
|
|
272
290
|
|
|
273
291
|
Use `references/worker-contract.md` for every assignment. Never allow two
|
|
@@ -125,18 +125,21 @@ condition contains `conditionId`, `destinationWaveId`, `afterWaveIds`, one of
|
|
|
125
125
|
`insufficient-evidence` or `unresolved-material-challenge`, and
|
|
126
126
|
`maxActivations: 1`. Each conditional destination is a uniquely identified
|
|
127
127
|
`contradiction-resolution` evidence wave, has exactly one condition, appears
|
|
128
|
-
after every named predecessor and before
|
|
128
|
+
after every named predecessor and before controller reconciliation, and
|
|
129
129
|
owns unique lane IDs and write roots. Conversely, every wave marked
|
|
130
130
|
`conditional: true` must be the destination of exactly one activating condition;
|
|
131
131
|
dead conditional waves are invalid. Quick permits no conditional wave;
|
|
132
|
-
standard
|
|
132
|
+
standard and thorough each permit one. The profile's 4/10/20 evidence-lane cap
|
|
133
133
|
counts `gather`, `semantic-verification`, `adversarial`, `coverage`,
|
|
134
134
|
`redundant-gather`, `redundant-verification`, and
|
|
135
135
|
`contradiction-resolution` when those modes are permitted by the profile.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
capped at 4/6/8.
|
|
136
|
+
Only `gather` and `redundant-gather` may partition a wave across multiple lanes.
|
|
137
|
+
Every typed review wave and every permitted mode outside the counted set is
|
|
138
|
+
fixed at exactly one lane, including `map` and `compile`. The resulting
|
|
139
|
+
worker-lane maxima are 6/12/22, and concurrency remains capped at 4/6/8.
|
|
140
|
+
Each review lane's approved write root must contain the corresponding fixed
|
|
141
|
+
path in `execution.reconciliation`; a different approved root is invalid before
|
|
142
|
+
launch.
|
|
140
143
|
|
|
141
144
|
Triggered dispositions require exact complete same-run artifacts from every
|
|
142
145
|
approved predecessor and concrete typed predicate evidence from those same-run
|
|
@@ -150,17 +153,14 @@ no achieved pass. Accepted failed, cancelled, timed-out, or missing predecessor
|
|
|
150
153
|
work cannot activate replacement work. Required profile passes remain required
|
|
151
154
|
regardless of conditional annotations.
|
|
152
155
|
|
|
153
|
-
`reconciliation-needs-judgment` is a
|
|
154
|
-
condition predicate
|
|
155
|
-
|
|
156
|
-
unresolved out-of-envelope gap until renewed approval or a new run; it never
|
|
157
|
-
mutates the approved target or launches a second reconciliation.
|
|
156
|
+
`reconciliation-needs-judgment` is a caller-owned unresolved outcome, never a
|
|
157
|
+
condition predicate or target escalation. The controller preserves completed
|
|
158
|
+
work and records the gap; it never dispatches reconciliation work.
|
|
158
159
|
|
|
159
|
-
The controller maps the
|
|
160
|
-
|
|
160
|
+
The controller maps the nine manifest wave modes onto the worker contract's
|
|
161
|
+
six assignment modes: redundant gathering uses `gather`; semantic and
|
|
161
162
|
redundant verification use `verify`; adversarial and contradiction-resolution
|
|
162
|
-
use `adversary
|
|
163
|
-
does not change the approved manifest mode used for artifact and pass checks.
|
|
163
|
+
use `adversary`. Reconciliation is not a worker mode or approval target.
|
|
164
164
|
|
|
165
165
|
Approval is `{ type: "explicit-user-approval", approvedAt }`. It is valid only
|
|
166
166
|
for the exact proposal shown in the same uninterrupted controller flow. Resume,
|
|
@@ -183,7 +183,6 @@ complete typed artifacts of the same run:
|
|
|
183
183
|
| `semantic-verification` | a `recon.review-result` with kind `semantic` |
|
|
184
184
|
| `adversarial` | a `recon.review-result` with kind `adversarial` |
|
|
185
185
|
| `coverage` | a `recon.review-result` with kind `coverage` |
|
|
186
|
-
| `reconciliation` | a `recon.review-result` with kind `reconciliation` |
|
|
187
186
|
| `redundant-gather` | a complete `gather` dossier owned by the approved `redundant-gather` wave |
|
|
188
187
|
| `redundant-verification` | a `recon.review-result` with kind `redundant-verification` |
|
|
189
188
|
| `contradiction-resolution` | a `recon.review-result` with kind `contradiction-resolution` |
|
|
@@ -191,11 +190,11 @@ complete typed artifacts of the same run:
|
|
|
191
190
|
`quick` requires `map` and `gather`; the canonical ledger itself is the
|
|
192
191
|
compile result, so an approved `compile` lane needs no separate artifact, and
|
|
193
192
|
locator validation is performed by the validator.
|
|
194
|
-
`standard` adds `semantic-verification`, `adversarial`, `coverage`,
|
|
195
|
-
|
|
193
|
+
`standard` adds `semantic-verification`, `adversarial`, and `coverage`, then
|
|
194
|
+
requires the manifest-bound controller reconciliation. `thorough` adds `redundant-gather` and
|
|
196
195
|
`redundant-verification`. A predeclared conditional
|
|
197
196
|
`contradiction-resolution` evidence pass may feed the same mandatory terminal
|
|
198
|
-
reconciliation when its predicate triggers
|
|
197
|
+
controller reconciliation when its predicate triggers.
|
|
199
198
|
|
|
200
199
|
Thorough routing completes `redundant-gather` before `compile`. The compiled
|
|
201
200
|
ledger must directly reference by exact path and digest at least one complete
|
|
@@ -346,17 +345,21 @@ branch; source ineligibility never bypasses persistence safety.
|
|
|
346
345
|
claim/evidence associations, coverage findings, unresolved issues, and
|
|
347
346
|
completion status. Every new evidence record has at least one exact
|
|
348
347
|
association, and an association cannot name evidence absent from that result
|
|
349
|
-
or a claim without a disposition in that result.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
348
|
+
or a claim without a disposition in that result.
|
|
349
|
+
|
|
350
|
+
Every `unresolvedIssues` member is a string. Review results are closed objects:
|
|
351
|
+
unknown fields and object-valued issue entries are invalid.
|
|
352
|
+
Reconciliation results replace the brief reference with prior-ledger/revision,
|
|
353
|
+
additions/removals, exact transitions, and coverage-disposition bindings.
|
|
354
|
+
Coverage findings are closed records bound to affected claims and exact
|
|
355
|
+
manifest gaps. Accepted
|
|
356
|
+
material gaps require a legal downgrade for every affected claim; a resolved
|
|
357
|
+
finding instead names exact typed evidence. Non-material coverage gaps
|
|
358
|
+
downgrade verified claims and transition provisional claims to unresolved,
|
|
359
|
+
while existing supported claims remain supported without verified promotion.
|
|
360
|
+
Thorough redundant verification and contradiction resolution are
|
|
361
|
+
claim-bearing typed review results, not raw dossiers: they bind immutable
|
|
362
|
+
briefs, claim dispositions, and explicit affected-contradiction dispositions.
|
|
360
363
|
|
|
361
364
|
Create immutable mode-specific review projections with
|
|
362
365
|
`scripts/create-review-brief.mjs`. Verification briefs expose only claim
|