atris 3.74.20 → 3.74.21
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/commands/task.js +20 -3
- package/lib/auto-accept-certified.js +10 -2
- package/lib/context-gatherer.js +2 -0
- package/lib/first-minute.js +11 -5
- package/lib/voice-gate.js +1 -0
- package/package.json +1 -1
package/commands/task.js
CHANGED
|
@@ -32,7 +32,7 @@ const {
|
|
|
32
32
|
runVerifyCommand,
|
|
33
33
|
DENIED_TAGS,
|
|
34
34
|
} = require('../lib/auto-accept-certified');
|
|
35
|
-
const { evaluateAcceptVerify } = require('../lib/accept-verify-gate');
|
|
35
|
+
const { evaluateAcceptVerify, nonFalsifyingIssue } = require('../lib/accept-verify-gate');
|
|
36
36
|
const { checkedCodeSha, runVerifyAtJobCode } = require('../lib/job-code-verify');
|
|
37
37
|
const { proveTaskLanding } = require('../lib/landing-proof');
|
|
38
38
|
const {
|
|
@@ -10089,7 +10089,7 @@ function certifyVerifyCandidate(task) {
|
|
|
10089
10089
|
// The evidence extractor can keep trailing prose ("git diff --check. Evidence
|
|
10090
10090
|
// inspected: ..."); try the full clause, then the first sentence of it.
|
|
10091
10091
|
for (const candidate of [raw, raw.split(/\.(?:\s|$)/)[0]].map((c) => String(c || '').trim())) {
|
|
10092
|
-
if (candidate && isAutoCertifyVerifyCommandAllowed(candidate) && !verifyIsOnlyFileChecks(candidate)) return candidate;
|
|
10092
|
+
if (candidate && !nonFalsifyingIssue(candidate) && isAutoCertifyVerifyCommandAllowed(candidate) && !verifyIsOnlyFileChecks(candidate)) return candidate;
|
|
10093
10093
|
}
|
|
10094
10094
|
}
|
|
10095
10095
|
return null;
|
|
@@ -10702,6 +10702,10 @@ function evaluateSweepAutoAccept(task, root) {
|
|
|
10702
10702
|
if (denied) return { eligible: false, ref, reason: denied };
|
|
10703
10703
|
const proof = autoAcceptSweepLatestProof(task);
|
|
10704
10704
|
if (!proof) return { eligible: false, ref, reason: 'no_proof' };
|
|
10705
|
+
const storedVerify = typeof metadata.verify === 'string' ? metadata.verify.trim() : '';
|
|
10706
|
+
if (storedVerify && nonFalsifyingIssue(storedVerify)) {
|
|
10707
|
+
return { eligible: false, ref, reason: 'non_falsifying_verify', verify: storedVerify };
|
|
10708
|
+
}
|
|
10705
10709
|
const policyGate = candidatePolicyGate(task, { executeDetectors: true });
|
|
10706
10710
|
if (!policyGate.ok) return { ...policyGate, ref };
|
|
10707
10711
|
|
|
@@ -10712,7 +10716,6 @@ function evaluateSweepAutoAccept(task, root) {
|
|
|
10712
10716
|
// executed) in the proof, but the older receipt-path check below could not
|
|
10713
10717
|
// find file evidence for it once `ready --verify` stopped writing a
|
|
10714
10718
|
// receipt file and started embedding the executed result into proof text.
|
|
10715
|
-
const storedVerify = typeof metadata.verify === 'string' ? metadata.verify.trim() : '';
|
|
10716
10719
|
if (storedVerify) {
|
|
10717
10720
|
const result = runVerifyCommand(storedVerify, root);
|
|
10718
10721
|
if (!result.ok) return { eligible: false, ref, reason: result.reason, verify: storedVerify };
|
|
@@ -10770,6 +10773,12 @@ function evaluateSweepAutoAccept(task, root) {
|
|
|
10770
10773
|
}
|
|
10771
10774
|
return { eligible: false, ref, reason: result.reason, verify: derived };
|
|
10772
10775
|
}
|
|
10776
|
+
// Certification filters these out, but the sweep should explain why a
|
|
10777
|
+
// legacy proof with only whitespace evidence cannot approve the work.
|
|
10778
|
+
const proofCommands = taskReviewEvidenceCommands(proof);
|
|
10779
|
+
if (proofCommands.length && proofCommands.every(command => nonFalsifyingIssue(command))) {
|
|
10780
|
+
return { eligible: false, ref, reason: 'non_falsifying_verify' };
|
|
10781
|
+
}
|
|
10773
10782
|
}
|
|
10774
10783
|
|
|
10775
10784
|
return { eligible: false, ref, reason: verifier.reason, evidence: verifier.evidence };
|
|
@@ -10783,6 +10792,14 @@ function cmdSweep(args) {
|
|
|
10783
10792
|
'atris task sweep currently requires --auto-accept for the explicit verified accept policy',
|
|
10784
10793
|
);
|
|
10785
10794
|
}
|
|
10795
|
+
// Sweep has no dry-run implementation: every successful invocation can
|
|
10796
|
+
// write accepts, even when an unrecognized --dry-run flag is supplied.
|
|
10797
|
+
if (agentProofOnlyMode()) {
|
|
10798
|
+
failAgentProofOnly(
|
|
10799
|
+
'atris task sweep',
|
|
10800
|
+
'agents cannot accept tasks or award xp. leave proof in review for a human to accept or revise.',
|
|
10801
|
+
);
|
|
10802
|
+
}
|
|
10786
10803
|
const actor = String(flag(args, '--as') || 'orb-autoaccept');
|
|
10787
10804
|
const parsedReward = parseAcceptReward(flag(args, '--reward'));
|
|
10788
10805
|
if (!parsedReward.ok) {
|
|
@@ -10,6 +10,7 @@ const { computeTrustTier, taskTrustSubject } = require('./trust-tiers');
|
|
|
10
10
|
const { rewriteWorktreeVerify, missingOnMain } = require('./verify-worktree-rewrite');
|
|
11
11
|
const { policyLessonsForFiles, readPolicyLessons } = require('./policy-lessons');
|
|
12
12
|
const { skillEvalGate } = require('./skill-eval-gate');
|
|
13
|
+
const escapeRegExp = require('./escape-regexp');
|
|
13
14
|
|
|
14
15
|
const AGENT_CERTIFICATION_REVIEW_PASSES = 2;
|
|
15
16
|
|
|
@@ -1074,9 +1075,10 @@ function declaredProtectedLane(task) {
|
|
|
1074
1075
|
// work and would hold the whole queue.
|
|
1075
1076
|
const PROTECTED_LANE_TERMS = [
|
|
1076
1077
|
['billing', ['stripe', 'checkout url', 'checkout_url', 'invoice', 'paywall', 'subscription', 'wallet', 'credit card', 'pricing tier']],
|
|
1078
|
+
['money', ['money', 'refund', 'reimburs', 'expense', 'payment', 'cash', 'bank', 'venmo', 'zelle', 'tax']],
|
|
1077
1079
|
['security', ['session minting', 'mints a', 'service token', 'api key', 'credential', 'secret key', 'csrf', 'access token']],
|
|
1078
1080
|
['deploy', ['render deploy', 'production deploy', 'deploy to prod', 'migration apply']],
|
|
1079
|
-
['customer', ['send email to', 'customer email', 'outbound email', 'notify users']],
|
|
1081
|
+
['customer', ['send email to', 'customer email', 'outbound email', 'notify users', 'imessage', 'text message', 'send a text', 'reply to', 'outreach', 'cold email', 'send to', 'dm ']],
|
|
1080
1082
|
];
|
|
1081
1083
|
|
|
1082
1084
|
function sniffedProtectedLane(task) {
|
|
@@ -1086,7 +1088,7 @@ function sniffedProtectedLane(task) {
|
|
|
1086
1088
|
.toLowerCase();
|
|
1087
1089
|
if (!haystack.trim()) return null;
|
|
1088
1090
|
for (const [lane, terms] of PROTECTED_LANE_TERMS) {
|
|
1089
|
-
const term = terms.find((t) =>
|
|
1091
|
+
const term = terms.find((t) => new RegExp(`\\b${escapeRegExp(t)}`).test(haystack));
|
|
1090
1092
|
if (term) return { lane, term };
|
|
1091
1093
|
}
|
|
1092
1094
|
return null;
|
|
@@ -1216,6 +1218,12 @@ function evaluateAutoAccept(task, options = {}) {
|
|
|
1216
1218
|
};
|
|
1217
1219
|
}
|
|
1218
1220
|
|
|
1221
|
+
// Load lazily: the human accept gate reuses this module's parser and runner.
|
|
1222
|
+
const verifyCommand = resolveTaskVerify(task).verify;
|
|
1223
|
+
if (verifyCommand && require('./accept-verify-gate').nonFalsifyingIssue(verifyCommand)) {
|
|
1224
|
+
return { eligible: false, ref, reason: 'non_falsifying_verify', verify: verifyCommand };
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1219
1227
|
// accept-all: the protected lanes above are the only human gate. No
|
|
1220
1228
|
// certification, pass-count, reviewer, or proof-quality bar, but work
|
|
1221
1229
|
// is never marked done against evidence it isn't: a proof naming an
|
package/lib/context-gatherer.js
CHANGED
|
@@ -155,6 +155,8 @@ function createStarterTask(root, answer) {
|
|
|
155
155
|
title,
|
|
156
156
|
status: task && task.status || 'claimed',
|
|
157
157
|
claimed_by: (task && task.claimed_by) || who,
|
|
158
|
+
workspace_root: workspaceRoot,
|
|
159
|
+
metadata: task && task.metadata,
|
|
158
160
|
};
|
|
159
161
|
} catch (error) {
|
|
160
162
|
return {
|
package/lib/first-minute.js
CHANGED
|
@@ -6,10 +6,12 @@ const path = require('path');
|
|
|
6
6
|
const { stateHome } = require('./state-home');
|
|
7
7
|
const { spawnSync } = require('child_process');
|
|
8
8
|
const { isGenericScratchRoot } = require('./scratch-root');
|
|
9
|
+
const { resolveDefaultVerifier } = require('./default-verifier');
|
|
10
|
+
const { nonFalsifyingIssue } = require('./accept-verify-gate');
|
|
9
11
|
|
|
10
12
|
const FIRST_USE_COMMAND = 'atris "help me choose the first useful step for this project"';
|
|
11
13
|
const FIRST_TALK_ASK = 'what do you want here';
|
|
12
|
-
const
|
|
14
|
+
const ADD_TEST_COMMAND = 'atris "add a test for the claimed task"';
|
|
13
15
|
const LATER_NOTE_FILE = '.atris-later';
|
|
14
16
|
const BARE_ATRIS_FLAGS = new Set(['--yes', '-y', '--json', '--verbose']);
|
|
15
17
|
const NOT_A_PERSON = new Set([
|
|
@@ -237,13 +239,17 @@ function loadLatestRecap(root = process.cwd()) {
|
|
|
237
239
|
|
|
238
240
|
function claimedKeepWorkingCommand(task) {
|
|
239
241
|
const ref = taskRef(task);
|
|
242
|
+
const recorded = task && task.metadata && task.metadata.verify;
|
|
243
|
+
const available = recorded || (task && task.workspace_root && resolveDefaultVerifier(task.workspace_root));
|
|
244
|
+
if (!available || nonFalsifyingIssue(available)) return ADD_TEST_COMMAND;
|
|
240
245
|
return ref
|
|
241
|
-
? `atris task ready ${ref} --verify "${
|
|
242
|
-
: `atris task ready --verify "${
|
|
246
|
+
? `atris task ready ${ref} --verify "${available}"`
|
|
247
|
+
: `atris task ready --verify "${available}"`;
|
|
243
248
|
}
|
|
244
249
|
|
|
245
250
|
function isClaimedKeepWorkingCommand(command) {
|
|
246
|
-
return /^atris task ready\b/.test(String(command || ''))
|
|
251
|
+
return /^atris task ready\b/.test(String(command || ''))
|
|
252
|
+
|| command === ADD_TEST_COMMAND;
|
|
247
253
|
}
|
|
248
254
|
|
|
249
255
|
function taskCommand(task, person) {
|
|
@@ -709,7 +715,7 @@ function firstTalkNext({ starter = null, person = '', folder = 'this folder' } =
|
|
|
709
715
|
const room = folder || 'this folder';
|
|
710
716
|
if (starter && starter.display_id) {
|
|
711
717
|
return taskCommand({
|
|
712
|
-
|
|
718
|
+
...starter,
|
|
713
719
|
status: starter.status || 'claimed',
|
|
714
720
|
}, person);
|
|
715
721
|
}
|
package/lib/voice-gate.js
CHANGED
|
@@ -250,6 +250,7 @@ const LANDING_REASON_SENTENCES = {
|
|
|
250
250
|
receipt_verifier_failed: 'a saved receipt failed its re-check, fix what it names and re-certify',
|
|
251
251
|
slop_gate: 'the touched prose breaks a deterministic taste rule, fix the named lines and re-certify',
|
|
252
252
|
strict_verify_missing: 'no recorded check command to re-run, add one and re-certify',
|
|
253
|
+
non_falsifying_verify: 'its recorded check cannot prove the work is correct, add a real check and re-certify',
|
|
253
254
|
untagged_protected_lane_text: 'the description reads like protected-lane work without the tag, tag it or decide it yourself',
|
|
254
255
|
verification_pending: 'the recorded check has not been re-run yet, the next hourly pass runs it',
|
|
255
256
|
verifier_is_builder: 'the re-check actor built this row, another actor must re-check',
|
package/package.json
CHANGED