taskforce-loop-engineering 0.15.16 → 0.15.17
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
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.15.17 - 2026-09-13
|
|
6
|
+
|
|
7
|
+
- Stop OpenClaw doctor and disposable smoke probes from attaching the synthetic `doctor` account to dry-run notifications.
|
|
8
|
+
- Preserve normal account forwarding for real source-bound notifications while remaining compatible with OpenClaw installations that reject unknown account ids during dry-run validation.
|
|
9
|
+
- Add strict installer regression coverage that rejects any synthetic `--account doctor` delivery.
|
|
10
|
+
|
|
5
11
|
## 0.15.16 - 2026-09-13
|
|
6
12
|
|
|
7
13
|
- Resolve canonical project identifiers from nested checkpoint completion metadata so migrated project tasks continue against the correct authoritative backlog.
|
package/package.json
CHANGED
|
@@ -70,7 +70,7 @@ async function main() {
|
|
|
70
70
|
if (await present(notifier)) {
|
|
71
71
|
const smoke = await run(process.execPath, [notifier, 'Loop Engineering notification dry-run'], {
|
|
72
72
|
cwd: args.root,
|
|
73
|
-
env: { ...process.env, LOOP_NOTIFICATION_DRY_RUN: '1', LOOP_NOTIFICATION_SOURCE: JSON.stringify({ channel: 'feishu', target: 'user:loop-doctor-dry-run',
|
|
73
|
+
env: { ...process.env, LOOP_NOTIFICATION_DRY_RUN: '1', LOOP_NOTIFICATION_SOURCE: JSON.stringify({ channel: 'feishu', target: 'user:loop-doctor-dry-run', reply_to: 'doctor-message' }) }
|
|
74
74
|
});
|
|
75
75
|
checks.push({ id: 'notification_dry_run', ok: smoke.code === 0, detail: (smoke.stdout || smoke.stderr).trim().slice(0, 500) });
|
|
76
76
|
}
|
|
@@ -15,6 +15,10 @@ await writeFile(mockOpenClaw, `#!/usr/bin/env node
|
|
|
15
15
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
16
16
|
import path from 'node:path';
|
|
17
17
|
const args = process.argv.slice(2);
|
|
18
|
+
if (args.includes('--account') && args[args.indexOf('--account') + 1] === 'doctor') {
|
|
19
|
+
console.error('Unknown account: doctor');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
18
22
|
if (args[0] === '--version') console.log('OpenClaw mock 1.0');
|
|
19
23
|
else if (args[0] === 'agents' && args[1] === 'list') console.log(JSON.stringify([{ id: 'builder' }]));
|
|
20
24
|
else {
|
|
@@ -132,6 +136,7 @@ const doctorResult = await new Promise((resolve) => {
|
|
|
132
136
|
if (doctorResult.code !== 0) throw new Error(`doctor failed: ${doctorResult.stderr}`);
|
|
133
137
|
const doctorReport = JSON.parse(doctorResult.stdout);
|
|
134
138
|
if (doctorReport.status !== 'ok' || doctorReport.externalWrite !== false || !doctorReport.checks.some((check) => check.id === 'notification_dry_run' && check.ok) || !doctorReport.checks.some((check) => check.id === 'human_gate_bridge_self_test' && check.ok)) throw new Error('doctor did not complete safe notification and Human Gate self-tests');
|
|
139
|
+
if (JSON.parse(await readFile(deliveryCapture, 'utf8')).includes('doctor')) throw new Error('doctor notification forwarded a synthetic account');
|
|
135
140
|
const smoke = new URL('./openclaw-smoke.mjs', import.meta.url).pathname;
|
|
136
141
|
const smokeSource = await readFile(smoke, 'utf8');
|
|
137
142
|
if (!smokeSource.includes('Do not change user or project files, configuration, credentials, or external state.')
|
|
@@ -151,6 +156,7 @@ const smokeResult = await new Promise((resolve) => {
|
|
|
151
156
|
if (smokeResult.code !== 0) throw new Error(`smoke failed: ${smokeResult.stderr}`);
|
|
152
157
|
const smokeReport = JSON.parse(smokeResult.stdout);
|
|
153
158
|
if (smokeReport.status !== 'ok' || smokeReport.externalWrite !== false || !smokeReport.steps.every((step) => step.ok)) throw new Error('end-to-end smoke did not pass safely');
|
|
159
|
+
if (JSON.parse(await readFile(deliveryCapture, 'utf8')).includes('doctor')) throw new Error('smoke notification forwarded a synthetic account');
|
|
154
160
|
try { await readFile(path.join(root, `configs/loops/queues/${smokeReport.smokeQueue}.json`)); throw new Error('smoke config was not cleaned'); } catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
155
161
|
try { await readFile(path.join(root, `runtime/loops/${smokeReport.smokeQueue}/state.json`)); throw new Error('smoke runtime was not cleaned'); } catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
156
162
|
for (const generated of ['scripts/loops/openclaw-loop-dispatch.mjs', 'scripts/loops/openclaw-loop.mjs', 'scripts/loops/openclaw-loop-notify.mjs', 'scripts/loops/openclaw-loop-gate.mjs']) {
|
|
@@ -65,7 +65,7 @@ async function main() {
|
|
|
65
65
|
await writeFile(smokeConfig, `${JSON.stringify(config, null, 2)}\n`);
|
|
66
66
|
const smokeRuntimeRel = path.relative(args.root, smokeRuntime);
|
|
67
67
|
const message = `走 loop:Perform a read-only integration smoke. Do not change user or project files, configuration, credentials, or external state. Writing the required Loop checkpoint and verification evidence under ${smokeRuntimeRel}/ is allowed and required; do not write anywhere else. Report SMOKE_OK with verification evidence.`;
|
|
68
|
-
const route = await run(args.loopBin, ['route-message', '--root', args.root, '--queue', smokeQueue, '--message', message, '--route', '--confirm-execute', '--source-channel', 'feishu', '--source-target', 'user:loop-smoke-dry-run', '--source-
|
|
68
|
+
const route = await run(args.loopBin, ['route-message', '--root', args.root, '--queue', smokeQueue, '--message', message, '--route', '--confirm-execute', '--source-channel', 'feishu', '--source-target', 'user:loop-smoke-dry-run', '--source-message-id', `smoke-${runToken}`, '--source-reply-to', `smoke-${runToken}`, '--json'], { cwd: args.root });
|
|
69
69
|
steps.push({ id: 'route', ok: route.code === 0 });
|
|
70
70
|
if (route.code !== 0) throw new Error(`route failed: ${route.stderr || route.stdout}`);
|
|
71
71
|
const routed = JSON.parse(route.stdout);
|