feat: integrate confidential ballot provider evidence
This commit is contained in:
@@ -73,6 +73,11 @@ Approval, and evidence.
|
|||||||
|
|
||||||
A governed ballot is delegated to `voting.ballots` when Voting is installed;
|
A governed ballot is delegated to `voting.ballots` when Voting is installed;
|
||||||
Committee retains the meeting/agendum linkage and verified aggregate result.
|
Committee retains the meeting/agendum linkage and verified aggregate result.
|
||||||
|
For provider-backed Voting ballots, Committee also snapshots the assurance
|
||||||
|
profile, provider and ballot references, and sanitized provider evidence. A
|
||||||
|
reference `local_confidential` result remains explicitly uncertified; its
|
||||||
|
server-encrypted casts do not satisfy secret-ballot, anonymity,
|
||||||
|
coercion-resistance, or legal-certification requirements.
|
||||||
The following direct provider path remains a 0.1 compatibility contract only.
|
The following direct provider path remains a 0.1 compatibility contract only.
|
||||||
A provider-bound vote on that path is finalized through
|
A provider-bound vote on that path is finalized through
|
||||||
`committee.ballot_adapter.<provider>`. The adapter receives tenant, vote,
|
`committee.ballot_adapter.<provider>`. The adapter receives tenant, vote,
|
||||||
|
|||||||
@@ -275,6 +275,13 @@ class CommitteeBallotFinalizer:
|
|||||||
expected_revision=voting_expected_revision,
|
expected_revision=voting_expected_revision,
|
||||||
idempotency_key=f"committee:{idempotency_key}",
|
idempotency_key=f"committee:{idempotency_key}",
|
||||||
)
|
)
|
||||||
|
assurance_profile = str(
|
||||||
|
ballot.get("assurance_profile") or "recorded"
|
||||||
|
).strip()
|
||||||
|
if assurance_profile != "recorded" and not result.evidence:
|
||||||
|
raise CommitteeWorkspaceError(
|
||||||
|
"Provider-backed Voting results require sanitized provider evidence."
|
||||||
|
)
|
||||||
choices = tuple(str(item) for item in current.attributes.get("choices", ()))
|
choices = tuple(str(item) for item in current.attributes.get("choices", ()))
|
||||||
if set(result.counts) != set(choices):
|
if set(result.counts) != set(choices):
|
||||||
raise CommitteeWorkspaceError(
|
raise CommitteeWorkspaceError(
|
||||||
@@ -301,6 +308,12 @@ class CommitteeBallotFinalizer:
|
|||||||
"voting_ballot_id": voting_ballot_id,
|
"voting_ballot_id": voting_ballot_id,
|
||||||
"voting_ballot_revision": result.revision,
|
"voting_ballot_revision": result.revision,
|
||||||
"voting_result_sha256": result.result_sha256,
|
"voting_result_sha256": result.result_sha256,
|
||||||
|
"voting_assurance_profile": assurance_profile,
|
||||||
|
"voting_provider_id": ballot.get("provider_id"),
|
||||||
|
"voting_provider_ballot_ref": ballot.get("provider_ballot_ref"),
|
||||||
|
"voting_provider_evidence": [
|
||||||
|
dict(item) for item in result.evidence
|
||||||
|
],
|
||||||
"counts": {key: int(value) for key, value in result.counts.items()},
|
"counts": {key: int(value) for key, value in result.counts.items()},
|
||||||
"weighted_counts": {
|
"weighted_counts": {
|
||||||
key: int(value) for key, value in result.weighted_counts.items()
|
key: int(value) for key, value in result.weighted_counts.items()
|
||||||
|
|||||||
@@ -219,7 +219,9 @@ DOCUMENTATION = (
|
|||||||
"effects. Committee does not own generic Mandate or Decision persistence; optional "
|
"effects. Committee does not own generic Mandate or Decision persistence; optional "
|
||||||
"providers resolve and record those objects when installed, while a protected local "
|
"providers resolve and record those objects when installed, while a protected local "
|
||||||
"projection preserves the bounded fallback. Provider-bound ballots are finalized "
|
"projection preserves the bounded fallback. Provider-bound ballots are finalized "
|
||||||
"through adapter capabilities without retaining individual ballots."
|
"through adapter capabilities without retaining individual ballots. Voting-backed "
|
||||||
|
"results preserve sanitized provider assurance evidence and never promote the "
|
||||||
|
"uncertified local confidential reference provider to a certified profile."
|
||||||
),
|
),
|
||||||
layer="available",
|
layer="available",
|
||||||
documentation_types=("admin", "user"),
|
documentation_types=("admin", "user"),
|
||||||
|
|||||||
@@ -90,6 +90,9 @@ class VotingBallots:
|
|||||||
"id": ballot_id,
|
"id": ballot_id,
|
||||||
"revision": 2,
|
"revision": 2,
|
||||||
"state": "open",
|
"state": "open",
|
||||||
|
"assurance_profile": "confidential",
|
||||||
|
"provider_id": "local_confidential",
|
||||||
|
"provider_ballot_ref": "local-confidential:ballot-1",
|
||||||
"context": {"module": "committee", "resource_id": "vote-voting"},
|
"context": {"module": "committee", "resource_id": "vote-voting"},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,6 +120,13 @@ class VotingBallots:
|
|||||||
threshold_met=True,
|
threshold_met=True,
|
||||||
winning_options=("yes",),
|
winning_options=("yes",),
|
||||||
result_sha256="c" * 64,
|
result_sha256="c" * 64,
|
||||||
|
evidence=(
|
||||||
|
{
|
||||||
|
"kind": "reference_provider_result",
|
||||||
|
"provider_id": "local_confidential",
|
||||||
|
"certified": False,
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
def certify_ballot(self, *args, **kwargs):
|
def certify_ballot(self, *args, **kwargs):
|
||||||
@@ -732,6 +742,11 @@ class CommitteeWorkspaceTests(unittest.TestCase):
|
|||||||
self.assertEqual("closed", closed.state)
|
self.assertEqual("closed", closed.state)
|
||||||
self.assertEqual({"yes": 2, "no": 1}, closed.attributes["counts"])
|
self.assertEqual({"yes": 2, "no": 1}, closed.attributes["counts"])
|
||||||
self.assertEqual("c" * 64, closed.attributes["voting_result_sha256"])
|
self.assertEqual("c" * 64, closed.attributes["voting_result_sha256"])
|
||||||
|
self.assertEqual(
|
||||||
|
"confidential",
|
||||||
|
closed.attributes["voting_assurance_profile"],
|
||||||
|
)
|
||||||
|
self.assertFalse(closed.attributes["voting_provider_evidence"][0]["certified"])
|
||||||
self.assertEqual("voting", closed.evidence[0].owner_module)
|
self.assertEqual("voting", closed.evidence[0].owner_module)
|
||||||
self.assertNotIn("selections", closed.attributes)
|
self.assertNotIn("selections", closed.attributes)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user