atris 3.58.7 → 3.59.0

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 (95) hide show
  1. package/AGENTS.md +3 -2
  2. package/GETTING_STARTED.md +2 -2
  3. package/PERSONA.md +11 -11
  4. package/README.md +1 -1
  5. package/atris/GEMINI.md +7 -6
  6. package/atris/PERSONA.md +4 -4
  7. package/atris/features/README.md +77 -6
  8. package/atris/skills/README.md +44 -15
  9. package/atris/skills/aeo/SKILL.md +10 -10
  10. package/atris/skills/atris/SKILL.md +1 -1
  11. package/atris/skills/engines/SKILL.md +69 -50
  12. package/atris/skills/meta/SKILL.md +4 -4
  13. package/bin/atris.js +8 -6
  14. package/commands/aeo.js +9 -12
  15. package/commands/analytics.js +8 -11
  16. package/commands/autopilot.js +79 -8
  17. package/commands/avail.js +1 -1
  18. package/commands/brain.js +3 -1
  19. package/commands/business.js +13 -8
  20. package/commands/clean.js +17 -0
  21. package/commands/close.js +2 -1
  22. package/commands/computer.js +15 -10
  23. package/commands/context-sync.js +3 -3
  24. package/commands/doc-health.js +55 -2
  25. package/commands/engine.js +384 -4
  26. package/commands/experiments.js +13 -4
  27. package/commands/host.js +53 -0
  28. package/commands/improve.js +1 -1
  29. package/commands/integrations.js +2 -5
  30. package/commands/learn.js +1 -1
  31. package/commands/member.js +231 -53
  32. package/commands/mission.js +129 -19
  33. package/commands/one-lap.js +58 -3
  34. package/commands/pack.js +2 -2
  35. package/commands/probe.js +11 -5
  36. package/commands/pull.js +1 -1
  37. package/commands/push.js +1 -1
  38. package/commands/rainmaker.js +3 -16
  39. package/commands/run.js +4 -1
  40. package/commands/scout.js +1 -1
  41. package/commands/search.js +5 -3
  42. package/commands/spaceship.js +1 -1
  43. package/commands/stream.js +27 -2
  44. package/commands/study.js +15 -9
  45. package/commands/sync.js +1 -1
  46. package/commands/task.js +13 -2
  47. package/commands/unknowns.js +5 -0
  48. package/commands/verify.js +12 -4
  49. package/commands/visualize.js +20 -3
  50. package/commands/workflow.js +23 -7
  51. package/lib/codex-flight.js +28 -5
  52. package/lib/dispatch-scout.js +31 -22
  53. package/lib/engine-registry.js +1482 -13
  54. package/lib/feedback-triage.js +2 -0
  55. package/lib/fleet.js +111 -12
  56. package/lib/host.js +336 -0
  57. package/lib/improve-vitals-html.js +3 -2
  58. package/lib/known-commands.js +1 -1
  59. package/lib/lesson-contradiction.js +2 -2
  60. package/lib/map-refs.js +226 -0
  61. package/lib/member-engine.js +253 -0
  62. package/lib/mission-room.js +4 -4
  63. package/lib/noninteractive.js +15 -4
  64. package/lib/policy-lessons.js +1 -1
  65. package/lib/receipt-block.js +137 -28
  66. package/lib/roster-markdown.js +206 -0
  67. package/lib/roster-models.js +179 -0
  68. package/lib/runner-command.js +95 -15
  69. package/lib/scorecard.js +4 -4
  70. package/lib/self-drive.js +9 -1
  71. package/lib/sleep-sync.js +11 -0
  72. package/lib/state-detection.js +28 -9
  73. package/lib/task-db.js +3 -2
  74. package/lib/todo-fallback.js +1 -1
  75. package/lib/wish-audit.js +10 -6
  76. package/lib/wish-delegate.js +29 -5
  77. package/mcp/atris-mcp/index.mjs +77 -24
  78. package/package.json +2 -5
  79. package/scripts/det/det.js +2 -2
  80. package/scripts/det/pr-description.js +2 -2
  81. package/scripts/det/test.js +2 -2
  82. package/templates/members/host/MEMBER.md +23 -0
  83. package/templates/members/host/MISSION.md +11 -0
  84. package/templates/members/host/SOUL.md +7 -0
  85. package/templates/members/host/context/why.md +3 -0
  86. package/templates/members/host/goals.json +5 -0
  87. package/templates/members/host/goals.md +3 -0
  88. package/templates/members/host/skills/host/SKILL.md +13 -0
  89. package/utils/api.js +1 -1
  90. package/utils/auth.js +17 -7
  91. package/utils/backend-root.js +31 -0
  92. package/utils/config.js +5 -4
  93. package/utils/owner-identity.js +61 -0
  94. package/atris/policies/LESSONS.md +0 -13
  95. package/atris/skills/autopilot/atris-autopilot.md +0 -71
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: engines
3
3
  description: "Dispatch work to an installed terminal agent or named Atris engine profile. Supports Atris Fast, Claude, Codex, Cursor, Fable, Composer, Haiku, Devin, Grok, Antigravity (agy), and opencode. Triggers on: use codex, use cursor, use devin, use grok, use agy, use antigravity, use gemini, gemini session, use fable, use claude, use opencode, use atris, engine, dispatch to, worker agent, second opinion build."
4
- version: 1.5.2
4
+ version: 1.7.0
5
5
  tags:
6
6
  - engines
7
7
  - claude
@@ -20,10 +20,36 @@ tags:
20
20
  - orchestration
21
21
  ---
22
22
 
23
- # Engines — interchangeable terminal workers
23
+ # Engines: interchangeable terminal workers
24
24
 
25
25
  One contract, eleven live profiles. The orchestrator writes a bounded task prompt, dispatches it to an engine, then **independently verifies, lands, and pushes** the result. Engines never self-certify.
26
26
 
