@plurnk/plurnk-service 3.2.0 → 3.3.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.
Files changed (94) hide show
  1. package/.env.defaults +2 -2
  2. package/SPEC.md +122 -40
  3. package/dist/build-info.json +1 -1
  4. package/dist/core/BudgetReadout.d.ts +1 -3
  5. package/dist/core/BudgetReadout.d.ts.map +1 -1
  6. package/dist/core/BudgetReadout.js +43 -46
  7. package/dist/core/BudgetReadout.js.map +1 -1
  8. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  9. package/dist/core/CoreSchemeServices.js +1 -0
  10. package/dist/core/CoreSchemeServices.js.map +1 -1
  11. package/dist/core/Dispatcher.d.ts.map +1 -1
  12. package/dist/core/Dispatcher.js +7 -5
  13. package/dist/core/Dispatcher.js.map +1 -1
  14. package/dist/core/LoopLifecycle.sql +3 -2
  15. package/dist/core/PacketBuilder.d.ts.map +1 -1
  16. package/dist/core/PacketBuilder.js +16 -13
  17. package/dist/core/PacketBuilder.js.map +1 -1
  18. package/dist/core/PacketBuilder.sql +14 -0
  19. package/dist/core/ProposalLifecycle.d.ts +1 -0
  20. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  21. package/dist/core/ProposalLifecycle.js +1 -0
  22. package/dist/core/ProposalLifecycle.js.map +1 -1
  23. package/dist/core/TurnDispositionHandler.d.ts +1 -1
  24. package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
  25. package/dist/core/TurnDispositionHandler.js +5 -3
  26. package/dist/core/TurnDispositionHandler.js.map +1 -1
  27. package/dist/core/WorkerControlHandler.d.ts.map +1 -1
  28. package/dist/core/WorkerControlHandler.js +38 -2
  29. package/dist/core/WorkerControlHandler.js.map +1 -1
  30. package/dist/core/caps/SchemeCtxImpl.d.ts +1 -0
  31. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  32. package/dist/core/caps/SchemeCtxImpl.js +2 -0
  33. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  34. package/dist/core/fork.sql +2 -2
  35. package/dist/core/mutation-types.d.ts +1 -0
  36. package/dist/core/mutation-types.d.ts.map +1 -1
  37. package/dist/core/packet-wire.d.ts +1 -1
  38. package/dist/core/packet-wire.d.ts.map +1 -1
  39. package/dist/core/packet-wire.js +6 -2
  40. package/dist/core/packet-wire.js.map +1 -1
  41. package/dist/core/plurnk-uri.d.ts +1 -1
  42. package/dist/core/plurnk-uri.d.ts.map +1 -1
  43. package/dist/core/plurnk-uri.js +1 -1
  44. package/dist/core/plurnk-uri.js.map +1 -1
  45. package/dist/core/scheme-types.d.ts +1 -0
  46. package/dist/core/scheme-types.d.ts.map +1 -1
  47. package/dist/man/plurnk-service.1 +4 -4
  48. package/dist/schemes/Exec.d.ts.map +1 -1
  49. package/dist/schemes/Exec.js +5 -1
  50. package/dist/schemes/Exec.js.map +1 -1
  51. package/dist/schemes/ExecutionInput.d.ts +2 -1
  52. package/dist/schemes/ExecutionInput.d.ts.map +1 -1
  53. package/dist/schemes/ExecutionInput.js +44 -6
  54. package/dist/schemes/ExecutionInput.js.map +1 -1
  55. package/dist/schemes/File.d.ts.map +1 -1
  56. package/dist/schemes/File.js +17 -5
  57. package/dist/schemes/File.js.map +1 -1
  58. package/dist/schemes/Log.d.ts.map +1 -1
  59. package/dist/schemes/Log.js +4 -1
  60. package/dist/schemes/Log.js.map +1 -1
  61. package/dist/schemes/Log.sql +24 -1
  62. package/dist/server/Daemon.d.ts +7 -1
  63. package/dist/server/Daemon.d.ts.map +1 -1
  64. package/dist/server/Daemon.js +50 -1
  65. package/dist/server/Daemon.js.map +1 -1
  66. package/dist/server/DrainSupervisor.d.ts +3 -1
  67. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  68. package/dist/server/DrainSupervisor.js +17 -1
  69. package/dist/server/DrainSupervisor.js.map +1 -1
  70. package/dist/server/Functionality.d.ts.map +1 -1
  71. package/dist/server/Functionality.js +86 -49
  72. package/dist/server/Functionality.js.map +1 -1
  73. package/dist/server/FunctionalityManager.d.ts +1 -4
  74. package/dist/server/FunctionalityManager.d.ts.map +1 -1
  75. package/dist/server/FunctionalityManager.js +5 -5
  76. package/dist/server/FunctionalityManager.js.map +1 -1
  77. package/dist/server/MembersFunctionality.d.ts +7 -8
  78. package/dist/server/MembersFunctionality.d.ts.map +1 -1
  79. package/dist/server/MembersFunctionality.js +86 -54
  80. package/dist/server/MembersFunctionality.js.map +1 -1
  81. package/dist/server/MessageAbandonment.d.ts +7 -0
  82. package/dist/server/MessageAbandonment.d.ts.map +1 -0
  83. package/dist/server/MessageAbandonment.js +57 -0
  84. package/dist/server/MessageAbandonment.js.map +1 -0
  85. package/dist/server/Rollback.d.ts +29 -0
  86. package/dist/server/Rollback.d.ts.map +1 -0
  87. package/dist/server/Rollback.js +111 -0
  88. package/dist/server/Rollback.js.map +1 -0
  89. package/dist/server/drain.sql +9 -0
  90. package/docs/members.md +14 -9
  91. package/migrations/026_message_obligations.sql +32 -0
  92. package/migrations/027_members_identity.sql +20 -0
  93. package/migrations/028_mutation_patch.sql +20 -0
  94. package/package.json +10 -11
