diff --git a/data/contribution-workspaces/fork-bootstrap-proposal.json b/data/contribution-workspaces/fork-bootstrap-proposal.json index d236b9c6..16ff2a4a 100644 --- a/data/contribution-workspaces/fork-bootstrap-proposal.json +++ b/data/contribution-workspaces/fork-bootstrap-proposal.json @@ -3,7 +3,7 @@ "state": "READ_ONLY_PROPOSAL_NOT_APPROVED", "destination_org": "opensiro-contributions", "source_registry": "data/contribution-workspaces/registry.json", - "source_registry_sha256": "42473da71ca585ea951c380a64784522234c694ae34056d18ec0e57763b2832a", + "source_registry_sha256": "ff91c1b0b961f134021291b89cdd0e6a52b4d57dcb26f4238edd9fc0a68dfca1", "priority_snapshot": "data/fork-priorities/2026-11-08.json", "batch_size_max": 10, "counts": { @@ -35,7 +35,7 @@ { "id": "bootstrap-001", "state": "UNAPPROVED_REQUIRES_TRUSTED_PREFLIGHT", - "selection_sha256": "bd8d79271ab61cd3fea0d4253878940fbb51ad060f6fc55c6a2279a699011d67", + "selection_sha256": "c9f7a324b1bf54053fff2a85963862b3b271e80ae6a7dee27b56b422d5e62069", "source_count": 10, "has_collision_name_proposals": true, "selection": [ @@ -84,7 +84,7 @@ { "id": "bootstrap-002", "state": "UNAPPROVED_REQUIRES_TRUSTED_PREFLIGHT", - "selection_sha256": "d11fd8242eb50be8738ef09c188779fc44981eefc09cffb37bf2547c5e8aa437", + "selection_sha256": "2e84240f54b1c9c949ba9f542c2e0a2b70a6856c3139f9343d67f05b2c030895", "source_count": 10, "has_collision_name_proposals": true, "selection": [ @@ -133,7 +133,7 @@ { "id": "bootstrap-003", "state": "UNAPPROVED_REQUIRES_TRUSTED_PREFLIGHT", - "selection_sha256": "52a8704c3f2f0c872764960d87754ec021c00400d5c45d962f6c52490f9e9442", + "selection_sha256": "a04cac89bc8c64c17a4ea003675c5052f187c71cdb5643f576013791c5532914", "source_count": 10, "has_collision_name_proposals": false, "selection": [ @@ -182,7 +182,7 @@ { "id": "bootstrap-004", "state": "UNAPPROVED_REQUIRES_TRUSTED_PREFLIGHT", - "selection_sha256": "80ecaa06cc4dea6ace95d9e00abd7a99016d010b4034f8095654cd2655a918cd", + "selection_sha256": "adbd52fa31afa99bca06a97b0750d98ceaf0d7f3ea49d91cf767dbb109297e79", "source_count": 10, "has_collision_name_proposals": false, "selection": [ @@ -231,7 +231,7 @@ { "id": "bootstrap-005", "state": "UNAPPROVED_REQUIRES_TRUSTED_PREFLIGHT", - "selection_sha256": "c396827113724ae1d377be06873f1103f0e99d2a8fd977beaafe9da679a85273", + "selection_sha256": "90186c90f9cc1dc8f60b0428602467eca49ecd043393272ea18f28023fa4b2a4", "source_count": 10, "has_collision_name_proposals": false, "selection": [ @@ -280,7 +280,7 @@ { "id": "bootstrap-006", "state": "UNAPPROVED_REQUIRES_TRUSTED_PREFLIGHT", - "selection_sha256": "905b1d69a48e49a79a1c6acbcdfb73b5506e3faac60656fba3d0e128a4da0ecb", + "selection_sha256": "bdd6ae6343113d2112f4bea753a773c5a3d2b01020bf07cdcce2c9f375b655d9", "source_count": 10, "has_collision_name_proposals": false, "selection": [ @@ -329,7 +329,7 @@ { "id": "bootstrap-007", "state": "UNAPPROVED_REQUIRES_TRUSTED_PREFLIGHT", - "selection_sha256": "7d5d971286bedde1bf2b86fdd9af1eabf4370c54b32607de1266025b80bfba62", + "selection_sha256": "af52919731a2dded222a46a731c9ac74accf138da405f2ff826dd5501edc5ed9", "source_count": 10, "has_collision_name_proposals": true, "selection": [ @@ -378,7 +378,7 @@ { "id": "bootstrap-008", "state": "UNAPPROVED_REQUIRES_TRUSTED_PREFLIGHT", - "selection_sha256": "18af312d49e33b748c02c98c0b832baba99f921f46e8b50173ce231b50659798", + "selection_sha256": "64f26d69de687ea36621b891fa54cda59bdb6bb56347b9ce53f6267ad15c9f91", "source_count": 10, "has_collision_name_proposals": false, "selection": [ @@ -427,7 +427,7 @@ { "id": "bootstrap-009", "state": "UNAPPROVED_REQUIRES_TRUSTED_PREFLIGHT", - "selection_sha256": "17095dd5c81982bc56dbcfadcb3057716cc8540e54672e3224f256dbbdde2ad3", + "selection_sha256": "27f7c59ce3225681c1bf4603101d166296fab6afbe47f6975376269d81ea1576", "source_count": 1, "has_collision_name_proposals": false, "selection": [ diff --git a/data/contribution-workspaces/registry.json b/data/contribution-workspaces/registry.json index d93cafcc..f74d4f8f 100644 --- a/data/contribution-workspaces/registry.json +++ b/data/contribution-workspaces/registry.json @@ -26,7 +26,8 @@ "execution": { "fork_and_patch_writes": "SEPARATE_TRUSTED_HOST_STAGE_GRANT_REQUIRED", "continuous_link": "REGENERATE_WITH_EVERY_BATCH_OR_PATCH_RECORD_CHANGE", - "external_pr": "HUMAN_EXPLICIT_APPROVAL_REQUIRED", + "external_pr": "CHECK_EFFECTIVE_STANDING_MANDATE_AND_NATIVE_GATES", + "external_pr_policy_source": "policies/standing-external-publication-p1.json", "auto_external_pr": false, "live_forks_verified_by_this_script": false, "dated_fork_readback_is_live_authority": false diff --git a/docs/FORK_BOOTSTRAP_AND_COLLABORATION.md b/docs/FORK_BOOTSTRAP_AND_COLLABORATION.md index 83a17c08..11dc89b0 100644 --- a/docs/FORK_BOOTSTRAP_AND_COLLABORATION.md +++ b/docs/FORK_BOOTSTRAP_AND_COLLABORATION.md @@ -1,10 +1,10 @@ # Shared working forks: supervised bootstrap and cross-plugin contribution -**Status: OWNER-SUPERVISED ONE-OFF ROUTE DOCUMENTED; NO BULK FORK WRITES YET.** +**Status: historical owner-supervised bootstrap plus current standing preparation/publication routing; no blanket unattended bulk executor is attested.** **Owner:** [Upstream #28](https://github.com/opensiro/upstream/issues/28) under the [#6 protected executor boundary](https://github.com/opensiro/upstream/issues/6). **Destination:** public GitHub organization `opensiro-contributions`. -**External PR publication:** an explicit human decision for **each exact PR**. +**External PR publication:** the separate [standing publication mandate](STANDING_EXTERNAL_PR_PUBLICATION.md) authorizes qualified PR submissions without a new owner approval for each PR. The target policy, native checks, legal attestations and third-party merge rights remain separate. ## Why bootstrap once @@ -69,7 +69,7 @@ For five-source waves with eligible reviewed packets, the host now makes bound one-use *execution* decisions automatically under a current-main standing owner policy; it does not ask for a new human vote on each fork or patch. The technical provider, fork, source and write controls still -apply, and `submit_external_pr` remains human-approved per PR. +apply. The [separate standing publication mandate](STANDING_EXTERNAL_PR_PUBLICATION.md) allows a qualifying `submit_external_pr` without another per-PR owner vote; legal attestations, target checks and third-party merges remain reserved. Unattended/batch-agent execution using [`batch_fork_executor.py`](../scripts/batch_fork_executor.py), an automated @@ -128,13 +128,17 @@ automatic requirement to create a fork for negative or historical-only cases. | OpenSiro agent or plugin with an effective fork-write grant | Update a dedicated working branch with exact source and fork SHA checks; retain native CI and readback | A new exact `prepare_contribution` stage decision; never infer it from the bootstrap grant | | External contributor without organization write rights | Use their own branch/fork in the compatible GitHub fork network, then propose an **internal PR targeting `opensiro-contributions/`** | Normal GitHub author identity and repository PR/CI/review settings; **no shared OpenSiro SSH credentials** | | OpenSiro maintainer with actual merge rights | Review and integrate the internal PR into the organization's selected working branch | Existing branch protection and required CI; internal approval is not external publication approval | -| Human owner of publication | Review the exact files, upstream-native tests, source base, licensing/CLA/DCO, **target's original PR template/contributor requirements** and the completed title/body; explicitly release **that** external PR | Separate human decision for each external PR; external maintainers own acceptance/merge | +| Authorized publisher under a current standing mandate | Verify exact fork head, source-native checks, source base, licensing/CLA/DCO, **target's original PR template/contributor requirements**, disclosure and title/body before creating the external PR | No repeated owner prompt for qualifying publication; never fabricate legal assent or exercise the target maintainer's merge rights | An internal PR into an OpenSiro-owned fork and a PR **from** that fork **to** -the third-party parent are different operations. Neither a prior contributor -approval nor a successful internal CI run grants external publication rights. +the third-party parent are different operations. Neither a fork-local proposal, +a prior contributor approval nor successful CI **alone** satisfies the active +standing publication gate; each exact external PR needs current-policy, +provider and native-evidence checks. The `upstream auto` and `upstream upgrade batch` commands may produce and -update internal handoffs, but cannot silently publish to external projects. +update internal handoffs, but they are **not themselves external publishers**. +A separately authenticated publisher must verify the current standing mandate, +the exact fork and source, and every target-native/legal gate before submission. For multiple plugins working concurrently, use the same source identity and one reviewed branch/PR per intervention. The Upstream registry and GitHub diff --git a/scripts/contribution_workspaces.py b/scripts/contribution_workspaces.py index ef49971d..efd0fa4e 100644 --- a/scripts/contribution_workspaces.py +++ b/scripts/contribution_workspaces.py @@ -433,7 +433,11 @@ def build(root=ROOT): "execution": { "fork_and_patch_writes": "SEPARATE_TRUSTED_HOST_STAGE_GRANT_REQUIRED", "continuous_link": "REGENERATE_WITH_EVERY_BATCH_OR_PATCH_RECORD_CHANGE", - "external_pr": "HUMAN_EXPLICIT_APPROVAL_REQUIRED", + # Source-linked intent only: current-main policy and native gates + # must be independently checked by the actual publisher. + "external_pr": "CHECK_EFFECTIVE_STANDING_MANDATE_AND_NATIVE_GATES", + "external_pr_policy_source": "policies/standing-external-publication-p1.json", + # No unattended end-to-end executor is attested by this registry. "auto_external_pr": False, "live_forks_verified_by_this_script": False, "dated_fork_readback_is_live_authority": False, diff --git a/tests/test_contribution_workspaces.py b/tests/test_contribution_workspaces.py index 3ce899b6..04661790 100644 --- a/tests/test_contribution_workspaces.py +++ b/tests/test_contribution_workspaces.py @@ -65,7 +65,11 @@ def test_no_fork_or_pr_write_claimed_and_no_wrong_parent_by_basename(self): self.assertIsNone(talktorana["fork"]) self.assertIsNone(talktorana["proposed_fork"]) self.assertEqual(talktorana["next_step"], "RESOLVE_FORK_PARENT_AND_NAME") - self.assertEqual(report["execution"]["external_pr"], "HUMAN_EXPLICIT_APPROVAL_REQUIRED") + self.assertEqual(report["execution"]["external_pr"], + "CHECK_EFFECTIVE_STANDING_MANDATE_AND_NATIVE_GATES") + self.assertEqual(report["execution"]["external_pr_policy_source"], + "policies/standing-external-publication-p1.json") + self.assertTrue((ROOT / report["execution"]["external_pr_policy_source"]).is_file()) self.assertFalse(report["execution"]["auto_external_pr"]) self.assertFalse(report["execution"]["live_forks_verified_by_this_script"]) self.assertFalse(report["execution"]["dated_fork_readback_is_live_authority"])