@hasna/loops 0.4.7 → 0.4.8
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 +10 -0
- package/dist/api/index.js +1 -1
- package/dist/cli/index.js +15 -9
- package/dist/daemon/index.js +1 -1
- package/dist/index.js +15 -9
- package/dist/lib/mode.js +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/runner/index.js +1 -1
- package/dist/sdk/index.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ documented in this file. Version entries are generated from the
|
|
|
5
5
|
conventional-commit git history; one commit maps to one released patch version
|
|
6
6
|
unless noted.
|
|
7
7
|
|
|
8
|
+
## 0.4.8 (2026-07-04)
|
|
9
|
+
|
|
10
|
+
Lifecycle prompt hardening for routed task workflows.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Task lifecycle routing:** render copy-safe triage and planner marker comment
|
|
15
|
+
commands so agents can advance or block deterministic lifecycle gates without
|
|
16
|
+
emitting a separate placeholder evidence comment first.
|
|
17
|
+
|
|
8
18
|
## 0.4.7 (2026-07-04)
|
|
9
19
|
|
|
10
20
|
Routing hardening for PR merge/drain automation.
|
package/dist/api/index.js
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -4219,7 +4219,7 @@ class Store {
|
|
|
4219
4219
|
// package.json
|
|
4220
4220
|
var package_default = {
|
|
4221
4221
|
name: "@hasna/loops",
|
|
4222
|
-
version: "0.4.
|
|
4222
|
+
version: "0.4.8",
|
|
4223
4223
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
4224
4224
|
type: "module",
|
|
4225
4225
|
main: "dist/index.js",
|
|
@@ -10505,9 +10505,8 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
10505
10505
|
`) : "";
|
|
10506
10506
|
const shared = [
|
|
10507
10507
|
worktreePrompt(plan),
|
|
10508
|
-
...todosExactCommandsFragment(todosProjectPath, input.taskId, [
|
|
10509
|
-
|
|
10510
|
-
]),
|
|
10508
|
+
...todosExactCommandsFragment(todosProjectPath, input.taskId, []),
|
|
10509
|
+
"Use concrete task-specific text in lifecycle comments. Do not copy placeholder text into lifecycle comments; triage and planner comments must start with the exact stage marker when advancing or blocking the workflow.",
|
|
10511
10510
|
NO_TMUX_DISPATCH_FRAGMENT,
|
|
10512
10511
|
"Preserve unrelated user changes and keep scope tied to the task acceptance criteria.",
|
|
10513
10512
|
prReviewFollowUpFragment(input),
|
|
@@ -10518,6 +10517,8 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
10518
10517
|
`);
|
|
10519
10518
|
const gateMarker = (stage, state) => `openloops:${stage}=${state} task=${input.taskId}${input.eventId ? ` event=${input.eventId}` : ""}`;
|
|
10520
10519
|
const blockTaskCommand = `todos --project ${todosProjectPath} update ${input.taskId} --status blocked`;
|
|
10520
|
+
const markerCommentCommand = (stage, state, evidencePlaceholder) => `todos --project ${todosProjectPath} comment ${input.taskId} "${gateMarker(stage, state)}
|
|
10521
|
+
<${evidencePlaceholder}>"`;
|
|
10521
10522
|
const gateStopFragment = (stage, stops) => `The deterministic ${stage} gate will stop ${stops} unless the latest ${stage} marker is the exact go marker and the task has no blocked/completed/done/cancelled/failed/archived/no-auto/manual/approval-required state.`;
|
|
10522
10523
|
const triagePrompt = [
|
|
10523
10524
|
...boundedStepHeaderFragment(`Triage todos task ${input.taskId} for safe automated execution.`, "triage", "lifecycle"),
|
|
@@ -10525,9 +10526,12 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
10525
10526
|
"Decide whether the task is eligible for loop execution. Check status, dependencies, duplicate tasks, no-auto/manual/approval metadata, project path, acceptance criteria, and whether the requested work should be split before implementation.",
|
|
10526
10527
|
"Do not implement repo changes in this step.",
|
|
10527
10528
|
`If the task is eligible for automated planning, add a task comment whose first line is exactly: ${gateMarker("triage", "go")}`,
|
|
10528
|
-
|
|
10529
|
+
`Use this copy-safe marker comment command for triage go: ${markerCommentCommand("triage", "go", "task-specific triage evidence")}`,
|
|
10530
|
+
"Do not run a separate generic evidence comment before the marker; include the triage decision, duplicates/dependencies found, and any follow-up tasks created in that same marker comment.",
|
|
10529
10531
|
`If the task should not proceed automatically, run: ${blockTaskCommand}`,
|
|
10530
10532
|
`Then add a task comment whose first line is exactly: ${gateMarker("triage", "blocked")}`,
|
|
10533
|
+
`Use this copy-safe marker comment command for triage blocked: ${markerCommentCommand("triage", "blocked", "task-specific triage evidence")}`,
|
|
10534
|
+
"Do not run a separate generic blocker comment before the marker; include the blocker evidence in that same marker comment.",
|
|
10531
10535
|
gateStopFragment("triage", "later steps")
|
|
10532
10536
|
].join(`
|
|
10533
10537
|
`);
|
|
@@ -10536,17 +10540,19 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
10536
10540
|
shared,
|
|
10537
10541
|
"Read the triage comment and current task details.",
|
|
10538
10542
|
`If the task is ready for implementation, add a task comment whose first line is exactly: ${gateMarker("planner", "go")}`,
|
|
10539
|
-
|
|
10543
|
+
`Use this copy-safe marker comment command for planner go: ${markerCommentCommand("planner", "go", "task-specific plan/evidence")}`,
|
|
10544
|
+
"Do not run a separate generic evidence comment before the marker; in that same marker comment, include a concise implementation plan: files/areas to inspect, validation commands, risk checks, expected commit/PR behavior, and any cross-repo tasks that should be created separately.",
|
|
10540
10545
|
`Do not implement repo changes in this step. If the task is too broad or unsafe for automation, run: ${blockTaskCommand}`,
|
|
10541
10546
|
`Then add a task comment whose first line is exactly: ${gateMarker("planner", "blocked")}`,
|
|
10542
|
-
`
|
|
10547
|
+
`Use this copy-safe marker comment command for planner blocked: ${markerCommentCommand("planner", "blocked", "task-specific plan/evidence")}`,
|
|
10548
|
+
`Do not run a separate generic blocker comment before the marker; create smaller deduped tasks and record blocker evidence in that same marker comment. ${gateStopFragment("planner", "the worker")}`
|
|
10543
10549
|
].join(`
|
|
10544
10550
|
`);
|
|
10545
10551
|
const workerPrompt = [
|
|
10546
10552
|
...boundedStepHeaderFragment(`Complete todos task ${input.taskId} according to the planner evidence.`, "worker", "lifecycle"),
|
|
10547
10553
|
shared,
|
|
10548
10554
|
todosStartLine(todosProjectPath, input.taskId),
|
|
10549
|
-
"Read the triage and planner comments first. Implement only the scoped task, run focused validation, and record changed files, commits,
|
|
10555
|
+
"Read the triage and planner comments first. Implement only the scoped task, run focused validation, and record concrete worker evidence in todos: changed files, commits, validation results, blockers, and residual risks.",
|
|
10550
10556
|
input.prHandoff ? `When only GitHub network access is blocked after a successful commit/validation, record the handoff artifact at ${handoffArtifactPath} instead of repeatedly retrying push/PR creation.` : undefined,
|
|
10551
10557
|
WORKER_LEAVES_COMPLETION_FRAGMENT
|
|
10552
10558
|
].filter(Boolean).join(`
|
|
@@ -10554,7 +10560,7 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
10554
10560
|
const verifierPrompt = [
|
|
10555
10561
|
...boundedStepHeaderFragment(`Verify todos task ${input.taskId} after the full lifecycle worker step.`, "verifier", "lifecycle"),
|
|
10556
10562
|
shared,
|
|
10557
|
-
|
|
10563
|
+
"Before completion, record concrete verification evidence in todos with changed files, validation results, findings, and the task decision.",
|
|
10558
10564
|
todosDoneLine(todosProjectPath, input.taskId),
|
|
10559
10565
|
adversarialReviewFragment("triage, plan, worker evidence, repo state, commits, tests, and acceptance criteria", TASK_REVIEW_FOCUS),
|
|
10560
10566
|
verifierRuntimeGuidance(input),
|
package/dist/daemon/index.js
CHANGED
|
@@ -8003,7 +8003,7 @@ function enableStartup(result) {
|
|
|
8003
8003
|
// package.json
|
|
8004
8004
|
var package_default = {
|
|
8005
8005
|
name: "@hasna/loops",
|
|
8006
|
-
version: "0.4.
|
|
8006
|
+
version: "0.4.8",
|
|
8007
8007
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
8008
8008
|
type: "module",
|
|
8009
8009
|
main: "dist/index.js",
|
package/dist/index.js
CHANGED
|
@@ -4217,7 +4217,7 @@ class Store {
|
|
|
4217
4217
|
// package.json
|
|
4218
4218
|
var package_default = {
|
|
4219
4219
|
name: "@hasna/loops",
|
|
4220
|
-
version: "0.4.
|
|
4220
|
+
version: "0.4.8",
|
|
4221
4221
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
4222
4222
|
type: "module",
|
|
4223
4223
|
main: "dist/index.js",
|
|
@@ -11261,9 +11261,8 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
11261
11261
|
`) : "";
|
|
11262
11262
|
const shared = [
|
|
11263
11263
|
worktreePrompt(plan),
|
|
11264
|
-
...todosExactCommandsFragment(todosProjectPath, input.taskId, [
|
|
11265
|
-
|
|
11266
|
-
]),
|
|
11264
|
+
...todosExactCommandsFragment(todosProjectPath, input.taskId, []),
|
|
11265
|
+
"Use concrete task-specific text in lifecycle comments. Do not copy placeholder text into lifecycle comments; triage and planner comments must start with the exact stage marker when advancing or blocking the workflow.",
|
|
11267
11266
|
NO_TMUX_DISPATCH_FRAGMENT,
|
|
11268
11267
|
"Preserve unrelated user changes and keep scope tied to the task acceptance criteria.",
|
|
11269
11268
|
prReviewFollowUpFragment(input),
|
|
@@ -11274,6 +11273,8 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
11274
11273
|
`);
|
|
11275
11274
|
const gateMarker = (stage, state) => `openloops:${stage}=${state} task=${input.taskId}${input.eventId ? ` event=${input.eventId}` : ""}`;
|
|
11276
11275
|
const blockTaskCommand = `todos --project ${todosProjectPath} update ${input.taskId} --status blocked`;
|
|
11276
|
+
const markerCommentCommand = (stage, state, evidencePlaceholder) => `todos --project ${todosProjectPath} comment ${input.taskId} "${gateMarker(stage, state)}
|
|
11277
|
+
<${evidencePlaceholder}>"`;
|
|
11277
11278
|
const gateStopFragment = (stage, stops) => `The deterministic ${stage} gate will stop ${stops} unless the latest ${stage} marker is the exact go marker and the task has no blocked/completed/done/cancelled/failed/archived/no-auto/manual/approval-required state.`;
|
|
11278
11279
|
const triagePrompt = [
|
|
11279
11280
|
...boundedStepHeaderFragment(`Triage todos task ${input.taskId} for safe automated execution.`, "triage", "lifecycle"),
|
|
@@ -11281,9 +11282,12 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
11281
11282
|
"Decide whether the task is eligible for loop execution. Check status, dependencies, duplicate tasks, no-auto/manual/approval metadata, project path, acceptance criteria, and whether the requested work should be split before implementation.",
|
|
11282
11283
|
"Do not implement repo changes in this step.",
|
|
11283
11284
|
`If the task is eligible for automated planning, add a task comment whose first line is exactly: ${gateMarker("triage", "go")}`,
|
|
11284
|
-
|
|
11285
|
+
`Use this copy-safe marker comment command for triage go: ${markerCommentCommand("triage", "go", "task-specific triage evidence")}`,
|
|
11286
|
+
"Do not run a separate generic evidence comment before the marker; include the triage decision, duplicates/dependencies found, and any follow-up tasks created in that same marker comment.",
|
|
11285
11287
|
`If the task should not proceed automatically, run: ${blockTaskCommand}`,
|
|
11286
11288
|
`Then add a task comment whose first line is exactly: ${gateMarker("triage", "blocked")}`,
|
|
11289
|
+
`Use this copy-safe marker comment command for triage blocked: ${markerCommentCommand("triage", "blocked", "task-specific triage evidence")}`,
|
|
11290
|
+
"Do not run a separate generic blocker comment before the marker; include the blocker evidence in that same marker comment.",
|
|
11287
11291
|
gateStopFragment("triage", "later steps")
|
|
11288
11292
|
].join(`
|
|
11289
11293
|
`);
|
|
@@ -11292,17 +11296,19 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
11292
11296
|
shared,
|
|
11293
11297
|
"Read the triage comment and current task details.",
|
|
11294
11298
|
`If the task is ready for implementation, add a task comment whose first line is exactly: ${gateMarker("planner", "go")}`,
|
|
11295
|
-
|
|
11299
|
+
`Use this copy-safe marker comment command for planner go: ${markerCommentCommand("planner", "go", "task-specific plan/evidence")}`,
|
|
11300
|
+
"Do not run a separate generic evidence comment before the marker; in that same marker comment, include a concise implementation plan: files/areas to inspect, validation commands, risk checks, expected commit/PR behavior, and any cross-repo tasks that should be created separately.",
|
|
11296
11301
|
`Do not implement repo changes in this step. If the task is too broad or unsafe for automation, run: ${blockTaskCommand}`,
|
|
11297
11302
|
`Then add a task comment whose first line is exactly: ${gateMarker("planner", "blocked")}`,
|
|
11298
|
-
`
|
|
11303
|
+
`Use this copy-safe marker comment command for planner blocked: ${markerCommentCommand("planner", "blocked", "task-specific plan/evidence")}`,
|
|
11304
|
+
`Do not run a separate generic blocker comment before the marker; create smaller deduped tasks and record blocker evidence in that same marker comment. ${gateStopFragment("planner", "the worker")}`
|
|
11299
11305
|
].join(`
|
|
11300
11306
|
`);
|
|
11301
11307
|
const workerPrompt = [
|
|
11302
11308
|
...boundedStepHeaderFragment(`Complete todos task ${input.taskId} according to the planner evidence.`, "worker", "lifecycle"),
|
|
11303
11309
|
shared,
|
|
11304
11310
|
todosStartLine(todosProjectPath, input.taskId),
|
|
11305
|
-
"Read the triage and planner comments first. Implement only the scoped task, run focused validation, and record changed files, commits,
|
|
11311
|
+
"Read the triage and planner comments first. Implement only the scoped task, run focused validation, and record concrete worker evidence in todos: changed files, commits, validation results, blockers, and residual risks.",
|
|
11306
11312
|
input.prHandoff ? `When only GitHub network access is blocked after a successful commit/validation, record the handoff artifact at ${handoffArtifactPath} instead of repeatedly retrying push/PR creation.` : undefined,
|
|
11307
11313
|
WORKER_LEAVES_COMPLETION_FRAGMENT
|
|
11308
11314
|
].filter(Boolean).join(`
|
|
@@ -11310,7 +11316,7 @@ function renderTaskLifecycleWorkflow(input) {
|
|
|
11310
11316
|
const verifierPrompt = [
|
|
11311
11317
|
...boundedStepHeaderFragment(`Verify todos task ${input.taskId} after the full lifecycle worker step.`, "verifier", "lifecycle"),
|
|
11312
11318
|
shared,
|
|
11313
|
-
|
|
11319
|
+
"Before completion, record concrete verification evidence in todos with changed files, validation results, findings, and the task decision.",
|
|
11314
11320
|
todosDoneLine(todosProjectPath, input.taskId),
|
|
11315
11321
|
adversarialReviewFragment("triage, plan, worker evidence, repo state, commits, tests, and acceptance criteria", TASK_REVIEW_FOCUS),
|
|
11316
11322
|
verifierRuntimeGuidance(input),
|
package/dist/lib/mode.js
CHANGED
package/dist/mcp/index.js
CHANGED
|
@@ -7719,7 +7719,7 @@ async function tick(deps) {
|
|
|
7719
7719
|
// package.json
|
|
7720
7720
|
var package_default = {
|
|
7721
7721
|
name: "@hasna/loops",
|
|
7722
|
-
version: "0.4.
|
|
7722
|
+
version: "0.4.8",
|
|
7723
7723
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
7724
7724
|
type: "module",
|
|
7725
7725
|
main: "dist/index.js",
|
package/dist/runner/index.js
CHANGED
package/dist/sdk/index.js
CHANGED
|
@@ -4217,7 +4217,7 @@ class Store {
|
|
|
4217
4217
|
// package.json
|
|
4218
4218
|
var package_default = {
|
|
4219
4219
|
name: "@hasna/loops",
|
|
4220
|
-
version: "0.4.
|
|
4220
|
+
version: "0.4.8",
|
|
4221
4221
|
description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
|
|
4222
4222
|
type: "module",
|
|
4223
4223
|
main: "dist/index.js",
|