atris 3.57.4 → 3.58.6

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 +55 -12
  6. package/atris/skills/x-search/SKILL.md +8 -6
  7. package/atris/skills/youtube/SKILL.md +66 -21
  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 +153 -23
  16. package/bin/atris.js +78 -30
  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 +59 -31
  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 +3 -3
  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 +216 -50
  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 +90 -82
  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 +101 -48
  98. package/commands/worktree.js +1 -1
  99. package/commands/write.js +9 -9
  100. package/commands/x-search.js +93 -22
  101. package/commands/xp.js +6 -3
  102. package/commands/youtube.js +1019 -123
  103. package/lib/accept-verify-gate.js +3 -3
  104. package/lib/activity-stream.js +1 -1
  105. package/lib/auto-accept-certified.js +28 -12
  106. package/lib/autoland.js +6 -6
  107. package/lib/bench/engines.js +59 -8
  108. package/lib/bench/report.js +1 -1
  109. package/lib/bench/runner.js +204 -5
  110. package/lib/bench/tree-render.js +76 -0
  111. package/lib/card.js +1 -1
  112. package/lib/chat-log-scan.js +1 -1
  113. package/lib/claude-boot-block.js +2 -0
  114. package/lib/deck-compose.js +1 -1
  115. package/lib/deck-history.js +2 -2
  116. package/lib/deck-layout.js +1 -1
  117. package/lib/deck-review.js +5 -5
  118. package/lib/deck-schema.js +1 -1
  119. package/lib/default-verifier.js +1 -1
  120. package/lib/developer-api.js +3 -0
  121. package/lib/engine-ask.js +45 -7
  122. package/lib/engine-registry.js +15 -2
  123. package/lib/file-ops.js +1 -1
  124. package/lib/fleet.js +71 -35
  125. package/lib/html-render.js +1 -1
  126. package/lib/intents.js +172 -0
  127. package/lib/journal.js +1 -1
  128. package/lib/known-commands.js +2 -2
  129. package/lib/land-green.js +364 -0
  130. package/lib/lesson-contradiction.js +2 -2
  131. package/lib/lesson-preflight.js +1 -1
  132. package/lib/manifest.js +5 -5
  133. package/lib/member-alive.js +9 -5
  134. package/lib/memory-view.js +3 -3
  135. package/lib/mission-protected-lane.js +1 -1
  136. package/lib/mission-root.js +2 -2
  137. package/lib/orb-context.js +10 -10
  138. package/lib/pack-capabilities.js +861 -3
  139. package/lib/permission-grants.js +2 -2
  140. package/lib/policy-lessons.js +6 -6
  141. package/lib/pulse.js +8 -8
  142. package/lib/receipt-evidence.js +1 -1
  143. package/lib/reel.js +1 -1
  144. package/lib/review-integrity.js +1 -1
  145. package/lib/runner-command.js +35 -5
  146. package/lib/scorecard.js +4 -4
  147. package/lib/security-scan.js +5 -5
  148. package/lib/self-drive.js +1 -1
  149. package/lib/site-publish.js +372 -0
  150. package/lib/site.js +1 -1
  151. package/lib/slides-deck.js +2 -2
  152. package/lib/state-detection.js +2 -2
  153. package/lib/sync-telemetry.js +2 -2
  154. package/lib/task-db.js +17 -10
  155. package/lib/task-explanation.js +1 -1
  156. package/lib/task-proof.js +2 -2
  157. package/lib/task-receipt.js +3 -3
  158. package/lib/theme.js +12 -12
  159. package/lib/todo-fallback.js +3 -3
  160. package/lib/todo-sections.js +1 -1
  161. package/lib/tree-hash.js +79 -0
  162. package/lib/usage.js +1 -1
  163. package/lib/wish-delegate.js +1 -1
  164. package/lib/workspace-safety.js +4 -4
  165. package/lib/workspace-scaffold.js +3 -3
  166. package/package.json +1 -1
  167. package/scripts/det/concierge-bakeoff.sh +26 -0
  168. package/scripts/det/ytnotes +82 -5
  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 +47 -9
  174. package/utils/config.js +0 -1
  175. package/scripts/det/checklist-score.js +0 -191
