atris 3.57.3 → 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.
- package/README.md +12 -1
- package/atris/skills/clawhub/member-runtime/SKILL.md +9 -9
- package/atris/skills/create-member/SKILL.md +16 -13
- package/atris/skills/design/SKILL.md +27 -4
- package/atris/skills/engines/SKILL.md +57 -13
- package/atris/skills/x-search/SKILL.md +13 -5
- package/atris/skills/youtube/SKILL.md +39 -8
- package/atris/team/brainstormer/MEMBER.md +2 -2
- package/atris/team/executor/MEMBER.md +3 -3
- package/atris/team/launcher/MEMBER.md +4 -4
- package/atris/team/navigator/MEMBER.md +3 -3
- package/atris/team/researcher/MEMBER.md +2 -2
- package/atris/team/validator/MEMBER.md +1 -1
- package/atris.md +14 -0
- package/ax +157 -24
- package/bin/atris.js +79 -31
- package/commands/aeo.js +4 -4
- package/commands/agents.js +1 -1
- package/commands/align.js +12 -12
- package/commands/analytics.js +3 -3
- package/commands/app.js +5 -5
- package/commands/ask.js +175 -0
- package/commands/auth.js +7 -7
- package/commands/autoland.js +29 -5
- package/commands/autopilot-front.js +6 -6
- package/commands/autopilot.js +90 -90
- package/commands/avail.js +5 -5
- package/commands/bench.js +63 -1
- package/commands/browse.js +2 -2
- package/commands/business.js +12 -12
- package/commands/card.js +2 -2
- package/commands/clean.js +13 -13
- package/commands/close.js +297 -15
- package/commands/compile.js +13 -13
- package/commands/computer.js +94 -27
- package/commands/console.js +5 -5
- package/commands/decide.js +1 -1
- package/commands/deck.js +9 -9
- package/commands/drive.js +4 -4
- package/commands/engine.js +173 -19
- package/commands/errors.js +3 -3
- package/commands/experiments.js +18 -1
- package/commands/feed.js +2 -2
- package/commands/feedback.js +4 -4
- package/commands/fleet.js +12 -12
- package/commands/game.js +1 -1
- package/commands/guide.js +49 -0
- package/commands/improve.js +19 -17
- package/commands/init.js +67 -19
- package/commands/interview.js +7 -7
- package/commands/land.js +40 -15
- package/commands/learn.js +12 -12
- package/commands/lesson.js +2 -2
- package/commands/log.js +1 -1
- package/commands/loops.js +4 -4
- package/commands/member.js +75 -50
- package/commands/mission.js +97 -98
- package/commands/one-lap.js +1 -1
- package/commands/pack.js +74 -8
- package/commands/plugin.js +6 -6
- package/commands/probe.js +9 -9
- package/commands/pull.js +35 -35
- package/commands/pulse.js +1 -1
- package/commands/push.js +21 -21
- package/commands/rainmaker.js +1 -1
- package/commands/recap.js +7 -7
- package/commands/reel.js +2 -2
- package/commands/release.js +3 -3
- package/commands/report.js +1 -1
- package/commands/review.js +5 -5
- package/commands/revisions.js +1 -1
- package/commands/run.js +1 -1
- package/commands/scout.js +3 -3
- package/commands/security-review.js +7 -7
- package/commands/serve.js +9 -9
- package/commands/setup.js +1 -1
- package/commands/sign.js +4 -4
- package/commands/signup.js +4 -4
- package/commands/site.js +8 -3
- package/commands/skill.js +6 -6
- package/commands/slop.js +18 -18
- package/commands/social.js +3 -3
- package/commands/soul.js +14 -14
- package/commands/spaceship.js +1 -1
- package/commands/status.js +2 -2
- package/commands/strings.js +13 -13
- package/commands/study.js +4 -4
- package/commands/sync.js +8 -8
- package/commands/task.js +50 -25
- package/commands/teach.js +2 -2
- package/commands/team.js +1 -1
- package/commands/theme.js +2 -2
- package/commands/tree.js +34 -0
- package/commands/truth.js +7 -7
- package/commands/verify.js +12 -12
- package/commands/visualize.js +2 -2
- package/commands/workflow.js +97 -40
- package/commands/worktree.js +57 -1
- package/commands/write.js +9 -9
- package/commands/x-search.js +304 -15
- package/commands/xp.js +6 -3
- package/commands/youtube.js +916 -65
- package/lib/accept-verify-gate.js +3 -3
- package/lib/activity-stream.js +1 -1
- package/lib/apply-gate.js +13 -0
- package/lib/auto-accept-certified.js +28 -12
- package/lib/autoland.js +6 -6
- package/lib/bench/engines.js +59 -8
- package/lib/bench/report.js +1 -1
- package/lib/bench/runner.js +204 -5
- package/lib/bench/tree-render.js +76 -0
- package/lib/card.js +1 -1
- package/lib/chat-log-scan.js +1 -1
- package/lib/claude-boot-block.js +2 -0
- package/lib/deck-compose.js +1 -1
- package/lib/deck-history.js +2 -2
- package/lib/deck-layout.js +1 -1
- package/lib/deck-review.js +5 -5
- package/lib/deck-schema.js +1 -1
- package/lib/default-verifier.js +1 -1
- package/lib/developer-api.js +3 -0
- package/lib/engine-ask.js +66 -9
- package/lib/engine-registry.js +15 -2
- package/lib/file-ops.js +1 -1
- package/lib/fleet.js +71 -35
- package/lib/html-render.js +1 -1
- package/lib/intents.js +172 -0
- package/lib/journal.js +1 -1
- package/lib/known-commands.js +2 -2
- package/lib/land-green.js +364 -0
- package/lib/lesson-contradiction.js +2 -2
- package/lib/lesson-preflight.js +1 -1
- package/lib/manifest.js +5 -5
- package/lib/member-alive.js +9 -5
- package/lib/memory-view.js +3 -3
- package/lib/mission-protected-lane.js +1 -1
- package/lib/mission-root.js +2 -2
- package/lib/orb-context.js +10 -10
- package/lib/pack-capabilities.js +861 -3
- package/lib/permission-grants.js +2 -2
- package/lib/policy-lessons.js +6 -6
- package/lib/pulse.js +8 -8
- package/lib/receipt-evidence.js +1 -1
- package/lib/reel.js +1 -1
- package/lib/review-integrity.js +1 -1
- package/lib/runner-command.js +35 -5
- package/lib/scorecard.js +4 -4
- package/lib/security-scan.js +5 -5
- package/lib/self-drive.js +1 -1
- package/lib/site-publish.js +372 -0
- package/lib/site.js +1 -1
- package/lib/slides-deck.js +2 -2
- package/lib/state-detection.js +2 -2
- package/lib/sync-telemetry.js +2 -2
- package/lib/task-db.js +17 -10
- package/lib/task-explanation.js +1 -1
- package/lib/task-proof.js +2 -2
- package/lib/task-receipt.js +3 -3
- package/lib/theme.js +12 -12
- package/lib/todo-fallback.js +3 -3
- package/lib/todo-sections.js +1 -1
- package/lib/tree-hash.js +79 -0
- package/lib/usage.js +1 -1
- package/lib/wish-delegate.js +1 -1
- package/lib/workspace-safety.js +4 -4
- package/lib/workspace-scaffold.js +3 -3
- package/package.json +1 -1
- package/scripts/det/concierge-bakeoff.sh +26 -0
- package/scripts/det/ytquote-repair.js +0 -1
- package/scripts/det/ytrail-race.js +0 -5
- package/scripts/member-operate.mjs +52 -14
- package/scripts/outbound-artifact-gate.js +0 -3
- package/utils/auth.js +0 -3
- package/utils/config.js +0 -1
- package/scripts/det/checklist-score.js +0 -191
package/commands/close.js
CHANGED
|
@@ -23,6 +23,7 @@ const FAILED_TASK_BATCH_PREFIX = 'tasks:failed:';
|
|
|
23
23
|
const PROBE_TIMEOUT_MS = 60 * 1000;
|
|
24
24
|
const PROBE_HOLD_GAP_MS = 60 * 60 * 1000;
|
|
25
25
|
const PROBE_STOP_AFTER_FAILED_CHECKS = 2;
|
|
26
|
+
const MAX_OPEN_FLAGS = 10;
|
|
26
27
|
|
|
27
28
|
function ledgerPath(cwd = process.cwd()) {
|
|
28
29
|
return path.join(cwd, LEDGER_RELATIVE_PATH);
|
|
@@ -144,6 +145,15 @@ function foldEvents(events, options = {}) {
|
|
|
144
145
|
flag.why = event.why || '';
|
|
145
146
|
} else if (event.kind === 'snoozed') {
|
|
146
147
|
flag.snoozed_until = event.until || null;
|
|
148
|
+
} else if (event.kind === 'parked') {
|
|
149
|
+
flag.status = 'parked';
|
|
150
|
+
flag.parked_at = event.parked_at || eventTime(event);
|
|
151
|
+
flag.why = normalizeSpaces(event.why || 'parked by request');
|
|
152
|
+
} else if (event.kind === 'reopened') {
|
|
153
|
+
flag.status = 'open';
|
|
154
|
+
flag.reopened_at = eventTime(event);
|
|
155
|
+
delete flag.parked_at;
|
|
156
|
+
delete flag.why;
|
|
147
157
|
} else if (event.kind === 'escalated') {
|
|
148
158
|
const day = event.day || (event.at ? String(event.at).slice(0, 10) : null);
|
|
149
159
|
if (day && !flag.escalated_days.includes(day)) flag.escalated_days.push(day);
|
|
@@ -223,6 +233,12 @@ function openFlags(cwd = process.cwd(), options = {}) {
|
|
|
223
233
|
return foldEvents(readEvents(cwd), options).filter((flag) => flag.status === 'open');
|
|
224
234
|
}
|
|
225
235
|
|
|
236
|
+
function parkedFlags(cwd = process.cwd(), options = {}) {
|
|
237
|
+
return foldEvents(readEvents(cwd), options)
|
|
238
|
+
.filter((flag) => flag.status === 'parked')
|
|
239
|
+
.sort((a, b) => (parseDate(b.parked_at) || 0) - (parseDate(a.parked_at) || 0));
|
|
240
|
+
}
|
|
241
|
+
|
|
226
242
|
function ownerWaitRank(flag) {
|
|
227
243
|
return /^(you|me|operator|human|keshav)$/.test(String(flag.owner || '').toLowerCase()) ? 0 : 1;
|
|
228
244
|
}
|
|
@@ -264,6 +280,16 @@ function listLine(flag) {
|
|
|
264
280
|
return parts.join(', ');
|
|
265
281
|
}
|
|
266
282
|
|
|
283
|
+
function parkedLine(flag) {
|
|
284
|
+
const what = trimSentenceEnd(flag.what.toLowerCase());
|
|
285
|
+
const day = flag.parked_at ? String(flag.parked_at).slice(0, 10) : 'unknown date';
|
|
286
|
+
return `${what}, parked ${day}, ${trimSentenceEnd(flag.why || 'parked by request').toLowerCase()}`;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function migratedParkLine(flag) {
|
|
290
|
+
return `parked ${trimSentenceEnd(flag.what).toLowerCase()}: ${trimSentenceEnd(flag.why).toLowerCase()}.`;
|
|
291
|
+
}
|
|
292
|
+
|
|
267
293
|
function sweepLine(flag) {
|
|
268
294
|
const what = trimSentenceEnd(flag.what.toLowerCase());
|
|
269
295
|
const closeCondition = trimSentenceEnd(flag.close_condition.toLowerCase());
|
|
@@ -325,9 +351,12 @@ function publicFlag(flag) {
|
|
|
325
351
|
}
|
|
326
352
|
|
|
327
353
|
function printHelp() {
|
|
328
|
-
console.log('usage: atris close <add|list|done|dissolve|snooze|sweep|scan|check>');
|
|
329
|
-
console.log('add "<what>" [--owner x] [--lane life|business|code] [--ttl 7] [--when "<condition>"] [--source y] [--probe "<cmd that fails now>"]');
|
|
354
|
+
console.log('usage: atris close <add|list|park|parked|reopen|done|dissolve|snooze|sweep|scan|check>');
|
|
355
|
+
console.log('add "<what>" [--owner x] [--lane life|business|code] [--ttl 7] [--when "<condition>"] [--source y] [--probe "<cmd that fails now>"] [--replace <id>]');
|
|
330
356
|
console.log('list [--json] [--lane x]');
|
|
357
|
+
console.log('park <id> [--why "..."]');
|
|
358
|
+
console.log('parked [--json]');
|
|
359
|
+
console.log('reopen <id> [--replace <id>]');
|
|
331
360
|
console.log('done <id> [--proof "..."] (a probed incident closes only after its probe holds)');
|
|
332
361
|
console.log('check [id] [--json] (run live probes; failing probe = old theory still live)');
|
|
333
362
|
console.log('dissolve <id> --why "..."');
|
|
@@ -336,6 +365,58 @@ function printHelp() {
|
|
|
336
365
|
console.log('scan [--json]');
|
|
337
366
|
}
|
|
338
367
|
|
|
368
|
+
function appendParkedEvent(flag, cwd, now, why, options = {}) {
|
|
369
|
+
const at = now.toISOString();
|
|
370
|
+
const event = {
|
|
371
|
+
kind: 'parked',
|
|
372
|
+
at,
|
|
373
|
+
id: flag.id,
|
|
374
|
+
parked_at: at,
|
|
375
|
+
why: normalizeSpaces(why || 'parked by request'),
|
|
376
|
+
};
|
|
377
|
+
if (options.automatic) event.automatic = true;
|
|
378
|
+
appendEvent(event, cwd);
|
|
379
|
+
return {
|
|
380
|
+
id: flag.id,
|
|
381
|
+
what: flag.what,
|
|
382
|
+
parked_at: at,
|
|
383
|
+
why: event.why,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function slotCapError(flags) {
|
|
388
|
+
const fullest = flags
|
|
389
|
+
.slice()
|
|
390
|
+
.sort(compareOpenFlags)
|
|
391
|
+
.slice(0, 3)
|
|
392
|
+
.map((flag) => trimSentenceEnd(flag.what).toLowerCase())
|
|
393
|
+
.join(', ');
|
|
394
|
+
const names = fullest ? ` the fullest loops are ${fullest}.` : '';
|
|
395
|
+
return new Error(`ten open loops is the cap.${names} park one first or use --replace <id>`);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function makeRoomForOpenFlag(cwd, now, replaceId, incomingWhat) {
|
|
399
|
+
const flags = openFlags(cwd, { now });
|
|
400
|
+
const hasReplacement = replaceId !== undefined;
|
|
401
|
+
if (hasReplacement && (replaceId === true || !normalizeSpaces(replaceId))) {
|
|
402
|
+
throw new Error('--replace needs an id');
|
|
403
|
+
}
|
|
404
|
+
if (!hasReplacement) {
|
|
405
|
+
if (flags.length >= MAX_OPEN_FLAGS) throw slotCapError(flags);
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
if (flags.length > MAX_OPEN_FLAGS) throw slotCapError(flags);
|
|
409
|
+
|
|
410
|
+
const replacement = flags.find((flag) => flag.id === replaceId);
|
|
411
|
+
if (!replacement) throw new Error(`${replaceId} is not open`);
|
|
412
|
+
return appendParkedEvent(
|
|
413
|
+
replacement,
|
|
414
|
+
cwd,
|
|
415
|
+
now,
|
|
416
|
+
`replaced to make room for ${trimSentenceEnd(incomingWhat).toLowerCase()}`
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
339
420
|
function commandAdd(args, context = {}) {
|
|
340
421
|
const { positional, options } = parseArgs(args);
|
|
341
422
|
const what = normalizeSpaces(positional.join(' '));
|
|
@@ -366,6 +447,8 @@ function commandAdd(args, context = {}) {
|
|
|
366
447
|
probeExit = result.exit_code;
|
|
367
448
|
}
|
|
368
449
|
|
|
450
|
+
makeRoomForOpenFlag(context.cwd, now, options.replace, what);
|
|
451
|
+
|
|
369
452
|
const openedAt = now.toISOString();
|
|
370
453
|
const event = {
|
|
371
454
|
kind: 'opened',
|
|
@@ -389,7 +472,7 @@ function commandAdd(args, context = {}) {
|
|
|
389
472
|
return 0;
|
|
390
473
|
}
|
|
391
474
|
|
|
392
|
-
// atris close check [id]
|
|
475
|
+
// atris close check [id]: run the live probes on open incidents. This is the
|
|
393
476
|
// first move for any session entering an area: if a probe still fails, the
|
|
394
477
|
// recorded theory is still live; do not diagnose fresh. After
|
|
395
478
|
// PROBE_STOP_AFTER_FAILED_CHECKS failing re-checks the verdict is automatic:
|
|
@@ -461,21 +544,88 @@ function commandCheck(args, context = {}) {
|
|
|
461
544
|
return anyFailing ? 1 : 0;
|
|
462
545
|
}
|
|
463
546
|
|
|
547
|
+
function hasRunnableNextAction(flag) {
|
|
548
|
+
if (flag.probe) return true;
|
|
549
|
+
return /\b(accept|add|approve|book|build|call|check|choose|close|confirm|deploy|dissolve|email|fix|merge|pay|publish|reply|resolve|retry|review|run|send|ship|sign|test|update|verify|write)\b/.test(canonicalWhat(flag.what));
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function hasMachineCheckableClose(flag) {
|
|
553
|
+
if (flag.probe) return true;
|
|
554
|
+
if (canonicalWhat(flag.close_condition).includes('source store resolves')) return true;
|
|
555
|
+
return /^(experiment|lesson|mission|pulse|task|usage|watch):/.test(canonicalWhat(flag.source));
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function migrationReason(flag, duplicateIds) {
|
|
559
|
+
if (ownerWaitRank(flag) === 0) return 'waiting on a human';
|
|
560
|
+
if (!hasRunnableNextAction(flag)) return 'no runnable next action';
|
|
561
|
+
if (!hasMachineCheckableClose(flag)) return 'no machine-checkable close condition';
|
|
562
|
+
if (duplicateIds.has(flag.id)) return 'duplicates another open loop';
|
|
563
|
+
return '';
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function compareOldestFlags(a, b) {
|
|
567
|
+
const aOpened = parseDate(a.opened_at) || 0;
|
|
568
|
+
const bOpened = parseDate(b.opened_at) || 0;
|
|
569
|
+
if (aOpened !== bOpened) return aOpened - bOpened;
|
|
570
|
+
return a.id.localeCompare(b.id);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function migrateOpenSlots(cwd = process.cwd(), now = new Date(), options = {}) {
|
|
574
|
+
const flags = openFlags(cwd, { now });
|
|
575
|
+
const excess = flags.length - MAX_OPEN_FLAGS;
|
|
576
|
+
const shouldWrite = options.dryRun !== true && options.write !== false && options.readOnly !== true;
|
|
577
|
+
if (excess <= 0 || !shouldWrite) return [];
|
|
578
|
+
|
|
579
|
+
const byWhat = new Map();
|
|
580
|
+
for (const flag of flags) {
|
|
581
|
+
const key = canonicalWhat(flag.what);
|
|
582
|
+
if (!byWhat.has(key)) byWhat.set(key, []);
|
|
583
|
+
byWhat.get(key).push(flag);
|
|
584
|
+
}
|
|
585
|
+
const duplicateIds = new Set(
|
|
586
|
+
Array.from(byWhat.values())
|
|
587
|
+
.filter((group) => group.length > 1)
|
|
588
|
+
.flat()
|
|
589
|
+
.map((flag) => flag.id)
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
const preferred = [];
|
|
593
|
+
const fallback = [];
|
|
594
|
+
for (const flag of flags.slice().sort(compareOldestFlags)) {
|
|
595
|
+
const why = migrationReason(flag, duplicateIds);
|
|
596
|
+
if (why) preferred.push({ flag, why });
|
|
597
|
+
else fallback.push({ flag, why: 'oldest loop above the active slot cap' });
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return preferred.concat(fallback).slice(0, excess).map(({ flag, why }) => (
|
|
601
|
+
appendParkedEvent(flag, cwd, now, why, { automatic: true })
|
|
602
|
+
));
|
|
603
|
+
}
|
|
604
|
+
|
|
464
605
|
function commandList(args, context = {}) {
|
|
465
606
|
const { options } = parseArgs(args);
|
|
466
607
|
const lane = options.lane ? String(options.lane).toLowerCase() : null;
|
|
467
608
|
if (lane && !VALID_LANES.has(lane)) throw new Error('lane must be business, life, or code');
|
|
468
609
|
|
|
610
|
+
const now = context.now ? new Date(context.now) : new Date();
|
|
611
|
+
const autoClosed = autoCloseResolvedTaskReviewFlags(context.cwd, now);
|
|
612
|
+
const autoParked = migrateOpenSlots(context.cwd, now);
|
|
469
613
|
let flags = openFlags(context.cwd, { now: context.now });
|
|
470
614
|
if (lane) flags = flags.filter((flag) => flag.lane === lane);
|
|
471
615
|
flags.sort(compareOpenFlags);
|
|
472
616
|
|
|
473
617
|
if (options.json) {
|
|
474
|
-
|
|
618
|
+
const payload = { open: flags.map(publicFlag) };
|
|
619
|
+
if (autoClosed.length > 0) payload.auto_closed = autoClosed.map(publicTaskReviewClosed);
|
|
620
|
+
if (autoParked.length > 0) payload.auto_parked = autoParked;
|
|
621
|
+
printJson(payload);
|
|
475
622
|
return 0;
|
|
476
623
|
}
|
|
477
624
|
|
|
625
|
+
autoClosed.forEach((item) => console.log(taskReviewClosedLine(item)));
|
|
626
|
+
autoParked.forEach((flag) => console.log(migratedParkLine(flag)));
|
|
478
627
|
if (flags.length === 0) {
|
|
628
|
+
if (autoClosed.length > 0) return 0;
|
|
479
629
|
console.log('nothing open.');
|
|
480
630
|
return 0;
|
|
481
631
|
}
|
|
@@ -484,6 +634,50 @@ function commandList(args, context = {}) {
|
|
|
484
634
|
return 0;
|
|
485
635
|
}
|
|
486
636
|
|
|
637
|
+
function commandPark(args, context = {}) {
|
|
638
|
+
const { positional, options } = parseArgs(args);
|
|
639
|
+
const id = positional[0];
|
|
640
|
+
if (!id) throw new Error('id is required');
|
|
641
|
+
const flag = requireOpenFlag(id, context);
|
|
642
|
+
const now = context.now ? new Date(context.now) : new Date();
|
|
643
|
+
const why = options.why === true ? undefined : options.why;
|
|
644
|
+
appendParkedEvent(flag, context.cwd, now, why);
|
|
645
|
+
console.log(`parked ${id}`);
|
|
646
|
+
return 0;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function commandParked(args, context = {}) {
|
|
650
|
+
const { options } = parseArgs(args);
|
|
651
|
+
const flags = parkedFlags(context.cwd, { now: context.now });
|
|
652
|
+
if (options.json) {
|
|
653
|
+
printJson({ parked: flags.map(publicFlag) });
|
|
654
|
+
return 0;
|
|
655
|
+
}
|
|
656
|
+
if (flags.length === 0) {
|
|
657
|
+
console.log('nothing parked.');
|
|
658
|
+
return 0;
|
|
659
|
+
}
|
|
660
|
+
flags.forEach((flag) => console.log(parkedLine(flag)));
|
|
661
|
+
return 0;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function commandReopen(args, context = {}) {
|
|
665
|
+
const { positional, options } = parseArgs(args);
|
|
666
|
+
const id = positional[0];
|
|
667
|
+
if (!id) throw new Error('id is required');
|
|
668
|
+
const flag = parkedFlags(context.cwd, { now: context.now }).find((item) => item.id === id);
|
|
669
|
+
if (!flag) throw new Error(`${id} is not parked`);
|
|
670
|
+
const now = context.now ? new Date(context.now) : new Date();
|
|
671
|
+
makeRoomForOpenFlag(context.cwd, now, options.replace, flag.what);
|
|
672
|
+
appendEvent({
|
|
673
|
+
kind: 'reopened',
|
|
674
|
+
at: now.toISOString(),
|
|
675
|
+
id,
|
|
676
|
+
}, context.cwd);
|
|
677
|
+
console.log(`reopened ${id}`);
|
|
678
|
+
return 0;
|
|
679
|
+
}
|
|
680
|
+
|
|
487
681
|
function requireOpenFlag(id, context = {}) {
|
|
488
682
|
const flag = openFlags(context.cwd, { now: context.now }).find((item) => item.id === id);
|
|
489
683
|
if (!flag) throw new Error(`${id} is not open`);
|
|
@@ -559,6 +753,8 @@ function commandSnooze(args, context = {}) {
|
|
|
559
753
|
// one escalated event per overdue flag per day, return the resulting state.
|
|
560
754
|
function sweepState(cwd = process.cwd(), now = new Date(), options = {}) {
|
|
561
755
|
const today = isoDateOnly(now);
|
|
756
|
+
const autoClosed = autoCloseResolvedTaskReviewFlags(cwd, now);
|
|
757
|
+
const autoParked = migrateOpenSlots(cwd, now, options);
|
|
562
758
|
let flags = openFlags(cwd, { now });
|
|
563
759
|
const overdue = flags.filter((flag) => flag.overdue).sort(compareSweepFlags);
|
|
564
760
|
const escalatedToday = [];
|
|
@@ -578,24 +774,39 @@ function sweepState(cwd = process.cwd(), now = new Date(), options = {}) {
|
|
|
578
774
|
}
|
|
579
775
|
|
|
580
776
|
flags = openFlags(cwd, { now });
|
|
581
|
-
|
|
777
|
+
const result = { open: flags.length, overdue, escalated_today: escalatedToday };
|
|
778
|
+
if (autoClosed.length > 0) result.auto_closed = autoClosed;
|
|
779
|
+
if (autoParked.length > 0) result.auto_parked = autoParked;
|
|
780
|
+
return result;
|
|
582
781
|
}
|
|
583
782
|
|
|
584
783
|
function commandSweep(args, context = {}) {
|
|
585
784
|
const { options } = parseArgs(args);
|
|
586
785
|
const now = context.now ? new Date(context.now) : new Date();
|
|
587
|
-
const {
|
|
786
|
+
const {
|
|
787
|
+
open,
|
|
788
|
+
overdue,
|
|
789
|
+
escalated_today: escalatedToday,
|
|
790
|
+
auto_closed: autoClosed = [],
|
|
791
|
+
auto_parked: autoParked = [],
|
|
792
|
+
} = sweepState(context.cwd, now);
|
|
588
793
|
|
|
589
794
|
if (options.json) {
|
|
590
|
-
|
|
795
|
+
const payload = {
|
|
591
796
|
open,
|
|
592
797
|
overdue: overdue.length,
|
|
593
798
|
escalated_today: escalatedToday,
|
|
594
|
-
}
|
|
799
|
+
};
|
|
800
|
+
if (autoClosed.length > 0) payload.auto_closed = autoClosed.map(publicTaskReviewClosed);
|
|
801
|
+
if (autoParked.length > 0) payload.auto_parked = autoParked;
|
|
802
|
+
printJson(payload);
|
|
595
803
|
return 0;
|
|
596
804
|
}
|
|
597
805
|
|
|
806
|
+
autoClosed.forEach((item) => console.log(taskReviewClosedLine(item)));
|
|
807
|
+
autoParked.forEach((flag) => console.log(migratedParkLine(flag)));
|
|
598
808
|
if (overdue.length === 0) {
|
|
809
|
+
if (autoClosed.length > 0) return 0;
|
|
599
810
|
console.log('nothing is overdue.');
|
|
600
811
|
return 0;
|
|
601
812
|
}
|
|
@@ -682,7 +893,7 @@ function pulseLivenessState(cwd, now) {
|
|
|
682
893
|
// decommissioned or intermittent loop leaves stale receipts frozen; without a
|
|
683
894
|
// guard the last N finished ticks stay net-negative forever and the flag can
|
|
684
895
|
// never clear (no fresh positive receipt arrives). It also must not treat a
|
|
685
|
-
// scattered set
|
|
896
|
+
// scattered set, e.g. one tick today plus five from days ago, as a "run":
|
|
686
897
|
// summing rewards across a multi-day gap is meaningless. So we only consider
|
|
687
898
|
// finished ticks from the last few days and require a full window of them; a
|
|
688
899
|
// silent or sparse loop is the pulse-liveness sensor's concern, and here it
|
|
@@ -830,7 +1041,11 @@ function taskSource(task) {
|
|
|
830
1041
|
|
|
831
1042
|
function taskResolved(task) {
|
|
832
1043
|
const status = normalizeSpaces(task && task.status).toLowerCase();
|
|
833
|
-
|
|
1044
|
+
const review = reviewObject(task);
|
|
1045
|
+
const metadata = task && task.metadata && typeof task.metadata === 'object' ? task.metadata : {};
|
|
1046
|
+
const approval = normalizeSpaces(review.approval_status || metadata.approval_status).toLowerCase();
|
|
1047
|
+
return ['done', 'accepted', 'closed'].includes(status)
|
|
1048
|
+
|| ['accepted', 'approved', 'done', 'closed'].includes(approval);
|
|
834
1049
|
}
|
|
835
1050
|
|
|
836
1051
|
function reviewObject(task) {
|
|
@@ -938,7 +1153,15 @@ function shortText(text, maxLength = 70) {
|
|
|
938
1153
|
return clipped || value.slice(0, maxLength).trim();
|
|
939
1154
|
}
|
|
940
1155
|
|
|
941
|
-
function appendOpenedFromSource({
|
|
1156
|
+
function appendOpenedFromSource({
|
|
1157
|
+
what,
|
|
1158
|
+
owner = 'operator',
|
|
1159
|
+
lane = 'code',
|
|
1160
|
+
ttlDays = 3,
|
|
1161
|
+
source,
|
|
1162
|
+
probe,
|
|
1163
|
+
closeCondition = 'the source store resolves it',
|
|
1164
|
+
}, cwd, now) {
|
|
942
1165
|
const event = {
|
|
943
1166
|
kind: 'opened',
|
|
944
1167
|
at: now.toISOString(),
|
|
@@ -948,25 +1171,79 @@ function appendOpenedFromSource({ what, owner = 'operator', lane = 'code', ttlDa
|
|
|
948
1171
|
lane: VALID_LANES.has(lane) ? lane : 'code',
|
|
949
1172
|
opened_at: now.toISOString(),
|
|
950
1173
|
ttl_days: Number(ttlDays) || 3,
|
|
951
|
-
close_condition: 'the source store resolves it',
|
|
1174
|
+
close_condition: normalizeSpaces(closeCondition) || 'the source store resolves it',
|
|
952
1175
|
source,
|
|
953
1176
|
};
|
|
1177
|
+
const normalizedProbe = normalizeSpaces(probe || '');
|
|
1178
|
+
if (normalizedProbe) event.probe = normalizedProbe;
|
|
954
1179
|
appendEvent(event, cwd);
|
|
955
1180
|
return event;
|
|
956
1181
|
}
|
|
957
1182
|
|
|
958
|
-
function
|
|
1183
|
+
function upsertSourceFlag(candidate, cwd = process.cwd(), options = {}) {
|
|
1184
|
+
const now = options.now ? new Date(options.now) : new Date();
|
|
1185
|
+
const source = normalizeSpaces(candidate && candidate.source);
|
|
1186
|
+
if (!source) throw new Error('source is required');
|
|
1187
|
+
|
|
1188
|
+
const flags = openFlags(cwd, { now });
|
|
1189
|
+
const existing = flags.find((flag) => flag.source === source);
|
|
1190
|
+
if (!existing && flags.length >= MAX_OPEN_FLAGS) {
|
|
1191
|
+
return { filed: false, refreshed: false, reason: 'full' };
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
const event = appendOpenedFromSource({ ...candidate, source }, cwd, now);
|
|
1195
|
+
return { filed: true, refreshed: Boolean(existing), event };
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
function appendClosedFromSource(flag, cwd, now, note, proof = RESOLVED_IN_SOURCE_PROOF) {
|
|
959
1199
|
const event = {
|
|
960
1200
|
kind: 'closed',
|
|
961
1201
|
at: now.toISOString(),
|
|
962
1202
|
id: flag.id,
|
|
963
|
-
proof
|
|
1203
|
+
proof,
|
|
964
1204
|
};
|
|
965
1205
|
if (note) event.note = note;
|
|
966
1206
|
appendEvent(event, cwd);
|
|
967
1207
|
return event;
|
|
968
1208
|
}
|
|
969
1209
|
|
|
1210
|
+
function taskReviewFlag(flag) {
|
|
1211
|
+
return flag && flag.source && flag.source.startsWith('task:')
|
|
1212
|
+
&& canonicalWhat(flag.what).includes('has waited in review too long');
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
function autoCloseResolvedTaskReviewFlags(cwd = process.cwd(), now = new Date()) {
|
|
1216
|
+
const tasksById = latestById(readTaskProjection(cwd));
|
|
1217
|
+
if (tasksById.size === 0) return [];
|
|
1218
|
+
|
|
1219
|
+
const closed = [];
|
|
1220
|
+
const flags = foldEvents(readEvents(cwd), { now })
|
|
1221
|
+
.filter((flag) => flag.status === 'open' || flag.status === 'parked')
|
|
1222
|
+
.filter(taskReviewFlag);
|
|
1223
|
+
for (const flag of flags) {
|
|
1224
|
+
const watchedTaskId = flag.source.slice('task:'.length);
|
|
1225
|
+
const task = tasksById.get(watchedTaskId);
|
|
1226
|
+
if (!task || !taskResolved(task)) continue;
|
|
1227
|
+
const ref = taskRef(task) || watchedTaskId;
|
|
1228
|
+
const proof = `task ${ref.toLowerCase()} is closed in task projection`;
|
|
1229
|
+
const event = appendClosedFromSource(flag, cwd, now, null, proof);
|
|
1230
|
+
closed.push({ flag, event, task_ref: ref });
|
|
1231
|
+
}
|
|
1232
|
+
return closed;
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
function taskReviewClosedLine(item) {
|
|
1236
|
+
return `auto-closed review reminder for task ${item.task_ref.toLowerCase()}.`;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
function publicTaskReviewClosed(item) {
|
|
1240
|
+
return {
|
|
1241
|
+
id: item.event.id,
|
|
1242
|
+
task_ref: item.task_ref,
|
|
1243
|
+
proof: item.event.proof,
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
|
|
970
1247
|
function scanState(cwd = process.cwd(), options = {}) {
|
|
971
1248
|
const now = options.now ? new Date(options.now) : new Date();
|
|
972
1249
|
const tasks = readTaskProjection(cwd);
|
|
@@ -1247,7 +1524,7 @@ function run(args = [], context = {}) {
|
|
|
1247
1524
|
printHelp();
|
|
1248
1525
|
return 0;
|
|
1249
1526
|
}
|
|
1250
|
-
// `close <sub> --help` is a help request, not a run
|
|
1527
|
+
// `close <sub> --help` is a help request, not a run, sweep and scan mutate
|
|
1251
1528
|
// the ledger (escalations, auto-closes), so never execute a subcommand just
|
|
1252
1529
|
// to show usage.
|
|
1253
1530
|
if (rest.includes('--help') || rest.includes('-h')) {
|
|
@@ -1256,6 +1533,9 @@ function run(args = [], context = {}) {
|
|
|
1256
1533
|
}
|
|
1257
1534
|
if (subcommand === 'add') return commandAdd(rest, context);
|
|
1258
1535
|
if (subcommand === 'list') return commandList(rest, context);
|
|
1536
|
+
if (subcommand === 'park') return commandPark(rest, context);
|
|
1537
|
+
if (subcommand === 'parked') return commandParked(rest, context);
|
|
1538
|
+
if (subcommand === 'reopen') return commandReopen(rest, context);
|
|
1259
1539
|
if (subcommand === 'done') return commandDone(rest, context);
|
|
1260
1540
|
if (subcommand === 'dissolve') return commandDissolve(rest, context);
|
|
1261
1541
|
if (subcommand === 'snooze') return commandSnooze(rest, context);
|
|
@@ -1277,6 +1557,8 @@ module.exports = {
|
|
|
1277
1557
|
closeIdForWhat,
|
|
1278
1558
|
foldEvents,
|
|
1279
1559
|
openFlags,
|
|
1560
|
+
parkedFlags,
|
|
1561
|
+
upsertSourceFlag,
|
|
1280
1562
|
ledgerPath,
|
|
1281
1563
|
readEvents,
|
|
1282
1564
|
sweepLine,
|
package/commands/compile.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Atris Compile
|
|
2
|
+
* Atris Compile: learn like AI, run like code.
|
|
3
3
|
*
|
|
4
4
|
* The compile loop: execution records → compile-to-deterministic-code →
|
|
5
5
|
* statistical backtest → gated promote. The LLM is only in the build path;
|
|
@@ -328,7 +328,7 @@ function deepEqual(a, b) {
|
|
|
328
328
|
function loadRunner(root, name) {
|
|
329
329
|
const p = runnerPath(root, name);
|
|
330
330
|
if (!fs.existsSync(p)) {
|
|
331
|
-
throw new Error(`no compiled artifact at ${path.relative(root, p)}
|
|
331
|
+
throw new Error(`no compiled artifact at ${path.relative(root, p)}, run "atris compile build ${name}" first`);
|
|
332
332
|
}
|
|
333
333
|
const resolved = require.resolve(p);
|
|
334
334
|
delete require.cache[resolved]; // always load the latest compiled version
|
|
@@ -344,7 +344,7 @@ function loadRunner(root, name) {
|
|
|
344
344
|
async function runBacktest(root, name, options = {}) {
|
|
345
345
|
const records = readRecords(root, name);
|
|
346
346
|
if (records.length === 0) {
|
|
347
|
-
throw new Error(`no execution records for "${name}"
|
|
347
|
+
throw new Error(`no execution records for "${name}", add some with "atris compile record ${name} --input ... --output ..."`);
|
|
348
348
|
}
|
|
349
349
|
const runner = loadRunner(root, name);
|
|
350
350
|
const manifest = readManifest(root, name) || defaultManifest(name);
|
|
@@ -395,7 +395,7 @@ async function runBacktest(root, name, options = {}) {
|
|
|
395
395
|
function promoteProcess(root, name, options = {}) {
|
|
396
396
|
const manifest = readManifest(root, name);
|
|
397
397
|
if (!manifest) {
|
|
398
|
-
throw new Error(`no manifest for "${name}"
|
|
398
|
+
throw new Error(`no manifest for "${name}", compile and backtest it first`);
|
|
399
399
|
}
|
|
400
400
|
if (options.threshold !== undefined) {
|
|
401
401
|
// the only way to change the standing gate: deliberate, at promote time
|
|
@@ -403,13 +403,13 @@ function promoteProcess(root, name, options = {}) {
|
|
|
403
403
|
}
|
|
404
404
|
const bt = manifest.backtest;
|
|
405
405
|
if (!bt) {
|
|
406
|
-
throw new Error(`"${name}" has no backtest
|
|
406
|
+
throw new Error(`"${name}" has no backtest, run "atris compile backtest ${name}" first`);
|
|
407
407
|
}
|
|
408
408
|
if (bt.version !== manifest.version) {
|
|
409
|
-
throw new Error(`backtest is for version ${bt.version} but current version is ${manifest.version}
|
|
409
|
+
throw new Error(`backtest is for version ${bt.version} but current version is ${manifest.version}, re-run the backtest`);
|
|
410
410
|
}
|
|
411
411
|
if (bt.accuracy < manifest.threshold) {
|
|
412
|
-
throw new Error(`backtest accuracy ${(bt.accuracy * 100).toFixed(2)}% is below the ${(manifest.threshold * 100).toFixed(2)}% gate (${bt.passed}/${bt.total})
|
|
412
|
+
throw new Error(`backtest accuracy ${(bt.accuracy * 100).toFixed(2)}% is below the ${(manifest.threshold * 100).toFixed(2)}% gate (${bt.passed}/${bt.total}), not promoting`);
|
|
413
413
|
}
|
|
414
414
|
manifest.status = 'active';
|
|
415
415
|
manifest.promotedAt = new Date().toISOString();
|
|
@@ -436,10 +436,10 @@ Write the compiled artifact to ${runRel} with this exact contract:
|
|
|
436
436
|
- CommonJS module exporting { run }: \`module.exports = { run };\`
|
|
437
437
|
- \`run(input)\` takes one input value and returns the output (sync return or promise both fine)
|
|
438
438
|
- deterministic: no network calls, no Date.now()/new Date() in outputs, no randomness, no environment reads
|
|
439
|
-
- Node.js built-ins only
|
|
439
|
+
- Node.js built-ins only, zero npm dependencies
|
|
440
440
|
- handle malformed input by throwing a clear Error, never by guessing
|
|
441
441
|
|
|
442
|
-
The artifact will be verified by replaying every execution record through run() and requiring near-perfect accuracy, so generalize the rules
|
|
442
|
+
The artifact will be verified by replaying every execution record through run() and requiring near-perfect accuracy, so generalize the rules, do not hardcode a lookup table of the sample records.
|
|
443
443
|
|
|
444
444
|
Reply [COMPILE_COMPLETE] when ${runRel} is written.`;
|
|
445
445
|
}
|
|
@@ -448,7 +448,7 @@ function executeBuild(root, name, options = {}) {
|
|
|
448
448
|
const { verbose = false, timeout = 600000, notes = '', cmdOverride } = options;
|
|
449
449
|
const records = readRecords(root, name);
|
|
450
450
|
if (records.length === 0) {
|
|
451
|
-
throw new Error(`no execution records for "${name}"
|
|
451
|
+
throw new Error(`no execution records for "${name}", record real runs first, then compile`);
|
|
452
452
|
}
|
|
453
453
|
|
|
454
454
|
if (!cmdOverride) {
|
|
@@ -526,7 +526,7 @@ function formatAccuracy(accuracy) {
|
|
|
526
526
|
function printBacktest(name, result, failureCount, manifest) {
|
|
527
527
|
console.log(`backtest ${name} v${result.version}: ${result.passed}/${result.total} passed (${formatAccuracy(result.accuracy)}), gate ${formatAccuracy(result.threshold)}`);
|
|
528
528
|
if (result.total < LOW_RECORD_WARNING) {
|
|
529
|
-
console.log(`note: only ${result.total} record${result.total === 1 ? '' : 's'}
|
|
529
|
+
console.log(`note: only ${result.total} record${result.total === 1 ? '' : 's'}, accuracy means more with ${LOW_RECORD_WARNING}+`);
|
|
530
530
|
}
|
|
531
531
|
if (failureCount > 0) {
|
|
532
532
|
console.log(`${failureCount} mismatch${failureCount === 1 ? '' : 'es'}. first ${Math.min(failureCount, 3)}:`);
|
|
@@ -675,7 +675,7 @@ function showCompileHelp() {
|
|
|
675
675
|
console.log('Server commands require --agent <id> and Atris auth from atris login or ATRIS_TOKEN.');
|
|
676
676
|
console.log('Loop: record real runs -> build -> backtest -> promote -> exec --record');
|
|
677
677
|
console.log('When backtest accuracy drops below the gate, the process is marked drifted');
|
|
678
|
-
console.log('and a recompile is suggested
|
|
678
|
+
console.log('and a recompile is suggested, self-healing against process drift.');
|
|
679
679
|
console.log('');
|
|
680
680
|
}
|
|
681
681
|
|
|
@@ -830,7 +830,7 @@ async function compileCommand(subcommand, ...rawArgs) {
|
|
|
830
830
|
}
|
|
831
831
|
const manifest = readManifest(root, name);
|
|
832
832
|
if (manifest && manifest.status === 'drifted') {
|
|
833
|
-
console.error(`warning: ${name} is marked drifted
|
|
833
|
+
console.error(`warning: ${name} is marked drifted, outputs may be stale. recompile with "atris compile build ${name}"`);
|
|
834
834
|
}
|
|
835
835
|
const runner = loadRunner(root, name);
|
|
836
836
|
const output = await runner.run(input);
|