docs(architecture): resolve review #002 remaining Tier 4 findings

Add ADR-026 (vault key model — HD derivation) recording the foundational
HD-derivation decision, 74' coin type reservation, SLIP-0010/Ed25519
default, secp256k1 feature-gating, and AES-256-GCM cipher choice. These
were previously inline rationale with no ADR (W9).

Extend ADR-018 with an explicit EncryptedData wire format lock — fields,
encoding, and semantics are frozen; no removal without a format-version
migration (W10).

Resolve the remaining guard clauses and spec decisions:

- W2: Capabilities must be immutable after construction (no interior
  mutability). Makes the Arc vs deep-copy clone semantics genuinely
  two-way.
- W5: Published to_* specs are compatibility contracts — best-effort
  mappings are two-way before first publication, one-way after. Version
  generated specs.
- W6: Salt field clarification — v2 salt is permanently unused; a future
  KDF is a different derivation family, not a version-indexed path; the
  field saves a wire-format change only.
- W7: unlock_new returns Zeroizing<String> — the mnemonic is the root of
  trust and must not linger in freed memory.
- W17: OQ-09 WASM — server-side dispatch door is honestly closed
  (Connection is concrete, tokio-bound), not implicitly preserved.
- W18: OQ-10 git — composability fork (raw smart protocol vs call-protocol
  projection) is a separate decision from ERC721 scope.
- W20: from_openapi must prefix imported error codes (HTTP_404) to avoid
  collision with protocol-level codes (NOT_FOUND). Normative rule, not
  naming convention.
- W21: ScopedOperationEnv field is private — construction via new()/
  empty(), query via allows(). Makes the future subgraph refactor
  non-breaking.
- C13: Connection::set_identity — the endpoint does not read identity()
  after handle() returns (Connection is moved into the spawned task).
  Observability is handler-side logging. Simplest honest answer.
- W1: OperationAdapter trait is async, returns Vec<HandlerRegistration>.
  from_call requires async discovery; ADR-022 changed the return type.
- W11: CompositionAuthority::as_identity() defined — constructs a
  synthetic Identity (label as id, scopes, resources) not resolvable via
  IdentityProvider. Second Identity construction path, acknowledged.
- W14: SecretKey is iroh::SecretKey (Ed25519) — consistent with the
  endpoint's iroh dependency.
- W19: Grandchild abort propagation is inherit-by-default (option a) —
  invoke() with no explicit policy inherits parent's policy. ContinueRunning
  auto-propagates to grandchildren unless explicitly overridden.
This commit is contained in:
2026-06-23 08:20:27 +00:00
parent 91159bf574
commit cb98f42cd4
17 changed files with 413 additions and 47 deletions

View File

@@ -152,10 +152,25 @@ context.env.invoke_with_policy(
```
The child's `OperationContext` carries the policy. If the child itself
composes grandchildren, the policy propagates unless the child explicitly
overrides it. This is consistent with the composition authority and scoped
env propagation in ADR-022 — the parent handler decides the child's
runtime context, including abort policy.
composes grandchildren, the policy **propagates by inheritance** — the
grandchild inherits the child's policy (which was the parent's policy,
unless the parent overrode it for the child via `invoke_with_policy`).
`ContinueRunning` does auto-propagate to grandchildren: if a parent opts
its child into `ContinueRunning`, and the child composes grandchildren
without explicitly overriding, the grandchildren also get
`ContinueRunning`. This is consistent with the composition authority and
scoped env propagation in ADR-022 — the parent handler decides the
child's runtime context, including abort policy, and that decision
propagates through the composition tree by default.
**Review #002 W19 resolution**: `invoke()` with no explicit policy
argument inherits the parent's current policy (option a). It does **not**
reset to `AbortDependents`. A handler that wants a child to reset to the
default must explicitly call `invoke_with_policy(...,
AbortPolicy::AbortDependents)`. This makes the propagation predictable:
the policy I set for my child applies to my child's children unless they
re-decide. The `invoke()` default in operation-registry.md
(`abort_policy: parent.abort_policy.clone()`) is correct.
The `OperationEnv` trait gains an optional policy parameter. The specific
API shape (a separate `invoke_with_policy` method, a policy field on an