@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 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
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@hasna/loops",
6
- version: "0.4.7",
6
+ version: "0.4.8",
7
7
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
8
8
  type: "module",
9
9
  main: "dist/index.js",
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.7",
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
- todosEvidenceLine(todosProjectPath, input.taskId, "concise evidence, decision, or blocker")
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
- "Include the triage decision, duplicates/dependencies found, and any follow-up tasks created in that same comment.",
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
- "In that same 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.",
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
- `Create smaller deduped tasks and record evidence. ${gateStopFragment("planner", "the worker")}`
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, evidence, blockers, and residual risks.",
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
- todosVerificationLine(todosProjectPath, input.taskId),
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),
@@ -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.7",
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.7",
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
- todosEvidenceLine(todosProjectPath, input.taskId, "concise evidence, decision, or blocker")
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
- "Include the triage decision, duplicates/dependencies found, and any follow-up tasks created in that same comment.",
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
- "In that same 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.",
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
- `Create smaller deduped tasks and record evidence. ${gateStopFragment("planner", "the worker")}`
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, evidence, blockers, and residual risks.",
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
- todosVerificationLine(todosProjectPath, input.taskId),
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
@@ -2,7 +2,7 @@
2
2
  // package.json
3
3
  var package_default = {
4
4
  name: "@hasna/loops",
5
- version: "0.4.7",
5
+ version: "0.4.8",
6
6
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
7
7
  type: "module",
8
8
  main: "dist/index.js",
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.7",
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",
@@ -3,7 +3,7 @@
3
3
  // package.json
4
4
  var package_default = {
5
5
  name: "@hasna/loops",
6
- version: "0.4.7",
6
+ version: "0.4.8",
7
7
  description: "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
8
8
  type: "module",
9
9
  main: "dist/index.js",
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.7",
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",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/loops",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "Persistent local loop and workflow runner for deterministic commands and headless AI coding agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",