@@ -0,0 +1,111 @@
1
+ // {§rollback} — undo a worker's settled file mutations after one of its log coordinates. Each row's
2
+ // patch ({§log-mutation-patch}) is reversed against the file as it is now and written back as an
3
+ // ordinary operation of the acting client, so every step is a proposal, a row and a patch of its
4
+ // own; the first file that no longer holds what its mutation wrote stops the rollback where it is.
5
+ import { readFile } from "node:fs/promises";
6
+ import { join } from "node:path";
7
+ import { applyPatch, parsePatch, reversePatch } from "diff";
8
+ import { PlurnkParser } from "@plurnk/plurnk-parser";
9
+ import { entryAddress } from "../core/plurnk-uri.js";
10
+ import { daemonFailure } from "./daemon-results.js";
11
+ export default class Rollback {
12
+ // `<loop>/<turn>/<item>`, with or without the `log:///` prefix and a trailing `/<OP>`.
13
+ static coordinate(to) {
14
+ const match = /^(?:log:\/\/\/)?(\d+)\/(\d+)\/(\d+)(?:\/[A-Za-z]+)?$/u.exec(to.trim());
15
+ if (match === null)
16
+ return null;
17
+ const [loopSeq, turnSeq, sequence] = match.slice(1).map(Number);
18
+ return loopSeq > 0 && turnSeq > 0 && sequence > 0 ? { loopSeq: loopSeq, turnSeq: turnSeq, sequence: sequence } : null;
19
+ }
20
+ static async run(seam, args) {
21
+ const coordinate = Rollback.coordinate(args.to);
22
+ if (coordinate === null) {
23
+ throw daemonFailure("daemon:rollback", "coordinate-invalid", 400, `'${args.to}' is not a log coordinate.`, {
24
+ to: args.to, recovery: "Name the coordinate as <loop>/<turn>/<item>, as the log shows it.",
25
+ });
26
+ }
27
+ const at = `${coordinate.loopSeq}/${coordinate.turnSeq}/${coordinate.sequence}`;
28
+ const anchor = await seam.db.log_id_by_coordinate.get({
29
+ worker_id: args.workerId, loop_seq: coordinate.loopSeq, turn_seq: coordinate.turnSeq, sequence: coordinate.sequence,
30
+ });
31
+ if (anchor === undefined) {
32
+ throw daemonFailure("daemon:rollback", "coordinate-not-found", 404, `No log entry exists at log:///${at}.`, { to: at });
33
+ }
34
+ const root = await seam.projectRoot(args.workspaceId);
35
+ if (root === null) {
36
+ throw daemonFailure("daemon:rollback", "project-root-required", 400, "Rollback restores files, and this workspace has no project root.", {});
37
+ }
38
+ const rows = await seam.db.log_file_mutations_after.all({
39
+ worker_id: args.workerId, loop_seq: coordinate.loopSeq, turn_seq: coordinate.turnSeq, sequence: coordinate.sequence,
40
+ });
41
+ const restored = [];
42
+ const skipped = [];
43
+ const executors = seam.executorTags(args.workspaceId);
44
+ for (const row of rows) {
45
+ const rowCoordinate = `${row.loop_seq}/${row.turn_seq}/${row.sequence}`;
46
+ if (row.patch === null) {
47
+ // A file mutation that kept no patch: nothing is invented for it ({§log-mutation-patch}).
48
+ if (row.scheme === null || row.scheme === "file")
49
+ skipped.push({ coordinate: rowCoordinate, reason: "no-patch" });
50
+ continue;
51
+ }
52
+ // A row that wrote several files is reversed last file first.
53
+ for (const file of parsePatch(row.patch).toReversed()) {
54
+ const outcome = await Rollback.#reverse(seam, { root, executors, file, coordinate: rowCoordinate }, restored);
55
+ if (outcome === "already-reversed") {
56
+ skipped.push({ coordinate: rowCoordinate, reason: "already-reversed" });
57
+ continue;
58
+ }
59
+ if (outcome !== null)
60
+ return { to: at, restored, skipped, stopped: outcome };
61
+ }
62
+ }
63
+ return { to: at, restored, skipped, stopped: null };
64
+ }
65
+ static async #reverse(seam, { root, executors, file, coordinate }, restored) {
66
+ const path = file.oldFileName;
67
+ if (typeof path !== "string" || path.length === 0)
68
+ throw new Error(`log:///${coordinate} keeps a patch that names no file.`);
69
+ let current;
70
+ try {
71
+ current = await readFile(join(root, path), "utf8");
72
+ }
73
+ catch (cause) {
74
+ if (cause.code !== "ENOENT")
75
+ throw cause;
76
+ current = null;
77
+ }
78
+ const created = file.hunks.every((hunk) => hunk.oldLines === 0);
79
+ const removed = !created && file.hunks.every((hunk) => hunk.newLines === 0);
80
+ // A row whose forward patch still applies was reversed already, by an earlier rollback whose
81
+ // own rows live in the client's log: nothing to do for it, and nothing invented.
82
+ const alreadyReversed = () => applyPatch(current ?? "", file) !== false;
83
+ if (removed ? current !== null : current === null) {
84
+ if (alreadyReversed())
85
+ return "already-reversed";
86
+ return { coordinate, path, reason: "drift", detail: `'${path}' ${current === null ? "no longer exists" : "exists again"}; log:///${coordinate} ${removed ? "removed" : "wrote"} it.` };
87
+ }
88
+ const previous = applyPatch(current ?? "", reversePatch(file));
89
+ if (previous === false) {
90
+ if (alreadyReversed())
91
+ return "already-reversed";
92
+ return { coordinate, path, reason: "drift", detail: `'${path}' no longer holds what log:///${coordinate} wrote.` };
93
+ }
94
+ const action = created ? "delete" : removed ? "create" : "update";
95
+ const target = entryAddress("file", "", path, root);
96
+ const source = action === "delete"
97
+ ? `\`\`\`\`KILL (${target})\`\`\`\``
98
+ : `\`\`\`\`EDIT (${target})${action === "update" ? " <1,-1>" : ""}\n${previous}\n\`\`\`\``;
99
+ const { items } = PlurnkParser.parseClient(source, { executors: [...executors] });
100
+ const item = items[0];
101
+ if (items.length !== 1 || item?.kind !== "statement")
102
+ throw new Error(`rollback of log:///${coordinate} built an operation the parser refused: ${source.slice(0, 120)}`);
103
+ const result = await seam.apply(item.statement);
104
+ if (result.status >= 300) {
105
+ return { coordinate, path, reason: "refused", detail: result.problem?.detail ?? `The reversing operation answered ${result.status}.` };
106
+ }
107
+ restored.push({ coordinate, path, action });
108
+ return null;
109
+ }
110
+ }
111
+ //# sourceMappingURL=Rollback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Rollback.js","sourceRoot":"","sources":["../../src/server/Rollback.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,iGAAiG;AACjG,iGAAiG;AACjG,mGAAmG;AACnG,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAwB,MAAM,MAAM,CAAC;AAElF,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAmBpD,MAAM,CAAC,OAAO,OAAO,QAAQ;IACzB,uFAAuF;IACvF,MAAM,CAAC,UAAU,CAAC,EAAU;QACxB,MAAM,KAAK,GAAG,uDAAuD,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QACtF,IAAI,KAAK,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QAChC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChE,OAAO,OAAQ,GAAG,CAAC,IAAI,OAAQ,GAAG,CAAC,IAAI,QAAS,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAQ,EAAE,OAAO,EAAE,OAAQ,EAAE,QAAQ,EAAE,QAAS,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAChI,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,IAAkB,EAAE,IAA2D;QAC5F,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAChD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACtB,MAAM,aAAa,CAAC,iBAAiB,EAAE,oBAAoB,EAAE,GAAG,EAAE,IAAI,IAAI,CAAC,EAAE,4BAA4B,EAAE;gBACvG,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,mEAAmE;aAC7F,CAAC,CAAC;QACP,CAAC;QACD,MAAM,EAAE,GAAG,GAAG,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,OAAO,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;QAChF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAiB;YAClE,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ;SACtH,CAAC,CAAC;QACH,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,aAAa,CAAC,iBAAiB,EAAE,sBAAsB,EAAE,GAAG,EAAE,iCAAiC,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5H,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAChB,MAAM,aAAa,CAAC,iBAAiB,EAAE,uBAAuB,EAAE,GAAG,EAAE,kEAAkE,EAAE,EAAE,CAAC,CAAC;QACjJ,CAAC;QACD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,wBAAwB,CAAC,GAAG,CAAc;YACjE,SAAS,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ;SACtH,CAAC,CAAC;QACH,MAAM,QAAQ,GAAe,EAAE,CAAC;QAChC,MAAM,OAAO,GAAwC,EAAE,CAAC;QACxD,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtD,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,aAAa,GAAG,GAAG,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;YACxE,IAAI,GAAG,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;gBACrB,0FAA0F;gBAC1F,IAAI,GAAG,CAAC,MAAM,KAAK,IAAI,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM;oBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;gBAClH,SAAS;YACb,CAAC;YACD,8DAA8D;YAC9D,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC;gBACpD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,EAAE,QAAQ,CAAC,CAAC;gBAC9G,IAAI,OAAO,KAAK,kBAAkB,EAAE,CAAC;oBAAC,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC,CAAC;oBAAC,SAAS;gBAAC,CAAC;gBAC1H,IAAI,OAAO,KAAK,IAAI;oBAAE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YACjF,CAAC;QACL,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IACxD,CAAC;IAED,MAAM,CAAC,KAAK,CAAC,QAAQ,CACjB,IAAkB,EAClB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAA6F,EAChI,QAAoB;QAEpB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;QAC9B,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,UAAU,UAAU,oCAAoC,CAAC,CAAC;QAC7H,IAAI,OAAsB,CAAC;QAC3B,IAAI,CAAC;YACD,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACvD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACb,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,MAAM,KAAK,CAAC;YACpE,OAAO,GAAG,IAAI,CAAC;QACnB,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC;QAC5E,6FAA6F;QAC7F,iFAAiF;QACjF,MAAM,eAAe,GAAG,GAAY,EAAE,CAAC,UAAU,CAAC,OAAO,IAAI,EAAE,EAAE,IAAI,CAAC,KAAK,KAAK,CAAC;QACjF,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;YAChD,IAAI,eAAe,EAAE;gBAAE,OAAO,kBAAkB,CAAC;YACjD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,IAAI,KAAK,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,cAAc,YAAY,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,CAAC;QAC3L,CAAC;QACD,MAAM,QAAQ,GAAG,UAAU,CAAC,OAAO,IAAI,EAAE,EAAE,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/D,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACrB,IAAI,eAAe,EAAE;gBAAE,OAAO,kBAAkB,CAAC;YACjD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,IAAI,iCAAiC,UAAU,SAAS,EAAE,CAAC;QACvH,CAAC;QACD,MAAM,MAAM,GAAuB,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QACtF,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,MAAM,KAAK,QAAQ;YAC9B,CAAC,CAAC,iBAAiB,MAAM,WAAW;YACpC,CAAC,CAAC,iBAAiB,MAAM,IAAI,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,YAAY,CAAC;QAC/F,MAAM,EAAE,KAAK,EAAE,GAAG,YAAY,CAAC,WAAW,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,CAAC,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;QAClF,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,EAAE,IAAI,KAAK,WAAW;YAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,UAAU,2CAA2C,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;QACzK,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAA4B,CAAC,CAAC;QACnE,IAAI,MAAM,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACvB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,IAAI,oCAAoC,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC;QAC3I,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC;IAChB,CAAC;CACJ"}
@@ -52,6 +52,15 @@ WHERE worker_id = $worker_id
52
52
  ORDER BY (status = 102) DESC, sequence ASC
