@telora/daemon 0.17.59 → 0.17.70
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 +19 -3
- package/dist/cascade-restatement.d.ts +10 -0
- package/dist/cascade-restatement.d.ts.map +1 -1
- package/dist/cascade-restatement.js +107 -37
- package/dist/cascade-restatement.js.map +1 -1
- package/dist/ci-escalation.d.ts.map +1 -1
- package/dist/ci-escalation.js +5 -3
- package/dist/ci-escalation.js.map +1 -1
- package/dist/cli/backfill-frt-prose.d.ts +101 -0
- package/dist/cli/backfill-frt-prose.d.ts.map +1 -0
- package/dist/cli/backfill-frt-prose.js +195 -0
- package/dist/cli/backfill-frt-prose.js.map +1 -0
- package/dist/completion/event-escalations.d.ts +4 -11
- package/dist/completion/event-escalations.d.ts.map +1 -1
- package/dist/completion/event-escalations.js +21 -5
- package/dist/completion/event-escalations.js.map +1 -1
- package/dist/completion/event.d.ts +4 -11
- package/dist/completion/event.d.ts.map +1 -1
- package/dist/completion/event.js.map +1 -1
- package/dist/completion/review-exit-phase.d.ts.map +1 -1
- package/dist/completion/review-exit-phase.js +37 -7
- package/dist/completion/review-exit-phase.js.map +1 -1
- package/dist/completion/status-advance-phase.d.ts.map +1 -1
- package/dist/completion/status-advance-phase.js +46 -13
- package/dist/completion/status-advance-phase.js.map +1 -1
- package/dist/crash-recovery-cleanup.d.ts.map +1 -1
- package/dist/crash-recovery-cleanup.js +7 -1
- package/dist/crash-recovery-cleanup.js.map +1 -1
- package/dist/crash-recovery-types.d.ts +4 -9
- package/dist/crash-recovery-types.d.ts.map +1 -1
- package/dist/crash-recovery-types.js.map +1 -1
- package/dist/crash-recovery.d.ts +4 -9
- package/dist/crash-recovery.d.ts.map +1 -1
- package/dist/crash-recovery.js +5 -1
- package/dist/crash-recovery.js.map +1 -1
- package/dist/delivery-advance-block.d.ts +8 -6
- package/dist/delivery-advance-block.d.ts.map +1 -1
- package/dist/delivery-advance-block.js +14 -12
- package/dist/delivery-advance-block.js.map +1 -1
- package/dist/delivery-guards.d.ts.map +1 -1
- package/dist/delivery-guards.js +4 -1
- package/dist/delivery-guards.js.map +1 -1
- package/dist/delivery-lifecycle.d.ts.map +1 -1
- package/dist/delivery-lifecycle.js +47 -1
- package/dist/delivery-lifecycle.js.map +1 -1
- package/dist/delivery-merge.d.ts.map +1 -1
- package/dist/delivery-merge.js +5 -1
- package/dist/delivery-merge.js.map +1 -1
- package/dist/delivery-reconciler-apply.d.ts +85 -0
- package/dist/delivery-reconciler-apply.d.ts.map +1 -0
- package/dist/delivery-reconciler-apply.js +80 -0
- package/dist/delivery-reconciler-apply.js.map +1 -0
- package/dist/delivery-reconciler-shadow.d.ts +65 -0
- package/dist/delivery-reconciler-shadow.d.ts.map +1 -0
- package/dist/delivery-reconciler-shadow.js +127 -0
- package/dist/delivery-reconciler-shadow.js.map +1 -0
- package/dist/delivery-reconciler.d.ts +133 -0
- package/dist/delivery-reconciler.d.ts.map +1 -0
- package/dist/delivery-reconciler.js +160 -0
- package/dist/delivery-reconciler.js.map +1 -0
- package/dist/directive/directive-dispatch.d.ts.map +1 -1
- package/dist/directive/directive-dispatch.js +4 -1
- package/dist/directive/directive-dispatch.js.map +1 -1
- package/dist/escalation-provenance.d.ts +60 -0
- package/dist/escalation-provenance.d.ts.map +1 -0
- package/dist/escalation-provenance.js +153 -0
- package/dist/escalation-provenance.js.map +1 -0
- package/dist/escalations.d.ts +33 -3
- package/dist/escalations.d.ts.map +1 -1
- package/dist/escalations.js +58 -12
- package/dist/escalations.js.map +1 -1
- package/dist/focus-executor.js +1 -1
- package/dist/focus-executor.js.map +1 -1
- package/dist/focus-merge.d.ts.map +1 -1
- package/dist/focus-merge.js +3 -1
- package/dist/focus-merge.js.map +1 -1
- package/dist/focus-stage-lifecycle.d.ts.map +1 -1
- package/dist/focus-stage-lifecycle.js +5 -2
- package/dist/focus-stage-lifecycle.js.map +1 -1
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -1
- package/dist/listener-auto-advance.d.ts.map +1 -1
- package/dist/listener-auto-advance.js +73 -34
- package/dist/listener-auto-advance.js.map +1 -1
- package/dist/listener.d.ts.map +1 -1
- package/dist/listener.js +12 -0
- package/dist/listener.js.map +1 -1
- package/dist/merge-back-loop.d.ts.map +1 -1
- package/dist/merge-back-loop.js +7 -2
- package/dist/merge-back-loop.js.map +1 -1
- package/dist/nexus-escalation-bridge.d.ts +92 -0
- package/dist/nexus-escalation-bridge.d.ts.map +1 -0
- package/dist/nexus-escalation-bridge.js +109 -0
- package/dist/nexus-escalation-bridge.js.map +1 -0
- package/dist/nexus-incoming.d.ts +47 -0
- package/dist/nexus-incoming.d.ts.map +1 -0
- package/dist/nexus-incoming.js +93 -0
- package/dist/nexus-incoming.js.map +1 -0
- package/dist/nexus-return.d.ts +131 -0
- package/dist/nexus-return.d.ts.map +1 -0
- package/dist/nexus-return.js +326 -0
- package/dist/nexus-return.js.map +1 -0
- package/dist/queries/deliveries.d.ts +12 -0
- package/dist/queries/deliveries.d.ts.map +1 -1
- package/dist/queries/deliveries.js +17 -0
- package/dist/queries/deliveries.js.map +1 -1
- package/dist/queries/issues.d.ts +62 -12
- package/dist/queries/issues.d.ts.map +1 -1
- package/dist/queries/issues.js +46 -13
- package/dist/queries/issues.js.map +1 -1
- package/dist/resolvers/escalation-answers.d.ts +22 -0
- package/dist/resolvers/escalation-answers.d.ts.map +1 -0
- package/dist/resolvers/escalation-answers.js +98 -0
- package/dist/resolvers/escalation-answers.js.map +1 -0
- package/dist/resolvers/index.d.ts.map +1 -1
- package/dist/resolvers/index.js +2 -0
- package/dist/resolvers/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activation layer for the single-writer delivery reconciler.
|
|
3
|
+
*
|
|
4
|
+
* Delivery 4 of the Delivery Execution Pathway Consolidation focus (reality-tree
|
|
5
|
+
* injection #10). This is the step that makes the pure reconciler
|
|
6
|
+
* (delivery-reconciler.ts) the DECISION AUTHORITY for every daemon
|
|
7
|
+
* execution_status write, with the two safety properties the focus method
|
|
8
|
+
* (characterize -> shadow -> flip, reversible) requires:
|
|
9
|
+
*
|
|
10
|
+
* 1. Runtime self-check (replaces the manual prod shadow-observation gate).
|
|
11
|
+
* Each write site still computes what the legacy ("old path") logic would
|
|
12
|
+
* have written; `resolveReconciledWrite` compares that to the reconciler's
|
|
13
|
+
* target and logs `[reconciler-divergence]` on any mismatch. The retained
|
|
14
|
+
* legacy computation is exactly what lets prod self-verify the reconciler
|
|
15
|
+
* against the prior behavior for one release -- no maintenance-window or
|
|
16
|
+
* manual shadow observation needed.
|
|
17
|
+
*
|
|
18
|
+
* 2. Reversibility. A single switch, `TELORA_RECONCILER_WRITE`, DEFAULT-ON,
|
|
19
|
+
* selects whose decision is WRITTEN: ON -> the reconciler; OFF -> the
|
|
20
|
+
* retained legacy target. Flip to '0'/'off'/'false'/'no' (and restart) to
|
|
21
|
+
* revert instantly; the whole consolidation also reverts as one merge.
|
|
22
|
+
*
|
|
23
|
+
* Why activation is safe (suite stays green by construction): the reconciler was
|
|
24
|
+
* built (Delivery 3) to MIRROR the live writers and proven so by the Delivery 2
|
|
25
|
+
* parity tests. So in every characterized case `result.target === legacyTarget`
|
|
26
|
+
* -- the write is byte-identical and the characterization suite stays green. The
|
|
27
|
+
* self-check exists to catch any UN-characterized prod case where they differ.
|
|
28
|
+
*
|
|
29
|
+
* The single shared "issues are done" predicate (areDeliveryIssuesDone) and the
|
|
30
|
+
* agreed terminal set live in delivery-reconciler.ts. The agreed set is
|
|
31
|
+
* {Done, Verified, In Review} (OPEN = {To Do, In Progress, Blocked}); the
|
|
32
|
+
* divergent Postgres literal-Done predicate is removed in Delivery 5.
|
|
33
|
+
*/
|
|
34
|
+
import { type ReconcileInput, type ReconcileResult } from './delivery-reconciler.js';
|
|
35
|
+
import type { ExecutionStatus } from '@telora/daemon-core';
|
|
36
|
+
/** Reversibility switch. DEFAULT-ON (opt-out), unlike the shadow's opt-in flag. */
|
|
37
|
+
export declare const RECONCILER_WRITE_ENV = "TELORA_RECONCILER_WRITE";
|
|
38
|
+
/**
|
|
39
|
+
* Whether the reconciler's decision is the one WRITTEN. DEFAULT-ON: unset / ''
|
|
40
|
+
* / any value other than the explicit off-tokens enables it. Only
|
|
41
|
+
* '0'/'off'/'false'/'no' (case-insensitive) revert to writing the legacy target.
|
|
42
|
+
*
|
|
43
|
+
* This mirrors the opt-OUT semantics of shouldRunLoop (undefined/'' -> on,
|
|
44
|
+
* '0' -> off) rather than the shadow harness's opt-IN, because the reconciler is
|
|
45
|
+
* the intended default writer after this delivery -- reverting is the exception.
|
|
46
|
+
*/
|
|
47
|
+
export declare function reconcilerWriteEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
48
|
+
export interface ReconciledWriteDecision {
|
|
49
|
+
/**
|
|
50
|
+
* The status the caller should WRITE: the reconciler's target when the switch
|
|
51
|
+
* is ON (default), otherwise the retained legacy target.
|
|
52
|
+
*/
|
|
53
|
+
target: ExecutionStatus;
|
|
54
|
+
/** The reconciler's computed result -- always present, regardless of the switch. */
|
|
55
|
+
result: ReconcileResult;
|
|
56
|
+
/** Whether the reconciler's target differs from the legacy target. */
|
|
57
|
+
diverged: boolean;
|
|
58
|
+
/** Whether the reconciler's decision is the one being written (the switch state). */
|
|
59
|
+
reconcilerAuthoritative: boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface ResolveReconciledWriteParams {
|
|
62
|
+
/** Short site label for the divergence log (e.g. 'auto-advance', 'completion'). */
|
|
63
|
+
site: string;
|
|
64
|
+
deliveryId: string;
|
|
65
|
+
deliveryName: string;
|
|
66
|
+
/** The full signal for the reconciler. currentStatus is the status at THIS step. */
|
|
67
|
+
input: ReconcileInput;
|
|
68
|
+
/** What the legacy/old-path code decided to write for this same step. */
|
|
69
|
+
legacyTarget: ExecutionStatus;
|
|
70
|
+
}
|
|
71
|
+
export interface ReconciledWriteDeps {
|
|
72
|
+
/** Divergence sink. Defaults to console.log; injected in tests for capture. */
|
|
73
|
+
log?: (message: string) => void;
|
|
74
|
+
/** Switch state. Defaults to reconcilerWriteEnabled(); injected in tests. */
|
|
75
|
+
writeEnabled?: () => boolean;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Decide what status to write for one transition step, with the runtime
|
|
79
|
+
* self-check. Single-step: callers that walk multiple steps in one pass
|
|
80
|
+
* (e.g. queued -> coding -> verify) call this once per actual write, keying
|
|
81
|
+
* `input.currentStatus` + `legacyTarget` to that step, so the reconciler's
|
|
82
|
+
* single-step decision and the legacy step correspond 1:1.
|
|
83
|
+
*/
|
|
84
|
+
export declare function resolveReconciledWrite(params: ResolveReconciledWriteParams, deps?: ReconciledWriteDeps): ReconciledWriteDecision;
|
|
85
|
+
//# sourceMappingURL=delivery-reconciler-apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-reconciler-apply.d.ts","sourceRoot":"","sources":["../src/delivery-reconciler-apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,mFAAmF;AACnF,eAAO,MAAM,oBAAoB,4BAA4B,CAAC;AAE9D;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAMpF;AAED,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,MAAM,EAAE,eAAe,CAAC;IACxB,oFAAoF;IACpF,MAAM,EAAE,eAAe,CAAC;IACxB,sEAAsE;IACtE,QAAQ,EAAE,OAAO,CAAC;IAClB,qFAAqF;IACrF,uBAAuB,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,4BAA4B;IAC3C,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,KAAK,EAAE,cAAc,CAAC;IACtB,yEAAyE;IACzE,YAAY,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAmB;IAClC,+EAA+E;IAC/E,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,6EAA6E;IAC7E,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC;CAC9B;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,4BAA4B,EACpC,IAAI,GAAE,mBAAwB,GAC7B,uBAAuB,CAsBzB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Activation layer for the single-writer delivery reconciler.
|
|
3
|
+
*
|
|
4
|
+
* Delivery 4 of the Delivery Execution Pathway Consolidation focus (reality-tree
|
|
5
|
+
* injection #10). This is the step that makes the pure reconciler
|
|
6
|
+
* (delivery-reconciler.ts) the DECISION AUTHORITY for every daemon
|
|
7
|
+
* execution_status write, with the two safety properties the focus method
|
|
8
|
+
* (characterize -> shadow -> flip, reversible) requires:
|
|
9
|
+
*
|
|
10
|
+
* 1. Runtime self-check (replaces the manual prod shadow-observation gate).
|
|
11
|
+
* Each write site still computes what the legacy ("old path") logic would
|
|
12
|
+
* have written; `resolveReconciledWrite` compares that to the reconciler's
|
|
13
|
+
* target and logs `[reconciler-divergence]` on any mismatch. The retained
|
|
14
|
+
* legacy computation is exactly what lets prod self-verify the reconciler
|
|
15
|
+
* against the prior behavior for one release -- no maintenance-window or
|
|
16
|
+
* manual shadow observation needed.
|
|
17
|
+
*
|
|
18
|
+
* 2. Reversibility. A single switch, `TELORA_RECONCILER_WRITE`, DEFAULT-ON,
|
|
19
|
+
* selects whose decision is WRITTEN: ON -> the reconciler; OFF -> the
|
|
20
|
+
* retained legacy target. Flip to '0'/'off'/'false'/'no' (and restart) to
|
|
21
|
+
* revert instantly; the whole consolidation also reverts as one merge.
|
|
22
|
+
*
|
|
23
|
+
* Why activation is safe (suite stays green by construction): the reconciler was
|
|
24
|
+
* built (Delivery 3) to MIRROR the live writers and proven so by the Delivery 2
|
|
25
|
+
* parity tests. So in every characterized case `result.target === legacyTarget`
|
|
26
|
+
* -- the write is byte-identical and the characterization suite stays green. The
|
|
27
|
+
* self-check exists to catch any UN-characterized prod case where they differ.
|
|
28
|
+
*
|
|
29
|
+
* The single shared "issues are done" predicate (areDeliveryIssuesDone) and the
|
|
30
|
+
* agreed terminal set live in delivery-reconciler.ts. The agreed set is
|
|
31
|
+
* {Done, Verified, In Review} (OPEN = {To Do, In Progress, Blocked}); the
|
|
32
|
+
* divergent Postgres literal-Done predicate is removed in Delivery 5.
|
|
33
|
+
*/
|
|
34
|
+
import { reconcileDeliveryStatus, } from './delivery-reconciler.js';
|
|
35
|
+
/** Reversibility switch. DEFAULT-ON (opt-out), unlike the shadow's opt-in flag. */
|
|
36
|
+
export const RECONCILER_WRITE_ENV = 'TELORA_RECONCILER_WRITE';
|
|
37
|
+
/**
|
|
38
|
+
* Whether the reconciler's decision is the one WRITTEN. DEFAULT-ON: unset / ''
|
|
39
|
+
* / any value other than the explicit off-tokens enables it. Only
|
|
40
|
+
* '0'/'off'/'false'/'no' (case-insensitive) revert to writing the legacy target.
|
|
41
|
+
*
|
|
42
|
+
* This mirrors the opt-OUT semantics of shouldRunLoop (undefined/'' -> on,
|
|
43
|
+
* '0' -> off) rather than the shadow harness's opt-IN, because the reconciler is
|
|
44
|
+
* the intended default writer after this delivery -- reverting is the exception.
|
|
45
|
+
*/
|
|
46
|
+
export function reconcilerWriteEnabled(env = process.env) {
|
|
47
|
+
const raw = env[RECONCILER_WRITE_ENV];
|
|
48
|
+
if (raw === undefined)
|
|
49
|
+
return true;
|
|
50
|
+
const v = raw.trim().toLowerCase();
|
|
51
|
+
if (v === '')
|
|
52
|
+
return true;
|
|
53
|
+
return !['0', 'off', 'false', 'no'].includes(v);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Decide what status to write for one transition step, with the runtime
|
|
57
|
+
* self-check. Single-step: callers that walk multiple steps in one pass
|
|
58
|
+
* (e.g. queued -> coding -> verify) call this once per actual write, keying
|
|
59
|
+
* `input.currentStatus` + `legacyTarget` to that step, so the reconciler's
|
|
60
|
+
* single-step decision and the legacy step correspond 1:1.
|
|
61
|
+
*/
|
|
62
|
+
export function resolveReconciledWrite(params, deps = {}) {
|
|
63
|
+
const log = deps.log ?? ((m) => console.log(m));
|
|
64
|
+
const writeEnabled = (deps.writeEnabled ?? reconcilerWriteEnabled)();
|
|
65
|
+
const result = reconcileDeliveryStatus(params.input);
|
|
66
|
+
const diverged = result.target !== params.legacyTarget;
|
|
67
|
+
if (diverged) {
|
|
68
|
+
log(`[reconciler-divergence] site=${params.site} delivery=${params.deliveryId} ` +
|
|
69
|
+
`("${params.deliveryName}") legacy=${params.legacyTarget} ` +
|
|
70
|
+
`reconciled=${result.target} canonical=${result.canonical} ` +
|
|
71
|
+
`reason="${result.reason}"`);
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
target: writeEnabled ? result.target : params.legacyTarget,
|
|
75
|
+
result,
|
|
76
|
+
diverged,
|
|
77
|
+
reconcilerAuthoritative: writeEnabled,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=delivery-reconciler-apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-reconciler-apply.js","sourceRoot":"","sources":["../src/delivery-reconciler-apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EACL,uBAAuB,GAGxB,MAAM,0BAA0B,CAAC;AAGlC,mFAAmF;AACnF,MAAM,CAAC,MAAM,oBAAoB,GAAG,yBAAyB,CAAC;AAE9D;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAAyB,OAAO,CAAC,GAAG;IACzE,MAAM,GAAG,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC;IACtC,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAClD,CAAC;AAkCD;;;;;;GAMG;AACH,MAAM,UAAU,sBAAsB,CACpC,MAAoC,EACpC,OAA4B,EAAE;IAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,MAAM,YAAY,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,sBAAsB,CAAC,EAAE,CAAC;IAErE,MAAM,MAAM,GAAG,uBAAuB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,YAAY,CAAC;IAEvD,IAAI,QAAQ,EAAE,CAAC;QACb,GAAG,CACD,gCAAgC,MAAM,CAAC,IAAI,aAAa,MAAM,CAAC,UAAU,GAAG;YAC1E,KAAK,MAAM,CAAC,YAAY,aAAa,MAAM,CAAC,YAAY,GAAG;YAC3D,cAAc,MAAM,CAAC,MAAM,cAAc,MAAM,CAAC,SAAS,GAAG;YAC5D,WAAW,MAAM,CAAC,MAAM,GAAG,CAC9B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY;QAC1D,MAAM;QACN,QAAQ;QACR,uBAAuB,EAAE,YAAY;KACtC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow harness for the single-writer delivery reconciler.
|
|
3
|
+
*
|
|
4
|
+
* Delivery 3 (single-writer reconciler, shadow mode) of the Delivery Execution
|
|
5
|
+
* Pathway Consolidation focus (reality-tree injection #10). De-risking method:
|
|
6
|
+
* characterize -> SHADOW -> flip one transition at a time. This is the shadow
|
|
7
|
+
* step.
|
|
8
|
+
*
|
|
9
|
+
* On each poll, for every active focus's deliveries, this gathers the live
|
|
10
|
+
* signal (issue rollup, worktree-committed flag), runs the PURE
|
|
11
|
+
* reconcileDeliveryStatus(), compares its computed target to the delivery's
|
|
12
|
+
* LIVE execution_status, and LOGS every divergence. It makes ZERO writes to
|
|
13
|
+
* execution_status -- there is no status writer in its dependency surface by
|
|
14
|
+
* construction. When divergence holds steady at zero, the reconciler is safe to
|
|
15
|
+
* flip into the write path one transition at a time (Delivery 4).
|
|
16
|
+
*
|
|
17
|
+
* Default-OFF: unlike the focus/verification loops (opt-OUT via shouldRunLoop),
|
|
18
|
+
* the shadow is opt-IN. It runs only when TELORA_RECONCILER_SHADOW is a truthy
|
|
19
|
+
* value ('1'/'true'/etc); unset / '' / '0' keep it a no-op, so wiring it into
|
|
20
|
+
* the live poll changes nothing until an operator deliberately enables it.
|
|
21
|
+
*
|
|
22
|
+
* Poll-time signal note: at poll time there is no exiting review session, so the
|
|
23
|
+
* harness supplies a conservative review state (a verify delivery with no open
|
|
24
|
+
* work stays in verify -- matching what the live poll does; verify->done is
|
|
25
|
+
* driven by the review-exit path, not the poll). The transitions the shadow
|
|
26
|
+
* meaningfully exercises at poll time are the issue-rollup-driven ones
|
|
27
|
+
* (queued/coding/verify_failed) and verify->verify_failed on open work.
|
|
28
|
+
*/
|
|
29
|
+
import type { DaemonConfig } from './types.js';
|
|
30
|
+
import { getActiveFocuses, getDeliveryIssues } from './supabase.js';
|
|
31
|
+
import { getFocusDeliveries } from './queries/focuses.js';
|
|
32
|
+
import { getFocusWorktree } from './focus-worktree-state.js';
|
|
33
|
+
import { worktreeHasUncommittedChanges } from './worktree-safety.js';
|
|
34
|
+
/** Opt-in flag name. Default-OFF (only a truthy value enables the shadow). */
|
|
35
|
+
export declare const RECONCILER_SHADOW_ENV = "TELORA_RECONCILER_SHADOW";
|
|
36
|
+
/** Default-off opt-in: only '1'/'true'/'yes'/'on' (case-insensitive) enable. */
|
|
37
|
+
export declare function reconcilerShadowEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Injectable collaborators. The surface is READ-ONLY by design: there is NO
|
|
40
|
+
* status-writer dependency, which is the structural guarantee that the shadow
|
|
41
|
+
* cannot mutate execution_status.
|
|
42
|
+
*/
|
|
43
|
+
export interface ReconcilerShadowDeps {
|
|
44
|
+
isEnabled: () => boolean;
|
|
45
|
+
getActiveFocuses: typeof getActiveFocuses;
|
|
46
|
+
getFocusDeliveries: typeof getFocusDeliveries;
|
|
47
|
+
getDeliveryIssues: typeof getDeliveryIssues;
|
|
48
|
+
getFocusWorktree: typeof getFocusWorktree;
|
|
49
|
+
worktreeHasUncommittedChanges: typeof worktreeHasUncommittedChanges;
|
|
50
|
+
/** Divergence sink. Defaults to console.log; injected in tests for capture. */
|
|
51
|
+
log: (message: string) => void;
|
|
52
|
+
}
|
|
53
|
+
export interface ReconcilerShadowSummary {
|
|
54
|
+
/** Whether the shadow actually ran (false when the flag is off). */
|
|
55
|
+
ran: boolean;
|
|
56
|
+
/** Number of deliveries whose live status was compared. */
|
|
57
|
+
checked: number;
|
|
58
|
+
/** Number where the reconciled target differed from the live status. */
|
|
59
|
+
divergences: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Run one shadow pass for a (product-scoped) config. Reads only; never writes.
|
|
63
|
+
*/
|
|
64
|
+
export declare function runReconcilerShadow(config: DaemonConfig, deps?: ReconcilerShadowDeps): Promise<ReconcilerShadowSummary>;
|
|
65
|
+
//# sourceMappingURL=delivery-reconciler-shadow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-reconciler-shadow.d.ts","sourceRoot":"","sources":["../src/delivery-reconciler-shadow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AAKrE,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,6BAA6B,CAAC;AAEhE,gFAAgF;AAChF,wBAAgB,uBAAuB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAIrF;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,OAAO,CAAC;IACzB,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,kBAAkB,EAAE,OAAO,kBAAkB,CAAC;IAC9C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,gBAAgB,EAAE,OAAO,gBAAgB,CAAC;IAC1C,6BAA6B,EAAE,OAAO,6BAA6B,CAAC;IACpE,+EAA+E;IAC/E,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAYD,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,GAAG,EAAE,OAAO,CAAC;IACb,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,YAAY,EACpB,IAAI,GAAE,oBAAkC,GACvC,OAAO,CAAC,uBAAuB,CAAC,CA4ElC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow harness for the single-writer delivery reconciler.
|
|
3
|
+
*
|
|
4
|
+
* Delivery 3 (single-writer reconciler, shadow mode) of the Delivery Execution
|
|
5
|
+
* Pathway Consolidation focus (reality-tree injection #10). De-risking method:
|
|
6
|
+
* characterize -> SHADOW -> flip one transition at a time. This is the shadow
|
|
7
|
+
* step.
|
|
8
|
+
*
|
|
9
|
+
* On each poll, for every active focus's deliveries, this gathers the live
|
|
10
|
+
* signal (issue rollup, worktree-committed flag), runs the PURE
|
|
11
|
+
* reconcileDeliveryStatus(), compares its computed target to the delivery's
|
|
12
|
+
* LIVE execution_status, and LOGS every divergence. It makes ZERO writes to
|
|
13
|
+
* execution_status -- there is no status writer in its dependency surface by
|
|
14
|
+
* construction. When divergence holds steady at zero, the reconciler is safe to
|
|
15
|
+
* flip into the write path one transition at a time (Delivery 4).
|
|
16
|
+
*
|
|
17
|
+
* Default-OFF: unlike the focus/verification loops (opt-OUT via shouldRunLoop),
|
|
18
|
+
* the shadow is opt-IN. It runs only when TELORA_RECONCILER_SHADOW is a truthy
|
|
19
|
+
* value ('1'/'true'/etc); unset / '' / '0' keep it a no-op, so wiring it into
|
|
20
|
+
* the live poll changes nothing until an operator deliberately enables it.
|
|
21
|
+
*
|
|
22
|
+
* Poll-time signal note: at poll time there is no exiting review session, so the
|
|
23
|
+
* harness supplies a conservative review state (a verify delivery with no open
|
|
24
|
+
* work stays in verify -- matching what the live poll does; verify->done is
|
|
25
|
+
* driven by the review-exit path, not the poll). The transitions the shadow
|
|
26
|
+
* meaningfully exercises at poll time are the issue-rollup-driven ones
|
|
27
|
+
* (queued/coding/verify_failed) and verify->verify_failed on open work.
|
|
28
|
+
*/
|
|
29
|
+
import { getActiveFocuses, getDeliveryIssues } from './supabase.js';
|
|
30
|
+
import { getFocusDeliveries } from './queries/focuses.js';
|
|
31
|
+
import { getFocusWorktree } from './focus-worktree-state.js';
|
|
32
|
+
import { worktreeHasUncommittedChanges } from './worktree-safety.js';
|
|
33
|
+
import { reconcileDeliveryStatus } from './delivery-reconciler.js';
|
|
34
|
+
import { isExecutionStatus } from '@telora/daemon-core';
|
|
35
|
+
/** Opt-in flag name. Default-OFF (only a truthy value enables the shadow). */
|
|
36
|
+
export const RECONCILER_SHADOW_ENV = 'TELORA_RECONCILER_SHADOW';
|
|
37
|
+
/** Default-off opt-in: only '1'/'true'/'yes'/'on' (case-insensitive) enable. */
|
|
38
|
+
export function reconcilerShadowEnabled(env = process.env) {
|
|
39
|
+
const raw = env[RECONCILER_SHADOW_ENV];
|
|
40
|
+
if (raw === undefined)
|
|
41
|
+
return false;
|
|
42
|
+
return ['1', 'true', 'yes', 'on'].includes(raw.trim().toLowerCase());
|
|
43
|
+
}
|
|
44
|
+
const defaultDeps = {
|
|
45
|
+
isEnabled: () => reconcilerShadowEnabled(),
|
|
46
|
+
getActiveFocuses,
|
|
47
|
+
getFocusDeliveries,
|
|
48
|
+
getDeliveryIssues,
|
|
49
|
+
getFocusWorktree,
|
|
50
|
+
worktreeHasUncommittedChanges,
|
|
51
|
+
log: (m) => console.log(m),
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Run one shadow pass for a (product-scoped) config. Reads only; never writes.
|
|
55
|
+
*/
|
|
56
|
+
export async function runReconcilerShadow(config, deps = defaultDeps) {
|
|
57
|
+
if (!deps.isEnabled()) {
|
|
58
|
+
return { ran: false, checked: 0, divergences: 0 };
|
|
59
|
+
}
|
|
60
|
+
let checked = 0;
|
|
61
|
+
let divergences = 0;
|
|
62
|
+
let focuses;
|
|
63
|
+
try {
|
|
64
|
+
focuses = await deps.getActiveFocuses(config.organizationId, config.productId);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
deps.log(`[reconciler-shadow] failed to fetch active focuses: ${err.message}`);
|
|
68
|
+
return { ran: true, checked, divergences };
|
|
69
|
+
}
|
|
70
|
+
for (const focus of focuses) {
|
|
71
|
+
const reviewRequested = !!focus.review_requested_at;
|
|
72
|
+
let deliveries;
|
|
73
|
+
try {
|
|
74
|
+
deliveries = await deps.getFocusDeliveries(focus.focus_id);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
deps.log(`[reconciler-shadow] failed to fetch deliveries for focus ${focus.focus_id}: ${err.message}`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
// Resolve the worktree-committed signal once per focus (one worktree per focus).
|
|
81
|
+
const worktree = deps.getFocusWorktree(focus.focus_id);
|
|
82
|
+
let worktreeCommitted = true; // no worktree => nothing uncommitted can block (matches the advance precheck)
|
|
83
|
+
if (worktree) {
|
|
84
|
+
try {
|
|
85
|
+
worktreeCommitted = !deps.worktreeHasUncommittedChanges(worktree.worktreePath);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Best-effort: a git failure should not break the shadow; assume committed.
|
|
89
|
+
worktreeCommitted = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const delivery of deliveries) {
|
|
93
|
+
const liveStatus = delivery.executionStatus ?? '';
|
|
94
|
+
if (!isExecutionStatus(liveStatus))
|
|
95
|
+
continue;
|
|
96
|
+
let issues;
|
|
97
|
+
try {
|
|
98
|
+
issues = await deps.getDeliveryIssues(delivery.id);
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
deps.log(`[reconciler-shadow] failed to fetch issues for delivery ${delivery.id}: ${err.message}`);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
const result = reconcileDeliveryStatus({
|
|
105
|
+
currentStatus: liveStatus,
|
|
106
|
+
issues: issues.map(i => ({ status: i.status, issueType: i.issueType })),
|
|
107
|
+
gitState: { worktreeCommitted },
|
|
108
|
+
guardVerdict: null,
|
|
109
|
+
// Poll-time review state: conservative. verify with no open work stays
|
|
110
|
+
// verify (verify->done is review-exit-driven, not poll-driven).
|
|
111
|
+
reviewState: { reviewRequested, reportExists: false, sessionType: 'coding', succeeded: false },
|
|
112
|
+
});
|
|
113
|
+
checked++;
|
|
114
|
+
if (result.changed) {
|
|
115
|
+
divergences++;
|
|
116
|
+
deps.log(`[reconciler-shadow] DIVERGENCE delivery=${delivery.id} ("${delivery.name}") ` +
|
|
117
|
+
`live=${liveStatus} reconciled=${result.target} ` +
|
|
118
|
+
`canonical=${result.canonical} reason="${result.reason}" (no write performed)`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (checked > 0) {
|
|
123
|
+
deps.log(`[reconciler-shadow] pass complete: ${checked} deliveries checked, ${divergences} divergence(s), 0 writes`);
|
|
124
|
+
}
|
|
125
|
+
return { ran: true, checked, divergences };
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=delivery-reconciler-shadow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-reconciler-shadow.js","sourceRoot":"","sources":["../src/delivery-reconciler-shadow.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single-writer delivery reconciler (PURE).
|
|
3
|
+
*
|
|
4
|
+
* Delivery 3 of the Delivery Execution Pathway Consolidation focus
|
|
5
|
+
* (reality-tree injection #10: "Delivery execution_status has exactly one
|
|
6
|
+
* writer -- the daemon reconciler; every transition flows through it").
|
|
7
|
+
*
|
|
8
|
+
* This module is the consolidation's center of gravity: ONE pure function that
|
|
9
|
+
* computes the single next execution_status the daemon should write, plus ONE
|
|
10
|
+
* shared "issues are done" predicate that replaces the divergent copies spread
|
|
11
|
+
* across the live writers (advanceDeliveryStage / advanceDeliveryStatuses /
|
|
12
|
+
* runReviewExitHandler / checkDeliveryAutoAdvance / checkVerifyDeliveriesForOpenIssues).
|
|
13
|
+
*
|
|
14
|
+
* It is deliberately PURE -- no I/O, no supabase, no git. Callers gather the
|
|
15
|
+
* signal (issues, worktree-committed flag, guard verdict, review state) and
|
|
16
|
+
* pass it in; the reconciler only computes. This is what makes it
|
|
17
|
+
* shadow-testable: run it alongside the live path, compare its target to the
|
|
18
|
+
* live status, log divergence, write nothing (see delivery-reconciler-shadow.ts).
|
|
19
|
+
*
|
|
20
|
+
* The behavior here MIRRORS the CURRENT live writers as pinned by the
|
|
21
|
+
* Delivery 2 characterization suite
|
|
22
|
+
* (__tests__/delivery-state-machine-characterization.test.ts). It characterizes
|
|
23
|
+
* what the system does today, not an idealized target -- the shadow phase exists
|
|
24
|
+
* precisely to prove parity before any cutover.
|
|
25
|
+
*
|
|
26
|
+
* The coding -> queued re-queue edge (open work at team exit -> re-queue) is
|
|
27
|
+
* present in EXECUTION_STATUS_TRANSITIONS as of the D5 cleanup. `result.canonical`
|
|
28
|
+
* flags transitions NOT in the canonical map; the coding -> queued edge now
|
|
29
|
+
* resolves to `canonical=true`.
|
|
30
|
+
*/
|
|
31
|
+
import type { ExecutionStatus } from '@telora/daemon-core';
|
|
32
|
+
/** Minimal issue shape the reconciler needs. */
|
|
33
|
+
export interface ReconcilerIssue {
|
|
34
|
+
status: string;
|
|
35
|
+
issueType: string;
|
|
36
|
+
}
|
|
37
|
+
/** Git-safety signal: the irreducible worktree-commit gate. */
|
|
38
|
+
export interface ReconcilerGitState {
|
|
39
|
+
/**
|
|
40
|
+
* Whether the focus worktree has NO uncommitted changes. The daemon is the
|
|
41
|
+
* only place this can be checked (Postgres has no git), which is the core
|
|
42
|
+
* reason the daemon -- not a PG trigger -- must own the coding -> verify write.
|
|
43
|
+
* When false, an otherwise-ready coding delivery is HELD in coding.
|
|
44
|
+
*/
|
|
45
|
+
worktreeCommitted: boolean;
|
|
46
|
+
}
|
|
47
|
+
/** Verify-gate verdict (from the guard engine). null = no guard evaluated. */
|
|
48
|
+
export interface ReconcilerGuardVerdict {
|
|
49
|
+
/** True when a hard guard blocked the transition -> delivery pauses. */
|
|
50
|
+
blocked: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** Review-window signal used to route verify -> done. */
|
|
53
|
+
export interface ReconcilerReviewState {
|
|
54
|
+
/** Whether the focus currently has review_requested_at set. */
|
|
55
|
+
reviewRequested: boolean;
|
|
56
|
+
/** Whether an explicit focus_reviews report exists for the review session. */
|
|
57
|
+
reportExists: boolean;
|
|
58
|
+
/** The exiting session's type. */
|
|
59
|
+
sessionType: 'coding' | 'review';
|
|
60
|
+
/** Whether that session exited successfully. */
|
|
61
|
+
succeeded: boolean;
|
|
62
|
+
}
|
|
63
|
+
export interface ReconcileInput {
|
|
64
|
+
currentStatus: ExecutionStatus;
|
|
65
|
+
issues: ReadonlyArray<ReconcilerIssue>;
|
|
66
|
+
gitState: ReconcilerGitState;
|
|
67
|
+
guardVerdict?: ReconcilerGuardVerdict | null;
|
|
68
|
+
reviewState?: ReconcilerReviewState | null;
|
|
69
|
+
}
|
|
70
|
+
export interface ReconcileResult {
|
|
71
|
+
/** The execution_status the daemon should write (equal to currentStatus when no change). */
|
|
72
|
+
target: ExecutionStatus;
|
|
73
|
+
/** Whether target differs from currentStatus. */
|
|
74
|
+
changed: boolean;
|
|
75
|
+
/** Human-readable rationale (for shadow divergence logs + debugging). */
|
|
76
|
+
reason: string;
|
|
77
|
+
/**
|
|
78
|
+
* Whether (currentStatus -> target) is present in the canonical
|
|
79
|
+
* EXECUTION_STATUS_TRANSITIONS map. Always true for a no-op (stay). False
|
|
80
|
+
* flags a live-vs-canonical gap (notably coding -> queued); see the module
|
|
81
|
+
* header.
|
|
82
|
+
*/
|
|
83
|
+
canonical: boolean;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* THE shared "issues are done" predicate. A delivery's work is done when none
|
|
87
|
+
* of its work issues (Tasks/Bugs, excluding Context Groups) are in an OPEN
|
|
88
|
+
* status. OPEN = {To Do, In Progress, Blocked}; 'In Review' and 'Verified' are
|
|
89
|
+
* terminal here -- this is the daemon predicate, deliberately chosen as the
|
|
90
|
+
* single source of truth over the Postgres auto_close predicate (which requires
|
|
91
|
+
* every issue to be literally 'Done'). A delivery with zero work issues is
|
|
92
|
+
* vacuously done.
|
|
93
|
+
*
|
|
94
|
+
* Replaces the previously-divergent inline copies in advanceDeliveryStatuses
|
|
95
|
+
* (`!TERMINAL_ISSUE_STATUSES.has`), listener-auto-advance (`OPEN_ISSUE_STATUSES`),
|
|
96
|
+
* and review-exit (`ACTIVE_WORK_STATUSES`) -- all three sets have identical
|
|
97
|
+
* membership; this names the predicate once.
|
|
98
|
+
*/
|
|
99
|
+
export declare function areDeliveryIssuesDone(issues: ReadonlyArray<ReconcilerIssue>): boolean;
|
|
100
|
+
/** Whether the delivery has at least one work issue (Task/Bug). */
|
|
101
|
+
export declare function hasWorkIssues(issues: ReadonlyArray<ReconcilerIssue>): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Whether any work issue is in an OPEN (active) status. This is the routing
|
|
104
|
+
* signal for verify -> verify_failed (open work on a verify delivery) and the
|
|
105
|
+
* coding/verify_failed re-queue (open work re-queues). Mirror image of
|
|
106
|
+
* areDeliveryIssuesDone for deliveries that have work.
|
|
107
|
+
*/
|
|
108
|
+
export declare function hasOpenWork(issues: ReadonlyArray<ReconcilerIssue>): boolean;
|
|
109
|
+
/** Whether any work issue is actively In Progress -- the queued -> coding driver. */
|
|
110
|
+
export declare function hasActiveWork(issues: ReadonlyArray<ReconcilerIssue>): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* Compute the single next execution_status for a delivery from its full signal.
|
|
113
|
+
*
|
|
114
|
+
* Faithful to the current live writers (Delivery 2 characterization):
|
|
115
|
+
* - queued : has work + all done -> coding (step toward verify);
|
|
116
|
+
* has work + active -> coding (work started);
|
|
117
|
+
* otherwise -> stay (scoping gate / not started).
|
|
118
|
+
* - coding : open work -> queued (re-queue);
|
|
119
|
+
* all done + uncommitted worktree -> stay coding (git-safety gate);
|
|
120
|
+
* all done + committed + guard blocked -> paused;
|
|
121
|
+
* all done + committed -> verify.
|
|
122
|
+
* - verify : open work -> verify_failed;
|
|
123
|
+
* guard blocked -> paused;
|
|
124
|
+
* report exists -> done;
|
|
125
|
+
* clean review session -> done (auto-approve);
|
|
126
|
+
* otherwise -> stay verify (awaiting review outcome).
|
|
127
|
+
* - verify_failed : open work -> queued (re-queue);
|
|
128
|
+
* all done -> coding (re-iterate).
|
|
129
|
+
* - draft/planning/paused/done/cancelled : stay (out of reconciler scope;
|
|
130
|
+
* paused is cleared by a human re-queue, terminal states are final).
|
|
131
|
+
*/
|
|
132
|
+
export declare function reconcileDeliveryStatus(input: ReconcileInput): ReconcileResult;
|
|
133
|
+
//# sourceMappingURL=delivery-reconciler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-reconciler.d.ts","sourceRoot":"","sources":["../src/delivery-reconciler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,gDAAgD;AAChD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,+DAA+D;AAC/D,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED,8EAA8E;AAC9E,MAAM,WAAW,sBAAsB;IACrC,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,yDAAyD;AACzD,MAAM,WAAW,qBAAqB;IACpC,+DAA+D;IAC/D,eAAe,EAAE,OAAO,CAAC;IACzB,8EAA8E;IAC9E,YAAY,EAAE,OAAO,CAAC;IACtB,kCAAkC;IAClC,WAAW,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACjC,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,EAAE,eAAe,CAAC;IAC/B,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IACvC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,YAAY,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC7C,WAAW,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;CAC5C;AAED,MAAM,WAAW,eAAe;IAC9B,4FAA4F;IAC5F,MAAM,EAAE,eAAe,CAAC;IACxB,iDAAiD;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAID;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,OAAO,CAIrF;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,OAAO,CAE7E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,OAAO,CAE3E;AAED,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,OAAO,CAE7E;AAcD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,CA8D9E"}
|