Skip to content

feat: rename Squad/Platoon/Company → Cell/Cohort/Federation + add Coalition tier (peat#904 Phases 1+2)#957

Merged
kitplummer merged 8 commits into
mainfrom
peat-904-rename-coalition
May 31, 2026
Merged

feat: rename Squad/Platoon/Company → Cell/Cohort/Federation + add Coalition tier (peat#904 Phases 1+2)#957
kitplummer merged 8 commits into
mainfrom
peat-904-rename-coalition

Conversation

@kitplummer
Copy link
Copy Markdown
Collaborator

Summary

Phases 1+2 of the peat#904 workspace rename epic, per ADR-066 (merged in #956).

Folded into one PR by necessity: peat-schema and peat-protocol are workspace siblings and ADR-066 commits to no deprecated aliases — splitting Phase 1 (schema) from Phase 2 (protocol) would leave the workspace uncompilable between merges.

What changes

Vocabulary (4-tier rigid-schema model; tier 5+ deferred to ADR-067):

Old (military) New (abstract)
Squad Cell (consolidates with existing Cell concept — squad_id already matched cell_id)
Platoon Cohort
Company Federation
Battalion (1000+ scale) Coalitionnew tier 4, net-new code

peat-schema (Phase 1 commit 53990a2b):

  • proto/hierarchy.proto: messages renamed, CoalitionSummary added
  • proto/cell.proto: platoon_idcohort_id on CellState
  • proto/command.proto: Scope::SQUAD/PLATOONCELL/COHORT, plus new FEDERATION/COALITION
  • proto/security.proto: HierarchyLevel enum SQUAD/PLATOON/COMPANY/BATTALION → CELL/COHORT/FEDERATION/COALITION; removed BRIGADE/DIVISION (deferred to ADR-067)
  • src/ontology.rs, src/type_registry.rs, src/validation/command.rs, tests/json_roundtrip_test.rs aligned

peat-protocol (Phase 2 commits b9e201d2..3f8d9826):

  • Comprehensive rename across ~30 files / ~459 grep matches
  • New Coalition tier code paths (net-new, not a rename):
    • CoalitionDelta + CoalitionFieldUpdate (16 variants) mirroring FederationDelta
    • SummaryStorage trait: create_/update_/get_/delete_coalition_summary
    • automerge_summary_storage.rs: history-preservation pattern from peat#903 reused; regression test test_update_coalition_summary_preserves_history_peat903 added
    • HierarchicalAggregator: coalition CRUD methods
    • aggregate_coalition(coalition_id, leader_id, Vec<FederationSummary>) -> Result<CoalitionSummary> with full Cell → Cohort → Federation → Coalition build-up test
    • HierarchyLevel::Coalition variant; enum given PartialOrd/Ord/Default so Node < Cell < Cohort < Federation < Coalition ordering is canonical
    • AggregateToCoalition permission granted to Commander role
  • EchelonType/EchelonAggregator enums consolidated into HierarchyLevel/HierarchyAggregator (the event/aggregator.rs re-exports peat_protocol::security::HierarchyLevel)
  • Error variants renamed (SquadFormation { squad_id }CellFormation { cell_id })
  • Legacy aliases (SquadConfig/SquadState/SquadRole/SquadStore) removed; orthogonal Platform* aliases kept

peat-transport (Phase 2 commit 5a26cee6):

  • TAK bridge: EchelonLevel (which had a pre-rename bug treating Squad and Cell as adjacent distinct levels) consolidated to HierarchyLevel { Node, Cell, Cohort, Federation, Coalition }. Old PlatformNode, old Squad+CellCell, PlatoonCohort, CompanyFederation, Formation (battalion+) → Coalition
  • External CoT type-string literals (e.g., a-f-G-U-C-I) intentionally untouched — that's the boundary the ADR carves out

Semantic decisions worth reviewer attention

  1. Scope::Federation / Scope::Coalition routing in command/routing.rs — these tiers don't have a local membership signal on CommandRouter (which only knows about its own cell_id/cohort_id). Default-routed to leader-broadcast (mirrors existing Scope::Broadcast behavior). Higher-tier coordinators pick their own targets at the coordinator layer. Flag if a stricter "drop unknown-tier" behavior is preferred.
  2. peat-transport/src/lib.rs doctest was pre-existing rot (the example called AutomergeIrohBackend::new() with zero args; current signature takes (AutomergeBackend, IrohTransport)). The error was hidden behind the workspace compile failures. Marked the doctest rust,ignore with a placeholder. Worth a follow-up issue.
  3. peat-protocol/src/squad.rs module: not renamed in place — its contents merged into peat-protocol/src/cell/summary.rs (or equivalent; see git log -p for exact placement).

Not in this PR (deliberately)

  • peat-mesh (sibling repo) — Phase 3, separate PR; will develop against this branch via [patch.crates-io] path overrides per the SKILL.md gotcha.
  • CoT/TAK bridge external boundary verification + CI grep gate — Phase 4, separate PR.
  • spec/proto/cap/v1/ spec snapshot — follow-up doc-amendment PR per ADR-066.
  • ADR-021 / ADR-024 / ADR-027 code-example amendments — follow-up doc-amendment PR per ADR-066.
  • Release — explicitly held until peat-mesh integration tests (Phase 3) validate the rename end-to-end. No release: PR is unblocked by this merge alone.

Verification

  • cargo +stable check --workspace: clean
  • cargo +stable test --workspace: 1453 pass / 0 fail
  • Grep gate: grep -rEln '\b(Squad|Platoon|Company|Battalion|Regiment|Brigade|Division|Fireteam)\w*' --include='*.rs' --include='*.proto' peat-schema/ peat-protocol/ peat-transport/ returns zero hits

Test plan

  • cargo check --workspace passes
  • cargo test --workspace: all green
  • Workspace grep gate clean (excludes ADRs/docs/CHANGELOG per ADR-066 carveouts)
  • QA Review pass
  • peat-mesh integration tests (Phase 3, separate PR) prove end-to-end before any release
  • Reviewer reads the three semantic decisions flagged above

Tracks peat#904. Implements ADR-066.

…ier (peat#904 Phase 1)

Per ADR-066. Wire-incompatible — major version bump comes when the
full workspace rename lands.

proto/hierarchy.proto:
- SquadSummary → CellSummary (squad_id → cell_id, consolidates with NodeState.cell_id)
- PlatoonSummary → CohortSummary (platoon_id → cohort_id, squad_ids → cell_ids)
- CompanySummary → FederationSummary (company_id → federation_id, platoon_ids → cohort_ids)
- NEW: CoalitionSummary (coalition_id, federation_ids) — tier 4 of 4
- BoundingBox + AggregationMetadata comments updated for new vocabulary

proto/cell.proto:
- platoon_id → cohort_id on CellState
- file-header comment updated

proto/command.proto:
- Scope::SQUAD → Scope::CELL
- Scope::PLATOON → Scope::COHORT
- NEW: Scope::FEDERATION (= 5), Scope::COALITION (= 6)

proto/security.proto:
- HierarchyLevel: SQUAD/PLATOON/COMPANY/BATTALION → CELL/COHORT/FEDERATION/COALITION
- Removed HIERARCHY_LEVEL_BRIGADE/DIVISION (tier 5+ deferred to ADR-067)

src/ontology.rs: cell/cohort/federation/coalition concepts; military-domain mention dropped
src/type_registry.rs: CellState field descriptor uses cohort_id label
src/validation/command.rs: test fixture uses Scope::Cell
tests/json_roundtrip_test.rs: rewrites SquadSummary tests against CellSummary

peat-schema compiles + tests build clean. Workspace check surfaces 43
consumer-side errors in peat-protocol — Phase 2 work.
… (peat#904 Phase 2 — part 1)

Implements ADR-066's four-tier vocabulary across the hierarchical
aggregation core:

  Squad → Cell, Platoon → Cohort, Company → Federation,
  + new Coalition top tier.

src/hierarchy/deltas.rs:
- SquadDelta/SquadFieldUpdate     → CellDelta/CellFieldUpdate
- PlatoonDelta/PlatoonFieldUpdate → CohortDelta/CohortFieldUpdate
- CompanyDelta/CompanyFieldUpdate → FederationDelta/FederationFieldUpdate
- NEW CoalitionDelta / CoalitionFieldUpdate mirroring FederationDelta
  (federation_count, federation_ids, etc.)

src/hierarchy/storage_trait.rs:
- SummaryStorage trait renamed across four tiers
- NEW create/update/get/delete_coalition_summary methods

src/hierarchy/aggregation_coordinator.rs:
- HierarchicalAggregator surface renamed across four tiers
- NEW coalition methods + deprecated upsert_cell_summary alias

src/hierarchy/state_aggregation.rs:
- aggregate_squad/platoon/company → aggregate_cell/cohort/federation
- NEW aggregate_coalition over FederationSummary inputs
- Shared aggregate_boxes helper replaces three near-duplicate impls

src/hierarchy/maintenance.rs:
- CellState.platoon_id reads → cohort_id (per peat-schema Phase 1)

src/storage/automerge_summary_storage.rs:
- AutomergeSummaryStorage gains coalition_key + full coalition CRUD
- Preserves peat#903 history-fork pattern across all four tiers
- Adds peat#903 regression test for update_coalition_summary

src/storage/{cell_store,node_store,automerge_conversion,
capabilities,file_distribution,automerge_command_storage,mod}.rs:
- platoon_id → cohort_id, squad_id → cell_id at storage layer
- Test fixtures + doc comments updated to new vocabulary
- Legacy Squad* alias dropped from storage/mod.rs

Removed unused stubs:
- peat-protocol/src/squad.rs (dead file, not in lib.rs)
- peat-protocol/src/hierarchy/platoon.rs (1-byte empty stub)

cargo +stable test --workspace passes.
…t#904 Phase 2 — part 2)

Carries the ADR-066 four-tier vocabulary into the security, error,
event-routing, and cell-formation surfaces.

src/security/authorization.rs:
- HierarchyLevel: Node/Squad/Platoon/Company/Battalion
  → Node/Cell/Cohort/Federation/Coalition (top tier per ADR-066)
- Derived `PartialOrd`/`Ord`/`Default` so comparison code outside
  peat-protocol (e.g. the TAK bridge tier filter) can rely on
  `Node < Cell < Cohort < Federation < Coalition`
- Permission::FormPlatoon → FormCohort
- Permission::AggregateToCompany → AggregateToFederation
  + NEW Permission::AggregateToCoalition for the top-tier op
- Display + default policy + tests updated

src/security/{user_auth,audit}.rs: doc/example string refresh.

src/error.rs:
- Error::SquadFormation { squad_id } → CellFormation { cell_id }
- ErrorContext.squad_id → cell_id (+ tests)

src/event/aggregator.rs:
- EchelonType removed; replaced by re-export of HierarchyLevel
- EchelonAggregator → HierarchyAggregator (with `tier`/`tier_id` accessors)
- All test fixtures use Cell/Cohort/Federation/Coalition vocabulary

src/event/{mod,query,emitter,priority_queue,summary,transmitter}.rs:
- Public re-export surface aligned with HierarchyAggregator
- Doc-comments and test fixtures use the new tier vocabulary

src/cell/{coordinator,capability_aggregation,election_policy,
leader_election,messaging}.rs:
- squad_id → cell_id, "squad leader" prose → "cell leader"
- FormationStatus + CellCoordinator surface preserved (cell already
  the canonical inner module)

src/models/{cell/mod.rs,cell/tests.rs,role.rs,operator.rs,mod.rs}:
- CellState.platoon_id → cohort_id (peat-schema Phase 1 wiring)
- assign_platoon/leave_platoon → assign_cohort/leave_cohort
- Test variable names (`squad` → `cell`) and fixture strings normalized
- Legacy `Squad*` aliases dropped from models/mod.rs; Platform* kept

Removed:
- peat-protocol/src/squad.rs (dead stub, was not in lib.rs)

cargo +stable test --workspace passes.
…904 Phase 2 — part 3)

Sweeps the remaining peat-protocol modules to the ADR-066 vocabulary.

src/command/{routing,coordinator,mod,policy_impl,conflict_resolver,
storage_trait}.rs:
- CommandRouter: squad_id/squad_members → cell_id/cell_members
- Scope::Squad/Platoon match arms → Cell/Cohort + new Federation/
  Coalition arms (defer to higher-tier coordinator for routing)
- TargetResolution::AllSquadMembers → AllCellMembers
- derive_authority_level prefix heuristics: cohort-*/cell-* (was
  platoon-*/squad-*) — non-wire vocabulary, doc-only
- All command tests/fixtures use cell-/cohort- IDs

src/discovery/{geographic,coordinator,directed,capability_query}.rs:
- MIN_SQUAD_SIZE → MIN_CELL_SIZE
- can_form_squad / find_formable_squads / should_initiate_squad_formation /
  get_squad_members → can_form_cell / find_formable_cells / etc.
- ValidationResult::SquadNotFound/SquadFull → CellNotFound/CellFull
- transition_to_squad_phase → transition_to_cell_phase
- avg_squad_size → avg_cell_size, squads_formed → cells_formed

src/composition/rules.rs:
- doc-comment "Cell or squad ID" → "Cell ID"

src/mesh_integration/aggregator.rs:
- PacketAggregator now wraps StateAggregator::aggregate_cell
- extract_squad_summary → extract_cell_summary
- SquadSummary payload type → CellSummary

src/network/peer_config.rs:
- doc/example formation id "alpha-company" → "alpha-federation"

src/sync/automerge.rs:
- DQL test fixtures + query strings refreshed to cell/cohort vocabulary
- Internal comment: "soldier"/"squad leader" → "member"/"cell leader"

examples/secure_node.rs:
- AuthorizationContext::for_cell("alpha-squad") → "alpha-cell"

cargo +stable test --workspace passes.
…ase 2 — part 4)

tests/event_routing_integration.rs:
- EchelonAggregator/EchelonType → HierarchyAggregator/HierarchyLevel
- Test fixture IDs use cell-/cohort-/federation- vocabulary
- New `test_tier_hierarchy` covers Cell < Cohort < Federation ordering

tests/discovery_e2e.rs, tests/mdns_discovery_e2e.rs:
- MIN_SQUAD_SIZE → MIN_CELL_SIZE, get_squad_members → get_cell_members
- "alpha-company" formation id → "alpha-federation"

tests/hierarchical_sync_e2e.rs:
- Topology header re-labelled Cohort/member (was Platoon/soldier)

tests/hierarchical_sim_test.rs:
- Role/fixture strings normalized to coalition/cohort/cell/member

tests/iroh_file_distribution_e2e.rs:
- "alpha-squad" distribution scope → "alpha-cell"

peat-schema/src/ontology.rs:
- Phase 1 missed the `test_ontology_creation` test still asserting the
  old "platoon" concept key; corrected to cell/cohort/federation/coalition
  (matches the concepts already registered by build_cap_ontology).
- Updated "hierarchy" concept description from "(platoon/company)" to
  "(cohort/federation/coalition)".

cargo +stable test --workspace now passes end-to-end.
…eat#904 Phase 2 — part 5)

src/tak/bridge/config.rs:
- EchelonLevel enum removed; the bridge now re-exports
  peat_protocol::security::HierarchyLevel.
- Old EchelonLevel had a structural bug where Squad and Cell were
  adjacent distinct levels (pre-rename); collapsed to Cell. Formation
  semantically corresponds to Coalition.
- AggregationPolicy::SquadLeaderOnly → CellLeaderOnly
- should_publish_echelon → should_publish_tier, viewer_echelon →
  viewer_tier; filtering logic rewritten over the four tiers.
- Tests cover Node < Cell < Cohort < Federation < Coalition ordering
  (derived from HierarchyLevel's PartialOrd/Ord).

src/tak/bridge/mod.rs:
- PeatMessage::echelon() → tier(), returning HierarchyLevel
- FormationSummary now maps to HierarchyLevel::Coalition (top tier)

src/tak/bridge/filter.rs:
- should_publish_echelon → should_publish_tier in BridgeFilter
- Test renamed test_bridge_filter_echelon → test_bridge_filter_tier
- "filtering friendly platform" → "filtering friendly node"

src/lib.rs:
- Pre-existing broken doctest (AutomergeIrohBackend::new() needs args)
  marked `rust,ignore`; it was previously hidden by other compile
  errors in the workspace and surfaced cleanly after the rename.

The bridge still reads external CoT type strings verbatim
(e.g. "a-f-G-U-C-I") — those are wire-format artifacts and remain
untouched, only peat-side identifiers were renamed.

cargo +stable test --workspace passes.
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peat QA Review (SHA: 5a26cee)

Scope: peat-schema, peat-protocol, peat-transport vocabulary rename (Squad/Platoon/Company/Battalion → Cell/Cohort/Federation/Coalition) plus net-new Coalition tier. No cryptographic primitive changes. No peat-mesh (Iroh/Automerge sync wire protocol) changes — Phase 3 per PR description. FFI/BLE surface not touched; no UniFFI/JNI/BlueZ review required.


[WARNING] security.proto HierarchyLevel value 4 reused with new semantics

HIERARCHY_LEVEL_BATTALION = 4HIERARCHY_LEVEL_COALITION = 4. Values 5 (BRIGADE) and 6 (DIVISION) are removed from the enum entirely. Any persisted binary-protobuf DeviceIdentity records carrying BATTALION will silently decode as COALITION on upgrade; records carrying BRIGADE or DIVISION will silently decode as HIERARCHY_LEVEL_UNSPECIFIED (0) — proto3 treats out-of-range enum values as the zero default.

ADR-066's no-alias policy drives the intentional value reuse, but there is no annotation in the proto file or upgrade note calling out the silent semantic mutation at value 4 and the silent drop at 5–6.

Action: Add a comment in security.proto at the HierarchyLevel enum (or in the ADR-066 reference doc) explicitly documenting the wire-semantic break at value 4 and that values 5–6 now decode as UNSPECIFIED. If any deployed nodes carry these values in stored identity records, document the required re-provisioning step before merge of any release built on this branch.


[WARNING] Automerge storage key prefixes renamed with no migration path

AutomergeSummaryStorage key format changed across all tiers: squad-summary:*cell-summary:*, platoon-summary:*cohort-summary:*, company-summary:*federation-summary:*. Any Automerge store with existing documents under the old key prefixes will silently return None after upgrade — existing summaries are orphaned in place with no error.

The PR correctly notes that no release is unblocked without Phase 3 integration tests. But Phase 3 will exercise AutomergeSummaryStorage against nodes that may carry state from pre-rename test runs, and get_cell_summary returning None for a pre-existing squad-summary: document is a silent data-loss path.

Action: Either add a one-time key migration step in AutomergeSummaryStorage::new() (scan for old-prefix keys, re-write under new prefixes, delete originals) or explicitly document in Phase 3 test setup that stores must be provisioned clean. The pub(crate) cell_key / cohort_key / federation_key / coalition_key helpers introduced here are a good hook point for such a migration.


[WARNING] Scope::Federation | Scope::Coalition routing paths have no test coverage

command/routing.rs introduces a new match arm for the two new tier scopes. All routing tests in the diff exercise Scope::Cell, Scope::Cohort, Scope::Individual, and Scope::Broadcast; none exercise Scope::Federation or Scope::Coalition. The fallback behavior (cell-leader fanout vs. self-target) is non-trivial and was explicitly flagged in the PR description under "Semantic decisions worth reviewer attention."

Action: Add unit tests for Scope::Federation and Scope::Coalition covering at minimum: (a) node is cell leader → AllCellMembers; (b) node is not cell leader → Self_. These should live alongside test_resolve_cell and test_resolve_individual_self in the existing mod tests block.


[ARCH] Scope::Federation | Scope::Coalition command routing implicit fanout design

The new match arm in CommandRouter::resolve_target falls back to AllCellMembers(cell_members) for any node that leads a cell, independent of whether that cell is actually part of the targeted federation or coalition. A Scope::Federation command addressed to fed-1 will fan out to all cell members on any cell-leading node that sees the command — including nodes in entirely different federations. The router has no federation/coalition membership signal to gate this.

The PR description raises this explicitly and suggests it mirrors Scope::Broadcast behavior, with higher-tier coordinators selecting targets at the coordinator layer. That may be the right call, but the consequence is that federation-scoped commands become de-facto broadcasts at cell boundaries, which could be surprising at Coalition scale (~1000+ peers) if not explicitly intended.

Action: Escalate for explicit design decision: should CommandRouter drop Federation/Coalition commands when it has no membership signal for those tiers (stricter "not for me → NotApplicable"), or is implicit cell-level fanout the intended behavior? Do not inline-fix — this is an ADR-territory question about command delivery semantics at tier 3/4.


[IDIOM] peat-transport/src/lib.rs doctest silenced with ignore instead of corrected

The doctest annotation was changed from rust,no_run to rust,ignore. no_run compiles but does not execute — it would have caught AutomergeIrohBackend::new() requiring arguments at compile time. ignore bypasses compilation entirely. The example now contains placeholder comments (/* AutomergeBackend */, /* IrohTransport */) and provides zero compile-time coverage of the API surface.

Action: Update the example to call AutomergeIrohBackend::new(backend, transport) with correctly-typed bindings and restore rust,no_run. If constructing the argument types is impractical in a doc example, remove the example rather than ignoring it — a silently-stale doc example that bypasses compilation is strictly worse than no example.

Addresses 4 of 5 QA findings from the PR-957 review (5a26cee). Finding #4
(ARCH — Federation/Coalition routing fanout semantics) is deferred per
the reviewer's own framing ("Do not inline-fix — this is an ADR-territory
question") and surfaced to Kit for a design call.

[WARNING] #1 — security.proto HierarchyLevel wire-semantic break
- Added a documentation block at the enum explicitly mapping the old
  SQUAD/PLATOON/COMPANY/BATTALION/BRIGADE/DIVISION values to their new
  CELL/COHORT/FEDERATION/COALITION/(removed) successors.
- Called out that value 4 is rebound (BATTALION → COALITION) and values
  5–6 will decode as UNSPECIFIED on proto3 deserialization of pre-rename
  DeviceIdentity records.
- Added `reserved 5, 6;` to lock those values against accidental reuse.

[WARNING] #2 — Automerge key prefix rename without migration
- Added a "Wire-format break" documentation block on AutomergeSummaryStorage
  explaining that pre-rename squad-summary:* / platoon-summary:* /
  company-summary:* documents are intentionally orphaned per ADR-066's
  "discard old state, replay from peers" decision.
- Noted Phase 3 must exercise this against fresh stores; pointed at the
  cell_key/cohort_key/federation_key/coalition_key helpers as the hook
  point for any future deployment needing pre-rename state migration.

[WARNING] #3 — Scope::Federation/Coalition routing untested
- Added 4 unit tests covering cell-leader fanout + non-leader self-target
  for both Federation and Coalition scopes.
- Added a header comment flagging that these tests pin the CURRENT
  implicit-fanout behaviour, NOT the FINAL behaviour (open per #4).

[IDIOM] #5 — peat-transport doctest silenced with `ignore` instead of fixed
- Rewrote doctest with real Arc<AutomergeBackend> + Arc<IrohTransport>
  bindings so the example type-checks against AutomergeIrohBackend::new.
- Restored `rust,no_run` (gives compile-time coverage, doesn't execute).
- Also corrected stale "CAP Transport" / "cap-*" naming in the module doc.

Verification:
- cargo +stable check --workspace: clean
- cargo +stable test -p peat-protocol command::routing::tests: 8/8 pass
- cargo +stable test -p peat-transport --doc: 1/1 pass (was `ignore`, now compiles)
@kitplummer
Copy link
Copy Markdown
Collaborator Author

Addressing the QA review findings on commit 5a26cee. New commit f0cc7683 lands the four mechanical fixes; finding #4 is deferred per the reviewer's own framing and escalated below.

Fixed

[WARNING] #1security.proto HierarchyLevel wire-semantic break

Added an extensive documentation block on the enum explicitly mapping each old value (SQUAD/PLATOON/COMPANY/BATTALION/BRIGADE/DIVISION) to its new successor (CELL/COHORT/FEDERATION/COALITION/removed). Called out the value-4 rebind (BATTALION → COALITION) and the silent-drop semantic at values 5–6 on proto3 decode. Added reserved 5, 6; to lock those values against accidental reuse — tier 5+ goes through ADR-067, not by re-introducing these values.

[WARNING] #2 — Automerge key-prefix rename with no migration path

Added a "Wire-format break" documentation block on AutomergeSummaryStorage that:

  • Names the old prefixes (squad-summary:*, platoon-summary:*, company-summary:*) explicitly so a future reader sees them
  • Documents that pre-rename docs are intentionally orphaned per ADR-066's "discard old state, replay from peers" decision
  • Calls out the Phase 3 test setup requirement (fresh stores)
  • Points at the cell_key/cohort_key/federation_key/coalition_key helpers as the migration hook point if a future deployment needs to preserve pre-rename state

Chose this over implementing migration because ADR-066 explicitly pre-decided against carrying a one-shot migration layer. If you'd rather ship migration code, happy to add it.

[WARNING] #3Scope::Federation/Coalition routing untested

Added 4 unit tests in peat-protocol/src/command/routing.rs:

  • test_resolve_federation_cell_leader_fans_out
  • test_resolve_federation_non_leader_targets_self
  • test_resolve_coalition_cell_leader_fans_out
  • test_resolve_coalition_non_leader_targets_self

Added a header comment block flagging that these pin the current implicit-fanout behaviour, not the final behaviour (open per finding #4 below). All 8 routing tests pass.

[IDIOM] #5peat-transport/src/lib.rs doctest silenced with ignore

Rewrote the doctest with real Arc<AutomergeBackend> + Arc<IrohTransport> type bindings (as fn arguments, so the example type-checks against AutomergeIrohBackend::new's actual signature) and restored rust,no_run. Doctest now compiles. Also corrected stale "CAP Transport" / "cap-*" naming in the module doc that I noticed while in there.

Deferred per reviewer framing

[ARCH] #4Scope::Federation/Coalition routing fanout semantics

The reviewer explicitly said "Do not inline-fix — this is an ADR-territory question about command delivery semantics at tier 3/4." So I haven't.

@kitplummer — design call needed. Two options:

(a) Keep implicit fanout (current behaviour). Federation/Coalition commands fall back to cell-leader broadcast when the router has no local membership signal. Consequence: federation-scoped commands become de-facto broadcasts at cell boundaries, which could be surprising at Coalition scale (~1000+ peers).

(b) Stricter drop on unknown-tier membership. Federation/Coalition commands resolve to TargetResolution::NotApplicable when the router has no federation/coalition membership signal. Consequence: commands silently drop if the coordinator-layer routing isn't wired up correctly, but no surprise fanout.

Worth noting: this question dissolves cleanly under ADR-067 (parametric N-tier aggregation), since federations would carry membership identifiers the router could check directly. Until ADR-067 lands, we need to pick (a) or (b) for the current rigid model.

My lean is (a) keep implicit fanout, document it as intentional with the same caveat I added to the new tests — but happy to switch to (b) if you'd rather the conservative default.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peat QA Review (SHA: f0cc768)

[ARCH] — CommandRouter Federation/Coalition routing conflates tier membership with cell membership

Location: peat-protocol/src/command/routing.rs, Scope::Federation | Scope::Coalition arm

The router's resolve_target falls back to AllCellMembers(self.cell_members.clone()) for both Scope::Federation and Scope::Coalition because the CommandRouter struct carries no federation_id or coalition_id field — only cell_id / cohort_id. The effect is that any Federation- or Coalition-scoped command arriving at a node fans out to that node's cell members, regardless of whether the node is actually a member of the targeted federation or coalition. The behavior is indistinguishable from a cell-scoped broadcast, but the caller believes they issued a higher-tier command.

The PR documents this explicitly (Semantic Decision #1) and pins the current behavior with four new tests, explicitly marking the tests as pinning current behavior rather than intended behavior, and defers the stricter "drop on unknown-tier membership" alternative to ADR-067. The concern is that this implicit fanout could be inadvertently relied upon before ADR-067 lands — at which point changing the semantics becomes a breaking behavior change for any coordinator that has come to depend on the cell-fanout fallback. Escalating rather than proposing a fix, per the [ARCH] policy.


[WARNING] — CoalitionSummary lacks a JSON roundtrip test in peat-schema

Location: peat-schema/tests/json_roundtrip_test.rs

CoalitionSummary is the only net-new proto message in this PR (the other three tier messages are renames). The json_roundtrip_test.rs file was updated to rename the SquadSummaryCellSummary roundtrip test but no equivalent test was added for CoalitionSummary. The existing test_update_coalition_summary_preserves_history_peat903 in automerge_summary_storage.rs verifies CRDT delta-history semantics, but it does not verify the JSON field names and structure that the Ditto CRDT backend requires for OR-Set / LWW-Register merge operations (member_ids, federation_ids, scalar fields). A test_coalition_summary_json_roundtrip in peat-schema/tests/json_roundtrip_test.rs — parallel to the test_cell_summary_json_roundtrip structure — would close this gap before Phase 3 exercises Coalition against a live Ditto mesh.

…cable (peat#904 PR #957 QA #4)

Per Kit's design call on QA review finding #4 (ARCH): peat commits to
the **coordinator-mediated** dissemination model. Federation- and
Coalition-scope commands are resolved at their tier coordinator and
re-emitted as Cell- or Individual-scope to specific cells/nodes. A
per-cell CommandRouter should never see these scopes inbound.

The previous implementation fell back to "fan out cell-locally when
the router has no higher-tier membership signal" — silently committing
to a fourth dissemination model that no design doc described, and one
that amplifies one command into thousands of cell-level broadcasts at
Coalition scale (~1000+ peers).

This commit:

- Updates the `Scope::Federation | Scope::Coalition` arm in
  `CommandRouter::resolve_target` to return `TargetResolution::NotApplicable`
  and emit `tracing::warn!` so misrouted higher-tier commands are visible
  to operators.

- Adds a substantial module-level doc-comment spelling out the
  coordinator-mediated dissemination model: Cell/Cohort scopes use the
  existing gossip-down-to-cell-leaders path; Federation/Coalition scopes
  are coordinator-resolved at their tier and never reach per-cell
  routers under correct operation.

- Updates the 4 routing tests (added in commit f0cc768) to assert
  `NotApplicable` rather than implicit fanout. Test names made explicit
  about the model:
    - test_resolve_federation_at_cell_router_is_not_applicable_even_as_leader
    - test_resolve_federation_at_cell_router_is_not_applicable_for_non_leader
    - test_resolve_coalition_at_cell_router_is_not_applicable_even_as_leader
    - test_resolve_coalition_at_cell_router_is_not_applicable_for_non_leader
  Header comment block updated to document the model and the rejected
  alternative.

Verification: cargo +stable test -p peat-protocol command::routing::tests
— 8 passed / 0 failed.

This resolves PR #957 QA review finding #4. ADR amendment formalizing
the dissemination model belongs to the follow-up doc PR after Phase 4
ships, per ADR-066's stated convention.
@kitplummer
Copy link
Copy Markdown
Collaborator Author

Finding #4 resolved per Kit's design call: (A) coordinator-mediated dissemination.

Commit 8e680e27 lands:

  • Scope::Federation | Scope::Coalition arm now returns TargetResolution::NotApplicable + emits tracing::warn! so misrouted higher-tier commands are visible to operators
  • Module-level doc-comment added to peat-protocol/src/command/routing.rs spelling out the coordinator-mediated dissemination model
  • 4 routing tests updated to assert NotApplicable (test names made explicit about the model)
  • Test header comment block updated to document the model and the rejected fanout alternative

ADR amendment formalizing the dissemination model belongs to the follow-up doc PR after Phase 4 ships, per ADR-066's stated convention.

8/8 routing tests pass. All 5 QA findings now addressed.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Peat QA Review (SHA: 8e680e2)

No findings.

Security primitives — No cryptographic code touched. No non-FIPS primitives introduced. Clean.

Protocol/schema backward compatibility — Intentional, ADR-066-sanctioned break, documented correctly. Field numbers within messages are preserved (binary wire-compat); values 5–6 dropped from HierarchyLevel are explicitly called out in security.proto with the Old→New mapping table and operator upgrade path. Proto files bump to v2.0.0. The JSON/gRPC-JSON key changes (platoonIdcohortId, SQUADCELL enum strings, etc.) are a natural consequence of the versioned rename and fully in scope per ADR-066's no-deprecated-aliases policy.

Automerge sync semantics (peat-mesh) — Coalition-tier storage follows the peat#903/peat#896-proven pattern (message_to_automerge_into with existing doc). The test_update_coalition_summary_preserves_history_peat903 regression test mirrors the three prior tiers. Automerge key prefix migration ("orphaned documents, replay from peers") is documented in the storage module comment. No sync-semantic breaks found.

peat-ffi / peat-btle — Not touched. Cross-platform binding analysis N/A for this PR.

Surface-tier E2E coverage — No UniFFI/JNI/BLE/FFI surfaces added or modified. All new Coalition API is internal Rust. CRUD coverage (create-once enforcement, update, get, delete) and the peat#903 history-preservation regression are present for all four storage tiers including Coalition.

Command routing (Scope::Federation/Coalition → NotApplicable) — Design decision is sound and well-documented in the module docstring. The coordinator-mediated dissemination model is the correct choice: fanning out a Federation/Coalition-scope command at every cell-tier router would amplify one command into thousands of cell-level broadcasts at Coalition scale. Four tests cover both leader/non-leader cases at both Federation and Coalition scope. No concern.

TAK bridge consolidationEchelonLevelHierarchyLevel correctly fixes the pre-rename bug where Squad and Cell were treated as adjacent distinct levels in the old enum. Filter semantics (CapabilitySummaryOnly top-tier guard, CellLeaderOnly threshold, HierarchicalFiltering viewer-tier logic) are preserved under the new vocabulary. CoT type-string literals correctly left untouched per ADR-066.

peat-transport doctest — Fixed from pre-existing rot; AutomergeIrohBackend::new(backend, transport) now matches the actual constructor signature. Still no_run (cannot construct the full backend in a doctest), which is correct.

Implementation idiom — Production code uses ? for error propagation throughout. unwrap_or(HealthStatus::Failed) fallbacks in health aggregation are intentionally conservative. Clones in delta application helpers are unavoidable (building Vec<FieldUpdate>). No borrow-checker workarounds.

Consumer references — None introduced. Grep gate on the new Coalition code paths is clean.

@kitplummer kitplummer merged commit 4d11dc8 into main May 31, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant