@telora/daemon 0.22.169 → 0.22.187
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/build-info.json +9 -3
- package/dist/audit/audit-floor-policy.d.ts +188 -0
- package/dist/audit/audit-floor-policy.d.ts.map +1 -0
- package/dist/audit/audit-floor-policy.js +353 -0
- package/dist/audit/audit-floor-policy.js.map +1 -0
- package/dist/audit/audit-prompt.d.ts +4 -177
- package/dist/audit/audit-prompt.d.ts.map +1 -1
- package/dist/audit/audit-prompt.js +6 -329
- package/dist/audit/audit-prompt.js.map +1 -1
- package/dist/completion/commit-verdict-release.d.ts +37 -1
- package/dist/completion/commit-verdict-release.d.ts.map +1 -1
- package/dist/completion/commit-verdict-release.js +90 -15
- package/dist/completion/commit-verdict-release.js.map +1 -1
- package/dist/completion/escalations.d.ts.map +1 -1
- package/dist/completion/escalations.js +3 -1
- package/dist/completion/escalations.js.map +1 -1
- package/dist/completion/review-exit-verdict-gate.d.ts +11 -5
- package/dist/completion/review-exit-verdict-gate.d.ts.map +1 -1
- package/dist/completion/review-exit-verdict-gate.js +6 -3
- package/dist/completion/review-exit-verdict-gate.js.map +1 -1
- package/dist/delivery/commit-verdict-hold.d.ts.map +1 -1
- package/dist/delivery/commit-verdict-hold.js +2 -1
- package/dist/delivery/commit-verdict-hold.js.map +1 -1
- package/dist/directive/stranded-review-reroute.js +1 -1
- package/dist/directive/stranded-review-reroute.js.map +1 -1
- package/dist/focus/focus-executor.d.ts.map +1 -1
- package/dist/focus/focus-executor.js +2 -0
- package/dist/focus/focus-executor.js.map +1 -1
- package/dist/focus/pending-review-approved-head.d.ts +1 -1
- package/dist/focus/pending-review-approved-head.d.ts.map +1 -1
- package/dist/focus/pending-review-approved-head.js +4 -3
- package/dist/focus/pending-review-approved-head.js.map +1 -1
- package/dist/focus/session-health.d.ts.map +1 -1
- package/dist/focus/session-health.js +2 -1
- package/dist/focus/session-health.js.map +1 -1
- package/dist/focus-stage-lifecycle/planning-release.d.ts +52 -17
- package/dist/focus-stage-lifecycle/planning-release.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle/planning-release.js +58 -70
- package/dist/focus-stage-lifecycle/planning-release.js.map +1 -1
- package/dist/focus-stage-lifecycle/ratify-bar-gate.d.ts +108 -0
- package/dist/focus-stage-lifecycle/ratify-bar-gate.d.ts.map +1 -0
- package/dist/focus-stage-lifecycle/ratify-bar-gate.js +174 -0
- package/dist/focus-stage-lifecycle/ratify-bar-gate.js.map +1 -0
- package/dist/git/merge-to-main.d.ts.map +1 -1
- package/dist/git/merge-to-main.js.map +1 -1
- package/dist/nexus/index.d.ts.map +1 -1
- package/dist/nexus/index.js +2 -1
- package/dist/nexus/index.js.map +1 -1
- package/dist/nexus/intake-prompt-sections.d.ts +70 -0
- package/dist/nexus/intake-prompt-sections.d.ts.map +1 -0
- package/dist/nexus/intake-prompt-sections.js +145 -0
- package/dist/nexus/intake-prompt-sections.js.map +1 -0
- package/dist/nexus/intake-spawn.d.ts +49 -13
- package/dist/nexus/intake-spawn.d.ts.map +1 -1
- package/dist/nexus/intake-spawn.js +45 -122
- package/dist/nexus/intake-spawn.js.map +1 -1
- package/dist/nexus/intake.d.ts +55 -0
- package/dist/nexus/intake.d.ts.map +1 -1
- package/dist/nexus/intake.js +62 -1
- package/dist/nexus/intake.js.map +1 -1
- package/dist/prd/arm-readiness.d.ts +93 -0
- package/dist/prd/arm-readiness.d.ts.map +1 -0
- package/dist/prd/arm-readiness.js +254 -0
- package/dist/prd/arm-readiness.js.map +1 -0
- package/dist/prd/arm-refusal.d.ts +149 -0
- package/dist/prd/arm-refusal.d.ts.map +1 -0
- package/dist/prd/arm-refusal.js +219 -0
- package/dist/prd/arm-refusal.js.map +1 -0
- package/dist/prd/controller-shared.d.ts +111 -0
- package/dist/prd/controller-shared.d.ts.map +1 -1
- package/dist/prd/controller-shared.js +3 -1
- package/dist/prd/controller-shared.js.map +1 -1
- package/dist/prd/controller.d.ts +16 -10
- package/dist/prd/controller.d.ts.map +1 -1
- package/dist/prd/controller.js +28 -35
- package/dist/prd/controller.js.map +1 -1
- package/dist/prd/frontier-arming.d.ts +151 -0
- package/dist/prd/frontier-arming.d.ts.map +1 -0
- package/dist/prd/frontier-arming.js +315 -0
- package/dist/prd/frontier-arming.js.map +1 -0
- package/dist/prd/index.d.ts +10 -1
- package/dist/prd/index.d.ts.map +1 -1
- package/dist/prd/index.js +9 -1
- package/dist/prd/index.js.map +1 -1
- package/dist/prd/scoping-prompt.d.ts +68 -0
- package/dist/prd/scoping-prompt.d.ts.map +1 -0
- package/dist/prd/scoping-prompt.js +86 -0
- package/dist/prd/scoping-prompt.js.map +1 -0
- package/dist/prompt/prompt-contradictions.d.ts +20 -1
- package/dist/prompt/prompt-contradictions.d.ts.map +1 -1
- package/dist/prompt/prompt-contradictions.js +35 -0
- package/dist/prompt/prompt-contradictions.js.map +1 -1
- package/dist/prompt/team-prompt-base.d.ts +18 -0
- package/dist/prompt/team-prompt-base.d.ts.map +1 -1
- package/dist/prompt/team-prompt-base.js +56 -11
- package/dist/prompt/team-prompt-base.js.map +1 -1
- package/dist/prompt-sections/persona-sections.d.ts +36 -13
- package/dist/prompt-sections/persona-sections.d.ts.map +1 -1
- package/dist/prompt-sections/persona-sections.js +93 -16
- package/dist/prompt-sections/persona-sections.js.map +1 -1
- package/dist/prompt-sections/scoping-sections.d.ts +19 -5
- package/dist/prompt-sections/scoping-sections.d.ts.map +1 -1
- package/dist/prompt-sections/scoping-sections.js +45 -38
- package/dist/prompt-sections/scoping-sections.js.map +1 -1
- package/dist/prompt-sections/shared.d.ts +8 -0
- package/dist/prompt-sections/shared.d.ts.map +1 -1
- package/dist/prompt-sections/shared.js.map +1 -1
- package/dist/queries/delivery-gate-state.js +1 -1
- package/dist/queries/focus-criteria-standing.d.ts +170 -7
- package/dist/queries/focus-criteria-standing.d.ts.map +1 -1
- package/dist/queries/focus-criteria-standing.js +143 -8
- package/dist/queries/focus-criteria-standing.js.map +1 -1
- package/dist/queries/focus-ratify-release.d.ts +43 -0
- package/dist/queries/focus-ratify-release.d.ts.map +1 -0
- package/dist/queries/focus-ratify-release.js +45 -0
- package/dist/queries/focus-ratify-release.js.map +1 -0
- package/dist/queries/focuses.d.ts +0 -10
- package/dist/queries/focuses.d.ts.map +1 -1
- package/dist/queries/focuses.js +2 -15
- package/dist/queries/focuses.js.map +1 -1
- package/dist/queries/prd.d.ts +31 -0
- package/dist/queries/prd.d.ts.map +1 -1
- package/dist/queries/prd.js +26 -1
- package/dist/queries/prd.js.map +1 -1
- package/dist/queries/shared.js +1 -1
- package/dist/resolvers/focus-criteria-standing.d.ts.map +1 -1
- package/dist/resolvers/focus-criteria-standing.js +324 -25
- package/dist/resolvers/focus-criteria-standing.js.map +1 -1
- package/dist/resolvers/shared/review-window-commits.d.ts.map +1 -1
- package/dist/resolvers/shared/review-window-commits.js +2 -1
- package/dist/resolvers/shared/review-window-commits.js.map +1 -1
- package/dist/security/security-auto-inject.d.ts +51 -0
- package/dist/security/security-auto-inject.d.ts.map +1 -1
- package/dist/security/security-auto-inject.js +101 -11
- package/dist/security/security-auto-inject.js.map +1 -1
- package/dist/security/security-scan-engine.d.ts +4 -4
- package/dist/security/security-scan-engine.d.ts.map +1 -1
- package/dist/security/security-scan-engine.js +6 -0
- package/dist/security/security-scan-engine.js.map +1 -1
- package/dist/session/session-lineage.d.ts.map +1 -1
- package/dist/session/session-lineage.js +2 -0
- package/dist/session/session-lineage.js.map +1 -1
- package/dist/shell/constants.d.ts +5 -0
- package/dist/shell/constants.d.ts.map +1 -1
- package/dist/shell/constants.js +5 -0
- package/dist/shell/constants.js.map +1 -1
- package/dist/shell/daemon-instance.d.ts +74 -64
- package/dist/shell/daemon-instance.d.ts.map +1 -1
- package/dist/shell/daemon-instance.js +16 -121
- package/dist/shell/daemon-instance.js.map +1 -1
- package/dist/shell/health-events.js +1 -1
- package/dist/shell/health-events.js.map +1 -1
- package/dist/shell/instance-liveness.d.ts +112 -0
- package/dist/shell/instance-liveness.d.ts.map +1 -0
- package/dist/shell/instance-liveness.js +205 -0
- package/dist/shell/instance-liveness.js.map +1 -0
- package/dist/shell/schema-version-gate.d.ts +22 -0
- package/dist/shell/schema-version-gate.d.ts.map +1 -1
- package/dist/shell/schema-version-gate.js +116 -6
- package/dist/shell/schema-version-gate.js.map +1 -1
- package/dist/shell/worker-pidfile.d.ts +21 -8
- package/dist/shell/worker-pidfile.d.ts.map +1 -1
- package/dist/shell/worker-pidfile.js +38 -9
- package/dist/shell/worker-pidfile.js.map +1 -1
- package/dist/spawn/lead-launch.d.ts.map +1 -1
- package/dist/spawn/lead-launch.js +2 -1
- package/dist/spawn/lead-launch.js.map +1 -1
- package/dist/spawn/one-shot-scoping-spawn.d.ts +51 -0
- package/dist/spawn/one-shot-scoping-spawn.d.ts.map +1 -0
- package/dist/spawn/one-shot-scoping-spawn.js +156 -0
- package/dist/spawn/one-shot-scoping-spawn.js.map +1 -0
- package/dist/spawn/process-spawn.d.ts +12 -0
- package/dist/spawn/process-spawn.d.ts.map +1 -1
- package/dist/spawn/process-spawn.js +1 -1
- package/dist/spawn/process-spawn.js.map +1 -1
- package/dist/spawn/prompt-assembly.d.ts +8 -0
- package/dist/spawn/prompt-assembly.d.ts.map +1 -1
- package/dist/spawn/prompt-assembly.js +15 -3
- package/dist/spawn/prompt-assembly.js.map +1 -1
- package/dist/spawn/worktree-session.d.ts +24 -6
- package/dist/spawn/worktree-session.d.ts.map +1 -1
- package/dist/spawn/worktree-session.js +37 -14
- package/dist/spawn/worktree-session.js.map +1 -1
- package/dist/testing/arm-coverage-model.d.ts +75 -0
- package/dist/testing/arm-coverage-model.d.ts.map +1 -0
- package/dist/testing/arm-coverage-model.js +114 -0
- package/dist/testing/arm-coverage-model.js.map +1 -0
- package/dist/testing/fixtures/index.js +1 -1
- package/dist/types/index.js +1 -1
- package/dist/verify/commit-gate-configured.d.ts.map +1 -1
- package/dist/verify/commit-gate-configured.js +1 -1
- package/dist/verify/commit-gate-configured.js.map +1 -1
- package/dist/verify/commit-verdict-pull.d.ts +48 -0
- package/dist/verify/commit-verdict-pull.d.ts.map +1 -0
- package/dist/verify/commit-verdict-pull.js +148 -0
- package/dist/verify/commit-verdict-pull.js.map +1 -0
- package/dist/verify/commit-verdict-record.js +1 -1
- package/dist/verify/credit-rearm.js +1 -1
- package/dist/verify/credit-rearm.js.map +1 -1
- package/dist/verify/default-on-guards.d.ts +74 -0
- package/dist/verify/default-on-guards.d.ts.map +1 -0
- package/dist/verify/default-on-guards.js +305 -0
- package/dist/verify/default-on-guards.js.map +1 -0
- package/dist/verify/explicit-rearm.d.ts.map +1 -1
- package/dist/verify/explicit-rearm.js +1 -0
- package/dist/verify/explicit-rearm.js.map +1 -1
- package/dist/verify/gate-run-registry.d.ts.map +1 -1
- package/dist/verify/gate-run-registry.js +1 -0
- package/dist/verify/gate-run-registry.js.map +1 -1
- package/dist/verify/judged-ancestor.d.ts +41 -0
- package/dist/verify/judged-ancestor.d.ts.map +1 -0
- package/dist/verify/judged-ancestor.js +101 -0
- package/dist/verify/judged-ancestor.js.map +1 -0
- package/dist/verify/no-own-delta-verdict.d.ts +7 -2
- package/dist/verify/no-own-delta-verdict.d.ts.map +1 -1
- package/dist/verify/no-own-delta-verdict.js +14 -17
- package/dist/verify/no-own-delta-verdict.js.map +1 -1
- package/dist/verify/rework-attribution.d.ts +71 -0
- package/dist/verify/rework-attribution.d.ts.map +1 -0
- package/dist/verify/rework-attribution.js +263 -0
- package/dist/verify/rework-attribution.js.map +1 -0
- package/dist/verify/shortfall-rulings.d.ts.map +1 -1
- package/dist/verify/shortfall-rulings.js +3 -2
- package/dist/verify/shortfall-rulings.js.map +1 -1
- package/dist/verify/verification-boundary-env.d.ts +70 -17
- package/dist/verify/verification-boundary-env.d.ts.map +1 -1
- package/dist/verify/verification-boundary-env.js +88 -14
- package/dist/verify/verification-boundary-env.js.map +1 -1
- package/dist/verify/verification-commit-sharing.d.ts.map +1 -1
- package/dist/verify/verification-commit-sharing.js +2 -1
- package/dist/verify/verification-commit-sharing.js.map +1 -1
- package/dist/verify/verification-config.d.ts.map +1 -1
- package/dist/verify/verification-config.js +1 -0
- package/dist/verify/verification-config.js.map +1 -1
- package/dist/verify/verification-default-on.d.ts +0 -32
- package/dist/verify/verification-default-on.d.ts.map +1 -1
- package/dist/verify/verification-default-on.js +10 -234
- package/dist/verify/verification-default-on.js.map +1 -1
- package/dist/verify/verification-deps-default.d.ts.map +1 -1
- package/dist/verify/verification-deps-default.js +9 -0
- package/dist/verify/verification-deps-default.js.map +1 -1
- package/dist/verify/verification-engine.d.ts +2 -1
- package/dist/verify/verification-engine.d.ts.map +1 -1
- package/dist/verify/verification-engine.js +12 -4
- package/dist/verify/verification-engine.js.map +1 -1
- package/dist/verify/verification-feedback.d.ts +1 -1
- package/dist/verify/verification-feedback.d.ts.map +1 -1
- package/dist/verify/verification-feedback.js +5 -0
- package/dist/verify/verification-feedback.js.map +1 -1
- package/dist/verify/verification-rework-issue.d.ts +48 -7
- package/dist/verify/verification-rework-issue.d.ts.map +1 -1
- package/dist/verify/verification-rework-issue.js +111 -9
- package/dist/verify/verification-rework-issue.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/focus-stage-lifecycle/ratify-bar-gate.test.ts,
|
|
3
|
+
// packages/daemon/src/prd/prd-arm-readiness.test.ts,
|
|
4
|
+
// packages/daemon/src/prd/prd-frontier-arm-covers-stated-effects.test.ts,
|
|
5
|
+
// packages/daemon/src/prd/prd-frontier-arm-refused.test.ts,
|
|
6
|
+
// packages/daemon/src/prd/prd-frontier-arming.test.ts
|
|
7
|
+
// PINNED-BY-END
|
|
8
|
+
/**
|
|
9
|
+
* "Is this focus scoped enough to arm?" -- the question the PRD controller must
|
|
10
|
+
* answer BEFORE it spends the PRD's Play on a frontier focus.
|
|
11
|
+
*
|
|
12
|
+
* An arm covers the effects the focus held when the go-signal was given, and
|
|
13
|
+
* the database fixes that set AT THE ARM (`armed_baseline_target_node_ids`). So
|
|
14
|
+
* an arm placed on an empty focus covers nothing, permanently: every delivery
|
|
15
|
+
* scoped afterwards rests at `planning`, invisible to the running team, until
|
|
16
|
+
* someone arms again. The controller therefore asks this first.
|
|
17
|
+
*
|
|
18
|
+
* THE BAR: the focus holds at least ONE anchored, non-cancelled delivery whose
|
|
19
|
+
* injection names at least one target, and EVERY one of that injection's
|
|
20
|
+
* targets carries an `frt_statement`. An anchor says which cause the delivery
|
|
21
|
+
* changes; the restatement says what the world reads as once it has. A target
|
|
22
|
+
* with no restatement states no future, so an arm covering it covers nothing a
|
|
23
|
+
* later verify can check -- which is the whole failure this module exists to
|
|
24
|
+
* prevent.
|
|
25
|
+
*
|
|
26
|
+
* PREFER `criteria_standing` ONCE IT LANDS. A sibling delivery is adding a
|
|
27
|
+
* `criteria_standing` read that answers exactly this question for an un-armed
|
|
28
|
+
* focus, derived live at the edge. When it lands, this module's IO half
|
|
29
|
+
* (`readFocusArmReadiness`) should become a call to it and the local derivation
|
|
30
|
+
* below should be deleted -- a second definition of "scoped enough" is exactly
|
|
31
|
+
* the drift the arm-coverage work exists to remove. It has NOT landed yet, so
|
|
32
|
+
* the derivation here is direct and deliberately narrow.
|
|
33
|
+
*
|
|
34
|
+
* FAIL CLOSED = DO NOT ARM. Every unreadable input answers "not ready" with the
|
|
35
|
+
* reason. That is the safe direction here and the opposite of verify's: verify
|
|
36
|
+
* holds a delivery to every target when it cannot prove coverage, because
|
|
37
|
+
* judging one target too many costs a re-work cycle; arming on an unproven
|
|
38
|
+
* scope costs the human's go-signal itself, which only a new arm restores.
|
|
39
|
+
*
|
|
40
|
+
* TWO QUESTIONS LIVE HERE, AND THEY ARE NOT THE SAME BAR. Read the one you
|
|
41
|
+
* mean:
|
|
42
|
+
*
|
|
43
|
+
* `assessArmReadiness` -- "is there a delivery worth spending the PRD's Play
|
|
44
|
+
* on?" PER-DELIVERY: one anchored delivery whose own injection's targets all
|
|
45
|
+
* state futures is enough. The PRD frontier asks this because it chooses
|
|
46
|
+
* WHETHER to arm.
|
|
47
|
+
*
|
|
48
|
+
* `assessFocusStatesABar` -- the DATABASE's rule, restated: across the whole
|
|
49
|
+
* covered set, at least one undesired effect, and NOT ONE of them unstated.
|
|
50
|
+
* `stamp_focus_arm_baseline()` (migration 20260922173056) refuses on exactly
|
|
51
|
+
* this, at both go-signals -- the role arm, and the read_only TRUE -> FALSE
|
|
52
|
+
* ratify crossing of an audit focus. A caller about to cross one of those
|
|
53
|
+
* asks THIS one, so the refusal never has to arrive mid-crossing.
|
|
54
|
+
*
|
|
55
|
+
* The second is strictly the stricter: a focus with one stated delivery and one
|
|
56
|
+
* unstated one passes the first and is refused by the second. That is why they
|
|
57
|
+
* are separate functions rather than one with a flag -- they answer different
|
|
58
|
+
* questions, and collapsing them would silently move the PRD frontier's bar.
|
|
59
|
+
*/
|
|
60
|
+
import { DELIVERY_STATUS } from '@telora/daemon-core';
|
|
61
|
+
import { getFocusDeliveries } from '../queries/focuses.js';
|
|
62
|
+
import { fetchInjectionBundles } from '../resolvers/shared/loop.js';
|
|
63
|
+
import { createLogger } from '../shell/log.js';
|
|
64
|
+
const log = createLogger({ module: 'prd-controller' });
|
|
65
|
+
/** Does this injection's target set state a future? */
|
|
66
|
+
function targetsStateAFuture(targets) {
|
|
67
|
+
if (targets.length === 0)
|
|
68
|
+
return false;
|
|
69
|
+
return targets.every((t) => (t.frtStatement ?? '').trim().length > 0);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The bar, PURE. `targetsByInjectionId` maps each anchored delivery's injection
|
|
73
|
+
* to the effects it promises to flip.
|
|
74
|
+
*
|
|
75
|
+
* A delivery is COUNTED when it is anchored, not cancelled, and its injection's
|
|
76
|
+
* targets all carry a restatement. An injection with NO targets is not counted:
|
|
77
|
+
* "every target carries a restatement" is vacuously true of an empty set, and
|
|
78
|
+
* an arm over an empty set is precisely the zero-effect arm this check refuses.
|
|
79
|
+
*/
|
|
80
|
+
export function assessArmReadiness(deliveries, targetsByInjectionId) {
|
|
81
|
+
const live = deliveries.filter((d) => d.executionStatus !== DELIVERY_STATUS.CANCELLED);
|
|
82
|
+
if (live.length === 0) {
|
|
83
|
+
return { ready: false, reason: 'the focus holds no non-cancelled delivery', readyDeliveryIds: [] };
|
|
84
|
+
}
|
|
85
|
+
const anchored = live.filter((d) => (d.injectionId ?? '').length > 0);
|
|
86
|
+
if (anchored.length === 0) {
|
|
87
|
+
return {
|
|
88
|
+
ready: false,
|
|
89
|
+
reason: `${live.length} delivery/deliveries, none anchored to an injection`,
|
|
90
|
+
readyDeliveryIds: [],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
const readyDeliveryIds = anchored
|
|
94
|
+
.filter((d) => targetsStateAFuture(targetsByInjectionId.get(d.injectionId) ?? []))
|
|
95
|
+
.map((d) => d.id);
|
|
96
|
+
if (readyDeliveryIds.length === 0) {
|
|
97
|
+
return {
|
|
98
|
+
ready: false,
|
|
99
|
+
reason: `${anchored.length} anchored delivery/deliveries, but none whose injection targets ` +
|
|
100
|
+
'all carry an frt_statement (an arm over them would cover no stated effect)',
|
|
101
|
+
readyDeliveryIds: [],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
ready: true,
|
|
106
|
+
reason: `${readyDeliveryIds.length} of ${anchored.length} anchored delivery/deliveries state their targets' futures`,
|
|
107
|
+
readyDeliveryIds,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** Default: the focus's deliveries, narrowed to the fields the bar reads. */
|
|
111
|
+
async function defaultLoadDeliveries(focusId) {
|
|
112
|
+
const deliveries = await getFocusDeliveries(focusId);
|
|
113
|
+
return deliveries.map((d) => ({
|
|
114
|
+
id: d.id,
|
|
115
|
+
name: d.name,
|
|
116
|
+
executionStatus: d.executionStatus,
|
|
117
|
+
injectionId: d.injectionId ?? null,
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Default: the injection-context read's `nowTargets` -- per targeted undesired
|
|
122
|
+
* effect, its current statement and the `frt_statement` it becomes.
|
|
123
|
+
*
|
|
124
|
+
* `nowTargets` reports the UDE targets, which is exactly the set this bar is
|
|
125
|
+
* about: those are the effects an injection PROMISES TO FLIP, and the ones a
|
|
126
|
+
* later verify reconciles against. An injection that targets only a cause
|
|
127
|
+
* entity reports none here and is not counted -- it states no effect, so an arm
|
|
128
|
+
* over it covers nothing checkable. A backend predating the field omits it,
|
|
129
|
+
* which reads the same way: not ready, never a silent pass.
|
|
130
|
+
*/
|
|
131
|
+
async function defaultLoadTargets(injectionIds) {
|
|
132
|
+
const bundles = await fetchInjectionBundles(injectionIds);
|
|
133
|
+
const out = new Map();
|
|
134
|
+
for (const [injectionId, bundle] of bundles) {
|
|
135
|
+
out.set(injectionId, (bundle.nowTargets ?? [])
|
|
136
|
+
.filter((t) => typeof t.nodeId === 'string' && t.nodeId.length > 0)
|
|
137
|
+
.map((t) => ({ nodeId: t.nodeId, frtStatement: t.target })));
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Read a focus's arm readiness. NEVER throws: an unreadable input answers "not
|
|
143
|
+
* ready" with the failure as its reason, and the caller logs it and re-tries on
|
|
144
|
+
* a later tick. Nothing is escalated -- an unscoped focus is a waiting state,
|
|
145
|
+
* not an incident.
|
|
146
|
+
*/
|
|
147
|
+
export async function readFocusArmReadiness(focusId, deps = {}) {
|
|
148
|
+
const loadDeliveries = deps.loadDeliveries ?? defaultLoadDeliveries;
|
|
149
|
+
const loadTargets = deps.loadTargetsByInjectionId ?? defaultLoadTargets;
|
|
150
|
+
try {
|
|
151
|
+
const deliveries = await loadDeliveries(focusId);
|
|
152
|
+
const injectionIds = deliveries
|
|
153
|
+
.filter((d) => d.executionStatus !== DELIVERY_STATUS.CANCELLED)
|
|
154
|
+
.map((d) => d.injectionId)
|
|
155
|
+
.filter((id) => typeof id === 'string' && id.length > 0);
|
|
156
|
+
const targets = injectionIds.length > 0 ? await loadTargets(injectionIds) : new Map();
|
|
157
|
+
return assessArmReadiness(deliveries, targets);
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
const message = err.message;
|
|
161
|
+
log.warn(`[prd-controller] arm-readiness read failed for focus ${focusId}: ${message}`);
|
|
162
|
+
return {
|
|
163
|
+
ready: false,
|
|
164
|
+
reason: `arm-readiness could not be read (${message}) -- holding the arm`,
|
|
165
|
+
readyDeliveryIds: [],
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The database's rule, PURE, over the same inputs `assessArmReadiness` reads.
|
|
171
|
+
*
|
|
172
|
+
* Mirrors `stamp_focus_arm_baseline()` / `focus_arm_baseline_snapshot()`: the
|
|
173
|
+
* covered set is the union of every non-cancelled anchored delivery's injection
|
|
174
|
+
* targets (the injection-context read's `nowTargets`, which reports the
|
|
175
|
+
* UNDESIRED EFFECT targets -- the same node type the trigger counts), it must be
|
|
176
|
+
* non-empty, and every member must carry a non-blank restatement.
|
|
177
|
+
*
|
|
178
|
+
* It is a PRE-CHECK, never the authority: the trigger derives its own answer at
|
|
179
|
+
* the instant of the write, and the two are read seconds apart. A caller still
|
|
180
|
+
* handles the refusal arriving anyway.
|
|
181
|
+
*/
|
|
182
|
+
export function assessFocusStatesABar(deliveries, targetsByInjectionId) {
|
|
183
|
+
const covered = new Map();
|
|
184
|
+
for (const delivery of deliveries) {
|
|
185
|
+
if (delivery.executionStatus === DELIVERY_STATUS.CANCELLED)
|
|
186
|
+
continue;
|
|
187
|
+
const injectionId = delivery.injectionId ?? '';
|
|
188
|
+
if (injectionId === '')
|
|
189
|
+
continue;
|
|
190
|
+
for (const target of targetsByInjectionId.get(injectionId) ?? []) {
|
|
191
|
+
covered.set(target.nodeId, target);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (covered.size === 0) {
|
|
195
|
+
return {
|
|
196
|
+
statesABar: false,
|
|
197
|
+
readable: true,
|
|
198
|
+
reason: 'the focus covers no undesired effect (no non-cancelled delivery anchored to an ' +
|
|
199
|
+
'injection that targets one), so it states no future to judge the work against',
|
|
200
|
+
coveredEffectCount: 0,
|
|
201
|
+
unstatedNodeIds: [],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const unstatedNodeIds = [...covered.values()]
|
|
205
|
+
.filter((t) => (t.frtStatement ?? '').trim().length === 0)
|
|
206
|
+
.map((t) => t.nodeId);
|
|
207
|
+
if (unstatedNodeIds.length > 0) {
|
|
208
|
+
return {
|
|
209
|
+
statesABar: false,
|
|
210
|
+
readable: true,
|
|
211
|
+
reason: `${unstatedNodeIds.length} of ${covered.size} covered undesired effect(s) state no ` +
|
|
212
|
+
`future (frt_statement null or blank): ${unstatedNodeIds.join(', ')}`,
|
|
213
|
+
coveredEffectCount: covered.size,
|
|
214
|
+
unstatedNodeIds,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
statesABar: true,
|
|
219
|
+
readable: true,
|
|
220
|
+
reason: `${covered.size} covered undesired effect(s), every one of them stating its future`,
|
|
221
|
+
coveredEffectCount: covered.size,
|
|
222
|
+
unstatedNodeIds: [],
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Read whether a focus states a bar, live. NEVER throws: an unreadable input
|
|
227
|
+
* answers `{ statesABar: false, readable: false }` with the failure as its
|
|
228
|
+
* reason, so the caller holds rather than crossing on an unproven bar.
|
|
229
|
+
*/
|
|
230
|
+
export async function readFocusStatesABar(focusId, deps = {}) {
|
|
231
|
+
const loadDeliveries = deps.loadDeliveries ?? defaultLoadDeliveries;
|
|
232
|
+
const loadTargets = deps.loadTargetsByInjectionId ?? defaultLoadTargets;
|
|
233
|
+
try {
|
|
234
|
+
const deliveries = await loadDeliveries(focusId);
|
|
235
|
+
const injectionIds = deliveries
|
|
236
|
+
.filter((d) => d.executionStatus !== DELIVERY_STATUS.CANCELLED)
|
|
237
|
+
.map((d) => d.injectionId)
|
|
238
|
+
.filter((id) => typeof id === 'string' && id.length > 0);
|
|
239
|
+
const targets = injectionIds.length > 0 ? await loadTargets(injectionIds) : new Map();
|
|
240
|
+
return assessFocusStatesABar(deliveries, targets);
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
const message = err.message;
|
|
244
|
+
log.warn(`[prd-controller] bar-standing read failed for focus ${focusId}: ${message}`);
|
|
245
|
+
return {
|
|
246
|
+
statesABar: false,
|
|
247
|
+
readable: false,
|
|
248
|
+
reason: `the focus's covered effects could not be read (${message})`,
|
|
249
|
+
coveredEffectCount: 0,
|
|
250
|
+
unstatedNodeIds: [],
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=arm-readiness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arm-readiness.js","sourceRoot":"","sources":["../../src/prd/arm-readiness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A refused arm, SURVIVED: classify it, and record it where an operator reads it.
|
|
3
|
+
*
|
|
4
|
+
* WHAT REFUSES, AND WHY THE MACHINE MUST EXPECT IT. The arm edge
|
|
5
|
+
* (`stamp_focus_arm_baseline()`, migration 20260922173056) RAISES when the arm
|
|
6
|
+
* would cover no undesired effect, or would cover one that states no future --
|
|
7
|
+
* `check_violation` with a message beginning `arm_criteria_required:`, carrying
|
|
8
|
+
* the offending NODES by seq and statement. The product-api answers 400 with
|
|
9
|
+
* that message verbatim and `code: 'arm_criteria_required'`. So the daemon's own
|
|
10
|
+
* readiness gate (`prd/arm-readiness.ts`) is no longer the last word on whether
|
|
11
|
+
* an arm lands: the database is. This module is the BACKSTOP for the case where
|
|
12
|
+
* the daemon thought the focus was ready and the database disagreed -- the two
|
|
13
|
+
* read different things (the readiness gate reads the injection bundle's
|
|
14
|
+
* `nowTargets`; the trigger reads `targets` edges on the live tree), and they
|
|
15
|
+
* are read seconds apart, so they CAN disagree without either being wrong.
|
|
16
|
+
*
|
|
17
|
+
* WHY EVERY ARM FAILURE IS CAUGHT, NOT ONLY THE RECOGNIZED REFUSAL. This
|
|
18
|
+
* delivery exists to remove the PRD frontier's dependence on DEPLOY ORDER, and
|
|
19
|
+
* the deploy-order window is exactly the window in which the refusal is NOT
|
|
20
|
+
* recognizable: with the migration live and the edge function not, the same
|
|
21
|
+
* refused arm comes back as a generic 500 `backend_error` carrying none of the
|
|
22
|
+
* database's text. Catching only the recognized shape would leave the tick
|
|
23
|
+
* aborting in precisely the window the catch was written for. So the frontier
|
|
24
|
+
* catches whatever the arm throws, records what it can name, and continues; the
|
|
25
|
+
* frontier recomputes every tick, so the arm is naturally re-attempted.
|
|
26
|
+
*
|
|
27
|
+
* WHERE THE RECORD GOES, AND WHY THERE. A `focus_debug_artifacts` section, one
|
|
28
|
+
* row per (focus, section_kind), rendered on the focus Debug tab with its own
|
|
29
|
+
* "Updated ..." stamp, read by NOTHING in the machine's decision path -- so
|
|
30
|
+
* recording a refusal cannot misroute a delivery -- and cleared by DELETING the
|
|
31
|
+
* row, which is exactly the shape "until an arm succeeds" wants. It is the same
|
|
32
|
+
* surface, for the same reasons, that `integration_sync` (20260920152723) and
|
|
33
|
+
* `directive_undelivered` (20260920202553) already use; the second of those was
|
|
34
|
+
* written because "the ONLY trace was a log warn" is how a focus goes quiet for
|
|
35
|
+
* 51 minutes with nobody able to learn what it was owed. A log warn alone would
|
|
36
|
+
* repeat that here: the operator who needs this fact is looking at a PRD whose
|
|
37
|
+
* frontier is not advancing, not at a daemon host's journal. The log warn stays
|
|
38
|
+
* as the FLOOR -- it is what a failed record degrades to.
|
|
39
|
+
*
|
|
40
|
+
* NOTHING HERE ESCALATES. A refused arm is a recoverable state the machine
|
|
41
|
+
* re-tries, not a question for a human queue: the fix is a restatement on a
|
|
42
|
+
* tree node, and the scoping agent the frontier re-spawns is the actor that
|
|
43
|
+
* writes it.
|
|
44
|
+
*/
|
|
45
|
+
import { type CallApiDeps } from '../queries/shared.js';
|
|
46
|
+
/** The wire code the product-api answers a refused arm with. */
|
|
47
|
+
export declare const ARM_CRITERIA_REQUIRED_CODE = "arm_criteria_required";
|
|
48
|
+
/**
|
|
49
|
+
* The prefix the DATABASE declares as the contract (the prose after it is not).
|
|
50
|
+
* Matched as well as the wire code so a refusal that reaches us through a
|
|
51
|
+
* transport that dropped the code -- or an older edge that never set one -- is
|
|
52
|
+
* still recognized by the text the trigger wrote.
|
|
53
|
+
*/
|
|
54
|
+
export declare const ARM_CRITERIA_REQUIRED_PREFIX = "arm_criteria_required:";
|
|
55
|
+
/** The focus_debug_artifacts section this record lives in. */
|
|
56
|
+
export declare const ARM_REFUSAL_SECTION_KIND = "arm_refused";
|
|
57
|
+
/** The line carrying the first-observed timestamp across re-records. */
|
|
58
|
+
export declare const REFUSED_SINCE_PREFIX = "Refused since: ";
|
|
59
|
+
/** What an arm failure was, once classified. */
|
|
60
|
+
export interface ArmFailure {
|
|
61
|
+
/**
|
|
62
|
+
* 'criteria_required' -- positively the arm-criteria refusal: the database
|
|
63
|
+
* refused because the arm states no future to judge the work against, and
|
|
64
|
+
* `detail` carries its node list. 'unclassified' -- any other arm failure,
|
|
65
|
+
* including the deploy-order window's opaque 500.
|
|
66
|
+
*/
|
|
67
|
+
kind: 'criteria_required' | 'unclassified';
|
|
68
|
+
/** The wire code the failure carried, or null when it carried none. */
|
|
69
|
+
code: string | null;
|
|
70
|
+
/**
|
|
71
|
+
* The refusal text VERBATIM from its prefix onward (or the raw error message
|
|
72
|
+
* when there is no prefix). Never parsed or re-worded: the node list -- which
|
|
73
|
+
* effects are covered, or which of them state no future -- is the half a
|
|
74
|
+
* human needs and only the database could resolve.
|
|
75
|
+
*/
|
|
76
|
+
detail: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Classify whatever `armFocus` threw. TOTAL: every input yields a record, so a
|
|
80
|
+
* caller never has to decide whether an unrecognized failure is reportable.
|
|
81
|
+
*/
|
|
82
|
+
export declare function classifyArmFailure(err: unknown): ArmFailure;
|
|
83
|
+
/** Everything the rendered record names. */
|
|
84
|
+
export interface ArmRefusalRecord {
|
|
85
|
+
focusId: string;
|
|
86
|
+
/** The overcome node whose focus this is (the frontier's own label for it). */
|
|
87
|
+
nodeId: string;
|
|
88
|
+
prdId: string;
|
|
89
|
+
prdTitle: string;
|
|
90
|
+
failure: ArmFailure;
|
|
91
|
+
/** The daemon's own readiness verdict at the moment it decided to arm. */
|
|
92
|
+
readinessReason: string;
|
|
93
|
+
/** ISO timestamp of THIS observation. */
|
|
94
|
+
observedAt: string;
|
|
95
|
+
}
|
|
96
|
+
/** The `Refused since:` value in a stored artifact, or null when absent. */
|
|
97
|
+
export declare function parseRefusedSince(content: string | undefined): string | null;
|
|
98
|
+
/**
|
|
99
|
+
* Render the human-facing record. PURE.
|
|
100
|
+
*
|
|
101
|
+
* The first-observed timestamp is the one line a re-record must be able to find
|
|
102
|
+
* again, so it is emitted on its own line with a fixed prefix: the frontier
|
|
103
|
+
* re-attempts the arm every tick, so `updated_at` alone would always read "just
|
|
104
|
+
* now" and hide a frontier that has been refused for a day.
|
|
105
|
+
*/
|
|
106
|
+
export declare function renderArmRefusalArtifact(record: ArmRefusalRecord, since: string): string;
|
|
107
|
+
/**
|
|
108
|
+
* Record a refused arm, carrying forward the first-observed timestamp when one
|
|
109
|
+
* is already on record.
|
|
110
|
+
*
|
|
111
|
+
* NEVER THROWS. The record is the operator's view of a waiting state, not a
|
|
112
|
+
* step in the tick: a recording failure (the section kind not yet deployed, the
|
|
113
|
+
* API down) must not cost the tick that survived the refusal. It degrades to
|
|
114
|
+
* the log warn, which carries the same code and node list.
|
|
115
|
+
*/
|
|
116
|
+
export declare function recordArmRefusal(record: ArmRefusalRecord, deps?: CallApiDeps): Promise<void>;
|
|
117
|
+
/**
|
|
118
|
+
* Write the `arm_refused` section, carrying the first-observed timestamp
|
|
119
|
+
* forward. The IO half of the record, with the RENDERING left to the caller.
|
|
120
|
+
*
|
|
121
|
+
* Two crossings can be refused for stating no bar -- the role arm (the PRD
|
|
122
|
+
* frontier's, rendered above) and the audit focus's ratify crossing
|
|
123
|
+
* (`focus-stage-lifecycle/ratify-bar-gate.ts`) -- and they are refusals of
|
|
124
|
+
* DIFFERENT acts, so they read differently to an operator. What they share is
|
|
125
|
+
* the section they live in, the since-when line and the never-throws contract;
|
|
126
|
+
* that is what lives here, so there is one writer of the row rather than two
|
|
127
|
+
* that must be kept in step.
|
|
128
|
+
*
|
|
129
|
+
* NEVER THROWS. The record is the operator's view of a waiting state, not a
|
|
130
|
+
* step in the tick: a recording failure (the section kind not yet deployed, the
|
|
131
|
+
* API down) must not cost the tick that survived the refusal. It degrades to
|
|
132
|
+
* the log warn, which carries the same detail.
|
|
133
|
+
*/
|
|
134
|
+
export declare function recordArmRefusalSection(args: {
|
|
135
|
+
focusId: string;
|
|
136
|
+
/** ISO timestamp of THIS observation; the since-when when none is stored. */
|
|
137
|
+
observedAt: string;
|
|
138
|
+
/** What the refusal said -- the floor the log warn degrades to. */
|
|
139
|
+
detail: string;
|
|
140
|
+
/** Render the artifact, given the first-observed timestamp to carry. */
|
|
141
|
+
render: (since: string) => string;
|
|
142
|
+
}, deps?: CallApiDeps): Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* Clear the record. Idempotent (deleting a section that is not there is a
|
|
145
|
+
* no-op) and, like the record, never throws: a failed clear leaves a stale
|
|
146
|
+
* record on a focus that IS armed, which is a stale note -- not a wedge.
|
|
147
|
+
*/
|
|
148
|
+
export declare function clearArmRefusal(focusId: string, deps?: CallApiDeps): Promise<void>;
|
|
149
|
+
//# sourceMappingURL=arm-refusal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arm-refusal.d.ts","sourceRoot":"","sources":["../../src/prd/arm-refusal.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EAAW,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAKjE,gEAAgE;AAChE,eAAO,MAAM,0BAA0B,0BAA0B,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,2BAA2B,CAAC;AAErE,8DAA8D;AAC9D,eAAO,MAAM,wBAAwB,gBAAgB,CAAC;AAEtD,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,oBAAoB,CAAC;AAEtD,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB;;;;;OAKG;IACH,IAAI,EAAE,mBAAmB,GAAG,cAAc,CAAC;IAC3C,uEAAuE;IACvE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAQD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAc3D;AAQD,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,+EAA+E;IAC/E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,UAAU,CAAC;IACpB,0EAA0E;IAC1E,eAAe,EAAE,MAAM,CAAC;IACxB,yCAAyC;IACzC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,4EAA4E;AAC5E,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAS5E;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,MAAM,GACZ,MAAM,CA4BR;AASD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,gBAAgB,EACxB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE;IACJ,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACnC,EACD,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAYf"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
// PINNED-BY-BEGIN (generated by gen:pinned-by -- do not hand-edit; checked by lint:pinned-by)
|
|
2
|
+
// Pinned by: packages/daemon/src/prd/prd-frontier-arm-refused.test.ts
|
|
3
|
+
// PINNED-BY-END
|
|
4
|
+
/**
|
|
5
|
+
* A refused arm, SURVIVED: classify it, and record it where an operator reads it.
|
|
6
|
+
*
|
|
7
|
+
* WHAT REFUSES, AND WHY THE MACHINE MUST EXPECT IT. The arm edge
|
|
8
|
+
* (`stamp_focus_arm_baseline()`, migration 20260922173056) RAISES when the arm
|
|
9
|
+
* would cover no undesired effect, or would cover one that states no future --
|
|
10
|
+
* `check_violation` with a message beginning `arm_criteria_required:`, carrying
|
|
11
|
+
* the offending NODES by seq and statement. The product-api answers 400 with
|
|
12
|
+
* that message verbatim and `code: 'arm_criteria_required'`. So the daemon's own
|
|
13
|
+
* readiness gate (`prd/arm-readiness.ts`) is no longer the last word on whether
|
|
14
|
+
* an arm lands: the database is. This module is the BACKSTOP for the case where
|
|
15
|
+
* the daemon thought the focus was ready and the database disagreed -- the two
|
|
16
|
+
* read different things (the readiness gate reads the injection bundle's
|
|
17
|
+
* `nowTargets`; the trigger reads `targets` edges on the live tree), and they
|
|
18
|
+
* are read seconds apart, so they CAN disagree without either being wrong.
|
|
19
|
+
*
|
|
20
|
+
* WHY EVERY ARM FAILURE IS CAUGHT, NOT ONLY THE RECOGNIZED REFUSAL. This
|
|
21
|
+
* delivery exists to remove the PRD frontier's dependence on DEPLOY ORDER, and
|
|
22
|
+
* the deploy-order window is exactly the window in which the refusal is NOT
|
|
23
|
+
* recognizable: with the migration live and the edge function not, the same
|
|
24
|
+
* refused arm comes back as a generic 500 `backend_error` carrying none of the
|
|
25
|
+
* database's text. Catching only the recognized shape would leave the tick
|
|
26
|
+
* aborting in precisely the window the catch was written for. So the frontier
|
|
27
|
+
* catches whatever the arm throws, records what it can name, and continues; the
|
|
28
|
+
* frontier recomputes every tick, so the arm is naturally re-attempted.
|
|
29
|
+
*
|
|
30
|
+
* WHERE THE RECORD GOES, AND WHY THERE. A `focus_debug_artifacts` section, one
|
|
31
|
+
* row per (focus, section_kind), rendered on the focus Debug tab with its own
|
|
32
|
+
* "Updated ..." stamp, read by NOTHING in the machine's decision path -- so
|
|
33
|
+
* recording a refusal cannot misroute a delivery -- and cleared by DELETING the
|
|
34
|
+
* row, which is exactly the shape "until an arm succeeds" wants. It is the same
|
|
35
|
+
* surface, for the same reasons, that `integration_sync` (20260920152723) and
|
|
36
|
+
* `directive_undelivered` (20260920202553) already use; the second of those was
|
|
37
|
+
* written because "the ONLY trace was a log warn" is how a focus goes quiet for
|
|
38
|
+
* 51 minutes with nobody able to learn what it was owed. A log warn alone would
|
|
39
|
+
* repeat that here: the operator who needs this fact is looking at a PRD whose
|
|
40
|
+
* frontier is not advancing, not at a daemon host's journal. The log warn stays
|
|
41
|
+
* as the FLOOR -- it is what a failed record degrades to.
|
|
42
|
+
*
|
|
43
|
+
* NOTHING HERE ESCALATES. A refused arm is a recoverable state the machine
|
|
44
|
+
* re-tries, not a question for a human queue: the fix is a restatement on a
|
|
45
|
+
* tree node, and the scoping agent the frontier re-spawns is the actor that
|
|
46
|
+
* writes it.
|
|
47
|
+
*/
|
|
48
|
+
import { callApi } from '../queries/shared.js';
|
|
49
|
+
import { createLogger } from '../shell/log.js';
|
|
50
|
+
const log = createLogger({ module: 'prd-controller' });
|
|
51
|
+
/** The wire code the product-api answers a refused arm with. */
|
|
52
|
+
export const ARM_CRITERIA_REQUIRED_CODE = 'arm_criteria_required';
|
|
53
|
+
/**
|
|
54
|
+
* The prefix the DATABASE declares as the contract (the prose after it is not).
|
|
55
|
+
* Matched as well as the wire code so a refusal that reaches us through a
|
|
56
|
+
* transport that dropped the code -- or an older edge that never set one -- is
|
|
57
|
+
* still recognized by the text the trigger wrote.
|
|
58
|
+
*/
|
|
59
|
+
export const ARM_CRITERIA_REQUIRED_PREFIX = 'arm_criteria_required:';
|
|
60
|
+
/** The focus_debug_artifacts section this record lives in. */
|
|
61
|
+
export const ARM_REFUSAL_SECTION_KIND = 'arm_refused';
|
|
62
|
+
/** The line carrying the first-observed timestamp across re-records. */
|
|
63
|
+
export const REFUSED_SINCE_PREFIX = 'Refused since: ';
|
|
64
|
+
/** The wire code an ApiError carries, when the thrown value is one. */
|
|
65
|
+
function readWireCode(err) {
|
|
66
|
+
const code = err?.wireCode;
|
|
67
|
+
return typeof code === 'string' && code.length > 0 ? code : null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Classify whatever `armFocus` threw. TOTAL: every input yields a record, so a
|
|
71
|
+
* caller never has to decide whether an unrecognized failure is reportable.
|
|
72
|
+
*/
|
|
73
|
+
export function classifyArmFailure(err) {
|
|
74
|
+
const message = err?.message ?? String(err);
|
|
75
|
+
const code = readWireCode(err);
|
|
76
|
+
const at = message.indexOf(ARM_CRITERIA_REQUIRED_PREFIX);
|
|
77
|
+
if (code === ARM_CRITERIA_REQUIRED_CODE || at !== -1) {
|
|
78
|
+
return {
|
|
79
|
+
kind: 'criteria_required',
|
|
80
|
+
code: code ?? ARM_CRITERIA_REQUIRED_CODE,
|
|
81
|
+
// Slice from the prefix when it is present: that drops any transport
|
|
82
|
+
// preamble while keeping every word the database wrote.
|
|
83
|
+
detail: at !== -1 ? message.slice(at) : message,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return { kind: 'unclassified', code, detail: message };
|
|
87
|
+
}
|
|
88
|
+
/** The `Refused since:` value in a stored artifact, or null when absent. */
|
|
89
|
+
export function parseRefusedSince(content) {
|
|
90
|
+
if (!content)
|
|
91
|
+
return null;
|
|
92
|
+
for (const line of content.split('\n')) {
|
|
93
|
+
if (line.startsWith(REFUSED_SINCE_PREFIX)) {
|
|
94
|
+
const value = line.slice(REFUSED_SINCE_PREFIX.length).trim();
|
|
95
|
+
if (value)
|
|
96
|
+
return value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Render the human-facing record. PURE.
|
|
103
|
+
*
|
|
104
|
+
* The first-observed timestamp is the one line a re-record must be able to find
|
|
105
|
+
* again, so it is emitted on its own line with a fixed prefix: the frontier
|
|
106
|
+
* re-attempts the arm every tick, so `updated_at` alone would always read "just
|
|
107
|
+
* now" and hide a frontier that has been refused for a day.
|
|
108
|
+
*/
|
|
109
|
+
export function renderArmRefusalArtifact(record, since) {
|
|
110
|
+
const { focusId, nodeId, prdId, prdTitle, failure, readinessReason, observedAt } = record;
|
|
111
|
+
const headline = failure.kind === 'criteria_required'
|
|
112
|
+
? 'Arm refused: this focus states no future to judge the work against'
|
|
113
|
+
: 'Arm failed: the focus could not be armed';
|
|
114
|
+
return [
|
|
115
|
+
`# ${headline}`,
|
|
116
|
+
'',
|
|
117
|
+
'The PRD frontier materialized this focus and tried to arm it. The write was',
|
|
118
|
+
'refused, so the focus stays materialized and UN-ARMED. Nothing is lost and',
|
|
119
|
+
'nothing is frozen: the frontier recomputes every tick and re-attempts the arm,',
|
|
120
|
+
'and this record clears the moment one lands.',
|
|
121
|
+
'',
|
|
122
|
+
`Refusal code: ${failure.code ?? '(none carried)'}`,
|
|
123
|
+
`${REFUSED_SINCE_PREFIX}${since}`,
|
|
124
|
+
`Last refused: ${observedAt}`,
|
|
125
|
+
`PRD: ${prdTitle} (${prdId})`,
|
|
126
|
+
`Overcome node: ${nodeId}`,
|
|
127
|
+
`Focus: ${focusId}`,
|
|
128
|
+
'',
|
|
129
|
+
'## What the database said',
|
|
130
|
+
'',
|
|
131
|
+
failure.detail,
|
|
132
|
+
'',
|
|
133
|
+
'## What the daemon thought before it asked',
|
|
134
|
+
'',
|
|
135
|
+
readinessReason,
|
|
136
|
+
].join('\n');
|
|
137
|
+
}
|
|
138
|
+
/** The first-observed timestamp already on record for this focus, or null. */
|
|
139
|
+
async function readRefusedSince(focusId, api) {
|
|
140
|
+
const result = await api('focus_debug_get', { focusId });
|
|
141
|
+
const section = (result?.sections ?? []).find(s => s.sectionKind === ARM_REFUSAL_SECTION_KIND);
|
|
142
|
+
return parseRefusedSince(section?.content);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Record a refused arm, carrying forward the first-observed timestamp when one
|
|
146
|
+
* is already on record.
|
|
147
|
+
*
|
|
148
|
+
* NEVER THROWS. The record is the operator's view of a waiting state, not a
|
|
149
|
+
* step in the tick: a recording failure (the section kind not yet deployed, the
|
|
150
|
+
* API down) must not cost the tick that survived the refusal. It degrades to
|
|
151
|
+
* the log warn, which carries the same code and node list.
|
|
152
|
+
*/
|
|
153
|
+
export async function recordArmRefusal(record, deps = {}) {
|
|
154
|
+
await recordArmRefusalSection({
|
|
155
|
+
focusId: record.focusId,
|
|
156
|
+
observedAt: record.observedAt,
|
|
157
|
+
detail: record.failure.detail,
|
|
158
|
+
render: (since) => renderArmRefusalArtifact(record, since),
|
|
159
|
+
}, deps);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Write the `arm_refused` section, carrying the first-observed timestamp
|
|
163
|
+
* forward. The IO half of the record, with the RENDERING left to the caller.
|
|
164
|
+
*
|
|
165
|
+
* Two crossings can be refused for stating no bar -- the role arm (the PRD
|
|
166
|
+
* frontier's, rendered above) and the audit focus's ratify crossing
|
|
167
|
+
* (`focus-stage-lifecycle/ratify-bar-gate.ts`) -- and they are refusals of
|
|
168
|
+
* DIFFERENT acts, so they read differently to an operator. What they share is
|
|
169
|
+
* the section they live in, the since-when line and the never-throws contract;
|
|
170
|
+
* that is what lives here, so there is one writer of the row rather than two
|
|
171
|
+
* that must be kept in step.
|
|
172
|
+
*
|
|
173
|
+
* NEVER THROWS. The record is the operator's view of a waiting state, not a
|
|
174
|
+
* step in the tick: a recording failure (the section kind not yet deployed, the
|
|
175
|
+
* API down) must not cost the tick that survived the refusal. It degrades to
|
|
176
|
+
* the log warn, which carries the same detail.
|
|
177
|
+
*/
|
|
178
|
+
export async function recordArmRefusalSection(args, deps = {}) {
|
|
179
|
+
const api = deps.callApi ?? callApi;
|
|
180
|
+
let since = null;
|
|
181
|
+
try {
|
|
182
|
+
since = await readRefusedSince(args.focusId, api);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// A failed read costs the ORIGINAL since-when, never the record itself.
|
|
186
|
+
since = null;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
await api('focus_debug_set', {
|
|
190
|
+
focusId: args.focusId,
|
|
191
|
+
fields: {
|
|
192
|
+
sectionKind: ARM_REFUSAL_SECTION_KIND,
|
|
193
|
+
content: args.render(since ?? args.observedAt),
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
catch (err) {
|
|
198
|
+
log.warn(`[prd-controller] could not record the arm refusal for focus ${args.focusId} ` +
|
|
199
|
+
`(${err.message}) -- the refusal itself: ${args.detail}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Clear the record. Idempotent (deleting a section that is not there is a
|
|
204
|
+
* no-op) and, like the record, never throws: a failed clear leaves a stale
|
|
205
|
+
* record on a focus that IS armed, which is a stale note -- not a wedge.
|
|
206
|
+
*/
|
|
207
|
+
export async function clearArmRefusal(focusId, deps = {}) {
|
|
208
|
+
const api = deps.callApi ?? callApi;
|
|
209
|
+
try {
|
|
210
|
+
await api('focus_debug_clear', {
|
|
211
|
+
focusId,
|
|
212
|
+
fields: { sectionKind: ARM_REFUSAL_SECTION_KIND },
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
catch (err) {
|
|
216
|
+
log.debug(`[prd-controller] could not clear the arm-refusal record for focus ${focusId}: ${err.message}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=arm-refusal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arm-refusal.js","sourceRoot":"","sources":["../../src/prd/arm-refusal.ts"],"names":[],"mappings":"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"}
|