53
53
  LIMIT 1;
54
54
 
55
+ -- PREP: drain_abandoned_messages
56
+ -- {§message-abandonment}: the messages other workers sent that this ended loop still holds without a completion.
57
+ SELECT m.path
58
+ FROM message_sources m
59
+ WHERE m.loop_id = $loop_id
60
+ AND m.source LIKE 'worker://%'
61
+ AND NOT EXISTS (SELECT 1 FROM message_completions c WHERE c.message_id = m.id)
62
+ ORDER BY m.ordinal ASC;
63
+
55
64
  -- PREP: drain_message_source
56
65
  SELECT l.worker_id, w.workspace_id, l.status
57
66
  FROM loops l JOIN workers w ON w.id = l.worker_id
package/docs/members.md CHANGED
@@ -50,8 +50,8 @@ candidate: its `provenance.kind` is the verdict and its `summary` the reason.
50
50
  - **Untracked, scope `root` or `namespace`**: a definition makes it a member
51
51
  from the next turn.
52
52
 
53
- ```members (add) <!-- include the generated reports -->
54
- {"alias": "reports", "definition": {"glob": "build/*.json"}}
53
+ ```members (add) <!-- include the generated reports; each glob is one definition -->
54
+ {"files": ["build/*.json"]}
55
55
  ```
56
56
 
57
57
  - **Untracked, scope `none`** (an operator narrowed it): the refusal names the
@@ -95,10 +95,15 @@ cleanup conflicts; its receipt identifies that completed effect.
95
95
 
96
96
  ## Definitions belong to the workspace
97
97
 
98
- `list` shows every definition with its origin (`service` from the operator's
99
- environment, `workspace` from an `add`) and whether it is enabled. `enable` and
100
- `disable` flip one shared alias; `remove` forgets a workspace override and
101
- restores inheritance. Inherited definitions cannot be removed here. Changes
102
- apply to every worker.
103
- Each enabled definition is projected as
104
- `worker:///_plurnk/members/<alias>.md` showing what its glob resolved to.
98
+ A definition is its glob: `list` shows every definition with its origin (`service` from the
99
+ operator's environment, `workspace` from an `add`) and whether it is enabled. `enable` and
100
+ `disable` flip one definition, and `remove` forgets a workspace definition and restores
101
+ inheritance, each named by the glob as `list` shows it:
102
+
103
+ ```members (disable)
104
+ {"file": "build/*.json"}
105
+ ```
106
+
107
+ Inherited definitions cannot be removed here. Changes apply to every worker. Each enabled
108
+ definition is projected as `worker:///_plurnk/members/<glob>.md`, the glob percent-encoded,
109
+ showing what it resolved to.
@@ -0,0 +1,32 @@
1
+ -- MIGRATE: 26 message obligations
2
+ -- Released in 3.3.0 and frozen ({§db-migrations}): a shape change is the next MIGRATE version, never an edit here.
3
+ -- {§worker-obligations}: a message a worker sent to another worker holds the sender, beside its live children
4
+ -- and open streams, until the recipient completes it ({§message-completion}) or the recipient's loop ends
5
+ -- ({§message-abandonment}). The two liveness views are recreated with that third column; no row changes.
6
+ DROP VIEW IF EXISTS loop_obligations;
7
+ DROP VIEW IF EXISTS worker_obligations;
8
+
9
+ CREATE VIEW worker_obligations AS
10
+ SELECT w.id AS worker_id,
11
+ EXISTS (
12
+ SELECT 1 FROM subscriptions s
13
+ WHERE s.worker_id = w.id AND s.closed_at IS NULL AND s.detached = 0
14
+ ) AS streams,
15
+ EXISTS (
16
+ SELECT 1 FROM workers c JOIN loops l ON l.worker_id = c.id
17
+ WHERE c.parent_worker_id = w.id AND l.status IN (100, 102, 202)
18
+ ) AS workers,
19
+ EXISTS (
20
+ SELECT 1 FROM loop_messages m
21
+ JOIN loops l ON l.id = m.loop_id
22
+ JOIN workers r ON r.id = l.worker_id
23
+ WHERE r.workspace_id = w.workspace_id
24
+ AND m.source = 'worker://' || w.name
25
+ AND l.status IN (100, 102, 202)
26
+ AND NOT EXISTS (SELECT 1 FROM message_completions c WHERE c.message_id = m.id)
27
+ ) AS messages
28
+ FROM workers w;
29
+
30
+ CREATE VIEW loop_obligations AS
31
+ SELECT l.id AS loop_id, held.streams, held.workers, held.messages
32
+ FROM loops l JOIN worker_obligations held ON held.worker_id = l.worker_id;
@@ -0,0 +1,20 @@
1
+ -- MIGRATE: 27 members identity
2
+ -- Released in 3.3.0 and frozen ({§db-migrations}): a shape change is the next MIGRATE version, never an edit here.
3
+ -- {§members-functionality} {§functionality-addressing}: a members definition is its glob. Workspace
4
+ -- definitions are re-keyed by their glob. A service-origin row held only an enabledness override
5
+ -- keyed by the operator's environment alias, which no longer names a definition, so it is dropped;
6
+ -- a state left with no definition is removed, as the coordinator persists none.
7
+ UPDATE workspace_module_state
8
+ SET state = (
9
+ SELECT json_object(
10
+ 'version', 1,
11
+ 'definitions', COALESCE(json_group_object(json_extract(value, '$.definition.glob'), json(value)), json('{}'))
12
+ )
13
+ FROM json_each(workspace_module_state.state, '$.definitions')
14
+ WHERE json_extract(value, '$.origin') = 'workspace'
15
+ )
16
+ WHERE namespace_owner = '@plurnk/plurnk-core/members';
17
+
18
+ DELETE FROM workspace_module_state
19
+ WHERE namespace_owner = '@plurnk/plurnk-core/members'
20
+ AND (SELECT COUNT(*) FROM json_each(workspace_module_state.state, '$.definitions')) = 0;
@@ -0,0 +1,20 @@
1
+ -- MIGRATE: 28 mutation patch
2
+ -- Released in 3.3.0 and frozen ({§db-migrations}): a shape change is the next MIGRATE version, never an edit here.
3
+ -- {§log-mutation-patch}: a settled file mutation's row keeps the unified diff of what it did, for the
4
+ -- life of the row; it is served as the row's `patch` channel and never enters a receipt or a packet.
5
+ -- Rows settled before this version, byte writes and operations that changed no text hold NULL.
6
+ ALTER TABLE log_entries ADD COLUMN patch TEXT;
7
+
8
+ DROP VIEW active_log_entries;
9
+ CREATE VIEW active_log_entries AS
10
+ SELECT le.id, le.version, le.worker_id, le.loop_id, le.turn_id, le.sequence,
11
+ le.at, le.origin, le.source, le.ambient_event_id, le.inherited_history,
12
+ le.deep_hash, le.model_call_id, le.subscription_publication_id,
13
+ le.op, le.signal, le.scheme, le.username, le.password,
14
+ le.hostname, le.port, le.pathname, le.query, le.fragment, le.lineMarker,
15
+ le.tx, le.mimetype_tx,
16
+ le.rx, le.mimetype_rx, le.status_rx, le.weight,
17
+ le.state, le.outcome, le.attrs, le.initial_folded, le.patch, projection.folded
18
+ FROM log_entries le
19
+ JOIN log_entry_projections projection ON projection.log_entry_id = le.id
20
+ WHERE projection.active = 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plurnk/plurnk-service",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "Plurnk agent runtime — the server / engine / core. Implements the @plurnk/plurnk-contracts contract.",
5
5
  "keywords": [
6
6
  "plurnk",
@@ -50,14 +50,13 @@
50
50
  "test:lint": "npm run types:sqlrite:check && tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
51
51
  "test:unit": "node --conditions=plurnk-dev --import=./test/setup.ts --env-file-if-exists=.env.defaults --test --test-timeout=30000 scripts/*.test.mjs src/**/*.test.ts",
52
52
  "test:intg": "export PLURNK_TEST_RUN=$(date -u +%Y%m%dT%H%M%SZ); PLURNK_SERVICE_FILES_ITEMS=0 node --conditions=plurnk-dev --import=./test/setup.ts --env-file-if-exists=.env.defaults --test --test-concurrency=8 --test-timeout=30000 test/intg/*.test.ts && node ../scripts/reclaim-green-run.mjs core",
53
- "test:mcp:dogfood": "PLURNK_TEST_MCP_DOGFOOD=1 node --conditions=plurnk-dev --import=./test/setup.ts --env-file-if-exists=.env.defaults --test --test-concurrency=1 --test-timeout=120000 test/intg/mcp-agui-composition.test.ts",
54
- "test:a2a:tck": "node scripts/a2a-tck.mjs",
55
53
  "test:live": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev scripts/live.mjs",
56
54
  "test:live:specimen": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev scripts/live.mjs --specimen",
57
55
  "test:live:zeropin": "PLURNK_ZERO_PIN=1 npm run test:live",
58
56
  "test:demo": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev scripts/demo.mjs",
59
57
  "test:demo:specimen": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev scripts/demo.mjs --specimen",
60
58
  "test:demo:zeropin": "PLURNK_ZERO_PIN=1 npm run test:demo",
59
+ "tier:history": "node --conditions=plurnk-dev scripts/tier-history.mjs",
61
60
  "test:benchlet": "bash ../scripts/operator-environment.sh node scripts/benchlet.mjs",
62
61
  "fixture:demo": "node --conditions=plurnk-dev test/demo/_fixture.ts",
63
62
  "test": "npm run test:lint && npm run test:unit && npm run test:intg",
@@ -90,17 +89,17 @@
90
89
  "@opentelemetry/sdk-metrics": "^2.12.0",
91
90
  "@opentelemetry/sdk-trace-base": "^2.12.0",
92
91
  "@plurnk/plurnk-a2a": "3.1.1",
93
- "@plurnk/plurnk-agui": "3.0.0",
94
- "@plurnk/plurnk-contracts": "3.2.0",
92
+ "@plurnk/plurnk-agui": "3.1.0",
93
+ "@plurnk/plurnk-contracts": "3.3.0",
95
94
  "@plurnk/plurnk-parser": "3.0.3",
96
- "@plurnk/plurnk-schedule": "3.0.1",
95
+ "@plurnk/plurnk-schedule": "3.0.2",
97
96
  "@plurnk/plurnk-skills": "3.0.1",
98
- "@plurnk/plurnk-execs": "3.1.1",
97
+ "@plurnk/plurnk-execs": "3.2.0",
99
98
  "@plurnk/plurnk-execs-common": "2.0.2",
100
99
  "@plurnk/plurnk-execs-jq": "2.0.2",
101
100
  "@plurnk/plurnk-execs-sqlite": "2.0.2",
102
101
  "@plurnk/plurnk-hooks": "2.0.1",
103
- "@plurnk/plurnk-mcp": "3.0.1",
102
+ "@plurnk/plurnk-mcp": "3.1.0",
104
103
  "@plurnk/plurnk-meta": "2.1.1",
105
104
  "@plurnk/plurnk-agent-plugins": "2.0.0",
106
105
  "@plurnk/plurnk-agent-skills": "2.0.1",
@@ -146,9 +145,9 @@
146
145
  "@plurnk/plurnk-mimetypes-text-markdown": "2.0.1",
147
146
  "@plurnk/plurnk-mimetypes-text-plain": "2.0.1",
148
147
  "@plurnk/plurnk-models": "3.0.1",
149
- "@plurnk/plurnk-modules": "3.1.0",
148
+ "@plurnk/plurnk-modules": "3.2.0",
150
149
  "@plurnk/plurnk-providers": "3.0.2",
151
- "@plurnk/plurnk-schemes": "3.1.1",
150
+ "@plurnk/plurnk-schemes": "3.2.0",
152
151
  "@plurnk/plurnk-schemes-http": "2.0.1",
153
152
  "@possumtech/sqlrite": "^6.6.1",
154
153
  "diff": "^9.0.0",
@@ -172,5 +171,5 @@
172
171
  "url": "git+https://github.com/plurnk/plurnk-service.git",
173
172
  "directory": "plurnk-core"
174
173
  },
175
- "gitHead": "f96502a26d62a6f87b22950a9e2006348ac8a6bc"
174
+ "gitHead": "1dcf640b6bad0f5972443dd59354a5fc094acf46"
176
175
  }