lemming-cli 0.3.0__tar.gz → 0.3.2__tar.gz

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.
Files changed (135) hide show
  1. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/PKG-INFO +1 -1
  2. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docs/HOOKS.md +7 -1
  3. lemming_cli-0.3.2/docs/screenshots/dashboard-desktop.png +0 -0
  4. lemming_cli-0.3.2/docs/screenshots/dashboard-mobile.png +0 -0
  5. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/pyproject.toml +1 -1
  6. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/tasks_test.py +13 -0
  7. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/progress_test.py +20 -0
  8. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/tasks.py +2 -2
  9. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/tasks_test.py +12 -0
  10. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/models.py +2 -0
  11. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/models_test.py +2 -0
  12. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/orchestrator.py +7 -1
  13. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/orchestrator_test.py +35 -2
  14. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/hooks/90-roadmap.md +14 -9
  15. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/taskrunner.md +21 -21
  16. lemming_cli-0.3.2/src/lemming/prompts.py +525 -0
  17. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts_test.py +106 -3
  18. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/runner.py +13 -0
  19. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/runner_test.py +49 -1
  20. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/__init__.py +9 -0
  21. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/lifecycle.py +42 -11
  22. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/lifecycle_test.py +80 -13
  23. lemming_cli-0.3.2/src/lemming/tasks/limits.py +45 -0
  24. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/operations.py +12 -1
  25. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/operations_test.py +67 -1
  26. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/progress.py +2 -0
  27. lemming_cli-0.3.2/src/lemming/tasks/progress_test.py +54 -0
  28. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/dashboard.spec.js +37 -0
  29. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/dashboard.test.js +46 -1
  30. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/index.js +15 -1
  31. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/uv.lock +1 -1
  32. lemming_cli-0.3.0/docs/screenshots/dashboard-desktop.png +0 -0
  33. lemming_cli-0.3.0/docs/screenshots/dashboard-mobile.png +0 -0
  34. lemming_cli-0.3.0/src/lemming/prompts.py +0 -267
  35. lemming_cli-0.3.0/src/lemming/tasks/progress_test.py +0 -22
  36. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.github/workflows/ci.yml +0 -0
  37. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.github/workflows/publish.yml +0 -0
  38. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.github/workflows/screenshots.yml +0 -0
  39. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.gitignore +0 -0
  40. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.prettierignore +0 -0
  41. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/.prettierrc +0 -0
  42. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/Dockerfile +0 -0
  43. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/LICENSE +0 -0
  44. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/README.md +0 -0
  45. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/biome.json +0 -0
  46. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docker-compose.yml +0 -0
  47. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docs/EVALS.md +0 -0
  48. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docs/screenshots/task-log-desktop.png +0 -0
  49. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/docs/screenshots/task-log-mobile.png +0 -0
  50. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/package-lock.json +0 -0
  51. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/package.json +0 -0
  52. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/playwright.config.js +0 -0
  53. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/__init__.py +0 -0
  54. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/__init__.py +0 -0
  55. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/auth.py +0 -0
  56. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/auth_test.py +0 -0
  57. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/config.py +0 -0
  58. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/config_test.py +0 -0
  59. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/conftest.py +0 -0
  60. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/context.py +0 -0
  61. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/context_test.py +0 -0
  62. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/directories.py +0 -0
  63. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/directories_test.py +0 -0
  64. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/files.py +0 -0
  65. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/files_test.py +0 -0
  66. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/hooks.py +0 -0
  67. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/hooks_test.py +0 -0
  68. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/logging.py +0 -0
  69. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/logging_test.py +0 -0
  70. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/loop.py +0 -0
  71. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/loop_test.py +0 -0
  72. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/main.py +0 -0
  73. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/main_test.py +0 -0
  74. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api/tasks.py +0 -0
  75. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/api_test.py +0 -0
  76. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/__init__.py +0 -0
  77. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/config.py +0 -0
  78. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/config_test.py +0 -0
  79. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/goal.py +0 -0
  80. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/goal_test.py +0 -0
  81. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/hooks.py +0 -0
  82. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/hooks_test.py +0 -0
  83. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/main.py +0 -0
  84. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/main_test.py +0 -0
  85. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/operations.py +0 -0
  86. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/operations_test.py +0 -0
  87. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/progress.py +0 -0
  88. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/readability_cli.py +0 -0
  89. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli/readability_cli_test.py +0 -0
  90. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/cli_test.py +0 -0
  91. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/conftest.py +0 -0
  92. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/__init__.py +0 -0
  93. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/__main__.py +0 -0
  94. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/cli.py +0 -0
  95. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/cli_test.py +0 -0
  96. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/container.py +0 -0
  97. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/container_test.py +0 -0
  98. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/fixtures.py +0 -0
  99. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/fixtures_test.py +0 -0
  100. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/harness.py +0 -0
  101. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/harness_test.py +0 -0
  102. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/readability.py +0 -0
  103. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/readability_test.py +0 -0
  104. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/roadmap.py +0 -0
  105. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/roadmap_test.py +0 -0
  106. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/scenarios.py +0 -0
  107. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/scenarios_test.py +0 -0
  108. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/suites.py +0 -0
  109. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/trial.py +0 -0
  110. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/evals/trial_test.py +0 -0
  111. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/hooks.py +0 -0
  112. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/hooks_test.py +0 -0
  113. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/integration_test.py +0 -0
  114. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/main.py +0 -0
  115. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/main_test.py +0 -0
  116. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/paths.py +0 -0
  117. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/paths_test.py +0 -0
  118. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/persistence.py +0 -0
  119. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/persistence_test.py +0 -0
  120. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/hooks/50-readability.md +0 -0
  121. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/hooks/60-testing.md +0 -0
  122. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/prompts/hooks/70-ux.md +0 -0
  123. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/providers.py +0 -0
  124. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/providers_test.py +0 -0
  125. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/queries.py +0 -0
  126. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/tasks/queries_test.py +0 -0
  127. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/favicon.js +0 -0
  128. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/favicon.spec.js +0 -0
  129. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/files.html +0 -0
  130. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/files.spec.js +0 -0
  131. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/index.html +0 -0
  132. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/logs.html +0 -0
  133. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/logs.test.js +0 -0
  134. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/mancha.js +0 -0
  135. {lemming_cli-0.3.0 → lemming_cli-0.3.2}/src/lemming/web/screenshots.spec.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lemming-cli
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: An autonomous, iterative task runner for AI coding agents.
5
5
  Project-URL: Homepage, https://github.com/owahltinez/lemming