27
+ ## Pick the job owner first
28
+
29
+ Before choosing an engine for search, build, or review, run `atris engine roster` and use that job's lead worker. Only deviate when the user names an engine.
30
+
31
+ The roster is a markdown file the owner can edit any time: `atris/ROSTER.md` for this project, `~/.atris/ROSTER.md` for every project on this machine. A project job wins over the machine job with the same name. Each `## job` heading lists its workers in order, one per line, naming the tool and the model:
32
+
33
+ ```
34
+ ## review
35
+ - codex, model: gpt-6-astra, effort: medium, max: 20 min
36
+ - claude code, model: opus 5.5
37
+
38
+ ## small build
39
+ - devin, model: swe-2-max, until 2026-10-24
40
+ - grok, model: grok 4.7 fast
41
+ ```
42
+
43
+ The first worker that is ready and not expired leads; the rest are its backups, in order. Tools take engine ids or `claude code`, `atris fast`, and `gemini`. The older one-line shape (`build: claude opus 5.5, backup codex`) still reads the same, and the first `atris engine assign` rewrites the file into sections. `assign <job> <tool>` sets the lead and keeps the rest; `--add` puts a worker at the end, `--remove <tool>` drops one, `--clear` drops the job, and `--everywhere` writes the machine file. `atris engine roster --available` lists the tools and models on this machine.
44
+
45
+ A job's list is also its team. When a task splits into parallel parts, give the parts to that job's workers in order, first worker first, instead of piling them all on the lead. Heavy models like astra and fable should only judge: have the search lead write them a trimmed brief first.
46
+
47
+ Changes for one shell only go in a session file above both rosters. Set `ATRIS_ROSTER_SESSION=<name>` (agent shells have no terminal, so they must), then `atris engine assign <job> <tool> --session`. A session job replaces that job's whole list for this shell; `atris engine roster session` shows the changes and `atris engine roster session clear` drops them. A session nobody used for a day is dropped on its own.
48
+
49
+ Jobs are open-ended: besides search, build, and review, the owner can add a job like `## small build`, or `## quick fixes (like build)` when the name does not say its kind. For a small, tightly specified build slice, use the small build lead when `atris engine roster` shows one; bigger builds use build.
50
+
51
+ Team members pick automatically. Navigators, researchers, and scouts do search; validators, reviewers, judges, critics, and verifiers do review; everyone else builds. A line under `## team` overrides that, with a job (`- researcher: search`) or a tool and model (`- judge: claude code, model: opus 5.5`). `member run`, missions with an owner, and autopilot's plan, do, and review use the member's pick; `--engine` still wins. `atris engine roster` lists the team and warns about any line it could not use.
52
+
27
53
  ## three verbs
28
54
 
29
55
  - ask: `atris engine <name> "<question>"`; pin a model with `atris engine <name> --model <model> "<question>"`
@@ -59,54 +85,47 @@ substitute raw Claude or another engine and call the result FABLE.
59
85
 
60
86
  Raw spawns are not the default because they skip Atris receipts, watch, and coaching.
61
87
 
