atris 3.57.3 → 3.58.5

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 (175) hide show
  1. package/README.md +12 -1
  2. package/atris/skills/clawhub/member-runtime/SKILL.md +9 -9
  3. package/atris/skills/create-member/SKILL.md +16 -13
  4. package/atris/skills/design/SKILL.md +27 -4
  5. package/atris/skills/engines/SKILL.md +57 -13
  6. package/atris/skills/x-search/SKILL.md +13 -5
  7. package/atris/skills/youtube/SKILL.md +39 -8
  8. package/atris/team/brainstormer/MEMBER.md +2 -2
  9. package/atris/team/executor/MEMBER.md +3 -3
  10. package/atris/team/launcher/MEMBER.md +4 -4
  11. package/atris/team/navigator/MEMBER.md +3 -3
  12. package/atris/team/researcher/MEMBER.md +2 -2
  13. package/atris/team/validator/MEMBER.md +1 -1
  14. package/atris.md +14 -0
  15. package/ax +157 -24
  16. package/bin/atris.js +79 -31
  17. package/commands/aeo.js +4 -4
  18. package/commands/agents.js +1 -1
  19. package/commands/align.js +12 -12
  20. package/commands/analytics.js +3 -3
  21. package/commands/app.js +5 -5
  22. package/commands/ask.js +175 -0
  23. package/commands/auth.js +7 -7
  24. package/commands/autoland.js +29 -5
  25. package/commands/autopilot-front.js +6 -6
  26. package/commands/autopilot.js +90 -90
  27. package/commands/avail.js +5 -5
  28. package/commands/bench.js +63 -1
  29. package/commands/browse.js +2 -2
  30. package/commands/business.js +12 -12
  31. package/commands/card.js +2 -2
  32. package/commands/clean.js +13 -13
  33. package/commands/close.js +297 -15
  34. package/commands/compile.js +13 -13
  35. package/commands/computer.js +94 -27
  36. package/commands/console.js +5 -5
  37. package/commands/decide.js +1 -1
  38. package/commands/deck.js +9 -9
  39. package/commands/drive.js +4 -4
  40. package/commands/engine.js +173 -19
  41. package/commands/errors.js +3 -3
  42. package/commands/experiments.js +18 -1
  43. package/commands/feed.js +2 -2
  44. package/commands/feedback.js +4 -4
  45. package/commands/fleet.js +12 -12
  46. package/commands/game.js +1 -1
  47. package/commands/guide.js +49 -0
  48. package/commands/improve.js +19 -17
  49. package/commands/init.js +67 -19
  50. package/commands/interview.js +7 -7
  51. package/commands/land.js +40 -15
  52. package/commands/learn.js +12 -12
  53. package/commands/lesson.js +2 -2
  54. package/commands/log.js +1 -1
  55. package/commands/loops.js +4 -4
  56. package/commands/member.js +75 -50
  57. package/commands/mission.js +97 -98
  58. package/commands/one-lap.js +1 -1
  59. package/commands/pack.js +74 -8
  60. package/commands/plugin.js +6 -6
  61. package/commands/probe.js +9 -9
  62. package/commands/pull.js +35 -35
  63. package/commands/pulse.js +1 -1
  64. package/commands/push.js +21 -21
  65. package/commands/rainmaker.js +1 -1
  66. package/commands/recap.js +7 -7
  67. package/commands/reel.js +2 -2
  68. package/commands/release.js +3 -3
  69. package/commands/report.js +1 -1
  70. package/commands/review.js +5 -5
  71. package/commands/revisions.js +1 -1
  72. package/commands/run.js +1 -1
  73. package/commands/scout.js +3 -3
  74. package/commands/security-review.js +7 -7
  75. package/commands/serve.js +9 -9
  76. package/commands/setup.js +1 -1
  77. package/commands/sign.js +4 -4
  78. package/commands/signup.js +4 -4
  79. package/commands/site.js +8 -3
  80. package/commands/skill.js +6 -6
  81. package/commands/slop.js +18 -18
  82. package/commands/social.js +3 -3
  83. package/commands/soul.js +14 -14
  84. package/commands/spaceship.js +1 -1
  85. package/commands/status.js +2 -2
  86. package/commands/strings.js +13 -13
  87. package/commands/study.js +4 -4
  88. package/commands/sync.js +8 -8
  89. package/commands/task.js +50 -25
  90. package/commands/teach.js +2 -2
  91. package/commands/team.js +1 -1
  92. package/commands/theme.js +2 -2
  93. package/commands/tree.js +34 -0
  94. package/commands/truth.js +7 -7
  95. package/commands/verify.js +12 -12
  96. package/commands/visualize.js +2 -2
  97. package/commands/workflow.js +97 -40
  98. package/commands/worktree.js +57 -1
  99. package/commands/write.js +9 -9
  100. package/commands/x-search.js +304 -15
  101. package/commands/xp.js +6 -3
  102. package/commands/youtube.js +916 -65
  103. package/lib/accept-verify-gate.js +3 -3
  104. package/lib/activity-stream.js +1 -1
  105. package/lib/apply-gate.js +13 -0
  106. package/lib/auto-accept-certified.js +28 -12
  107. package/lib/autoland.js +6 -6
  108. package/lib/bench/engines.js +59 -8
  109. package/lib/bench/report.js +1 -1
  110. package/lib/bench/runner.js +204 -5
  111. package/lib/bench/tree-render.js +76 -0
  112. package/lib/card.js +1 -1
  113. package/lib/chat-log-scan.js +1 -1
  114. package/lib/claude-boot-block.js +2 -0
  115. package/lib/deck-compose.js +1 -1
  116. package/lib/deck-history.js +2 -2
  117. package/lib/deck-layout.js +1 -1
  118. package/lib/deck-review.js +5 -5
  119. package/lib/deck-schema.js +1 -1
  120. package/lib/default-verifier.js +1 -1
  121. package/lib/developer-api.js +3 -0
  122. package/lib/engine-ask.js +66 -9
  123. package/lib/engine-registry.js +15 -2
  124. package/lib/file-ops.js +1 -1
  125. package/lib/fleet.js +71 -35
  126. package/lib/html-render.js +1 -1
  127. package/lib/intents.js +172 -0
  128. package/lib/journal.js +1 -1
  129. package/lib/known-commands.js +2 -2
  130. package/lib/land-green.js +364 -0
  131. package/lib/lesson-contradiction.js +2 -2
  132. package/lib/lesson-preflight.js +1 -1
  133. package/lib/manifest.js +5 -5
  134. package/lib/member-alive.js +9 -5
  135. package/lib/memory-view.js +3 -3
  136. package/lib/mission-protected-lane.js +1 -1
  137. package/lib/mission-root.js +2 -2
  138. package/lib/orb-context.js +10 -10
  139. package/lib/pack-capabilities.js +861 -3
  140. package/lib/permission-grants.js +2 -2
  141. package/lib/policy-lessons.js +6 -6
  142. package/lib/pulse.js +8 -8
  143. package/lib/receipt-evidence.js +1 -1
  144. package/lib/reel.js +1 -1
  145. package/lib/review-integrity.js +1 -1
  146. package/lib/runner-command.js +35 -5
  147. package/lib/scorecard.js +4 -4
  148. package/lib/security-scan.js +5 -5
  149. package/lib/self-drive.js +1 -1
  150. package/lib/site-publish.js +372 -0
  151. package/lib/site.js +1 -1
  152. package/lib/slides-deck.js +2 -2
  153. package/lib/state-detection.js +2 -2
  154. package/lib/sync-telemetry.js +2 -2
  155. package/lib/task-db.js +17 -10
  156. package/lib/task-explanation.js +1 -1
  157. package/lib/task-proof.js +2 -2
  158. package/lib/task-receipt.js +3 -3
  159. package/lib/theme.js +12 -12
  160. package/lib/todo-fallback.js +3 -3
  161. package/lib/todo-sections.js +1 -1
  162. package/lib/tree-hash.js +79 -0
  163. package/lib/usage.js +1 -1
  164. package/lib/wish-delegate.js +1 -1
  165. package/lib/workspace-safety.js +4 -4
  166. package/lib/workspace-scaffold.js +3 -3
  167. package/package.json +1 -1
  168. package/scripts/det/concierge-bakeoff.sh +26 -0
  169. package/scripts/det/ytquote-repair.js +0 -1
  170. package/scripts/det/ytrail-race.js +0 -5
  171. package/scripts/member-operate.mjs +52 -14
  172. package/scripts/outbound-artifact-gate.js +0 -3
  173. package/utils/auth.js +0 -3
  174. package/utils/config.js +0 -1
  175. package/scripts/det/checklist-score.js +0 -191
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Atris Autopilot Suggest, justify, execute. One task at a time.
2
+ * Atris Autopilot, Suggest, justify, execute. One task at a time.
3
3
  *
