@fabricorg/sdui-release 0.6.0 → 0.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - e41c1f5: `deriveAuthorizationGrants` stopped emitting intents whose action the contract does not declare, on the grounds that such a grant is one validation refuses and promotion could never have routed. The view loop three lines above had the same defect and was not fixed: a view published at two versions, which is a first-class pattern, produced one versionless reference that validation reports as `ambiguous_capability_reference` and promotion cannot route. Those views are now left out of `grants.views` and reported in a new `unaddressable` list on the result, with the reference and the versions that made it unaddressable.
8
+
9
+ ## 0.7.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 8436c64: Close what a seventh review found in the previous follow-up, two of them regressions it introduced.
14
+
15
+ **An idempotency conflict is settled, but which way depends on why.** The previous release treated
16
+ every conflict as "never applied" and reported it refused. Only a `parameters` or `actor` conflict
17
+ means that. An `authority_binding` or `action_version` conflict means the _original_ invocation
18
+ exists and may well have applied — the capture-authority adapter mints a fresh binding on every
19
+ attempt, so a first attempt whose response was lost conflicts with itself on the second. Deleting that
20
+ capture and calling it refused was the worst available answer; it is now surfaced as `unknown` under
21
+ status `conflict_prior_invocation`.
22
+
23
+ **A policy block or validation failure is now `unknown`, not `refused`.** Within one attempt both are
24
+ decided before any effect. On a recovery re-execution the worker skips adapter steps that already
25
+ succeeded, then re-evaluates policy with a fresh clock and re-parses parameters against a possibly
26
+ newer action, and either can flip after effects committed. Unlike the authority path, the host
27
+ persists no execution reason for these, so the client cannot tell first delivery from recovery; it
28
+ errs toward a person checking. The precise fix is for the host to persist `executionReason` on failed
29
+ invocations, which is a store change and is recorded rather than attempted here.
30
+
31
+ **`failed` means "never delivered" again.** A store fault after a successful delivery was being counted
32
+ in it; it now lives only in `storeFailures`. The 0.3.0 changelog's disclosure misdescribed what 0.2.0
33
+ had changed — `parked` never shipped as a count; only `expired` did — and it is corrected in place.
34
+
35
+ Every validation pass now sees a duplicated contract once, so one root cause produces one finding
36
+ rather than one per copy. `deriveAuthorizationGrants` no longer emits a reference for an intent whose
37
+ action the contract does not declare, matching what promotion resolves. A superseded comment above
38
+ `PORT_ID` that asserted the claim the fix retracted is removed.
39
+
3
40
  ## 0.6.0
4
41
 
5
42
  ### Minor Changes
@@ -7,11 +44,14 @@
7
44
  - 4780974: Close what a sixth review found in the previously unreviewed fix batch, and disclose a breaking change
8
45
  that batch shipped under a minor.
9
46
 
10
- **Disclosure, first.** `experience-runtime@0.2.0` changed `OfflineReplaySummary.parked` and
11
- `expired` from counts to arrays of records. That is breaking for any consumer of 0.1.0 `parked > 0`
12
- becomes permanently false and `if (expired)` permanently true and its changelog described the
13
- counts as still present. This release adds `storeFailures` to the same summary; treat the shape as
14
- having changed at 0.2.0.
47
+ **Disclosure, first.** `experience-runtime@0.2.0` changed `OfflineReplaySummary.expired` from a
48
+ count to an array of records breaking for a 0.1.0 consumer, for whom `if (summary.expired)` becomes
49
+ permanently true. (`parked` was introduced and reshaped inside the 0.2.0 window and never shipped as
50
+ a count, so no published consumer could have depended on it.) The 0.2.0 changelog did disclose the
51
+ change, but one of its four aggregated entries still described counts. This release adds
52
+ `storeFailures`, a **required** field on the returned summary: source-breaking for anything that
53
+ constructs an `OfflineReplaySummary` or implements the exported `OfflineCaptureQueue`, which a test
54
+ double may. Treat the shape as changed.
15
55
 
16
56
  **Offline replay.** An authority refusal on a _recovery_ re-execution lands after adapter steps
17
57
  already committed, since the lease worker re-runs authorization but skips succeeded steps rather
package/dist/index.cjs CHANGED
@@ -715,6 +715,25 @@ function validateSduiRelease(input) {
715
715
  ...resolved.remote ? { remote: resolved.remote } : {}
716
716
  });
