@indigoai-us/hq-cli 5.345.58 → 5.345.60
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/CHANGELOG.md +17 -0
- package/dist/command-catalog.generated.d.ts +8 -0
- package/dist/command-catalog.generated.js +10 -0
- package/dist/commands/agent-kit.js +20 -10
- package/dist/commands/billing.d.ts +13 -0
- package/dist/commands/billing.js +31 -0
- package/dist/commands/db-dump.js +2 -2
- package/dist/commands/db-remote-deps.js +6 -0
- package/dist/commands/lanes-loop.js +3 -1
- package/dist/commands/lanes-questions.js +16 -0
- package/dist/commands/lanes-session-start.js +19 -3
- package/dist/commands/lanes-shared.d.ts +1 -0
- package/dist/commands/lanes.d.ts +3 -1
- package/dist/commands/lanes.js +52 -2
- package/dist/lib/agent-kit/inbox-poll-backoff-flag.d.ts +19 -0
- package/dist/lib/agent-kit/inbox-poll-backoff-flag.js +70 -0
- package/dist/lib/agent-kit/run/inbox.d.ts +7 -3
- package/dist/lib/agent-kit/run/inbox.js +45 -6
- package/dist/lib/agent-kit/run/stop-signals.d.ts +14 -0
- package/dist/lib/agent-kit/run/stop-signals.js +26 -0
- package/dist/lib/agent-kit/services.d.ts +2 -2
- package/dist/lib/agent-kit/services.js +5 -3
- package/dist/lib/anywhere-runtime-flag-key.d.ts +1 -0
- package/dist/lib/anywhere-runtime-flag-key.js +1 -0
- package/dist/lib/anywhere-runtime-revocation-marker.d.ts +4 -0
- package/dist/lib/anywhere-runtime-revocation-marker.js +26 -0
- package/dist/lib/daemon/run.d.ts +9 -0
- package/dist/lib/daemon/run.js +42 -5
- package/dist/lib/daemon/server.d.ts +11 -0
- package/dist/lib/daemon/server.js +58 -0
- package/dist/lib/db/control-plane.d.ts +12 -1
- package/dist/lib/db/usage-format.d.ts +2 -1
- package/dist/lib/db/usage-format.js +19 -0
- package/dist/lib/install/provider-wrappers.js +13 -0
- package/dist/lib/lanes/loop.d.ts +23 -1
- package/dist/lib/lanes/loop.js +134 -19
- package/dist/lib/lanes/questions.d.ts +5 -0
- package/dist/lib/lanes/reconcile.js +3 -0
- package/dist/lib/lanes/story-ledger.js +30 -20
- package/dist/lib/lanes/types.js +1 -1
- package/package.json +1 -1
package/dist/lib/lanes/loop.js
CHANGED
|
@@ -14,6 +14,16 @@ export function loopAdmissionPath(state, hasPendingResume, initialAdmission) {
|
|
|
14
14
|
return "deferred";
|
|
15
15
|
return "resume";
|
|
16
16
|
}
|
|
17
|
+
function asidePath(file, marker) {
|
|
18
|
+
const parsed = path.parse(file);
|
|
19
|
+
let suffix = 1;
|
|
20
|
+
let candidate;
|
|
21
|
+
do {
|
|
22
|
+
candidate = path.join(parsed.dir, `${parsed.name}.${marker}.${suffix}${parsed.ext}`);
|
|
23
|
+
suffix += 1;
|
|
24
|
+
} while (fs.existsSync(candidate));
|
|
25
|
+
return candidate;
|
|
26
|
+
}
|
|
17
27
|
function isRecord(value) {
|
|
18
28
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19
29
|
}
|
|
@@ -331,11 +341,49 @@ export function enqueueLoopEnvelope(input) {
|
|
|
331
341
|
}
|
|
332
342
|
})
|
|
333
343
|
.filter((record) => record !== undefined);
|
|
334
|
-
|
|
344
|
+
const withdrawnDir = path.join(input.runDir, "loop", "withdrawn");
|
|
345
|
+
const stalledDir = path.join(input.runDir, "loop", "stalled");
|
|
346
|
+
const reusableRecords = records.filter((record) => record.envelope_id === envelopeId);
|
|
347
|
+
const terminalResult = fs.existsSync(path.join(resultsDir(input.runDir), resultName(envelopeId)))
|
|
348
|
+
? readJson(path.join(resultsDir(input.runDir), resultName(envelopeId))) : undefined;
|
|
349
|
+
const reusable = reusableRecords.length > 0 && reusableRecords.every((record) => [stalledDir, withdrawnDir].some((dir) => envelopeFiles(dir).some((file) => {
|
|
350
|
+
try {
|
|
351
|
+
return fileEnvelope(file).envelope_id === record.envelope_id;
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
}))) && (!isRecord(terminalResult) || terminalResult.status === "stalled");
|
|
357
|
+
if ((records.some((record) => record.envelope_id === envelopeId) && !reusable) ||
|
|
358
|
+
(terminalResult !== undefined && (!reusable || !isRecord(terminalResult) || terminalResult.status !== "stalled"))) {
|
|
335
359
|
throw new Error(`envelope id ${envelopeId} was already used in this loop lane`);
|
|
336
360
|
}
|
|
361
|
+
if (reusable) {
|
|
362
|
+
for (const file of envelopeFiles(withdrawnDir).concat(envelopeFiles(stalledDir))) {
|
|
363
|
+
try {
|
|
364
|
+
if (fileEnvelope(file).envelope_id === envelopeId)
|
|
365
|
+
fs.renameSync(file, asidePath(file, path.basename(path.dirname(file))));
|
|
366
|
+
}
|
|
367
|
+
catch (error) {
|
|
368
|
+
if (error.code !== "ENOENT")
|
|
369
|
+
throw error;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (terminalResult !== undefined) {
|
|
373
|
+
const resultFile = path.join(resultsDir(input.runDir), resultName(envelopeId));
|
|
374
|
+
fs.renameSync(resultFile, asidePath(resultFile, "stalled"));
|
|
375
|
+
}
|
|
376
|
+
}
|
|
337
377
|
const incomingStop = envelope.kind === "stop";
|
|
338
|
-
|
|
378
|
+
const blockingStop = [pendingDir(input.runDir), activeDir(input.runDir)].some((scanDir) => envelopeFiles(scanDir).some((file) => {
|
|
379
|
+
try {
|
|
380
|
+
return fileEnvelope(file).envelope.kind === "stop";
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
}));
|
|
386
|
+
if (blockingStop) {
|
|
339
387
|
throw new Error(incomingStop ? "a stop envelope is already queued or active" : "cannot enqueue a phase after a stop envelope");
|
|
340
388
|
}
|
|
341
389
|
const sequence = nextQueueSequence(input.runDir);
|
|
@@ -405,6 +453,77 @@ export function interruptLoopQueue(runDir) {
|
|
|
405
453
|
}
|
|
406
454
|
return { withdrawn, already_picked_up: alreadyPickedUp };
|
|
407
455
|
}
|
|
456
|
+
export function interruptLoopQueueForStory(runDir, storyId, phase) {
|
|
457
|
+
const withdrawn = [];
|
|
458
|
+
const alreadyPickedUp = [];
|
|
459
|
+
const matches = (record) => {
|
|
460
|
+
const envelope = record.envelope;
|
|
461
|
+
return envelope.story_id === storyId && (phase === undefined || envelope.phase === phase);
|
|
462
|
+
};
|
|
463
|
+
for (const file of envelopeFiles(pendingDir(runDir))) {
|
|
464
|
+
const record = fileEnvelope(file);
|
|
465
|
+
if (!matches(record))
|
|
466
|
+
continue;
|
|
467
|
+
const destination = path.join(runDir, "loop", "withdrawn", path.basename(file));
|
|
468
|
+
fs.mkdirSync(path.dirname(destination), { recursive: true });
|
|
469
|
+
fs.renameSync(file, destination);
|
|
470
|
+
withdrawn.push(record.envelope_id);
|
|
471
|
+
}
|
|
472
|
+
for (const file of envelopeFiles(activeDir(runDir))) {
|
|
473
|
+
const record = fileEnvelope(file);
|
|
474
|
+
if (matches(record))
|
|
475
|
+
alreadyPickedUp.push(record.envelope_id);
|
|
476
|
+
}
|
|
477
|
+
return { withdrawn, already_picked_up: alreadyPickedUp };
|
|
478
|
+
}
|
|
479
|
+
export function applyLoopStallAnswer(input) {
|
|
480
|
+
const results = fs.existsSync(resultsDir(input.runDir))
|
|
481
|
+
? fs.readdirSync(resultsDir(input.runDir)).filter((name) => name.endsWith(".json")) : [];
|
|
482
|
+
const resultPath = results.map((name) => path.join(resultsDir(input.runDir), name)).find((file) => {
|
|
483
|
+
try {
|
|
484
|
+
const result = readJson(file);
|
|
485
|
+
return isRecord(result) && result.status === "stalled" && result.question_id === input.questionId;
|
|
486
|
+
}
|
|
487
|
+
catch (error) {
|
|
488
|
+
if (error instanceof SyntaxError)
|
|
489
|
+
return false;
|
|
490
|
+
throw error;
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
if (!resultPath)
|
|
494
|
+
return undefined;
|
|
495
|
+
const result = readJson(resultPath);
|
|
496
|
+
const envelopeFile = envelopeFiles(path.join(input.runDir, "loop", "stalled")).find((file) => {
|
|
497
|
+
try {
|
|
498
|
+
return fileEnvelope(file).envelope_id === result.envelope_id;
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
if (!envelopeFile)
|
|
505
|
+
throw new Error(`stalled loop envelope ${result.envelope_id} is missing`);
|
|
506
|
+
const record = fileEnvelope(envelopeFile);
|
|
507
|
+
const action = input.choice === "Retry with a longer deadline" ? "requeued" : "parked";
|
|
508
|
+
if (action === "requeued") {
|
|
509
|
+
record.deadline_budget_seconds = Math.max(1, record.deadline_budget_seconds ?? phaseDeadlineBudget(record.envelope, 3600)) * 2;
|
|
510
|
+
const head = path.join(pendingDir(input.runDir), `0000000000000-answer-${path.basename(envelopeFile)}`);
|
|
511
|
+
fs.mkdirSync(pendingDir(input.runDir), { recursive: true });
|
|
512
|
+
fs.renameSync(envelopeFile, head);
|
|
513
|
+
fs.renameSync(resultPath, asidePath(resultPath, "stalled"));
|
|
514
|
+
const pending = fileEnvelope(head);
|
|
515
|
+
pending.deadline_budget_seconds = record.deadline_budget_seconds;
|
|
516
|
+
atomicJson(head, pending);
|
|
517
|
+
persistStatus(input.runDir, path.basename(input.runDir), { stall_counts: { [record.envelope_id]: 0 } });
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
atomicJson(resultPath, {
|
|
521
|
+
...result,
|
|
522
|
+
decision: { question_id: input.questionId, choice: input.choice, ...(input.notes ? { notes: input.notes } : {}), at: input.at },
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
return { lane_id: path.basename(input.runDir), envelope_id: result.envelope_id, action };
|
|
526
|
+
}
|
|
408
527
|
export function loopStatus(runDir, laneId) {
|
|
409
528
|
const loop = fs.existsSync(loopPath(runDir)) && isRecord(readJson(loopPath(runDir))) ? readJson(loopPath(runDir)) : {};
|
|
410
529
|
const active = envelopeFiles(activeDir(runDir)).map(fileEnvelope)[0];
|
|
@@ -578,22 +697,16 @@ export async function runLoopLane(options) {
|
|
|
578
697
|
const previousSession = sessions.get(sessionKey);
|
|
579
698
|
const startedAt = now().toISOString();
|
|
580
699
|
const startedAtMs = Date.parse(startedAt);
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
firstDeadline.set(record.envelope_id, budget);
|
|
589
|
-
record.deadline_budget_seconds = budget;
|
|
590
|
-
deadlineAtMs = startedAtMs + budget * 1000;
|
|
591
|
-
}
|
|
592
|
-
record.deadline_at_ms = deadlineAtMs;
|
|
593
|
-
}
|
|
700
|
+
const absoluteDeadline = envelope.deadline;
|
|
701
|
+
const budget = record.deadline_budget_seconds ?? firstDeadline.get(record.envelope_id) ??
|
|
702
|
+
(typeof absoluteDeadline === "string"
|
|
703
|
+
? Math.max(1, Math.round((Date.parse(absoluteDeadline) - startedAtMs) / 1000))
|
|
704
|
+
: phaseDeadlineBudget(envelope, defaultDeadlineSeconds));
|
|
705
|
+
firstDeadline.set(record.envelope_id, budget);
|
|
706
|
+
record.deadline_budget_seconds = budget;
|
|
594
707
|
atomicJson(file, record);
|
|
595
708
|
persistStatus(options.runDir, options.lane.lane_id, { state: "running", queue_depth: listLoopQueue(options.runDir).length, active_envelope_id: record.envelope_id, active_phase: phaseName(envelope, record.envelope_id), stall_counts: Object.fromEntries(stalls) });
|
|
596
|
-
const timer = abortAfter(
|
|
709
|
+
const timer = abortAfter(budget * 1000, options.signal);
|
|
597
710
|
try {
|
|
598
711
|
let result;
|
|
599
712
|
let fallback = false;
|
|
@@ -673,9 +786,11 @@ export async function runLoopLane(options) {
|
|
|
673
786
|
continue;
|
|
674
787
|
}
|
|
675
788
|
const message = error instanceof Error ? error.message : String(error);
|
|
676
|
-
const
|
|
789
|
+
const questionId = options.raiseQuestion ? randomUUID() : undefined;
|
|
790
|
+
const result = { schema_version: LOOP_SCHEMA, envelope_id: record.envelope_id, status: "stalled", started_at: startedAt, finished_at: now().toISOString(), error: message, ...(questionId ? { question_id: questionId } : {}) };
|
|
677
791
|
writeResultAt(options.runDir, file, record, result);
|
|
678
|
-
|
|
792
|
+
if (questionId)
|
|
793
|
+
await options.raiseQuestion?.({ envelope, envelopeId: record.envelope_id, attempt, error: message, questionId });
|
|
679
794
|
completed += 1;
|
|
680
795
|
persistStatus(options.runDir, options.lane.lane_id, { state: "parked", queue_depth: listLoopQueue(options.runDir).length, active_envelope_id: null, active_phase: null, stall_counts: Object.fromEntries(stalls) });
|
|
681
796
|
releaseRunner();
|
|
@@ -715,7 +830,7 @@ function writeResultAt(runDir, activeFile, record, result) {
|
|
|
715
830
|
summary: stalled ? "Phase stalled twice and needs operator direction." : "Phase execution failed.",
|
|
716
831
|
files_changed: [], commits: [],
|
|
717
832
|
back_pressure: { tests: "skip", lint: "skip", typecheck: "skip", build: "skip" },
|
|
718
|
-
context_for_next: result.error ?? result.status,
|
|
833
|
+
context_for_next: [result.error ?? result.status, ...(result.question_id ? [`Decision question: ${result.question_id}`] : [])].join("\n"),
|
|
719
834
|
};
|
|
720
835
|
atomicJson(path.resolve(externalPath), failure);
|
|
721
836
|
}
|
|
@@ -10,6 +10,11 @@ export interface QuestionBody {
|
|
|
10
10
|
options: string[];
|
|
11
11
|
recommended?: string;
|
|
12
12
|
notes?: string;
|
|
13
|
+
loop_stall?: {
|
|
14
|
+
lane_id: string;
|
|
15
|
+
envelope_id: string;
|
|
16
|
+
attempt: number;
|
|
17
|
+
};
|
|
13
18
|
}
|
|
14
19
|
export declare function assertQuestionBodySecretSafe(body: QuestionBody): void;
|
|
15
20
|
/** Errors that make an envelope ask unsafe or impossible to persist. */
|
|
@@ -12,6 +12,7 @@ import { compareLaneQueueOrder, compareLaneQueueOrderWithLeads, freeClaudeAccoun
|
|
|
12
12
|
import { accountStatus, isCredentialProven, resolveAccount } from "./accounts.js";
|
|
13
13
|
import { effectiveAccountHome } from "./account-home.js";
|
|
14
14
|
import { enforceRegistryApprovalGate } from "./approval-gate.js";
|
|
15
|
+
import { loopStatus } from "./loop.js";
|
|
15
16
|
import { loadLanesConfig, providerQuotaDefaultHoldMs } from "./config.js";
|
|
16
17
|
import { MAX_LANE_DEPTH } from "./constants.js";
|
|
17
18
|
import { laneDepthCap } from "./depth-cap.js";
|
|
@@ -3600,6 +3601,8 @@ async function resumeAwaiting(hqRoot, lanes, questions, summary, deps, onlyLaneI
|
|
|
3600
3601
|
const current = readLane(hqRoot, lane.lane_id);
|
|
3601
3602
|
if (current.state !== "awaiting_input")
|
|
3602
3603
|
continue;
|
|
3604
|
+
if (current.loop_mode === true && loopStatus(laneRunDir(hqRoot, current.lane_id), current.lane_id).state === "parked")
|
|
3605
|
+
continue;
|
|
3603
3606
|
const pending = questions.filter((q) => q.origin.id === lane.lane_id && q.status === "pending");
|
|
3604
3607
|
const answered = questions.filter((q) => q.origin.id === lane.lane_id && q.status === "answered");
|
|
3605
3608
|
if (pending.length > 0 || answered.length === 0)
|
|
@@ -9,7 +9,8 @@ const LOCK_TIMEOUT_MS = 10_000;
|
|
|
9
9
|
const LOCK_STALE_MS = 60_000;
|
|
10
10
|
const LOCK_RETRY_MS = 10;
|
|
11
11
|
const STORY_STATES = ["todo", "in_progress", "review", "waiting", "blocked", "done", "dropped"];
|
|
12
|
-
const
|
|
12
|
+
const LEAD_CONTROLLED_STATES = new Set(["waiting", "blocked", "dropped"]);
|
|
13
|
+
const STORY_STATE_ORDER = ["todo", "in_progress", "review", "done"];
|
|
13
14
|
const TERMINAL_STATES = new Set(["done", "failed", "interrupted", "cancelled", "killed"]);
|
|
14
15
|
function projectFocusOverrides(value) {
|
|
15
16
|
if (value === undefined)
|
|
@@ -561,6 +562,13 @@ function validPriorQuestions(value, storyId) {
|
|
|
561
562
|
}
|
|
562
563
|
return value;
|
|
563
564
|
}
|
|
565
|
+
function canonicalStoryState(value) {
|
|
566
|
+
if (value === "in_review" || value === "verification")
|
|
567
|
+
return "review";
|
|
568
|
+
return typeof value === "string" && STORY_STATES.includes(value)
|
|
569
|
+
? value
|
|
570
|
+
: undefined;
|
|
571
|
+
}
|
|
564
572
|
function setLaneStatusFields(story, lane, laneStoryStatus, isCurrentAttribution) {
|
|
565
573
|
const laneStatus = story.lane_status;
|
|
566
574
|
if (laneStatus !== undefined && !isJsonObject(laneStatus)) {
|
|
@@ -615,29 +623,31 @@ function setLaneStatusFields(story, lane, laneStoryStatus, isCurrentAttribution)
|
|
|
615
623
|
else {
|
|
616
624
|
delete story.pending_questions;
|
|
617
625
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
626
|
+
const currentState = canonicalStoryState(story.state) ?? canonicalStoryState(story.status);
|
|
627
|
+
const currentStates = currentState === undefined ? [] : [currentState];
|
|
628
|
+
const laneProgressState = laneStoryStatus === "review"
|
|
629
|
+
? "review"
|
|
630
|
+
: laneStoryStatus === "done"
|
|
631
|
+
? "done"
|
|
632
|
+
: laneStoryStatus === "in_progress" || lane.state === "queued" || lane.state === "running" || lane.state === "awaiting_input"
|
|
633
|
+
? "in_progress"
|
|
634
|
+
: undefined;
|
|
635
|
+
const currentProgressState = STORY_STATE_ORDER.reduce((highest, state) => currentStates.includes(state) || laneProgressState === state ? state : highest, undefined);
|
|
636
|
+
const laneManagedBlocked = isJsonObject(laneStatus) && isJsonObject(laneStatus.saved);
|
|
637
|
+
const leadControlledState = currentStates.find((state) => LEAD_CONTROLLED_STATES.has(state) && !(state === "blocked" && laneManagedBlocked));
|
|
638
|
+
if (currentStates.includes("done"))
|
|
631
639
|
story.state = "done";
|
|
640
|
+
else if (leadControlledState !== undefined)
|
|
641
|
+
story.state = leadControlledState;
|
|
642
|
+
else if (blockers.length > 0 && currentProgressState !== "review" && currentProgressState !== "done") {
|
|
643
|
+
story.state = "blocked";
|
|
632
644
|
}
|
|
633
|
-
else if (
|
|
634
|
-
story.state =
|
|
645
|
+
else if (currentProgressState !== undefined) {
|
|
646
|
+
story.state = currentProgressState;
|
|
635
647
|
}
|
|
636
648
|
else if (saved !== undefined && story.state === "blocked") {
|
|
637
|
-
const previous = saved.state;
|
|
638
|
-
story.state =
|
|
639
|
-
? previous
|
|
640
|
-
: "in_progress";
|
|
649
|
+
const previous = canonicalStoryState(saved.state);
|
|
650
|
+
story.state = previous ?? "in_progress";
|
|
641
651
|
}
|
|
642
652
|
if (typeof story.state === "string" && STORY_STATES.includes(story.state)) {
|
|
643
653
|
story.status = story.state;
|
package/dist/lib/lanes/types.js
CHANGED
|
@@ -236,7 +236,7 @@ export const LANE_FIELD_WRITERS = {
|
|
|
236
236
|
priority: ["command"], worker: ["creator", "command"], worker_path: ["creator", "command"], worker_pack: ["creator", "command"], worker_profile: ["creator", "command"], worker_profile_hash: ["creator", "command", "spawn"], registry_worker: ["creator", "command", "spawn"], registry_worker_approval_history: ["command", "watcher", "reconcile"], worker_profile_repin_history: ["command"], worker_profile_ignored: ["creator", "command", "spawn"], worker_policy_delivery: ["command", "spawn"],
|
|
237
237
|
keep_alive: ["creator", "command"], keep_alive_standing: ["command"], keep_alive_opted_in_at: ["creator", "command"], keep_alive_blocked_reason: ["command", "reconcile"], keep_alive_resume_count: ["command", "reconcile", "spawn"], keep_alive_history: ["reconcile"], keep_alive_blocked_resume_streak: ["reconcile", "spawn", "watcher"], keep_alive_last_resume_at: ["command", "reconcile", "spawn"], keep_alive_next_eligible_at: ["command", "reconcile", "spawn"], keep_alive_exhausted_at: ["command", "reconcile", "spawn"], loop_mode: ["creator"],
|
|
238
238
|
provider: ["creator", "command"], provider_source: ["creator", "command"], account: ["creator", "spawn", "command", "watcher", "reconcile"], account_source: ["creator", "command"], profile_settings_pinned: ["command"], account_selection: ["creator", "command"], admission_accounts: ["creator", "command"], account_history: ["command", "spawn", "watcher", "reconcile"], failed_resume_history: ["command"], fallback_question_cancellation_failures: ["command"], pending_resume: ["command", "spawn", "watcher", "reconcile"], placement_default: ["creator"], host: ["creator", "command"], origin_host: ["creator"], origin_host_source: ["creator"],
|
|
239
|
-
version: ["creator"], created_at: ["creator"], updated_at: ["creator"], ended_at: ["reconcile"], envelope_summary: ["reconcile"], attribution_history: ["command", "watcher", "reconcile"], story_switch_pending: ["command", "watcher", "reconcile"], story_switch_unacknowledged: ["watcher", "reconcile"],
|
|
239
|
+
version: ["creator"], created_at: ["creator"], updated_at: ["creator"], ended_at: ["reconcile"], envelope_summary: ["reconcile"], attribution_history: ["command", "watcher", "reconcile"], story_switch_pending: ["command", "watcher", "reconcile"], story_switch_unacknowledged: ["command", "watcher", "reconcile"],
|
|
240
240
|
last_envelope: ["watcher", "reconcile"], model: ["creator", "spawn", "command"], model_source: ["creator", "spawn", "command"], effort: ["creator", "spawn", "command"], effort_source: ["creator", "spawn", "command"], fast_mode: ["spawn"],
|
|
241
241
|
last_message_at: ["creator", "command"], status_gate: ["spawn", "watcher"], status_gate_history: ["watcher", "reconcile"], blocker_freshness_state: ["watcher"], kr_freshness_state: ["watcher"], last_compaction: ["spawn"], story_complete: ["watcher", "reconcile"], story_complete_reason: ["watcher", "reconcile"], story_status: ["watcher", "reconcile"],
|
|
242
242
|
overdue: ["reconcile"], overdue_at: ["reconcile"], last_host_report_at: ["watcher", "spawn", "reconcile"], stalled: ["watcher"], stalled_at: ["watcher"], stalled_reason: ["watcher"],
|