githolon 0.110.1 → 0.111.1

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 ADDED
@@ -0,0 +1,43 @@
1
+ # Nomos developer CLI
2
+
3
+ Compile a domain with `npx githolon compile` and run its generated Dart scenarios
4
+ with `npx githolon test`. Each command has a focused `--help` page.
5
+
6
+ ## Check an installed stack
7
+
8
+ From a consumer project with `githolon`, `@githolon/dsl` and `@githolon/testing`
9
+ installed, run:
10
+
11
+ ```sh
12
+ npx githolon conformance
13
+ ```
14
+
15
+ The command reports the installed CLI, compiler, runtime and testing package
16
+ versions and the kernel SHA-256 actually used. It compiles an isolated generated
17
+ booking application with your installed compiler, then uses the existing real
18
+ runtime harness to check a write, indexed and direct reads, an expected refusal
19
+ with no effect, persisted Git history, and a cold restore followed by another
20
+ write. It removes its temporary application and custody after the check.
21
+
22
+ For CI, use `npx githolon conformance --json`. Exit status is 0 when all checks
23
+ pass, 1 for a failed check, and 2 for invalid command options. A failure names the
24
+ stage, diagnostic and next action. The JSON report includes `passed`, `packages`,
25
+ `runtime`, `checks` and, on failure, `failure`.
26
+
27
+ First use may download verified runtime artifacts. `--offline` forbids runtime
28
+ acquisition from the network. To select an existing verified bundle explicitly:
29
+
30
+ ```sh
31
+ npx githolon conformance --offline --runtime /path/to/runtime --json
32
+ ```
33
+
34
+ The bundle contains `holon.wasm`, `kernel.repository.xml`, `bootstrap.law.usdz`
35
+ and `nomos.law.usdz`; the existing runtime loader verifies compatibility and
36
+ content identity. An invalid explicitly selected bundle fails rather than
37
+ switching to a cached one.
38
+
39
+ This checks the installed Node stack using a standard generated application.
40
+ It does not run your application's own acceptance tests or qualify Flutter,
41
+ cloud deployment or physical devices. Cold restore checks usable data and
42
+ persisted history; the report says when deeper history verification is still
43
+ materialising, rather than claiming a full historical audit.
@@ -3349,7 +3349,7 @@ var init_recovery_desk_pb = __esm({
3349
3349
  // ../adapter/proto/nomos/runtime/v2/observability_desk_pb.js
3350
3350
  import { enumDesc as enumDesc15, fileDesc as fileDesc22, messageDesc as messageDesc22, tsEnum as tsEnum15 } from "@bufbuild/protobuf/codegenv2";
3351
3351
  import { file_google_protobuf_any as file_google_protobuf_any2 } from "@bufbuild/protobuf/wkt";
3352
- var file_nomos_runtime_v2_observability_desk, RuntimeFailureSchema, RuntimeUnavailableSchema, RuntimeRefusalSchema, DiagnosticAttributeSchema, RuntimeEventSchema, FollowsSummarySchema, FollowObservationSchema, CredentialRefreshRequestedSchema, RuntimeReadySchema, WatchChangedSchema, SyncCompletedSchema, LinkChangedSchema, RuntimeSpanSchema, RuntimeFailureKindSchema, RuntimeFailureKind, RuntimeUnavailableReasonSchema, RuntimeUnavailableReason, RuntimeRefusalReasonSchema, RuntimeRefusalReason, FollowObservationPhaseSchema, FollowObservationPhase, LinkStatusSchema, LinkStatus;
3352
+ var file_nomos_runtime_v2_observability_desk, RuntimeFailureSchema, RuntimeUnavailableSchema, RuntimeRefusalSchema, DiagnosticAttributeSchema, RuntimeEventSchema, FollowsSummarySchema, FollowObservationSchema, CredentialRefreshRequestedSchema, RuntimeReadySchema, WatchChangedSchema, SyncCompletedSchema, LinkChangedSchema, RuntimeSpanSchema, WritePreparationSchema, RuntimeFailureKindSchema, RuntimeFailureKind, RuntimeUnavailableReasonSchema, RuntimeUnavailableReason, RuntimeRefusalReasonSchema, RuntimeRefusalReason, FollowObservationPhaseSchema, FollowObservationPhase, LinkStatusSchema, LinkStatus;
3353
3353
  var init_observability_desk_pb = __esm({
3354
3354
  "../adapter/proto/nomos/runtime/v2/observability_desk_pb.js"() {
3355
3355
  "use strict";
@@ -3361,7 +3361,7 @@ var init_observability_desk_pb = __esm({
3361
3361
  init_model_adoption_desk_pb();
3362
3362
  init_receiving_group_desk_pb();
3363
3363
  init_recovery_desk_pb();
3364
- file_nomos_runtime_v2_observability_desk = /* @__PURE__ */ fileDesc22("Cilub21vcy9ydW50aW1lL3YyL29ic2VydmFiaWxpdHlfZGVzay5wcm90bxIQbm9tb3MucnVudGltZS52MiJCCgxUcmFjZUNvbnRleHQSEAoIdHJhY2VfaWQYASABKAwSDwoHc3Bhbl9pZBgCIAEoDBIPCgdzYW1wbGVkGAMgASgIIp0CCg5SdW50aW1lRmFpbHVyZRIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSEQoJY29tcG9uZW50GAMgASgJEhYKCXdvcmtzcGFjZRgEIAEoCUgAiAEBEhMKBmRvbWFpbhgFIAEoCUgBiAEBEhYKCWRpcmVjdGl2ZRgGIAEoCUgCiAEBEjkKCmF0dHJpYnV0ZXMYByADKAsyJS5ub21vcy5ydW50aW1lLnYyLkRpYWdub3N0aWNBdHRyaWJ1dGUSMgoEa2luZBgIIAEoDjIkLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZUZhaWx1cmVLaW5kQgwKCl93b3Jrc3BhY2VCCQoHX2RvbWFpbkIMCgpfZGlyZWN0aXZlIr4BChJSdW50aW1lVW5hdmFpbGFibGUSOgoGcmVhc29uGAEgASgOMioubm9tb3MucnVudGltZS52Mi5SdW50aW1lVW5hdmFpbGFibGVSZWFzb24SEQoJd29ya3NwYWNlGAIgASgJEhEKCXJldHJ5YWJsZRgDIAEoCBIfChJyZXRyeV9hZnRlcl9taWxsaXMYBCABKA1IAIgBARIOCgZkZXRhaWwYBSABKAlCFQoTX3JldHJ5X2FmdGVyX21pbGxpcyJ+Cg5SdW50aW1lUmVmdXNhbBI2CgZyZWFzb24YASABKA4yJi5ub21vcy5ydW50aW1lLnYyLlJ1bnRpbWVSZWZ1c2FsUmVhc29uEhEKCXdvcmtzcGFjZRgCIAEoCRIRCglyZXRyeWFibGUYAyABKAgSDgoGZGV0YWlsGAQgASgJIqUBChNEaWFnbm9zdGljQXR0cmlidXRlEgwKBG5hbWUYASABKAkSFgoMc3RyaW5nX3ZhbHVlGAIgASgJSAASFwoNaW50ZWdlcl92YWx1ZRgDIAEoEkgAEhYKDGRvdWJsZV92YWx1ZRgEIAEoAUgAEhcKDWJvb2xlYW5fdmFsdWUYBSABKAhIABIVCgtieXRlc192YWx1ZRgGIAEoDEgAQgcKBXZhbHVlIuMKCgxSdW50aW1lRXZlbnQSEAoIc2VxdWVuY2UYASABKAQSFAoHc2Vzc2lvbhgCIAEoCUgBiAEBEi8KBXJlYWR5GAogASgLMh4ubm9tb3MucnVudGltZS52Mi5SdW50aW1lUmVhZHlIABI3Cg13YXRjaF9jaGFuZ2VkGAsgASgLMh4ubm9tb3MucnVudGltZS52Mi5XYXRjaENoYW5nZWRIABI5Cg5zeW5jX2NvbXBsZXRlZBgMIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU3luY0NvbXBsZXRlZEgAEjUKDGxpbmtfY2hhbmdlZBgNIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuTGlua0NoYW5nZWRIABI5Cg5zaWduYWxfY2hhbmdlZBgOIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU2lnbmFsQ2hhbmdlZEgAEjsKD2N1c3RvZHlfY2hhbmdlZBgPIAEoCzIgLm5vbW9zLnJ1bnRpbWUudjIuQ3VzdG9keUNoYW5nZWRIABJCChNsb2NhbF93b3JrX3JlamVjdGVkGBAgASgLMiMubm9tb3MucnVudGltZS52Mi5Mb2NhbFdvcmtSZWplY3RlZEgAEjMKC2xhd19jaGFuZ2VkGBEgASgLMhwubm9tb3MucnVudGltZS52Mi5MYXdDaGFuZ2VkSAASQwoPa2VybmVsX3Byb2dyZXNzGBIgASgLMigubm9tb3MucnVudGltZS52Mi5LZXJuZWxQcm9ncmVzc09ic2VydmVkSAASLQoEc3BhbhgTIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZVNwYW5IABI/ChF0cmFuc2Zlcl9wcm9ncmVzcxgUIAEoCzIiLm5vbW9zLnJ1bnRpbWUudjIuVHJhbnNmZXJQcm9ncmVzc0gAEkkKFG1haW50ZW5hbmNlX3Byb2dyZXNzGBUgASgLMikubm9tb3MucnVudGltZS52Mi5SZWFkTWFpbnRlbmFuY2VQcm9ncmVzc0gAEjsKDXNvY2tldF9lZmZlY3QYFiABKAsyIi5ub21vcy5ydW50aW1lLnYyLlBlZXJTb2NrZXRFZmZlY3RIABJUChxjcmVkZW50aWFsX3JlZnJlc2hfcmVxdWVzdGVkGBcgASgLMiwubm9tb3MucnVudGltZS52Mi5DcmVkZW50aWFsUmVmcmVzaFJlcXVlc3RlZEgAElIKG3JlY2VpdmluZ19ncm91cF9jb252ZXJnZW5jZRgYIAEoCzIrLm5vbW9zLnJ1bnRpbWUudjIuUmVjZWl2aW5nR3JvdXBDb252ZXJnZW5jZUgAEkoKF21vZGVsX2Fkb3B0aW9uX3Byb2dyZXNzGBkgASgLMicubm9tb3MucnVudGltZS52Mi5Nb2RlbEFkb3B0aW9uUHJvZ3Jlc3NIABI7Cg9mb2xsb3dzX3N1bW1hcnkYGiABKAsyIC5ub21vcy5ydW50aW1lLnYyLkZvbGxvd3NTdW1tYXJ5SAASRAoUbG9jYWxfY29weV9wcmVzZXJ2ZWQYGyABKAsyJC5ub21vcy5ydW50aW1lLnYyLlByZXNlcnZlZExvY2FsQ29weUgAEk4KGWZvcmVpZ25fcGxhY2VtZW50X2lnbm9yZWQYHCABKAsyKS5ub21vcy5ydW50aW1lLnYyLkZvcmVpZ25QbGFjZW1lbnRJZ25vcmVkSAASQwoTYmluZGluZ19xdWFyYW50aW5lZBgdIAEoCzIkLm5vbW9zLnJ1bnRpbWUudjIuUXVhcmFudGluZWRCaW5kaW5nSABCBwoFZXZlbnRCCgoIX3Nlc3Npb24ivAEKDkZvbGxvd3NTdW1tYXJ5EhAKCGZvbGxvd2VkGAEgASgNEg8KB3NldHRsZWQYAiABKA0SDAoEbGl2ZRgDIAEoDRI3Cgp3b3Jrc3BhY2VzGAQgAygLMiMubm9tb3MucnVudGltZS52Mi5Gb2xsb3dPYnNlcnZhdGlvbhIkChdvYnNlcnZlZF9hdF91bml4X21pbGxpcxgFIAEoBEgAiAEBQhoKGF9vYnNlcnZlZF9hdF91bml4X21pbGxpcyLRAgoRRm9sbG93T2JzZXJ2YXRpb24SEQoJd29ya3NwYWNlGAEgASgJEjcKBXBoYXNlGAIgASgOMigubm9tb3MucnVudGltZS52Mi5Gb2xsb3dPYnNlcnZhdGlvblBoYXNlEgwKBGxpdmUYAyABKAgSEwoGZGV0YWlsGAQgASgJSACIAQESIQoUcmV0cnlfYXRfdW5peF9taWxsaXMYBSABKARIAYgBARIfChJkdWVfYXRfdW5peF9taWxsaXMYBiABKARIAogBARIUCgdhdHRlbXB0GAcgASgNSAOIAQESGgoNYWN0aXZlX2VmZmVjdBgIIAEoCUgEiAEBQgkKB19kZXRhaWxCFwoVX3JldHJ5X2F0X3VuaXhfbWlsbGlzQhUKE19kdWVfYXRfdW5peF9taWxsaXNCCgoIX2F0dGVtcHRCEAoOX2FjdGl2ZV9lZmZlY3QiVgoaQ3JlZGVudGlhbFJlZnJlc2hSZXF1ZXN0ZWQSOAoHcGVuZGluZxgBIAEoCzInLm5vbW9zLnJ1bnRpbWUudjIuQ29ycmVzcG9uZGVuY2VQZW5kaW5nIj4KDFJ1bnRpbWVSZWFkeRIXCg9ydW50aW1lX3ZlcnNpb24YASABKAkSFQoNa2VybmVsX3NoYTI1NhgCIAEoDCL8AQoMV2F0Y2hDaGFuZ2VkEhcKD3N1YnNjcmlwdGlvbl9pZBgBIAEoCRITCgtyZXN1bHRfdXNkYRgCIAEoDBI2CgdmYWlsdXJlGAMgASgLMiAubm9tb3MucnVudGltZS52Mi5SdW50aW1lRmFpbHVyZUgAiAEBEjUKEmFwcGxpY2F0aW9uX3Jlc3VsdBgEIAEoCzIULmdvb2dsZS5wcm90b2J1Zi5BbnlIAYgBARIaCg1pbnRlZ2VyX3ZhbHVlGAUgASgDSAKIAQFCCgoIX2ZhaWx1cmVCFQoTX2FwcGxpY2F0aW9uX3Jlc3VsdEIQCg5faW50ZWdlcl92YWx1ZSKcAQoNU3luY0NvbXBsZXRlZBIOCgZyZWFzb24YASABKAkSNwoGcmVzdWx0GAIgASgLMicubm9tb3MucnVudGltZS52Mi5TeW5jV29ya3NwYWNlUmVzcG9uc2USNgoHZmFpbHVyZRgDIAEoCzIgLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZUZhaWx1cmVIAIgBAUIKCghfZmFpbHVyZSI7CgtMaW5rQ2hhbmdlZBIsCgZzdGF0dXMYASABKA4yHC5ub21vcy5ydW50aW1lLnYyLkxpbmtTdGF0dXMiOAoNU2lnbmFsQ2hhbmdlZBIOCgZkb21haW4YASABKAkSFwoPcHJvamVjdGlvbl91c2RhGAIgASgMIlAKCkxhd0NoYW5nZWQSDgoGZG9tYWluGAEgASgJEhYKDnBhY2thZ2Vfc2hhMjU2GAIgASgJEhoKEmNvbXBhdGliaWxpdHlfdXNkYRgDIAEoDCLdAQoLUnVudGltZVNwYW4SEAoIdHJhY2VfaWQYASABKAwSDwoHc3Bhbl9pZBgCIAEoDBIbCg5wYXJlbnRfc3Bhbl9pZBgDIAEoDEgAiAEBEgwKBG5hbWUYBCABKAkSGgoSc3RhcnRlZF91bml4X25hbm9zGAUgASgEEhYKDmR1cmF0aW9uX25hbm9zGAYgASgEEjkKCmF0dHJpYnV0ZXMYByADKAsyJS5ub21vcy5ydW50aW1lLnYyLkRpYWdub3N0aWNBdHRyaWJ1dGVCEQoPX3BhcmVudF9zcGFuX2lkIuoBChZLZXJuZWxQcm9ncmVzc09ic2VydmVkEjkKCHNuYXBzaG90GAEgASgLMicubm9tb3Mua2VybmVsLnYxLktlcm5lbFByb2dyZXNzU25hcHNob3QSJAocaG9zdF9vYnNlcnZlZF9lbGFwc2VkX21pY3JvcxgCIAEoBBIqCiJob3N0X29ic2VydmVkX3BoYXNlX2VsYXBzZWRfbWljcm9zGAMgASgEEh4KFnByb2Nlc3NfcmVzaWRlbnRfYnl0ZXMYBCABKAQSIwobcHJvY2Vzc19wZWFrX3Jlc2lkZW50X2J5dGVzGAUgASgEIoIBChBUcmFuc2ZlclByb2dyZXNzEhYKDnJlY2VpdmVkX2J5dGVzGAEgASgEEhgKC3RvdGFsX2J5dGVzGAIgASgESACIAQESLAoEa2luZBgDIAEoDjIeLm5vbW9zLnJ1bnRpbWUudjIuVHJhbnNmZXJLaW5kQg4KDF90b3RhbF9ieXRlcyrKAQoSUnVudGltZUZhaWx1cmVLaW5kEiQKIFJVTlRJTUVfRkFJTFVSRV9LSU5EX1VOU1BFQ0lGSUVEEAASIQodUlVOVElNRV9GQUlMVVJFX0tJTkRfSU5URVJOQUwQARIhCh1SVU5USU1FX0ZBSUxVUkVfS0lORF9QUk9UT0NPTBACEiIKHlJVTlRJTUVfRkFJTFVSRV9LSU5EX0lOVEVHUklUWRADEiQKIFJVTlRJTUVfRkFJTFVSRV9LSU5EX1VOU1VQUE9SVEVEEAQqpwMKGFJ1bnRpbWVVbmF2YWlsYWJsZVJlYXNvbhIqCiZSVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9VTlNQRUNJRklFRBAAEjIKLlJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX1JFTU9URV9PTkxZX09GRkxJTkUQARIxCi1SVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9SRU1PVEVfVU5SRUFDSEFCTEUQAhItCilSVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9SRU1PVEVfVElNRU9VVBADEjYKMlJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX0xPQ0FMX1JFUExJQ0FfTk9UX1JFQURZEAQSNQoxUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fQ1JFREVOVElBTF9VTkFWQUlMQUJMRRAFEjAKLFJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX1dPUktTUEFDRV9PUEVOSU5HEAYSKAokUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fUEVFUl9CVVNZEAcq8AEKFFJ1bnRpbWVSZWZ1c2FsUmVhc29uEiYKIlJVTlRJTUVfUkVGVVNBTF9SRUFTT05fVU5TUEVDSUZJRUQQABItCilSVU5USU1FX1JFRlVTQUxfUkVBU09OX0lOVkFMSURfQ1JFREVOVElBTBABEigKJFJVTlRJTUVfUkVGVVNBTF9SRUFTT05fQUNDRVNTX0RFTklFRBACEisKJ1JVTlRJTUVfUkVGVVNBTF9SRUFTT05fUExBQ0VNRU5UX1BPTElDWRADEioKJlJVTlRJTUVfUkVGVVNBTF9SRUFTT05fU0VTU0lPTl9VTkJPVU5EEAQqlQQKFkZvbGxvd09ic2VydmF0aW9uUGhhc2USKAokRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX1VOU1BFQ0lGSUVEEAASJAogRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX0RFU0lSRUQQARIgChxGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfRFVFEAISJAogRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX1JVTk5JTkcQAxIkCiBGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfU0VUVExFRBAEEjAKLEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9BV0FJVElOR19ERVBFTkRFTkNZEAUSJAogRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX09GRkxJTkUQBhIvCitGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfSU5WQUxJRF9DUkVERU5USUFMEAcSJAogRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX0JBQ0tPRkYQCBIsCihGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfUkVCT1JOX1JFTU9URUxZEAkSLAooRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX1JFUExBQ0lOR19MT0NBTBAKEjIKLkZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9MT0NBTF9DT1BZX1VOUkVBREFCTEUQCypXCgpMaW5rU3RhdHVzEhsKF0xJTktfU1RBVFVTX1VOU1BFQ0lGSUVEEAASFAoQTElOS19TVEFUVVNfT1BFThABEhYKEkxJTktfU1RBVFVTX0NMT1NFRBACKnYKDFRyYW5zZmVyS2luZBIdChlUUkFOU0ZFUl9LSU5EX1VOU1BFQ0lGSUVEEAASFwoTVFJBTlNGRVJfS0lORF9ERUxUQRABEhYKElRSQU5TRkVSX0tJTkRfRlVMTBACEhYKElRSQU5TRkVSX0tJTkRfUFVTSBADYgZwcm90bzM", [file_google_protobuf_any2, file_nomos_kernel_v1_progress, file_nomos_runtime_v2_correspondence_desk, file_nomos_runtime_v2_custody_desk, file_nomos_runtime_v2_placement_desk, file_nomos_runtime_v2_maintenance_desk, file_nomos_runtime_v2_model_adoption_desk, file_nomos_runtime_v2_receiving_group_desk, file_nomos_runtime_v2_recovery_desk]);
3364
+ file_nomos_runtime_v2_observability_desk = /* @__PURE__ */ fileDesc22("Cilub21vcy9ydW50aW1lL3YyL29ic2VydmFiaWxpdHlfZGVzay5wcm90bxIQbm9tb3MucnVudGltZS52MiJCCgxUcmFjZUNvbnRleHQSEAoIdHJhY2VfaWQYASABKAwSDwoHc3Bhbl9pZBgCIAEoDBIPCgdzYW1wbGVkGAMgASgIIp0CCg5SdW50aW1lRmFpbHVyZRIMCgRjb2RlGAEgASgJEg8KB21lc3NhZ2UYAiABKAkSEQoJY29tcG9uZW50GAMgASgJEhYKCXdvcmtzcGFjZRgEIAEoCUgAiAEBEhMKBmRvbWFpbhgFIAEoCUgBiAEBEhYKCWRpcmVjdGl2ZRgGIAEoCUgCiAEBEjkKCmF0dHJpYnV0ZXMYByADKAsyJS5ub21vcy5ydW50aW1lLnYyLkRpYWdub3N0aWNBdHRyaWJ1dGUSMgoEa2luZBgIIAEoDjIkLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZUZhaWx1cmVLaW5kQgwKCl93b3Jrc3BhY2VCCQoHX2RvbWFpbkIMCgpfZGlyZWN0aXZlIr4BChJSdW50aW1lVW5hdmFpbGFibGUSOgoGcmVhc29uGAEgASgOMioubm9tb3MucnVudGltZS52Mi5SdW50aW1lVW5hdmFpbGFibGVSZWFzb24SEQoJd29ya3NwYWNlGAIgASgJEhEKCXJldHJ5YWJsZRgDIAEoCBIfChJyZXRyeV9hZnRlcl9taWxsaXMYBCABKA1IAIgBARIOCgZkZXRhaWwYBSABKAlCFQoTX3JldHJ5X2FmdGVyX21pbGxpcyJ+Cg5SdW50aW1lUmVmdXNhbBI2CgZyZWFzb24YASABKA4yJi5ub21vcy5ydW50aW1lLnYyLlJ1bnRpbWVSZWZ1c2FsUmVhc29uEhEKCXdvcmtzcGFjZRgCIAEoCRIRCglyZXRyeWFibGUYAyABKAgSDgoGZGV0YWlsGAQgASgJIqUBChNEaWFnbm9zdGljQXR0cmlidXRlEgwKBG5hbWUYASABKAkSFgoMc3RyaW5nX3ZhbHVlGAIgASgJSAASFwoNaW50ZWdlcl92YWx1ZRgDIAEoEkgAEhYKDGRvdWJsZV92YWx1ZRgEIAEoAUgAEhcKDWJvb2xlYW5fdmFsdWUYBSABKAhIABIVCgtieXRlc192YWx1ZRgGIAEoDEgAQgcKBXZhbHVlIqQLCgxSdW50aW1lRXZlbnQSEAoIc2VxdWVuY2UYASABKAQSFAoHc2Vzc2lvbhgCIAEoCUgBiAEBEi8KBXJlYWR5GAogASgLMh4ubm9tb3MucnVudGltZS52Mi5SdW50aW1lUmVhZHlIABI3Cg13YXRjaF9jaGFuZ2VkGAsgASgLMh4ubm9tb3MucnVudGltZS52Mi5XYXRjaENoYW5nZWRIABI5Cg5zeW5jX2NvbXBsZXRlZBgMIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU3luY0NvbXBsZXRlZEgAEjUKDGxpbmtfY2hhbmdlZBgNIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuTGlua0NoYW5nZWRIABI5Cg5zaWduYWxfY2hhbmdlZBgOIAEoCzIfLm5vbW9zLnJ1bnRpbWUudjIuU2lnbmFsQ2hhbmdlZEgAEjsKD2N1c3RvZHlfY2hhbmdlZBgPIAEoCzIgLm5vbW9zLnJ1bnRpbWUudjIuQ3VzdG9keUNoYW5nZWRIABJCChNsb2NhbF93b3JrX3JlamVjdGVkGBAgASgLMiMubm9tb3MucnVudGltZS52Mi5Mb2NhbFdvcmtSZWplY3RlZEgAEjMKC2xhd19jaGFuZ2VkGBEgASgLMhwubm9tb3MucnVudGltZS52Mi5MYXdDaGFuZ2VkSAASQwoPa2VybmVsX3Byb2dyZXNzGBIgASgLMigubm9tb3MucnVudGltZS52Mi5LZXJuZWxQcm9ncmVzc09ic2VydmVkSAASLQoEc3BhbhgTIAEoCzIdLm5vbW9zLnJ1bnRpbWUudjIuUnVudGltZVNwYW5IABI/ChF0cmFuc2Zlcl9wcm9ncmVzcxgUIAEoCzIiLm5vbW9zLnJ1bnRpbWUudjIuVHJhbnNmZXJQcm9ncmVzc0gAEkkKFG1haW50ZW5hbmNlX3Byb2dyZXNzGBUgASgLMikubm9tb3MucnVudGltZS52Mi5SZWFkTWFpbnRlbmFuY2VQcm9ncmVzc0gAEjsKDXNvY2tldF9lZmZlY3QYFiABKAsyIi5ub21vcy5ydW50aW1lLnYyLlBlZXJTb2NrZXRFZmZlY3RIABJUChxjcmVkZW50aWFsX3JlZnJlc2hfcmVxdWVzdGVkGBcgASgLMiwubm9tb3MucnVudGltZS52Mi5DcmVkZW50aWFsUmVmcmVzaFJlcXVlc3RlZEgAElIKG3JlY2VpdmluZ19ncm91cF9jb252ZXJnZW5jZRgYIAEoCzIrLm5vbW9zLnJ1bnRpbWUudjIuUmVjZWl2aW5nR3JvdXBDb252ZXJnZW5jZUgAEkoKF21vZGVsX2Fkb3B0aW9uX3Byb2dyZXNzGBkgASgLMicubm9tb3MucnVudGltZS52Mi5Nb2RlbEFkb3B0aW9uUHJvZ3Jlc3NIABI7Cg9mb2xsb3dzX3N1bW1hcnkYGiABKAsyIC5ub21vcy5ydW50aW1lLnYyLkZvbGxvd3NTdW1tYXJ5SAASRAoUbG9jYWxfY29weV9wcmVzZXJ2ZWQYGyABKAsyJC5ub21vcy5ydW50aW1lLnYyLlByZXNlcnZlZExvY2FsQ29weUgAEk4KGWZvcmVpZ25fcGxhY2VtZW50X2lnbm9yZWQYHCABKAsyKS5ub21vcy5ydW50aW1lLnYyLkZvcmVpZ25QbGFjZW1lbnRJZ25vcmVkSAASQwoTYmluZGluZ19xdWFyYW50aW5lZBgdIAEoCzIkLm5vbW9zLnJ1bnRpbWUudjIuUXVhcmFudGluZWRCaW5kaW5nSAASPwoRd3JpdGVfcHJlcGFyYXRpb24YHiABKAsyIi5ub21vcy5ydW50aW1lLnYyLldyaXRlUHJlcGFyYXRpb25IAEIHCgVldmVudEIKCghfc2Vzc2lvbiK8AQoORm9sbG93c1N1bW1hcnkSEAoIZm9sbG93ZWQYASABKA0SDwoHc2V0dGxlZBgCIAEoDRIMCgRsaXZlGAMgASgNEjcKCndvcmtzcGFjZXMYBCADKAsyIy5ub21vcy5ydW50aW1lLnYyLkZvbGxvd09ic2VydmF0aW9uEiQKF29ic2VydmVkX2F0X3VuaXhfbWlsbGlzGAUgASgESACIAQFCGgoYX29ic2VydmVkX2F0X3VuaXhfbWlsbGlzItECChFGb2xsb3dPYnNlcnZhdGlvbhIRCgl3b3Jrc3BhY2UYASABKAkSNwoFcGhhc2UYAiABKA4yKC5ub21vcy5ydW50aW1lLnYyLkZvbGxvd09ic2VydmF0aW9uUGhhc2USDAoEbGl2ZRgDIAEoCBITCgZkZXRhaWwYBCABKAlIAIgBARIhChRyZXRyeV9hdF91bml4X21pbGxpcxgFIAEoBEgBiAEBEh8KEmR1ZV9hdF91bml4X21pbGxpcxgGIAEoBEgCiAEBEhQKB2F0dGVtcHQYByABKA1IA4gBARIaCg1hY3RpdmVfZWZmZWN0GAggASgJSASIAQFCCQoHX2RldGFpbEIXChVfcmV0cnlfYXRfdW5peF9taWxsaXNCFQoTX2R1ZV9hdF91bml4X21pbGxpc0IKCghfYXR0ZW1wdEIQCg5fYWN0aXZlX2VmZmVjdCJWChpDcmVkZW50aWFsUmVmcmVzaFJlcXVlc3RlZBI4CgdwZW5kaW5nGAEgASgLMicubm9tb3MucnVudGltZS52Mi5Db3JyZXNwb25kZW5jZVBlbmRpbmciPgoMUnVudGltZVJlYWR5EhcKD3J1bnRpbWVfdmVyc2lvbhgBIAEoCRIVCg1rZXJuZWxfc2hhMjU2GAIgASgMIvwBCgxXYXRjaENoYW5nZWQSFwoPc3Vic2NyaXB0aW9uX2lkGAEgASgJEhMKC3Jlc3VsdF91c2RhGAIgASgMEjYKB2ZhaWx1cmUYAyABKAsyIC5ub21vcy5ydW50aW1lLnYyLlJ1bnRpbWVGYWlsdXJlSACIAQESNQoSYXBwbGljYXRpb25fcmVzdWx0GAQgASgLMhQuZ29vZ2xlLnByb3RvYnVmLkFueUgBiAEBEhoKDWludGVnZXJfdmFsdWUYBSABKANIAogBAUIKCghfZmFpbHVyZUIVChNfYXBwbGljYXRpb25fcmVzdWx0QhAKDl9pbnRlZ2VyX3ZhbHVlIpwBCg1TeW5jQ29tcGxldGVkEg4KBnJlYXNvbhgBIAEoCRI3CgZyZXN1bHQYAiABKAsyJy5ub21vcy5ydW50aW1lLnYyLlN5bmNXb3Jrc3BhY2VSZXNwb25zZRI2CgdmYWlsdXJlGAMgASgLMiAubm9tb3MucnVudGltZS52Mi5SdW50aW1lRmFpbHVyZUgAiAEBQgoKCF9mYWlsdXJlIjsKC0xpbmtDaGFuZ2VkEiwKBnN0YXR1cxgBIAEoDjIcLm5vbW9zLnJ1bnRpbWUudjIuTGlua1N0YXR1cyI4Cg1TaWduYWxDaGFuZ2VkEg4KBmRvbWFpbhgBIAEoCRIXCg9wcm9qZWN0aW9uX3VzZGEYAiABKAwiUAoKTGF3Q2hhbmdlZBIOCgZkb21haW4YASABKAkSFgoOcGFja2FnZV9zaGEyNTYYAiABKAkSGgoSY29tcGF0aWJpbGl0eV91c2RhGAMgASgMIt0BCgtSdW50aW1lU3BhbhIQCgh0cmFjZV9pZBgBIAEoDBIPCgdzcGFuX2lkGAIgASgMEhsKDnBhcmVudF9zcGFuX2lkGAMgASgMSACIAQESDAoEbmFtZRgEIAEoCRIaChJzdGFydGVkX3VuaXhfbmFub3MYBSABKAQSFgoOZHVyYXRpb25fbmFub3MYBiABKAQSOQoKYXR0cmlidXRlcxgHIAMoCzIlLm5vbW9zLnJ1bnRpbWUudjIuRGlhZ25vc3RpY0F0dHJpYnV0ZUIRCg9fcGFyZW50X3NwYW5faWQi6gEKFktlcm5lbFByb2dyZXNzT2JzZXJ2ZWQSOQoIc25hcHNob3QYASABKAsyJy5ub21vcy5rZXJuZWwudjEuS2VybmVsUHJvZ3Jlc3NTbmFwc2hvdBIkChxob3N0X29ic2VydmVkX2VsYXBzZWRfbWljcm9zGAIgASgEEioKImhvc3Rfb2JzZXJ2ZWRfcGhhc2VfZWxhcHNlZF9taWNyb3MYAyABKAQSHgoWcHJvY2Vzc19yZXNpZGVudF9ieXRlcxgEIAEoBBIjChtwcm9jZXNzX3BlYWtfcmVzaWRlbnRfYnl0ZXMYBSABKAQiggEKEFRyYW5zZmVyUHJvZ3Jlc3MSFgoOcmVjZWl2ZWRfYnl0ZXMYASABKAQSGAoLdG90YWxfYnl0ZXMYAiABKARIAIgBARIsCgRraW5kGAMgASgOMh4ubm9tb3MucnVudGltZS52Mi5UcmFuc2ZlcktpbmRCDgoMX3RvdGFsX2J5dGVzIjkKEFdyaXRlUHJlcGFyYXRpb24SEgoKcmVxdWVzdF9pZBgBIAEoCRIRCgl3b3Jrc3BhY2UYAiABKAkqygEKElJ1bnRpbWVGYWlsdXJlS2luZBIkCiBSVU5USU1FX0ZBSUxVUkVfS0lORF9VTlNQRUNJRklFRBAAEiEKHVJVTlRJTUVfRkFJTFVSRV9LSU5EX0lOVEVSTkFMEAESIQodUlVOVElNRV9GQUlMVVJFX0tJTkRfUFJPVE9DT0wQAhIiCh5SVU5USU1FX0ZBSUxVUkVfS0lORF9JTlRFR1JJVFkQAxIkCiBSVU5USU1FX0ZBSUxVUkVfS0lORF9VTlNVUFBPUlRFRBAEKqcDChhSdW50aW1lVW5hdmFpbGFibGVSZWFzb24SKgomUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fVU5TUEVDSUZJRUQQABIyCi5SVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9SRU1PVEVfT05MWV9PRkZMSU5FEAESMQotUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fUkVNT1RFX1VOUkVBQ0hBQkxFEAISLQopUlVOVElNRV9VTkFWQUlMQUJMRV9SRUFTT05fUkVNT1RFX1RJTUVPVVQQAxI2CjJSVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9MT0NBTF9SRVBMSUNBX05PVF9SRUFEWRAEEjUKMVJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX0NSRURFTlRJQUxfVU5BVkFJTEFCTEUQBRIwCixSVU5USU1FX1VOQVZBSUxBQkxFX1JFQVNPTl9XT1JLU1BBQ0VfT1BFTklORxAGEigKJFJVTlRJTUVfVU5BVkFJTEFCTEVfUkVBU09OX1BFRVJfQlVTWRAHKvABChRSdW50aW1lUmVmdXNhbFJlYXNvbhImCiJSVU5USU1FX1JFRlVTQUxfUkVBU09OX1VOU1BFQ0lGSUVEEAASLQopUlVOVElNRV9SRUZVU0FMX1JFQVNPTl9JTlZBTElEX0NSRURFTlRJQUwQARIoCiRSVU5USU1FX1JFRlVTQUxfUkVBU09OX0FDQ0VTU19ERU5JRUQQAhIrCidSVU5USU1FX1JFRlVTQUxfUkVBU09OX1BMQUNFTUVOVF9QT0xJQ1kQAxIqCiZSVU5USU1FX1JFRlVTQUxfUkVBU09OX1NFU1NJT05fVU5CT1VORBAEKpUEChZGb2xsb3dPYnNlcnZhdGlvblBoYXNlEigKJEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9VTlNQRUNJRklFRBAAEiQKIEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9ERVNJUkVEEAESIAocRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX0RVRRACEiQKIEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9SVU5OSU5HEAMSJAogRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX1NFVFRMRUQQBBIwCixGT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfQVdBSVRJTkdfREVQRU5ERU5DWRAFEiQKIEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9PRkZMSU5FEAYSLworRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX0lOVkFMSURfQ1JFREVOVElBTBAHEiQKIEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9CQUNLT0ZGEAgSLAooRk9MTE9XX09CU0VSVkFUSU9OX1BIQVNFX1JFQk9STl9SRU1PVEVMWRAJEiwKKEZPTExPV19PQlNFUlZBVElPTl9QSEFTRV9SRVBMQUNJTkdfTE9DQUwQChIyCi5GT0xMT1dfT0JTRVJWQVRJT05fUEhBU0VfTE9DQUxfQ09QWV9VTlJFQURBQkxFEAsqVwoKTGlua1N0YXR1cxIbChdMSU5LX1NUQVRVU19VTlNQRUNJRklFRBAAEhQKEExJTktfU1RBVFVTX09QRU4QARIWChJMSU5LX1NUQVRVU19DTE9TRUQQAip2CgxUcmFuc2ZlcktpbmQSHQoZVFJBTlNGRVJfS0lORF9VTlNQRUNJRklFRBAAEhcKE1RSQU5TRkVSX0tJTkRfREVMVEEQARIWChJUUkFOU0ZFUl9LSU5EX0ZVTEwQAhIWChJUUkFOU0ZFUl9LSU5EX1BVU0gQA2IGcHJvdG8z", [file_google_protobuf_any2, file_nomos_kernel_v1_progress, file_nomos_runtime_v2_correspondence_desk, file_nomos_runtime_v2_custody_desk, file_nomos_runtime_v2_placement_desk, file_nomos_runtime_v2_maintenance_desk, file_nomos_runtime_v2_model_adoption_desk, file_nomos_runtime_v2_receiving_group_desk, file_nomos_runtime_v2_recovery_desk]);
3365
3365
  RuntimeFailureSchema = /* @__PURE__ */ messageDesc22(file_nomos_runtime_v2_observability_desk, 1);
3366
3366
  RuntimeUnavailableSchema = /* @__PURE__ */ messageDesc22(file_nomos_runtime_v2_observability_desk, 2);
3367
3367
  RuntimeRefusalSchema = /* @__PURE__ */ messageDesc22(file_nomos_runtime_v2_observability_desk, 3);
@@ -3375,6 +3375,7 @@ var init_observability_desk_pb = __esm({
3375
3375
  SyncCompletedSchema = /* @__PURE__ */ messageDesc22(file_nomos_runtime_v2_observability_desk, 11);
3376
3376
  LinkChangedSchema = /* @__PURE__ */ messageDesc22(file_nomos_runtime_v2_observability_desk, 12);
3377
3377
  RuntimeSpanSchema = /* @__PURE__ */ messageDesc22(file_nomos_runtime_v2_observability_desk, 15);
3378
+ WritePreparationSchema = /* @__PURE__ */ messageDesc22(file_nomos_runtime_v2_observability_desk, 18);
3378
3379
  RuntimeFailureKindSchema = /* @__PURE__ */ enumDesc15(file_nomos_runtime_v2_observability_desk, 0);
3379
3380
  RuntimeFailureKind = /* @__PURE__ */ tsEnum15(RuntimeFailureKindSchema);
3380
3381
  RuntimeUnavailableReasonSchema = /* @__PURE__ */ enumDesc15(file_nomos_runtime_v2_observability_desk, 1);
@@ -8915,6 +8916,22 @@ var init_credential_sessions = __esm({
8915
8916
  }
8916
8917
  });
8917
8918
 
8919
+ // ../peer-office/src/runtime/credential_failure.ts
8920
+ function recoverableCredential(failure) {
8921
+ return failure.code === "nomos.auth.token_expired" || failure.code === "nomos.auth.token_missing";
8922
+ }
8923
+ function syncNeedsCredentialRefresh(result, failure) {
8924
+ if (failure !== void 0 && recoverableCredential(failure)) return true;
8925
+ const offer = result?.offerReceipt?.outcome;
8926
+ const pushNeedsRefresh = offer?.case === "failure" ? recoverableCredential(offer.value) : result?.pushError?.status === 401;
8927
+ return pushNeedsRefresh || result?.discoveryError?.status === 401;
8928
+ }
8929
+ var init_credential_failure = __esm({
8930
+ "../peer-office/src/runtime/credential_failure.ts"() {
8931
+ "use strict";
8932
+ }
8933
+ });
8934
+
8918
8935
  // ../peer-office/src/runtime/credential_renewal.ts
8919
8936
  function identityTokenClaim(token, claim) {
8920
8937
  const claims = tokenClaims(token);
@@ -9113,19 +9130,11 @@ var init_sha256 = __esm({
9113
9130
 
9114
9131
  // ../peer-office/src/runtime/credential_recovery_desk.ts
9115
9132
  import { create as create16, fromBinary as fromBinary9, toBinary as toBinary8 } from "@bufbuild/protobuf";
9116
- function recoverableCredential(failure) {
9117
- return failure.code === "nomos.auth.token_expired" || failure.code === "nomos.auth.token_missing";
9118
- }
9119
- function syncNeedsCredentialRefresh(event) {
9120
- const failures = [event.result?.pushError, event.result?.discoveryError];
9121
- if (failures.some((failure) => failure?.status === 401 || failure?.status === 403)) return true;
9122
- const text = `${event.failure?.message ?? ""} ${failures.map((failure) => failure?.message ?? "").join(" ")}`.toLowerCase();
9123
- return text.includes("unauthenticated") || text.includes("token expired") || text.includes("invalid token");
9124
- }
9125
9133
  var CredentialRecoveryDesk;
9126
9134
  var init_credential_recovery_desk = __esm({
9127
9135
  "../peer-office/src/runtime/credential_recovery_desk.ts"() {
9128
9136
  "use strict";
9137
+ init_credential_failure();
9129
9138
  init_kernel_pb();
9130
9139
  init_correspondence_desk_pb();
9131
9140
  init_credentials_desk_pb();
@@ -9210,7 +9219,7 @@ var init_credential_recovery_desk = __esm({
9210
9219
  return false;
9211
9220
  }
9212
9221
  observeSync(session, event) {
9213
- if (!syncNeedsCredentialRefresh(event) || this.#tokenless(session)) return;
9222
+ if (!syncNeedsCredentialRefresh(event.result, event.failure) || this.#tokenless(session)) return;
9214
9223
  this.#prune();
9215
9224
  const capability = this.#ports.capabilityFor(session);
9216
9225
  const existing = this.#active.get(capability);
@@ -10417,6 +10426,7 @@ var init_receiving_group_desk = __esm({
10417
10426
  #accounts = /* @__PURE__ */ new Map();
10418
10427
  /** D22: device key hash → the workspaces the console reported this key enrolled in (this process's memory, not truth). */
10419
10428
  #consoleEnrolments = /* @__PURE__ */ new Map();
10429
+ #preparingWrites = /* @__PURE__ */ new Map();
10420
10430
  #sequence = 0;
10421
10431
  #internalRequestId = 30000000000n;
10422
10432
  constructor(ports) {
@@ -10456,16 +10466,124 @@ var init_receiving_group_desk = __esm({
10456
10466
  if (head === account.observedHead) return;
10457
10467
  this.#tails.set(workspace, { ...account, inFlight: false, attempts: 0, notBefore: 0n });
10458
10468
  }
10459
- /** A refused nominal Home action can wait for framework recovery and retry once.
10460
- * Sealed/pending history is never re-signed, and no child is mistaken for Home. */
10469
+ /** Only a proven non-admission can enter recovery. Console-enrolled children
10470
+ * share the owning application's route, selected by credential capability and
10471
+ * principal, never by workspace-name guessing or an unverified payload. */
10461
10472
  recoverOffer(request, response) {
10462
- if (!this.#ports.call || request.command.case !== "kernelCall" || request.command.value.credentialUse !== CredentialUse.SESSION || request.command.value.request?.operation.case !== "offer" || request.command.value.request.operation.value.intent.case !== "call" || response.outcome.case !== "failure" || !["nomos.authz.signer_enrollment_missing", "nomos.authz.device_key_revoked"].includes(response.outcome.value.code)) return;
10473
+ if (!this.#ports.call || request.command.case !== "kernelCall" || request.command.value.credentialUse !== CredentialUse.SESSION || request.command.value.request?.operation.case !== "offer" || request.command.value.request.operation.value.intent.case !== "call") return;
10474
+ const missing = response.outcome.case === "failure" ? response.outcome.value.code === "nomos.authz.signer_enrollment_missing" : response.outcome.case === "offer" && response.outcome.value.outcome.case === "refused" && response.outcome.value.outcome.value.refusal?.code === KernelRefusalCode.FORGED_PRINCIPAL && response.outcome.value.outcome.value.refusal.details?.fields.actor?.kind.case === "text" && response.outcome.value.outcome.value.refusal.details.fields.actor.kind.value === "unenrolled-signer-key";
10475
+ const revoked = response.outcome.case === "failure" && response.outcome.value.code === "nomos.authz.device_key_revoked";
10476
+ if (!missing && !revoked) return;
10463
10477
  const binding = this.#ports.binding(request.command.value.session);
10464
- const account = binding ? this.#accounts.get(binding.workspace) : void 0;
10465
- if (!account) return;
10478
+ if (!binding) return;
10479
+ const enrolled = [...this.#accounts.values()].find((account2) => {
10480
+ const owner = this.#ports.binding(account2.session);
10481
+ return account2.enrolment && owner?.credentialCapability === binding.credentialCapability && owner.principal === binding.principal;
10482
+ });
10466
10483
  const copy3 = fromBinary14(RuntimeRequestSchema, toBinary11(RuntimeRequestSchema, request));
10484
+ if (enrolled?.enrolment) {
10485
+ if (revoked) return this.#writeRefused(binding.workspace, "This device's access has been revoked.");
10486
+ return this.#begin(request, `enrolled-write-${++this.#sequence}`, (workId2) => this.#prepareEnrolledWrite(copy3, binding, enrolled.application, enrolled.enrolment, workId2));
10487
+ }
10488
+ const account = this.#accounts.get(binding.workspace);
10489
+ if (!account) return;
10467
10490
  return this.#begin(request, `receiving-group-authoring-${++this.#sequence}`, (workId2) => this.#recoverOffer(copy3, response, account, workId2));
10468
10491
  }
10492
+ #writeRefused(workspace, detail, reason = RuntimeRefusalReason.ACCESS_DENIED) {
10493
+ return { case: "refused", value: create23(RuntimeRefusalSchema, {
10494
+ reason,
10495
+ workspace,
10496
+ retryable: false,
10497
+ detail
10498
+ }) };
10499
+ }
10500
+ #writeWaiting(workspace, detail) {
10501
+ return { case: "unavailable", value: create23(RuntimeUnavailableSchema, {
10502
+ reason: RuntimeUnavailableReason.CREDENTIAL_UNAVAILABLE,
10503
+ workspace,
10504
+ retryable: true,
10505
+ retryAfterMillis: 1e3,
10506
+ detail
10507
+ }) };
10508
+ }
10509
+ *#prepareEnrolledWrite(request, binding, application, route, workId2) {
10510
+ if (request.command.case !== "kernelCall") throw new Error("write preparation lost its call");
10511
+ const command = request.command.value;
10512
+ const principal = binding.principal;
10513
+ const unavailable = () => this.#writeWaiting(
10514
+ binding.workspace,
10515
+ "This device is still being prepared to save changes. Your change has not been saved. Try again when connected."
10516
+ );
10517
+ this.#ports.emit(binding.session, { case: "writePreparation", value: create23(WritePreparationSchema, {
10518
+ requestId: command.request.requestId,
10519
+ workspace: binding.workspace
10520
+ }) });
10521
+ const key2 = JSON.stringify([binding.credentialCapability, principal, binding.workspace]);
10522
+ const shared = this.#preparingWrites.get(key2);
10523
+ const deadline = this.#ports.clock.monotonicNanoseconds() + 30000000000n;
10524
+ let wake = 0;
10525
+ if (shared) {
10526
+ while (!shared.settled) {
10527
+ if (this.#ports.clock.monotonicNanoseconds() >= deadline) return unavailable();
10528
+ const receipt = yield { case: "wake", value: create23(PeerWakeEffectSchema, {
10529
+ effectId: `${workId2}:join-${++wake}`,
10530
+ delayMillis: 250
10531
+ }) };
10532
+ if (receipt.outcome.case !== "woken") return unavailable();
10533
+ }
10534
+ if (shared.result) return shared.result;
10535
+ } else {
10536
+ const preparation = { settled: false };
10537
+ this.#preparingWrites.set(key2, preparation);
10538
+ try {
10539
+ const secret = this.#ports.authorSecretIfPresent(binding.session);
10540
+ if (!secret) return preparation.result = this.#writeRefused(binding.workspace, "Sign in again to set up this device.", RuntimeRefusalReason.INVALID_CREDENTIAL);
10541
+ const keyHash = sha256Hex(this.#ports.deriveSigningPublicKey(secret));
10542
+ let posture = this.#ports.posture(request, binding.session, principal, secret, false);
10543
+ if (posture.deviceSignerStatus === DeviceSignerStatus.REVOKED) {
10544
+ return preparation.result = this.#writeRefused(binding.workspace, "This device's access has been revoked.");
10545
+ }
10546
+ if (!posture.enrolledDevice || posture.deviceSignerStatus !== DeviceSignerStatus.ACTIVE) {
10547
+ const enrolled = yield* this.#enrolAtConsole(
10548
+ route,
10549
+ binding,
10550
+ `${application}|${principal}`,
10551
+ principal,
10552
+ keyHash,
10553
+ [binding.workspace],
10554
+ `${workId2}:console`
10555
+ );
10556
+ if (enrolled.case === "deferred") return preparation.result = enrolled.credentialRefused ? this.#writeRefused(binding.workspace, "Sign in again to set up this device.", RuntimeRefusalReason.INVALID_CREDENTIAL) : unavailable();
10557
+ if (!(binding.workspace in enrolled.value.heads)) {
10558
+ return preparation.result = this.#writeRefused(binding.workspace, "This device could not obtain access to save changes in this workspace.");
10559
+ }
10560
+ while (true) {
10561
+ posture = this.#ports.posture(request, binding.session, principal, secret, false);
10562
+ if (posture.deviceSignerStatus === DeviceSignerStatus.REVOKED) {
10563
+ return preparation.result = this.#writeRefused(binding.workspace, "This device's access has been revoked.");
10564
+ }
10565
+ if (posture.enrolledDevice && posture.deviceSignerStatus === DeviceSignerStatus.ACTIVE) break;
10566
+ if (this.#ports.clock.monotonicNanoseconds() >= deadline) return preparation.result = unavailable();
10567
+ const receipt = yield { case: "wake", value: create23(PeerWakeEffectSchema, {
10568
+ effectId: `${workId2}:observe-${++wake}`,
10569
+ delayMillis: 500
10570
+ }) };
10571
+ if (receipt.outcome.case !== "woken") return preparation.result = unavailable();
10572
+ }
10573
+ }
10574
+ } catch (error) {
10575
+ if (error instanceof ReceivingGroupRefusal) {
10576
+ return preparation.result = this.#writeRefused(binding.workspace, "This device could not obtain access to save changes in this workspace.");
10577
+ }
10578
+ preparation.result = unavailable();
10579
+ throw error;
10580
+ } finally {
10581
+ preparation.settled = true;
10582
+ this.#preparingWrites.delete(key2);
10583
+ }
10584
+ }
10585
+ return { case: "retried", request, response: this.#ports.call(request, command) };
10586
+ }
10469
10587
  *#recoverOffer(request, response, account, workId2) {
10470
10588
  const workspace = this.#ports.binding(account.session).workspace;
10471
10589
  const deadline = this.#ports.clock.monotonicNanoseconds() + 60000000000n;
@@ -10533,6 +10651,7 @@ var init_receiving_group_desk = __esm({
10533
10651
  this.#opening.clear();
10534
10652
  this.#accounts.clear();
10535
10653
  this.#consoleEnrolments.clear();
10654
+ this.#preparingWrites.clear();
10536
10655
  }
10537
10656
  #begin(request, workId2, procedure) {
10538
10657
  const held = {
@@ -14424,6 +14543,7 @@ var init_replica_follow_controller = __esm({
14424
14543
  init_replica_follow_state();
14425
14544
  init_local_copy_readability();
14426
14545
  init_placement_desk();
14546
+ init_credential_failure();
14427
14547
  ReplicaFollowReconciliationKind = Object.freeze({
14428
14548
  Following: "following",
14429
14549
  NotFollowing: "not_following"
@@ -14691,12 +14811,12 @@ var init_replica_follow_controller = __esm({
14691
14811
  this.#desk.transition(result.workspace, { case: ReplicaFollowEventKind.WentOffline, nowUnixMillis });
14692
14812
  return delayUntilDue(this.#desk.get(result.workspace), nowUnixMillis);
14693
14813
  }
14694
- const credentialFailure = [result.pushError, result.discoveryError].find((failure) => failure?.status === 401 || failure?.status === 403);
14695
- if (credentialFailure) {
14814
+ if (syncNeedsCredentialRefresh(result)) {
14815
+ const offer = result.offerReceipt?.outcome;
14696
14816
  const transition = this.#desk.transition(result.workspace, {
14697
14817
  case: ReplicaFollowEventKind.CredentialRejected,
14698
14818
  nowUnixMillis,
14699
- failure: credentialFailure.message || `remote rejected credential with HTTP ${credentialFailure.status}`
14819
+ failure: (offer?.case === "failure" ? offer.value.message : void 0) || result.pushError?.message || result.discoveryError?.message || "remote requires a refreshed credential"
14700
14820
  });
14701
14821
  if (transition.effects.some((effect) => effect.case === ReplicaFollowEffectKind.RefreshCredential)) {
14702
14822
  return 0;
@@ -16883,7 +17003,7 @@ import { create as create54, fromBinary as fromBinary20 } from "@bufbuild/protob
16883
17003
  function releaseSyncJudgment(workspace) {
16884
17004
  judgedThisLifetime.delete(workspace);
16885
17005
  }
16886
- var PLACEMENT_REF3, LAW_PRODUCTS_MEDIA_TYPE, WorkspaceSyncPhase, trackedMain, NO_COMMON_BASE, deferredRemote, deferredLocal, judgedThisLifetime, MAX_REBASES_PER_ROUND, elapsedMillis, WorkspaceSyncWork;
17006
+ var PLACEMENT_REF3, LAW_PRODUCTS_MEDIA_TYPE, WorkspaceSyncPhase, trackedMain, deferredRemote, deferredLocal, judgedThisLifetime, MAX_REBASES_PER_ROUND, elapsedMillis, WorkspaceSyncWork;
16887
17007
  var init_workspace_sync = __esm({
16888
17008
  "../peer-office/src/runtime/workspace_sync.ts"() {
16889
17009
  "use strict";
@@ -16909,7 +17029,6 @@ var init_workspace_sync = __esm({
16909
17029
  Done: "done"
16910
17030
  });
16911
17031
  trackedMain = "refs/remotes/nomos/main";
16912
- NO_COMMON_BASE = "this replica holds local history with no frontier its upstream ever confirmed; convergence needs a common admitted base \u2014 drop the local copy to refetch it, or re-discover a provider holding this lineage";
16913
17032
  deferredRemote = "refs/remotes/nomos/deferred";
16914
17033
  deferredLocal = "refs/nomos/sync-deferred-local";
16915
17034
  judgedThisLifetime = /* @__PURE__ */ new Map();
@@ -17117,38 +17236,13 @@ var init_workspace_sync = __esm({
17117
17236
  }), { case: "awaiting", requestId });
17118
17237
  }
17119
17238
  /**
17120
- * NO COMMON BASE — a round that cannot converge, and must not pay for history to find out.
17121
- *
17122
- * This replica holds a non-empty ledger but has never recorded a frontier its upstream
17123
- * confirmed (`refs/remotes/nomos/main` is absent). The kernel refuses convergence in that
17124
- * state ("a non-empty live ledger requires a common admitted base"), so every step after the
17125
- * O(1) frontier probe is wasted work — and it is not cheap waste: the fetch negotiates with
17126
- * no `have` (a full-history pack), and the dependency planner decodes EVERY intent from
17127
- * genesis and inspects every change, once per round. That is O(history) per follow round,
17128
- * repeated at every retry, for a round that can only end in a refusal.
17129
- *
17130
- * The one base this round can establish costs nothing and is established before this is
17131
- * asked: an upstream holding exactly this replica's head (the probe's answer) converges it
17132
- * and records the tracking frontier. Anything else needs the local copy to be dropped and
17133
- * refetched (an application decision, which preserves the copy), or a re-discovery that
17134
- * names a provider holding this lineage. Both are reported, at O(1), as this failure.
17135
- */
17136
- /**
17137
- * Establish this round's base when none is tracked, without walking any history it can avoid.
17138
- *
17139
- * The upstream just named its own frontier. If that commit is in THIS replica's lineage, it is
17140
- * a common admitted base by definition — both sides hold it — and adopting it turns the round
17141
- * into an ordinary fast-forward: the offer carries only the suffix above it, the fetch can name
17142
- * it as a truthful `have`, and convergence has the base the kernel requires. One commit walk
17143
- * (no change decoding, no pack, no network) decides it.
17144
- *
17145
- * If it is not in this replica's lineage, no base exists to establish and the round is refused
17146
- * (#noCommonBase) before it leases, fetches or plans anything.
17147
- *
17148
- * A kernel without the commit walk cannot be asked, so it keeps the previous behaviour.
17239
+ * Establish a cheap base when the provider's current tip is in our local chain.
17240
+ * A negative answer means the base is unknown: both sides may have advanced from
17241
+ * an earlier shared commit. Acquire the candidate and let the kernel discover
17242
+ * and judge that history. A tracking bookmark is not an admission prerequisite.
17149
17243
  */
17150
17244
  #establishBase(upstreamHead) {
17151
- if (!this.#noCommonBase() || !this.#kernel.historySummary) return true;
17245
+ if (!this.#missingTrackedBase() || !this.#kernel.historySummary) return true;
17152
17246
  const summary = this.#kernel.historySummary(this.#mount, this.#requestId, upstreamHead, this.#localHead);
17153
17247
  if (!summary.reachedBase) return false;
17154
17248
  this.#previousHead = upstreamHead;
@@ -17163,26 +17257,9 @@ var init_workspace_sync = __esm({
17163
17257
  get establishedBase() {
17164
17258
  return this.#establishedBase;
17165
17259
  }
17166
- #noCommonBase() {
17260
+ #missingTrackedBase() {
17167
17261
  return this.#previousHead === void 0 && this.#localHead !== void 0 && this.#replaceLocalHead === void 0;
17168
17262
  }
17169
- #noCommonBaseResult() {
17170
- return this.#finish(create54(SyncWorkspaceResponseSchema, {
17171
- workspace: this.#mount.workspace,
17172
- ...this.#localHead === void 0 ? {} : { currentHead: this.#localHead },
17173
- stillAhead: true,
17174
- ...this.#offerReceipt === void 0 ? {} : { offerReceipt: this.#offerReceipt },
17175
- pushError: create54(RuntimeTransportFailureSchema, {
17176
- endpoint: this.#offerEndpoint(),
17177
- status: 409,
17178
- refused: true,
17179
- message: NO_COMMON_BASE
17180
- }),
17181
- timings: create54(SyncPhaseTimingsSchema, {
17182
- convergeMillis: elapsedMillis(this.#clock, this.#started)
17183
- })
17184
- }));
17185
- }
17186
17263
  #holdsLocalWork() {
17187
17264
  return this.#command.admit && !!this.#localHead && this.#localHead !== this.#previousHead;
17188
17265
  }
@@ -17334,7 +17411,6 @@ var init_workspace_sync = __esm({
17334
17411
  if (!this.#desiredHead) return this.#transportFailure("resident peer returned no admitted frontier", false);
17335
17412
  this.#candidateDurable = admission.sealedMain === this.#desiredHead;
17336
17413
  if (state.phase === WorkspaceSyncPhase.Probe && this.#desiredHead !== this.#localHead) {
17337
- if (!this.#establishBase(this.#desiredHead)) return this.#noCommonBaseResult();
17338
17414
  if (this.#alreadyJudged(this.#desiredHead)) {
17339
17415
  return this.#finish(create54(SyncWorkspaceResponseSchema, {
17340
17416
  workspace: this.#mount.workspace,
@@ -17346,6 +17422,22 @@ var init_workspace_sync = __esm({
17346
17422
  offerReceipt: this.#offerReceipt
17347
17423
  }));
17348
17424
  }
17425
+ if (!this.#establishBase(this.#desiredHead)) {
17426
+ this.#offerAfterConverge = this.#command.admit;
17427
+ this.#rebaseFrom = void 0;
17428
+ return this.#leaseEffect();
17429
+ }
17430
+ if (this.#establishedBase && !this.#candidateDurable && this.#kernel.resolveRef(this.#mount, this.#requestId, trackedMain) === void 0) {
17431
+ return this.#finish(create54(SyncWorkspaceResponseSchema, {
17432
+ workspace: this.#mount.workspace,
17433
+ currentHead: this.#localHead,
17434
+ previousHead: this.#previousHead,
17435
+ stillAhead: true,
17436
+ directOfferDeferred: true,
17437
+ reconnectRequired: false,
17438
+ offerReceipt: this.#offerReceipt
17439
+ }));
17440
+ }
17349
17441
  if (this.#probeOnly) {
17350
17442
  this.#needsReconciliation = true;
17351
17443
  return this.#finish(create54(SyncWorkspaceResponseSchema, {
@@ -17391,7 +17483,7 @@ var init_workspace_sync = __esm({
17391
17483
  return this.#converge(this.#desiredHead, admission.resultPack.byteLength, 0);
17392
17484
  }
17393
17485
  if (this.#desiredHead === this.#previousHead) return this.#converge(this.#desiredHead, 0, 0);
17394
- if (!this.#establishBase(this.#desiredHead)) return this.#noCommonBaseResult();
17486
+ this.#establishBase(this.#desiredHead);
17395
17487
  if (admission.recoveryRequired !== false) return this.#leaseEffect();
17396
17488
  return this.#transportFailure("resident peer supplied neither the missing delta nor a history gap", false);
17397
17489
  }
@@ -17614,7 +17706,7 @@ var init_workspace_sync = __esm({
17614
17706
  }
17615
17707
  }
17616
17708
  #converge(candidateHead, receivedBytes, prefetchMillis) {
17617
- if (!this.#establishBase(candidateHead)) return this.#noCommonBaseResult();
17709
+ this.#establishBase(candidateHead);
17618
17710
  this.#beforeConverge?.(candidateHead, this.#previousHead);
17619
17711
  const convergeStarted = this.#clock.monotonicNanoseconds();
17620
17712
  let judged;
@@ -20148,13 +20240,21 @@ var init_correspondence_desk = __esm({
20148
20240
  }
20149
20241
  #observeSyncResult(requestId, workId2, result) {
20150
20242
  const timings = result.timings;
20243
+ const receipt = result.offerReceipt?.outcome;
20244
+ const admission = receipt?.case === "admission" ? receipt.value : void 0;
20151
20245
  this.#events.instant("peer.follow.sync-result", {
20152
20246
  request: create59(RuntimeRequestSchema, { protocolVersion: 2, requestId }),
20153
20247
  workspace: result.workspace
20154
20248
  }, {
20155
20249
  "sync.workId": workId2,
20156
20250
  ...result.currentHead ? { "sync.head": result.currentHead } : {},
20251
+ ...result.previousHead ? { "sync.previousHead": result.previousHead } : {},
20252
+ ...admission?.main ? { "sync.providerHead": admission.main } : {},
20253
+ ...admission?.sealedMain ? { "sync.sealedHead": admission.sealedMain } : {},
20157
20254
  "sync.stillAhead": result.stillAhead,
20255
+ "sync.reconnectRequired": result.reconnectRequired,
20256
+ "sync.refused": result.refused,
20257
+ "sync.deferred": result.deferred,
20158
20258
  "sync.directOfferDeferred": result.directOfferDeferred,
20159
20259
  "sync.offline": result.offline,
20160
20260
  ...timings ? {
@@ -23432,6 +23532,7 @@ var init_peer_office = __esm({
23432
23532
  return this.#responses.modelAdoption(request, step);
23433
23533
  }
23434
23534
  #receivingGroupResponse(request, step) {
23535
+ if (step.case === "refused") return this.#response(request, { case: "refused", value: step.value });
23435
23536
  if (step.case === "retried") return this.#birthCustodyResponse(step.request, this.#birthCustody.afterKernelCall(step.request, step.response));
23436
23537
  if (step.case === "pending") return this.#response(request, { case: "correspondencePending", value: step.value });
23437
23538
  if (step.case === "failure") return this.#failure(request, step.code, step.message);
@@ -26456,8 +26557,19 @@ async function ensureApplicationWorkspace(application, opts = {}) {
26456
26557
  }
26457
26558
  const statusUrl = `${cloud}/v2/workspaces/${encodeURIComponent(application)}`;
26458
26559
  const current = await fetch(statusUrl).catch(() => null);
26459
- if (current?.ok) {
26460
- const status = await current.json().catch(() => null);
26560
+ const status = current?.ok ? await current.json().catch(() => null) : null;
26561
+ let unborn = false;
26562
+ if (current?.ok && !status?.lineage && !status?.durableHead && !status?.sealedHead) {
26563
+ const domains2 = await fetch(`${statusUrl}/domains`);
26564
+ if (domains2.status === 409) {
26565
+ const body2 = await domains2.json().catch(() => null);
26566
+ unborn = typeof body2?.error === "string" && body2.error.includes("unborn");
26567
+ }
26568
+ if (!unborn && !domains2.ok) {
26569
+ throw new Error(`could not establish whether application '${application}' has custody (${domains2.status})`);
26570
+ }
26571
+ }
26572
+ if (current?.ok && !unborn) {
26461
26573
  const principal = String(status?.lineage?.principal ?? "");
26462
26574
  const actualParent = String(status?.lineage?.parent ?? "");
26463
26575
  const expectedParent = requestedParent === "root" ? "" : requestedParent;
@@ -26493,7 +26605,7 @@ async function ensureApplicationWorkspace(application, opts = {}) {
26493
26605
  ...principal !== "" && session !== "" && sameIdentity(principal, session) ? { deployPrincipal: principal } : {}
26494
26606
  };
26495
26607
  }
26496
- if (current !== null && current.status !== 404 && current.status !== 409) {
26608
+ if (current !== null && !unborn && current.status !== 404 && current.status !== 409) {
26497
26609
  throw new Error(`could not inspect deploy target '${application}' (${current.status}); refusing to guess that it is unborn`);
26498
26610
  }
26499
26611
  if (requestedParent !== void 0) {
@@ -0,0 +1,72 @@
1
+ // THE HEADLINE FIXTURE — a law with a REAL aggregate invariant (#41: declared
2
+ // invariants run at the gate). `@githolon/testing`'s own tests prove the
3
+ // promise: `expect(app.takeSeat(...)).rejects.toThrow(/seats-exceed-capacity/)`.
4
+ import { z, aggregate, t, Lww, MapOf, directive, create, idOf, refOf, instance, setEntry, query, valueObject } from "@githolon/dsl";
5
+
6
+ const Seat = valueObject("Seat", {
7
+ status: z.enum(["active", "cancelled"]),
8
+ takenAt: z.string(),
9
+ });
10
+
11
+ export const Booking = aggregate(
12
+ "Booking",
13
+ {
14
+ label: t.string().merge(Lww),
15
+ capacity: t.int().merge(Lww),
16
+ seats: t.map(Seat.field()).merge(MapOf(Lww)),
17
+ },
18
+ {
19
+ /** ACTIVE SEATS ≤ CAPACITY — the declared aggregate invariant. */
20
+ invariant: (snap) => {
21
+ const capacity = typeof snap["capacity"] === "number" ? (snap["capacity"] as number) : 0;
22
+ const seats = snap["seats"];
23
+ let active = 0;
24
+ if (seats && typeof seats === "object") {
25
+ for (const seat of Object.values(seats as Record<string, { status?: string }>)) {
26
+ if (seat.status === "active") active += 1;
27
+ }
28
+ }
29
+ return active > capacity ? { reject: "seats-exceed-capacity" } : { accept: true };
30
+ },
31
+ },
32
+ ).public();
33
+
34
+ export const openBooking = directive("openBooking")
35
+ .creates(Booking)
36
+ .payload(z.object({ label: z.string().min(1), capacity: z.number().int() }))
37
+ .plan((p) => {
38
+ create(Booking).set("label", p.label).set("capacity", p.capacity);
39
+ return [];
40
+ });
41
+
42
+ export const takeSeat = directive("takeSeat")
43
+ .mutates(Booking)
44
+ .payload(z.object({ bookingId: idOf(Booking), attendee: z.string().min(1), takenAt: z.string() }))
45
+ .plan((p) => [
46
+ setEntry(
47
+ instance(Booking, p.bookingId),
48
+ "seats",
49
+ p.attendee,
50
+ { status: "active", takenAt: p.takenAt },
51
+ ),
52
+ ]);
53
+
54
+ export const bookingsByLabel = query("bookingsByLabel").key("label").returns(Booking);
55
+
56
+ // Exercise full addresses and explicit null through the generated facade,
57
+ // real offers, custody and query-result decoder, not a hand-built response.
58
+ export const ReferenceCard = aggregate("ReferenceCard", {
59
+ label: t.string().merge(Lww),
60
+ booking: t.nomosRef(Booking).merge(Lww),
61
+ note: t.nullable(t.string().merge(Lww), { reason: "A card may explicitly have no note" }),
62
+ }).public();
63
+
64
+ export const createReferenceCard = directive("createReferenceCard")
65
+ .creates(ReferenceCard)
66
+ .payload(z.object({ label: z.string(), booking: refOf(Booking), note: z.string().nullable() }))
67
+ .plan((p) => {
68
+ create(ReferenceCard).set("label", p.label).set("booking", p.booking).set("note", p.note);
69
+ return [];
70
+ });
71
+
72
+ export const referenceCardsByLabel = query("referenceCardsByLabel").key("label").returns(ReferenceCard);
@@ -0,0 +1 @@
1
+ export default { name: "seats", domains: [{ key: "seats", features: [{ key: "seats", modules: ["./domains/booking.ts"] }] }] };