6
6
  Project-URL: Repository, https://github.com/owahltinez/lemming
@@ -155,11 +155,17 @@ Your hook template can use the following placeholders:
155
155
 
156
156
  - `{{roadmap}}`: A structured summary of the long-term goal and all tasks.
157
157
  - `{{finished_task}}`: Details about the task that just finished (ID,
158
- description, progress, and the last 100 lines of its execution log).
158
+ description, recent progress, and a recent execution-log excerpt capped at
159
+ 16 KiB).
159
160
  - `{{finished_task_id}}`: The ID of the task that just finished.
160
161
  - `{{tasks_file_name}}`: The filename of the tasks YAML file.
161
162
  - `{{tasks_file_path}}`: The full path to the tasks YAML file.
162
163
 
164
+ Roadmap context is size-bounded and prioritizes current and actionable tasks
165
+ over older completed history. Readability, testing, and UX hooks receive a
166
+ smaller roadmap summary because their primary context is the finished task and
167
+ the workspace.
168
+
163
169
  ## Developer Ergonomics
164
170
 
165
171
  ### Listing Hooks
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "lemming-cli"
7
- version = "0.3.0"
7
+ version = "0.3.2"
8
8
  description = "An autonomous, iterative task runner for AI coding agents."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -30,6 +30,19 @@ def test_add_task(client, test_tasks):
30
30
  assert any(t.description == "New task from test" for t in roadmap.tasks)
31
31
 
32
32
 