717
717
  }
718
+ const contractNamespaces = /* @__PURE__ */ new Set();
719
+ const distinctContracts = contracts.filter((contract) => {
720
+ if (contractNamespaces.has(contract.namespace)) {
721
+ add(
722
+ "ambiguous_capability_reference",
723
+ `contracts.${contract.namespace}`,
724
+ `contract for "${contract.namespace}" is supplied more than once`
725
+ );
726
+ return false;
727
+ }
728
+ contractNamespaces.add(contract.namespace);
729
+ return true;
730
+ });
731
+ const seenDocumentNamespaces = /* @__PURE__ */ new Set();
732
+ const distinctDocuments = input.contracts.filter((document) => {
733
+ if (seenDocumentNamespaces.has(document.capability.namespace)) return false;
734
+ seenDocumentNamespaces.add(document.capability.namespace);
735
+ return true;
736
+ });
718
737
  {
719
738
  for (const pack of resolvedPacks) {
720
739
  const approved = input.assembly.componentPacks.find(
@@ -766,7 +785,7 @@ function validateSduiRelease(input) {
766
785
  );
767
786
  }
768
787
  }
769
- for (const document of input.contracts) {
788
+ for (const document of distinctDocuments) {
770
789
  const approved = input.assembly.capabilities.find(
771
790
  (capability) => capability.namespace === document.capability.namespace && capability.usageContractDigest === (0, import_assembly2.computeUsageContractDocumentDigest)(document)
772
791
  );
@@ -786,22 +805,9 @@ function validateSduiRelease(input) {
786
805
  }
787
806
  }
788
807
  const coreComponents = new Set(input.coreComponents ?? []);
789
- const knownViews = buildKnownViews(contracts);
808
+ const knownViews = buildKnownViews(distinctContracts);
790
809
  const grantedViews = buildGrantedReferences(input.grants.views, "grants.views");
791
810
  const grantedIntents = buildGrantedReferences(input.grants.intents, "grants.intents");
792
- const contractNamespaces = /* @__PURE__ */ new Set();
793
- const distinctContracts = contracts.filter((contract) => {
794
- if (contractNamespaces.has(contract.namespace)) {
795
- add(
796
- "ambiguous_capability_reference",
797
- `contracts.${contract.namespace}`,
798
- `contract for "${contract.namespace}" is supplied more than once`
799
- );
800
- return false;
801
- }
802
- contractNamespaces.add(contract.namespace);
803
- return true;
804
- });
805
811
  const knownIntents = buildKnownActionIntents(distinctContracts, add, grantedIntents);
806
812
  reportAmbiguousGrantedViews(distinctContracts, grantedViews, add);
807
813
  for (const reference of grantedViews) {
@@ -820,11 +826,11 @@ function validateSduiRelease(input) {
820
826
  add(
821
827
  "mutation_bypass",
822
828
  `grants.intents.${reference}`,
823
- `release grants "${reference}", which no supplied capability declares as an intent; promotion would fail to route it`
829
+ `release grants "${reference}", which no supplied capability declares as an intent backed by a declared action; promotion would fail to route it`
824
830
  );
825
831
  }
826
832
  }
827
- validateIntentActionDeclarations(contracts, add);
833
+ validateIntentActionDeclarations(distinctContracts, add);
828
834
  const fragmentGrants = /* @__PURE__ */ new Map();
829
835
  for (const [fragmentId, declared] of Object.entries(input.grants.fragments ?? {})) {
830
836
  for (const view of declared.views ?? []) {
@@ -1126,7 +1132,9 @@ function deriveAuthorizationGrants(contracts, held, options = {}) {
1126
1132
  const reference = `capability://${contract.namespace}/${shortName(view.name, contract.namespace, "/")}`;
1127
1133
  if (!bound || bound.views.has(reference)) views.push(reference);
1128
1134
  }
1135
+ const declaredActions = new Set(contract.actions.map((action) => action.actionId));
1129
1136
  for (const intent of actionIntentsOf(contract)) {
1137
+ if (!declaredActions.has(intent.actionId)) continue;
1130
1138
  const reference = `capability://${contract.namespace}/${shortName(intent.name, contract.namespace, ".")}`;
1131
1139
  if (!bound || bound.intents.has(reference)) intents.push(reference);
1132
1140
  }