62
- | Engine | Command | Notes |
63
- |--------|---------|-------|
64
- | Atris Fast | `atris chat --print "<prompt>"` (equivalently `ax --fast --print`) | Local tool runtime over the api.atris.ai fast lane. Best for bounded lookups and small verified edits. |
65
- | Claude | `claude -p "<prompt>"` | Uses the local Claude configuration. Add `--model opus` for maximum-depth review or `--model sonnet` for speed. |
66
- | Codex | `codex exec --dangerously-bypass-approvals-and-sandbox -o <result-file> "<prompt>"` (run from the target repo/worktree; read-only research: `--sandbox read-only`) | Headless, exits when done — run it as a tracked background Bash task like the other engines and completion auto-wakes the session. Final answer lands in the `-o` file. Verified live 2026-08-11. Outside a git repo add `--skip-git-repo-check` or it exits 1. ONE-SHOT SESSIONS (`claude -p` workers): run codex in the FOREGROUND and wait — a one-shot session never wakes again, so backgrounding strands the result (observed 2026-08-11). The old plugin path (`codex-companion.mjs task --background`) is deprecated for dispatch: its job store never notifies the session (a finished result sat unread overnight, 2026-08-10) |
67
- | Cursor | `cursor-agent --trust -p "<prompt>"` (run from the target repo) | Headless print mode; `--trust` required for non-interactive |
68
- | Fable | `atris engine fable "<question>"` | Canonical read-only FABLE ask with guards, live log, receipt, and health tracking. Scale `--timeout` to the work when needed. |
69
- | Composer | `atris run "<objective>" --engine composer` | Fast navigator/executor profile routed through the installed `ax` binary. |
70
- | Haiku | `claude -p "<prompt>" --model claude-haiku-4-5` | Fast validation and bounded read-only checks. |
71
- | Devin | `devin -p --permission-mode dangerous -- "<prompt>"` (run from the target repo) | Default permission mode is read-only for writes — build work NEEDS `--permission-mode dangerous`, so only run it in an isolated worktree. Also `devin cloud` for sessions that outlive this machine. Supports `--model swe-2-max`; `devin models list` verifies availability and price. |
72
- | Grok | `grok --always-approve -p "<prompt>"` (run from the target repo) | Headless single-turn via `-p`; default model grok-4.6. Very fast on lookups (~5-10s, reads MAP first). Great for quick second opinions; use `--best-of-n <N>` for tricky bounded builds. Uses grok.com login |
73
- | Antigravity | `agy --mode accept-edits --add-dir "$PWD" -p "<prompt>"` (run from the target repo) | `agy` executor profile; also answers to "gemini". **`--add-dir` is mandatory for writes** — without it agy edits its own scratch folder (`~/.gemini/antigravity-cli/scratch/`) and the project never changes, which looks like a silent failure (verified live 2026-08-28). Use `--mode plan --sandbox` for read-only review, `--model <id>` to pin a model, and `--dangerously-skip-permissions` if a build still stalls on an approval prompt. |
74
- | opencode | `opencode run "<prompt>"` (read-only ask: `opencode run --agent plan "<prompt>"`) | Headless print mode; exits when done. Pin a model with `-m provider/model`. Build work needs `--auto` to auto-approve permissions (dangerous: run in an isolated worktree). Verified live 2026-08-21, ~7s per plan-mode lookup. |
88
+ Smoke: `reply with the word OK`, run from `$HOME`, 120s cap, one at a time on a loaded Mac (2026-09-23). Seconds are wall time under that load.
89
+
90
+ | Engine | Exact command | Pinned model | Verified | Seconds | Notes |
91
+ |--------|---------------|--------------|----------|--------:|-------|
92
+ | Atris Fast | `atris chat --print "<prompt>"` | `atris:fast` | 2026-09-23 | 3.2 | Needs an initialized `atris/` workspace; see runtime requirements below. |
93
+ | Claude | `claude -p "<prompt>" --model claude-opus-5-5 --output-format json --no-session-persistence` | `claude-opus-5-5` | 2026-09-23 | 20.3 | `--model opus` also resolves to `claude-opus-5-5` today; pin the id for reproducibility. |
94
+ | Codex | `codex exec --dangerously-bypass-approvals-and-sandbox --ephemeral -m gpt-6-sol -c model_reasoning_effort=high -o <result-file> "<prompt>" </dev/null` (run from the target repo/worktree; read-only: swap the bypass flag for `--sandbox read-only`) | `gpt-6-sol`, high | 2026-09-23 | 13.8 | Keep the `</dev/null`: with stdin open codex prints "Reading additional input from stdin..." and waits forever (observed 2026-09-23). Outside a git repo add `--skip-git-repo-check` or it exits 1 (re-checked 2026-09-23). Final answer lands in the `-o` file. Run as a tracked background Bash task; in one-shot `claude -p` workers run it in the FOREGROUND, a one-shot session never wakes again (2026-08-11). `codex-companion.mjs task --background` is deprecated: its job store never notifies the session (2026-08-10). |
95
+ | Cursor | `cursor-agent --trust --model composer-2.5 -p "<prompt>"` (run from the target repo) | `composer-2.5` | 2026-09-23 | 37.5 | `--trust` required for non-interactive. |
96
+ | Fable | `atris engine fable --timeout 120 --json "<question>"` | `claude-fable-5` | 2026-09-23 | 21.4 | Canonical read-only FABLE ask with receipt; scale `--timeout` to the work. |
97
+ | Composer | `cursor-agent --trust --model composer-2.5 -p "<prompt>"` | `composer-2.5` via Cursor | 2026-09-23 | 37.5 | Same binary as Cursor. |
98
+ | Haiku | `claude -p "<prompt>" --model claude-haiku-4-5 --output-format json --no-session-persistence` | `claude-haiku-4-5` | 2026-09-23 | 19.0 | Fast validation and bounded read-only checks. |
99
+ | Devin (build) | `devin -p --permission-mode dangerous --model swe-2-max --prompt-file <brief>` (run from an isolated worktree) | `swe-2-max` (Free tag in `devin models list`, 2026-09-23) | 2026-09-23 | 14.0 | In `-p` mode the default permission mode rejects shell commands too, so even a read-only audit dies after one step (2026-09-23). Keep the brief outside the worktree. Long runs (5+ min) can print nothing even on success: judge by the worktree diff. `devin cloud` outlives this machine. |
100
+ | Devin (search) | `devin -p --permission-mode dangerous --model swe-1.7-lightning -- "<prompt>"` | `swe-1.7-lightning` (paid, no Free tag on 2026-09-23) | 2026-09-23 | 13.9 | |
101
+ | Grok | `grok --always-approve --model grok-4.7 -p "<prompt>"` (run from the target repo) | `grok-4.7` | 2026-09-23 | 11.7 | Unpinned default is now `grok-4.7-build-fast`; `grok models` lists the menu. Uses grok.com login. `--best-of-n <N>` for tricky bounded builds. |
102
+ | Antigravity (agy) | `agy --mode accept-edits --add-dir "$PWD" --model gemini-3.8-flash-high --output-format json -p "<prompt>"` (run from the target repo) | `gemini-3.8-flash-high` | 2026-09-23 | 15.9 | **`--add-dir` is mandatory for writes**: without it agy edits `~/.gemini/antigravity-cli/scratch/` and the project never changes (2026-08-28). `--mode plan --sandbox` for read-only review. Also answers to "gemini". |
103
+ | opencode | `opencode run -m opencode/muse-spark-1.3-contributor-free "<prompt>"` (read-only: add `--agent plan`) | `opencode/muse-spark-1.3-contributor-free` | 2026-09-23 | 41.1 | Build work needs `--auto` (isolated worktree only). `opencode models` lists the live menu. |
75
104
 
76
105
  Headless dispatch permissions (verified 2026-08-11): `codex exec`, `grok`, and `cursor-agent` are allowlisted in `~/.claude/settings.json` so fresh and one-shot sessions can dispatch without a human approval click. A cold session that gets "requires approval" on an engine command means that allowlist regressed.
77
106
 
78
107
  ## Picking an engine
79
108
 