33
+ def test_add_task_rejects_oversized_description(client, test_tasks):
34
+ response = client.post(
35
+ "/api/tasks",
36
+ json={
37
+ "description": "x" * (tasks.MAX_TASK_DESCRIPTION_CHARS + 1),
38
+ },
39
+ )
40
+
41
+ assert response.status_code == 400
42
+ assert "2,001 characters (limit 2,000)" in response.json()["detail"]
43
+ assert len(tasks.load_tasks(test_tasks).tasks) == 3
44
+
45
+
33
46
  def test_add_task_with_runner(client, test_tasks):
34
47
  response = client.post(
35
48
  "/api/tasks", json={"description": "Runner task", "runner": "claude"}
@@ -38,3 +38,23 @@ def test_progress(setup_env):
38
38
 
39
39
  data = tasks.load_tasks(tasks_file)
40
40
  assert "Observed behavior X" in data.tasks[0].progress
41
+
42
+
43
+ def test_progress_reports_actionable_size_error(setup_env):
44
+ runner, base_args, tasks_file = setup_env
45
+
46
+ result = runner.invoke(
47
+ cli.cli,
48
+ base_args
49
+ + [
50
+ "progress",
51
+ "12345678",
52
+ "x" * (tasks.MAX_PROGRESS_ENTRY_CHARS + 1),
53
+ ],
54
+ )
55
+
56
+ assert result.exit_code == 1
57
+ assert "281 characters (limit 280)" in result.output
58
+ assert "Write detailed evidence or verbose command output" in result.output
59
+ assert str(tasks_file.parent) not in result.output
60
+ assert tasks.load_tasks(tasks_file).tasks[0].progress == []
@@ -645,11 +645,11 @@ def fail(ctx: click.Context, task_id: str):
645
645
  ctx.exit(1)
646
646
 
647
647
 
648
- @cli.command(short_help="<taskid> Stop an in-progress task")
648
+ @cli.command(short_help="<taskid> Cancel a pending or active task")
649
649
  @click.argument("task_id")
650
650
  @click.pass_context
651
651
  def cancel(ctx: click.Context, task_id: str):
652
- """Kills the runner and cancels an in-progress task."""
652
+ """Cancels a task, stopping its runner if it is active."""
653
653
  tasks_file = ctx.obj["TASKS_FILE"]
654
654
  if tasks.cancel_task(tasks_file, task_id):
655
655
  click.echo(f"Task {task_id} cancelled.")
@@ -51,6 +51,18 @@ class TestCLITasks(unittest.TestCase):
51
51
  task_descs = [t.description for t in data.tasks]
52
52
  self.assertIn("New Task", task_descs)
53
53
 
54
+ def test_add_task_reports_actionable_size_error(self):
55
+ result = self.cli_runner.invoke(
56
+ cli.cli,
57
+ self.base_args
58
+ + ["add", "x" * (tasks.MAX_TASK_DESCRIPTION_CHARS + 1)],
59
+ )
60
+
61
+ self.assertEqual(result.exit_code, 1)
62
+ self.assertIn("2,001 characters (limit 2,000)", result.output)
63
+ self.assertIn("Keep the brief task-specific", result.output)
64
+ self.assertEqual(len(tasks.load_tasks(self.test_tasks_file).tasks), 1)
65
+
54
66
  def test_edit_task_description(self):
55
67
  result = self.cli_runner.invoke(
56
68
  cli.cli,
@@ -60,6 +60,8 @@ class Task(pydantic.BaseModel):
60
60
  created_at: float = pydantic.Field(default_factory=time.time)
61
61
  run_time: float = 0.0
62
62
  execution_times: dict[str, float] | None = None
63
+ active_execution_component: str | None = None
64
+ active_execution_started_at: float | None = None
63
65
  pid: int | None = None
64
66
  last_heartbeat: float | None = None
65
67
  has_runner_log: bool = False
@@ -19,6 +19,8 @@ def test_task_model_defaults():
19
19
  assert task.progress == []
20
20
  assert task.run_time == 0.0
21
21
  assert task.execution_times is None
22
+ assert task.active_execution_component is None
23
+ assert task.active_execution_started_at is None
22
24
  assert task.superseded_at is None
23
25
  assert task.superseded_reason is None
24
26
  assert task.created_at > 0
@@ -357,8 +357,14 @@ def _handle_runner_exit(
357
357
  click.echo(stderr, err=True)
358
358
 
359
359
  if returncode == -15:
360
+ if post_task.status == tasks.TaskStatus.CANCELLED:
361
+ if verbose:
362
+ click.echo("Task was cancelled. Continuing orchestrator.")
363
+ return False
360
364
  if verbose:
361
- click.echo("Task was cancelled. Stopping orchestrator loop.")
365
+ click.echo(
366
+ "Runner exited after SIGTERM. Stopping orchestrator loop."
367
+ )
362
368
  return True
363
369
 
364
370
  if runner_failed and not post_task.requested_status:
@@ -483,7 +483,9 @@ def test_handle_runner_exit_completes(mock_sleep, mock_run_hooks, setup_env):
483
483
 
484
484
 
485
485
  @mock.patch("lemming.orchestrator.run_hooks")
486
- def test_handle_runner_exit_cancelled(mock_run_hooks, setup_env):
486
+ def test_handle_runner_exit_stops_on_unexpected_sigterm(
487
+ mock_run_hooks, setup_env
488
+ ):
487
489
  test_tasks_file, initial_data = setup_env
488
490
  data = tasks.load_tasks(test_tasks_file)
489
491
  task = data.tasks[0]
@@ -507,7 +509,38 @@ def test_handle_runner_exit_cancelled(mock_run_hooks, setup_env):
507
509
  working_dir=None,
508
510
  time_limit=1,
509
511
  )
510
- assert should_abort, "Cancellation (-15) should abort the loop"
512
+ assert should_abort
513
+
514
+
515
+ @mock.patch("lemming.orchestrator.run_hooks")
516
+ def test_handle_runner_exit_continues_after_cancellation(
517
+ mock_run_hooks, setup_env
518
+ ):
519
+ test_tasks_file, initial_data = setup_env
520
+ data = tasks.load_tasks(test_tasks_file)
521
+ task = data.tasks[0]
522
+ task.status = tasks.TaskStatus.CANCELLED
523
+ tasks.save_tasks(test_tasks_file, data)
524
+
525
+ should_abort = _handle_runner_exit(
526
+ test_tasks_file,
527
+ task.id,
528
+ returncode=-15,
529
+ stdout="",
530
+ stderr="",
531
+ retries=3,
532
+ retry_delay=5,
533
+ runner_name="agy",
534
+ yolo=True,
535
+ runner_args=(),
536
+ no_defaults=False,
537
+ verbose=False,
538
+ active_hooks=[],
539
+ working_dir=None,
540
+ time_limit=1,
541
+ )
542
+
543
+ assert not should_abort
511
544
 
512
545
 
513
546
  @mock.patch("lemming.orchestrator.time.sleep")
@@ -49,16 +49,21 @@ the plan up-to-date with minimal friction.
49
49
  forgotten teardowns, missing tests reported by the testing hook, or
50
50
  formatting issues reported by the readability hook), add new tasks to
51
51
  address them.
52
- 7. **No Code Changes**: Your only persistent changes may be to the roadmap via
52
+ 7. **Task-Specific Briefs**: Keep new or rewritten task descriptions concise,
53
+ self-contained, and no more than {{max_task_description_chars}} characters.
54
+ Include the concrete files, symbols, motivation, and acceptance criteria the
55
+ task needs, but do not repeat project-wide rules already present in the
56
+ long-term goal.
57
+ 8. **No Code Changes**: Your only persistent changes may be to the roadmap via
53
58
  the `lemming` CLI. Do NOT edit source or configuration files. Reading the
54
- workspace and running existing build, test, and entry-point commands for the
55
- queue-drain audit is explicitly allowed.
56
- 8. **Fast Exit**: If the roadmap is accurate and well-structured, AND there are
57
- no failed tasks that have reached their maximum attempts, you may exit
58
- immediately without running any commands. The queue-drain audit in directive
59
- 4 must finish before this clause applies. If a task is marked as FAILED and
60
- has reached its maximum attempts, a Fast Exit will result in the entire
61
- project ABORTING. In that case, you MUST repair it.
59
+ workspace and running existing build, test, and entry-point commands for
60
+ the queue-drain audit is explicitly allowed.
61
+ 9. **Fast Exit**: If the roadmap is accurate and well-structured, AND there
62
+ are no failed tasks that have reached their maximum attempts, you may exit
63
+ immediately without running any commands. The queue-drain audit in
64
+ directive 4 must finish before this clause applies. If a task is marked as
65
+ FAILED and has reached its maximum attempts, a Fast Exit will result in the
66
+ entire project ABORTING. In that case, you MUST repair it.
62
67
 
63
68
  ## Commands
64
69
 
@@ -36,33 +36,33 @@ Your CURRENT, EXCLUSIVE task is: **{{description}}**
36
36
  a fresh, empty conversation history. Their only context is the global
37
37
  roadmap, previously recorded progress from completed tasks, and the state
38
38
  of the file system.
39
- - **Self-Contained Descriptions:** Because of this isolation, you MUST write
40
- extremely thorough, self-contained descriptions for any new tasks you
41
- schedule. Reference specific task IDs, file paths, and exact symbols so the
42
- new task knows exactly what to do.
43
- - **Complex Handoffs:** If you need to pass extensive context (like detailed
44
- architectural plans, specific error traces, or large code snippets) to a
45
- downstream task, do not rely on the brief progress messages alone. Instead,
46
- create a dedicated file in the workspace to hold this context, and
47
- explicitly mention its path in the new task's description. Use this
48
- technique judiciously to avoid creating a mess of orphaned files in the
49
- project.
39
+ - **Task-Specific Descriptions:** New task descriptions must be concise,
40
+ self-contained, and no more than {{max_task_description_chars}} characters.
41
+ Include the file paths, symbols, motivation, and acceptance criteria that
42
+ task needs, but do not restate project-wide rules already present in the
43
+ long-term goal.
44
+ - **Detailed Evidence:** Keep verbose gate output, transcripts, error traces,
45
+ and other ephemeral evidence in `{{tasks_dir}}`, which is Lemming's
46
+ per-project scratch directory outside the workspace. Reference the file
47
+ from a task description or progress entry when another attempt needs it.
48
+ Put conclusions that must outlive the task in the commit message or a
49
+ decision document in the repository.
50
50
 
51
- 4. **Progress:** Your first action should be to record a brief progress entry
52
- describing how you intend to approach this task e.g. which files you will
53
- modify, what strategy you will use. This lets the operator verify your
54
- direction early. Then continue recording progress as you work: each time you
55
- complete a meaningful step, discover something relevant, or hit a problem,
56
- record it immediately. If your process is killed, recorded progress carries
57
- over to the next attempt. At least one progress entry is required before
51
+ 4. **Progress:** Your first action should be to record a one-line progress entry
52
+ describing your approach. Continue recording concise findings as you work,
53
+ but keep each entry to one line and no more than
54
+ {{max_progress_entry_chars}} characters. Do not paste command output or
55
+ detailed evidence into progress; write it under `{{tasks_dir}}` and reference
56
+ the file instead. If your process is killed, recorded progress carries over
57
+ to the next attempt. At least one progress entry is required before
58
58
  completing or failing a task:
59
59
  `lemming --tasks-file {{tasks_file_path}} progress {{task_id}} '<what you did or found>'`
60
60
  5. **Success:** When you have completely finished and verified the task, and
61
- recorded ALL relevant progress (at least one is required), run:
61
+ recorded relevant progress (at least one entry is required), run:
62
62
  `lemming --tasks-file {{tasks_file_path}} complete {{task_id}}`
63
63
  6. **Failure/Blocker:** If you hit a technical roadblock, cannot fix a bug, or
64
- are unable to complete the task, after recording ALL relevant progress (at
65
- least one is required), run:
64
+ are unable to complete the task, after recording relevant progress (at least
65
+ one entry is required), run:
66
66
  `lemming --tasks-file {{tasks_file_path}} fail {{task_id}}`
67
67
 
68
68
  7. Stop and exit after running either the complete or fail command.