package/commands/task.js CHANGED
@@ -1622,8 +1622,21 @@ function buildTaskStreams(tasks, goals) {
1622
1622
  // from the existing review handoff, so this relabels what the certification
1623
1623
  // rules already allow and can never widen it.
1624
1624
  function taskApprovalFor(task, { reviewer = 'codex-review', hasExistingReviewFollowUp = null } = {}) {
1625
- const current = taskPageCurrentStage(task);
1626
1625
  const metadata = task && task.metadata || {};
1626
+ const approvalStatus = (task && task.review && task.review.approval_status) || metadata.approval_status || null;
1627
+ if (task && task.status === 'done' && approvalStatus === 'accepted') {
1628
+ return taskApprovalControls({
1629
+ question: 'Landed and accepted. Nothing to do.',
1630
+ approveLabel: 'Approve the completed work',
1631
+ acceptEnabled: false,
1632
+ acceptCommand: null,
1633
+ requestChangeEnabled: false,
1634
+ requestChangeCommand: null,
1635
+ blockedReason: null,
1636
+ waitingOn: null,
1637
+ });
1638
+ }
1639
+ const current = taskPageCurrentStage(task);
1627
1640
  const actions = taskPageActions(task, { reviewer, hasExistingReviewFollowUp });
1628
1641
  const inReview = current === 'review';
1629
1642
  const handoff = inReview
@@ -5333,7 +5346,7 @@ function evaluateReviewAutoAccept(task, root) {
5333
5346
  if (!protectedGate.ok) return { eligible: false, ref, reason: protectedGate.reason };
5334
5347
  const sizeGate = reviewAutoAcceptSizeGate(task, root);
5335
5348
  if (!sizeGate.ok) return { eligible: false, ref, reason: sizeGate.reason, size: sizeGate.stats };
5336
- // executeVerify: false this is a READ path. The verdict comes from
5349
+ // executeVerify: false, this is a READ path. The verdict comes from
5337
5350
  // metadata.verify_cache, stamped by the two lanes allowed to spawn
5338
5351
  // checks (certify-verified, autoland landing re-check). Spawning here is
5339
5352
  // what turned `atris task reviews` into a fork-bomb multiplier.
@@ -5840,7 +5853,7 @@ function stampAcceptGroupMetadata(taskDb, db, taskId, { actor, group, verified,
5840
5853
  }
5841
5854
 
5842
5855
  // Accept a whole certified cluster honestly: the human spot-checks K rows (real career XP), the rest
5843
- // are accepted-by-sampling (cleared from the backlog, provenance recorded, NO career XP never fake XP).
5856
+ // are accepted-by-sampling (cleared from the backlog, provenance recorded, NO career XP, never fake XP).
5844
5857
  function cmdAcceptGroup(args) {
5845
5858
  const pos = positional(args);
5846
5859
  const spec = pos[0];
@@ -5891,7 +5904,7 @@ function cmdAcceptGroup(args) {
5891
5904
  });
5892
5905
  return;
5893
5906
  }
5894
- console.log(`accept-group DRY RUN ${key}=${value}: ${group.length} certified task(s)`);
5907
+ console.log(`accept-group DRY RUN, ${key}=${value}: ${group.length} certified task(s)`);
5895
5908
  console.log(`Spot-check these ${need} (weakest evidence first; open + verify), then accept the whole cluster:`);
5896
5909
  sample.forEach((task) => {
5897
5910
  console.log(` ${taskRef(task)} ${task.title}`);
@@ -5908,7 +5921,7 @@ function cmdAcceptGroup(args) {
5908
5921
  }
5909
5922
  });
5910
5923
  console.log('');
5911
- console.log(`If they hold up accept all ${group.length} (career XP only on the ${need} you verified):`);
5924
+ console.log(`If they hold up, accept all ${group.length} (career XP only on the ${need} you verified):`);
5912
5925
  console.log(` atris task accept-group ${key}=${JSON.stringify(value)} --spot-check ${spotCheck} --confirm-human-accept --as <you> --verified ${sample.map(task => task.id).join(',')}`);
5913
5926
  return;
5914
5927
  }
@@ -6215,7 +6228,7 @@ function cmdAdd(args) {
6215
6228
  const db = taskDb.open();
6216
6229
  const ws = taskDb.workspaceRoot();
6217
6230
  const operatorTitleWarning = warnIfTaskTitleNeedsOperatorWhy(title);
6218
- // Generation throttle the named root cause is generation > human-review rate. An AGENT cannot keep
6231
+ // Generation throttle, the named root cause is generation > human-review rate. An AGENT cannot keep
6219
6232
  // minting tasks while a wall of certified-but-unaccepted work waits; that treadmill is what accept-group
6220
6233
  // only drains. Humans and --force bypass. Closes the tap instead of just enlarging the bucket.
6221
6234
  if (agentProofOnlyMode() && !hasFlag(args, '--force')) {
@@ -6387,7 +6400,7 @@ function taskDayTextGroups(groups, { full = false } = {}) {
6387
6400
  }
6388
6401
 
6389
6402
  function taskDayTitle(title, maxLength = 120) {
6390
- const text = String(title || '').replace(/\s*[—–]\s*/g, ' - ').replace(/\s+/g, ' ').trim();
6403
+ const text = String(title || '').replace(/\s*[\u2014\u2013]\s*/g, ' - ').replace(/\s+/g, ' ').trim();
6391
6404
  if (text.length <= maxLength) return text;
6392
6405
  const sentence = text.slice(0, maxLength + 1).match(/^(.{48,}?[.!?])(?:\s|$)/);
6393
6406
  if (sentence) return sentence[1];
@@ -7468,6 +7481,7 @@ function cmdPlan(args) {
7468
7481
  goal,
7469
7482
  summary,
7470
7483
  owner: automaticPlan.ownerForStage || owner,
7484
+ ownerExplicit: Boolean(owner),
7471
7485
  exit,
7472
7486
  proofNeeded,
7473
7487
  firstMove,
@@ -8402,9 +8416,13 @@ function buildAutomaticPlanTrace(taskDb, task, { actor, actorExplicit = false, o
8402
8416
  const metadata = task.metadata || {};
8403
8417
  const purpose = cleanPublicText(goal, 240) || taskPurpose(task);
8404
8418
  const claimedOwner = cleanPublicText(task.claimed_by, 80);
8419
+ // A delegated owner (metadata.assigned_to) is a functional decision already
8420
+ // recorded on the task. Only an explicit caller owner or an existing claim
8421
+ // outranks it; automatic team choice never does.
8422
+ const delegatedOwner = cleanPublicText(metadata.assigned_to, 80);
8405
8423
  const actorNamed = taskTextMentionsActor(actor, `${purpose} ${task.title || ''} ${task.tag || ''}`);
8406
8424
  const requestedActor = actorExplicit && (!isGenericPlanActor(actor) || actorNamed) ? actor : null;
8407
- const requestedOwner = owner || claimedOwner || requestedActor || null;
8425
+ const requestedOwner = owner || claimedOwner || delegatedOwner || requestedActor || null;
8408
8426
  const ownerChoice = chooseTaskOwner({
8409
8427
  purpose,
8410
8428
  tag: task.tag,
@@ -9778,7 +9796,7 @@ function cmdReapMissionBlockers(args) {
9778
9796
  // 'failed' (no distinct archived status existed yet). This relabels exactly
9779
9797
  // the rows that carry that reset's metadata marker, using the same
9780
9798
  // UPDATE+appendTaskEvent write path as every other status transition in
9781
- // lib/task-db.js never a raw projection-JSON edit.
9799
+ // lib/task-db.js, never a raw projection-JSON edit.
9782
9800
  function cmdRelabelArchived(args) {
9783
9801
  const apply = hasFlag(args, '--apply');
9784
9802
  const taskDb = getTaskDb();
@@ -9892,7 +9910,7 @@ function cmdReady(args) {
9892
9910
  const { classifyVerifier } = require('../lib/verifier-quality');
9893
9911
  const quality = classifyVerifier(verifyFlag);
9894
9912
  if (!quality.ok) {
9895
- console.error(`atris task ready: weak verifier ${quality.reason}`);
9913
+ console.error(`atris task ready: weak verifier, ${quality.reason}`);
9896
9914
  console.error(` command: ${verifyFlag}`);
9897
9915
  console.error(' allowlisted examples: node --test, npm test, git diff --check, node --check, rg <symbol>');
9898
9916
  console.error(' or pass --no-falsify-check to accept anyway.');
@@ -9900,7 +9918,7 @@ function cmdReady(args) {
9900
9918
  }
9901
9919
  const probe = probeVerifierCanFail({ command: verifyFlag });
9902
9920
  if (probe.probed && probe.canFail === false) {
9903
- console.error(`atris task ready: this check cannot fail ${probe.reason}`);
9921
+ console.error(`atris task ready: this check cannot fail, ${probe.reason}`);
9904
9922
  console.error(` command: ${verifyFlag}`);
9905
9923
  console.error(' give a check that fails when the work is missing, or pass --no-falsify-check to accept anyway.');
9906
9924
  process.exit(1);
@@ -9908,7 +9926,7 @@ function cmdReady(args) {
9908
9926
  if (!probe.probed && !wantsJson(args)) console.log(` ${probe.reason}`);
9909
9927
  }
9910
9928
  const base = proof.trim();
9911
- proof = `[verified] \`${verifyFlag}\` passed (exit 0)${base ? ` ${base}` : ''}${receipt.output ? `\n${receipt.output}` : ''}\nReceipt: ${receipt.receiptPath}`;
9929
+ proof = `[verified] \`${verifyFlag}\` passed (exit 0)${base ? `, ${base}` : ''}${receipt.output ? `\n${receipt.output}` : ''}\nReceipt: ${receipt.receiptPath}`;
9912
9930
  if (!wantsJson(args)) console.log(`✓ verified: \`${verifyFlag}\` exited 0 (receipt ${receipt.receiptPath})`);
9913
9931
  }
9914
9932
  if (!proof) {
@@ -10306,7 +10324,7 @@ function acceptReviewTask(taskDb, db, taskId, { actor, proof, reward, lesson = '
10306
10324
 
10307
10325
  // Second-actor certification for proof-backed Review rows whose proof names a
10308
10326
  // runnable, allowlisted check. Re-running that check as a distinct actor IS
10309
- // the independent verification the certification gate asks for the row
10327
+ // the independent verification the certification gate asks for, the row
10310
10328
  // becomes certified and autoland can land it on the same heartbeat. Rows in
10311
10329
  // denied lanes, without a runnable check, or already certified keep their
10312
10330
  // existing paths (review chats, human accept).
@@ -10355,8 +10373,8 @@ function currentHeadSha(workspaceRoot) {
10355
10373
 
10356
10374
  // metadata.verify_cache is the read path's only verify verdict: `reviews`
10357
10375
  // must never spawn a check (that read-side execution fork-bombed the fleet
10358
- // on 2026-07-29), so the two lanes that legitimately execute verifies
10359
- // certify-verified and the autoland landing re-check persist the outcome
10376
+ // on 2026-07-29), so the two lanes that legitimately execute verifies,
10377
+ // certify-verified and the autoland landing re-check, persist the outcome
10360
10378
  // here for the read path to consult.
10361
10379
  function stampVerifyCacheMetadata(taskDb, db, taskId, { verify, result, actor, workspaceRoot }) {
10362
10380
  const row = taskDb.getTask(db, taskId);
@@ -10414,8 +10432,8 @@ function reverifyBeforeLanding(taskDb, db, task, { actor = 'autoland-verifier',
10414
10432
  const verify = certifyVerifyCandidate(task);
10415
10433
  if (!verify) {
10416
10434
  // No runnable check on record. Landing anyway converts "never verified"
10417
- // into "accepted with XP" the exact signal poisoning this gate exists
10418
- // to prevent so bounce the task back for a recorded verify instead.
10435
+ // into "accepted with XP", the exact signal poisoning this gate exists
10436
+ // to prevent, so bounce the task back for a recorded verify instead.
10419
10437
  const note = 'Autoland refused to land without a runnable verify command: record one with `atris task ready --verify "<cmd>" --result "<sentence>"`.';
10420
10438
  const revised = taskDb.reviseTask(db, { id: task.id, actor, note });
10421
10439
  return {
@@ -10494,11 +10512,11 @@ function cmdCertifyVerified(args, options = {}) {
10494
10512
  }
10495
10513
  // Skip only rows the accept lane can already land, or rows blocked by
10496
10514
  // something an executed second-actor check cannot cure. A row with two
10497
- // passes from ONE actor is exactly what this command exists to cure
10515
+ // passes from ONE actor is exactly what this command exists to cure,
10498
10516
  // "certified" alone is not landable.
10499
10517
  // strictVerify stays off in this eligibility probe: certify-verified runs
10500
10518
  // the check itself right below, and strict mode here would execute it a
10501
- // second time per row before the real run. executeVerify: false as well
10519
+ // second time per row before the real run. executeVerify: false as well,
10502
10520
  // an untrusted trust tier re-enables the strict block internally even
10503
10521
  // with strictVerify off, which is how this probe spawned a verify per row.
10504
10522
  const evaluation = evaluateAutoAccept(task, { strictVerify: false, executeVerify: false });
@@ -10626,7 +10644,7 @@ function cmdAutoAcceptCertified(args) {
10626
10644
  const hasExplicitLimit = Boolean(limitRaw) && limitRaw !== true;
10627
10645
  // --all means sweep the full certified backlog, not just the first page of
10628
10646
  // it. Before this fix `max` was hard-capped at 12 even under --all, so a
10629
- // real backlog (78 certified rows observed live) only ever drained 12/run
10647
+ // real backlog (78 certified rows observed live) only ever drained 12/run,
10630
10648
  // an invisible undercount the autoland heartbeat repeated every hour.
10631
10649
  // AUTO_ACCEPT_ALL_SWEEP_CAP is a safety ceiling, not a target: a well-formed
10632
10650
  // --all run should always scan fewer rows than this.
@@ -10659,7 +10677,7 @@ function cmdAutoAcceptCertified(args) {
10659
10677
  // anyway. Without this exception `atris autoland tick` was blind whenever
10660
10678
  // invoked from an agent session (CLAUDECODE etc. in env): the spawned
10661
10679
  // sweep failTask'd with no summary and the tick receipt showed nulls.
10662
- // A per-run --confirm-human-accept claim from an agent is still refused
10680
+ // A per-run --confirm-human-accept claim from an agent is still refused,
10663
10681
  // policyAuth is only consulted when no per-run confirmation is passed.
10664
10682
  if (agentProofOnlyMode() && !dryRun && !policyAuth.ok) {
10665
10683
  failAgentProofOnly(
@@ -10792,7 +10810,7 @@ function cmdAutoAcceptCertified(args) {
10792
10810
  return { ...summary, results, certification, projection_path: finalPath, queue };
10793
10811
  }
10794
10812
  console.log(`AUTO-ACCEPT CERTIFIED (${dryRun ? 'dry-run' : 'execute'})`);
10795
- console.log(`${summary.certified} certified, ${summary.scanned} scanned, ${summary.accepted || summary.would_accept} accepted, ${summary.skipped} skipped${summary.revised ? `, ${summary.revised} revised` : ''}${summary.failed ? `, ${summary.failed} failed` : ''}${summary.undercounted ? ' (UNDERCOUNTED raise --limit or the sweep cap)' : ''}`);
10813
+ console.log(`${summary.certified} certified, ${summary.scanned} scanned, ${summary.accepted || summary.would_accept} accepted, ${summary.skipped} skipped${summary.revised ? `, ${summary.revised} revised` : ''}${summary.failed ? `, ${summary.failed} failed` : ''}${summary.undercounted ? ' (UNDERCOUNTED, raise --limit or the sweep cap)' : ''}`);
10796
10814
  for (const row of results) {
10797
10815
  const nextAction = row.next_action ? ` next_action=${row.next_action}` : '';
10798
10816
  const reviewChat = row.review_chat_command ? ` review_chat=${row.review_chat_command}` : '';
@@ -11104,10 +11122,16 @@ function taskAuditReceiptPath(at) {
11104
11122
 
11105
11123
  function runTaskAuditVerify(task, verify) {
11106
11124
  const { spawnSync } = require('child_process');
11125
+ // Same measured ceiling as the landing gate: honest suites need minutes,
11126
+ // not seconds. See lib/auto-accept-certified.js verifyTimeoutMs().
11127
+ const rawTimeout = Number(process.env.ATRIS_VERIFY_TIMEOUT_MS);
11128
+ const timeout = Number.isFinite(rawTimeout) && rawTimeout > 0
11129
+ ? Math.min(rawTimeout, 60 * 60 * 1000)
11130
+ : 10 * 60 * 1000;
11107
11131
  const result = spawnSync('bash', ['-c', verify], {
11108
11132
  cwd: task.workspace_root,
11109
11133
  encoding: 'utf8',
11110
- timeout: 120000,
11134
+ timeout,
11111
11135
  });
11112
11136
  const passed = !result.error && result.status === 0;
11113
11137
  return {
@@ -11997,7 +12021,7 @@ function taskBoardTemplate(model) {
11997
12021
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11998
12022
  <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
11999
12023
  <style>
12000
- /* aesthetic: machine-room telemetry warm-tinted dark, mono data, calm signals (no neon) */
12024
+ /* aesthetic: machine-room telemetry, warm-tinted dark, mono data, calm signals (no neon) */
12001
12025
  :root {
12002
12026
  color-scheme: dark;
12003
12027
  --bg: oklch(18% 0.012 160);
@@ -12208,7 +12232,7 @@ function taskBoardTemplate(model) {
12208
12232
 
12209
12233
  function renderActivity(events) {
12210
12234
  const el = $('activity');
12211
- if (!events.length) { el.innerHTML = '<h2>Live Stream</h2><div class="empty">no activity yet fire a tick: atris pulse tick</div>'; return; }
12235
+ if (!events.length) { el.innerHTML = '<h2>Live Stream</h2><div class="empty">no activity yet, fire a tick: atris pulse tick</div>'; return; }
12212
12236
  let html = '<h2>Live Stream · ' + events.length + ' events</h2>';
12213
12237
  for (const e of events) {
12214
12238
  const rwCls = e.reward == null ? '' : (e.reward > 0 ? 'pos' : (e.reward < 0 ? 'neg' : ''));
@@ -12819,6 +12843,7 @@ function postTaskApiPlan({ res, taskDb, db, taskId, body }) {
12819
12843
  goal,
12820
12844
  summary,
12821
12845
  owner: automaticPlan.ownerForStage || owner,
12846
+ ownerExplicit: Boolean(owner),
12822
12847
  exit,
12823
12848
  proofNeeded: String(body.proof_needed || body.proofNeeded || body.proof || body.verify || ''),
12824
12849
  firstMove,
package/commands/teach.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  /**
4
- * atris teach local cases under ./atris/teach only.
4
+ * atris teach: local cases under ./atris/teach only.
5
5
  *
6
6
  * atris teach list cases in atris/teach
7
7
  * atris teach <id> show one case file
@@ -16,7 +16,7 @@ const path = require('path');
16
16
  const TEACH_DIR = path.join('atris', 'teach');
17
17
 
18
18
  function usage() {
19
- console.log('atris teach local cases under ./atris/teach');
19
+ console.log('atris teach: local cases under ./atris/teach');
20
20
  console.log('');
21
21
  console.log(' atris teach list case files in atris/teach');
22
22
  console.log(' atris teach <id> show atris/teach/<id>.md (or .json)');
package/commands/team.js CHANGED
@@ -145,7 +145,7 @@ function memberFocus(rawNow, { awake, alwaysOn }) {
145
145
  }
146
146
 
147
147
  function memberIsActive({ frontmatterEngine, awake }) {
148
- // A stale focus line in now.md does not make a member active only an
148
+ // A stale focus line in now.md does not make a member active, only an
149
149
  // assigned engine or live presence does.
150
150
  return Boolean(frontmatterEngine) || awake;
151
151
  }
package/commands/theme.js CHANGED
@@ -1,4 +1,4 @@
1
- // atris theme brand themes for the whole design system. Define your colors and
1
+ // atris theme, brand themes for the whole design system. Define your colors and
2
2
  // fonts once in .atris/theme.json; every deck, HTML page, and site uses them.
3
3
  //
4
4
  // atris theme create guided interview -> your own theme (alias: new)
@@ -79,7 +79,7 @@ async function interview(defaults = {}) {
79
79
  ans.mode = mp.startsWith('l') ? 'light' : mp.startsWith('d') ? 'dark' : dmode;
80
80
  }
81
81
 
82
- console.log('\n 4/5 your accent the one color that is unmistakably yours:');
82
+ console.log('\n 4/5 your accent, the one color that is unmistakably yours:');
83
83
  mood.swatches.forEach((s, i) => console.log(` ${i + 1}. ${sw(s)} ${s}`));
84
84
  const dac = normHex(defaults.accent) || mood.swatches[0];
85
85
  const ap = (await ask(` pick 1-${mood.swatches.length}, or paste a hex [${dac}]: `)).trim();
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ const { computeTreeHash } = require('../lib/tree-hash');
4
+
5
+ function showTreeHelp() {
6
+ console.log('usage: atris tree hash [--json]');
7
+ }
8
+
9
+ function treeCommand(subcommand, ...args) {
10
+ if (['help', '--help', '-h'].includes(subcommand)) {
11
+ showTreeHelp();
12
+ return 0;
13
+ }
14
+ if (subcommand !== 'hash') {
15
+ console.error(`unknown tree command: ${subcommand || ''}`.trim());
16
+ showTreeHelp();
17
+ return 2;
18
+ }
19
+
20
+ let result;
21
+ try {
22
+ result = computeTreeHash(process.cwd());
23
+ } catch (error) {
24
+ console.error(`tree hash failed: ${error.message || error}`);
25
+ return 1;
26
+ }
27
+ if (args.includes('--json')) console.log(JSON.stringify(result, null, 2));
28
+ else console.log(`${result.short} ${result.files} files`);
29
+ return 0;
30
+ }
31
+
32
+ module.exports = {
33
+ treeCommand,
34
+ };
package/commands/truth.js CHANGED
@@ -1,9 +1,9 @@
1
- // atris truth one table of what is actually proven, blocked, or stale.
1
+ // atris truth: one table of what is actually proven, blocked, or stale.
2
2
  // Rolls up sources that already exist; writes nothing. SQL/state is truth, this is the render.
3
- // 1. .atris/state/missions.jsonl mission states (dedupe by id, latest wins)
4
- // 2. ~/.atris/tasks.db task counts by status, scoped to this workspace unless --all is passed
5
- // 3. atris/features/*/ validate.md frontmatter + newest proof/ receipt age
6
- // 4. atris/loops/*.md workspace loops by default; pass --global for ~/.atris/heartbeat
3
+ // 1. .atris/state/missions.jsonl : mission states (dedupe by id, latest wins)
4
+ // 2. ~/.atris/tasks.db : task counts by status, scoped to this workspace unless --all is passed
5
+ // 3. atris/features/*/ : validate.md frontmatter + newest proof/ receipt age
6
+ // 4. atris/loops/*.md : workspace loops by default; pass --global for ~/.atris/heartbeat
7
7
 
8
8
  const fs = require('fs');
9
9
  const path = require('path');
@@ -116,7 +116,7 @@ const PARKED_DAYS = 30;
116
116
 
117
117
  // One git call: every feature dir touched in the last PARKED_DAYS.
118
118
  // An unproven feature nobody has edited in a month is a parked idea
119
- // packet, not work-in-progress counting it as "unproven" buries the
119
+ // packet, not work-in-progress. Counting it as "unproven" buries the
120
120
  // handful of live lanes that actually need a proof receipt.
121
121
  function recentlyTouchedFeatureDirs(cwd) {
122
122
  try {
@@ -275,7 +275,7 @@ function truthCommand(args = []) {
275
275
  return 0;
276
276
  }
277
277
 
278
- line('ATRIS TRUTH live state, not belief\n');
278
+ line('ATRIS TRUTH: live state, not belief\n');
279
279
  line(`Scope: ${taskScopeLine(scope)}`);
280
280
 
281
281
  line(`Missions active: ${missions.length}`);
@@ -76,12 +76,12 @@ function verifyWorkspace(cwd, atrisDir) {
76
76
 
77
77
  // MAP status
78
78
  if (results.mapValid) {
79
- console.log('✓ MAP.md Valid');
79
+ console.log('✓ MAP.md: Valid');
80
80
  if (mapResult.stats) {
81
81
  console.log(` ${mapResult.stats.fileRefs} file refs, ${mapResult.stats.lineRefs} line refs`);
82
82
  }
83
83
  } else {
84
- console.log('✗ MAP.md Issues found');
84
+ console.log('✗ MAP.md: Issues found');
85
85
  mapResult.issues.forEach(issue => console.log(` • ${issue}`));
86
86
  }
87
87
  (mapResult.notes || []).forEach(note => console.log(` ○ ${note}`));
@@ -90,20 +90,20 @@ function verifyWorkspace(cwd, atrisDir) {
90
90
  // Test status
91
91
  if (testResult.run) {
92
92
  if (results.testsPass) {
93
- console.log(`✓ Tests Pass (${testResult.summary})`);
93
+ console.log(`✓ Tests: Pass (${testResult.summary})`);
94
94
  } else {
95
- console.log(`✗ Tests Fail (${testResult.summary})`);
95
+ console.log(`✗ Tests: Fail (${testResult.summary})`);
96
96
  }
97
97
  } else {
98
- console.log('○ Tests Not configured');
98
+ console.log('○ Tests: Not configured');
99
99
  }
100
100
  console.log('');
101
101
 
102
102
  // Docs vs changes
103
103
  if (results.docsUpToDate) {
104
- console.log('✓ Docs Up to date with recent changes');
104
+ console.log('✓ Docs: Up to date with recent changes');
105
105
  } else {
106
- console.log('⚠ Docs May need updating');
106
+ console.log('⚠ Docs: May need updating');
107
107
  docResult.issues.forEach(issue => console.log(` • ${issue}`));
108
108
  }
109
109
  console.log('');
@@ -227,7 +227,7 @@ function verifyMap(cwd, atrisDir) {
227
227
  const content = fs.readFileSync(mapFile, 'utf8');
228
228
 
229
229
  // Check for placeholder content. Match the specific phrases written by
230
- // commands/init.js:357 a substring search for "placeholder" is too broad
230
+ // commands/init.js:357, where a substring search for "placeholder" is too broad
231
231
  // and false-flags any MAP.md that mentions the word in prose.
232
232
  const lower = content.toLowerCase();
233
233
  const isPlaceholder = lower.includes('generated by your ai agent')
@@ -239,13 +239,13 @@ function verifyMap(cwd, atrisDir) {
239
239
  }
240
240
 
241
241
  // Let the repo's own map validator have the final say, when it ships one.
242
- // A missing python3 is a skip, not a failure the CLI has no python dependency.
242
+ // A missing python3 is a skip, not a failure. The CLI has no python dependency.
243
243
  const validator = path.join(cwd, 'scripts', 'validate_map.py');
244
244
  if (fs.existsSync(validator)) {
245
245
  const proc = spawnSync('python3', [validator], { cwd, encoding: 'utf8', env: process.env });
246
246
  if (proc.error) {
247
247
  const reason = proc.error.code === 'ENOENT' ? 'python3 not available' : proc.error.message;
248
- result.notes.push(`Skipped scripts/validate_map.py ${reason}`);
248
+ result.notes.push(`Skipped scripts/validate_map.py: ${reason}`);
249
249
  } else if ((proc.status ?? 0) !== 0) {
250
250
  const detail = firstOutputLine(proc.stderr) || firstOutputLine(proc.stdout) || `exit ${proc.status}`;
251
251
  result.issues.push(`scripts/validate_map.py failed: ${detail}`);
@@ -539,7 +539,7 @@ function verifyChange(cwd, change) {
539
539
  };
540
540
  }
541
541
 
542
- // No specific check possible refuse to auto-pass
542
+ // No specific check possible, refuse to auto-pass
543
543
  return {
544
544
  pass: false,
545
545
  description: change.description,
@@ -583,7 +583,7 @@ function checkMapForFiles(atrisDir, changes) {
583
583
  *
584
584
  * Contract (per atris.md): the rubric must be read-only, deterministic, and
585
585
  * reference only the working tree. The command fails loudly when the rubric
586
- * or section is missing that prevents silent "trivial Verify" regressions.
586
+ * or section is missing. That prevents silent "trivial Verify" regressions.
587
587
  */
588
588
  function verifyRubric(slug, section, opts = {}) {
589
589
  const cwd = opts.cwd || process.cwd();
@@ -41,7 +41,7 @@ function legacyVisualizeInbox() {
41
41
 
42
42
  console.log('');
43
43
  console.log('┌─────────────────────────────────────────────────────────────┐');
44
- console.log('│ Atris VisualizeBreak Down & Approval Gate │');
44
+ console.log('│ Atris Visualize: Break Down & Approval Gate │');
45
45
  console.log('└─────────────────────────────────────────────────────────────┘');
46
46
  console.log('');
47
47
 
@@ -368,7 +368,7 @@ async function generateVisual(prompt, options = {}) {
368
368
  }
369
369
 
370
370
  async function visualizeAtris(args = process.argv.slice(3)) {
371
- // Bare-word `help` should not be treated as a prompt it would trigger a
371
+ // Bare-word `help` should not be treated as a prompt, it would trigger a
372
372
  // paid image-generation call ("draw me a picture of help"). Promote it to
373
373
  // the help intent.
374
374
  if (args[0] === 'help') {