80
- - **Atris Fast** — cheap bounded lookups, single-file facts, small verified edits, and high-volume fan-out.
81
- - **FABLE** — strongest judgment lane. Use the canonical Atris profile for deep review, synthesis, validation, and complex builds.
82
- - **Claude** — direct Claude profile when the operator names Claude rather than FABLE.
83
- - **Codex** — deep root-cause work, long autonomous builds, second-opinion diagnosis. Slowest; runs sandboxed.
84
- - **Cursor** — fast bounded edits and refactors in a single repo.
85
- - **Composer / Haiku** — fast, bounded navigation, edits, and validation where a max-tier model would be wasteful.
86
- - **Devin** — multi-step feature work; use `cloud` when the run should survive laptop sleep.
87
- - **Grok** - fastest frontier lookups and quick second opinions (grok-4.6, ~5-10s; reads MAP first); use `--best-of-n` for tricky bounded builds. Uses grok.com login.
88
- - **Antigravity / agy** — flexible executor work across Gemini, Claude, and GPT-OSS models; use `agy` as the canonical short name.
89
- - **opencode** — multi-provider executor in one CLI (Claude, GPT, Gemini, DeepSeek menus via `opencode models`); plan agent for read-only asks, `--auto` builds only inside an isolated worktree.
109
+ - **Cheap bounded errands**: Devin `swe-2-max` while its Free tag holds (check `devin models list`).
110
+ - **Multi-file or long build**: Use the project's build pick. If there is no pick, use the router's choice.
111
+ - **Judgment-heavy build**: Opus 5.5 subagent (Claude row).
112
+ - **Review / deep judgment**: FABLE profile; Codex `gpt-6-sol` or Opus 5.5 as second validator.
113
+ - **Quick fix**: Cursor.
114
+ - **Search / bounded lookup**: Atris Fast; Haiku or Devin `swe-1.7-lightning` for read-only sweeps; Grok for quick second opinions.
115
+ - **agy / opencode**: extra executors when you want a different model family.
90
116
  - Parallel builds across repos: one engine job per repo, never two engines writing the same checkout.
91
117
 
92
- ## Models worth pinning (verified live 2026-08-15)
93
-
94
- Each engine CLI can pin a specific model. Current best picks:
118
+ ## Models worth pinning (verified live 2026-09-23)
95
119
 
96
- | Engine | Flag | Best models today |
97
- |--------|------|-------------------|
98
- | Claude / Fable | `--model opus` | `opus` currently resolves to Opus 5; use the explicit Opus 4.8 identifier only for reproducibility |
99
- | Devin | `--model swe-2-max` | `swe-2-max`, `swe-2-high`, `swe-2-medium` verified Free in the live CLI on 2026-09-10. Use Max for Keshav’s team; recheck with `devin models list` before unattended work. |
100
- | Cursor | `--model cursor-grok-4.6-xhigh` | `cursor-grok-4.6-xhigh` for second-opinion builds, `cursor-grok-4.6-high-fast` for quick pinned asks (answered in ~12s live 2026-08-12), `composer-2.5` for fast edits; parameterized Claude via `'claude-opus-4-8[effort=high]'`; `--list-models` shows the full menu |
101
- | Composer | `--engine composer` | `composer 2.5` through the Atris profile |
102
- | Haiku | `--model claude-haiku-4-5` | `haiku` for fast validation |
103
- | Grok | (default) | `grok-4.6` default (confirmed live 2026-08-12, ~5s lookup), `grok-4.5` still available via `-m` |
104
- | Codex | `-m <model>` | CLI default rides `~/.codex/config.toml`; pin with `-m` only when the task needs it |
105
- | Antigravity / agy | `--model <id>` | `gemini-3.7-flash-high` for speed, `gemini-3.1-pro-high` for depth, `claude-sonnet-4-6`, `claude-opus-4-6-thinking`, or `gpt-oss-120b-medium` |
106
- | opencode | `-m <provider/model>` | `opencode/big-pickle`, `opencode/gpt-5.2`, `opencode/claude-opus-4-8`; `opencode models` lists the live menu |
107
- | Atris Fast | (fixed) | api.atris.ai fast lane |
120
+ | Engine | Flag | Alternates on the live menu |
121
+ |--------|------|-----------------------------|
122
+ | Claude | `--model <id>` | `claude-opus-5-5`, `claude-haiku-4-5` |
123
+ | Devin | `--model <id>` | `swe-2-max`, `swe-2-high`, `swe-2-medium` (Free); `claude-opus-5-5-high`, `gpt-6-sol` (paid) |
124
+ | Cursor | `--model <id>` | `composer-2.5`, `grok-4.7-xhigh`, `claude-opus-5-thinking-high`; `--list-models` |
125
+ | Grok | `--model <id>` | `grok-4.7`, `grok-4.7-build-fast` (default), `grok-4.6`, `grok-4.5`; `grok models` |
126
+ | Codex | `-m <id>` | `gpt-6-sol` is the `~/.codex/config.toml` default |
108
127
 
109
- Re-verify this table when a lab ships a new model: run each CLI's model-list command, smoke one lookup, and update the row. Free-tier windows (like swe-1.7 now) are the moment to fan out volume work.
128
+ Pins expire 30 days after their verified date. Re-verify on a new model: run the CLI's model list, smoke one prompt, update the row.
110
129
 
111
130
  ## Keep the local roster current
112
131
 
@@ -115,7 +134,7 @@ Run `atris engine doctor`, then `atris engine --help`. The canonical profiles li
115
134
  ## Atris Fast runtime requirements (verified live 2026-07-03)
116
135
 
117
136
  - Must run from an **initialized Atris workspace** (an `atris/` folder) under an allowed workspace root (e.g. `~/arena/*`). `atris chat --print` outside one exits 1 with "Run atris init"; `ax --fast --print` outside an allowed root fail-closes with `{ok:false, error:"workspace_path must be under an allowed local workspace root"}`.
118
- - In an allowed root **without** an `atris/` folder the turn silently routes to the cloud no-tools chat lane — the model will honestly refuse file work ("no Atris Desktop runtime attached"). If the output says that, you dispatched from the wrong directory; it is not an engine failure.
137
+ - In an allowed root **without** an `atris/` folder the turn silently routes to the cloud no-tools chat lane. The model will honestly refuse file work ("no Atris Desktop runtime attached"). If the output says that, you dispatched from the wrong directory; it is not an engine failure.
119
138
 
120
139
  ## Prompt contract (every dispatch)
121
140
 
@@ -140,16 +159,16 @@ the actual final state: draft, queued, and merged are different outcomes.
140
159
  Record elapsed time, engine calls, retries, and exposed token usage. Unknown
141
160
  usage stays unknown; one pilot does not establish a speedup or perfect accuracy.
142
161
 
143
- 1. Name the absolute repo path and tell the engine to `cd` there (Atris Fast scopes to the cwd it runs from — cd first, and name absolute paths in the prompt).
162
+ 1. Name the absolute repo path and tell the engine to `cd` there (Atris Fast scopes to the cwd it runs from, so cd first and name absolute paths in the prompt).
144
163
  2. Bound the slice: one task, explicit exit criteria, the verify command to run.
145
- 3. Git rules: `git status` first; stage only own files; never revert others' changes; never destructive git; work on a branch `member/<name>-<slug>` or a worktree. (Atris Fast does not run git — for edit tasks the orchestrator commits after verifying.)
146
- 4. Require a final report: files changed, verify command + result, branch name. (Atris Fast returns one JSON `output` field — ask for file:line evidence in it.)
164
+ 3. Git rules: `git status` first; stage only own files; never revert others' changes; never destructive git; work on a branch `member/<name>-<slug>` or a worktree. (Atris Fast does not run git. For edit tasks the orchestrator commits after verifying.)
165
+ 4. Require a final report: files changed, verify command + result, branch name. (Atris Fast returns one JSON `output` field. Ask for file:line evidence in it.)
147
166
 
148
- ## Landing (orchestrator duties — never skip)
167
+ ## Landing (orchestrator duties, never skip)
149
168
 
150
169
  - **Codex sandbox cannot reach github.com and may get read-only repo access.** Expect temp clones / `git format-patch` fallbacks under `/private/tmp`. Apply patches in a fresh worktree, re-run the verify command yourself, then push.
151
- - Cursor and Devin run unsandboxed — still re-run the verify command yourself before pushing.
152
- - Long Devin runs (5+ min) can return empty stdout even when the build fully succeeded — judge by `git status` and the diff in its worktree, never by the printed report.
153
- - Atris Fast answers are model output over a real tool runtime — treat `output` as a claim, spot-check the cited file:line, and re-run any verifier yourself before acting on it.
170
+ - Cursor and Devin run unsandboxed. Still re-run the verify command yourself before pushing.
171
+ - Long Devin runs (5+ min) can return empty stdout even when the build fully succeeded. Judge by `git status` and the diff in its worktree, never by the printed report.
172
+ - Atris Fast answers are model output over a real tool runtime. Treat `output` as a claim, spot-check the cited file:line, and re-run any verifier yourself before acting on it.
154
173
  - Engine task DBs and receipts written inside a sandbox are snapshots; reconcile against the live `atris task` plane after landing.
155
- - A stalled job (no log output for 30+ min) gets cancelled and taken over; don't wait on it. Atris Fast turns that exceed ~60s have hung — kill and retry once with a tighter prompt.
174
+ - A stalled job (no log output for 30+ min) gets cancelled and taken over; don't wait on it. Atris Fast turns that exceed ~60s have hung. Kill and retry once with a tighter prompt.
@@ -23,7 +23,7 @@ How to be an effective agent. Load this when you need to check yourself.
23
23
 
24
24
  1. **MAP first** - Read `atris/MAP.md` before any search. It's the index.
25
25
  2. **Journal context** - Check today's log for recent work, patterns, blockers.
26
- 3. **LESSONS.md** - What failed before? Don't repeat.
26
+ 3. **atris/lessons.md** - What failed before? Don't repeat.
27
27
  4. **Skill routing** - Frontend? Load `design`. Backend? Load `backend`. Writing? Load `writing`.
28
28
 
29
29
  ## Pace control
@@ -48,7 +48,7 @@ Stop and reassess when:
48
48
  ## Memory leverage
49
49
 
50
50
  Before acting:
51
- 1. Check `atris/policies/LESSONS.md` - short lessons from past mistakes
51
+ 1. Check `atris/lessons.md` - short lessons from past mistakes
52
52
  2. Check `atris/features/` - prior art for similar work
53
53
  3. Check journal history - `grep -i "<keyword>" atris/logs/**/*.md`
54
54
 
@@ -72,7 +72,7 @@ Act (execute with focus)
72
72
  ↓
73
73
  Check (output match intent?)
74
74
  ↓
75
- Learn (add to LESSONS if miss)
75
+ Learn (add to lessons.md if miss)
76
76
  ```
77
77
 
78
78
  ## Anti-patterns
@@ -88,7 +88,7 @@ Learn (add to LESSONS if miss)
88
88
  ## Skill evolution
89
89
 
90
90
  Skills improve from your mistakes:
91
- 1. After REVIEW, log learnings to `LESSONS.md` (format: `date | skill | lesson`)
91
+ 1. After REVIEW, log learnings to `atris/lessons.md` (one line per lesson)
92
92
  2. Repeated lessons (2-3x) get promoted into the relevant skill
93
93
  3. Next agent loads the improved skill
94
94
 
package/bin/atris.js CHANGED
@@ -47,7 +47,7 @@ const {
47
47
  requireAccountBound,
48
48
  refuseAccountGlobal,
49
49
  } = require('../lib/account-bound');
50
- const { isHelpToken, argsWantHelp, hasYesFlag } = require('../lib/noninteractive');
50
+ const { isHelpToken, argsWantHelp, hasYesFlag, envForcesNonInteractive } = require('../lib/noninteractive');
51
51
 
52
52
  // State detection for smart default
53
53
  const { detectWorkspaceState, loadContext } = require('../lib/state-detection');
@@ -577,7 +577,7 @@ function showHelpAll() {
577
577
  console.log(' watch - Turn one sentence into an always-on background watcher');
578
578
  console.log(' ctop - Show a process-first live agent CPU/memory view');
579
579
  console.log(' doctor - Node/task/auth/workspace readiness (--json for agents)');
580
- console.log(' doc-health - workspace document size, navigation, and freshness (--json)');
580
+ console.log(' doc-health - workspace document size, navigation, freshness, and map line refs (--json, --fix-refs)');
581
581
  console.log(' launchpad - Show the next action from local brain, task, mission, and proof state');
582
582
  console.log(' brief - Show the one-glance operator brief');
583
583
  console.log(' status - See local work and completions (`atris status <business>` for remote)');
@@ -1248,6 +1248,7 @@ if (!command || !knownCommands.includes(command)) {
1248
1248
  error: command ? `unknown command: ${command}` : 'unknown command',
1249
1249
  command: command || null,
1250
1250
  input: rawNaturalArgs.join(' '),
1251
+ suggestions: suggestCommands(command),
1251
1252
  usage: 'atris help',
1252
1253
  }, null, 2));
1253
1254
  process.exit(2);
@@ -1299,8 +1300,7 @@ function useInteractiveAtrisUi() {
1299
1300
  return Boolean(
1300
1301
  process.stdin.isTTY
1301
1302
  && process.stdout.isTTY
1302
- && !process.env.ATRIS_NO_INTERACTIVE
1303
- && process.env.ATRIS_NONINTERACTIVE !== '1'
1303
+ && !envForcesNonInteractive()
1304
1304
  );
1305
1305
  }
1306
1306
 
@@ -1464,7 +1464,7 @@ async function interactiveEntry(userInput, options = {}) {
1464
1464
  : (context.inProgressFeatures || []).length;
1465
1465
 
1466
1466
  // Pull active missions (durable goals), these outrank dev-pipeline state
1467
- // because a mission with an unverified verifier is a Keshav-attributable
1467
+ // because a mission with an unverified verifier is an owner commitment
1468
1468
  // commitment that hasn't been closed yet.
1469
1469
  const activeMissions = loadActiveMissions(workspaceDir);
1470
1470
  const liveMissionsCount = activeMissions.length;
@@ -2959,7 +2959,7 @@ if (command === 'guide') {
2959
2959
  const subcommand = process.argv[3];
2960
2960
  const args = process.argv.slice(4);
2961
2961
  Promise.resolve(require('../commands/member').memberCommand(subcommand, ...args))
2962
- .then(() => process.exit(0))
2962
+ .then(() => process.exit(subcommand === 'run' ? (process.exitCode || 0) : 0))
2963
2963
  .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
2964
2964
  } else if (command === 'app') {
2965
2965
  const subcommand = process.argv[3];
@@ -3289,6 +3289,8 @@ if (command === 'guide') {
3289
3289
  .then(() => process.exit(0))
3290
3290
  .catch((err) => { console.error(`✗ Error: ${err.message || err}`); process.exit(1); });
3291
3291
  }
3292
+ } else if (command === 'host') {
3293
+ require('../commands/host').hostCommand();
3292
3294
  } else if (command === 'social') {
3293
3295
  require('../commands/social').socialCommand()
3294
3296
  .then(() => process.exit(0))
package/commands/aeo.js CHANGED
@@ -15,32 +15,29 @@
15
15
  * atris aeo discover <source> [...] # discovery audit
16
16
  * atris aeo audit <source> [...] # agent-usability audit
17
17
  *
18
- * Local workspace resolution: $ATRIS_AEO_ROOT or legacy $ATRIS_BACKEND_ROOT.
18
+ * Local workspace resolution: $ATRIS_AEO_ROOT or the configured backend root.
19
19
  */
20
20
 
21
21
  const fs = require('fs');
22
22
  const os = require('os');
23
23
  const path = require('path');
24
24
  const { spawnSync } = require('child_process');
25
+ const { BACKEND_ROOT_HINT, resolveBackendRoot: resolveConfiguredBackendRoot } = require('../utils/backend-root');
25
26
  const { loadCredentials, abortOnAuthFailure } = require('../utils/auth');
26
27
  const { apiRequestJson } = require('../utils/api');
27
28
  const { loadBusinesses, saveBusinesses } = require('./business');
28
29
 
29
- function resolveBackendRoot() {
30
- const candidates = [
31
- process.env.ATRIS_AEO_ROOT,
32
- process.env.ATRIS_BACKEND_ROOT,
33
- ].filter(Boolean);
34
- for (const root of candidates) {
35
- if (fs.existsSync(path.join(root, 'atris', 'features', 'aeo'))) return root;
36
- }
37
- return null;
30
+ function resolveAeoRoot() {
31
+ const explicit = process.env.ATRIS_AEO_ROOT;
32
+ if (explicit && fs.existsSync(path.join(explicit, 'atris', 'features', 'aeo'))) return explicit;
33
+ const backend = resolveConfiguredBackendRoot();
34
+ return backend && fs.existsSync(path.join(backend, 'atris', 'features', 'aeo')) ? backend : null;
38
35
  }
39
36
 
40
37
  function requireBackendRoot() {
41
- const root = resolveBackendRoot();
38
+ const root = resolveAeoRoot();
42
39
  if (!root) {
43
- console.error('Cannot find local AEO workspace. Set ATRIS_AEO_ROOT to a workspace containing atris/features/aeo.');
40
+ console.error(`cannot find local aeo workspace; ${BACKEND_ROOT_HINT}`);
44
41
  process.exit(1);
45
42
  }
46
43
  return root;
@@ -68,17 +68,14 @@ function analyticsAtris() {
68
68
  }
69
69
 
70
70
  // Parse timestamps for productivity hours. Match the journal heading
71
- // format `### Title -- HH:MM` (the dashed form) and the legacy `**HH:MM:SS**` form.
72
- const timestampMatches = content.match(/(?:\u2014|--)\s*(\d{2}):\d{2}(?::\d{2})?\b|\*\*(\d{2}):\d{2}(?::\d{2})?\*\*/g);
73
- if (timestampMatches) {
74
- timestampMatches.forEach(ts => {
75
- const m = ts.match(/(\d{2}):/);
76
- if (!m) return;
77
- const hour = parseInt(m[1], 10);
78
- if (Number.isFinite(hour) && hour >= 0 && hour < 24) {
79
- hourCounts[hour] = (hourCounts[hour] || 0) + 1;
80
- }
81
- });
71
+ // formats `### Title -- HH:MM`, `### Title - HH:MM`, and legacy `**HH:MM:SS**`.
72
+ // Only heading lines count, so a prose range like "10:00 - 12:30" is not a timestamp.
73
+ const timestampPattern = /^#{2,4} .*(?:\u2014|--| - )\s*(\d{2}):\d{2}(?::\d{2})?\s*$|\*\*(\d{2}):\d{2}(?::\d{2})?\*\*/gm;
74
+ for (const match of content.matchAll(timestampPattern)) {
75
+ const hour = parseInt(match[1] || match[2], 10);
76
+ if (Number.isFinite(hour) && hour >= 0 && hour < 24) {
77
+ hourCounts[hour] = (hourCounts[hour] || 0) + 1;
78
+ }
82
79
  }
83
80
  });
84
81
 
@@ -18,6 +18,7 @@ const {
18
18
  runnerAvailabilityFailureMessage,
19
19
  } = require('../lib/runner-command');
20
20
  const { findStalePages, findStaleTasks, healBrokenMapRefs } = require('./clean');
21
+ const { mapNotesPath } = require('../lib/map-refs');
21
22
  const {
22
23
  buildScorecardData,
23
24
  readScorecards,
@@ -25,14 +26,15 @@ const {
25
26
  detectEndgameCompletion
26
27
  } = require('../lib/scorecard');
27
28
  const { REWARD_CONFIG, REWARD_CHECKSUM } = require('../lib/reward-config');
29
+ const { ownerIdentity } = require('../utils/owner-identity');
28
30
 
29
31
  const pkg = require('../package.json');
30
32
 
31
33
  const PHASE_TIMEOUT = 600000; // 10 min per phase
32
34
 
33
35
  function looksOwnerClaimed(claimed) {
34
- const text = String(claimed || '').toLowerCase();
35
- return /\bkeshav(?:rao)?\b/.test(text) || /\b(owner|human|operator)\b/.test(text);
36
+ const text = String(claimed || '').trim().toLowerCase();
37
+ return /\b(owner|human|operator)\b/.test(text) || ownerIdentity().names.includes(text.split(/\s+/)[0]);
36
38
  }
37
39
 
38
40
  function looksOwnerGatedTitle(title) {
@@ -482,19 +484,73 @@ function execPhaseCommandSync(cmd, opts = {}) {
482
484
  }
483
485
  }
484
486
 
487
+ // plan, do, and review run as the navigator, executor, and validator. When
488
+ // the roster names an engine for that member, the phase spawns it. A runner
489
+ // set in the environment still wins, and with no roster line behind the
490
+ // member the configured runner runs exactly as before.
491
+ const AUTOPILOT_PHASE_MEMBERS = Object.freeze({ plan: 'navigator', do: 'executor', review: 'validator' });
492
+ const RUNNER_ENV_OVERRIDES = Object.freeze([
493
+ 'ATRIS_RUNNER_PROFILE',
494
+ 'ATRIS_RUNNER_COMMAND_TEMPLATE',
495
+ 'ATRIS_RUNNER_BIN',
496
+ 'ATRIS_CLAUDE_COMMAND_TEMPLATE',
497
+ 'ATRIS_CLAUDE_BIN',
498
+ ]);
499
+
500
+ function autopilotPhaseEngine(phase, cwd = process.cwd()) {
501
+ const member = AUTOPILOT_PHASE_MEMBERS[phase];
502
+ if (!member) return null;
503
+ if (RUNNER_ENV_OVERRIDES.some((name) => String(process.env[name] || '').trim())) return null;
504
+ const { memberRosterEngine } = require('../lib/member-engine');
505
+ return memberRosterEngine(member, cwd, { requireMember: false });
506
+ }
507
+
508
+ // The phase's launch command and, when its roster line sets "max 20 min",
509
+ // the time cap in ms (null means the caller's own timeout stands).
510
+ function buildPhaseRunner(phase, promptFile, cwd = process.cwd(), allowedTools = 'Bash,Read,Write,Edit,Glob,Grep') {
511
+ const picked = autopilotPhaseEngine(phase, cwd);
512
+ if (!picked) return { command: buildRunnerCommand({ promptFile, allowedTools }), timeoutMs: null };
513
+ if (process.env.ATRIS_ROUTER_EXPLAIN !== '0') console.error(picked.reason);
514
+ const previous = process.env.ATRIS_RUNNER_PROFILE;
515
+ process.env.ATRIS_RUNNER_PROFILE = picked.engine.id;
516
+ try {
517
+ const command = buildRunnerCommand({
518
+ promptFile,
519
+ allowedTools,
520
+ ...(picked.model ? { model: picked.model } : {}),
521
+ ...(picked.engine.roster_effort ? { effort: picked.engine.roster_effort } : {}),
522
+ });
523
+ const maxSeconds = Number(picked.engine.roster_max_seconds) || 0;
524
+ return { command, timeoutMs: maxSeconds > 0 ? maxSeconds * 1000 : null };
525
+ } finally {
526
+ if (previous === undefined) delete process.env.ATRIS_RUNNER_PROFILE;
527
+ else process.env.ATRIS_RUNNER_PROFILE = previous;
528
+ }
529
+ }
530
+
531
+ function buildPhaseRunnerCommand(phase, promptFile, cwd = process.cwd()) {
532
+ return buildPhaseRunner(phase, promptFile, cwd).command;
533
+ }
534
+
485
535
  /**
486
536
  * Run a phase via the configured runner subprocess.
487
537
  */
488
538
  function executePhaseDetailed(phase, context, options = {}) {
489
- const { verbose = false, timeout = PHASE_TIMEOUT } = options;
539
+ const { verbose = false } = options;
540
+ let timeout = options.timeout || PHASE_TIMEOUT;
490
541
 
491
542
  const prompt = buildPrompt(phase, context, options);
492
543
  const tmpFile = path.join(process.cwd(), '.autopilot-prompt.tmp');
493
544
  fs.writeFileSync(tmpFile, prompt);
494
545
 
495
546
  try {
496
- const cmd = options.cmdOverride
497
- || buildRunnerCommand({ promptFile: tmpFile, allowedTools: 'Bash,Read,Write,Edit,Glob,Grep' });
547
+ let cmd = options.cmdOverride;
548
+ if (!cmd) {
549
+ const runner = buildPhaseRunner(phase, tmpFile);
550
+ cmd = runner.command;
551
+ // A roster time cap stops the phase at that time.
552
+ if (runner.timeoutMs) timeout = runner.timeoutMs;
553
+ }
498
554
  const env = { ...process.env };
499
555
  delete env.CLAUDECODE;
500
556
  const output = execPhaseCommandSync(cmd, {
@@ -624,6 +680,8 @@ function getContextFiles(phase, options = {}) {
624
680
  agentSpec && fs.existsSync(path.join(cwd, agentSpec)) ? agentSpec : null,
625
681
  'atris/PERSONA.md',
626
682
  'atris/MAP.md',
683
+ // Exact file:line refs live beside the short routing map.
684
+ mapNotesPath(cwd),
627
685
  'atris/TODO.md',
628
686
  fs.existsSync(path.join(cwd, 'atris/lessons.md')) ? 'atris/lessons.md' : null,
629
687
  (() => { const { logFile } = getLogPath(); return fs.existsSync(logFile) ? path.relative(cwd, logFile) : null; })(),
@@ -640,7 +698,7 @@ function getContextFiles(phase, options = {}) {
640
698
  // checkout is forbidden (COORDINATION.md Rule 4). Interpolated into the
641
699
  // default and self-heal do prompts, never the benchmark prompt (it never
642
700
  // 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.
701
+ const SHARED_CHECKOUT_GIT_CONTRACT = `- Shared-checkout git safety (COORDINATION.md Rule 4): edits to any repo OTHER than this tick's cwd (including 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
702
  - On a shared checkout, \`git reset\`, \`git checkout --\`, \`git clean\`, and stashing other agents' work are FORBIDDEN, concurrent ticks' uncommitted work lives there.`;
645
703
 
646
704
  /**
@@ -1356,15 +1414,25 @@ function parseProposedBlock(lines) {
1356
1414
  return Object.keys(proposed).length ? proposed : null;
1357
1415
  }
1358
1416
 
1417
+ // Plan review is review work, so it runs on the review pick like the review
1418
+ // phase, with that line's model, effort, and time cap. With no roster line
1419
+ // behind it, the configured runner runs exactly as before.
1420
+ function buildPlanReviewRunner(promptFile, cwd = process.cwd(), timeout = 180000) {
1421
+ const runner = buildPhaseRunner('review', promptFile, cwd, 'Bash,Read,Grep,Glob');
1422
+ return { command: runner.command, timeoutMs: runner.timeoutMs || timeout };
1423
+ }
1424
+
1359
1425
  /**
1360
- * Default executor for plan-review: spawn a fresh configured runner call.
1426
+ * Default executor for plan-review: spawn a fresh runner call on the review pick.
1361
1427
  * Kept thin so tests can inject a stub via options.planReviewExec.
1362
1428
  */
1363
1429
  function defaultPlanReviewExecutor(prompt, { cwd, timeout = 180000 } = {}) {
1364
1430
  const tmpFile = path.join(cwd, '.autopilot-plan-review.tmp');
1365
1431
  fs.writeFileSync(tmpFile, prompt);
1366
1432
  try {
1367
- const cmd = buildRunnerCommand({ promptFile: tmpFile, allowedTools: 'Bash,Read,Grep,Glob' });
1433
+ const runner = buildPlanReviewRunner(tmpFile, cwd, timeout);
1434
+ const cmd = runner.command;
1435
+ timeout = runner.timeoutMs;
1368
1436
  const env = { ...process.env };
1369
1437
  delete env.CLAUDECODE;
1370
1438
  const output = execPhaseCommandSync(cmd, {
@@ -3871,6 +3939,9 @@ module.exports = {
3871
3939
  isPhaseKillError,
3872
3940
  execPhaseCommandSync,
3873
3941
  executePhaseDetailed,
3942
+ buildPhaseRunnerCommand,
3943
+ buildPhaseRunner,
3944
+ buildPlanReviewRunner,
3874
3945
  buildAutopilotGlassLog,
3875
3946
  writeAutopilotGlassLog,
3876
3947
  lessonSlug
package/commands/avail.js CHANGED
@@ -308,7 +308,7 @@ async function slotsAvail(argv) {
308
308
  if (!username) {
309
309
  const identity = await apiRequestJson('/identity/me', { method: 'GET', token });
310
310
  if (!identity.ok || !identity.data?.username) {
311
- console.error('Could not resolve username, pass --user keshav');
311
+ console.error('could not resolve username; pass --user <username>.');
312
312
  return 1;
313
313
  }
314
314
  username = identity.data.username;
package/commands/brain.js CHANGED
@@ -3,6 +3,7 @@ const path = require('path');
3
3
  const crypto = require('crypto');
4
4
  const { spawnSync } = require('child_process');
5
5
  const { refreshNowFile } = require('./now');
6
+ const { ownerIdentity } = require('../utils/owner-identity');
6
7
  const escapeRegExp = require('../lib/escape-regexp');
7
8
  const { hasRenderedSections, isOpenSection, isDoneSection } = require('../lib/todo-sections');
8
9
  const { isNonInteractive } = require('../lib/noninteractive');
@@ -794,7 +795,8 @@ function memberNextMove(member, state = null) {
794
795
  if (member.slug === 'justin' || /justin/i.test(member.name || '')) {
795
796
  return `${name}: run one customer-moving GTM rep, update the relevant workspace state within 10 minutes, and leave a scorecard.`;
796
797
  }
797
- if (member.slug === 'keshav' || /keshav/i.test(member.name || '')) {
798
+ const ownerNames = ownerIdentity().names;
799
+ if (ownerNames.includes(String(member.slug || '').toLowerCase()) || ownerNames.includes(String(member.name || '').toLowerCase())) {
798
800
  return `${name}: act as Customer 0: choose one allocation, narrative, system, or talent leverage move; leave proof and route the next owner.`;
799
801
  }
800
802
  if (/opus|overnight/i.test(identity)) {