From baa86dfe94015f66570844ce81ed1310729cded1 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Mon, 20 Jul 2026 13:21:40 +0100 Subject: [PATCH 1/5] feat(auth): transfer REV activation custody --- .../ACTIVATION_CUSTODY.md | 10 +- ...REV-CUSTODY-activation-custody-transfer.md | 78 ++++- .../WS-AUTH-001-REV-CUSTODY.json | 9 + .../app/modules/authorization/catalogue.py | 52 +-- backend/tests/test_authorization.py | 303 ++++++++++++++---- docs/operations_authorization_service.md | 21 +- docs/spec_authorization_service.md | 71 ++-- 7 files changed, 421 insertions(+), 123 deletions(-) create mode 100644 .agent-loop/merge-intents/WS-AUTH-001-REV-CUSTODY.json diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/ACTIVATION_CUSTODY.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/ACTIVATION_CUSTODY.md index ea98607eb..ded8d9b00 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/ACTIVATION_CUSTODY.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/ACTIVATION_CUSTODY.md @@ -60,9 +60,13 @@ eleven-membership service matrix is unchanged. | `WS-AUTH-001-REV-11` | `review.lease.force_release`, `review.queue.routing.override`, `review.queue.routing.correct`, `review.queue.close`, `review.reconcile.run` | | `WS-AUTH-001-REV-12` | `review.artifact_reference.reconcile`, `review.projection.rebuild` | -`WS-AUTH-001-REV-CUSTODY` performs the atomic 19-row transfer and removes the -seven historical REV owner enum values. It changes no mapping or availability -and adds no migration. +`WS-AUTH-001-REV-CUSTODY` atomically transfers these 19 rows with exact owner +cardinalities `2/5/3/1/1/5/2` in the table order above and removes the seven +historical REV owner enum values. It changes no mapping or availability and +adds no migration. All 19 actions remain planned and unavailable; these AUTH +custodian labels grant no reviewer, Operator, or service authority. The four +proposed lifecycle actions remain unregistered, and PREP remains separately +human-gated. ## Additive registration gates diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-REV-CUSTODY-activation-custody-transfer.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-REV-CUSTODY-activation-custody-transfer.md index 4ed462a98..160b07e90 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-REV-CUSTODY-activation-custody-transfer.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-REV-CUSTODY-activation-custody-transfer.md @@ -54,10 +54,54 @@ partial transfer or retained REV activation-owner enum ## Acceptance criteria - Exactly the 19 canonical rows move to the seven AUTH owner values. +- The exact owner cardinalities are `2/5/3/1/1/5/2` for + `WS-AUTH-001-REV-05`, `WS-AUTH-001-REV-06`, `WS-AUTH-001-REV-07`, + `WS-AUTH-001-REV-08`, `WS-AUTH-001-REV-09A`, `WS-AUTH-001-REV-11`, and + `WS-AUTH-001-REV-12`, respectively. - All seven REV owner enum values are removed atomically. -- Catalogue counts, mappings, active/planned state, PostgreSQL audit parity, - and denial behavior remain unchanged. -- Every REV action remains unavailable through the real kernel. +- Frozen expectations independent of the modified catalogue bind the entry + baseline to trusted `main` SHA + `be2a79a243ec50049c37f1f634322a9b3ab895ba`: all 65 + `(ActionId, PermissionId, availability)` tuples, 74 PermissionIds, + 65 ActionIds, 17 active and 48 planned actions, and the exact seven-identity, + eleven-membership fixed-service matrix remain unchanged. This chunk has a + zero-count, zero-mapping, zero-availability, and zero-service-matrix delta. +- The exact 19-row action, permission, owner, and `planned` map is frozen in one + hand-authored test-only fixture independent of `ACTION_DEFINITIONS`, + `ACTION_BY_ID`, owner enums, identifier prefixes, grouping logic, and + documentation. Tests assert all seven new AUTH REV owners are present, all + seven historical `WS-REV-*` owner values are absent, and catalogue + construction rejects a missing row, extra or duplicate row, wrong or swapped + custodian, retained historical or dual custody, changed mapping, and changed + availability. +- Every non-REV owner assignment remains exactly equal to the frozen trusted + baseline, including all 25 merged ART AUTH custody rows. The four proposed + REV lifecycle actions remain absent from the registered catalogue. +- `ActionOwner` changes only in the typed catalogue. PostgreSQL and historical + audit evidence have no owner field and receive no write or rewrite. Database + and audit proof preserves the existing ActionId-to-PermissionId and evidence + contracts; it does not invent persisted owner parity. +- Canonical documentation tables enumerate the same exact 19-row, + seven-custodian handoff and cardinalities. Documentation parity is parsed and + checked deterministically in addition to stale-wording and Markdown-link + scans. Custodian labels grant no reviewer, Operator, or service authority. +- Every one of the 19 REV actions remains unavailable through + `AuthorizationService.require()` using the real kernel. Each denial is + sensitive `action_unavailable`, records the exact action and permission, is + not revalidated, records one bounded denial event, and reaches no grant, + evaluator, revalidation, route, job, or REV behavior path. +- Alembic remains at the immutable entry head + `0029_shared_transactional_outbox`; `backend/alembic/**` has no diff and no + migration is added, edited, reserved, or allocated. +- Existing tests are not removed, skipped, xfailed, deselected, weakened, or + rewritten to derive expected truth from changed production metadata or docs. + Modified existing expectations retain all baseline assertions and change only + the exact 19 owner values. +- This chunk transfers REV custody only. `WS-AUTH-001-PREP` remains a separate + later human-started chunk; combined REV/PREP work is forbidden. +- Exactly one schema-v2 merge intent is added for this chunk, naming only the + declared same-initiative `WS-AUTH-001-PREP` successor with a separate explicit + human start. ## Verification commands @@ -66,9 +110,37 @@ partial transfer or retained REV activation-owner enum (cd backend && WORKSTREAM_DATABASE_URL= .venv/bin/python -m pytest -q tests/test_authorization.py tests/test_auth.py --cov=app.modules.authorization --cov-report=term-missing --cov-fail-under=90) python3 scripts/check_stale_authorization_docs.py python3 scripts/check_markdown_links.py +test -z "$(git diff --name-only be2a79a243ec50049c37f1f634322a9b3ab895ba -- backend/alembic)" +(cd backend && test "$(.venv/bin/alembic heads | tr -d '[:space:]')" = "0029_shared_transactional_outbox(head)") git diff --check ``` +After push, the unchanged GitHub `Backend` workflow is the authoritative full +suite gate. It must pass its isolated backend suite, preserve repository-wide +coverage at or above 78 percent, preserve authorization-subsystem coverage at +or above 90 percent, and pass every existing workflow gate. The full suite runs +in GitHub Actions rather than on the user's slow local machine. This chunk does +not change workflows, scripts, exclusions, thresholds, coverage configuration, +or package commands. + +## Implementation and test reuse constraints + +- Extend the existing exact catalogue expectation and `_index_actions()` + fail-closed tests in `backend/tests/test_authorization.py`; do not add a + parallel catalogue validator or a second 65-row fixture. +- Add one hand-authored test-only 19-row REV custody fixture and reuse it for + owner/cardinality, mutation, real-kernel denial, and documentation-parity + proof. +- Reuse the existing ART custody table parser pattern, `_runtime_context()`, + `_runtime_service()`, and decision-evidence abstractions. Exploding + revalidation, admin/grant, evaluator, and downstream dependencies must prove + planned actions fail before dispatch. +- Add no production owner-family helper, prefix classifier, compatibility + alias, new registry abstraction, evaluator, service identity, or matrix path. +- Documentation parity may consume the independent test fixture, but neither + production metadata nor rendered documentation may derive the other's + expected values. + ## Required reviewers Senior engineering, QA/test, security/auth, product/ops, architecture, diff --git a/.agent-loop/merge-intents/WS-AUTH-001-REV-CUSTODY.json b/.agent-loop/merge-intents/WS-AUTH-001-REV-CUSTODY.json new file mode 100644 index 000000000..9067950d1 --- /dev/null +++ b/.agent-loop/merge-intents/WS-AUTH-001-REV-CUSTODY.json @@ -0,0 +1,9 @@ +{ + "chunk_id": "WS-AUTH-001-REV-CUSTODY", + "chunk_title": "REV Activation Custody Transfer", + "initiative_id": "WS-AUTH-001", + "next_chunk_id": "WS-AUTH-001-PREP", + "next_chunk_title": "Prepared Mutation Authorization Protocol", + "next_requires_explicit_start": true, + "schema_version": 2 +} diff --git a/backend/app/modules/authorization/catalogue.py b/backend/app/modules/authorization/catalogue.py index 8442cf26c..90cd4b9c6 100644 --- a/backend/app/modules/authorization/catalogue.py +++ b/backend/app/modules/authorization/catalogue.py @@ -174,13 +174,13 @@ class ActionOwner(StrEnum): AUTH_09D_B = "WS-AUTH-001-09D-B" AUTH_13 = "WS-AUTH-001-13" AUTH_14 = "WS-AUTH-001-14" - REV_05 = "WS-REV-001-05" - REV_06 = "WS-REV-001-06" - REV_07 = "WS-REV-001-07" - REV_08 = "WS-REV-001-08" - REV_09A = "WS-REV-001-09A" - REV_11 = "WS-REV-001-11" - REV_12 = "WS-REV-001-12" + AUTH_REV_05 = "WS-AUTH-001-REV-05" + AUTH_REV_06 = "WS-AUTH-001-REV-06" + AUTH_REV_07 = "WS-AUTH-001-REV-07" + AUTH_REV_08 = "WS-AUTH-001-REV-08" + AUTH_REV_09A = "WS-AUTH-001-REV-09A" + AUTH_REV_11 = "WS-AUTH-001-REV-11" + AUTH_REV_12 = "WS-AUTH-001-REV-12" AUTH_ART_02D_INTERNAL = "WS-AUTH-001-ART-02D-INTERNAL" AUTH_ART_02D_OPERATOR = "WS-AUTH-001-ART-02D-OPERATOR" AUTH_ART_03 = "WS-AUTH-001-ART-03" @@ -325,80 +325,80 @@ def _active( ActionOwner.AUTH_14, ), _planned(ActionId.SUBMISSION_CREATE, PermissionId.SUBMISSION_CREATE, ActionOwner.AUTH_14), - _planned(ActionId.REVIEW_QUEUE_READ, PermissionId.REVIEW_QUEUE_READ, ActionOwner.REV_05), + _planned(ActionId.REVIEW_QUEUE_READ, PermissionId.REVIEW_QUEUE_READ, ActionOwner.AUTH_REV_05), _planned( ActionId.REVIEW_QUEUE_INSPECT, PermissionId.REVIEW_QUEUE_INSPECT, - ActionOwner.REV_05, + ActionOwner.AUTH_REV_05, ), - _planned(ActionId.REVIEW_CLAIM, PermissionId.REVIEW_CLAIM, ActionOwner.REV_06), - _planned(ActionId.REVIEW_RELEASE, PermissionId.REVIEW_RELEASE, ActionOwner.REV_06), + _planned(ActionId.REVIEW_CLAIM, PermissionId.REVIEW_CLAIM, ActionOwner.AUTH_REV_06), + _planned(ActionId.REVIEW_RELEASE, PermissionId.REVIEW_RELEASE, ActionOwner.AUTH_REV_06), _planned( ActionId.REVIEW_DECLINE_PREFERENCE, PermissionId.REVIEW_DECLINE_PREFERENCE, - ActionOwner.REV_06, + ActionOwner.AUTH_REV_06, ), _planned( ActionId.REVIEW_PREFERENCE_EXPIRY_RUN, PermissionId.OPERATIONS_TIMER_RUN, - ActionOwner.REV_06, + ActionOwner.AUTH_REV_06, ), _planned( ActionId.REVIEW_LEASE_EXPIRY_RUN, PermissionId.OPERATIONS_TIMER_RUN, - ActionOwner.REV_06, + ActionOwner.AUTH_REV_06, ), _planned( ActionId.REVIEW_CONTEXT_READ, PermissionId.SUBMISSION_READ_FOR_REVIEW, - ActionOwner.REV_07, + ActionOwner.AUTH_REV_07, ), - _planned(ActionId.REVIEW_CHAIN_READ, PermissionId.REVIEW_CHAIN_READ, ActionOwner.REV_07), + _planned(ActionId.REVIEW_CHAIN_READ, PermissionId.REVIEW_CHAIN_READ, ActionOwner.AUTH_REV_07), _planned( ActionId.REVIEW_FINDING_EVIDENCE_INGEST, PermissionId.REVIEW_DECISION, - ActionOwner.REV_07, + ActionOwner.AUTH_REV_07, ), - _planned(ActionId.REVIEW_DECISION, PermissionId.REVIEW_DECISION, ActionOwner.REV_08), + _planned(ActionId.REVIEW_DECISION, PermissionId.REVIEW_DECISION, ActionOwner.AUTH_REV_08), _planned( ActionId.REVIEW_FINDING_RESPONSE_EVIDENCE_INGEST, PermissionId.SUBMISSION_CREATE, - ActionOwner.REV_09A, + ActionOwner.AUTH_REV_09A, ), _planned( ActionId.REVIEW_LEASE_FORCE_RELEASE, PermissionId.REVIEW_LEASE_FORCE_RELEASE, - ActionOwner.REV_11, + ActionOwner.AUTH_REV_11, ), _planned( ActionId.REVIEW_QUEUE_ROUTING_OVERRIDE, PermissionId.REVIEW_QUEUE_OVERRIDE, - ActionOwner.REV_11, + ActionOwner.AUTH_REV_11, ), _planned( ActionId.REVIEW_QUEUE_ROUTING_CORRECT, PermissionId.REVIEW_QUEUE_OVERRIDE, - ActionOwner.REV_11, + ActionOwner.AUTH_REV_11, ), _planned( ActionId.REVIEW_QUEUE_CLOSE, PermissionId.REVIEW_QUEUE_OVERRIDE, - ActionOwner.REV_11, + ActionOwner.AUTH_REV_11, ), _planned( ActionId.REVIEW_RECONCILE_RUN, PermissionId.OPERATIONS_RECONCILE_RUN, - ActionOwner.REV_11, + ActionOwner.AUTH_REV_11, ), _planned( ActionId.REVIEW_ARTIFACT_REFERENCE_RECONCILE, PermissionId.OPERATIONS_RECONCILE_RUN, - ActionOwner.REV_12, + ActionOwner.AUTH_REV_12, ), _planned( ActionId.REVIEW_PROJECTION_REBUILD, PermissionId.OPERATIONS_PROJECTION_REBUILD, - ActionOwner.REV_12, + ActionOwner.AUTH_REV_12, ), _planned( ActionId.ARTIFACT_BINDING_READ, diff --git a/backend/tests/test_authorization.py b/backend/tests/test_authorization.py index e61e2b9dd..02621c889 100644 --- a/backend/tests/test_authorization.py +++ b/backend/tests/test_authorization.py @@ -266,6 +266,76 @@ ), } +REV_CUSTODY_EXPECTATIONS = { + "review.queue.read": ("review.queue.read", "WS-AUTH-001-REV-05", "planned"), + "review.queue.inspect": ("review.queue.inspect", "WS-AUTH-001-REV-05", "planned"), + "review.claim": ("review.claim", "WS-AUTH-001-REV-06", "planned"), + "review.release": ("review.release", "WS-AUTH-001-REV-06", "planned"), + "review.decline_preference": ( + "review.decline_preference", + "WS-AUTH-001-REV-06", + "planned", + ), + "review.preference_expiry.run": ( + "operations.timer.run", + "WS-AUTH-001-REV-06", + "planned", + ), + "review.lease_expiry.run": ( + "operations.timer.run", + "WS-AUTH-001-REV-06", + "planned", + ), + "review.context.read": ( + "submission.read_for_review", + "WS-AUTH-001-REV-07", + "planned", + ), + "review.chain.read": ("review.chain.read", "WS-AUTH-001-REV-07", "planned"), + "review.finding_evidence.ingest": ( + "review.decision", + "WS-AUTH-001-REV-07", + "planned", + ), + "review.decision": ("review.decision", "WS-AUTH-001-REV-08", "planned"), + "review.finding_response_evidence.ingest": ( + "submission.create", + "WS-AUTH-001-REV-09A", + "planned", + ), + "review.lease.force_release": ( + "review.lease.force_release", + "WS-AUTH-001-REV-11", + "planned", + ), + "review.queue.routing.override": ( + "review.queue.override", + "WS-AUTH-001-REV-11", + "planned", + ), + "review.queue.routing.correct": ( + "review.queue.override", + "WS-AUTH-001-REV-11", + "planned", + ), + "review.queue.close": ("review.queue.override", "WS-AUTH-001-REV-11", "planned"), + "review.reconcile.run": ( + "operations.reconcile.run", + "WS-AUTH-001-REV-11", + "planned", + ), + "review.artifact_reference.reconcile": ( + "operations.reconcile.run", + "WS-AUTH-001-REV-12", + "planned", + ), + "review.projection.rebuild": ( + "operations.projection.rebuild", + "WS-AUTH-001-REV-12", + "planned", + ), +} + def _admin_resource_context( request: AdminRoleGrantIssueRequest | AdminRoleGrantRevokeRequest, @@ -327,31 +397,10 @@ def test_closed_permission_and_action_catalogue_is_exact_and_non_executable() -> ), "operations.checker.retry": ("operations.checker.retry", "WS-AUTH-001-14"), "submission.create": ("submission.create", "WS-AUTH-001-14"), - "review.queue.read": ("review.queue.read", "WS-REV-001-05"), - "review.queue.inspect": ("review.queue.inspect", "WS-REV-001-05"), - "review.claim": ("review.claim", "WS-REV-001-06"), - "review.release": ("review.release", "WS-REV-001-06"), - "review.decline_preference": ("review.decline_preference", "WS-REV-001-06"), - "review.preference_expiry.run": ("operations.timer.run", "WS-REV-001-06"), - "review.lease_expiry.run": ("operations.timer.run", "WS-REV-001-06"), - "review.context.read": ("submission.read_for_review", "WS-REV-001-07"), - "review.chain.read": ("review.chain.read", "WS-REV-001-07"), - "review.finding_evidence.ingest": ("review.decision", "WS-REV-001-07"), - "review.decision": ("review.decision", "WS-REV-001-08"), - "review.finding_response_evidence.ingest": ( - "submission.create", - "WS-REV-001-09A", - ), - "review.lease.force_release": ("review.lease.force_release", "WS-REV-001-11"), - "review.queue.routing.override": ("review.queue.override", "WS-REV-001-11"), - "review.queue.routing.correct": ("review.queue.override", "WS-REV-001-11"), - "review.queue.close": ("review.queue.override", "WS-REV-001-11"), - "review.reconcile.run": ("operations.reconcile.run", "WS-REV-001-11"), - "review.artifact_reference.reconcile": ( - "operations.reconcile.run", - "WS-REV-001-12", - ), - "review.projection.rebuild": ("operations.projection.rebuild", "WS-REV-001-12"), + **{ + action: (permission, owner) + for action, (permission, owner, _availability) in REV_CUSTODY_EXPECTATIONS.items() + }, **{ action: (permission, owner) for action, (permission, owner, _availability) in ART_CUSTODY_EXPECTATIONS.items() @@ -419,6 +468,14 @@ def test_closed_permission_and_action_catalogue_is_exact_and_non_executable() -> ) for action in ART_CUSTODY_EXPECTATIONS } == ART_CUSTODY_EXPECTATIONS + assert { + action: ( + ACTION_BY_ID[ActionId(action)].permission_id.value, + ACTION_BY_ID[ActionId(action)].owner.value, + ACTION_BY_ID[ActionId(action)].availability.value, + ) + for action in REV_CUSTODY_EXPECTATIONS + } == REV_CUSTODY_EXPECTATIONS assert { owner: sum(definition.owner is owner for definition in ACTION_DEFINITIONS) for owner in { @@ -442,6 +499,33 @@ def test_closed_permission_and_action_catalogue_is_exact_and_non_executable() -> ActionOwner.AUTH_ART_06B: 2, } assert all(not owner.value.startswith("WS-ART-") for owner in ActionOwner) + assert { + owner: sum(definition.owner is owner for definition in ACTION_DEFINITIONS) + for owner in { + ActionOwner.AUTH_REV_05, + ActionOwner.AUTH_REV_06, + ActionOwner.AUTH_REV_07, + ActionOwner.AUTH_REV_08, + ActionOwner.AUTH_REV_09A, + ActionOwner.AUTH_REV_11, + ActionOwner.AUTH_REV_12, + } + } == { + ActionOwner.AUTH_REV_05: 2, + ActionOwner.AUTH_REV_06: 5, + ActionOwner.AUTH_REV_07: 3, + ActionOwner.AUTH_REV_08: 1, + ActionOwner.AUTH_REV_09A: 1, + ActionOwner.AUTH_REV_11: 5, + ActionOwner.AUTH_REV_12: 2, + } + assert all(not owner.value.startswith("WS-REV-") for owner in ActionOwner) + assert { + "review.revision_context.repair", + "review.revision_context.legacy_close", + "review.revision_obligation.close", + "review.lifecycle.activation.manage", + }.isdisjoint(action.value for action in ACTION_IDS) assert sum( definition.availability is ActionAvailability.ACTIVE for definition in ACTION_DEFINITIONS @@ -494,6 +578,32 @@ def test_fixed_service_action_matrix_is_exact_planned_and_immutable() -> None: SERVICE_ACTIONS_BY_IDENTITY[ServiceIdentity.ARTIFACT_VERIFIER] = frozenset() # type: ignore[index] +def _parse_custody_table(document: Path, expected_actions: set[str]) -> dict[str, str]: + rows = document.read_text(encoding="utf-8").splitlines() + for header_index, row in enumerate(rows): + if row not in { + "| AUTH activation custodian | Exact planned ActionIds |", + "| AUTH activation chunk | Exact planned ActionIds |", + }: + continue + parsed: dict[str, str] = {} + duplicates: set[str] = set() + for table_row in rows[header_index + 2 :]: + if not table_row.startswith("|"): + break + cells = [cell.strip() for cell in table_row.split("|")] + assert len(cells) == 4 + owner = cells[1].strip("`") + for action in cells[2].split("`")[1::2]: + if action in parsed: + duplicates.add(action) + parsed[action] = owner + assert duplicates == set() + if set(parsed) == expected_actions: + return parsed + raise AssertionError(f"exact custody table missing from {document}") + + def test_art_custody_documentation_matches_the_independent_catalogue_fixture() -> None: repository_root = Path(__file__).resolve().parents[2] custody_documents = ( @@ -518,32 +628,7 @@ def test_art_custody_documentation_matches_the_independent_catalogue_fixture() - } for document in custody_documents: - rows = document.read_text(encoding="utf-8").splitlines() - header_index = next( - index - for index, row in enumerate(rows) - if row - in { - "| AUTH activation custodian | Exact planned ActionIds |", - "| AUTH activation chunk | Exact planned ActionIds |", - } - ) - table_rows: list[str] = [] - for row in rows[header_index + 2 :]: - if not row.startswith("|"): - break - table_rows.append(row) - parsed: dict[str, str] = {} - duplicates: set[str] = set() - for row in table_rows: - cells = [cell.strip() for cell in row.split("|")] - assert len(cells) == 4 - owner = cells[1].strip("`") - for action in cells[2].split("`")[1::2]: - if action in parsed: - duplicates.add(action) - parsed[action] = owner - assert duplicates == set() + parsed = _parse_custody_table(document, set(expected_custody)) assert parsed == expected_custody assert { owner: sum(parsed_owner == owner for parsed_owner in parsed.values()) @@ -571,13 +656,68 @@ def test_art_custody_documentation_matches_the_independent_catalogue_fixture() - encoding="utf-8" ) assert "all 25 ART rows to eight exact AUTH custodians" in operations - assert "The 19 REV rows retain their historical" in operations + assert "all 19 REV\nrows to seven exact AUTH custodians" in operations assert "The ART transfer adds no migration" in operations assert "does not grant Operator" in operations assert "verification retry remains independently gated" in operations assert "74 PermissionIds, 65 ActionIds, 17 active actions, and\n48 planned actions" in operations +def test_rev_custody_documentation_matches_the_independent_catalogue_fixture() -> None: + repository_root = Path(__file__).resolve().parents[2] + custody_documents = ( + repository_root / "docs/spec_authorization_service.md", + repository_root + / ".agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service" + / "ACTIVATION_CUSTODY.md", + ) + expected_custody = { + action: owner + for action, (_permission, owner, _availability) in REV_CUSTODY_EXPECTATIONS.items() + } + expected_owner_counts = { + "WS-AUTH-001-REV-05": 2, + "WS-AUTH-001-REV-06": 5, + "WS-AUTH-001-REV-07": 3, + "WS-AUTH-001-REV-08": 1, + "WS-AUTH-001-REV-09A": 1, + "WS-AUTH-001-REV-11": 5, + "WS-AUTH-001-REV-12": 2, + } + for document in custody_documents: + parsed = _parse_custody_table(document, set(expected_custody)) + assert parsed == expected_custody + assert { + owner: sum(parsed_owner == owner for parsed_owner in parsed.values()) + for owner in set(parsed.values()) + } == expected_owner_counts + + spec_rows = (repository_root / "docs/spec_authorization_service.md").read_text( + encoding="utf-8" + ).splitlines() + parsed_permissions: dict[str, str] = {} + for row in spec_rows: + cells = [cell.strip() for cell in row.split("|")] + if len(cells) < 5: + continue + action = cells[1].strip("`") + if action in REV_CUSTODY_EXPECTATIONS: + assert action not in parsed_permissions + parsed_permissions[action] = cells[2].strip("`") + assert parsed_permissions == { + action: permission + for action, (permission, _owner, _availability) in REV_CUSTODY_EXPECTATIONS.items() + } + + operations = (repository_root / "docs/operations_authorization_service.md").read_text( + encoding="utf-8" + ) + assert "all 19 REV\nrows to seven exact AUTH custodians" in operations + assert "all 19 REV actions remain planned and unavailable" in operations + assert "The REV transfer\nadds no migration" in operations + assert "four proposed REV lifecycle actions remain\nunregistered" in operations + + @pytest.mark.parametrize( "mutation", ["missing_identity", "extra_action", "duplicate_action", "swapped_rows"], @@ -1198,6 +1338,55 @@ def test_action_catalogue_rejects_count_and_permission_partition_drift( _index_actions(ACTION_DEFINITIONS) +@pytest.mark.parametrize( + "mutation", + ["historical_owner", "wrong_custodian", "swapped_custodians", "mapping", "availability"], +) +def test_rev_custody_catalogue_mutations_fail_closed(mutation: str) -> None: + definitions = list(ACTION_DEFINITIONS) + first_index = next( + index + for index, definition in enumerate(definitions) + if definition.action_id is ActionId.REVIEW_QUEUE_READ + ) + second_index = next( + index + for index, definition in enumerate(definitions) + if definition.action_id is ActionId.REVIEW_CLAIM + ) + if mutation == "historical_owner": + definitions[first_index] = replace( + definitions[first_index], owner="WS-REV-001-05" # type: ignore[arg-type] + ) + message = "invalid row" + elif mutation == "wrong_custodian": + definitions[first_index] = replace( + definitions[first_index], owner=ActionOwner.AUTH_REV_12 + ) + message = "metadata mismatch" + elif mutation == "swapped_custodians": + definitions[first_index] = replace( + definitions[first_index], owner=ActionOwner.AUTH_REV_06 + ) + definitions[second_index] = replace( + definitions[second_index], owner=ActionOwner.AUTH_REV_05 + ) + message = "metadata mismatch" + elif mutation == "mapping": + definitions[first_index] = replace( + definitions[first_index], permission_id=PermissionId.REVIEW_QUEUE_INSPECT + ) + message = "metadata mismatch" + else: + definitions[first_index] = replace( + definitions[first_index], availability=ActionAvailability.ACTIVE + ) + message = "active action boundary mismatch" + + with pytest.raises(RuntimeError, match=message): + _index_actions(tuple(definitions)) + + def _runtime_context( *, actor_status: ActorStatus = ActorStatus.ACTIVE, @@ -3469,7 +3658,7 @@ async def test_authorization_kernel_denies_active_action_without_implemented_aut active_unhandled = ActionDefinition( action_id=ActionId.REVIEW_QUEUE_READ, permission_id=PermissionId.REVIEW_QUEUE_READ, - owner=ActionOwner.REV_05, + owner=ActionOwner.AUTH_REV_05, availability=ActionAvailability.ACTIVE, ) monkeypatch.setattr( @@ -3487,9 +3676,9 @@ async def test_authorization_kernel_denies_active_action_without_implemented_aut @pytest.mark.parametrize( ("action_id", "expected_metadata"), - ART_CUSTODY_EXPECTATIONS.items(), + {**ART_CUSTODY_EXPECTATIONS, **REV_CUSTODY_EXPECTATIONS}.items(), ) -async def test_art_custody_actions_remain_unavailable_without_runtime_dispatch( +async def test_custody_actions_remain_unavailable_without_runtime_dispatch( action_id: str, expected_metadata: tuple[str, str, str], ) -> None: @@ -3498,12 +3687,12 @@ async def test_art_custody_actions_remain_unavailable_without_runtime_dispatch( context = _runtime_context() async def unexpected_revalidation(*_args, **_kwargs): - raise AssertionError("planned ART custody action reached runtime revalidation") + raise AssertionError("planned custody action reached runtime revalidation") class UnexpectedAuthorizationDependency: def __getattr__(self, name: str): async def unexpected(*_args, **_kwargs): - raise AssertionError(f"planned ART custody action reached {name}") + raise AssertionError(f"planned custody action reached {name}") return unexpected diff --git a/docs/operations_authorization_service.md b/docs/operations_authorization_service.md index 7f9695ba1..794960182 100644 --- a/docs/operations_authorization_service.md +++ b/docs/operations_authorization_service.md @@ -614,19 +614,26 @@ actions, `actor.service.provision`, `actor.profile.read`, identity-link lifecycle actions are active; the other 48 entries remain planned and non-executable. The target post-custody invariant is that planned runtime entries contain only action, permission, exact -AUTH activation owner, and availability. The availability-neutral ART custody -transfer assigns all 25 ART rows to eight exact AUTH custodians without changing -their mappings or planned availability. The 19 REV rows retain their historical -feature-owner values as an explicitly blocked exception until the separate REV -custody chunk receives its own human start and merges. +AUTH activation owner, and availability. The availability-neutral custody +transfers assign all 25 ART rows to eight exact AUTH custodians and all 19 REV +rows to seven exact AUTH custodians without changing mappings or planned +availability. The REV owner cardinalities are `2/5/3/1/1/5/2` for +`WS-AUTH-001-REV-05`, `WS-AUTH-001-REV-06`, `WS-AUTH-001-REV-07`, +`WS-AUTH-001-REV-08`, `WS-AUTH-001-REV-09A`, `WS-AUTH-001-REV-11`, and +`WS-AUTH-001-REV-12`. Custodian labels grant no reviewer, Operator, or service +authority; all 19 REV actions remain planned and unavailable. The REV transfer +adds no migration, registration, evaluator, route, job, service identity, or +lifecycle behavior, and the four proposed REV lifecycle actions remain +unregistered. Their owning feature must publish the approved principal/resource/guard/surface/ transaction contract before registration or activation, but those foreign facts do not become free-form catalogue fields. Startup validation failure is a release blocker, not a reason to relax catalogue checks. PR #139 requires availability-neutral transfer of all 25 ART and 19 REV owner -rows to exact AUTH chunks before feature activation. Counts and mappings remain -unchanged. The ART transfer adds no migration and does not grant Operator +rows to exact AUTH chunks before feature activation. Both transfers are now +complete. Counts and mappings remain unchanged. The ART transfer adds no migration. +The REV transfer adds no migration. The ART transfer does not grant Operator authority; its `OPERATOR` suffix denotes only future activation custody, and verification retry remains independently gated from read/status actions. Catalogue totals remain 74 PermissionIds, 65 ActionIds, 17 active actions, and diff --git a/docs/spec_authorization_service.md b/docs/spec_authorization_service.md index 8746f97d7..6b1bd4f89 100644 --- a/docs/spec_authorization_service.md +++ b/docs/spec_authorization_service.md @@ -288,35 +288,51 @@ route, typed resource context, evaluator, guards, transaction proof, and availability change. AUTH-09A supplies none of those runtime paths. The submission/review dependency matrix is closed. AUTH-07A registers only the -four stable planned fields shown here; resource facts, candidates, guards, and -hidden behavior remain with the listed feature owner. The current owner values -are planned pre-transfer catalogue state, not permission for a feature chunk to -activate. Before any review action activates, AUTH must transfer activation -custody according to `ACTIVATION_CUSTODY.md` and the reviewed +stable planned fields shown here; resource facts, candidates, guards, and +hidden behavior remain with REV. `WS-AUTH-001-REV-CUSTODY` has replaced only +the 19 historical REV owner values with the exact AUTH activation custodians +below. Mappings and planned availability are unchanged, and the custodian +labels grant no reviewer, Operator, or service authority. Before any review +action activates, its dedicated AUTH custodian must integrate the complete +feature proof according to `ACTIVATION_CUSTODY.md` and the reviewed `.agent-loop/initiatives/WS-XINT-001-lifecycle-boundary-reconciliation/AUTH_REV_HANDOFF.md`. -| ActionId | PermissionId | Historical pre-transfer owner value | +| AUTH activation custodian | Exact planned ActionIds | +|---|---| +| `WS-AUTH-001-REV-05` | `review.queue.read`, `review.queue.inspect` | +| `WS-AUTH-001-REV-06` | `review.claim`, `review.release`, `review.decline_preference`, `review.preference_expiry.run`, `review.lease_expiry.run` | +| `WS-AUTH-001-REV-07` | `review.context.read`, `review.chain.read`, `review.finding_evidence.ingest` | +| `WS-AUTH-001-REV-08` | `review.decision` | +| `WS-AUTH-001-REV-09A` | `review.finding_response_evidence.ingest` | +| `WS-AUTH-001-REV-11` | `review.lease.force_release`, `review.queue.routing.override`, `review.queue.routing.correct`, `review.queue.close`, `review.reconcile.run` | +| `WS-AUTH-001-REV-12` | `review.artifact_reference.reconcile`, `review.projection.rebuild` | + +All 19 actions remain planned and unavailable. The transfer adds no migration, +registration, evaluator, route, job, grant, service identity, or lifecycle +behavior. The four proposed REV lifecycle actions remain unregistered. + +| ActionId | PermissionId | AUTH activation custodian | |---|---|---| | `submission.create` | `submission.create` | `WS-AUTH-001-14` | -| `review.queue.read` | `review.queue.read` | `WS-REV-001-05` | -| `review.queue.inspect` | `review.queue.inspect` | `WS-REV-001-05` | -| `review.claim` | `review.claim` | `WS-REV-001-06` | -| `review.release` | `review.release` | `WS-REV-001-06` | -| `review.decline_preference` | `review.decline_preference` | `WS-REV-001-06` | -| `review.preference_expiry.run` | `operations.timer.run` | `WS-REV-001-06` | -| `review.lease_expiry.run` | `operations.timer.run` | `WS-REV-001-06` | -| `review.context.read` | `submission.read_for_review` | `WS-REV-001-07` | -| `review.chain.read` | `review.chain.read` | `WS-REV-001-07` | -| `review.finding_evidence.ingest` | `review.decision` | `WS-REV-001-07` | -| `review.decision` | `review.decision` | `WS-REV-001-08` | -| `review.finding_response_evidence.ingest` | `submission.create` | `WS-REV-001-09A` | -| `review.lease.force_release` | `review.lease.force_release` | `WS-REV-001-11` | -| `review.queue.routing.override` | `review.queue.override` | `WS-REV-001-11` | -| `review.queue.routing.correct` | `review.queue.override` | `WS-REV-001-11` | -| `review.queue.close` | `review.queue.override` | `WS-REV-001-11` | -| `review.reconcile.run` | `operations.reconcile.run` | `WS-REV-001-11` | -| `review.artifact_reference.reconcile` | `operations.reconcile.run` | `WS-REV-001-12` | -| `review.projection.rebuild` | `operations.projection.rebuild` | `WS-REV-001-12` | +| `review.queue.read` | `review.queue.read` | `WS-AUTH-001-REV-05` | +| `review.queue.inspect` | `review.queue.inspect` | `WS-AUTH-001-REV-05` | +| `review.claim` | `review.claim` | `WS-AUTH-001-REV-06` | +| `review.release` | `review.release` | `WS-AUTH-001-REV-06` | +| `review.decline_preference` | `review.decline_preference` | `WS-AUTH-001-REV-06` | +| `review.preference_expiry.run` | `operations.timer.run` | `WS-AUTH-001-REV-06` | +| `review.lease_expiry.run` | `operations.timer.run` | `WS-AUTH-001-REV-06` | +| `review.context.read` | `submission.read_for_review` | `WS-AUTH-001-REV-07` | +| `review.chain.read` | `review.chain.read` | `WS-AUTH-001-REV-07` | +| `review.finding_evidence.ingest` | `review.decision` | `WS-AUTH-001-REV-07` | +| `review.decision` | `review.decision` | `WS-AUTH-001-REV-08` | +| `review.finding_response_evidence.ingest` | `submission.create` | `WS-AUTH-001-REV-09A` | +| `review.lease.force_release` | `review.lease.force_release` | `WS-AUTH-001-REV-11` | +| `review.queue.routing.override` | `review.queue.override` | `WS-AUTH-001-REV-11` | +| `review.queue.routing.correct` | `review.queue.override` | `WS-AUTH-001-REV-11` | +| `review.queue.close` | `review.queue.override` | `WS-AUTH-001-REV-11` | +| `review.reconcile.run` | `operations.reconcile.run` | `WS-AUTH-001-REV-11` | +| `review.artifact_reference.reconcile` | `operations.reconcile.run` | `WS-AUTH-001-REV-12` | +| `review.projection.rebuild` | `operations.projection.rebuild` | `WS-AUTH-001-REV-12` | Initial and revision submission use the same `submission.create` action, permission, and route. Revision preparation is an internal participant and @@ -389,8 +405,9 @@ The `OPERATOR` suffix names future activation custody only; it creates no Operator grant or entitlement. All 25 actions remain planned and unavailable. `artifact.verification_job.retry` requires its own later evaluator, guards, and independent activation proof; read/status proof cannot activate retry. The -transfer adds no migration, and all 19 REV rows retain their historical owners -until the separately started REV custody chunk. +ART transfer adds no migration. The separately started REV custody transfer is +also complete: all 19 REV rows now name exact AUTH custodians, remain planned +and unavailable, and add no migration. | ActionId | PermissionId | Principal class | Canonical resource | Resource-owning WS-ART chunk | |---|---|---|---|---| From 9150960ce1f4fdd1f7a02129401f97f43a5ec667 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Mon, 20 Jul 2026 13:31:20 +0100 Subject: [PATCH 2/5] docs(agent-loop): record REV custody review --- .agent-loop/REVIEW_LOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.agent-loop/REVIEW_LOG.md b/.agent-loop/REVIEW_LOG.md index ce87fd1de..cc92a51fb 100644 --- a/.agent-loop/REVIEW_LOG.md +++ b/.agent-loop/REVIEW_LOG.md @@ -2646,3 +2646,17 @@ rejected the stale pre-final evidence record. This state-transition commit is therefore the exact review target; its evidence-only descendant must bind the reviewed SHA, record every reviewer run, pass the evidence gate, and then receive final CI/docs confirmation before external checks resume. +## 2026-07-20 - WS-AUTH-001-REV-CUSTODY Internal Review + +The inherited REV custody contract failed preimplementation review because it +did not independently freeze the exact 19-row transfer, non-dispatch denial, +documentation parity, migration boundary, or hosted coverage gates. The +repaired contract passed all nine plan-review tracks before production edits. + +Implementation candidate `c95239b9` then received all nine required tracks. +Review found a retained ART documentation regression and a stale spec statement +that still described REV custody as pending. Candidate `baa86dfe` restores the +exact ART no-migration invariant, records REV no-migration separately, and +states the completed owner-only transfer. Sixty-three focused cases and all +deterministic scans pass; all nine exact-SHA reviewers report PASS with no open +finding. GitHub full-suite, Agent Gates, CodeRabbit, and human review remain. From 3abc7641b9a95ddd0de4c903944433bf9fd9e58c Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Mon, 20 Jul 2026 13:33:33 +0100 Subject: [PATCH 3/5] docs(agent-loop): bind REV custody evidence --- ...01-REV-CUSTODY-internal-review-evidence.md | 76 ++++++++++++++++++ ...WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md | 79 +++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-internal-review-evidence.md create mode 100644 .agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-internal-review-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-internal-review-evidence.md new file mode 100644 index 000000000..95b86326c --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-internal-review-evidence.md @@ -0,0 +1,76 @@ +# WS-AUTH-001-REV-CUSTODY Internal Review Evidence + +Reviewed code SHA: `9150960ce1f4fdd1f7a02129401f97f43a5ec667` + +Reviewed implementation SHA: `baa86dfe94015f66570844ce81ed1310729cded1` + +Reviewed pre-CI status SHA: `9150960ce1f4fdd1f7a02129401f97f43a5ec667` + +Reviewed against trusted main: `be2a79a243ec50049c37f1f634322a9b3ab895ba` + +Reviewed at: `2026-07-20T12:32:53Z` + +Reviewer run IDs: `rev_plan_core`, `rev_plan_security_qa`, +`rev_plan_ops_ci_docs` + +Reviewer tracks: senior engineering, QA/test, security/auth, product/ops, +architecture, CI integrity, docs, reuse/dedup, and test delta + +## Deterministic Evidence + +- Ruff passed for the complete backend application and test trees. +- Sixty-three focused catalogue, fail-closed construction, documentation + parity, and real-kernel custody cases passed after review repair. The 19 REV + actions each deny as sensitive `action_unavailable` evidence with exact + action and permission and cannot reach revalidation or administrative grant + dependencies. +- The literal test fixture independently freezes all 19 action, permission, + owner, and `planned` values. Exact documentation parsing checks both canonical + tables, mappings, seven owner cardinalities, counts, and operations invariants. +- Stale Workstream and authorization wording scans, Markdown links, loop-memory + state, merge-intent validation, Alembic `0029_shared_transactional_outbox` + head, migration-directory no-diff, and diff integrity pass. +- No workflow, dependency, ActionId, PermissionId, mapping, availability, + evaluator, grant, route, service identity, matrix membership, persistence, + audit schema, or migration changed. The four proposed REV lifecycle actions + remain unregistered. +- A broader local authorization/auth command passed 288 non-database cases and + then failed closed because `WORKSTREAM_TEST_DATABASE_URL` is absent; its + coverage output is not claimed. The unchanged GitHub Backend workflow remains + the mandatory database/full-suite proof for the 78 percent repository-wide + and 90 percent authorization-subsystem coverage floors. + +## Reviewer Results + +| Reviewer | Result | Blocking findings | Notes | +|---|---|---|---| +| senior engineering | PASS AFTER FIXES | none | Restored the retained ART no-migration invariant before exact-SHA re-review. | +| QA/test | PASS AFTER FIXES | none | Corrected one broken ART docs regression and one stale REV-pending statement. | +| security/auth | PASS | none | All 19 actions stay planned and unavailable; no grant, runtime, audit, or persistence path changes. | +| product/ops | PASS AFTER FIXES | none | Custody labels grant no reviewer, Operator, or service authority; PREP remains human-gated. | +| architecture | PASS | none | AUTH changes activation-custody metadata while REV retains facts, guards, and behavior. | +| CI integrity | PASS AFTER FIXES | none | No CI weakening; focused proof is green and hosted full coverage remains mandatory. | +| docs | PASS AFTER FIXES | none | Spec, operations, custody plan, and exact parsed tables now agree. | +| reuse/dedup | PASS | none | Existing catalogue validator, parser pattern, kernel helpers, and evidence abstraction are reused. | +| test delta | PASS AFTER FIXES | none | No removal, skip, xfail, deselection, assertion relaxation, or threshold change. | + +## Findings Resolved + +Valid findings addressed: yes + +Open sub-agent sessions: none + +Initial candidate `c95239b9` replaced the exact retained sentence `The ART +transfer adds no migration` and left a stale spec statement claiming REV +custody was still pending. Repair candidate `baa86dfe` restores the independent +ART invariant, separately records the REV no-migration truth, and states that +all 19 REV rows now have AUTH custody while remaining planned. All nine tracks +passed exact-SHA re-review with no remaining finding. +Status candidate `9150960c` adds only the canonical review-log chronology; all +nine tracks confirmed that exact SHA without a remaining finding. + +## Remaining Risk And Gate + +GitHub Backend, Agent Gates, external review, and explicit human review remain. +All 19 REV actions remain planned and unavailable. PREP is the only declared +successor and requires signed merge memory plus a separate explicit human start. diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md new file mode 100644 index 000000000..fc88d03c4 --- /dev/null +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md @@ -0,0 +1,79 @@ +# WS-AUTH-001-REV-CUSTODY PR Trust Bundle + +## Chunk + +`WS-AUTH-001-REV-CUSTODY` - REV Activation Custody Transfer (L1). + +## Goal And Human-Approved Intent + +Transfer exactly 19 planned REV action-owner labels to seven exact AUTH +activation custodians without changing permission mappings, availability, or +REV behavior. The user explicitly started this chunk after ART custody merged +and signed memory stopped at the REV gate. + +## What Changed And Why + +- Removes seven historical REV `ActionOwner` enum values and adds seven exact + AUTH activation-custodian values. +- Changes only the owner field of the 19 canonical REV definitions. +- Preserves 74 PermissionIds, 65 ActionIds, 17 active and 48 planned actions, + every ActionId-to-PermissionId pair, all non-REV owners, and the exact + seven-identity/eleven-membership service matrix. +- Adds one literal independent 19-row fixture, exact docs parity, mutation + rejection, and all-19 real-kernel denial proof. + +The owner-only typed-catalogue transfer was chosen because AUTH owns activation +custody while REV continues to own resources, facts, guards, jobs, and hidden +behavior. Registration, runtime activation, a migration, or a combined PREP +change were rejected as boundary violations. + +## Scope And Product Behavior + +No migration, database or audit rewrite, availability change, evaluator, +resource composer, route, job, grant, service identity, matrix membership, or +REV lifecycle behavior is included. Custodian labels create no reviewer, +Operator, or service entitlement. All 19 actions remain planned and unavailable; +the four proposed lifecycle actions remain unregistered. + +## Acceptance Proof And Test Delta + +- Ruff passed for `app` and `tests`. +- 63 focused catalogue/kernel/documentation cases passed after review repair. +- Stale wording, stale authorization docs, Markdown links, loop-memory state, + merge intent, Alembic-head/no-migration, and diff checks passed. +- No test or assertion was removed or weakened; no skip, xfail, deselection, + exclusion, workflow, script, dependency, or threshold changed. +- The local environment has no test database; GitHub Backend remains the + authoritative full-suite gate for global coverage at or above 78 percent and + authorization coverage at or above 90 percent. + +## Internal Review And CI Integrity + +Final reviewed status SHA `9150960ce1f4fdd1f7a02129401f97f43a5ec667`, containing +reviewed implementation `baa86dfe94015f66570844ce81ed1310729cded1`, against +trusted main `be2a79a243ec50049c37f1f634322a9b3ab895ba` passes senior engineering, +QA/test, security/auth, product/ops, architecture, CI integrity, docs, +reuse/dedup, and test-delta review after two documentation findings were fixed. +No CI, coverage configuration, package command, workflow, or migration file +changed. + +## External Review And Remaining Risks + +GitHub Backend, Agent Gates, and CodeRabbit remain pending until publication. +The remaining risk is exact metadata/docs drift, bounded by literal independent +fixtures, exact table parsing, frozen whole-catalogue expectations, and hosted +full-suite proof. + +## Follow-Up And Human Review Focus + +The sole merge-intent successor is `WS-AUTH-001-PREP`, which must not start +until this PR merges, signed memory succeeds, and the user explicitly starts it. +Human review should verify the exact 19-row/seven-custodian owner-only delta, +`2/5/3/1/1/5/2` cardinalities, unchanged ART/non-REV rows, mappings, counts, +matrix and availability, absent lifecycle registration, and zero migration. + +## Human Merge Ownership + +The agent may publish and repair this branch but may not merge it. Only the +human may approve this PR for merge. Trusted-main automation owns signed +post-merge memory generation. From 438c2bcf6a91b97f46264a1b6e5d0110a28f16d5 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Mon, 20 Jul 2026 13:41:11 +0100 Subject: [PATCH 4/5] fix(agent-loop): normalize PREP successor heading --- .../chunks/WS-AUTH-001-PREP-prepared-mutation-protocol.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-PREP-prepared-mutation-protocol.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-PREP-prepared-mutation-protocol.md index 69413b49d..3a07fcb5c 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-PREP-prepared-mutation-protocol.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/chunks/WS-AUTH-001-PREP-prepared-mutation-protocol.md @@ -1,4 +1,4 @@ -# Chunk Contract: WS-AUTH-001-PREP — Prepared Mutation Authorization Protocol +# Chunk Contract: WS-AUTH-001-PREP - Prepared Mutation Authorization Protocol ## Parent initiative From ec77e64c746947c24b3da47a221869dd11bce642 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Mon, 20 Jul 2026 13:43:23 +0100 Subject: [PATCH 5/5] docs(agent-loop): bind REV custody CI repair --- ...WS-AUTH-001-REV-CUSTODY-internal-review-evidence.md | 10 ++++++++-- .../reviews/WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-internal-review-evidence.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-internal-review-evidence.md index 95b86326c..d86efe60f 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-internal-review-evidence.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-internal-review-evidence.md @@ -1,14 +1,16 @@ # WS-AUTH-001-REV-CUSTODY Internal Review Evidence -Reviewed code SHA: `9150960ce1f4fdd1f7a02129401f97f43a5ec667` +Reviewed code SHA: `438c2bcf6a91b97f46264a1b6e5d0110a28f16d5` Reviewed implementation SHA: `baa86dfe94015f66570844ce81ed1310729cded1` Reviewed pre-CI status SHA: `9150960ce1f4fdd1f7a02129401f97f43a5ec667` +Reviewed CI-repair SHA: `438c2bcf6a91b97f46264a1b6e5d0110a28f16d5` + Reviewed against trusted main: `be2a79a243ec50049c37f1f634322a9b3ab895ba` -Reviewed at: `2026-07-20T12:32:53Z` +Reviewed at: `2026-07-20T12:42:54Z` Reviewer run IDs: `rev_plan_core`, `rev_plan_security_qa`, `rev_plan_ops_ci_docs` @@ -68,6 +70,10 @@ all 19 REV rows now have AUTH custody while remaining planned. All nine tracks passed exact-SHA re-review with no remaining finding. Status candidate `9150960c` adds only the canonical review-log chronology; all nine tracks confirmed that exact SHA without a remaining finding. +GitHub Agent Gates then found that the existing PREP successor heading used an +em dash while the canonical schema-v2 parser requires ` - `. Repair +`438c2bcf` normalizes only that delimiter, starts no PREP work, and passes exact +merge-intent validation plus all nine exact-SHA repair tracks. ## Remaining Risk And Gate diff --git a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md index fc88d03c4..2dbf8e6a7 100644 --- a/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md +++ b/.agent-loop/initiatives/WS-AUTH-001-workstream-authorization-service/reviews/WS-AUTH-001-REV-CUSTODY-pr-trust-bundle.md @@ -49,13 +49,15 @@ the four proposed lifecycle actions remain unregistered. ## Internal Review And CI Integrity -Final reviewed status SHA `9150960ce1f4fdd1f7a02129401f97f43a5ec667`, containing +Final reviewed CI-repair SHA `438c2bcf6a91b97f46264a1b6e5d0110a28f16d5`, containing reviewed implementation `baa86dfe94015f66570844ce81ed1310729cded1`, against trusted main `be2a79a243ec50049c37f1f634322a9b3ab895ba` passes senior engineering, QA/test, security/auth, product/ops, architecture, CI integrity, docs, reuse/dedup, and test-delta review after two documentation findings were fixed. No CI, coverage configuration, package command, workflow, or migration file changed. +The repair only normalizes the PREP contract heading delimiter required by the +schema-v2 successor parser; it does not start or change PREP. ## External Review And Remaining Risks