atris 3.57.4 → 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 (174) 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 +37 -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 +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 +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 +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 +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 +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 +97 -40
  98. package/commands/worktree.js +1 -1
  99. package/commands/write.js +9 -9
  100. package/commands/x-search.js +89 -17
  101. package/commands/xp.js +6 -3
  102. package/commands/youtube.js +601 -48
  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/ytquote-repair.js +0 -1
  169. package/scripts/det/ytrail-race.js +0 -5
  170. package/scripts/member-operate.mjs +52 -14
  171. package/scripts/outbound-artifact-gate.js +0 -3
  172. package/utils/auth.js +0 -3
  173. package/utils/config.js +0 -1
  174. package/scripts/det/checklist-score.js +0 -191
@@ -123,7 +123,7 @@ function revokeGrant(grantId, file) {
123
123
  let grant = store.grants.find(g => g.grant_id === ref);
124
124
  if (!grant && ref) {
125
125
  // Prefix refs are a convenience, but an ambiguous prefix must never
126
- // revoke an arbitrary grant require exactly one match.
126
+ // revoke an arbitrary grant, require exactly one match.
127
127
  const matches = store.grants.filter(g => String(g.grant_id).startsWith(ref));
128
128
  if (matches.length > 1) return { ok: false, reason: `"${ref}" matches ${matches.length} grants; use more characters` };
129
129
  grant = matches[0];
@@ -258,7 +258,7 @@ function mergeRemoteGrants(store, remoteGrants = [], now = new Date()) {
258
258
  // Push the local grants to the backend and merge back the authoritative set.
259
259
  // apiRequestJson is injected (utils/api) so this stays offline-testable. The
260
260
  // call is best-effort: on any transport/HTTP failure the local store is left
261
- // untouched and { ok: false, reason } is returned grants keep working locally.
261
+ // untouched and { ok: false, reason } is returned, grants keep working locally.
262
262
  async function syncGrants({ apiRequestJson, token, file, endpoint = GRANTS_SYNC_ENDPOINT, now = new Date() } = {}) {
263
263
  if (typeof apiRequestJson !== 'function') {
264
264
  return { ok: false, reason: 'no api client' };
@@ -3,8 +3,8 @@
3
3
  // The workspace already records its own history: career XP receipts (human
4
4
  // accepts), task episodes (every review turn with an RL label), and
5
5
  // scorecards (improve/brain ticks). This module mines that history into a
6
- // small set of deterministic policy lessons each one carries the evidence
7
- // counts it was computed from and surfaces them at the moment an agent
6
+ // small set of deterministic policy lessons, each one carries the evidence
7
+ // counts it was computed from, and surfaces them at the moment an agent
8
8
  // submits proof (`atris task ready`), so the next submission behaves
9
9
  // differently than the last. No LLM calls; re-mining refreshes the numbers.
10
10
  const fs = require('fs');
@@ -23,7 +23,7 @@ const DAY_MS = 24 * 60 * 60 * 1000;
23
23
  // Review actors that are agents, not the human gate. Mining must split the
24
24
  // two: agent self-review churn and human accept/bounce are different signals.
25
25
  const AGENT_ACTOR_PATTERN = /review|validator|certifier|auto|codex|claude|devin|droid|improver|second-pass|agent|bot/i;
26
- // "Names a runnable verify command" the check a reviewer could replay.
26
+ // "Names a runnable verify command": the check a reviewer could replay.
27
27
  const VERIFY_COMMAND_PATTERN = /\b(npm (run )?test|node --test|node --check|node bin\/|pytest|cargo test|go test|make test|atris verify|grep\s+-[A-Za-z]*q[A-Za-z]*|rg\s+(?:-\S+\s+)*(?:"[^"]+"|'[^']+'|\S+)\s+(?:\.{0,2}\/|~\/|\/|[\w.-]+\/|[\w.-]+\.[A-Za-z0-9]|\b(?:atris|bin|commands|lib|scripts|src|test)\b)|git diff --(?:check|exit-code|quiet)|diff (?:-u|--brief)|cmp -s|test -[fs])\b|--verify\b|\bverify:\s/;
28
28
  const COMMIT_REF_PATTERN = /\bcommit\s+[0-9a-f]{7,40}\b/i;
29
29
 
@@ -87,7 +87,7 @@ function acceptRate(withCount, againstCount) {
87
87
  }
88
88
 
89
89
  // True when proofs carrying the feature get accepted at a higher rate than
90
- // proofs without it the bar for emitting a feature lesson at all.
90
+ // proofs without it, the bar for emitting a feature lesson at all.
91
91
  function featureHelps(counts) {
92
92
  const withRate = acceptRate(counts.accepted_with, counts.revised_with);
93
93
  const withoutRate = acceptRate(counts.accepted_without, counts.revised_without);
@@ -366,7 +366,7 @@ function syncLessonsMd(root, mined) {
366
366
  const today = (mined.mined_at || new Date().toISOString()).split('T')[0];
367
367
  const lines = (mined.lessons || []).map((lesson) => ({
368
368
  id: lesson.id,
369
- line: `- **[${today}] policy-${lesson.id}** ${lesson.status || 'pass'} ${lesson.lesson} (mined from ${mined.sources.career_xp_receipts} receipts / ${mined.sources.task_episodes} episodes / ${mined.sources.scorecards} scorecards)`,
369
+ line: `- **[${today}] policy-${lesson.id}** \u2014 ${lesson.status || 'pass'} \u2014 ${lesson.lesson} (mined from ${mined.sources.career_xp_receipts} receipts / ${mined.sources.task_episodes} episodes / ${mined.sources.scorecards} scorecards)`,
370
370
  }));
371
371
  if (!lines.length) return { path: lessonsPath, written: [] };
372
372
 
@@ -374,7 +374,7 @@ function syncLessonsMd(root, mined) {
374
374
  try {
375
375
  content = fs.readFileSync(lessonsPath, 'utf8');
376
376
  } catch {
377
- content = '# lessons.md What We Learned\n\n> Append-only. One line per lesson.\n\n---\n';
377
+ content = '# lessons.md: What We Learned\n\n> Append-only. One line per lesson.\n\n---\n';
378
378
  }
379
379
  const written = [];
380
380
  for (const { id, line } of lines) {
package/lib/pulse.js CHANGED
@@ -4,7 +4,7 @@
4
4
  //
5
5
  // The /loop skill schedules a heartbeat via Claude Code's CronCreate, but that
6
6
  // only fires while Claude Code is open and idle and dies with the session. The
7
- // proven overnight pattern is an OS cron (see the commander tick.sh template)
7
+ // proven overnight pattern is an OS cron (see the commander tick.sh template),
8
8
  // it fires regardless of whether Claude Code is running. `atris pulse` brings
9
9
  // that pattern home: one OS-cron tick that runs the existing mission engine,
10
10
  // verifies, and writes BOTH a pulse receipt (revives the Pulse AGI loop-health
@@ -32,7 +32,7 @@ const DEFAULT_CADENCE_CRON = '23 * * * *';
32
32
  const STALE_TICK_MS = 30 * 60 * 1000;
33
33
  // Liveness timeout: how long since the last finished tick before the loop reads
34
34
  // as "stale" (stopped firing). Must exceed the cadence or it flaps stale between
35
- // every tick default cadence is hourly, so allow ~2 missed ticks before alarm.
35
+ // every tick, default cadence is hourly, so allow ~2 missed ticks before alarm.
36
36
  const LIVENESS_STALE_MS = 150 * 60 * 1000;
37
37
 
38
38
  function stateDir(root) {
@@ -273,7 +273,7 @@ function buildInterruptedPulseReceipt(input = {}) {
273
273
 
274
274
  // The heartbeat's full composition (mirrors the /loop skill): run the due
275
275
  // mission to continue an existing goal; if none is due, fall back to an
276
- // autopilot tick that path is where proposeCandidateHorizons AUTHORS a new
276
+ // autopilot tick, that path is where proposeCandidateHorizons AUTHORS a new
277
277
  // goal at an endgame boundary. The fallback needs a worker, so skip it under
278
278
  // --no-claude (goal-authoring can't happen without the model in the loop).
279
279
  function shouldFallbackToAutopilot({ missionReason, autopilotFallback = true, noClaude = false } = {}) {
@@ -285,12 +285,12 @@ function shouldFallbackToAutopilot({ missionReason, autopilotFallback = true, no
285
285
  // Reward gating mirrors the improve.js tick-5 lesson: only verified,
286
286
  // work-producing ticks earn positive reward; verify failure is punished;
287
287
  // a tick that produced no work scores 0. `producedWork` MUST be the tick's
288
- // actual delta (new commit or newly-dirtied files), never the whole dirty tree
288
+ // actual delta (new commit or newly-dirtied files), never the whole dirty tree.
289
289
  // crediting pre-existing dirt re-rewards the same change every tick (the reward
290
290
  // inflation bug). The caller computes producedWork from a before/after snapshot.
291
291
  //
292
292
  // A crashed engine must also score -1. Before this, a hard `mission error` tick
293
- // produced no work and ran no verifier, so it scored 0 indistinguishable from
293
+ // produced no work and ran no verifier, so it scored 0, indistinguishable from
294
294
  // a healthy idle tick. The 2026-07-16 heartbeat death proved the cost: 25
295
295
  // consecutive error ticks left reward_sum flat, nothing escalated, and the loop
296
296
  // stopped with a ledger that still read green. `actorReason: 'error'` is a
@@ -312,7 +312,7 @@ function shouldWriteScorecard({ reward } = {}) {
312
312
  //
313
313
  // A workspace without .git made producedWork permanently false (git snapshots
314
314
  // return an empty delta), so every tick scored 0 regardless of what the engine
315
- // wrote the loop was blind, not idle. When git is absent, pulse falls back to
315
+ // wrote, the loop was blind, not idle. When git is absent, pulse falls back to
316
316
  // a filesystem snapshot: path → mtime+size, excluding churn dirs.
317
317
 
318
318
  const FS_SNAPSHOT_SKIP = new Set(['.git', 'node_modules', '.atris']);
@@ -346,7 +346,7 @@ function fsSnapshot(root) {
346
346
  return entries;
347
347
  }
348
348
 
349
- // New or modified files between two fsSnapshots. Deletions are ignored the
349
+ // New or modified files between two fsSnapshots. Deletions are ignored, the
350
350
  // signal we need is "did the tick author anything", not a full diff.
351
351
  function diffFsSnapshots(before, after) {
352
352
  const changed = [];
@@ -373,7 +373,7 @@ function defaultVerifyCmd(root) {
373
373
  // --- ghost / stale detection (pure) ---
374
374
 
375
375
  // Pair started+finished by tick_index; any 'started' with no 'finished' partner
376
- // is a tick that crashed mid-run exactly the silent-runner-death failure mode.
376
+ // is a tick that crashed mid-run, exactly the silent-runner-death failure mode.
377
377
  function findOrphanStarts(receipts) {
378
378
  if (!Array.isArray(receipts)) return [];
379
379
  const finished = new Set();
@@ -4,7 +4,7 @@
4
4
  // atris/runs/. Validating those paths on disk turns "trust the prose" into
5
5
  // "read the verifier state": the queue and accept surfaces show whether the
6
6
  // receipts named in a proof exist and whether their verifiers passed. This
7
- // informs the human gate it never blocks it.
7
+ // informs the human gate, it never blocks it.
8
8
  const fs = require('fs');
9
9
  const path = require('path');
10
10
 
package/lib/reel.js CHANGED
@@ -1,4 +1,4 @@
1
- // atris reel a card, animated. A reel is the card from lib/card.js rendered at
1
+ // atris reel, a card, animated. A reel is the card from lib/card.js rendered at
2
2
  // progress t in [0,1]: each element fades and rises in on a staggered schedule.
3
3
  // Pure: (spec, t) -> HTML for that single frame. commands/reel.js screenshots the
4
4
  // frames (same Chrome as card) and ffmpeg-encodes them. No new dependency.
@@ -67,7 +67,7 @@ function independentReviewActors(task) {
67
67
 
68
68
  // True when at least one review pass came from someone other than the
69
69
  // builder. When the builder cannot be resolved at all, fall back to
70
- // requiring two distinct actors independence cannot be proven from one.
70
+ // requiring two distinct actors. Independence cannot be proven from one.
71
71
  function hasIndependentReview(task) {
72
72
  const builder = taskBuilder(task);
73
73
  if (!builder) return reviewEventActors(task).size >= 2;
@@ -24,7 +24,7 @@ const RUNNER_PROFILE_DEFS = Object.freeze({
24
24
  commandTemplate: '{bin} --fast {prompt}',
25
25
  }),
26
26
  // Guest engines: any installed headless coding CLI is a swappable worker.
27
- // No commandTemplate on claude the default spawn shape is already
27
+ // No commandTemplate on claude, the default spawn shape is already
28
28
  // claude-compatible (bin -p prompt --model X --allowedTools ...).
29
29
  claude: Object.freeze({
30
30
  bin: 'claude',
@@ -70,8 +70,26 @@ const RUNNER_PROFILE_DEFS = Object.freeze({
70
70
  }),
71
71
  agy: Object.freeze({
72
72
  bin: 'agy',
73
- model: 'gemini-3.7-flash-high',
74
- commandTemplate: '{bin} --mode accept-edits {modelFlag} -p {prompt}',
73
+ // gemini 3.8 flash is the 2026-09-04 bake-off pick beside opencode muse.
74
+ // --add-dir or agy edits its own scratch folder; the headless note or
75
+ // gemini stops to ask "may I edit?" and exits 0 having changed nothing.
76
+ model: 'gemini-3.8-flash-high',
77
+ commandTemplate: '{bin} --mode accept-edits --dangerously-skip-permissions --add-dir "$PWD" {modelFlag} -p "You are running headless with edit permission already granted. Apply changes directly and never ask for confirmation. "{prompt}',
78
+ }),
79
+ opencode: Object.freeze({
80
+ bin: 'opencode',
81
+ model: '',
82
+ commandTemplate: '{bin} run {prompt}',
83
+ }),
84
+ // Private engine: fully functional (routable, selectable, probeable) but
85
+ // kept out of every operator-facing list. For engines an operator wants to
86
+ // use without advertising them on the roster. No pinned model: headless
87
+ // print mode rides whatever this machine's Command Code default is.
88
+ commandcode: Object.freeze({
89
+ bin: 'cmd',
90
+ model: '',
91
+ commandTemplate: '{bin} -p {prompt}',
92
+ hidden: true,
75
93
  }),
76
94
  });
77
95
 
@@ -94,8 +112,19 @@ const RUNNER_PROFILES = Object.freeze(
94
112
  ])
95
113
  );
96
114
 
97
- // Operator-facing list: canonical profile names only (no alias noise).
98
- const RUNNER_PROFILE_NAMES = Object.freeze(Object.keys(RUNNER_PROFILE_DEFS));
115
+ // Operator-facing list: canonical profile names only (no alias noise), and no
116
+ // hidden profiles. Hidden profiles stay resolvable through RUNNER_PROFILES and
117
+ // canonicalEngineName; they just never appear in help text, error hints, or
118
+ // rosters.
119
+ const HIDDEN_PROFILE_NAMES = Object.freeze(
120
+ Object.entries(RUNNER_PROFILE_DEFS)
121
+ .filter(([, def]) => def.hidden === true)
122
+ .map(([name]) => name)
123
+ );
124
+
125
+ const RUNNER_PROFILE_NAMES = Object.freeze(
126
+ Object.keys(RUNNER_PROFILE_DEFS).filter((name) => !HIDDEN_PROFILE_NAMES.includes(name))
127
+ );
99
128
 
100
129
  function shellWord(value) {
101
130
  const s = String(value || '');
@@ -227,6 +256,7 @@ module.exports = {
227
256
  RUNNER_PROFILE_DEFS,
228
257
  RUNNER_PROFILE_ALIASES,
229
258
  RUNNER_PROFILE_NAMES,
259
+ HIDDEN_PROFILE_NAMES,
230
260
  resolveRunnerProfile,
231
261
  resolveClaudeRunnerModel,
232
262
  resolveClaudeRunnerBin,
package/lib/scorecard.js CHANGED
@@ -245,14 +245,14 @@ function writeScorecard(atrisDir, data) {
245
245
 
246
246
  // Ensure scorecards.md exists
247
247
  if (!fs.existsSync(scorecardsPath)) {
248
- const template = `# scorecards.md Endgame Results\n\n> Append-only. One line per closed endgame. Records outcome metrics from the horizon.\n\n---\n\n`;
248
+ const template = `# scorecards.md \u2014 Endgame Results\n\n> Append-only. One line per closed endgame. Records outcome metrics from the horizon.\n\n---\n\n`;
249
249
  fs.writeFileSync(scorecardsPath, template, 'utf8');
250
250
  }
251
251
 
252
- // Format: - **[date] slug** shipped: X/Y wall-clock: Nh halt: Z% reward: total lessons: N
252
+ // Format: - **[date] slug**, shipped: X/Y, wall-clock: Nh, halt: Z%, reward: total, lessons: N
253
253
  const haltPercent = Math.round(haltRatio * 100);
254
254
  const wallClockStr = wallClockHours < 1 ? `${Math.round(wallClockHours * 60)}m` : `${wallClockHours.toFixed(1)}h`;
255
- const line = `- **[${endDate}] ${slug}** shipped: ${tasksShipped}/${tasksAttempted} wall-clock: ${wallClockStr} halt: ${haltPercent}% reward: ${totalReward} lessons: ${lessonsGenerated}\n`;
255
+ const line = `- **[${endDate}] ${slug}** \u2014 shipped: ${tasksShipped}/${tasksAttempted} \u2014 wall-clock: ${wallClockStr} \u2014 halt: ${haltPercent}% \u2014 reward: ${totalReward} \u2014 lessons: ${lessonsGenerated}\n`;
256
256
 
257
257
  // Append to file
258
258
  fs.appendFileSync(scorecardsPath, line, 'utf8');
@@ -302,7 +302,7 @@ function readScorecards(atrisDir) {
302
302
  const scorecards = [];
303
303
 
304
304
  for (const line of content.split('\n')) {
305
- const match = line.match(/^- \*\*\[(.+?)\]\s+(.+?)\*\*\s*—\s*shipped:\s*(\d+)\/(\d+)\s*—\s*wall-clock:\s*(.+?)\s*—\s*halt:\s*(\d+)%\s*—\s*reward:\s*(-?\d+)\s*—\s*lessons:\s*(\d+)$/);
305
+ const match = line.match(/^- \*\*\[(.+?)\]\s+(.+?)\*\*\s*\u2014\s*shipped:\s*(\d+)\/(\d+)\s*\u2014\s*wall-clock:\s*(.+?)\s*\u2014\s*halt:\s*(\d+)%\s*\u2014\s*reward:\s*(-?\d+)\s*\u2014\s*lessons:\s*(\d+)$/);
306
306
  if (!match) continue;
307
307
 
308
308
  const [, endDate, slug, shipped, attempted, wallClockStr, haltPercent, reward, lessons] = match;
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
- // atris security scan deterministic secrets / PII / privacy detectors (no LLM).
3
+ // atris security scan: deterministic secrets / PII / privacy detectors (no LLM).
4
4
  //
5
5
  // A finding is a fact (file:line + rule + severity), so it drops straight into a
6
6
  // loop / mission / CI gate and doubles as a SOC 2 evidence artifact (machine
7
7
  // JSON). Precision over recall: a noisy gate gets muted, and a muted gate is dead.
8
8
  // Suppress a single line with a trailing `atris-allow-secret` comment.
9
9
  //
10
- // Zero external deps (Node built-ins only) repo contract.
10
+ // Zero external deps (Node built-ins only). Repo contract.
11
11
 
12
12
  const fs = require('fs');
13
13
  const path = require('path');
@@ -77,7 +77,7 @@ const PII_RULES = [
77
77
  // Code-execution risks. For an AI CLI that runs autonomous loops and shells out,
78
78
  // these are the "are we actually safe" checks beyond data exposure. eval/Function
79
79
  // are almost never legitimate (HIGH); shelling out with interpolated input is the
80
- // command-injection class (MEDIUM common and sometimes safe, so it asks for a
80
+ // command-injection class (MEDIUM, common and sometimes safe, so it asks for a
81
81
  // human look rather than hard-failing the gate).
82
82
  const CODE_RULES = [
83
83
  { id: 'eval-call', sev: 'medium', cat: 'code', re: /(?<![.\w])eval\((?!\s*\))/, why: 'eval() can run untrusted code' },
@@ -271,7 +271,7 @@ function scanText(text, rules = RULES) {
271
271
  return out;
272
272
  }
273
273
 
274
- // Code-execution rules only make sense in actual code files `eval(` written in
274
+ // Code-execution rules only make sense in actual code files, `eval(` written in
275
275
  // a markdown doc is prose, not a vuln.
276
276
  const CODE_EXTS = new Set(['.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx', '.py', '.rb', '.go', '.java', '.php', '.sh', '.bash', '.zsh']);
277
277
  const DOC_EXTS = new Set(['.md', '.mdx', '.txt', '.patch']);
@@ -359,7 +359,7 @@ function resolveTargets({ root = process.cwd(), paths = [], staged = false } = {
359
359
 
360
360
  // Full scan: line-level findings across files + repo hygiene findings.
361
361
  // The scanner's own pattern database and its fixtures necessarily contain the
362
- // very strings it detects never scan them (a tool that flags itself is noise).
362
+ // very strings it detects. Never scan them (a tool that flags itself is noise).
363
363
  const SELF_FILES = new Set(['lib/security-scan.js', 'commands/security-review.js', 'test/security-scan.test.js']);
364
364
 
365
365
  function runScan({ root = process.cwd(), paths = [], staged = false } = {}) {
package/lib/self-drive.js CHANGED
@@ -66,7 +66,7 @@ function postBlockerToFleet({ task, blockerClass, stopReason, mission }, injecte
66
66
  const apiKey = loadKey();
67
67
  if (!apiKey) return { posted: false, channel };
68
68
  const ref = taskRef(task);
69
- const content = `Mission blocker filed with no local engine ready: ${ref} ${String(stopReason || blockerClass).trim()}. Mission: ${String(mission?.objective || mission?.id || '').trim()}.`;
69
+ const content = `Mission blocker filed with no local engine ready: ${ref}: ${String(stopReason || blockerClass).trim()}. Mission: ${String(mission?.objective || mission?.id || '').trim()}.`;
70
70
  const response = httpPost(`${SWARLO_HUB}/api/${SWARLO_HUB_ID}/channels/${channel}/posts`, {
71
71
  headers: {
72
72
  'Content-Type': 'application/json',