@bluewombat/runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +473 -0
- package/SPECS.md +303 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +172 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/defaults.d.ts +17 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +17 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/find-config.d.ts +9 -0
- package/dist/config/find-config.d.ts.map +1 -0
- package/dist/config/find-config.js +25 -0
- package/dist/config/find-config.js.map +1 -0
- package/dist/config/home.d.ts +17 -0
- package/dist/config/home.d.ts.map +1 -0
- package/dist/config/home.js +23 -0
- package/dist/config/home.js.map +1 -0
- package/dist/config/load-config.d.ts +13 -0
- package/dist/config/load-config.d.ts.map +1 -0
- package/dist/config/load-config.js +537 -0
- package/dist/config/load-config.js.map +1 -0
- package/dist/config/parse-args.d.ts +25 -0
- package/dist/config/parse-args.d.ts.map +1 -0
- package/dist/config/parse-args.js +478 -0
- package/dist/config/parse-args.js.map +1 -0
- package/dist/config/types.d.ts +144 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/loop/authority.d.ts +51 -0
- package/dist/loop/authority.d.ts.map +1 -0
- package/dist/loop/authority.js +166 -0
- package/dist/loop/authority.js.map +1 -0
- package/dist/loop/context.d.ts +44 -0
- package/dist/loop/context.d.ts.map +1 -0
- package/dist/loop/context.js +2 -0
- package/dist/loop/context.js.map +1 -0
- package/dist/loop/cursor.d.ts +4 -0
- package/dist/loop/cursor.d.ts.map +1 -0
- package/dist/loop/cursor.js +20 -0
- package/dist/loop/cursor.js.map +1 -0
- package/dist/loop/deliveries.d.ts +22 -0
- package/dist/loop/deliveries.d.ts.map +1 -0
- package/dist/loop/deliveries.js +240 -0
- package/dist/loop/deliveries.js.map +1 -0
- package/dist/loop/describe.d.ts +40 -0
- package/dist/loop/describe.d.ts.map +1 -0
- package/dist/loop/describe.js +48 -0
- package/dist/loop/describe.js.map +1 -0
- package/dist/loop/drive.d.ts +15 -0
- package/dist/loop/drive.d.ts.map +1 -0
- package/dist/loop/drive.js +42 -0
- package/dist/loop/drive.js.map +1 -0
- package/dist/loop/journal.d.ts +22 -0
- package/dist/loop/journal.d.ts.map +1 -0
- package/dist/loop/journal.js +49 -0
- package/dist/loop/journal.js.map +1 -0
- package/dist/loop/lock.d.ts +11 -0
- package/dist/loop/lock.d.ts.map +1 -0
- package/dist/loop/lock.js +60 -0
- package/dist/loop/lock.js.map +1 -0
- package/dist/loop/message.d.ts +34 -0
- package/dist/loop/message.d.ts.map +1 -0
- package/dist/loop/message.js +48 -0
- package/dist/loop/message.js.map +1 -0
- package/dist/loop/open-host.d.ts +13 -0
- package/dist/loop/open-host.d.ts.map +1 -0
- package/dist/loop/open-host.js +180 -0
- package/dist/loop/open-host.js.map +1 -0
- package/dist/loop/probe.d.ts +9 -0
- package/dist/loop/probe.d.ts.map +1 -0
- package/dist/loop/probe.js +57 -0
- package/dist/loop/probe.js.map +1 -0
- package/dist/loop/report.d.ts +26 -0
- package/dist/loop/report.d.ts.map +1 -0
- package/dist/loop/report.js +328 -0
- package/dist/loop/report.js.map +1 -0
- package/dist/loop/tick.d.ts +24 -0
- package/dist/loop/tick.d.ts.map +1 -0
- package/dist/loop/tick.js +169 -0
- package/dist/loop/tick.js.map +1 -0
- package/dist/loop/trace.d.ts +16 -0
- package/dist/loop/trace.d.ts.map +1 -0
- package/dist/loop/trace.js +49 -0
- package/dist/loop/trace.js.map +1 -0
- package/dist/loop/transformers.d.ts +27 -0
- package/dist/loop/transformers.d.ts.map +1 -0
- package/dist/loop/transformers.js +229 -0
- package/dist/loop/transformers.js.map +1 -0
- package/dist/loop/work-line.d.ts +73 -0
- package/dist/loop/work-line.d.ts.map +1 -0
- package/dist/loop/work-line.js +109 -0
- package/dist/loop/work-line.js.map +1 -0
- package/dist/operator/board.d.ts +31 -0
- package/dist/operator/board.d.ts.map +1 -0
- package/dist/operator/board.js +67 -0
- package/dist/operator/board.js.map +1 -0
- package/dist/operator/cancel.d.ts +19 -0
- package/dist/operator/cancel.d.ts.map +1 -0
- package/dist/operator/cancel.js +70 -0
- package/dist/operator/cancel.js.map +1 -0
- package/dist/operator/doctor.d.ts +15 -0
- package/dist/operator/doctor.d.ts.map +1 -0
- package/dist/operator/doctor.js +416 -0
- package/dist/operator/doctor.js.map +1 -0
- package/dist/operator/git-remote.d.ts +39 -0
- package/dist/operator/git-remote.d.ts.map +1 -0
- package/dist/operator/git-remote.js +78 -0
- package/dist/operator/git-remote.js.map +1 -0
- package/dist/operator/init.d.ts +28 -0
- package/dist/operator/init.d.ts.map +1 -0
- package/dist/operator/init.js +522 -0
- package/dist/operator/init.js.map +1 -0
- package/dist/operator/live.d.ts +19 -0
- package/dist/operator/live.d.ts.map +1 -0
- package/dist/operator/live.js +149 -0
- package/dist/operator/live.js.map +1 -0
- package/dist/operator/prompt.d.ts +35 -0
- package/dist/operator/prompt.d.ts.map +1 -0
- package/dist/operator/prompt.js +125 -0
- package/dist/operator/prompt.js.map +1 -0
- package/dist/operator/setup.d.ts +18 -0
- package/dist/operator/setup.d.ts.map +1 -0
- package/dist/operator/setup.js +62 -0
- package/dist/operator/setup.js.map +1 -0
- package/dist/plugins/discover.d.ts +13 -0
- package/dist/plugins/discover.d.ts.map +1 -0
- package/dist/plugins/discover.js +81 -0
- package/dist/plugins/discover.js.map +1 -0
- package/dist/plugins/isolation.d.ts +78 -0
- package/dist/plugins/isolation.d.ts.map +1 -0
- package/dist/plugins/isolation.js +64 -0
- package/dist/plugins/isolation.js.map +1 -0
- package/dist/plugins/load.d.ts +35 -0
- package/dist/plugins/load.d.ts.map +1 -0
- package/dist/plugins/load.js +109 -0
- package/dist/plugins/load.js.map +1 -0
- package/dist/plugins/manager.d.ts +43 -0
- package/dist/plugins/manager.d.ts.map +1 -0
- package/dist/plugins/manager.js +47 -0
- package/dist/plugins/manager.js.map +1 -0
- package/dist/plugins/persist.d.ts +33 -0
- package/dist/plugins/persist.d.ts.map +1 -0
- package/dist/plugins/persist.js +26 -0
- package/dist/plugins/persist.js.map +1 -0
- package/package.json +66 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export { CONFIG_FILENAME, findConfig } from "./config/find-config.js";
|
|
2
|
+
export type { ParseInput, ParseResult } from "./config/parse-args.js";
|
|
3
|
+
export { parseArgs } from "./config/parse-args.js";
|
|
4
|
+
export type { HostInvocation, HostOptions } from "./config/types.js";
|
|
5
|
+
export type { CancelResult, Host, HostTickResult, OpenHostDeps } from "./loop/open-host.js";
|
|
6
|
+
export { openHost } from "./loop/open-host.js";
|
|
7
|
+
export type { TransformerSlots } from "./loop/transformers.js";
|
|
8
|
+
export { createTransformers } from "./loop/transformers.js";
|
|
9
|
+
export type { WorkLineState } from "./loop/work-line.js";
|
|
10
|
+
export { inspectWorkLine } from "./loop/work-line.js";
|
|
11
|
+
export type { CancelInput } from "./operator/cancel.js";
|
|
12
|
+
export { runCancel } from "./operator/cancel.js";
|
|
13
|
+
export type { DoctorInput } from "./operator/doctor.js";
|
|
14
|
+
export { runDoctor } from "./operator/doctor.js";
|
|
15
|
+
export { cloneWorkLine, remoteBranches } from "./operator/git-remote.js";
|
|
16
|
+
export type { InitInput, InitResult } from "./operator/init.js";
|
|
17
|
+
export { runInit } from "./operator/init.js";
|
|
18
|
+
export type { LiveInput } from "./operator/live.js";
|
|
19
|
+
export { runLive } from "./operator/live.js";
|
|
20
|
+
export type { Ask } from "./operator/prompt.js";
|
|
21
|
+
export { isInteractive, terminalAsk } from "./operator/prompt.js";
|
|
22
|
+
export type { SetupInput } from "./operator/setup.js";
|
|
23
|
+
export { runSetup } from "./operator/setup.js";
|
|
24
|
+
export type { DiscoveredManager } from "./plugins/discover.js";
|
|
25
|
+
export { discoverManagers } from "./plugins/discover.js";
|
|
26
|
+
export type { ResolveIsolationResult } from "./plugins/isolation.js";
|
|
27
|
+
export { resolveIsolationStrategy } from "./plugins/isolation.js";
|
|
28
|
+
export type { LoadedModule, ManagerRequest, ResolveResult } from "./plugins/manager.js";
|
|
29
|
+
export { loadManagerModule, openManager, resolveManagerModule } from "./plugins/manager.js";
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACtE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACzE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAC7C,YAAY,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAClE,YAAY,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { CONFIG_FILENAME, findConfig } from "./config/find-config.js";
|
|
2
|
+
export { parseArgs } from "./config/parse-args.js";
|
|
3
|
+
export { openHost } from "./loop/open-host.js";
|
|
4
|
+
export { createTransformers } from "./loop/transformers.js";
|
|
5
|
+
export { inspectWorkLine } from "./loop/work-line.js";
|
|
6
|
+
export { runCancel } from "./operator/cancel.js";
|
|
7
|
+
export { runDoctor } from "./operator/doctor.js";
|
|
8
|
+
export { cloneWorkLine, remoteBranches } from "./operator/git-remote.js";
|
|
9
|
+
export { runInit } from "./operator/init.js";
|
|
10
|
+
export { runLive } from "./operator/live.js";
|
|
11
|
+
export { isInteractive, terminalAsk } from "./operator/prompt.js";
|
|
12
|
+
export { runSetup } from "./operator/setup.js";
|
|
13
|
+
export { discoverManagers } from "./plugins/discover.js";
|
|
14
|
+
export { resolveIsolationStrategy } from "./plugins/isolation.js";
|
|
15
|
+
export { loadManagerModule, openManager, resolveManagerModule } from "./plugins/manager.js";
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEtE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGnD,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAEzE,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAElE,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAElE,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { AuthorityPort } from "@bluewombat/conductor";
|
|
2
|
+
import type { ManagerPort } from "@bluewombat/manager-kit";
|
|
3
|
+
import type { PassSpec } from "../config/types.js";
|
|
4
|
+
import type { Journal } from "./journal.js";
|
|
5
|
+
/**
|
|
6
|
+
* The Authority, as Host wires it.
|
|
7
|
+
*
|
|
8
|
+
* Two halves that do not belong to the same world: publishing the work is an
|
|
9
|
+
* operation on a directory Host owns, and offering it to the outside belongs to
|
|
10
|
+
* the FeatureManager. Host runs the first as a slot it never reads into, then
|
|
11
|
+
* hands the second the name that slot published under. Neither of them judges:
|
|
12
|
+
* that is a Gate.
|
|
13
|
+
*
|
|
14
|
+
* The Publisher is a slot for the same reason the Builder is one: `git push` is
|
|
15
|
+
* one way to put work in front of a judge, not the only one, and a Project that
|
|
16
|
+
* places its work some other way changes that command and nothing else.
|
|
17
|
+
*/
|
|
18
|
+
export declare function openAuthority(input: {
|
|
19
|
+
manager: ManagerPort;
|
|
20
|
+
workLineStable: string;
|
|
21
|
+
workLineTarget: string;
|
|
22
|
+
publishArgv: string[];
|
|
23
|
+
timeoutMs: number;
|
|
24
|
+
/** The isolation strategy's name for a Child, from its id. */
|
|
25
|
+
refOf: (id: string) => string;
|
|
26
|
+
/** The work line's git environment (identity, credentials), for the Publisher. */
|
|
27
|
+
env?: Record<string, string> | undefined;
|
|
28
|
+
/** How the work describes itself, when the Project has a slot for it. */
|
|
29
|
+
describe?: PassSpec | undefined;
|
|
30
|
+
workspaceRoot?: string | undefined;
|
|
31
|
+
journal?: Journal | undefined;
|
|
32
|
+
}): AuthorityPort | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Bring the work line copy up to what the Authority holds, through the slot the
|
|
35
|
+
* Project named.
|
|
36
|
+
*
|
|
37
|
+
* The other half of the same seam as the Publisher: one puts the work where the
|
|
38
|
+
* judge reads, the other reads back what the judge accepted. `git fetch` is one
|
|
39
|
+
* way to do that, not the only one, so neither belongs in Host.
|
|
40
|
+
*/
|
|
41
|
+
export declare function refreshWorkLine(input: {
|
|
42
|
+
workLineStable: string;
|
|
43
|
+
workLineTarget: string;
|
|
44
|
+
refreshArgv: string[];
|
|
45
|
+
timeoutMs: number;
|
|
46
|
+
env?: Record<string, string> | undefined;
|
|
47
|
+
}): {
|
|
48
|
+
ok: boolean;
|
|
49
|
+
detail?: string;
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=authority.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authority.d.ts","sourceRoot":"","sources":["../../src/loop/authority.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,WAAW,EAAqB,MAAM,yBAAyB,CAAC;AAC9E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE;IACnC,OAAO,EAAE,WAAW,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IAC9B,kFAAkF;IAClF,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACzC,yEAAyE;IACzE,QAAQ,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC/B,GAAG,aAAa,GAAG,SAAS,CAyD5B;AA2ED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;CAC1C,GAAG;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAwBnC"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { featureWorkspacePath, subjectOf } from "@bluewombat/conductor";
|
|
3
|
+
import { describe } from "./describe.js";
|
|
4
|
+
/**
|
|
5
|
+
* The Authority, as Host wires it.
|
|
6
|
+
*
|
|
7
|
+
* Two halves that do not belong to the same world: publishing the work is an
|
|
8
|
+
* operation on a directory Host owns, and offering it to the outside belongs to
|
|
9
|
+
* the FeatureManager. Host runs the first as a slot it never reads into, then
|
|
10
|
+
* hands the second the name that slot published under. Neither of them judges:
|
|
11
|
+
* that is a Gate.
|
|
12
|
+
*
|
|
13
|
+
* The Publisher is a slot for the same reason the Builder is one: `git push` is
|
|
14
|
+
* one way to put work in front of a judge, not the only one, and a Project that
|
|
15
|
+
* places its work some other way changes that command and nothing else.
|
|
16
|
+
*/
|
|
17
|
+
export function openAuthority(input) {
|
|
18
|
+
const { manager, workLineStable, workLineTarget, publishArgv, timeoutMs, refOf } = input;
|
|
19
|
+
if (manager.submit === undefined || manager.fold === undefined) {
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
const submit = manager.submit.bind(manager);
|
|
23
|
+
const fold = manager.fold.bind(manager);
|
|
24
|
+
return {
|
|
25
|
+
async submit(offer) {
|
|
26
|
+
// The name the strategy gave this feature's Child. What the Publisher
|
|
27
|
+
// does with it — push it, copy it, upload it — is the Publisher's business.
|
|
28
|
+
const ref = refOf(offer.ref);
|
|
29
|
+
const published = publish({
|
|
30
|
+
argv: publishArgv,
|
|
31
|
+
cwd: workLineStable,
|
|
32
|
+
id: offer.key,
|
|
33
|
+
ref,
|
|
34
|
+
target: workLineTarget,
|
|
35
|
+
timeoutMs,
|
|
36
|
+
env: input.env,
|
|
37
|
+
});
|
|
38
|
+
if (!published.ok) {
|
|
39
|
+
return { outcome: "refused", reason: published.reason };
|
|
40
|
+
}
|
|
41
|
+
// The Project's own words for what it submits, when it has a slot for
|
|
42
|
+
// them; the Authority puts them where its kind of Submission is read.
|
|
43
|
+
// Asked in the feature workspace, where the whole change is.
|
|
44
|
+
let description;
|
|
45
|
+
if (input.describe !== undefined && input.workspaceRoot !== undefined) {
|
|
46
|
+
const asked = describe(input.describe, {
|
|
47
|
+
id: offer.key,
|
|
48
|
+
title: offer.title ?? offer.key,
|
|
49
|
+
intention: offer.intention ?? "",
|
|
50
|
+
target: workLineTarget,
|
|
51
|
+
cwd: featureWorkspacePath(input.workspaceRoot, offer.key),
|
|
52
|
+
});
|
|
53
|
+
if (asked.ok) {
|
|
54
|
+
description = asked.description;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
input.journal?.append({ event: "describe-skipped", id: offer.key, detail: asked.detail });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return await submit({
|
|
61
|
+
...offer,
|
|
62
|
+
ref: published.ref,
|
|
63
|
+
target: workLineTarget,
|
|
64
|
+
// What the Submission lists as its work: one line per Subtask, the way
|
|
65
|
+
// the plan was said on the tracker, not the whole intentions.
|
|
66
|
+
...(offer.steps === undefined ? {} : { steps: offer.steps.map(subjectOf) }),
|
|
67
|
+
...(description === undefined ? {} : { description }),
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
async fold(asked) {
|
|
71
|
+
return await fold(asked);
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Run the Publisher slot in the work line and read the one line it answers.
|
|
77
|
+
*
|
|
78
|
+
* Same contract as every other slot: argv in, one JSON object on stdout. A slot
|
|
79
|
+
* that cannot run at all is a refusal like any other here — the Submission does
|
|
80
|
+
* not happen, the reason reaches the tracker, and the next pass tries again.
|
|
81
|
+
*/
|
|
82
|
+
function publish(input) {
|
|
83
|
+
const [command, ...rest] = input.argv;
|
|
84
|
+
if (command === undefined) {
|
|
85
|
+
return { ok: false, reason: "No Publisher is configured, so nothing can be submitted." };
|
|
86
|
+
}
|
|
87
|
+
const run = spawnSync(command, [...rest, "--id", input.id, "--ref", input.ref, "--target", input.target], {
|
|
88
|
+
cwd: input.cwd,
|
|
89
|
+
encoding: "utf8",
|
|
90
|
+
timeout: input.timeoutMs,
|
|
91
|
+
env: { ...process.env, ...input.env },
|
|
92
|
+
});
|
|
93
|
+
if (run.error !== undefined) {
|
|
94
|
+
return { ok: false, reason: `Publisher could not run: ${run.error.message}` };
|
|
95
|
+
}
|
|
96
|
+
const answer = lastJsonLine(run.stdout ?? "");
|
|
97
|
+
if (answer === undefined) {
|
|
98
|
+
const detail = `${run.stderr ?? ""}`.trim().slice(-1000);
|
|
99
|
+
return {
|
|
100
|
+
ok: false,
|
|
101
|
+
reason: `Publisher stdout is not a JSON object.${detail.length > 0 ? ` ${detail}` : ""}`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (typeof answer.ref === "string" && answer.ref.length > 0) {
|
|
105
|
+
return { ok: true, ref: answer.ref };
|
|
106
|
+
}
|
|
107
|
+
if (answer.outcome === "refused") {
|
|
108
|
+
const reason = typeof answer.reason === "string" ? answer.reason : "no reason given";
|
|
109
|
+
return { ok: false, reason };
|
|
110
|
+
}
|
|
111
|
+
return { ok: false, reason: "Publisher answered neither a reference nor a refusal." };
|
|
112
|
+
}
|
|
113
|
+
function lastJsonLine(stdout) {
|
|
114
|
+
const lines = stdout.trim().split("\n");
|
|
115
|
+
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
|
116
|
+
const line = lines[i]?.trim();
|
|
117
|
+
if (line === undefined || line.length === 0) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
try {
|
|
121
|
+
const parsed = JSON.parse(line);
|
|
122
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
123
|
+
return parsed;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Not the contract line. A slot may write anything else to stderr, but
|
|
128
|
+
// stdout noise is its own bug — keep looking for a line that parses.
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Bring the work line copy up to what the Authority holds, through the slot the
|
|
135
|
+
* Project named.
|
|
136
|
+
*
|
|
137
|
+
* The other half of the same seam as the Publisher: one puts the work where the
|
|
138
|
+
* judge reads, the other reads back what the judge accepted. `git fetch` is one
|
|
139
|
+
* way to do that, not the only one, so neither belongs in Host.
|
|
140
|
+
*/
|
|
141
|
+
export function refreshWorkLine(input) {
|
|
142
|
+
const [command, ...rest] = input.refreshArgv;
|
|
143
|
+
if (command === undefined) {
|
|
144
|
+
return { ok: false, detail: "No Refresher is configured." };
|
|
145
|
+
}
|
|
146
|
+
const run = spawnSync(command, [...rest, "--target", input.workLineTarget], {
|
|
147
|
+
cwd: input.workLineStable,
|
|
148
|
+
encoding: "utf8",
|
|
149
|
+
timeout: input.timeoutMs,
|
|
150
|
+
env: { ...process.env, ...input.env },
|
|
151
|
+
});
|
|
152
|
+
if (run.error !== undefined) {
|
|
153
|
+
return { ok: false, detail: `Refresher could not run: ${run.error.message}` };
|
|
154
|
+
}
|
|
155
|
+
const answer = lastJsonLine(run.stdout ?? "");
|
|
156
|
+
if (answer === undefined) {
|
|
157
|
+
const detail = `${run.stderr ?? ""}`.trim().slice(-500);
|
|
158
|
+
return { ok: false, detail: `Refresher stdout is not a JSON object. ${detail}`.trim() };
|
|
159
|
+
}
|
|
160
|
+
if (answer.ok === true) {
|
|
161
|
+
return { ok: true };
|
|
162
|
+
}
|
|
163
|
+
const reason = typeof answer.reason === "string" ? answer.reason : "no reason given";
|
|
164
|
+
return { ok: false, detail: reason };
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=authority.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authority.js","sourceRoot":"","sources":["../../src/loop/authority.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AuthorityPort, Conductor, ConductorMoment } from "@bluewombat/conductor";
|
|
2
|
+
import type { EventName, ManagerPort } from "@bluewombat/manager-kit";
|
|
3
|
+
import type { WorkLedger } from "@bluewombat/work-ledger";
|
|
4
|
+
import type { HostOptions } from "../config/types.js";
|
|
5
|
+
import type { Journal } from "./journal.js";
|
|
6
|
+
import type { Trace } from "./trace.js";
|
|
7
|
+
/**
|
|
8
|
+
* What one Host tick needs after composition. Shared so tick, deliveries and
|
|
9
|
+
* report do not import the factory.
|
|
10
|
+
*/
|
|
11
|
+
export type HostRunInput = {
|
|
12
|
+
options: HostOptions;
|
|
13
|
+
/** The work line as resolved at boot: the directory, and the name work is offered to. */
|
|
14
|
+
workLine: {
|
|
15
|
+
stable: string;
|
|
16
|
+
target?: string;
|
|
17
|
+
env?: Record<string, string> | undefined;
|
|
18
|
+
};
|
|
19
|
+
ledger: WorkLedger;
|
|
20
|
+
conductor: Conductor;
|
|
21
|
+
manager: ManagerPort;
|
|
22
|
+
journal: Journal;
|
|
23
|
+
trace: Trace;
|
|
24
|
+
/** Present when the Project declared an Authority and the manager can submit. */
|
|
25
|
+
authority?: AuthorityPort;
|
|
26
|
+
/**
|
|
27
|
+
* Where Conductor's moments land during a pass. A tick points it at its own
|
|
28
|
+
* report function before driving, so what is said mid-pass counts as that
|
|
29
|
+
* tick's Events; between ticks nobody listens.
|
|
30
|
+
*/
|
|
31
|
+
watcher: {
|
|
32
|
+
current?: ((moment: ConductorMoment) => Promise<void>) | undefined;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Event ids this process already reported. A fact said once is not said
|
|
36
|
+
* again by this process; the manager's own memory covers a restart.
|
|
37
|
+
*/
|
|
38
|
+
said: Set<string>;
|
|
39
|
+
};
|
|
40
|
+
/** One Delivery (or sweep) within a tick: the run input plus Events collected so far. */
|
|
41
|
+
export type HostDeliveryInput = HostRunInput & {
|
|
42
|
+
reported: EventName[];
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/loop/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,WAAW,CAAC;IACrB,yFAAyF;IACzF,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAA;KAAE,CAAC;IACxF,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;IACb,iFAAiF;IACjF,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B;;;;OAIG;IACH,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,SAAS,CAAA;KAAE,CAAC;IAChF;;;OAGG;IACH,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACnB,CAAC;AAEF,yFAAyF;AACzF,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG;IAC7C,QAAQ,EAAE,SAAS,EAAE,CAAC;CACvB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/loop/context.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function cursorPath(ledgerRoot: string): string;
|
|
2
|
+
export declare function loadCursor(ledgerRoot: string): Promise<string | undefined>;
|
|
3
|
+
export declare function saveCursor(ledgerRoot: string, cursor: string): Promise<void>;
|
|
4
|
+
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/loop/cursor.ts"],"names":[],"mappings":"AAGA,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAOhF;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAIlF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
export function cursorPath(ledgerRoot) {
|
|
4
|
+
return join(ledgerRoot, "cursor");
|
|
5
|
+
}
|
|
6
|
+
export async function loadCursor(ledgerRoot) {
|
|
7
|
+
try {
|
|
8
|
+
const text = (await readFile(cursorPath(ledgerRoot), "utf8")).trim();
|
|
9
|
+
return text.length > 0 ? text : undefined;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export async function saveCursor(ledgerRoot, cursor) {
|
|
16
|
+
const path = cursorPath(ledgerRoot);
|
|
17
|
+
await mkdir(dirname(path), { recursive: true });
|
|
18
|
+
await writeFile(path, `${cursor}\n`, "utf8");
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/loop/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,OAAO,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAkB;IACjD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACrE,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAkB,EAAE,MAAc;IACjE,MAAM,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;IACpC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,MAAM,IAAI,EAAE,MAAM,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ProjectRunResult } from "@bluewombat/conductor";
|
|
2
|
+
import type { HostDeliveryInput, HostRunInput } from "./context.js";
|
|
3
|
+
export declare function handleDelivery(input: HostDeliveryInput & {
|
|
4
|
+
payload: Record<string, unknown>;
|
|
5
|
+
}): Promise<{
|
|
6
|
+
advance: boolean;
|
|
7
|
+
run?: ProjectRunResult;
|
|
8
|
+
project?: string;
|
|
9
|
+
}>;
|
|
10
|
+
export declare function handleCancel(feature: {
|
|
11
|
+
key: string;
|
|
12
|
+
project: string;
|
|
13
|
+
}, input: HostDeliveryInput): Promise<void>;
|
|
14
|
+
export type CancelResult = {
|
|
15
|
+
ok: true;
|
|
16
|
+
} | {
|
|
17
|
+
ok: false;
|
|
18
|
+
code: "not-found" | "point-of-no-return" | "already-cancelled" | "done";
|
|
19
|
+
};
|
|
20
|
+
/** Abandon one Feature by key — the same path a `cancel` delivery takes. */
|
|
21
|
+
export declare function cancelFeature(input: HostRunInput, key: string): Promise<CancelResult>;
|
|
22
|
+
//# sourceMappingURL=deliveries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliveries.d.ts","sourceRoot":"","sources":["../../src/loop/deliveries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAG9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAqBpE,wBAAsB,cAAc,CAClC,KAAK,EAAE,iBAAiB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC9D,OAAO,CAAC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,gBAAgB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA8DzE;AAuFD,wBAAsB,YAAY,CAChC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACzC,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,IAAI,CAAC,CAuCf;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GACZ;IACE,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,WAAW,GAAG,oBAAoB,GAAG,mBAAmB,GAAG,MAAM,CAAC;CACzE,CAAC;AAEN,4EAA4E;AAC5E,wBAAsB,aAAa,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAkB3F"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { driveProject } from "./drive.js";
|
|
2
|
+
import { pushReport, resumePointOf } from "./report.js";
|
|
3
|
+
/**
|
|
4
|
+
* An upsert leaves Features that ready / cancel / too-late already own.
|
|
5
|
+
* SPECS §6: leave in-flight / terminal (including submitted); do not restart.
|
|
6
|
+
* The ledger still records a *new* fingerprint as pending_fingerprint; Host
|
|
7
|
+
* does not admit here, so an echo of Submitted cannot look like one.
|
|
8
|
+
*/
|
|
9
|
+
const IN_FLIGHT_OR_TERMINAL = new Set([
|
|
10
|
+
"planning",
|
|
11
|
+
"running",
|
|
12
|
+
"escalated",
|
|
13
|
+
"integrating",
|
|
14
|
+
"submitted",
|
|
15
|
+
"merging",
|
|
16
|
+
"done",
|
|
17
|
+
"cancelled",
|
|
18
|
+
]);
|
|
19
|
+
export async function handleDelivery(input) {
|
|
20
|
+
const adapted = await input.manager.adapt(input.payload);
|
|
21
|
+
switch (adapted.outcome) {
|
|
22
|
+
case "unavailable":
|
|
23
|
+
case "interrupted":
|
|
24
|
+
return { advance: false };
|
|
25
|
+
case "invalid": {
|
|
26
|
+
// Telling the tracker writes to it, and that write delivers the same
|
|
27
|
+
// intention again. What a human wrote has not changed: said once.
|
|
28
|
+
const before = await input.ledger.get(adapted.key);
|
|
29
|
+
if (before.ok &&
|
|
30
|
+
before.aggregate.state === "invalid" &&
|
|
31
|
+
before.aggregate.intention.fingerprint === adapted.fingerprint) {
|
|
32
|
+
input.journal.append({ event: "skipped", key: adapted.key, state: "invalid" });
|
|
33
|
+
return { advance: true };
|
|
34
|
+
}
|
|
35
|
+
await input.ledger.recordInvalid({
|
|
36
|
+
key: adapted.key,
|
|
37
|
+
project: adapted.project,
|
|
38
|
+
code: adapted.invalid.code,
|
|
39
|
+
reason: adapted.invalid.reason,
|
|
40
|
+
fingerprint: adapted.fingerprint,
|
|
41
|
+
});
|
|
42
|
+
input.journal.append({
|
|
43
|
+
event: "invalid",
|
|
44
|
+
key: adapted.key,
|
|
45
|
+
reason: adapted.invalid.reason,
|
|
46
|
+
});
|
|
47
|
+
await pushReport(input, {
|
|
48
|
+
event: "invalid",
|
|
49
|
+
key: adapted.key,
|
|
50
|
+
project: adapted.project,
|
|
51
|
+
// One per intention as written: an edit is a new fingerprint and is said again.
|
|
52
|
+
eventId: `${adapted.key}:invalid:${adapted.fingerprint}`,
|
|
53
|
+
fields: { reason: adapted.invalid.reason },
|
|
54
|
+
});
|
|
55
|
+
return { advance: true };
|
|
56
|
+
}
|
|
57
|
+
case "converted":
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
const feature = adapted.feature;
|
|
61
|
+
// Poll snapshots have no labeled edge: a ready signal on an escalated upsert
|
|
62
|
+
// is the same instruction as an explicit ready intent.
|
|
63
|
+
if (feature.intent === "upsert" && input.manager.signalsReady?.(input.payload) === true) {
|
|
64
|
+
const before = await input.ledger.get(feature.key);
|
|
65
|
+
if (before.ok && before.aggregate.state === "escalated") {
|
|
66
|
+
return await handleReady(feature, input);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
switch (feature.intent) {
|
|
70
|
+
case "ready":
|
|
71
|
+
return await handleReady(feature, input);
|
|
72
|
+
case "cancel":
|
|
73
|
+
await handleCancel(feature, input);
|
|
74
|
+
return { advance: true };
|
|
75
|
+
case "upsert":
|
|
76
|
+
return await handleUpsert(feature, input);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function handleUpsert(feature, input) {
|
|
80
|
+
const admission = toAdmission(feature);
|
|
81
|
+
if (admission === undefined) {
|
|
82
|
+
return { advance: true };
|
|
83
|
+
}
|
|
84
|
+
const before = await input.ledger.get(admission.key);
|
|
85
|
+
if (before.ok && IN_FLIGHT_OR_TERMINAL.has(before.aggregate.state)) {
|
|
86
|
+
input.journal.append({
|
|
87
|
+
event: "skipped",
|
|
88
|
+
key: admission.key,
|
|
89
|
+
state: before.aggregate.state,
|
|
90
|
+
});
|
|
91
|
+
return { advance: true };
|
|
92
|
+
}
|
|
93
|
+
const admitted = await input.ledger.admit(admission);
|
|
94
|
+
if (!admitted.ok) {
|
|
95
|
+
if (admitted.code === "too-late" || admitted.code === "point-of-no-return") {
|
|
96
|
+
await pushReport(input, {
|
|
97
|
+
event: "reject_late",
|
|
98
|
+
key: admission.key,
|
|
99
|
+
project: admission.project,
|
|
100
|
+
// One per intention: the same edit delivered again is the echo of
|
|
101
|
+
// this report, not another edit.
|
|
102
|
+
eventId: `${admission.key}:reject_late:${admission.fingerprint}`,
|
|
103
|
+
fields: { reason: `Admission refused: ${admitted.code}` },
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return { advance: true };
|
|
107
|
+
}
|
|
108
|
+
if (!before.ok || before.aggregate.state === "invalid") {
|
|
109
|
+
input.journal.append({ event: "admitted", key: admission.key, project: admission.project });
|
|
110
|
+
await pushReport(input, {
|
|
111
|
+
event: "accepted",
|
|
112
|
+
key: admission.key,
|
|
113
|
+
project: admission.project,
|
|
114
|
+
// One per intention as written, like `invalid`: a corrected issue is accepted again.
|
|
115
|
+
eventId: `${admission.key}:accepted:${admission.fingerprint}`,
|
|
116
|
+
fields: { priority: admission.priority },
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
const run = await driveProject(input, admission.project, admission.key);
|
|
120
|
+
if (run === undefined) {
|
|
121
|
+
// Frozen Project: do not mark it driven, so a later delivery in this pass
|
|
122
|
+
// (a cancel, a ready) can free it and the sweep can start the queue.
|
|
123
|
+
return { advance: true };
|
|
124
|
+
}
|
|
125
|
+
return { advance: true, run, project: admission.project };
|
|
126
|
+
}
|
|
127
|
+
async function handleReady(feature, input) {
|
|
128
|
+
const before = await input.ledger.get(feature.key);
|
|
129
|
+
if (!before.ok || before.aggregate.state !== "escalated") {
|
|
130
|
+
return { advance: true };
|
|
131
|
+
}
|
|
132
|
+
const resumePoint = resumePointOf(before.aggregate);
|
|
133
|
+
const ready = await input.ledger.resumeReady(feature.key);
|
|
134
|
+
if (!ready.ok) {
|
|
135
|
+
return { advance: true };
|
|
136
|
+
}
|
|
137
|
+
// The signal is an instruction, not a state: left where it is, it resumes the
|
|
138
|
+
// feature again on every pass and an escalation never freezes anything.
|
|
139
|
+
await input.manager.clearReady?.(feature.key);
|
|
140
|
+
// The ledger counts the rounds; this resume is the latest one.
|
|
141
|
+
const resumed = await input.ledger.get(feature.key);
|
|
142
|
+
const round = resumed.ok ? (resumed.aggregate.resumes ?? 0) : 0;
|
|
143
|
+
await pushReport(input, {
|
|
144
|
+
event: "resumed",
|
|
145
|
+
key: feature.key,
|
|
146
|
+
project: feature.project,
|
|
147
|
+
eventId: `${feature.key}:resumed:${round}`,
|
|
148
|
+
fields: { resume_point: resumePoint },
|
|
149
|
+
});
|
|
150
|
+
const run = await driveProject(input, feature.project, feature.key);
|
|
151
|
+
if (run === undefined) {
|
|
152
|
+
return { advance: true };
|
|
153
|
+
}
|
|
154
|
+
return { advance: true, run, project: feature.project };
|
|
155
|
+
}
|
|
156
|
+
export async function handleCancel(feature, input) {
|
|
157
|
+
const before = await input.ledger.get(feature.key);
|
|
158
|
+
if (!before.ok) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const state = before.aggregate.state;
|
|
162
|
+
if (state === "cancelled") {
|
|
163
|
+
// The ledger takes a second cancel as done already; the tracker was told
|
|
164
|
+
// the first time, and that is what delivered this one.
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (state === "done") {
|
|
168
|
+
// A finished feature's issue closing is the tracker agreeing, not a
|
|
169
|
+
// cancel arriving late — the Submission itself asked for that close.
|
|
170
|
+
input.journal.append({ event: "skipped", key: feature.key, state });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
const cancelled = await input.conductor.cancel(feature.key);
|
|
174
|
+
if (!cancelled.ok) {
|
|
175
|
+
if (cancelled.code === "point-of-no-return") {
|
|
176
|
+
await pushReport(input, {
|
|
177
|
+
event: "reject_late",
|
|
178
|
+
key: feature.key,
|
|
179
|
+
project: feature.project,
|
|
180
|
+
// A closed issue stays closed: every later delivery is the same cancel.
|
|
181
|
+
eventId: `${feature.key}:reject_late:cancel`,
|
|
182
|
+
fields: { reason: "Cancel arrived after the point of no return." },
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
await pushReport(input, {
|
|
188
|
+
event: "cancelled",
|
|
189
|
+
key: feature.key,
|
|
190
|
+
project: feature.project,
|
|
191
|
+
// A Feature is cancelled once: the ledger refuses a second cancel and never re-admits the key.
|
|
192
|
+
eventId: `${feature.key}:cancelled`,
|
|
193
|
+
fields: { state },
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/** Abandon one Feature by key — the same path a `cancel` delivery takes. */
|
|
197
|
+
export async function cancelFeature(input, key) {
|
|
198
|
+
const got = await input.ledger.get(key);
|
|
199
|
+
if (!got.ok) {
|
|
200
|
+
return { ok: false, code: "not-found" };
|
|
201
|
+
}
|
|
202
|
+
const state = got.aggregate.state;
|
|
203
|
+
if (state === "cancelled") {
|
|
204
|
+
return { ok: false, code: "already-cancelled" };
|
|
205
|
+
}
|
|
206
|
+
if (state === "done") {
|
|
207
|
+
return { ok: false, code: "done" };
|
|
208
|
+
}
|
|
209
|
+
await handleCancel({ key, project: got.aggregate.intention.project }, { ...input, reported: [] });
|
|
210
|
+
const after = await input.ledger.get(key);
|
|
211
|
+
if (after.ok && after.aggregate.state === "cancelled") {
|
|
212
|
+
return { ok: true };
|
|
213
|
+
}
|
|
214
|
+
return { ok: false, code: "point-of-no-return" };
|
|
215
|
+
}
|
|
216
|
+
function toAdmission(feature) {
|
|
217
|
+
if (feature.intent !== "upsert") {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
if (feature.intention === undefined) {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
const admission = {
|
|
224
|
+
key: feature.key,
|
|
225
|
+
project: feature.project,
|
|
226
|
+
fingerprint: feature.fingerprint,
|
|
227
|
+
priority: feature.priority,
|
|
228
|
+
intention: feature.intention,
|
|
229
|
+
manager: feature.manager,
|
|
230
|
+
external_id: feature.external_id,
|
|
231
|
+
};
|
|
232
|
+
if (feature.title !== undefined) {
|
|
233
|
+
admission.title = feature.title;
|
|
234
|
+
}
|
|
235
|
+
if (feature.source !== undefined) {
|
|
236
|
+
admission.source = feature.source;
|
|
237
|
+
}
|
|
238
|
+
return admission;
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=deliveries.js.map
|