taskforce-loop-engineering 0.7.0 → 0.7.2

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
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.2 - 2026-08-06
4
+
5
+ - Keep `ready_for_human_review` tasks out of `done/` until an explicit human decision is recorded; emit a scoped acceptance notification, fail closed when delivery routing is missing, and transition approved tasks to `completed` only after approval.
6
+ - Forward configured human-gate policy into generated task contracts so queue state and final judgement agree.
7
+ - Add regression coverage for the complete `ready_for_human_review → approve → completed` lifecycle.
8
+
9
+ ## 0.7.1 - 2026-08-05
10
+
11
+ - Make the standalone ClawHub skill self-sufficient by documenting the official npm package, GitHub repository, Node.js requirement, license, and installation commands.
12
+ - Distinguish installing the skill, installing the CLI, and integrating the CLI with OpenClaw.
13
+ - Add plan, confirmed install, doctor, and disposable smoke commands for a verifiable OpenClaw deployment.
14
+ - Require checking an exact workspace source path before using the source-only CLI fallback.
15
+
3
16
  ## 0.7.0 - 2026-08-05
4
17
 
5
18
  - Rename the project, npm package, GitHub repository, and bundled skill to Taskforce Loop Engineering.
package/lib/core.mjs CHANGED
@@ -1673,13 +1673,20 @@ export async function routeLoopMessage(root, options = {}) {
1673
1673
  }
1674
1674
 
1675
1675
  function terminalNotificationMessage(queue, task) {
1676
- const needsHuman = ['needs_human_input', 'blocked'].includes(task.status);
1676
+ const needsReview = task.status === 'ready_for_human_review';
1677
+ const needsHuman = ['needs_human_input', 'blocked', 'ready_for_human_review'].includes(task.status);
1677
1678
  return [
1678
- needsHuman ? 'Loop task needs human input' : 'Loop task reached a terminal state',
1679
+ needsReview ? 'Loop task is ready for human acceptance'
1680
+ : needsHuman ? 'Loop task needs human input'
1681
+ : 'Loop task reached a terminal state',
1679
1682
  `task: ${task.title}`,
1680
1683
  `queue: ${queue}`,
1681
1684
  `status: ${task.status}`,
1682
- ...(needsHuman ? ['next: inspect the task final judgement and checkpoints, resolve the blocker, then explicitly continue or requeue.'] : [])
1685
+ ...(needsReview
1686
+ ? [`next: review the final judgement and record approve, request_changes, or reject for task ${task.id}.`]
1687
+ : needsHuman
1688
+ ? ['next: inspect the task final judgement and checkpoints, resolve the blocker, then explicitly continue or requeue.']
1689
+ : [])
1683
1690
  ].join('\n');
1684
1691
  }
1685
1692
 
