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 +43 -0
- package/dist/compile_mountability_entry.js +188 -76
- package/dist/conformance-fixture/domains/booking.ts +72 -0
- package/dist/conformance-fixture/nomos.project.ts +1 -0
- package/dist/conformance-probe.mjs +69 -0
- package/dist/dev-server.mjs +165 -71
- package/dist/full-cli.mjs +497 -139
- package/package.json +2 -2
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+
|
|
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
|
-
/**
|
|
10460
|
-
*
|
|
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"
|
|
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
|
-
|
|
10465
|
-
|
|
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
|
-
|
|
14695
|
-
|
|
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:
|
|
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,
|
|
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
|
-
*
|
|
17121
|
-
*
|
|
17122
|
-
*
|
|
17123
|
-
*
|
|
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.#
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
26460
|
-
|
|
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"] }] }] };
|