4
4
  * Scans the workspace for signals (stale pages, broken refs, abandoned tasks,
5
5
  * inbox items, backlog) and suggests the most important thing to do next.
@@ -117,7 +117,7 @@ async function suggestNextTask(cwd, skipped = new Set(), { auto = false } = {})
117
117
  const atrisDir = path.join(cwd, 'atris');
118
118
  const suggestions = [];
119
119
 
120
- // --- Endgame tasks (highest priority pursue the current horizon to completion) ---
120
+ // --- Endgame tasks (highest priority, pursue the current horizon to completion) ---
121
121
  const todoPath = path.join(atrisDir, 'TODO.md');
122
122
  const todo = parseTodo(todoPath);
123
123
 
@@ -172,7 +172,7 @@ async function suggestNextTask(cwd, skipped = new Set(), { auto = false } = {})
172
172
  if (skipped.has(key)) continue;
173
173
  suggestions.push({
174
174
  task: `Finish or remove stale task: ${st.title}`,
175
- why: `Claimed ${st.daysSinceClaim} days ago and never completed. Either finish it or delete it stale tasks add noise.`,
175
+ why: `Claimed ${st.daysSinceClaim} days ago and never completed. Either finish it or delete it, stale tasks add noise.`,
176
176
  kind: 'cleanup',
177
177
  priority: 3,
178
178
  skipKey: key
@@ -187,7 +187,7 @@ async function suggestNextTask(cwd, skipped = new Set(), { auto = false } = {})
187
187
  const sample = unhealable.slice(0, 3).map(r => `${r.file}:${r.line}`).join(', ');
188
188
  suggestions.push({
189
189
  task: `Fix ${unhealable.length} broken reference${unhealable.length > 1 ? 's' : ''} in MAP.md`,
190
- why: `These file:line references point to code that moved or was deleted: ${sample}. MAP.md is the navigation it needs to be accurate.`,
190
+ why: `These file:line references point to code that moved or was deleted: ${sample}. MAP.md is the navigation, it needs to be accurate.`,
191
191
  kind: 'docs',
192
192
  priority: 4
193
193
  });
@@ -277,7 +277,7 @@ async function suggestNextTask(cwd, skipped = new Set(), { auto = false } = {})
277
277
  const key = `feature:${dir.name}`;
278
278
  if (!skipped.has(key)) {
279
279
  suggestions.push({
280
- task: `Complete feature spec for "${dir.name}" missing ${missing.join(' and ')}`,
280
+ task: `Complete feature spec for "${dir.name}", missing ${missing.join(' and ')}`,
281
281
  why: `idea.md exists but the feature is incomplete. Navigator needs to create ${missing.join(' and ')} so executor can build it.`,
282
282
  kind: 'feature',
283
283
  priority: 6.5
@@ -297,7 +297,7 @@ async function suggestNextTask(cwd, skipped = new Set(), { auto = false } = {})
297
297
  const daysSinceMapUpdate = (Date.now() - mapStat.mtime.getTime()) / (1000 * 60 * 60 * 24);
298
298
  if (daysSinceMapUpdate > 7) {
299
299
  suggestions.push({
300
- task: 'Review and refresh MAP.md it hasn\'t been updated in over a week',
300
+ task: 'Review and refresh MAP.md, it hasn\'t been updated in over a week',
301
301
  why: `Last modified ${Math.floor(daysSinceMapUpdate)} days ago. Code may have drifted from the map. A quick review keeps navigation accurate.`,
302
302
  kind: 'review',
303
303
  priority: 7
@@ -385,8 +385,8 @@ async function suggestNextTask(cwd, skipped = new Set(), { auto = false } = {})
385
385
  const { logFile } = getLogPath();
386
386
  const now = new Date();
387
387
  const hhmm = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
388
- const lines = staleSkipped.map(s => `- ${s.task} (${s.status})${s.reasoning ? ` ${s.reasoning}` : ''}`);
389
- const note = `\n### Staleness skip ${hhmm}\n${lines.join('\n')}\n`;
388
+ const lines = staleSkipped.map(s => `- ${s.task} (${s.status})${s.reasoning ? `, ${s.reasoning}` : ''}`);
389
+ const note = `\n### Staleness skip, ${hhmm}\n${lines.join('\n')}\n`;
390
390
  if (fs.existsSync(logFile)) {
391
391
  const content = fs.readFileSync(logFile, 'utf8');
392
392
  const notesIdx = content.indexOf('## Notes');
@@ -422,7 +422,7 @@ function askApproval() {
422
422
 
423
423
  /**
424
424
  * Ask the human whether an unverified task is still relevant.
425
- * Interactive mode only in auto mode, caller skips silently.
425
+ * Interactive mode only, in auto mode, caller skips silently.
426
426
  * Returns { fresh: boolean }.
427
427
  */
428
428
  function askHuman(taskTitle) {
@@ -438,7 +438,7 @@ function askHuman(taskTitle) {
438
438
 
439
439
  /**
440
440
  * Type-check a child_process error as a real wall-clock timeout. Node's
441
- * execSync attaches `code: 'ETIMEDOUT'` (plus `signal`) on timeout it does
441
+ * execSync attaches `code: 'ETIMEDOUT'` (plus `signal`) on timeout, it does
442
442
  * NOT set `killed`, so a `killed`-only guard is dead code on the exact error
443
443
  * it was written for (lesson: etimedout-error-shape, 2026-06-10). A bare
444
444
  * `signal` without ETIMEDOUT is NOT a timeout: it's an OOM SIGKILL or an
@@ -449,7 +449,7 @@ function isPhaseTimeoutError(err) {
449
449
  }
450
450
 
451
451
  /**
452
- * Any abnormal child death timeout or signal kill. The group sweep in
452
+ * Any abnormal child death, timeout or signal kill. The group sweep in
453
453
  * execPhaseCommandSync uses this wider net (orphans need sweeping either
454
454
  * way); the thrown message uses the narrow predicate to name the cause.
455
455
  */
@@ -459,12 +459,12 @@ function isPhaseKillError(err) {
459
459
 
460
460
  /**
461
461
  * execSync with the phase-timeout orphan fix. Node's sync-exec timeout signals
462
- * only the direct child pid the `/bin/sh -c` wrapper so the `claude` it
462
+ * only the direct child pid, the `/bin/sh -c` wrapper, so the `claude` it
463
463
  * spawned kept committing 160–296s past the 600s wall (lesson:
464
464
  * etimedout-error-shape, 2026-06-10). `detached: true` makes the wrapper a
465
465
  * process-group leader; on timeout we sweep the whole group via
466
466
  * `process.kill(-pid, 'SIGKILL')`. ESRCH on the sweep means the group already
467
- * died fine. The original error is rethrown untouched so every call site
467
+ * died, fine. The original error is rethrown untouched so every call site
468
468
  * keeps its existing catch contract (err.stdout passthrough included).
469
469
  */
470
470
  function execPhaseCommandSync(cmd, opts = {}) {
@@ -511,10 +511,10 @@ function executePhaseDetailed(phase, context, options = {}) {
511
511
  } catch (err) {
512
512
  try { fs.unlinkSync(tmpFile); } catch {}
513
513
  if (isPhaseTimeoutError(err)) {
514
- throw new Error(`${phase} phase timed out after ${timeout / 1000}s (configured runner hit the wall; any work it committed survives reconcile from pre-tick HEADs)`);
514
+ throw new Error(`${phase} phase timed out after ${timeout / 1000}s (configured runner hit the wall; any work it committed survives, reconcile from pre-tick HEADs)`);
515
515
  }
516
516
  if (isPhaseKillError(err)) {
517
- throw new Error(`${phase} phase killed by ${err.signal || 'a signal'} before the ${timeout / 1000}s wall not a timeout; check memory pressure or an external supervisor`);
517
+ throw new Error(`${phase} phase killed by ${err.signal || 'a signal'} before the ${timeout / 1000}s wall, not a timeout; check memory pressure or an external supervisor`);
518
518
  }
519
519
  if (err.stdout) {
520
520
  return { prompt, output: err.stdout };
@@ -638,10 +638,10 @@ function getContextFiles(phase, options = {}) {
638
638
  // uncommitted work. Sibling-repo edits ride per-tick worktrees (the same
639
639
  // ../repo siblings snapshotRepoHeads tracks); destructive git on the shared
640
640
  // checkout is forbidden (COORDINATION.md Rule 4). Interpolated into the
641
- // default and self-heal do prompts never the benchmark prompt (it never
641
+ // default and self-heal do prompts, never the benchmark prompt (it never
642
642
  // commits).
643
- const SHARED_CHECKOUT_GIT_CONTRACT = `- Shared-checkout git safety (COORDINATION.md Rule 4): edits to any repo OTHER than this tick's cwd (../atrisos-backend-style sibling repos) go through a per-tick worktree start with \`atris worktree start --member <member> --task "<task>"\`, land with \`atris worktree ship --message "<msg>" --verify "<cmd>"\`. Never edit a sibling repo's shared checkout directly.
644
- - On a shared checkout, \`git reset\`, \`git checkout --\`, \`git clean\`, and stashing other agents' work are FORBIDDEN concurrent ticks' uncommitted work lives there.`;
643
+ const SHARED_CHECKOUT_GIT_CONTRACT = `- Shared-checkout git safety (COORDINATION.md Rule 4): edits to any repo OTHER than this tick's cwd (../atrisos-backend-style sibling repos) go through a per-tick worktree, start with \`atris worktree start --member <member> --task "<task>"\`, land with \`atris worktree ship --message "<msg>" --verify "<cmd>"\`. Never edit a sibling repo's shared checkout directly.
644
+ - On a shared checkout, \`git reset\`, \`git checkout --\`, \`git clean\`, and stashing other agents' work are FORBIDDEN, concurrent ticks' uncommitted work lives there.`;
645
645
 
646
646
  /**
647
647
  * Target files of experiment-queue entries that have not run yet. The gated
@@ -796,7 +796,7 @@ When done, reply: done.`;
796
796
  Task: ${task}
797
797
 
798
798
  Read today's journal completions and the git log from the past few days.
799
- Extract patterns worth remembering things that surprised you, approaches that worked,
799
+ Extract patterns worth remembering, things that surprised you, approaches that worked,
800
800
  mistakes that were caught. Append to atris/lessons.md. One line per lesson. Be specific.
801
801
 
802
802
  When done, reply: done.`;
@@ -809,7 +809,7 @@ When done, reply: done.`;
809
809
  Self-heal task: ${task}
810
810
 
811
811
  This is an unresolved \`fail\` lesson from atris/lessons.md. grep confirms the bug pattern
812
- is still present in-repo the fix has NOT been shipped yet.
812
+ is still present in-repo, the fix has NOT been shipped yet.
813
813
 
814
814
  Plan the smallest fix:
815
815
  1. Parse the lesson for file:line references and the described bug pattern.
@@ -818,7 +818,7 @@ Plan the smallest fix:
818
818
  - **Exit:** the specific behavior that proves the fix
819
819
  - **Verify:** a command that fails now and will pass after the fix${lessonSlug ? ` (include "${lessonSlug}" in the task title so the lesson auto-resolves)` : ''}
820
820
  - **Rollback:** how to revert if the fix misses
821
- 4. Do NOT fix it in this phase planner only. The executor will do the work.
821
+ 4. Do NOT fix it in this phase, planner only. The executor will do the work.
822
822
 
823
823
  When done, reply: done.`;
824
824
  }
@@ -827,7 +827,7 @@ When done, reply: done.`;
827
827
 
828
828
  Task: ${task}
829
829
 
830
- Understand the scope what files need to change? Break into sub-tasks if needed.
830
+ Understand the scope, what files need to change? Break into sub-tasks if needed.
831
831
  Add tasks to atris/TODO.md under ## Backlog.
832
832
 
833
833
  When done, reply: done.`;
@@ -867,7 +867,7 @@ Rules:
867
867
  - You CAN read and write code. You CANNOT plan or create new tasks.
868
868
  - Execute ONE step at a time. Verify each step before moving on.
869
869
  - Check MAP.md for file locations before grepping.
870
- - Stay in scope. Only fix the bug described in the lesson no side quests.
870
+ - Stay in scope. Only fix the bug described in the lesson, no side quests.
871
871
  ${SHARED_CHECKOUT_GIT_CONTRACT}
872
872
 
873
873
  Read these files first:
@@ -887,7 +887,7 @@ When done, reply: done.`;
887
887
 
888
888
  const pendingTargets = pendingExperimentTargets();
889
889
  const experimentGuard = pendingTargets.length
890
- ? `\n- Do NOT edit these files this tick the gated experiment loop owns pending patches to them: ${pendingTargets.join(', ')}. If the task requires changing them, stop and flag instead.`
890
+ ? `\n- Do NOT edit these files this tick, the gated experiment loop owns pending patches to them: ${pendingTargets.join(', ')}. If the task requires changing them, stop and flag instead.`
891
891
  : '';
892
892
  return `You are the executor. Read your MEMBER.md spec first if available.
893
893
 
@@ -936,7 +936,7 @@ Task: ${task}
936
936
  2. Name any tests or checks you ran and whether they passed.
937
937
  3. Call out bugs, edge cases, or drift.
938
938
  4. Reply \`done\` if this run passes the review bar.
939
- 5. Reply \`failed [reason]\` if it does not.`;
939
+ 5. Reply \`failed, [reason]\` if it does not.`;
940
940
  }
941
941
 
942
942
  return `You are the validator. Read your MEMBER.md spec first if available.
@@ -946,8 +946,8 @@ Rules:
946
946
  - Ultrathink: spec match, scope check, edge cases, integration.
947
947
  - Run tests if they exist. Check MAP.md is still accurate.
948
948
  - If you halted, were surprised, or learned a non-obvious lesson, append ONE line to atris/lessons.md in this exact format:
949
- - **[YYYY-MM-DD] short-slug** pass|fail One sentence on what surprised you or what to remember.
950
- Skip if the tick taught nothing non-obvious. Lessons compound future /endgame runs read this file before picking horizons.
949
+ - **[YYYY-MM-DD] short-slug**, pass|fail, One sentence on what surprised you or what to remember.
950
+ Skip if the tick taught nothing non-obvious. Lessons compound, future /endgame runs read this file before picking horizons.
951
951
 
952
952
  Read these files first:
953
953
  ${readFiles}
@@ -965,7 +965,7 @@ Task: ${task}
965
965
  6. If something is wrong, fix it before signing off.
966
966
 
967
967
  When done, reply: done.
968
- If broken beyond quick fix, reply: failed [reason].`;
968
+ If broken beyond quick fix, reply: failed, [reason].`;
969
969
  }
970
970
 
971
971
  return '';
@@ -975,7 +975,7 @@ If broken beyond quick fix, reply: failed — [reason].`;
975
975
  * Build a clean kebab-case lesson slug from free text. Strips non-alphanumerics
976
976
  * (em-dashes were leaking into slugs verbatim) and truncates at a word boundary
977
977
  * instead of mid-word (e.g. the old `.slice(0, 40)` produced
978
- * `verify-fail-per-member-model-selection-—-the-member-`).
978
+ * `verify-fail-per-member-model-selection-the-member-`).
979
979
  */
980
980
  function lessonSlug(text, maxLen = 40) {
981
981
  const base = String(text || 'unknown')
@@ -986,7 +986,7 @@ function lessonSlug(text, maxLen = 40) {
986
986
  if (base.length <= maxLen) return base;
987
987
  const cut = base.slice(0, maxLen);
988
988
  const lastDash = cut.lastIndexOf('-');
989
- // base[maxLen] continues a word back up to the last full word.
989
+ // base[maxLen] continues a word, back up to the last full word.
990
990
  const atBoundary = base[maxLen] === '-';
991
991
  const trimmed = atBoundary ? cut : (lastDash > 0 ? cut.slice(0, lastDash) : cut);
992
992
  return trimmed.replace(/-+$/g, '') || 'unknown';
@@ -994,22 +994,22 @@ function lessonSlug(text, maxLen = 40) {
994
994
 
995
995
  /**
996
996
  * Write a lesson to atris/lessons.md
997
- * Appends a line in format: - **[YYYY-MM-DD] slug** pass/fail explanation
997
+ * Appends a line in format: - **[YYYY-MM-DD] slug** - pass/fail - explanation
998
998
  */
999
999
  function writeLesson(cwd, slug, status, explanation) {
1000
1000
  const lessonsPath = path.join(cwd, 'atris', 'lessons.md');
1001
1001
  const today = new Date().toISOString().split('T')[0]; // YYYY-MM-DD
1002
- const lessonLine = `- **[${today}] ${slug}** ${status} ${explanation}`;
1002
+ const lessonLine = `- **[${today}] ${slug}** - ${status} - ${explanation}`;
1003
1003
 
1004
1004
  if (!fs.existsSync(lessonsPath)) {
1005
- fs.writeFileSync(lessonsPath, `# lessons.md What We Learned\n\n> Append-only. One line per lesson.\n\n---\n\n${lessonLine}\n`);
1005
+ fs.writeFileSync(lessonsPath, `# lessons.md, What We Learned\n\n> Append-only. One line per lesson.\n\n---\n\n${lessonLine}\n`);
1006
1006
  return;
1007
1007
  }
1008
1008
 
1009
1009
  let content = fs.readFileSync(lessonsPath, 'utf8');
1010
1010
  // Same-day dedup: if an identical line already exists, skip the write. A
1011
1011
  // cron firing every 13min produced 5 identical no-verify-field lessons in
1012
- // one day (2026-05-08) before the picker-side fix landed pure noise. The
1012
+ // one day (2026-05-08) before the picker-side fix landed, pure noise. The
1013
1013
  // append-only contract still holds across days because today's date is in
1014
1014
  // the line.
1015
1015
  if (content.includes(lessonLine)) return;
@@ -1094,7 +1094,7 @@ function regressionCheck(cwd) {
1094
1094
  * Get the verify command for a task from TODO.md
1095
1095
  * Reads TODO.md, finds the task by title across active/completed sections,
1096
1096
  * and extracts the verify field.
1097
- * Returns { cmd, explicit } explicit is true only if the task has an explicit Verify field.
1097
+ * Returns { cmd, explicit }, explicit is true only if the task has an explicit Verify field.
1098
1098
  */
1099
1099
  function getVerifyCommand(cwd, taskTitle) {
1100
1100
  const todoPath = path.join(cwd, 'atris', 'TODO.md');
@@ -1137,7 +1137,7 @@ function shouldAdoptPlannedVerify(kind) {
1137
1137
  // Task-plane status vocabulary lint. `atris task list/queue/current --status <s>`
1138
1138
  // only matches raw stored statuses (commands/task.js); `ready` is a TRANSITION
1139
1139
  // (`atris task ready` moves a task to review), so `--status ready` always
1140
- // returns "(no tasks)" a verify built on it is an unreachable gate; the
1140
+ // returns "(no tasks)", a verify built on it is an unreachable gate; the
1141
1141
  // matching listable form is --status review (lessons.md
1142
1142
  // verify-status-vocabulary, 3rd occurrence 2026-06-10).
1143
1143
  const LISTABLE_TASK_STATUSES = ['open', 'claimed', 'review', 'done', 'failed'];
@@ -1160,7 +1160,7 @@ function lintVerifyTaskStatusVocabulary(text) {
1160
1160
  : `use one of --status ${vocabulary}`;
1161
1161
  return {
1162
1162
  ok: false,
1163
- reason: `Verify uses unlistable task status "--status ${status}" the listable vocabulary is ${vocabulary}; ${suggestion}`,
1163
+ reason: `Verify uses unlistable task status "--status ${status}", the listable vocabulary is ${vocabulary}; ${suggestion}`,
1164
1164
  };
1165
1165
  }
1166
1166
  return null;
@@ -1197,7 +1197,7 @@ function haltInvalidVerify(cwd, context, verifyCmd, reason, startedAt, phaseResu
1197
1197
  /**
1198
1198
  * Infer a default verify command from the repo shape. Order matters:
1199
1199
  * package.json with a non-stub test script → `npm test`; then pytest/python;
1200
- * then rust/go; otherwise null (no default skip verify).
1200
+ * then rust/go; otherwise null (no default, skip verify).
1201
1201
  */
1202
1202
  function detectDefaultVerify(cwd) {
1203
1203
  const pkg = path.join(cwd, 'package.json');
@@ -1246,14 +1246,14 @@ function verifyJudgeIntegrity() {
1246
1246
  }
1247
1247
 
1248
1248
  /**
1249
- * Build the validator's plan-review prompt. Fresh context the validator
1249
+ * Build the validator's plan-review prompt. Fresh context, the validator
1250
1250
  * reads the plan output and the contract fields as if it has never seen them.
1251
1251
  */
1252
1252
  function buildPlanReviewPrompt(context, planOutput) {
1253
1253
  const files = Array.isArray(context.files) && context.files.length
1254
1254
  ? context.files.join(', ')
1255
1255
  : 'none declared in context';
1256
- return `You are the validator in plan-review mode. You have NOT seen the planning context read everything fresh.
1256
+ return `You are the validator in plan-review mode. You have NOT seen the planning context, read everything fresh.
1257
1257
 
1258
1258
  Task: "${context.task}"
1259
1259
  Kind: ${context.kind || 'unknown'}
@@ -1265,9 +1265,9 @@ ${planOutput || '(no plan output captured)'}
1265
1265
  ---
1266
1266
 
1267
1267
  Read from disk:
1268
- - atris/atris.md (the workspace protocol operating rules and task shape)
1268
+ - atris/atris.md (the workspace protocol, operating rules and task shape)
1269
1269
  - atris/TODO.md (find this task; inspect Files, Exit, Verify, After, Rollback)
1270
- - atris/lessons.md (recent failures last 20 lines)
1270
+ - atris/lessons.md (recent failures, last 20 lines)
1271
1271
 
1272
1272
  Decide if the plan is safe to execute. Check:
1273
1273
  1. Verify points at a falsifiable raw shell command or rubric (not \`true\`, \`echo ok\`, Markdown backticks, or English like "returns 1" / "shows today's date").
@@ -1305,7 +1305,7 @@ function parseVerdict(output) {
1305
1305
  const text = String(output || '');
1306
1306
  const rawLines = text.split('\n');
1307
1307
  const lines = rawLines.map((l) => l.trim()).filter(Boolean);
1308
- // Scan from the end backwards the verdict is supposed to be LAST.
1308
+ // Scan from the end backwards, the verdict is supposed to be LAST.
1309
1309
  for (let i = lines.length - 1; i >= 0; i--) {
1310
1310
  const line = lines[i];
1311
1311
  if (/^SIGNOFF\s*:/i.test(line)) {
@@ -1398,7 +1398,7 @@ function defaultCodexExecutor(prompt, { cwd, timeout = 180000 } = {}) {
1398
1398
  maxBuffer: 10 * 1024 * 1024,
1399
1399
  detached: true,
1400
1400
  });
1401
- // No sh wrapper here, but codex spawns its own children sweep the group
1401
+ // No sh wrapper here, but codex spawns its own children, sweep the group
1402
1402
  // on timeout so they cannot outlive the wall (same orphan class as the
1403
1403
  // claude sites; ESRCH means the tree is already dead).
1404
1404
  if (proc.pid && ((proc.error && proc.error.code === 'ETIMEDOUT') || proc.signal)) {
@@ -1508,7 +1508,7 @@ function runPlanReview({ cwd, context, planOutput, options = {} }) {
1508
1508
 
1509
1509
  /**
1510
1510
  * Append a plan-review rejection to today's journal under ## Notes.
1511
- * Intentionally does NOT write to lessons.md rejections only become lessons
1511
+ * Intentionally does NOT write to lessons.md, rejections only become lessons
1512
1512
  * if a human spots a reusable failure pattern.
1513
1513
  */
1514
1514
  function appendPlanRejection(cwd, context, review) {
@@ -1532,7 +1532,7 @@ function appendPlanRejection(cwd, context, review) {
1532
1532
  (review.proposed.rollback ? `- Rollback: ${review.proposed.rollback}\n` : '')
1533
1533
  : '';
1534
1534
  const block =
1535
- `\n### Plan rejected ${now}\n\n` +
1535
+ `\n### Plan rejected, ${now}\n\n` +
1536
1536
  `**Task:** ${context.task}\n` +
1537
1537
  `**Signers:** ${signers}\n` +
1538
1538
  `**Reason:** ${review.reason}\n` +
@@ -1606,7 +1606,7 @@ function gitHeadAt(dir) {
1606
1606
 
1607
1607
  /**
1608
1608
  * Snapshot HEAD of the workspace repo plus any sibling repos named in the
1609
- * task text both explicit `../atris-cli`-style refs (the journal convention)
1609
+ * task text, both explicit `../atris-cli`-style refs (the journal convention)
1610
1610
  * and bare sibling-directory names like `atris-cli` that resolve to a git
1611
1611
  * repo next to cwd. Returns [{ label, dir, head }].
1612
1612
  */
@@ -1645,7 +1645,7 @@ function diffAdvancedRepoHeads(snapshot) {
1645
1645
 
1646
1646
  /**
1647
1647
  * The T31-typed do-phase timeout message thrown by executePhaseDetailed.
1648
- * Plan/review timeouts stay human halts only the do phase commits work
1648
+ * Plan/review timeouts stay human halts, only the do phase commits work
1649
1649
  * worth reconciling.
1650
1650
  */
1651
1651
  function isDoPhaseTimeoutMessage(message) {
@@ -1728,7 +1728,7 @@ function appendTimeoutReconciliation(cwd, { task, advanced }) {
1728
1728
  .map((r) => `- ${r.label}: ${String(r.before).slice(0, 7)} → ${String(r.after).slice(0, 7)}`)
1729
1729
  .join('\n');
1730
1730
  const block =
1731
- `\n### Timeout reconciliation ${now} work-landed-receipt-died\n\n` +
1731
+ `\n### Timeout reconciliation, ${now}, work-landed-receipt-died\n\n` +
1732
1732
  `**Task:** ${task}\n` +
1733
1733
  `**What happened:** the do-phase wall killed the reporter, but commits landed:\n` +
1734
1734
  `${repoLines}\n` +
@@ -1739,9 +1739,9 @@ function appendTimeoutReconciliation(cwd, { task, advanced }) {
1739
1739
  function appendCheckAndAdvance(cwd, task, receiptLine) {
1740
1740
  const now = new Date().toISOString().slice(0, 16).replace('T', ' ');
1741
1741
  const block =
1742
- `\n### Check-and-advance ${now} advanced-already-done\n\n` +
1742
+ `\n### Check-and-advance, ${now}, advanced-already-done\n\n` +
1743
1743
  `**Task:** ${task}\n` +
1744
- `**What happened:** verify passed before work started AND today's journal already carries a completion receipt the work shipped on a prior tick whose reporter died before bookkeeping. Bullet marked, picker advanced.\n` +
1744
+ `**What happened:** verify passed before work started AND today's journal already carries a completion receipt, the work shipped on a prior tick whose reporter died before bookkeeping. Bullet marked, picker advanced.\n` +
1745
1745
  `**Receipt:** ${receiptLine}\n`;
1746
1746
  return appendUnderNotes(cwd, block);
1747
1747
  }
@@ -1781,7 +1781,7 @@ function reconcileTimedOutTick(cwd, snapshot, taskTitle) {
1781
1781
  function runTaskOnce(context, options = {}) {
1782
1782
  const { verbose = false, cwd = process.cwd() } = options;
1783
1783
 
1784
- // Judge integrity check halt if computeTickReward was tampered with
1784
+ // Judge integrity check, halt if computeTickReward was tampered with
1785
1785
  const integrity = verifyJudgeIntegrity();
1786
1786
  if (!integrity.ok) {
1787
1787
  writeLesson(cwd, 'judge-corruption', 'fail',
@@ -1812,7 +1812,7 @@ function runTaskOnce(context, options = {}) {
1812
1812
  // Reactive signals (inbox, staleness, imagined) use npm test as default.
1813
1813
  if (!verifyResult.explicit && context.kind === 'endgame') {
1814
1814
  writeLesson(cwd, 'no-verify-field', 'fail',
1815
- `Task "${context.task}" has no explicit **Verify:** field in TODO.md. Tick halted every endgame task must declare how to verify it.`);
1815
+ `Task "${context.task}" has no explicit **Verify:** field in TODO.md. Tick halted, every endgame task must declare how to verify it.`);
1816
1816
  return {
1817
1817
  outcome: 'halted',
1818
1818
  reason: 'no-verify-field',
@@ -1825,7 +1825,7 @@ function runTaskOnce(context, options = {}) {
1825
1825
 
1826
1826
  // Falsifiability gate (endgame + explicit Verify only).
1827
1827
  // Run Verify BEFORE the work. If it passes, the rubric is trivial or the
1828
- // task is already done either way, halt. This is the keystone that makes
1828
+ // task is already done, either way, halt. This is the keystone that makes
1829
1829
  // Verify load-bearing. The cmd is captured here and reused post-execute so
1830
1830
  // an agent cannot swap the rubric mid-tick.
1831
1831
  //
@@ -1868,7 +1868,7 @@ function runTaskOnce(context, options = {}) {
1868
1868
  verifyPass: false,
1869
1869
  };
1870
1870
  } catch {
1871
- // Pre-verify failed good, the rubric is falsifiable. Proceed.
1871
+ // Pre-verify failed, good, the rubric is falsifiable. Proceed.
1872
1872
  }
1873
1873
  }
1874
1874
 
@@ -1883,7 +1883,7 @@ function runTaskOnce(context, options = {}) {
1883
1883
  };
1884
1884
  }
1885
1885
 
1886
- // Phase: plan-review validator reads the plan fresh and signs off or rejects.
1886
+ // Phase: plan-review, validator reads the plan fresh and signs off or rejects.
1887
1887
  // Can be skipped via options.skipPlanReview (tests only). Codex is optional,
1888
1888
  // opt-in via env var / tags. On REJECT, the tick halts and the rejection is
1889
1889
  // journaled; lessons.md is NOT touched (only promoted lessons go there).
@@ -2074,7 +2074,7 @@ function computeTickReward(execution, tickOutcome, verifyCmd) {
2074
2074
  * - idle: when true, block must contain literal "0 tasks in 0s"
2075
2075
  * so getIdleTickCount still works.
2076
2076
  * - reward: optional tick reward score (from computeTickReward)
2077
- * Safe to call inside a try/catch a write failure must never crash a tick.
2077
+ * Safe to call inside a try/catch, a write failure must never crash a tick.
2078
2078
  */
2079
2079
  function appendTickSummary(cwd, { time, outcome, horizon, nextStep, idle, reward } = {}) {
2080
2080
  const now = new Date();
@@ -2257,7 +2257,7 @@ function printPlainBlock(text) {
2257
2257
  function getTickStatus(cwd) {
2258
2258
  const atrisDir = path.join(cwd, 'atris');
2259
2259
 
2260
- let identity = '(no identity set see atris/PERSONA.md)';
2260
+ let identity = '(no identity set, see atris/PERSONA.md)';
2261
2261
  const personaPath = path.join(atrisDir, 'PERSONA.md');
2262
2262
  if (fs.existsSync(personaPath)) {
2263
2263
  const lines = fs.readFileSync(personaPath, 'utf8').split('\n');
@@ -2271,7 +2271,7 @@ function getTickStatus(cwd) {
2271
2271
  }
2272
2272
 
2273
2273
  const endgame = readEndgameState(cwd);
2274
- const slug = endgame.slug === 'unset' ? '(no endgame active feed inbox or /endgame)' : endgame.slug;
2274
+ const slug = endgame.slug === 'unset' ? '(no endgame active, feed inbox or /endgame)' : endgame.slug;
2275
2275
  const horizon = endgame.horizon;
2276
2276
  const remaining = endgame.remaining;
2277
2277
  const completedEndgame = endgame.completed;
@@ -2318,7 +2318,7 @@ function renderHumanSuggestion(suggestion, step, maxIterations) {
2318
2318
  /**
2319
2319
  * Print the visual ASCII tick status block. Shows identity (forward / flow),
2320
2320
  * current endgame slug + horizon (backward / endgame), and progress through
2321
- * endgame steps. Two halves of the same engine flow and endgame meeting
2321
+ * endgame steps. Two halves of the same engine, flow and endgame, meeting
2322
2322
  * at the next tick. Called at the start of each autopilot run.
2323
2323
  */
2324
2324
  function printTickStatus(cwd, options = {}) {
@@ -2362,7 +2362,7 @@ function printTickStatus(cwd, options = {}) {
2362
2362
  * Idle marker is the literal substring `0 tasks in 0s` (case-insensitive). Scans
2363
2363
  * the Notes section bottom-up; the first non-marker, non-blank line breaks the
2364
2364
  * streak. Returns 0 when the journal is missing or has no `## Notes` section.
2365
- * Pure read-only no side effects, no callers yet.
2365
+ * Pure read-only, no side effects, no callers yet.
2366
2366
  */
2367
2367
  function getIdleTickCount(cwd) {
2368
2368
  const now = new Date();
@@ -2397,7 +2397,7 @@ function getIdleTickCount(cwd) {
2397
2397
  * - recentCommits: string[] from `git log --oneline -20` (empty on failure)
2398
2398
  * - wikiHealth: string of `atris/wiki/STATUS.md` contents, or null if missing
2399
2399
  * - recentLessons: string[] of last 10 non-empty lines from `atris/lessons.md`
2400
- * Pure read-only try/catch each source, safe defaults on failure. No callers yet.
2400
+ * Pure read-only, try/catch each source, safe defaults on failure. No callers yet.
2401
2401
  */
2402
2402
  function getRecentSignals(cwd) {
2403
2403
  let recentCommits = [];
@@ -2557,7 +2557,7 @@ function scoreEndgameCandidates(cwd, candidates) {
2557
2557
  }
2558
2558
 
2559
2559
  /**
2560
- * Proactive "surprise me" scanner surfaces things the user didn't ask about.
2560
+ * Proactive "surprise me" scanner, surfaces things the user didn't ask about.
2561
2561
  * Returns an array of suggestion objects in the same shape as the reactive
2562
2562
  * signals in suggestNextTask. Three orthogonal checks, none requiring
2563
2563
  * cross-session state:
@@ -2583,7 +2583,7 @@ function scanAnomalies(cwd) {
2583
2583
  const sample = untracked.slice(0, 3).map(t => `${t.file}:${t.line}`).join(', ');
2584
2584
  const firstText = first.text.slice(0, 60);
2585
2585
  results.push({
2586
- task: `Track the ${untracked.length} orphan TODO${untracked.length > 1 ? 's' : ''} in source first: "${firstText}"`,
2586
+ task: `Track the ${untracked.length} orphan TODO${untracked.length > 1 ? 's' : ''} in source, first: "${firstText}"`,
2587
2587
  why: `Code has ${untracked.length} \`// TODO\`/\`// FIXME\` comment${untracked.length > 1 ? 's' : ''} never written to TODO.md. First: "${firstText}" (${sample}). Either convert to real tasks or delete if obsolete.`,
2588
2588
  kind: 'orphan-todo',
2589
2589
  priority: 6,
@@ -2619,7 +2619,7 @@ function scanAnomalies(cwd) {
2619
2619
  const hotspot = findHotspot(cwd);
2620
2620
  if (hotspot) {
2621
2621
  results.push({
2622
- task: `Pause and review ${hotspot.file} ${hotspot.commits} commits in the last 24h`,
2622
+ task: `Pause and review ${hotspot.file}, ${hotspot.commits} commits in the last 24h`,
2623
2623
  why: `That file has churned more than any other file today. Could be genuine progress or a sign the change isn't sticking. Worth reading the diff before continuing.`,
2624
2624
  kind: 'hotspot',
2625
2625
  priority: 6.5,
@@ -2635,7 +2635,7 @@ function scanAnomalies(cwd) {
2635
2635
  * Grep source code for TODO/FIXME comments. Skips test/, node_modules/,
2636
2636
  * atris/, and .md files. Returns [{file, line, text}].
2637
2637
  *
2638
- * Uses a loose grep then filters to real comment prefixes in JS git grep's
2638
+ * Uses a loose grep then filters to real comment prefixes in JS, git grep's
2639
2639
  * -E flag doesn't support `\s` on macOS, so we keep the pattern simple and
2640
2640
  * refine post-hoc.
2641
2641
  */
@@ -2798,7 +2798,7 @@ function parseLessons(cwd) {
2798
2798
  for (const rawLine of content.split('\n')) {
2799
2799
  const line = rawLine;
2800
2800
  if (!line.trim().startsWith('- **[')) continue;
2801
- const m = line.match(/\*\*\[(\d{4}-\d{2}-\d{2})\]\s+([\w-]+)\*\*\s*[—-]\s*(pass|fail)?\s*[—-]?\s*(.*)$/);
2801
+ const m = line.match(/\*\*\[(\d{4}-\d{2}-\d{2})\]\s+([\w-]+)\*\*\s*[-\u2014]\s*(pass|fail)?\s*[-\u2014]?\s*(.*)$/);
2802
2802
  if (!m) continue;
2803
2803
  const [, date, id, verdict, rest] = m;
2804
2804
  const resolvedTag = /\[resolved\]/.test(rest);
@@ -2911,7 +2911,7 @@ function legacyLessonFileRefs(lessonLine) {
2911
2911
  }
2912
2912
 
2913
2913
  /**
2914
- * The pre-v3.8 resolver kept as an internal fallback for prose-only lessons
2914
+ * The pre-v3.8 resolver, kept as an internal fallback for prose-only lessons
2915
2915
  * that don't have detector metadata yet. Never auto-promotes a prose lesson to
2916
2916
  * resolved in the typed system (callers can still use the `resolvedTag` field
2917
2917
  * from parseLessons for hand-tagged entries).
@@ -2959,13 +2959,13 @@ function isLessonResolvedLegacy(lessonLine, cwd) {
2959
2959
  *
2960
2960
  * Self-healing seed: instead of imagining new horizons via LLM, use what the
2961
2961
  * system already wrote down about itself. A `fail` lesson with `isLessonResolved
2962
- * === false` means grep confirms the bug pattern is still present actionable.
2962
+ * === false` means grep confirms the bug pattern is still present, actionable.
2963
2963
  */
2964
2964
  /**
2965
2965
  * Returns true if a recent (within `windowDays`) `verify-not-falsifiable`
2966
2966
  * lesson references this exact task title. The falsifiability gate halts the
2967
2967
  * tick when the Verify clause already passes before work starts (task is
2968
- * already shipped or rubric is trivial), but nothing in TODO.md changes
2968
+ * already shipped or rubric is trivial), but nothing in TODO.md changes,
2969
2969
  * so the next tick re-picks the same task and burns another 90s+ halting in
2970
2970
  * the same place. Reading the lesson log breaks the loop without requiring
2971
2971
  * a TODO.md hand-edit (which is the structurally-broken file we route around
@@ -3009,7 +3009,7 @@ function pickUnresolvedFailLesson(cwd) {
3009
3009
  if (lesson.id === 'no-verify-field') continue;
3010
3010
  if (lesson.id === 'verify-failed' && lesson.legacy) continue;
3011
3011
  if (lesson.resolvedTag) continue;
3012
- // Typed lesson with explicit status wins respect the sidecar.
3012
+ // Typed lesson with explicit status wins, respect the sidecar.
3013
3013
  // `resolved` = done. `observed` = process rule, not a fixable code state.
3014
3014
  // `attempted` with attempts >= MAX_ATTEMPTS = needs human re-scoping, skip.
3015
3015
  // Only `open` and `attempted` (under the cap) flow to self-heal execution.
@@ -3034,13 +3034,13 @@ function pickUnresolvedFailLesson(cwd) {
3034
3034
 
3035
3035
  if (candidates.length === 0) return null;
3036
3036
 
3037
- // Oldest first longest-standing fails get priority
3037
+ // Oldest first, longest-standing fails get priority
3038
3038
  candidates.sort((a, b) => a.date.localeCompare(b.date));
3039
3039
  return candidates[0];
3040
3040
  }
3041
3041
 
3042
3042
  function getLessonVerdict(lessonLine) {
3043
- const match = lessonLine.match(/\*\*\[\d{4}-\d{2}-\d{2}\]\s+[\w-]+\*\*\s*[—-]\s*(pass|fail)\b/i);
3043
+ const match = lessonLine.match(/\*\*\[\d{4}-\d{2}-\d{2}\]\s+[\w-]+\*\*\s*[-\u2014]\s*(pass|fail)\b/i);
3044
3044
  return match ? match[1].toLowerCase() : null;
3045
3045
  }
3046
3046
 
@@ -3087,7 +3087,7 @@ Based on these signals, propose exactly 3 candidate next horizons for the loop t
3087
3087
  - Distinct from the other two candidates.
3088
3088
  - Not a restatement of a \`pass\` lesson; pass lessons are shipped constraints, not open work.
3089
3089
 
3090
- Output STRICT JSON ONLY no prose, no markdown code fences, no commentary. The output must be a single JSON array with exactly 3 objects, each shaped:
3090
+ Output STRICT JSON ONLY, no prose, no markdown code fences, no commentary. The output must be a single JSON array with exactly 3 objects, each shaped:
3091
3091
 
3092
3092
  [
3093
3093
  { "title": "one-line horizon title", "confidence": 0.0-1.0, "rationale": "one sentence why this is worth pursuing now" },
@@ -3118,7 +3118,7 @@ Reply with the JSON array and nothing else.`;
3118
3118
  throw new Error(`horizon-proposal phase timed out after ${PHASE_TIMEOUT / 1000}s`);
3119
3119
  }
3120
3120
  if (isPhaseKillError(err)) {
3121
- throw new Error(`horizon-proposal phase killed by ${err.signal || 'a signal'} before the ${PHASE_TIMEOUT / 1000}s wall not a timeout`);
3121
+ throw new Error(`horizon-proposal phase killed by ${err.signal || 'a signal'} before the ${PHASE_TIMEOUT / 1000}s wall, not a timeout`);
3122
3122
  }
3123
3123
  throw err;
3124
3124
  } finally {
@@ -3136,7 +3136,7 @@ Reply with the JSON array and nothing else.`;
3136
3136
  try {
3137
3137
  parsed = JSON.parse(jsonText);
3138
3138
  } catch (err) {
3139
- throw new Error(`proposeCandidateHorizons: JSON parse failed ${err.message}`);
3139
+ throw new Error(`proposeCandidateHorizons: JSON parse failed, ${err.message}`);
3140
3140
  }
3141
3141
 
3142
3142
  if (!Array.isArray(parsed)) {
@@ -3180,7 +3180,7 @@ Reply with the JSON array and nothing else.`;
3180
3180
  droppedByLesson = true;
3181
3181
  break;
3182
3182
  }
3183
- // Candidate matches this lesson check if the lesson is resolved
3183
+ // Candidate matches this lesson, check if the lesson is resolved
3184
3184
  if (isLessonResolved(lessonLine, cwd)) {
3185
3185
  // Tag lesson [resolved] in lessons.md
3186
3186
  try {
@@ -3336,7 +3336,7 @@ async function autopilotAtris(description, options = {}) {
3336
3336
 
3337
3337
  if (dryRun) {
3338
3338
  if (verbose) {
3339
- console.log(' (dry run would execute this)');
3339
+ console.log(' (dry run, would execute this)');
3340
3340
  console.log('');
3341
3341
  } else {
3342
3342
  printPlainBlock([
@@ -3434,7 +3434,7 @@ async function autopilotAtris(description, options = {}) {
3434
3434
  lastExecution = execution;
3435
3435
  lastVerifyCmd = execution.verifyCmd;
3436
3436
 
3437
- // Early halt judge corruption or no verify field
3437
+ // Early halt, judge corruption or no verify field
3438
3438
  if (execution.outcome === 'halted') {
3439
3439
  tickOutcome = 'halted';
3440
3440
  tickOutcomeText = `I halted before running "${lastTaskTitle}": ${execution.reason}.`;
@@ -3452,18 +3452,18 @@ async function autopilotAtris(description, options = {}) {
3452
3452
  break;
3453
3453
  }
3454
3454
 
3455
- // T33b: the falsifiability gate found a completion receipt the work
3455
+ // T33b: the falsifiability gate found a completion receipt, the work
3456
3456
  // already shipped, the bullet is checked, move straight to the next pick.
3457
3457
  if (execution.outcome === 'advanced-already-done') {
3458
3458
  completed++;
3459
3459
  tickOutcome = 'built';
3460
- tickOutcomeText = `"${lastTaskTitle}" was already done verify passed pre-work and today's journal carries its completion receipt, so I checked the bullet and advanced.`;
3460
+ tickOutcomeText = `"${lastTaskTitle}" was already done, verify passed pre-work and today's journal carries its completion receipt, so I checked the bullet and advanced.`;
3461
3461
  tickNextStep = 'pick the next endgame task';
3462
3462
  if (verbose) {
3463
3463
  console.log(' already done (journal receipt found). bullet checked, advancing.');
3464
3464
  } else {
3465
3465
  printPlainBlock([
3466
- 'That task was already done verify passed before work and a completion receipt exists in today\'s journal.',
3466
+ 'That task was already done, verify passed before work and a completion receipt exists in today\'s journal.',
3467
3467
  'I checked the bullet and advanced.',
3468
3468
  '',
3469
3469
  'Next I will look for the next task.'
@@ -3581,7 +3581,7 @@ async function autopilotAtris(description, options = {}) {
3581
3581
 
3582
3582
  } catch (err) {
3583
3583
  // T33a: a do-phase timeout with commits landed is a dead reporter, not
3584
- // dead work write the reconciliation receipt, mark the bullet, and
3584
+ // dead work, write the reconciliation receipt, mark the bullet, and
3585
3585
  // record work-landed-receipt-died instead of halting for a human.
3586
3586
  let reconciliation = null;
3587
3587
  if (isDoPhaseTimeoutMessage(err.message)) {
@@ -3595,10 +3595,10 @@ async function autopilotAtris(description, options = {}) {
3595
3595
  .map((r) => `${r.label} ${String(r.before).slice(0, 7)} → ${String(r.after).slice(0, 7)}`)
3596
3596
  .join(', ');
3597
3597
  tickOutcome = 'work-landed-receipt-died';
3598
- tickOutcomeText = `"${lastTaskTitle}" hit the do-phase wall but commits landed (${landed}). I wrote the reconciliation receipt and marked the bullet work-landed-receipt-died, no human halt.`;
3598
+ tickOutcomeText = `"${lastTaskTitle}" hit the do-phase wall but commits landed (${landed}). I wrote the reconciliation receipt and marked the bullet, work-landed-receipt-died, no human halt.`;
3599
3599
  tickNextStep = 'pick the next task';
3600
3600
  if (verbose) {
3601
- console.log(` do phase timed out, but work landed (${landed}). reconciled no human halt.`);
3601
+ console.log(` do phase timed out, but work landed (${landed}). reconciled, no human halt.`);
3602
3602
  } else {
3603
3603
  printPlainBlock([
3604
3604
  'The do phase timed out, but commits landed before the wall.',
@@ -3630,7 +3630,7 @@ async function autopilotAtris(description, options = {}) {
3630
3630
  const elapsed = Math.round((Date.now() - startTime) / 1000);
3631
3631
 
3632
3632
  // Heartbeat: plain-language tick summary into today's journal `## Notes`.
3633
- // Guarded a journal write failure must never crash the tick.
3633
+ // Guarded, a journal write failure must never crash the tick.
3634
3634
  try {
3635
3635
  const horizonSlug = readHorizonSlug(cwd);
3636
3636
  const time = new Date().toLocaleTimeString('en-US', {
@@ -3738,7 +3738,7 @@ function isStillTrue(fact, cwd) {
3738
3738
  });
3739
3739
  grepHits++;
3740
3740
  } catch {
3741
- // grep returns non-zero when no match that's fine
3741
+ // grep returns non-zero when no match, that's fine
3742
3742
  }
3743
3743
  }
3744
3744
 
@@ -3762,13 +3762,13 @@ function isStillTrue(fact, cwd) {
3762
3762
  // Strong mechanical evidence: grep found terms AND recent git activity
3763
3763
  if (gitHits > 0) return 'actionable';
3764
3764
 
3765
- // Grep found terms but no recent git activity can't fully verify
3765
+ // Grep found terms but no recent git activity, can't fully verify
3766
3766
  return 'unverified';
3767
3767
  }
3768
3768
 
3769
3769
  /**
3770
3770
  * Ask a local model whether a task/fact is still relevant.
3771
- * Called when isStillTrue returns 'unverified' the mechanical check
3771
+ * Called when isStillTrue returns 'unverified', the mechanical check
3772
3772
  * couldn't confirm or deny, so we ask the configured runner to inspect the codebase.
3773
3773
  *
3774
3774
  * @param {{ title: string, age: number, source?: string }} fact
@@ -3812,7 +3812,7 @@ Search the codebase to verify. Reply: YES <reason> or NO <reason>`;
3812
3812
  return { fresh, reasoning };
3813
3813
  } catch (err) {
3814
3814
  try { fs.unlinkSync(tmpFile); } catch {}
3815
- // On timeout or crash, treat as unverifiable conservative default
3815
+ // On timeout or crash, treat as unverifiable, conservative default
3816
3816
  return { fresh: false, reasoning: `Model check failed: ${(err.message || '').slice(0, 100)}` };
3817
3817
  }
3818
3818
  }