@@ -1696,7 +1703,16 @@ export async function notifyTerminalTasks(root, options = {}) {
1696
1703
  const dir = queueSubdirFor(root, queue, subdir);
1697
1704
  for (const file of await listJson(dir)) {
1698
1705
  const task = await readJson(path.join(dir, file));
1699
- if (!task?.source || !task?.id || !task?.status) continue;
1706
+ if (!task?.id || !task?.status) continue;
1707
+ if (!task?.source?.channel || !task?.source?.target) {
1708
+ results.push({
1709
+ taskId: task.id,
1710
+ status: task.status,
1711
+ outcome: 'failed',
1712
+ error: 'missing source.channel/source.target; refusing unscoped terminal delivery'
1713
+ });
1714
+ continue;
1715
+ }
1700
1716
  const key = `${safeTaskId(task.id)}.${normalizeLoopId(task.status)}.json`;
1701
1717
  const ledgerFile = path.join(notificationDir, key);
1702
1718
  if (await exists(ledgerFile)) {
@@ -2792,7 +2808,8 @@ function queueStatusFromFinalJudgement(currentStatus, finalJudgement) {
2792
2808
  if (!finalJudgement?.judgement) return currentStatus;
2793
2809
  if (currentStatus !== 'completed') return currentStatus;
2794
2810
  const outcome = finalJudgement.judgement.outcome;
2795
- if (outcome === 'ready_for_human_review' || outcome === 'ready_to_apply') return currentStatus;
2811
+ if (outcome === 'ready_to_apply') return currentStatus;
2812
+ if (outcome === 'ready_for_human_review') return outcome;
2796
2813
  return outcome;
2797
2814
  }
2798
2815
 
@@ -4069,6 +4086,27 @@ export async function queueHumanDecision(root, queue, taskId, options = {}) {
4069
4086
  };
4070
4087
  await writeJson(file, artifact);
4071
4088
 
4089
+ let transitionedTask = null;
4090
+ if (decision === 'approve' && found.subdir !== 'done') {
4091
+ const completedAt = artifact.created_at;
4092
+ transitionedTask = {
4093
+ ...task,
4094
+ status: 'completed',
4095
+ humanApprovedAt: completedAt,
4096
+ humanReviewDecision: path.relative(root, file)
4097
+ };
4098
+ const completedFile = path.join(queueSubdirFor(root, normalized, 'done'), path.basename(found.file));
4099
+ await writeJson(completedFile, transitionedTask);
4100
+ await rm(found.file, { force: true });
4101
+ artifact.effects.queueTransition = {
4102
+ from: found.subdir,
4103
+ to: 'done',
4104
+ status: 'completed',
4105
+ file: path.relative(root, completedFile)
4106
+ };
4107
+ await writeJson(file, artifact);
4108
+ }
4109
+
4072
4110
  let revisionRequest = null;
4073
4111
  if (decision === 'request_changes') {
4074
4112
  const revisionFile = path.join(dir, 'human_revision_request.json');
@@ -4098,6 +4136,7 @@ export async function queueHumanDecision(root, queue, taskId, options = {}) {
4098
4136
  decisionFile: path.relative(root, file),
4099
4137
  revisionRequestFile: revisionRequest?.path ?? null,
4100
4138
  revisionNext,
4139
+ transitionedTask,
4101
4140
  artifact
4102
4141
  };
4103
4142
  }
@@ -7901,7 +7940,11 @@ export async function runQueueOnce(root, options) {
7901
7940
  let revisionRequest = null;
7902
7941
 
7903
7942
  try {
7904
- taskContract = await writeTaskContract(root, queue, task);
7943
+ taskContract = await writeTaskContract(root, queue, task, {
7944
+ riskLevel: options.riskLevel,
7945
+ riskReasons: options.riskReasons,
7946
+ requiresHumanGate: options.requiresHumanGate
7947
+ });
7905
7948
  progress.emit('planning', 'task_contract', `Wrote task contract (${taskContract.contract.risk_level})`, {
7906
7949
  taskId: task.id,
7907
7950
  artifact: taskContract.file,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taskforce-loop-engineering",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "private": false,
5
5
  "description": "OpenClaw-native loop engineering CLI, templates, and skill for verifiable agent work loops.",
6
6
  "type": "module",
@@ -10,6 +10,8 @@ import {
10
10
  normalizeGoalDecision,
11
11
  notifyHumanInputRequests,
12
12
  notifyTerminalTasks,
13
+ queueHumanDecision,
14
+ queueStatus,
13
15
  queueSubdirFor,
14
16
  readJson,
15
17
  routeLoopMessage,
@@ -76,6 +78,55 @@ const contract = await readJson(path.join(taskRuntimeDirFor(root, queue, routed.
76
78
  assert.equal(contract.risk_level, 'model_assessed');
77
79
  assert.equal(contract.requires_human_gate, false);
78
80
 
81
+ const reviewQueue = 'human-review-smoke';
82
+ const reviewTask = await routeLoopMessage(root, {
83
+ route: true,
84
+ confirmExecute: true,
85
+ queue: reviewQueue,
86
+ message: '走 loop 生成需要人工验收的交付物',
87
+ sourceChannel: 'feishu',
88
+ sourceTarget: 'user-1'
89
+ });
90
+ let reviewCheckpointWrite = null;
91
+ const reviewRun = await runQueueOnce(root, {
92
+ queue: reviewQueue,
93
+ dispatcher: '/bin/sleep 0.1',
94
+ requiresHumanGate: true,
95
+ timeoutMs: 10_000,
96
+ leaseMs: 20_000,
97
+ staleActiveMs: 60_000,
98
+ onProgress: (event) => {
99
+ if (event.phase !== 'dispatch' || event.status !== 'running' || reviewCheckpointWrite) return;
100
+ reviewCheckpointWrite = writeJson(path.join(taskRuntimeDirFor(root, reviewQueue, reviewTask.task.id), 'checkpoints', 'cp-review.json'), {
101
+ version: 1,
102
+ task_id: reviewTask.task.id,
103
+ checkpoint_id: 'cp-review',
104
+ status: 'ready_for_acceptance',
105
+ summary: 'The deliverable is complete and explicitly awaits human acceptance.',
106
+ files_changed: [],
107
+ verification: [{ command: '/bin/true', outcome: 'passed' }],
108
+ blockers: [],
109
+ risks: [],
110
+ next_action: 'human_acceptance'
111
+ });
112
+ }
113
+ });
114
+ await reviewCheckpointWrite;
115
+ assert.equal(reviewRun.status, 'ready_for_human_review');
116
+ assert.match(reviewRun.taskPath, /failed/);
117
+ const reviewStatus = await queueStatus(root, reviewQueue);
118
+ assert.equal(reviewStatus.done, 0);
119
+ assert.equal(reviewStatus.failed, 1);
120
+ const reviewNotice = await notifyTerminalTasks(root, { queue: reviewQueue, dryRun: true });
121
+ assert.equal(reviewNotice.results[0].outcome, 'dry_run');
122
+ assert.match(reviewNotice.results[0].message, /ready for human acceptance/);
123
+ assert.match(reviewNotice.results[0].message, /approve, request_changes, or reject/);
124
+ const reviewDecision = await queueHumanDecision(root, reviewQueue, reviewTask.task.id, { decision: 'approve' });
125
+ assert.equal(reviewDecision.transitionedTask.status, 'completed');
126
+ const approvedStatus = await queueStatus(root, reviewQueue);
127
+ assert.equal(approvedStatus.done, 1);
128
+ assert.equal(approvedStatus.failed, 0);
129
+
79
130
  for (const suffix of ['second', 'third']) {
80
131
  await routeLoopMessage(root, {
81
132
  route: true,
@@ -9,6 +9,85 @@ Use this skill only when the user explicitly invokes Loop Engineering, says `走
9
9
 
10
10
  Do not route ordinary chat, research, explanations, or simple direct tasks into a loop unless the user explicitly invokes it.
11
11
 
12
+ ## Distribution and CLI Installation
13
+
14
+ A ClawHub installation may provide only this `SKILL.md`; it does **not** prove that the Loop Engineering CLI or OpenClaw integration is installed. Before running loop commands, check the deployment explicitly:
15
+
16
+ ```bash
17
+ command -v loop-engineering
18
+ loop-engineering --help
19
+ ```
20
+
21
+ Official distribution:
22
+
23
+ - npm package: `taskforce-loop-engineering`
24
+ - GitHub repository: `https://github.com/ambitioncn/taskforce-loop-engineering`
25
+ - ClawHub skill: `https://clawhub.ai/ambitioncn/skills/taskforce-loop-engineering`
26
+ - license: Apache-2.0
27
+ - runtime requirement: Node.js 22 or newer
28
+
29
+ Install the CLI globally from npm:
30
+
31
+ ```bash
32
+ node --version
33
+ npm install -g taskforce-loop-engineering
34
+ loop-engineering --help
35
+ ```
36
+
37
+ For a temporary read-only invocation without a global install:
38
+
39
+ ```bash
40
+ npx -p taskforce-loop-engineering loop-engineering --help
41
+ ```
42
+
43
+ For source-based development, clone the official repository and install its dependencies:
44
+
45
+ ```bash
46
+ git clone https://github.com/ambitioncn/taskforce-loop-engineering.git
47
+ cd taskforce-loop-engineering
48
+ npm install
49
+ npm run check
50
+ node bin/loop-engineering.mjs --help
51
+ ```
52
+
53
+ Do not guess a workspace source path. Use `node packages/loop-engineering/bin/loop-engineering.mjs ...` only after confirming that exact path exists in the current workspace.
54
+
55
+ ### OpenClaw Integration
56
+
57
+ Installing the npm package exposes the CLI, but it does not automatically route conversations, select a worker agent, or create queue wrappers. First generate a read-only installation plan:
58
+
59
+ ```bash
60
+ loop-engineering-openclaw-install \
61
+ --root /path/to/openclaw/workspace \
62
+ --queue agent-tasks
63
+ ```
64
+
65
+ Review the detected agents and planned files. Then install with an existing worker-agent id:
66
+
67
+ ```bash
68
+ loop-engineering-openclaw-install \
69
+ --root /path/to/openclaw/workspace \
70
+ --queue agent-tasks \
71
+ --worker-agent main \
72
+ --confirm-install
73
+ ```
74
+
75
+ The installer never creates the worker agent. After installation, verify wiring before using a real task:
76
+
77
+ ```bash
78
+ loop-engineering-openclaw-doctor \
79
+ --root /path/to/openclaw/workspace \
80
+ --queue agent-tasks \
81
+ --worker-agent main
82
+
83
+ loop-engineering-openclaw-smoke \
84
+ --root /path/to/openclaw/workspace \
85
+ --queue agent-tasks \
86
+ --worker-agent main
87
+ ```
88
+
89
+ If the CLI or integration is missing and the user requested installation or repair, install it within the authorized host/workspace scope, then run doctor and the disposable smoke. If the user only asked what is missing, report the exact package, repository, commands, and current deployment state without mutating the system.
90
+
12
91
  ## Conversation Contract
13
92
 
14
93
  Interpret explicit loop language as follows: