atris 3.74.16 → 3.74.17

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.
package/atris/atris.md CHANGED
@@ -58,7 +58,7 @@ You can move fast. You do not get to move blindly.
58
58
  We have done a lot of great work, and because of it we will do even more.
59
59
 
60
60
  - **Acceleration.** Progress compounds. The early days are slow, then the curve bends and everything speeds up. Build on what is already done and settled; never redo it or re-ask it. Each landed piece makes the next one faster.
61
- - **Accuracy.** A win counts when the result is validated. A test that failed before and passes after, a gate, or a real run proves it. Small fast tasks can be proven by a check alone; good and big work also gets a second reviewer. Accuracy is the floor the other three stand on, so better tests make everything faster.
61
+ - **Accuracy.** A win counts when the result is validated. A test that failed before and passes after, a gate, or a real run proves it. Not evidence: a test piped into `tail` or `grep` so its exit code is lost, a mock standing in for the real runtime, "should work", grading your own work, a test count with no repro that failed first, a summary of a file you did not open. Evidence: the real thing run end to end, a repro that failed and now passes, before and after numbers, the transcript or screenshot of the real run. Say done only next to that proof. Small fast tasks can be proven by a check alone; good and big work also gets a second reviewer. Accuracy is the floor the other three stand on, so better tests make everything faster.
62
62
  - **Ambition.** Always one more level. When the goal has a clear check, go for the bigger version or the great task. We are capable of it, and we always find ways to win.
63
63
  - **Agency.** Take action and own the outcome. Use every tool you have. When something is missing or broken, fix it the same turn instead of handing it back.
64
64
 
package/atris.md CHANGED
@@ -58,7 +58,7 @@ You can move fast. You do not get to move blindly.
58
58
  We have done a lot of great work, and because of it we will do even more.
59
59
 
60
60
  - **Acceleration.** Progress compounds. The early days are slow, then the curve bends and everything speeds up. Build on what is already done and settled; never redo it or re-ask it. Each landed piece makes the next one faster.
61
- - **Accuracy.** A win counts when the result is validated. A test that failed before and passes after, a gate, or a real run proves it. Small fast tasks can be proven by a check alone; good and big work also gets a second reviewer. Accuracy is the floor the other three stand on, so better tests make everything faster.
61
+ - **Accuracy.** A win counts when the result is validated. A test that failed before and passes after, a gate, or a real run proves it. Not evidence: a test piped into `tail` or `grep` so its exit code is lost, a mock standing in for the real runtime, "should work", grading your own work, a test count with no repro that failed first, a summary of a file you did not open. Evidence: the real thing run end to end, a repro that failed and now passes, before and after numbers, the transcript or screenshot of the real run. Say done only next to that proof. Small fast tasks can be proven by a check alone; good and big work also gets a second reviewer. Accuracy is the floor the other three stand on, so better tests make everything faster.
62
62
  - **Ambition.** Always one more level. When the goal has a clear check, go for the bigger version or the great task. We are capable of it, and we always find ways to win.
63
63
  - **Agency.** Take action and own the outcome. Use every tool you have. When something is missing or broken, fix it the same turn instead of handing it back.
64
64
 
@@ -6971,10 +6971,21 @@ function tickTouchedCheckDefinition(verifier, before, root = process.cwd()) {
6971
6971
  // Runners that find tests by name (node --test, jest, pytest, go test) never
6972
6972
  // list them, so any changed file shaped like a test counts as well.
6973
6973
  const testShaped = /(^|\/)(tests?|__tests__|specs?|checks?)\/|\.(test|spec)\.[cm]?[jt]sx?$|_test\.(go|py)$|(^|\/)test_[^/]*\.py$|(^|\/)conftest\.py$/i;
6974
+ // Whole paths only: order.js is not named by `node --test checks/order.js`.
6975
+ // Shell punctuation ends a word, and $PWD or this folder's own path is cut off.
6976
+ const here = [path.resolve(root), (() => { try { return fs.realpathSync(root); } catch { return ''; } })()].filter(Boolean);
6977
+ const named = checkText.split(/[\s'"`=;&|<>()]+/)
6978
+ .map((word) => {
6979
+ let clean = word.replace(/^\$\{?PWD\}?\//, '');
6980
+ const inside = here.find((dir) => clean.startsWith(`${dir}/`));
6981
+ if (inside) clean = clean.slice(inside.length + 1);
6982
+ return clean.replace(/^(\.\/)+/, '');
6983
+ })
6984
+ .filter(Boolean);
6974
6985
  return changed.some((file) => path.basename(file) === 'package.json'
6975
6986
  || testShaped.test(file)
6976
- || checkText.includes(file)
6977
- || (path.dirname(file) !== '.' && checkText.includes(`${path.dirname(file)}/`)));
6987
+ || named.includes(file)
6988
+ || (path.dirname(file) !== '.' && named.some((word) => word.startsWith(`${path.dirname(file)}/`))));
6978
6989
  }
6979
6990
 
6980
6991
  // The worker's `learned:` line from its receipt, so the lesson reaches the
@@ -10056,31 +10067,87 @@ function pickCrossEngineVerifier(mission, cwd = process.cwd(), preferredEngine =
10056
10067
  return workerEngine === 'codex' ? 'claude' : 'codex';
10057
10068
  }
10058
10069
 
10070
+ // Engine health lives in the main checkout, so a reviewer benched from one
10071
+ // member's side folder stays benched for the next member's review.
10072
+ function engineHealthRoot(cwd) {
10073
+ const common = spawnSync('git', ['rev-parse', '--path-format=absolute', '--git-common-dir'], { cwd, encoding: 'utf8' });
10074
+ const dir = common.status === 0 ? String(common.stdout || '').trim() : '';
10075
+ return dir && path.basename(dir) === '.git' ? path.dirname(dir) : cwd;
10076
+ }
10077
+
10078
+ // Every spelling and version of one model family counts as the same model,
10079
+ // with or without the long-context tag. Unknown counts as same.
10080
+ function reviewerMayBeSameModel(a, b) {
10081
+ const { CLAUDE_MODEL_ALIASES, CLAUDE_LONG_CONTEXT } = require('../lib/model-names');
10082
+ const key = (model) => {
10083
+ const name = String(model || '').toLowerCase().replace(CLAUDE_LONG_CONTEXT, '').replace(/^claude-/, '').trim();
10084
+ return CLAUDE_MODEL_ALIASES.find((family) => name.split(/[^a-z]+/).includes(family)) || name;
10085
+ };
10086
+ const left = key(a);
10087
+ const right = key(b);
10088
+ return !left || !right || left.includes(right) || right.includes(left);
10089
+ }
10090
+
10091
+ // Who reviews, in order. The usual reviewer first; then the roster's next
10092
+ // review worker, when it is not the builder grading itself: on the builder's
10093
+ // engine it must be a different model. A benched reviewer is skipped while
10094
+ // its backup can run. A named reviewer is never swapped.
10095
+ function engineVerifierLineup(mission, cwd, preferredEngine = '', options = {}) {
10096
+ const primary = pickCrossEngineVerifier(mission, cwd, preferredEngine);
10097
+ const lineup = [{ engine: primary, model: '' }];
10098
+ if (preferredEngine || mission?.referee_engine) return lineup;
10099
+ const root = options.healthRoot || engineHealthRoot(cwd);
10100
+ try {
10101
+ const registry = require('../lib/engine-registry');
10102
+ const workerEngine = canonicalEngineName(mission?.runner) || 'claude';
10103
+ const workerModel = resolveMissionTickRunnerModel(mission);
10104
+ const team = registry.resolveEngineForRoleRanked('validator', root, { job: 'review', quiet: true }).team || [];
10105
+ const backup = team.find((engine) => engine && engine.id !== primary
10106
+ && !(engine.id === workerEngine && reviewerMayBeSameModel(engine.roster_model, workerModel)));
10107
+ if (!backup) return lineup;
10108
+ lineup.push({ engine: backup.id, model: backup.roster_model || '' });
10109
+ const saved = registry.readEngineRegistry(root, { persist: false }).engines.find((engine) => engine.id === primary);
10110
+ if (saved && !registry.engineReadyAt(saved)) lineup.shift();
10111
+ } catch {}
10112
+ return lineup;
10113
+ }
10114
+
10059
10115
  async function runEngineVerifier(mission, options = {}) {
10060
10116
  const cwd = options.cwd || process.cwd();
10061
- const engine = pickCrossEngineVerifier(mission, cwd, options.refereeEngine);
10062
- const verifyMission = { ...mission, runner: engine, runner_kind: 'engine', model: '' };
10063
- const restoreRunnerProfile = applyMissionRunnerProfile(engine);
10064
- let engineResult;
10065
- try {
10066
- engineResult = await spawnClaudeTick(verifyMission, {
10067
- sessionMode: 'set',
10068
- sessionId: crypto.randomUUID(),
10069
- cwd,
10070
- signal: options.signal,
10071
- timeoutMs: ENGINE_VERIFY_TIMEOUT_MS,
10072
- prompt: buildEngineVerifyPrompt(mission, options.tickIndex || 1, missionDoneLooksLike(mission, cwd), {
10117
+ const healthRoot = engineHealthRoot(cwd);
10118
+ const lineup = engineVerifierLineup(mission, cwd, options.refereeEngine, { healthRoot });
10119
+ let verdict = null;
10120
+ for (const { engine, model } of lineup) {
10121
+ const verifyMission = { ...mission, runner: engine, runner_kind: 'engine', model };
10122
+ const restoreRunnerProfile = applyMissionRunnerProfile(engine);
10123
+ let engineResult;
10124
+ try {
10125
+ engineResult = await spawnClaudeTick(verifyMission, {
10126
+ sessionMode: 'set',
10127
+ sessionId: crypto.randomUUID(),
10128
+ cwd,
10129
+ signal: options.signal,
10073
10130
  timeoutMs: ENGINE_VERIFY_TIMEOUT_MS,
10074
- checkPassed: options.checkPassed || '',
10075
- }),
10076
- model: resolveMissionTickRunnerModel(verifyMission),
10077
- });
10078
- } catch (error) {
10079
- engineResult = { ok: false, error: error.message };
10080
- } finally {
10081
- restoreRunnerProfile();
10131
+ prompt: buildEngineVerifyPrompt(mission, options.tickIndex || 1, missionDoneLooksLike(mission, cwd), {
10132
+ timeoutMs: ENGINE_VERIFY_TIMEOUT_MS,
10133
+ checkPassed: options.checkPassed || '',
10134
+ }),
10135
+ model: resolveMissionTickRunnerModel(verifyMission),
10136
+ });
10137
+ } catch (error) {
10138
+ engineResult = { ok: false, error: error.message };
10139
+ } finally {
10140
+ restoreRunnerProfile();
10141
+ }
10142
+ verdict = engineVerifierResultFromRun(engineResult, engine);
10143
+ if (verdict.mode !== 'engine-unavailable' || engineResult?.aborted) return verdict;
10144
+ // Member lab 10/26: codex hung on every review, and each job waited out
10145
+ // the full five minutes. Bench it so the next review starts on the backup.
10146
+ if (engineResult?.timedOut) {
10147
+ try { setEngineHealth(engine, 'cooling', healthRoot); } catch {}
10148
+ }
10082
10149
  }
10083
- return engineVerifierResultFromRun(engineResult, engine);
10150
+ return verdict;
10084
10151
  }
10085
10152
 
10086
10153
  function stripClaudeReceiptLine(line) {
@@ -12931,6 +12998,7 @@ function missionCommand(args) {
12931
12998
  module.exports = {
12932
12999
  receiptLearnedLine,
12933
13000
  tickTouchedCheckDefinition,
13001
+ engineVerifierLineup,
12934
13002
  claudeRunnerMcpArgs,
12935
13003
  crystallizeMemberSkillLesson,
12936
13004
  listSiblingWorkspaceMissionHints,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atris",
3
- "version": "3.74.16",
3
+ "version": "3.74.17",
4
4
  "description": "you say what you want in plain words. atris builds it, checks it, and shows you proof.",
5
5
  "mcpName": "io.github.atrislabs/atris",
6
6
  "main": "bin/atris.js",