Skip to content

spec(v2.3): multi-cell fabric RFCs — cross-org set, cell-quorum, human-escalate (refs #53 #54 #55) - #57

Open
Tailor-AUS wants to merge 6 commits into
mainfrom
agent/spec-multicell
Open

Tailor-AUS wants to merge 6 commits into
mainfrom
agent/spec-multicell

Conversation

@Tailor-AUS

@Tailor-AUS Tailor-AUS commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

DO NOT MERGE without Knox sign-off

This PR is normative spec text and per AGENTS.md rule 5 it MUST NOT merge without explicit maintainer sign-off. It opens the spec/v2.3/ DRAFT line and lands the three multi-cell fabric RFCs as one coherent change set — quorum without a silence back-door.

Refs #53, refs #54, refs #55 (deliberately not Closes — the issues stay open until sign-off + merge).

What's here (one commit per RFC)

  1. DRAFT carry-forward scaffoldspec/v2.3/ opened as a mechanical copy of stable spec/v2.2/ (v2.0.3 body + §24 Matters), with a DRAFT / NOT-FOR-CITATION banner in README.md and the SPECIFICATION.md header. spec/v2.2/ is untouched (AGENTS.md rule 4).
  2. refs [SPEC] Multi-cell participant set is always cross-org (§15.4) #53 — Cells + fifth §15.4 trigger. Cell = vendor-neutral isolation grouping identified by a cell DID (SHOULD did:key); membership carries cell_id; participant cell set fixed at fabric establish. Different cell_id on the same fabric ⇒ cross-organisation, so §17.6 authorization_proof and the §17.9 Authorization-Required checks fire on the cross-cell wire. Cell boundaries are never inferred from DID method (two did:key agents in one cell is normal).
  3. refs [SPEC] cell-quorum + ban agent-count/silence-merge on multi-cell fabrics #54cell-quorum + policy bans. New ApprovalPolicy: ≥1 counted approval from each cell, bucketed by cell_id not head-count; self-approval rule applies inside a cell; a dark cell fails closed (never shrinks the set); human-only on multi-cell = human-only AND cell-quorum. When participant cells > 1: objection-based / auto / single / majority / unanimous MUST NOT be configured — establish/policy-change refused, fail closed. §10.5 notes silence-merge is single-cell only; §15.1 gains the cellQuorum capability flag.
  4. refs [SPEC] Multi-cell conflicts MUST human-escalate #55 — multi-cell conflicts MUST human-escalate. first-wins and merge-both MUST NOT be configured across cells; vote is not a substitute unless it is itself cell-quorum and still escalates when any cell is dark.

Scope and non-collisions

  • Target line is spec/v2.3/ only — frozen spec/v2.0/ and stable spec/v2.2/ are not edited; draft spec/v2.1/ is not touched.
  • No collision with PR spec(v2.1): §25 consensus/authorization/legal-execution boundary + §17.14 (#41, ports #44) #45 (§25 consensus/authorization/legal-execution boundary — v2.1-line): that work is complementary ("consensus ≠ legal signature"); this PR is "different cells ⇒ cross-org" + merge sovereignty. §25 folds into this line via its own vehicle when it lands; this PR references it non-normatively only.
  • Design record: docs/v2-prep/rfc-multi-cell-quorum.md Changes 1–4.
  • Schemas and conformance vectors are carried unchanged from v2.2; the cell_id membership schema field and multi-cell vectors are flagged in spec/v2.3/README.md as required before promotion.

Review order

#53#54#55 (each builds on the prior; #53 must land first or HAL never applies on the path #54/#55 protect).

Adversarial review response (F1–F4, addressed in d8fa365)

  • F1cell_id is no longer self-asserted: §15.4 now requires the claim to be bound to a proof of control of the cell DID — a cell membership attestation (signature over {fabric id, member DID, cell_id} verifiable against the cell DID) carried in join/_onboard, or a cell-scoped invite minted under the cell DID's authority (host/initiator-minted invites explicitly prove nothing). Unproven claims MUST be rejected, or at most bucketed as the joining operator's own attested cell — never the claimed one — closing the bucket-stuffing and dead-cell-kept-"alive" attacks. The residual limit (one operator minting N did:key cell DIDs) is named in the text: proof-of-control cannot prove distinct controllers, so the cell set is verified out-of-band at establish, like counterparty principal_ids. The attestation envelope + participant_cells schema work and negative vectors are on the README promotion-blocker list.
  • F2docs/v2-prep/rfc-multi-cell-quorum.md is now committed on this branch, with a landing addendum recording issue numbers, the single-PR vehicle, the "§25 is v2.1-line" correction, and the F1 hardening the landed text adds beyond the RFC.
  • F3 — enforcement is bound to behaviour, not the flag: §5 states the multi-cell MUSTs are unconditional; a server that does not implement them MUST reject multi-cell establishes and cell_id-bearing joins. README Extended claim and the §15.1 cellQuorum flag description re-cut to match (the flag advertises support only).
  • F4 — participant cell set is constituted by a participant_cells establish field, with first-join binding as the compatibility fallback; omitted-cell_id memberships form one implicit cell for every cardinality test ({∅, X} is two cells; new no-cell_id joins on a multi-cell fabric are rejected, pre-existing ones keep implicit-cell bucketing); human approvals bucket by the membership through which they are recorded (an approval carried by no membership satisfies no bucket).

⚠️ v2.3 scaffold collision

Branch spec/consensus-not-signature (14bc68a) also scaffolds spec/v2.3/ (carrying §25). Whichever line promotes first wins the scaffold; the other MUST rebase onto it rather than bring its own copy — two independent spec/v2.3/ directories will conflict file-by-file. Flagging here for whoever promotes first.

🤖 Generated with Claude Code


Note

Cursor Bugbot is generating a summary for commit 1163fa4. Configure here.

TailorAI-AUS and others added 4 commits August 20, 2026 13:28
…e (refs #53, refs #54, refs #55)

Mechanical copy of spec/v2.2/ into spec/v2.3/ plus a DRAFT banner
(README + SPECIFICATION.md header). No normative change in this commit;
spec/v2.2/ is untouched per AGENTS.md rule 4. NOT FOR CITATION until
maintainer sign-off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… cross-org (refs #53)

Defines cell as a vendor-neutral isolation grouping identified by a
cell DID (SHOULD did:key); membership carries cell_id; the participant
cell set is fixed at fabric establish. Adds the fifth §15.4
cross-organisation trigger: different cell_id on the same fabric =>
cross-org, so §17.6 authorization_proof and the §17.9
Authorization-Required checks fire on the cross-cell wire. Cell
boundaries are never inferred from DID method.

Design record: docs/v2-prep/rfc-multi-cell-quorum.md Change 1.
Complementary to the v2.1-line §25 work (#41 / PR #45), not a
substitute. DRAFT — no normative merge without maintainer sign-off
(AGENTS.md rule 5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…merge on multi-cell fabrics (refs #54)

Adds the cell-quorum ApprovalPolicy (§5): >=1 counted approval from
each cell in the fixed participant cell set, bucketed by cell_id, not
head-count. Self-approval rule still applies within a cell. A dark
cell does not shrink the set — fail closed. human-only on a multi-cell
fabric is evaluated as human-only AND cell-quorum.

When the participant cell set has cardinality > 1, objection-based,
auto, single, majority, and unanimous MUST NOT be configured; establish
and policy changes that would violate this are refused (fail closed,
never a silent downgrade). §10.5 notes objection-based merge is a
single-cell mechanism. §15.1 gains the cellQuorum capability flag.

Design record: docs/v2-prep/rfc-multi-cell-quorum.md Changes 2-3.
Builds on the §15.4 cell trigger (#53). Complementary to v2.1-line §25
(#41 / PR #45): sovereignty, not signature law. DRAFT — no normative
merge without maintainer sign-off (AGENTS.md rule 5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
§5 Conflict Detection: when the participant cell set (§15.4) has
cardinality > 1, the conflict strategy MUST be human-escalate;
first-wins and merge-both MUST NOT be configured (refused, fail
closed). vote is not a substitute unless it is itself cell-quorum AND
still escalates when any cell in the fixed participant set is dark.

Design record: docs/v2-prep/rfc-multi-cell-quorum.md Change 4. Lands
after the §15.4 cell trigger (#53) and cell-quorum (#54). DRAFT — no
normative merge without maintainer sign-off (AGENTS.md rule 5).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| `Observer` | Read content, sections, events. Set salience. |
| `Suggester` | All Observer permissions + propose, declare intent, publish constraints. |
| `Collaborator` | All Suggester permissions + approve, reject, object, lock sections. |
| `Autonomous` | All Collaborator permissions + proposals auto-merge (bypass approval policy). |

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Autonomous trust bypasses cell quorum

High Severity

The new multi-cell bans close auto and silence-merge so one cell cannot write into another without per-cell approval, but Autonomous still auto-merges and bypasses approval policy. A human in one cell can also raise a peer to Autonomous. That reopens the isolation back-door this line exists to close.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1163fa4. Configure here.

Comment thread spec/v2.3/SPECIFICATION.md Outdated
Comment thread spec/v2.3/SPECIFICATION.md Outdated
- **Edit the document directly** (creates a `pact.proposal.merged` event with `ActorKind = Individual`)
- **Change an agent's trust level** (upgrades/downgrades autonomy)
- **Remove an agent** (force `agent.leave`)
- **Change approval policy** (e.g., switch from `majority` to `human-only`)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Human policy change evades cell bans

Medium Severity

Section 5 refuses any policy change that would put objection-based, auto, single, majority, or unanimous on a multi-cell fabric. Section 9.2 still lets a human change approval policy at any time, including toward those banned policies, so the fail-closed rule and the human-override list disagree.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1163fa4. Configure here.

@Tailor-AUS

Copy link
Copy Markdown
Contributor Author

Adversarial spec review — verdict: FIX FIRST

Independent review against the three change contracts (#53 / #54 / #55), stable spec/v2.2/, and the in-flight PR #45 §25 line. Read-only review; no merge action (AGENTS.md rule 5).

What passed

  • Scaffold fidelity. At 651109d, spec/v2.3/ is byte-identical to spec/v2.2/ file-by-file except README.md and the SPECIFICATION.md header banner; no files dropped; the three RFC commits touch only spec/v2.3/SPECIFICATION.md (+46/−4). spec/v2.2/ is untouched — rule 4 respected.
  • Completeness vs the issue bodies. Every mandated item is present: fifth §15.4 trigger with no-DID-inference; fixed participant cell set; cell-quorum bucketing by cell_id; self-approval inside the cell; dark-cell fail-closed with the denominator-recompute ban; human-only = human-only AND cell-quorum; the exact five-policy ban (objection-based/auto/single/majority/unanimous — complete against the §5 policy table) with refuse + fail-closed and no silent downgrade; the §10.5 single-cell note; the §15.1 cellQuorum flag; human-escalate MUST with first-wins/merge-both MUST NOT and the vote carve-out (cell-quorum-bucketed AND dark-cell escalation) — complete against the conflict-strategy list.
  • No contradiction with v2.2 invariants or PR spec(v2.1): §25 consensus/authorization/legal-execution boundary + §17.14 (#41, ports #44) #45. §25 is referenced non-normatively and correctly as v2.1-line; §25 governs the consensus→legal-execution boundary, this PR governs the draft merge crossing an isolation boundary — orthogonal, and the single-cell objection-based recommendation is explicitly scoped so the two texts don't collide.
  • The two self-flagged gaps are genuinely non-blocking for a DRAFT. The missing cell_id membership-schema field and multi-cell conformance vectors are mechanical consequences of prose that is already normative, are correctly listed in spec/v2.3/README.md as promotion blockers, and can't change the design.

FIX FIRST findings

F1 (blocking for sign-off): cell_id is asserted, not proven — the gaming this line exists to ban is still open. §15.4 says cell_id "is declared at join / _onboard time" (SPECIFICATION.md:1564) and later joins "MUST declare a cell_id already in the set" (1565) — but nothing binds the claim to the cell DID's controller. Invite tokens are minted by the fabric host/initiator (§15.6) and are not cell-scoped, so: (a) the initiator can onboard its own agent with the counterparty's cell_id and fill that cell's bucket — defeating cell-quorum, the dark-cell fail-closed rule (the spoofed member keeps a dead cell "alive"), and the §5 conflict rule's vote carve-out in one move; (b) a single operator can mint N did:key cell DIDs at establish and satisfy cell-quorum alone. The cell DID SHOULD be did:key, so the fix is cheap and native: require the membership's cell claim to be authorized by the cell DID controller (e.g. a signature over {member DID, fabric id, cell_id} verifiable against the cell DID, carried in join/_onboard), or cell-scoped invite issuance. At absolute minimum this belongs on the README promotion-blocker list next to the schema field — right now it is unflagged, and it is the security core of the whole line, not a vector/schema mechanic.

F2: the design record cited everywhere is not in the PR. docs/v2-prep/rfc-multi-cell-quorum.md is referenced by the SPECIFICATION header banner, spec/v2.3/README.md, the PR body, and all three issues — but the file is absent from agent/spec-multicell (it exists only untracked in a local checkout). As merged, the draft's stated design authority is a broken link. Commit it or de-reference it.

F3: capability-gated enforcement contradicts the unconditional MUSTs. spec/v2.3/README.md (Conformance level claims) says Extended = v2.2 Extended + "implementations advertising capabilities.cellQuorum MUST enforce" the bans, while SPECIFICATION §5 states the bans unconditionally. Under the README reading, a server that accepts cell_id-bearing memberships but does not advertise cellQuorum can run majority across two cells and still claim Extended conformance. Bind enforcement to behaviour, not advertisement: a server that does not implement the multi-cell rules MUST reject multi-cell establishes / cell_id-bearing joins.

F4 (draft-polish, non-blocking): establish-time set declaration is circular. The participant cell set is "fixed at fabric establish" (1565) but is defined as the distinct cell_id values across memberships — which largely don't exist yet at establish. No manifest/establish field carries the intended set. Two adjacent ambiguities: mixed omitted/declared cell_id memberships make cardinality ambiguous (is {∅, X} one cell?); and human-only AND cell-quorum requires a human approval per cell bucket, but humans are principals, not memberships, and carry no cell_id — say explicitly which membership's cell a human approval buckets to. All three resolve naturally when the cell_id schema work lands, but the prose should say so.

Heads-up (not a PR #57 defect): branch spec/consensus-not-signature (14bc68a) also creates a spec/v2.3/ carrying §25. If that vehicle is still live, two independent v2.3 scaffolds will collide; the PR body's "§25 folds in via its own vehicle" needs that vehicle to rebase onto this scaffold, not bring its own.

Verdict: FIX FIRST — F1 (or at minimum flagging F1 as a promotion blocker), F2, and F3 before sign-off; F4 at leisure within the draft line.


Adversarial spec reviewer (read-only). Refs #53 #54 #55.

TailorAI-AUS and others added 2 commits August 20, 2026 18:35
…enforcement, cell-set constitution (refs #53, refs #54, refs #55)

Addresses PR #57 adversarial review findings F1, F3, F4:

- F1: cell_id MUST be bound to proof of control of the cell DID — a
  cell membership attestation (signature over {fabric id, member DID,
  cell_id} verifiable against the cell DID) carried in join/_onboard,
  or a cell-scoped invite minted under the cell DID's authority.
  Host/initiator-minted invites are explicitly not cell-scoped.
  Unproven cell_id claims MUST be rejected, or at most bucketed as the
  joining operator's own attested cell — never the claimed one. Notes
  the residual limit (proof-of-control cannot prove distinct
  controllers; the cell set is verified out-of-band at establish).
- F3: enforcement binds to behaviour, not the capabilities.cellQuorum
  flag — the Sec.5 MUSTs are unconditional; a server not implementing
  the multi-cell rules MUST reject multi-cell establishes and
  cell_id-bearing joins. README Extended claim and Sec.15.1 flag text
  re-cut to match; promotion-blocker list now names the attestation
  envelope, participant_cells field, and negative vectors.
- F4: participant cell set constituted via participant_cells at
  establish, with first-join binding as the compatibility fallback;
  omitted-cell_id memberships form one implicit cell for every
  cardinality test ({no cell_id, X} is two cells); human approvals
  bucket by the membership through which they are recorded.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g addendum (refs #53, refs #54, refs #55)

Fixes PR #57 review F2: the design record cited by the spec/v2.3
banner, README, PR body and all three issues existed only untracked in
a local checkout. Committed with a landing addendum recording: issue
numbers as filed, single-PR landing vehicle, the Sec.25-is-v2.1-line
correction, and the F1 proof-of-control hardening the landed text adds
beyond the RFC (plus the OQ2 resolution).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Tailor-AUS

Copy link
Copy Markdown
Contributor Author

Findings addressed in d8fa365 (spec fixes in 57d3b8a, design record in d8fa365).

  • F1: cell_id now normatively bound to proof-of-control of the cell DID (cell membership attestation over {fabric id, member DID, cell_id} in join/_onboard, or cell-scoped invite minted under the cell DID's authority); unproven claims MUST be rejected or at most bucketed as the joining operator's own attested cell. Residual N-cell-DIDs-one-operator limit named explicitly; attestation/participant_cells schema work + negative vectors added to the README promotion-blocker list.
  • F2: docs/v2-prep/rfc-multi-cell-quorum.md committed with a landing addendum (issue numbers, single-PR vehicle, §25-is-v2.1-line correction, F1 hardening noted as beyond-RFC).
  • F3: enforcement bound to behaviour — §5 MUSTs unconditional; non-implementing servers MUST reject multi-cell establishes and cell_id-bearing joins; README Extended claim and §15.1 flag text re-cut (flag advertises support only).
  • F4: participant_cells at establish with first-join-binding fallback; omitted cell_id = one implicit cell for cardinality ({∅, X} is two cells); human approvals bucket by the membership through which they are recorded.

The spec/consensus-not-signature scaffold collision is flagged in the PR body for whoever promotes first. Refs #53 #54 #55. No merge action (AGENTS.md rule 5).

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

There are 4 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit d8fa365. Configure here.

- Proof of control binds a membership to a cell key; it cannot prove that two cell DIDs have **distinct controllers** — a single operator can mint any number of `did:key` cell DIDs. The participant cell set is therefore part of what the parties agree to at establish (next bullet): counterparties SHOULD verify out-of-band that each cell DID in the set is controlled by the party they believe controls it, exactly as they verify counterparty `principal_id`s today. `cell-quorum` (§5) is only as strong as this establishment-time verification.
- **Participant cell set constitution.** The participant cell set is declared at fabric establish via a **`participant_cells`** field on the establish document / fabric manifest — an array of cell DIDs. Every membership MUST declare (and prove) a `cell_id` present in that array. For fabrics established without `participant_cells` (single-cell operation and pre-v2.3 compatibility), the set is constituted at **first-join binding**: it becomes fixed at the moment the first membership bearing a proven `cell_id` distinct from the existing members' cell is accepted, and from that event on it is closed exactly as if it had been declared at establish. In either mode, every membership added later MUST declare a `cell_id` already in the set, and extending the set is a re-establishment of the fabric, not a membership operation.
- Implementations MUST NOT infer cell boundaries from DID method or DID contents. Two `did:key` agents in one cell is normal; the first four triggers above continue to operate independently of cells.
- **Omitted `cell_id` and cardinality.** All memberships that omit `cell_id` are bucketed together as one **implicit cell** — the establishing operator's cell. For every cardinality test in this line the implicit cell counts as exactly one cell: a fabric whose memberships are `{no cell_id, cell X}` has a participant cell set of cardinality 2, and every multi-cell rule applies to it. Once the set has cardinality > 1, a *new* `join` / `_onboard` without a `cell_id` is invalid and MUST be rejected; memberships accepted while the fabric was still single-cell retain their implicit-cell bucketing. On single-cell fabrics `cell_id` MAY be omitted (the fabric is then a one-cell fabric by construction and this line changes nothing for it).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Join-time cells skip policy bans

High Severity

first-join binding and an omitted-cell_id join can raise participant-cell cardinality above 1 after establish, but §5 only refuses a banned approval policy or conflict strategy at establish or later policy/strategy change. A fabric that started single-cell under objection-based, majority, or first-wins can become multi-cell with that policy still in force, so silence-merge or agent-count merge can still write across a new cell boundary.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d8fa365. Configure here.

- a **cell-scoped invite**: the invite token redeemed by the join (§15.6) was itself minted under the cell DID's authority and names that `cell_id`, so token redemption is the proof. Invite tokens minted by the fabric host or initiator are **not** cell-scoped and prove nothing about the joiner's cell.
- A `join` / `_onboard` whose `cell_id` carries no valid proof of control **MUST be rejected**. As the only permitted alternative, an implementation MAY accept the membership with the unproven claim **discarded** and the membership bucketed under a cell the joining operator itself demonstrably controls (its own attested cell, or the implicit cell below when the joiner is the establishing operator); it MUST NOT record or act on the unproven `cell_id`. In no case may an unproven claim place a membership in another party's approval bucket — the attack this rule closes is the initiator onboarding its own agents under the counterparty's `cell_id` to fill that cell's quorum bucket (or to keep a dark cell artificially "alive").
- Proof of control binds a membership to a cell key; it cannot prove that two cell DIDs have **distinct controllers** — a single operator can mint any number of `did:key` cell DIDs. The participant cell set is therefore part of what the parties agree to at establish (next bullet): counterparties SHOULD verify out-of-band that each cell DID in the set is controlled by the party they believe controls it, exactly as they verify counterparty `principal_id`s today. `cell-quorum` (§5) is only as strong as this establishment-time verification.
- **Participant cell set constitution.** The participant cell set is declared at fabric establish via a **`participant_cells`** field on the establish document / fabric manifest — an array of cell DIDs. Every membership MUST declare (and prove) a `cell_id` present in that array. For fabrics established without `participant_cells` (single-cell operation and pre-v2.3 compatibility), the set is constituted at **first-join binding**: it becomes fixed at the moment the first membership bearing a proven `cell_id` distinct from the existing members' cell is accepted, and from that event on it is closed exactly as if it had been declared at establish. In either mode, every membership added later MUST declare a `cell_id` already in the set, and extending the set is a re-establishment of the fabric, not a membership operation.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Quorum set ignores first-join cells

High Severity

First-join binding now constitutes the participant cell set after establish, but cell-quorum still says the set used for bucketing and dark-cell fail-closed is the one fixed at establish. A cell added only at first-join can be left out of the denominator, so the new cell never has to approve and going dark does not block merge.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d8fa365. Configure here.

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.

2 participants