From 1fc8eed19ca60d3b3dff15bbcae338deb83ecd82 Mon Sep 17 00:00:00 2001 From: codex Date: Sun, 1 Nov 2026 23:32:07 +0100 Subject: [PATCH] docs: preserve reviewed NetKingdom session artifacts --- ...NK-WP-0013-playbook-capability-contract.md | 216 +++ ...r-engine-preparation-boundary-contracts.md | 183 ++ .../candidate.md | 1519 +++++++++++++++++ .../commit-receipt.json | 7 + .../consistency.json | 331 ++++ .../consistency.stderr.txt | 0 .../final_verify.py | 27 + .../identifier-preview.json | 11 + .../manifest.json | 332 ++++ .../preimage.json | 367 ++++ .../preservation-verification.json | 7 + .../projection.json | 996 +++++++++++ .../projection.stderr.txt | 0 .../restore_verify.py | 23 + .../tasks-after.json | 715 ++++++++ .../tasks-before.json | 715 ++++++++ .../verification.json | 96 ++ .../comparison-review.json | 164 ++ .../docs/security-case-execution-kernel-v1.md | 127 ++ .../delta-fixture/pytest.ini | 2 + .../delta-fixture/src/warden/__init__.py | 3 + .../delta-fixture/src/warden/case_inputs.py | 98 ++ .../src/warden/case_receipt_state.py | 146 ++ .../delta-fixture/src/warden/case_run.py | 981 +++++++++++ .../test_case_original_context_consistency.py | 198 +++ .../delta-fixture/tests/test_case_run.py | 700 ++++++++ .../tests/test_context_compare_probes.py | 131 ++ ...EN-WP-0044-security-case-follow-through.md | 289 ++++ .../delta-probe-results.json | 17 + .../delta-probes.stderr.txt | 0 .../delta-probes.stdout.txt | 2 + .../original-context-compare-r2/manifest.json | 124 ++ .../minimal-delta-tests.json | 17 + .../minimal-integration.patch | 427 +++++ .../patch-check.json | 7 + .../probe-results.json | 17 + .../probes.stderr.txt | 0 .../probes.stdout.txt | 2 + .../docs/security-case-execution-kernel-v1.md | 127 ++ .../snapshot/pytest.ini | 2 + .../snapshot/src/warden/__init__.py | 3 + .../snapshot/src/warden/case_inputs.py | 98 ++ .../snapshot/src/warden/case_receipt_state.py | 146 ++ .../snapshot/src/warden/case_run.py | 826 +++++++++ .../snapshot/tests/test_case_run.py | 700 ++++++++ .../tests/test_context_compare_probes.py | 131 ++ ...EN-WP-0044-security-case-follow-through.md | 289 ++++ .../source-state.json | 57 + .../after.json | 37 + .../before.json | 29 + 50 files changed, 11442 insertions(+) create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/backup/workplans/archived/260702-NK-WP-0013-playbook-capability-contract.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/backup/workplans/archived/260702-NK-WP-0014-user-engine-preparation-boundary-contracts.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/candidate.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/commit-receipt.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/consistency.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/consistency.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/final_verify.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/identifier-preview.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/manifest.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/preimage.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/preservation-verification.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/projection.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/projection.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/restore_verify.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/tasks-after.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/tasks-before.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/verification.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/comparison-review.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/docs/security-case-execution-kernel-v1.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/pytest.ini create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/__init__.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_inputs.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_receipt_state.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_run.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_case_original_context_consistency.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_case_run.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_context_compare_probes.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/workplans/WARDEN-WP-0044-security-case-follow-through.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probe-results.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probes.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probes.stdout.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/manifest.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/minimal-delta-tests.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/minimal-integration.patch create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/patch-check.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probe-results.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probes.stderr.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probes.stdout.txt create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/docs/security-case-execution-kernel-v1.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/pytest.ini create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/__init__.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_inputs.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_receipt_state.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_run.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/tests/test_case_run.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/tests/test_context_compare_probes.py create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/workplans/WARDEN-WP-0044-security-case-follow-through.md create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/source-state.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-owner-followthrough-r3/after.json create mode 100644 docs/changes/netkingdom-target-2026-10-17/original-context-owner-followthrough-r3/before.json diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/backup/workplans/archived/260702-NK-WP-0013-playbook-capability-contract.md b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/backup/workplans/archived/260702-NK-WP-0013-playbook-capability-contract.md new file mode 100644 index 00000000..aa5edb5f --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/backup/workplans/archived/260702-NK-WP-0013-playbook-capability-contract.md @@ -0,0 +1,216 @@ +--- +id: NK-WP-0013 +type: workplan +title: "Playbook Capability Contract" +domain: infotech +repo: net-kingdom +status: finished +owner: worsch +topic_slug: netkingdom +planning_priority: high +planning_order: 13 +created: "2026-05-21" +updated: "2026-05-22" +depends_on: + - NK-WP-0006 +state_hub_workstream_id: 32a54d8e-8633-42a6-8ec1-104842c581c1 +--- + +> **Terminology note:** Historical text in this archived workplan may use the legacy term "workstream". The fleet term is **workplan** (`canon/standards/workplan-terminology-fleet_v0.1.md`). + +# NK-WP-0013 — Playbook Capability Contract + +> The **orchestration-layer analog of the IAM Profile**. ADR-0007's +> meta-orchestration refinement named this as a prerequisite: for NetKingdom +> to select and parametrize playbooks reliably, each Railiance playbook must +> publish a **declared interface** — the capability it provisions, its +> parameters (with defaults and constraints), and the responsibility it +> claims. Without it, meta-orchestration composes against implicit behavior +> and breaks silently when a playbook changes. This workplan defines that +> contract. + +## Goal + +Define the **Playbook Capability Contract** — the schema and vocabulary by +which a playbook declares: + +1. **what capability it provisions** (controlled vocabulary, aligned to the + capability ladder and the responsibility-map resource kinds), +2. **its parameters** — name, type, default, constraints, required/optional, + and which are security-sensitive, and +3. **the responsibility it claims** — which element owns which resources, and + which trust state(s) / readiness checks it satisfies or requires. + +…plus the **catalog/consumption model** NetKingdom meta-orchestration uses +to select, parametrize, and build a responsibility map for a scenario, and a +**conformance validator** for declarations. + +## Ownership (the boundary this realizes) + +By symmetry with the IAM Profile, the **consumer that needs a stable +interface defines the contract; the provider conforms**: + +| | Contract owner | Publishes conformant declarations | Consumer | +|---|---|---|---| +| Identity | NetKingdom (IAM Profile) | key-cape / Keycloak | apps, flex-auth | +| Orchestration | **NetKingdom (this contract)** | Railiance playbooks | NetKingdom meta-orchestration | + +NetKingdom owns the contract schema; Railiance owns the playbooks and +publishes one declaration per playbook; **execution stays in Railiance** +(ADR-0007 unchanged). The contract makes a playbook change a versioned event +rather than a silent break. + +## Scope + +In scope: + +- ownership & boundary decision (ADR) and contract versioning governance +- the capability vocabulary a declaration draws from +- the parameter declaration format (defaults, constraints, sensitivity) +- the responsibility-claim and trust-state/readiness format +- the catalog and the meta-orchestration consumption model +- a conformance validator for declarations +- coordinated adoption proof with one reference Railiance playbook + +Out of scope: + +- authoring the playbooks themselves (Railiance owns execution) +- writing declarations for every existing playbook (Railiance work; this + workplan proves the contract with one reference declaration) +- a dedicated execution-orchestration repo (deferred per ADR-0007) + +## Tasks + +```task +id: NK-WP-0013-T1 +state_hub_task_id: d40f8b29-e983-4d52-bc1f-5f1c51709e7d +status: done +priority: high +``` + +**Ownership & boundary decision (ADR-0012).** Record that NetKingdom owns +the Playbook Capability Contract schema (consumer-defines-contract, IAM +Profile precedent), Railiance authors playbooks and publishes conformant +declarations, and execution stays in Railiance (ADR-0007). Decide whether +the contract is NetKingdom-owned outright or NetKingdom-owned with Railiance +co-design. Define contract **versioning and breaking-change governance**. + +```task +id: NK-WP-0013-T2 +state_hub_task_id: ece4b5b1-e1c2-449d-b0f4-83b7010bc838 +status: done +priority: high +``` + +**Capability vocabulary.** Define the controlled vocabulary of capabilities +a playbook can declare it provisions, aligned to the **capability ladder +(C0–C6)** and the **responsibility-map resource kinds** (identities, +roles/scopes/policies, secrets/credentials, infrastructure resources). This +is what "capability" means in a declaration, so two playbooks providing the +same capability are comparable. + +```task +id: NK-WP-0013-T3 +state_hub_task_id: c956f4a8-b9fa-44ab-8174-31999b98e3b1 +status: done +priority: high +``` + +**Parameter declaration format.** Define how a playbook declares its +parameters: name, type, **default**, **constraints** (allowed values / +ranges), required vs optional, and a **security-sensitivity** marker so +NetKingdom knows which parameters it may tune versus which are +security-critical and constrained. This is what makes "parametrize where +adequate" safe rather than guesswork. + +```task +id: NK-WP-0013-T4 +state_hub_task_id: e7de05a6-528a-4213-b6db-2c2e90353996 +status: done +priority: high +``` + +**Responsibility-claim & trust-state format.** Define how a declaration +states which element **owns which resources** (feeding the responsibility +map) and which **trust state(s) / readiness checks** (ADR-0007 model) it +satisfies or requires. This lets NetKingdom assemble a coherent +responsibility map and sequence for a scenario from the declarations. + +```task +id: NK-WP-0013-T5 +state_hub_task_id: 05a2ff7d-86c4-4de9-9ea8-39a9ad5352a8 +status: done +priority: high +``` + +**Catalog, consumption model, and conformance validator.** Define how +declarations are published and discovered (the catalog), how +meta-orchestration consumes them to **select**, **parametrize**, and build a +**responsibility map** for a scenario, and a **conformance validator** that +checks a declaration against the contract (the orchestration-layer analog of +the IAM Profile conformance check, NK-WP-0012-T5). + +```task +id: NK-WP-0013-T6 +state_hub_task_id: 769ed490-c091-41c1-b2e2-e8e378470b6b +status: done +priority: medium +``` + +**Reference adoption with Railiance.** Coordinate with Railiance to have +**one reference playbook publish a conformant declaration end-to-end**, and +demonstrate NetKingdom selecting + parametrizing it from the declaration +alone. Proves the contract is sufficient and is the template for adopting +the rest. Cross-repo coordination item for the Railiance domain. + +## Acceptance Criteria + +- An ADR records contract ownership, the Railiance-publishes/NetKingdom- + consumes split, and ADR-0007 is confirmed unchanged. +- The contract specifies capability vocabulary, parameter format (defaults, + constraints, sensitivity), and responsibility/trust-state claims. +- A catalog/consumption model and a declaration conformance validator exist. +- At least one Railiance playbook publishes a conformant declaration, and + NetKingdom composes and parametrizes it from that declaration alone. +- Contract versioning and breaking-change governance is documented. + +## Completion Notes + +- ADR: `docs/adr/ADR-0012-playbook-capability-contract-ownership.md` +- Canonical contract: + `canon/standards/playbook-capability-contract_v0.1.md` +- Machine-readable schema: + `canon/schemas/playbook-capability-declaration_v0.1.schema.json` +- Validator and composition demo: + `tools/playbook-capability-contract/playbook_contract_validator.py` +- Reference Railiance declaration: + `../railiance-infra/capabilities/playbooks/railiance-infra.bootstrap-host.yaml` +- Sample scenario: + `examples/playbook-capability-contract/scenario-s1-host-bootstrap.yaml` +- Fixture tests cover valid declarations, controlled vocabulary failures, + forbidden tenant overrides, missing required parameters, and successful + selection/parameter composition. + +## Dependencies & Sequencing + +- **Realizes** the playbook-contract dependency from ADR-0007's + meta-orchestration refinement; does not change ADR-0007. +- **Depends on NK-WP-0006** for the trust-state model and the + responsibility-map resource kinds the declarations reference. +- **Coordinates with the Railiance domain** — NetKingdom defines the + contract; Railiance publishes declarations (T6) and continues to own + execution. +- Parallels NK-WP-0012: same consumer-defines-contract pattern, same + conformance-check shape, applied to orchestration instead of identity. + +## Resolved Questions + +- Contract format and home: NetKingdom canon standard plus a + machine-readable JSON schema and standalone validator. +- Catalog mechanism: v0.1 uses file convention + `capabilities/playbooks/*.yaml`; a registry can be layered on later. +- Parameter sensitivity: tenant scenarios cannot override + `platform_only`, `forbidden`, `playbook_default`, + `security_sensitive`, or `secret_reference` parameters. +- Validator form: standalone NetKingdom tool for v0.1, mirroring + NK-WP-0012's executable-contract pattern. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/backup/workplans/archived/260702-NK-WP-0014-user-engine-preparation-boundary-contracts.md b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/backup/workplans/archived/260702-NK-WP-0014-user-engine-preparation-boundary-contracts.md new file mode 100644 index 00000000..ab065279 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/backup/workplans/archived/260702-NK-WP-0014-user-engine-preparation-boundary-contracts.md @@ -0,0 +1,183 @@ +--- +id: NK-WP-0014 +type: workplan +title: "User Engine Preparation And Boundary Contracts" +domain: infotech +repo: net-kingdom +status: finished +owner: codex +topic_slug: netkingdom +planning_priority: high +planning_order: 14 +created: "2026-05-22" +updated: "2026-05-22" +depends_on: + - NK-WP-0012 + - NK-WP-0013 +state_hub_workstream_id: "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c" +--- + +> **Terminology note:** Historical text in this archived workplan may use the legacy term "workstream". The fleet term is **workplan** (`canon/standards/workplan-terminology-fleet_v0.1.md`). + +# NK-WP-0014 - User Engine Preparation And Boundary Contracts + +## Goal + +Prepare implementation of `/home/worsch/user-engine` by turning the current +architecture review into concrete contracts. This workplan must be completed +before production code is started, so the MVP does not duplicate IAM, +authorization, application registration, membership, audit, or deployment +responsibilities already owned by NetKingdom-aligned components. + +## Context + +The user-engine PRD and architecture blueprint define a headless user-domain +service for users, accounts, profiles, preferences, memberships, application +catalogs, projections, audit, and events. + +The architecture review in +`docs/reviews/2026-05-22T19-19-59+0200-user-engine-architecture-review.md` +identified the main risk: duplicate truth across IAM providers, user-engine, +flex-auth, application registrations, OIDC clients, protected systems, and +profile projections. + +NetKingdom keeps this workplan because it owns cross-repo boundary and +orchestration guidance. Implementation work now lives in the `user-engine` +repository as `USER-WP-0001` through `USER-WP-0006`. + +## Scope + +In scope: + +- source-of-truth matrix; +- membership synchronization contract; +- application onboarding contract; +- projection boundary split; +- authorization performance model; +- audit correlation contract; +- user-engine repo preparation artifacts. +- NetKingdom/user-engine interface guidance. + +Out of scope: + +- implementing user-engine production code, which belongs in the user-engine + repository workplans; +- implementing UI repos; +- implementing SCIM or enterprise federation adapters; +- changing the NetKingdom IAM Profile. + +## Tasks + +```task +id: NK-WP-0014-T1 +status: done +priority: high +state_hub_task_id: "4e941174-ac55-4f6e-8568-40f45b1ed821" +``` + +**Source-of-truth matrix.** Define which system owns each resource kind: +identity claims, human subjects, local user records, account lifecycle, +groups, roles, memberships, tenants, applications, OIDC clients, +flex-auth protected systems, catalog namespaces, profile values, +effective-profile projections, audit records, and events. + +Initial guidance lives in `docs/user-engine-interface-guidance.md` and should +be hardened into versioned contracts as implementation feedback arrives. + +```task +id: NK-WP-0014-T2 +status: done +priority: high +state_hub_task_id: "5ddc46bb-6238-4944-a023-d8b46b410c76" +``` + +**Membership synchronization contract.** Specify which memberships are mastered +in user-engine, which are imported from IAM or provisioning systems, which are +exported to flex-auth, and how freshness, conflict handling, deletes, and +tenant boundaries are represented. + +```task +id: NK-WP-0014-T3 +status: done +priority: high +state_hub_task_id: "696bc65b-0f8d-47b5-bccc-65ed285b42e6" +``` + +**Application onboarding contract.** Define the binding between a user-engine +application, IAM OIDC client, flex-auth protected system, catalog namespace, +event/audit identity, and Railiance deployment metadata. Include a checklist +for adding one new application safely. + +```task +id: NK-WP-0014-T4 +status: done +priority: high +state_hub_task_id: "99ab4535-cbbf-4e13-a2c5-adfc814d5aeb" +``` + +**Projection and claims-enrichment boundaries.** Split projection types into +runtime, self-service UI, admin UI, audit, agent context, and optional claims +enrichment. Explicitly state that user-engine does not issue tokens and that +claims enrichment is adapter-owned and cache/freshness governed. + +```task +id: NK-WP-0014-T5 +status: done +priority: medium +state_hub_task_id: "d35a1356-a9fe-4cf3-8fb0-6f45cfbbccee" +``` + +**Authorization and audit contracts.** Define the user-engine resource/action +vocabulary for flex-auth, when checks are synchronous, when they may be +batched or cached, and how user-engine audit records correlate with flex-auth +decision IDs and platform audit sinks. + +```task +id: NK-WP-0014-T6 +status: done +priority: medium +state_hub_task_id: "04fd2f92-3aa4-468d-9e9e-9b092890507e" +``` + +**Prepare the user-engine repo.** Add or update user-engine `SCOPE.md`, +architecture notes, repo layout decision, local development contract, and +initial implementation readiness checklist. Do not add NetKingdom +orchestration relationships to user-engine `INTENT.md`; those belong in +NetKingdom documents per ADR-0010. + +## Acceptance Criteria + +- Boundary contracts exist and are reviewed against the PRD and architecture + review. +- user-engine responsibilities are distinct from key-cape, Keycloak, + flex-auth, OpenBao, Railiance, user-account, and user-manager. +- The first implementation slice can start without unresolved ownership of + memberships, applications, projections, authorization checks, or audit + correlation. +- user-engine repo-local workplans carry implementation tasks, while + NetKingdom retains only boundary, orchestration, and responsibility-map work. +- NetKingdom responsibility-map updates are either applied or explicitly + deferred until user-engine becomes a shared platform service. + +## Completion Notes + +Completed on 2026-05-22: + +- Added the canonical versioned contract at + `canon/standards/user-engine-boundary-contract_v0.1.md`. +- Promoted `docs/user-engine-interface-guidance.md` to an implementation guide + that points at the canonical contract. +- Confirmed `docs/responsibility-map.md` already includes `user-engine` as an + orchestrated repository and names NetKingdom's boundary responsibilities. +- Confirmed `/home/worsch/user-engine` already carries the required repo-local + preparation artifacts: `SCOPE.md`, `wiki/ArchitectureBlueprint.md`, + `docs/development.md`, `docs/configuration.md`, + `docs/interfaces/netkingdom-integration.md`, and `USER-WP-0001` through + `USER-WP-0006`. + +## Dependencies And Sequencing + +- Depends on NK-WP-0012 for IAM Profile consumption rules. +- Depends on NK-WP-0013 for NetKingdom meta-orchestration conventions. +- Gates implementation work in `/home/worsch/user-engine`, now tracked as + `USER-WP-0001` through `USER-WP-0006`. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/candidate.md b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/candidate.md new file mode 100644 index 00000000..8c3529a6 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/candidate.md @@ -0,0 +1,1519 @@ +--- +id: NK-WP-0048 +type: workplan +title: "Establish SecurityCaseTypes so that known security cases are light" +domain: infotech +repo: net-kingdom +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-17" +depends_on: [] +related_repos: + - ops-mason + - ops-warden + - key-cape + - railiance-platform + - informed-decision +state_hub_workstream_id: "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" +--- + +# NK-WP-0048 - Establish SecurityCaseTypes so that known security cases are light + +## Why + +The founder's direction (2026-10-08): setting up a security integration of a +kind that has been done before should be easy, meaning accept the case, then +ops-mason arranges it and ops-warden follows through. Setting up a new kind +needs ample documentation and tests and an informed-decision, because its shape +is part of the security architecture. + +Today every integration is hand-built: per-client helper scripts, per-lane +custody, founder checklists. The trigger was KEY-WP-0038 (Forgejo as an OIDC +client), whose next step was going to be yet another hand-written rollout +script. + +This plan continues the pattern work of NK-WP-0008 (pattern infospace), +NK-WP-0009 (tutorials) and NK-WP-0030 (scenario composition). It turns patterns +into **SecurityCaseTypes** that ops-mason and ops-warden can execute. Canon: +`canon/standards/security-case-types_v0.1.md` (proposed). + +Companion plans: ops-mason `MASON-WP-0010` (materialization) and ops-warden +`WARDEN-WP-0044` (follow-through). + +## Tasks + +```task +id: NK-WP-0048-T01 +status: done +priority: high +state_hub_task_id: "930ac3e9-20ec-5473-98cd-3fe8fddb730f" +``` + +### T01 — Accept the terminology and the canon + +Founder review of `canon/standards/security-case-types_v0.1.md`, including its +section 10 open questions. On acceptance the canon status goes from `proposed` +to `accepted`. + +2026-10-08: accepted by the founder (in session). Canon status `accepted`; +section 10's open questions are carried forward to the first type (T04, T05). + +```task +id: NK-WP-0048-T02 +status: done +priority: high +state_hub_task_id: "19c25d96-a55c-58f0-9168-3fdbc4955fc2" +``` + +### T02 — Inventory the de-facto types already built by hand + +Read-only. List every security integration built so far and group them by shape. +Known starting points: + +- browser OIDC relying parties on key-cape: informed-decision-approver, + coulomb-social, user-engine portal, openbao-admin, and Forgejo (pending) +- confidential service clients with `client_credentials`: secrets-engine + approval, requester and openbao clients, approval-engine operator +- workload KV read lanes on OpenBao: the railiance-platform credential lane + designs and CCRs, for example `railiance-backup-offsite-lane` +- short-lived SSH certificates through ops-warden (`warden sign`) +- tunnelled operator access (ops-bridge) + +For each: the repositories involved, the hand-built steps, attended human steps, +custody paths, verification evidence, and rollback (if any). Output +`docs/security-case-type-inventory.md`, ranking candidate types by how often the +shape recurs and how much manual work each instance costs today. + +2026-10-08: done, `docs/security-case-type-inventory.md`. 36 of 37 CCRs are +already `request_type: workload-kv-read`, a de-facto type reviewed each time as +if it were new. Browser clients: 5 hand-built plus Forgejo, each with its own +rollout script. Confidential service clients need a registration plus two or +three KV-read CCRs each. A forge read identity is needed in bulk by +ORGREF-WP-0002-T06. Recommendation: oidc-browser-relying-party first (unblocks +the forge), workload-kv-read second (largest payoff), then forge-read-identity. + +```task +id: NK-WP-0048-T03 +status: done +depends_on: [NK-WP-0048-T01] +priority: high +state_hub_task_id: "e487975b-73b1-516c-971d-da4cbbb93063" +``` + +### T03 — Schema, template and validator + +- `canon/schemas/security-case-type_v0.1.schema.json` and + `security-case_v0.1.schema.json` +- `security-case-types/_template/` with `type.yaml`, `README.md` and tests to copy +- a validator, extending `tools/security-scenario-composer/`, that checks a type, + checks a case against its type, and renders the case into the composer's + deterministic plan (still `execution.permitted: false`) + +Acceptance: the validator rejects missing sections, secret-shaped parameter +values, unpinned providers and cases against non-`implemented` types, with tests +for each. + +2026-10-08: done. `canon/schemas/security-case-type_v0.1.schema.json` and +`security-case_v0.1.schema.json`; `tools/security-case-types/security_case_validator.py` +(`validate-type`, `validate-case`, `render`, reusing the scenario composer); +`security-case-types/_template/` (a valid, runnable example type over +`net-kingdom.local-identity`, with README sections, an example case and tests that +travel with every copy). 31 tests pass, including the composer's existing suite. + +```task +id: NK-WP-0048-T04 +status: done +depends_on: [NK-WP-0048-T02, NK-WP-0048-T03] +priority: high +state_hub_task_id: "2fffe112-7d55-5680-9952-78d74f44f87f" +``` + +### T04 — Define the first type: browser OIDC relying party + +`security-case-types/oidc-browser-relying-party/`, version 1.0: + +- parameters: client id, display name, exact redirect URIs, scopes, client type + (confidential or public), MFA pin, the groups the consuming service maps, + custody path for the secret (confidential only), consuming service and its + owner +- composition: key-cape client registration (key-cape declares it as a playbook + capability), secret minting into custody (railiance-platform), LLDAP group + existence and membership (net-kingdom identity-provisioner), handover to the + consuming service +- materialization (ops-mason) and follow-through (ops-warden) sections +- verification: discovery lists the redirect URI; a test sign-in returns the + expected claims for one human; anonymous access to the custody path is denied +- rollback: remove exactly this client and its custody path +- README: threat model, MFA rationale, why redirect URIs are exact, which steps + still need an attended login and how that is minimised +- reference instances: informed-decision-approver, coulomb-social, user-engine + portal, openbao-admin + +Acceptance: complete per canon section 3, tests green, status `proposed`. + +2026-10-08: done. `security-case-types/oidc-browser-relying-party/` 1.0 (proposed): +`type.yaml` with 12 parameters (the custody reference is required only for +confidential clients), composition over key-cape's new +`key-cape.oidc-client-registration` declaration (contract PASS, `maturity: +planned`), materialization and follow-through, verification and rollback, all +conditional on client type where it matters. One attended step, +`register-client`, justified by the live registry being a Kubernetes Secret. +README with threat model, alternatives, an honest fit table (portal and vergabe +fit; Forgejo is the first confidential case; openbao-admin, coulomb.social and +informed-decision-approver do not fit 1.0), and known limits. Examples: Forgejo +(checked against the accepted request) and a public portal. The validator gained +`when` / `required_when` conditions and provider discovery. 43 tests pass. +ops-mason's and ops-warden's sections are drafted in the type. Their owners +review them in MASON-WP-0010-T01 and WARDEN-WP-0044-T01 before acknowledgment +(T05). + +```task +id: NK-WP-0048-T05 +status: done +depends_on: [NK-WP-0048-T04, MASON-WP-0010-T01, WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "3d987d3d-0797-538b-86e4-51185a327ae2" +``` + +### T05 — Acknowledge the type through informed-decision + +Prepare the Decision Memo from the type's README and `type.yaml` and submit it +through informed-decision's production intake. On acknowledgment the type +moves to `acknowledged`. + +2026-10-08: Decision Memo prepared. Hub decision `e418263e-a71f-4f01-a970-c87c9e187c09`. +`security-case-types/oidc-browser-relying-party/decision/`: `packet.md` (question, +one act, what acknowledging does not do, the shape, alternatives, attended +steps, limits, evidence), `build_intake.py` and the built `intake.json` (memo +class `decision-record`, review group `net-kingdom-admins`, resolves the hub +decision on answer). The envelope passes informed-decision's own +`validate_submission`. Queuing it is an attended operator step, a write inside +the production dispatcher pod: + + kubectl -n state-hub exec -i deploy/state-hub-memo-dispatch -- \ + python -m api.services.memo_dispatch --queue /data/private enqueue --envelope /dev/stdin \ + < security-case-types/oidc-browser-relying-party/decision/intake.json + +The answer then appears for review in informed-decision. + +2026-10-08: queued by the founder (`pending`, `queued`) after one refusal: the +first envelope's idempotency key was not State Hub's stable +`state-hub..` form, and the builder now validates against both +sides. The dispatcher delivered it to informed-decision (ledger 7 → 8 +delivered). T05 waits for the founder's answer in informed-decision, which +resolves hub decision `e418263e`. + +2026-10-08: ops-mason's review (MASON-WP-0010-T01) folded into the type, still +1.0 `proposed`: `secret_ref` is no longer a parameter, the type derives +`platform/workloads/keycape/{client_id}`; policies are +`workload-kv-read-keycape-{client_id}-eso` and +`workload-kv-read-keycape-{client_id}-{consuming_service}`, each with a templated +body (`read` on that one data path); new `openbao_role` +`external-secrets-keycape-{client_id}` (Kubernetes auth, ESO service account, +15m, no default policy); catalog entry `{consuming_service}-keycape-client`; +the two rollback steps on the KV path collapse into one metadata delete. The +schema and validator now require a `body` on policy and role items. 105 tests +pass. Two consequences: the delivered memo describes the type before these +changes (a case-set custody reference, two differently named policies, no ESO +role), so the founder answers on a superseded shape unless the memo is +re-sent; and Forgejo's custody path moves from the accepted request's proposed +`platform/workloads/forgejo/netkingdom-oidc-client` to +`platform/workloads/keycape/forgejo`, which key-cape (KEY-WP-0038) and +railiance-apps (RAPPS-WP-0016-T02) need to adopt in T06. ops-warden's review +(WARDEN-WP-0044-T01) is still in progress. + +2026-10-08: revised memo `nk-wp-0048-t05-oidc-browser-relying-party-r2` built +(`packet.md`, `intake.json`, packet `sha256:af6435e2…`); it states that it +supersedes the first memo and passes informed-decision's and the dispatcher's +validation. It needs a new memo id because the dispatcher refuses new content +under a delivered idempotency key (`idempotency_conflict`). Queuing it is the +same attended step as above. Both memos serve hub decision `e418263e`; the +first answer resolves it, so the first memo must not stay open: an accept on it +would acknowledge the superseded shape, and a decline would resolve the +decision against r2. Close it with `return` (a reviewer verb that requests a +new revision and does not resolve the hub decision; only accept and decline +do), commenting that r2 supersedes it, then answer r2. `withdraw` exists in +the vocabulary but is not offered to reviewers, and lifecycle `revise` only +applies to lifecycle-registered memos, not intake-delivered ones. + +2026-10-09: the founder reports r2 enqueued and the first memo returned. At +close, hub decision `e418263e` still showed `open` (not verified whether r2 +was answered or the answer is still in transit; the dispatcher ledger was not +read). ops-warden's review (WARDEN-WP-0044-T01, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`, d470f2e) arrived at +21:25, before r2 was built, and is not in r2. Required before WARDEN-WP-0044-T02: +R1 a per-step `lane` (missing: identity-provisioner caller credential, +railiance-platform broker grant), R2 canon meaning of `warden_executes: true` +(caller proxy, caller identity, no standing ops-warden token; ADR-0002), R3 a +per-step `value_handling`. Refinements R4 (one type-level catalog lane instead +of a per-case entry, which conflicts with ops-mason's item 5), R5 (ESO Ready gate +before bind-env), R6 (exact accepted memberships), R7 (mfa-pinned and +secret-delivered checks). Next: if r2 was accepted, fold R1-R3 and R5-R7 in as +a follow-up and settle R4 between ops-warden and ops-mason before T06; if not, +hold r2, fold them in and build r3. + key-cape and railiance-apps were told about Forgejo's new custody +path. + +2026-10-09: r2 was not answered (hub decision `e418263e` open; dispatcher 9 +delivered), so r2 is held and r3 replaces it. ops-warden withdrew R4 for R4′ +(message a66a9204): one catalog entry per case with its body templated in the +type. Folded in: + +- Canon section 3: a narrow meaning of `warden_executes: true` (caller proxy, + caller identity, no standing credential; ops-warden ADR-0002), plus per-step + `lane`, `value_handling` and an optional `ready_check`. A catalog entry body is + now required, like policy and role bodies. +- Schema and validator enforce all of these. +- The type: lanes `identity-provisioner-groups` and + `keycape-client-secret-generate` (both new) and `key-cape-oidc-login`; + `mint-secret` is `generated_in_custody`; a Ready gate on `deliver-secret`; + `ensure-groups` adds exactly the accepted case's memberships; new checks + `secret-delivered` and `mfa-pinned`; the catalog-entry body follows R4′. +- 110 tests pass. + +Memo r3 (`nk-wp-0048-t05-oidc-browser-relying-party-r3`, packet +`sha256:726dfc01…`) passes informed-decision's and the dispatcher's validation. +The founder queues it with the same command, returns r2 with a comment that r3 +supersedes it, then answers r3. + +Prerequisites for T06, not for T05: +- the two new lanes, each through its owner; +- a founder-approved rollout of an identity-provisioner image containing + 630ebfd (the deployed one is main-6c4fcaf from 2026-09-24, without + `/v1/groups/ensure`). + +ops-mason's request to create LLDAP group `fluid-telegram-operators` (message +259ec26d) was answered: not possible from here by that path; the founder creates +it in the LLDAP admin UI. + +2026-10-09: ops-warden finished its side of WARDEN-WP-0044-T01 (message +`58d33f40`, commit 0e7ceb8): the revised type conforms to `warden case +check-type`, with no further changes asked of the type. This acknowledges that +review. Its later addendum R8 (message `af274783`, ops-warden +`history/2026-10-08-oidc-rp-follow-through-review.md`) showed that lane +`identity-provisioner-groups` cannot exist as declared: the provisioner's only +HTTP credential is user-engine's runtime token, and its NetworkPolicy admits +only user-engine pods, so a caller-proxy lane would act as user-engine. Taken +option 1 (ops-warden's recommendation): `ensure-groups` and its rollback are +attended, in the `register-client` session on `key-cape-oidc-login`, run inside +the provisioner pod; no token is read. Option 2 (an operator credential of its +own on the provisioner) stays open for a later version. `warden case +check-type` now reports only `keycape-client-secret-generate` +(railiance-platform) as missing. 89 tests pass. + +Hub decision `e418263e` was still open, so memo r4 +(`nk-wp-0048-t05-oidc-browser-relying-party-r4`, packet `sha256:452ea421…`) +replaces r3; it passes informed-decision's and the dispatcher's validation. The +founder queues it with the same command, returns r2/r3 if still open with a +comment that r4 supersedes them, then answers r4. + +Prerequisites for T06 are now: lane `keycape-client-secret-generate` +(railiance-platform), and a founder-approved rollout of an identity-provisioner +image containing 630ebfd. + +2026-10-09: done. The founder queued r4 (`pending`, `queued`), returned r3 as +superseded and accepted r4 in informed-decision. informed-decision's hub +write-back is off in production (`INFD_HUB_WRITEBACK` unset), so net-kingdom +resolved hub decision `e418263e` from the founder's report, naming memo r4 in +the rationale. The type is `acknowledged`; it becomes `implemented` with T06. + +```task +id: NK-WP-0048-T06 +status: wait +depends_on: [NK-WP-0048-T05, NK-WP-0048-T08, NK-WP-0048-T09, NK-WP-0048-T12, NK-WP-0048-T13, NK-WP-0048-T14, NK-WP-0048-T15, NK-WP-0048-T16, NK-WP-0048-T17, NK-WP-0048-T18, NK-WP-0048-T20, MASON-WP-0010-T03, WARDEN-WP-0044-T03] +blocking_reason: "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status." +priority: high +state_hub_task_id: "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" +``` + +### T06 — First KnownSecurityCase: Forgejo + +Write `security-cases/forgejo-netkingdom-oidc.yaml` from the parameters the +founder accepted in KEY-WP-0038-T01 (`key-cape/docs/forgejo-oidc-client-request.yaml`), +so that acceptance carries over as the case acceptance. ops-mason materializes it; +ops-warden follows through and verifies it. The type becomes `implemented` once +this reference case is live. + +This replaces the hand-built path proposed for KEY-WP-0038-T02. + +2026-10-09: case written, `security-cases/forgejo-netkingdom-oidc.yaml`, status +`accepted`: the example's parameters plus the founder's 2026-10-08 acceptance of +the key-cape request (owner and founder), with the custody-path derivation +covered by the type's acknowledgment (memo r4). It validates and renders (plan +`execution.permitted: false`; three attended steps on `key-cape-oidc-login`); +it needs `--allow-unimplemented` until it is live, since it is the case that +makes the type `implemented`. A test keeps it equal to the type's example. +Still open before it runs: ops-mason renders and builds it (MASON-WP-0010-T02, +T03, unblocked by T05), ops-warden plans and runs it (WARDEN-WP-0044-T02, T03), +railiance-platform's `keycape-client-secret-generate` lane (asked by ops-mason +`789feb39` and ops-warden `d9f889b1`, both unread), and a founder-approved +identity-provisioner rollout containing 630ebfd. + +2026-10-09: identity-provisioner rolled out, founder-approved in session: +`main-630ebfd` (`sha256:bafb4860…`, built by the image workflow) replaces +`main-6c4fcaf` (`sha256:ffacd5d7…`; only change: 630ebfd, additive). Applied +with `kubectl set image` on the Deployment only (generation 17 → 18, 1/1 Ready); +the file's Service, NetworkPolicies and Ingress were not reapplied. Verified in +the pod: `ensure_service_groups` and `revoke_service_groups` present, `/readyz` +200; no token used, no group changed. Rollback: `kubectl -n sso set image +deploy/identity-provisioner provisioner=forgejo.coulomb.social/coulomb/identity-provisioner@sha256:ffacd5d790a5b7800e0535f3aa95b7de52c7a68e4f575b9192c3e1622fa672c2`. +The pin in `sso-mfa/k8s/identity-provisioner/deployment.yaml` follows. + +2026-10-09: ops-warden closed WARDEN-WP-0044-T01 and shipped `warden case plan` +(T02, message `06303d1d`); the accepted Forgejo case is refused until ops-mason +materializes it, and a materialized copy plans cleanly once +`keycape-client-secret-generate` exists. Its observation that `bind-env` carries +a literal `KEYCAPE__CLIENT_SECRET`: the validator now renders a +`{param:env}` format spec (upper case, `-` as `_`, as `keycape-client.py` +derives the name) and rejects unknown specs, and follow-through and rollback +text is now checked for unknown placeholders too (patch-level, canon section 6). +The type keeps the literal until ops-warden's planner (`text.format`) renders the +spec as well; asked of ops-warden. 53 tests pass. + +2026-10-10: ops-warden rendered `{param:env}` in `warden case plan` +(23ba584, message `c1df14b7`; other specs and `!conversion` refused). The type +adopts it as a 1.0 patch (canon section 6): `bind-env` and its rollback now read +`KEYCAPE_{client_id:env}_CLIENT_SECRET`. ops-mason reports the Forgejo case +rendered and built (message `1ebddae9`; CCR drafts handed to railiance-platform), +so T06 now waits on the platform apply, the mint-secret lane and the attended +register-client session. + +2026-10-10: key-cape adopted the type's custody path (key-cape ab5cccd, message +`88a4559c`): `docs/forgejo-oidc-client-request.yaml` and KEY-WP-0038-T02 name +`platform/workloads/keycape/forgejo`, field `CLIENT_SECRET`, delivered as +`sso/keycape-forgejo-client` through `external-secrets-keycape-forgejo`, and the +`forgejo/netkingdom-oidc-client` proposal is withdrawn. This matches the type and +the Forgejo example; no change needed here. + +2026-10-10: key-cape's type-composition review (key-cape 6038483, message +`8e5ed5f6`) found the composition sound with two blocking findings, taken as a +1.0 patch (stricter validation and documentation, canon section 6): +R1, redirect URIs now refuse `?` and `#` in the type pattern and in +`keycape-client.py`'s `REDIRECT` (KeyCape refuses `?` at startup and the single +replica would stop); R2, `allowed_scopes` always includes `groups`, the renderer +refuses `access_groups` without it, and `access_groups` now says the consumer +enforces the gate because KeyCape has none; R3, `mfa_required` "false" means the +per-user privacyIDEA policy; R4, register-client's justification records the +restart for every client, the quiet window, and that the rendered registry must +pass KeyCape's client validation before it. The accepted Forgejo case and both +examples still validate; 116 tests pass. +Open, needs a decision: R2's verification step that the consumer enforces the +group gate is a new verification step, hence 1.1 (minor), and the validator pins +cases to the exact type version, so the accepted 1.0 Forgejo case would need +re-pinning. Also open with key-cape: a KeyCape entry point that validates a +rendered client registry without starting the server; until then +`keycape-client.py` mirrors the redirect rule. + +2026-10-10: founder go for 1.1, so no 1.0 run overlaps. The type adds +verification `consumer-enforces-groups`, and hand-over now requires the consumer +to enforce `access_groups` (minor, canon section 6; risk class unchanged, no new +acknowledgment; history in the type README). The accepted Forgejo case and both +examples are re-pinned to `oidc-browser-relying-party@1.1` before any apply; the +acknowledgment intake keeps `@1.0` as the record of what was acknowledged. +ops-mason's build and ops-warden's case-ref digest predate this and need a +re-render. + +2026-10-10: ops-mason re-rendered the Forgejo case from `@1.1` (message +`22c6bba6`; plan digest `af1f4f81…`, no collisions) and rebuilt the bundle. The +rendered OpenBao objects are identical to 1.0, so the already applied +CCR-2026-0041 stands; 1.1 changes only consumer-side steps. key-cape shipped +`keycape check-config` (c528daa, message `a7b0aeff`): KeyCape's startup checks +without secret values, including the new duplicate-clientId rule. +`keycape-client.py --check-config ` now runs it on the live and the +proposed registry, fed on stdin, and apply refuses a proposed registry that +fails; register-client's entry point names the option (1.1 patch). The deployed +image predates c528daa, so the binary is built from key-cape source for now. +Before the next KeyCape rollout (Forgejo register-client or the CCR-2026-0018 +window), the attended operator runs `inspect --check-config` so the live +registry is checked too. + +```task +id: NK-WP-0048-T07 +status: wait +depends_on: [NK-WP-0048-T06] +blocking_reason: "The next types follow once the first one is proven end to end." +priority: medium +state_hub_task_id: "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7" +``` + +### T07 — Queue the next types + +From the T02 ranking, open the next type drafts (likely the confidential service +client and the workload KV read lane), each to be acknowledged on its own. +Record which existing hand-built instances get adopted as cases, without +rewriting them in place. + +```task +id: NK-WP-0048-T08 +status: done +priority: high +state_hub_task_id: "8e519082-dc8b-5e46-b660-03d03e55dcf6" +``` + +### T08 — identity-provisioner: ensure service-access groups + +The type's `ensure-groups` step calls `identity-provisioner groups ensure --groups + --members `. The provisioner already has idempotent +`_ensure_group` and `_add_group`, but exposes only tenant-scoped operations. Add +the general operation (create if absent, add the listed memberships, never +remove others), its rollback (remove only this case's memberships), tests, and +the ops-warden route for its service identity. + +2026-10-08: done. `identity-provisioner/provisioner.py` gained +`ensure_service_groups` (`POST /v1/groups/ensure`) and `revoke_service_groups` +(`POST /v1/groups/revoke`), behind the existing service-token check. Names +matching `tenant*`, `netkingdom-*` and `lldap_*` are reserved; members must +already exist; everything is validated before any change; memberships are read +back before success; nothing else is ever removed; groups are deleted only when +asked and empty. 7 new tests (39 in the provisioner suite). The type's +`ensure-groups` step and its rollback now name these endpoints. The ops-warden +route for the provisioner's service token belongs to WARDEN-WP-0044-T01. + +```task +id: NK-WP-0048-T09 +status: done +priority: high +state_hub_task_id: "0ee12dfc-8260-5038-bd39-cc7fb4113be0" +``` + +### T09 — The `keycape-client` entry point and its declaration + +Planned first as key-cape KEY-WP-0038-T04. Moved here because the machinery a +live registration needs already lives in net-kingdom, where KeyCape is deployed +(`sso-mfa/k8s/keycape/`): the contained kubectl, the cluster-identity pin and the +issuer pin. A copy in key-cape would drift. + +2026-10-08: done. `sso-mfa/k8s/keycape/keycape-client.py` derives the KeyCape +client record from a case (`client-from-case`). It adds or removes exactly one +client with the existing safeguards (byte-preserving merge, exact match or +refuse, shared-reference refusal, compare-and-swap, read-back, contained errors), +requires the Deployment env binding first for confidential clients, and requires +the case acceptance record for `apply` instead of a per-client founder decision +id. 15 tests, including a regression showing that the vergabe case reproduces +its hand-built registration exactly; all 71 helper tests pass. Declaration +`capabilities/playbooks/net-kingdom.keycape-client-registration.yaml` (contract +PASS, `maturity: implemented`) replaces key-cape's draft. The type now pins it, +and for confidential clients adds `deliver-secret` (delivery from custody into +the sso namespace) and `bind-env` (Deployment env), attended in the same session +as `register-client`. The per-client helpers stay until their clients are +adopted as cases. + +2026-10-10 — NetKingdom owner review of the execution seam, source only: +ops-warden/docs/security-case-execution-proposal.md SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was reviewed against INTENT, SecurityCase canon, acknowledged type1.1, current +case and owner source. docs/security-case-execution-owner-review.md supports +the closed adapter/receipt direction with explicit versioned schema, structural +materialization provenance, unchanged caller/session boundaries, safe CAS +reconciliation and exact-created-object rollback requirements. This is owner +review, not approval. Supporting file digests are in the adjacent sources JSON. + +Current source exposes a circular wait: ops-mason T03 waits on mint while Warden +and the broker refuse a non-materialized case. Structural acceptance therefore +needs its own native pinned receipt before mint, not a successful mint before +materialized. The platform broker additionally pins type1.0 while Forgejo is +accepted at1.1; platform-owner alignment and tests are required, not an unchecked +version bypass. T06 remains wait. No case/type lifecycle status, grant, authority +or live runtime changed by this review. A proposed informed-decision would +adopt the source contract only; native materialization and execution acceptance +stay distinct. + +Final proposal re-review: staged Warden proposal SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +adds only the explicitly local reducer appendix, not an accepted wire protocol +or executor. The exact-final review and sources JSON supersede the earlier +1035f049... proposal review. Platform staged the exact type1.1 broker correction +with ten fake-backend subprocess checks (script SHA256 +9576cd211cfc06526ec4cc9bd0a5b06f0f59287a68931bb2d6e3592a951ddc7d). +The next source work is the versioned closed adapter/receipt/materialization +contract, not another relaxation of the broker's status gate. Native work still +requires installed broker provenance, structural reader receipts, exact new +materialized-case bytes, caller grant/session revocation and verified execution. +No installed/native acceptance is inferred from that staged correction. + +2026-10-11 — new source-only type1.2 architecture acknowledgment: +Confirmed native founder ACCEPT of infd-20261011-forgejo-type12-source-r1@1 +at06:47:39Z, approval8e9332c8-754a-420c-b8b7-445cb4fe6765. Exact frozen packet +0b377d647ca748cd03c92f9792396412048055d4bde9f7bb06ff6d413f7acd68 and submission +585cee731de30d2b37883a57abfd9eb32f023ee694e8d911989109d5caf80569; native receipt +retained in docs/evidence/2026-10-11-forgejo-type12-source-acknowledgment/. +The complete acknowledged1.2 source package is isolated under +security-case-type-revisions/1.2/oidc-browser-relying-party; default loader and +current accepted1.1 case remain untouched because load_type is single-version. +The new proposed1.2 case is NOT accepted/materialized/live and has no acceptance +fields. Actual reference-migration design approval is a separate Apps owner +receipt; no1.1 approval is inherited. Source-recording review/test proof and exact +next gates are in docs/forgejo-type12-source-acknowledgment-followthrough.md. +T06 remains wait and T07 remains its existing dependent task; type is NOT +implemented and no first-case/native grant or broader workplan finish is asserted. + +2026-10-11 — bounded explicit version selection source preparation for T06: +The validator now offers optional exact version_roots/--version-root VERSION=PATH +selection with duplicate/mismatch/traversal/symlink refusal and no fallback. The +default1.1 source and historicalacceptedcase hashes/ordinaryrefusal/preparation +render stayunchanged; actual1.1 is acknowledged, not implemented. Acknowledged1.2 +ordinaryadmission also refuses; optionalpreparation remains nonexecuting.43local +validatorchecks pass and original1.1 CLIrenderbytes match exactly. Makerproof: +docs/evidence/2026-10-11-explicit-type-version-selection/source-integration-validation.json. +FormalNetownerreview/reference-source integration and all existing native adapter, +provenance/broker/materialization/attendedproof gates remain; T06 stayswait, no +new residual or firstcase/typeimplementation/native grant is inferred. + + +2026-10-16 — isolated source preparation for T06 (canonical integration pending): +Added `check-evidence` to the existing validator CLI. It reuses exact version +selection and ordinary type/case/render admission, checks actual Mason structural +request source/inventory/projection pins and reads actual Warden journal rows +read-only. Successful rows require exact type-declared descriptors and attempt/ +revocation fingerprints. Existing acknowledged types without machine descriptors +remain plan-only. The compatibility result never claims authenticated native +completion: Request and journal fingerprints are not portable native evidence. +Owner authentication/status binding and actual native gates remain unresolved; +T06/T07 statuses and both type/case lifecycles remain unchanged. No dispatch, +credential access or authority expansion is introduced. See tool README for the +existing owner protocol boundaries and source integration tests. + +Concrete next Warden owner integration: current `Executor.admit` refuses every +case parameter whose sensitivity is not public/operational, so browser +`redirect_uris`, `allowed_scopes`, `client_type`, `mfa_required` (and 1.2 +`consumer_namespace`) fail after otherwise prepared admission. The sensitive +classification is a security-change review level, not secret content. The +Warden owner must validate through NetKingdom's existing exact-version case/type +validator, then project only descriptor-declared safe target metadata; do not +remove the credential/value refusal or copy complete parameters into journals. +Actual owner descriptor acknowledgment and independent evidence admission remain +prerequisites, with normal admission still refusing unimplemented types. + + +## T10 — Source compatibility checker for Mason and Warden evidence + +```task +id: NK-WP-0048-T10 +status: done +priority: high +state_hub_task_id: "4b06ca97-c1e4-5680-bf22-ab75ab1646a4" +``` + +Completed 2026-10-16 in published source `c31d21ba`. The read-only +`check-evidence` command checks the actual Mason structural request and Warden +journal source protocols against an exact version/render. Independent source +review passed 103 tests at that checkpoint. It always reports native completion +false and execution prohibited. Fixtures and local journal fingerprints do not +provide authenticated owner/native evidence. This records completed source scope +previously described under T06; it does not duplicate native acceptance credit. + +## T11 — Validate the optional machine-descriptor pin contract + +```task +id: NK-WP-0048-T11 +status: done +priority: high +state_hub_task_id: "ef2a0119-f59e-5e45-9519-115da9df7b85" +``` + +Completed 2026-10-16. Reproduced the JSON schema rejecting a valid +`execution_descriptors` map while the Python validator accepted malformed maps. +The schema now supports the existing Warden optional exact-byte pin map; semantic +validation binds keys to declared steps, bounds the map and checks digest shape. +The direct evidence API refuses malformed maps before accessing them. Twenty-one +new checks and all 127 affected source checks passed. No current type/case bytes, +status, owner descriptors or native acceptance changed. Source evidence and +publication receipt: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`. + +## T12 — Prepare NetKingdom-owned attended step adapters + +```task +id: NK-WP-0048-T12 +status: todo +priority: high +state_hub_task_id: "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" +``` + +Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env, +config verification and restart/readiness steps around the existing helpers. +Next action: identify exact current entry points and their revision/readback +contracts, then implement fixed source-pinned callbacks or receipt-only attended +steps. Completion requires positive/refusal checks, exact target/source pins and +uncertainty behavior without acquiring user-engine runtime identity or exposing +registry/client secrets. Existing caller/session, cluster/issuer, CAS/readback +and status-only output boundaries remain mandatory. + +2026-10-16 task refinement: the original broad adapter scope is split into T13 +(completed registration source candidate), T14 (group creation provenance), T15 +(durable registration recovery) and T16 (descriptor/artifact selection and owner +review). This preserves T12's task ID and Hub UUID while giving each remaining +outcome an owner and completion gate. T06 now names every source dependency; +its native/reference lifecycle acceptance scope remains unchanged. Completion +of any source task supplies no Warden, Platform or Apps assent and no native +execution authority. + +## T13 — Prepare the fixed registration adapter source candidate + +```task +id: NK-WP-0048-T13 +status: done +priority: high +state_hub_task_id: "b264c10b-565c-5d29-b788-03cf5fbd2883" +``` + +Completed 2026-10-16, source implementation and synthetic fixture proof only. +Added an injection-only adapter around actual `keycape-client.run`: fixed +case/principal/human/lane/source/acceptance binding, existing CAS/readback, +metadata-only owner evidence and per-attempt parent-session revocation boundary. +The descriptor source composition pins the exact checker executable digest +alongside adapter/helper source. Creation-only rollback authenticates its +creation receipt and refuses changed postcreation UID/resourceVersion; +pre-existing clients confer no rollback authority. Missing readback, evidence, +revocation or process-local creation provenance cannot become retry-safe success. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/` +R3 candidate, explicit patch and postimages, retained R1 rejected custody finding +and R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks +use actual Warden kernel admission/dispatch/rollback with published Warden +`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust, +descriptor/type/case and Kubernetes objects remain synthetic fixtures. This +records a completed source deliverable, not independent review, publication, +type acknowledgment, exercised procedure, identity admission or native browser +acceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority. + +Source publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently +reviewed by the Custodian coordinator before integration. The required initial +projection and ordinary UUID writeback both completed with zero failures and +zero automation errors. Generated-only brief commits were refused by the normal +hook; exact pre-existing staged/working brief bytes were restored. The separate +canonical integration receipt retains those refusals and projection evidence. +Native admission and all other-owner acceptance remain open. + +## T14 — Admit governed group rollback against native owner boundaries + +```task +id: NK-WP-0048-T14 +status: progress +depends_on: [NK-WP-0048-T20, NK-WP-0048-T21] +priority: high +state_hub_task_id: "fce95440-a25b-5f27-8ef3-a3d182099113" +``` + +Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to +an admitted dedicated CNPG consumer through the existing `rapp-postgres` +database-per-consumer contract. Identify the actual deployment/package and +custody owner, admitted placement/capacity and role/renewable lease/TLS target; +implement the independently retained exact content checkpoint and authoritative +tenant/case/source/principal callbacks before enabling governed group operations. +The injection-only backend and fixture callbacks do not supply production scope +or authorization. Existing service tokens, caller-supplied JSON and provenance +receipts confer no new grant. The default production endpoint remains disabled +without the trusted integration. SQLite remains local/disposable only; any +production exception requires explicit scoped governance under ADR-0015. + +2026-10-17 owner-integration refinement: T23 records the completed source/package +placement review, distinct from T21's source/disposable-SQL proof. Its dedicated +`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted +package or existing repository. The full six-consumer rapp-postgres renderer +accepts the inert candidate on platform-pg-2 as its third of four declared +consumers; first-cell placement is refused as a fifth. Neither the estimated +four-consumer ceiling nor source requests establish native headroom or backup. + +Next independent owner contracts: NetKingdom/package/substrate owners accept +exact deployment/rollback fate; rapp-postgres admits the dedicated consumer, +roles, five-connection budget and actual backup/restore; Platform admits exact +renewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL, +broker and checkpoint paths without assuming a narrow policy subtracts additive +same-namespace access. Protected caller owners must provide authoritative +GroupJournalBinding construction and current authorization; an independent +existing holder must authenticate and atomically advance the exact content +checkpoint, not a counter or ordinary Audit receipt. Directory/runtime owners +must select and prove either an exclusive all-writer maintenance barrier or +provider-supported CAS/generation, including established sessions, lifecycle +cascades and remove/readd ABA. No source review establishes those owner facts. + +The default PostgreSQL activation and destructive native rollback remain refused. +Storage/lease acceptance can be proposed separately from LDAP mutation only after +exact owner source/target/custody/verification/recovery and cost/risk review; +State Hub guard approval grants none of it. Source-only integration review and +native acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's +existing status, UUID, dependencies and native completion gate are retained. + +2026-10-17 refinement: T20 retains the historical durable SQLite source and +actual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and +bounded real disposable SQL compatibility proof. T14 remains `progress` for +native CNPG admission/recovery, protected owner integration and exclusion of all +external LDAP writers. Separate source publication/projection and independent +cleanup receipts belong to coordinator follow-through; no missing native assent +is represented as received. + +Completion still requires independently reviewed/admitted ADR-0015-conforming +CNPG storage (or an explicit governed exception), backup/lost-store recovery and +migration of legacy requests; a native +writer-isolation/fencing or provider CAS/version contract that closes external +writer races and remove/re-add ABA; exact case/source/principal and current +native object bindings for protected callbacks; and actual per-attempt evidence, +revocation and readback/refusal verification through the existing owner paths. +Only independently retained confirmed creations may be undone. Every +pre-existing/shared group and membership must remain preserved; unresolved +intents never expire into retry authority. The disposable fixture supplies none +of these native custody, authorization, owner-acceptance or production claims. + +## T15 — Define authenticated recovery for lost registration process state + +```task +id: NK-WP-0048-T15 +status: wait +depends_on: [NK-WP-0048-T31] +blocking_reason: "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source." +priority: high +state_hub_task_id: "1b825705-4848-5edc-832f-b1622cd9bdb7" +``` + +2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional +paired `holder`/`observe` capabilities and installs `reconcile_original`; the T31 +fixtures now run through the adapter's own path. Rollback after restart is still +refused, and there is no add replay. This changes `keycape-registration-adapter.py`, +so the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json` +is superseded. The new revision has no independent review yet and must not be +pinned on the strength of the old one. + +2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner +selects the real authenticated original-attempt holder, and the T13 adapter +gains the holder capability, `reconcile_original` and reconcile-aware `verify` +described in T31. + +Owner: NetKingdom for registration state, with Warden owning kernel evidence +admission. T22 records the completed source review and exact existing-seam gaps; +T15 retains implementation and authenticated restart/lost-receipt acceptance. +Next action: select and admit the actual owner publisher/authenticator and bounded +original-attempt readback contract for the merged holder/observe adapter seam. +Warden's original-intent persistence/interface (`a45cf475`) and additive validation +(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected. +The local Journal retains admitted nonsecret original context, validates original +actor/attempt and metadata linkage to the original running receipt, and supplies +it separately from the new reconciliation attempt. This local consistency is not +independent original-holder authorization or creation provenance; neither the +journal nor a current matching registry entry reconstructs creation authority. + +The owner contract must bind the original Invocation, case/type/descriptor/catalog/ +acceptance pins, source, principal and admitted tenant/realm provenance, separately +from the new reconciliation attempt and its independently revoked parent session. +Use an existing authenticated holder/readback; Audit Core's custody API is a +candidate only until its actual registration source/class, load-bearing emission +contract and separate scoped reader are admitted. No new broker or receipt trust +root is proposed. A missing original record, ambiguous publication, NO_CHANGE, +changed native revision or lost readback stays uncertain; no add replay or undo. + +Completion requires restart/lost-receipt fixtures against the selected actual owner +contract: independently authenticated original created-only provenance, original +exact client entry and unchanged current postcreation UID/resourceVersion before +any undo, with wrong attempt/case/source/principal and unavailable-holder refusals. +The merged T13 adapter exposes reconciliation only through paired injected +holder/observe capabilities and still refuses post-restart rollback. Existing +source review, JSON receipts and synthetic trust do not satisfy independent +holder authorization/provenance or native admission, or authorize attended recovery. +Exact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json. + +2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface +is not published — ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03 +commit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt +fixture or readback seam was added this session; the work is deferred behind +T16's decomposition under the same token budget. Remains todo, gated on Warden +publishing the reviewed lookup/context interface and on owner selection of the +authenticated original-attempt holder. + +2026-10-17 current-source correction: the earlier same-day `d97652a` lookup +checkpoint and T31 adapter-next-action note above are historical and superseded +by the published Warden interface/validation and merged `061461e` adapter seam. +The 54 focused Warden checks establish source behavior only. Current admission +leaves tenant/realm empty; no admitted tenant/realm provenance is inferred. +T15 remains wait for actual independently authenticated owner-holder/readback +selection, independent review of the changed adapter, and native restart/lost- +receipt acceptance. Existing task metadata, owner gates and historical reviews +are unchanged. + +## T16 — Select and review the actual machine descriptor composition + +```task +id: NK-WP-0048-T16 +status: todo +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29, NK-WP-0048-T30] +priority: high +state_hub_task_id: "200bf500-5084-5ae7-be26-3033fe1f296e" +``` + +2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no +findings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose +option A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`): +1.3 with execution descriptors for all seven steps. Owner reviews for A are open +in the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom, +railiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet +supersedes the r1 composition pointer: T16's exact composition is now +`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`. + +2026-10-17 successor candidate prepared (source only, `proposed`): +`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus +an `execution_descriptors` map of the seven composed descriptors, with examples +pinned to `@1.3` and no acknowledgment. ensure-groups, hand-over, +deliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c). +`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type: +the proposed type is refused; an acknowledged copy is refused because ops-warden +pins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to +1.2's fingerprint) all seven are admitted. This is a major change under canon +section 6. Next: r4 source review of the changed adapters and the map, then owner +reviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human +question and the owner-run steps question, then the founder's Decision Memo. + +Owner: NetKingdom for descriptor/type source and exact checker artifact; Warden +independently owns global provenance/admission. T22 records the completed seven-step +source1.2 inventory and regenerated register-client descriptor using the delivered +checker and current sealed-snapshot source; T16 retains exact owner review, +complete descriptor composition and admission. Next action: bind actual native +publisher/authenticator, original/current session verification and revocation, +executable storage/custody and config pins, then obtain the six missing fixed +owner descriptors/adapters for ensure-groups, mint-secret, deliver-secret, +bind-env, deliver-consumer-secret and hand-over. The inventory identifies each +existing owner seam, exact missing contract and created-only rollback requirement; +it is not an executable bundle. T14 and T15 remain independent prerequisites. + +Review the dedicated consumer source1.2 composition only through the existing +explicit version-root selection. Keep canonical1.1/default and its accepted case +unchanged, and the source-only acknowledged1.2 reference case proposed without +acceptance fields. New descriptor bytes require a reviewed exact successor +revision/version under canon section6; they do not inherit historical source +acknowledgment. Changed authentication/materialization/trust needs the prescribed +informed-decision acknowledgment. Do not insert a partial descriptor map or use +--allow-unimplemented preparation as ordinary case/native admission. The registry +helper reports restart_required; separately review quiet-window restart/readiness +and actual consumer verification rather than infer activation from registry CAS. +Exact composition: Custodian native-registration-r1/descriptor-composition.json. +Completion requires separate owner source review of exact machine descriptors, +artifact/source/config pins, normal type/version admission and independent +evidence-verification boundaries. Warden d97652a refreshes producer validator +pins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any +machine descriptor into an acknowledged type is a separately reviewed source +change. Platform owns structural/mint/broker/delivery evidence; Apps owns actual +consumer login/group enforcement. Neither their assent nor native acceptance is +supplied by this task. + +2026-10-16 source checkpoint: T19 below extracts the completed per-attempt +executable snapshot implementation and reviewed fixture proof. T16 retains +native executable storage/custody, exact owner evidence/session callbacks, +complete descriptor composition and ordinary type/version admission. The +successor descriptor/source pins are proposed only; the earlier descriptor +history is preserved. T14 group provenance and T15 durable restart/recovery +remain independent open requirements. No native acceptance is counted here. + +2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The +T13 registration adapter alone is 217 source and 317 test lines plus an +independent review; six further owner descriptors/adapters cannot be completed +within one per-task token budget (soft 8000 / hard 10000), so the task was +stopped below 50% and is decomposed rather than pushed through. Proposed split, +one source-candidate task each with positive/refusal fixtures, canonical1.1 +default unchanged and no --allow-unimplemented admission: (a) ensure-groups, +bound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret, +Platform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret; +(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition +over the reviewed set. Each candidate needs its owner's source review before +pinning; none is authored here as owner-accepted. Status stays todo. + +## T17 — Build and exercise the exact offline config-checker artifact + +```task +id: NK-WP-0048-T17 +status: done +priority: high +state_hub_task_id: "8c3e4baa-a218-5996-af9e-fe1153a46dac" +``` + +Completed 2026-10-16, isolated artifact/source fixture proof only. Built the +existing `key-cape/src/cmd/keycape` command from published clean source revision +`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree +`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0, +CGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline +read-only module selection. The full retained closure pins 108 source files, +9876 toolchain files and ten used module source directories. Three isolated +build locations produced identical 13633409-byte artifacts: +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. + +Eight disposable checks use the actual executable and maintained helper/kernel, +with complete synthetic configs and a minimal environment: add/created-only +removal, normal pinned producer admission, rollback, lost-readback uncertainty, +stale revisions, substituted checker refusal, invalid startup config and missing +secret-env refusal. No checker stub or producer-pin override remains. The +artifact-bound descriptor proposal is +`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`. +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`. +The first two build attempts succeeded but their closure collection failed; +both failures are retained, as is refusal of the older incomplete synthetic +fixture. No production config, credentials or cluster mutation was used. + +This extracts completed build/fixture scope from T16 without claiming KeyCape +owner endorsement, native checker selection/storage custody, acknowledged type +integration, native evidence/session callbacks or attended browser acceptance. +T16 retains exact owner review and composition/admission scope. The attended +registration procedure remains unexercised. + +## T18 — Make checker input and executable selection deterministic + +```task +id: NK-WP-0048-T18 +status: done +priority: high +state_hub_task_id: "9c45da71-fb4c-5f20-9065-90f448f37c64" +``` + +Completed 2026-10-16, isolated source correction and real-artifact fixture proof +only; independent integration/publication remain separately recorded. KeyCape +source review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and +`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper +inherited ambient environment, allowing valid supplied configuration to be +rejected or invalid supplied URL configuration to be admitted. A bare relative +checker filename could also be replaced by PATH lookup after the adapter had +hashed the selected local file. All three counterexamples are retained against +the unchanged predecessor. + +The existing helper now resolves the selected checker file to an absolute path +and passes an explicit empty subprocess environment. Three opt-in real-checker +regressions verify both URL-verdict directions and relative-file selection under +a non-checker PATH decoy. All 39 affected checks pass with the exact supplied +artifact, as do 11 real-artifact helper/kernel scenarios. Source evidence: +Custodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`. +Historical descriptors remain retained; the helper correction changes successor +source-composition pins and requires their separate review/admission. An absolute +path is not immutable custody and does not eliminate file replacement races; +T16 retains native executable storage/custody and callback/type admission. +No production configuration, credentials or cluster mutation was used. + +Source integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent +Custodian source review in browser-checker-environment-r2/coordinator-review.json. +The ordinary protected projection pass assigned T17/T18 UUIDs with zero failures +and zero automation errors; generated-only brief commit refusal is retained. +The exact prior staged/working brief bytes were restored. Final source/publication/ +projection evidence is retained in Custodian browser-checker-environment-r2/ +canonical-integration.json. These source receipts do not admit native executable +custody, owner callbacks, acknowledged type changes or browser identities. + +## T19 — Bind checker execution to verified immutable attempt bytes + +```task +id: NK-WP-0048-T19 +status: done +priority: high +state_hub_task_id: "f50fdcd1-00be-5c3c-be0c-345aa2126032" +``` + +Completed 2026-10-16, independently reviewed source implementation and synthetic +fixture proof only. The registration adapter now opens a bounded regular checker +artifact without following its terminal symlink, verifies the reviewed digest, +and snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux +memfd before helper access. Live and proposed config checks execute that same fd +through procfs with the existing empty environment; the fd closes on completion +or failure. Unsupported snapshot/seal/exec policy refuses through the existing +parent-session revocation and uncertainty boundaries. The earlier unbounded +pre-snapshot path read was removed during independent review; actual adapter +symlink/FIFO/oversize refusal tests verify helper access never starts. + +Evidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`, +including exact base/preimages/postimages/patch, proposed descriptor, coordinator +source review and canonical integration receipt. Independent Custodian checks +passed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios; +the sets overlap and are not separate acceptance counts. Existing published +Warden d97652a producer pins remain unchanged. The exact KeyCape artifact remains +`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`. +The predecessor path-replacement refusal and the first expanded reopened-fd test +failure are retained; the latter fixture was corrected to restore write mode +before independently proving seals block reopened-fd writes and shared mmap. + +This source task adds no native owner custody, callback, evidence trust, type +acknowledgment, credential access, consumer sign-in or rollback acceptance. +Executable memfds, Linux seals and usable procfs are explicit runtime dependencies +and still require native admission. This is an extracted source outcome from +T16, not completion of T16; T12/T14/T15 and all external-owner gates remain open. + + +Publication verified by the coordinator after the source worker usage limit: +`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision. +All five canonical source hashes match the reviewed packet. Normal synchronization +completed with zero failures/automation errors; all 19 task statuses match the +projection. T19's assigned UUID is retained. The original staged and working brief +bytes were restored and independently compared. The exact source, projection and +publication checks are in Custodian browser-resume-r1/canonical-integration.json. +Native acceptance remains open under T16 and the other named owner tasks. + + +## T20 — Prepare durable group provenance and prove disposable LLDAP compatibility + +```task +id: NK-WP-0048-T20 +status: done +priority: high +state_hub_task_id: "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" +``` + +Completed 2026-10-17, source candidate preparation and bounded synthetic +compatibility proof only. This extracts evidenced implementation/fixture scope +from T14; T14 retains native storage, writer isolation, protected callback and +owner-acceptance gates. Source integration/publication and State Hub projection +are pending the coordinator's separate receipts; no revision or task UUID is +invented here. + +The existing identity-provisioner group ensure/revoke interface now has a +reviewed candidate for a bounded owner-private SQLite journal, proven only in +local/disposable use. The initial storage instructions wrongly suggested a native +persistent-volume path; source review against accepted ADR-0015 corrected them +before publication. Production SQLite-on-a-PVC is not admitted: T14 retains +CNPG/rapp-postgres storage admission or a separately governed exception. +The local journal saves +complete prestate and intent before each attempted directory mutation, inventories +only confirmed new groups/memberships, binds original ensure idempotency and +correlation to the exact request digest, and refuses unsigned caller inventories +or legacy broad revoke requests. Replays never recreate removed state. Missing, +replaced or untrusted storage and missing intent rows stop execution; definite +before-mutation refusals retain evidence without stranding unrelated valid work. +Interrupted writes/readback remain unresolved across reopening. Revoke validates +all targeted native ids and complete member postimages before destructive work, +removes only confirmed creations and never deletes a preexisting empty group. +Server/configuration wiring, image-copy closure and owner storage/request +migration instructions are included. The existing endpoint's caller +authentication remains the existing boundary, not a new grant. + +The candidate was prepared against NetKingdom +`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest +`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`. +The independent Custodian review passed all 65 provisioner checks. The earlier +62-check candidate, two independent-review findings and subsequent fixes remain +retained rather than overwritten. Seven additional fixture-plan checks exercise +the preparation boundary; these counts overlap the same candidate and do not +measure task-completion percentage or independent acceptance. + +A separately reviewed bounded fixture then exercised the exact candidate against +cached pinned disposable LLDAP and Python images, through actual GraphQL. All 14 +closed compatibility/containment oracles passed: exact creation inventory, +separate-process SQLite reopening/replay/revoke, complete preexisting directory +preservation including empty/shared groups and memberships, refusal of shared +membership drift, retained uncertainty after a lost response following a real +addition, and refusal of unknown/cross-case receipt bindings. The fixture used +only newly generated synthetic users/passwords/key material in private tmpfs, +with a network-none anchor and shared loopback helper, no host ports/mounts, +no image pulls and no production or actual credential contact. The coordinator's +safe receipt reports both exact owned containers absent after cleanup; final +independent cleanup verification remains the coordinator's integration duty. + +Evidence: Custodian +`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact +source base/pre/postimages, patch/manifest, independent review and retained +refinements. Native fixture manifest +`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`; +retained safe result `native-fixture/execution-result.json` (original +`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies +no native owner assent, production rollout, protected session/evidence callbacks, +ordinary type admission or consumer login proof. LLDAP has no conditional +membership mutation or generation token here: exact readback cannot exclude +ABA or a concurrent external writer, so T14 remains open. + +2026-10-17 verified source follow-through: source commit `4f65e41` passed normal +local scanning and all 65 canonical provisioner tests. The actual disposable +LLDAP receipt passed all 14 oracles; independent verification on the pinned +daemon found both exact names and the run label absent. Source integration +includes the ADR-0015 documentation correction; the journal remains local-only. + +T14 source preparation is underway. Twenty-three pinned public sources identify +NetKingdom implementation/reconciliation, Platform custody/database governance +and rapp-postgres consumer provisioning; the inspected runtime binding does not +assign identity-provisioner a package. The existing User Engine runtime token +and pod route are not a protected-case caller lane. The native proposal preserves +the same journal seam on a dedicated CNPG consumer and requires authoritative +scope binding, admitted role/lease/placement/recovery and all LDAP writers fenced. +The declared exclusive maintenance window is not mechanical exclusion of WebUI, +helper or lifecycle writers. Capacity placement and EUR cost/risk remain owner +decisions. No package, database, credential, rollout or owner assent was created. +Custodian evidence: group-provenance-r1/native-storage-proposal-r1; final source, +projection and cleanup review: resume-integration-r1/publication-verification.json. + + +## T21 — Implement the PostgreSQL journal and prove disposable SQL compatibility + +```task +id: NK-WP-0048-T21 +status: done +depends_on: [NK-WP-0048-T20] +priority: high +state_hub_task_id: "afe78e4d-408a-516a-ab92-e212e8a2d11d" +``` + +Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL +compatibility only. T14 retains native storage, custody, callbacks, authorization, +all-writer isolation and production admission. Coordinator source publication and +projection remain separate follow-through; no source revision or task UUID is +invented here. T20 remains the historical local SQLite/actual LLDAP proof. + +The eight-path source candidate adds an injection-only PostgreSQL implementation +behind the existing owner journal seam, with explicit trusted tenant/directory/ +case/source/principal/epoch binding and independent continuity callbacks. It +persists bounded intent/created-only receipts, exact-one version updates, durable +active fences, and a content-bound journal chain. SQL commits precede independent +exact-marker acknowledgment; lost replies/checkpoints poison continuation and +retain uncertainty. A counter-only checkpoint is explicitly insufficient for +divergent clones. Migration does not create database/schema/credential, admit an +epoch or clear quarantine; production HTTP activation is absent pending trusted +callbacks. Pinned psycopg package/image-copy closure is included, without claiming +an image build or native dependency admission. + +Candidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch +`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`. +All 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two +source-backed snapshot regressions. Counts overlap the same source and fixture; +they are not additive native acceptance or completion percentages. + +The independently reviewed R5 fixture ran the actual SQL migration/backend and +existing provisioner path against cached pinned disposable PostgreSQL. Directory +operations deliberately used the existing synthetic LLDAP test double. All +manifest-declared phase oracles passed: creation-only inventory and DDL refusal; +separate-process replay/revoke with complete preexisting/shared visible-state +preservation; foreign-context refusal; concurrent exactly-once reservation and +stale version refusal; actual database fork with divergent bodies at the same +counter and fresh-object rejection of the losing exact checkpoint; retained +uncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/ +pg_restore rollback refusal against the independently retained fixture checkpoint. +The checkpoint is fixture-only, not an admitted native owner evidence holder. + +Only generated credentials/state were used, with two nonroot/read-only containers +on private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts, +no pulls and no production contact. The safe receipt reports both exact owned +containers absent, with no daemon uncertainty. Independent final pinned-daemon +cleanup verification remains coordinator follow-through. + +Failed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure +subphase is unknown; R3 exposed a real backend bug where replacing nested receipt +objects lost the created ID held by ensure, fixed by acknowledging only the backend +version after commit/checkpoint. R4 verified that fix through actual SQL bootstrap, +then exposed fixture snapshot aliasing and empty deleted-group backing entries; +source-backed regressions corrected the snapshot without weakening complete +visible-prestate equality, and reject nonempty orphan membership. These refinements +belong to one trial, not extra native acceptance successes. + +Custodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`; +exact source/pre/postimages and retained predecessors; native R5 manifest +`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`; +`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt +(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native +storage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected +callbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production +endpoint activation is claimed. Those remain live T14 work. + + +## T22 — Establish registration recovery source gaps and complete descriptor review inventory + +```task +id: NK-WP-0048-T22 +status: done +priority: high +state_hub_task_id: "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1" +``` + +Completed 2026-10-17: source-only owner-contract review and complete review +inventory. Inspected the actual registration adapter, Warden Journal/Executor, +NetKingdom evidence compatibility checker and Audit Core custody/read surfaces. +No existing admitted holder/readback supplies authenticated original-attempt +recovery: payload/creation are process-local, persisted evidence references are +hashed, and reconcile receives a new attempt without original context. The exact +four-owner contract requirements and non-replayable missing/ambiguous cases are +recorded; no recovery callback, publisher, credential grant or native trust root +was installed. T15 retains implementation and actual acceptance. + +The complete source1.2 inventory maps all seven follow-through steps to their +existing owners, missing exact contracts and created-only rollback requirements. +The actual maintained adapter regenerated the sole existing register-client +machine descriptor against the delivered 13,633,409-byte checker and current +sealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode +round-trip passed; six owner descriptors remain absent. Actual source commands +render canonical1.1 and explicitly selected1.2 with execution.permitted false; +ordinary1.2 case admission still refuses its acknowledged/unimplemented type. +Canonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were +added or rerun, no native configuration or effect occurred, and no owner assent +or complete machine bundle is claimed. T16 retains native executable custody, +complete exact descriptors, independent source review and normal version/type +admission. This extracted outcome supplies no native gate completion credit. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially +original-attempt-review.json, descriptor-composition.json, source-verification.json, +source-manifest.json and manifest.json. The prior sixteen-gate audit remains +immutable. Publication, normal task UUID allocation and projection are separate +coordinator follow-through; no Hub UUID is assigned by this source record. + + +## T23 — Review native journal package assignment and storage integration boundaries + +```task +id: NK-WP-0048-T23 +status: done +priority: high +state_hub_task_id: "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee" +``` + +Completed 2026-10-17: source/package placement and protected integration review +only, separate from T21's completed PostgreSQL source and disposable-SQL proof. +The owner review recommends a dedicated independently rolled-back +`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a +schema-valid draft declaration and no reef binding. No repository, registry +entry, native package assignment or owner assent was created. Existing +rapp-user-engine ownership/token delivery does not assign it the provisioner. + +The complete current rapp-postgres catalog has six declared consumers: four on +platform-pg and two on platform-pg-2. The inert renderer-valid provisioner +candidate fits platform-pg-2 at three of four, requesting one migration plus +four runtime connections; placing it on the first cell is refused as a fifth. +Normative package schema and full-catalog capacity/render checks passed. The +four-consumer ceiling is estimated, not measured native headroom, and no current +SQL session, backup or restore acceptance is claimed. + +The exact review identifies remaining package/substrate, SQL role/lease/TLS, +network and backup/recovery contracts; independently authenticated protected +scope callbacks; an external content-bound checkpoint holder with atomic +advance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation, +including lifecycle cascades and remove/readd ABA. Generic Audit custody is not +that checkpoint service. A database-only fence, final reread or one replica +cannot exclude foreign directory writers. The native acceptance/recovery matrix +keeps storage/lease admission separate from separately approved directory +mutation; the production activation remains disabled pending trusted integration. + +Evidence in the-custodian: +`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`, +source-validation.json and hash-bound source-references.json, plus the inert +proposed declaration/consumer and renderer outputs. No native resource, lease, +credential, deployment, destructive effect, independent holder or native owner +acceptance was delivered. T14 retains these live gates with its original UUID +and progress status. This completed review adds no duplicate T21 acceptance +credit; publication, task UUID allocation and projection remain coordinator +follow-through and no UUID is hand-assigned here. + +## T24 — Ensure-groups adapter source candidate + +```task +id: NK-WP-0048-T24 +status: done +priority: high +state_hub_task_id: "1a3931ee-3b5f-52fc-9295-4073804f4bd2" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom (directory) owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T25 — Mint-secret adapter source candidate + +```task +id: NK-WP-0048-T25 +status: done +priority: high +state_hub_task_id: "0de2a7d9-874d-578b-a076-f653fe42e30b" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T26 — Deliver-secret adapter source candidate + +```task +id: NK-WP-0048-T26 +status: done +priority: high +state_hub_task_id: "94b3cbf0-c908-5ac6-b0db-67385c3e021c" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T27 — Bind-env adapter source candidate + +```task +id: NK-WP-0048-T27 +status: done +priority: high +state_hub_task_id: "e56365f8-29b5-5c89-a329-8115f9f3b2a7" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. NetKingdom runtime owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T28 — Deliver-consumer-secret adapter source candidate + +```task +id: NK-WP-0048-T28 +status: done +priority: high +state_hub_task_id: "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Platform and Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T29 — Hand-over adapter source candidate + +```task +id: NK-WP-0048-T29 +status: done +priority: high +state_hub_task_id: "129db41d-4560-5e20-9f3b-c3c91e0a5601" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15. + +Split from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam. +Source candidate only, modelled on the T13 registration adapter +(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and +`registration-adapter.md`). Requires positive and refusal fixtures and +created-only rollback. Keeps canonical 1.1 as the default and uses no +`--allow-unimplemented` admission. Done means the candidate and its tests are +committed. Apps owner source review before pinning, and any native action, +stay separate gates and are not supplied by this task. + +## T30 — Compose the descriptor set over the reviewed candidates + +```task +id: NK-WP-0048-T30 +status: done +depends_on: [NK-WP-0048-T24, NK-WP-0048-T25, NK-WP-0048-T26, NK-WP-0048-T27, NK-WP-0048-T28, NK-WP-0048-T29] +priority: high +state_hub_task_id: "6f6073ac-52dd-5b70-8874-6d76d4c072e4" +``` + +2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`, +fd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent +reconstruction of 117 passing and no open findings. This is a source review only: +it grants no owner admission, pin or native authority. Owner review and admission +continue under T16, which also takes the r2 observations, including the +`warden_executes: false` steps that carry execution descriptors. + +2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`) +approved five files and rejected three, plus the artifact. The fixes are merged in +2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant +independently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and +derives its Secret and custody path, with no caller reference (640292c); R2-F3, +bind-env pins its five-file closure, and a new closure-completeness test checks +every adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is +regenerated, and the full suite including kernel admission passed 117. Next is +the r3 review. Open for owner review under T16: the type marks four steps +`warden_executes: false`, yet each has an execution descriptor. + +2026-10-17 founder confirmed every proposed lane and schema name for T25-T29 +as-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17), +and no step is still `owner_confirmation_required`. Next: independent source +review of the seven adapters. + +2026-10-17 founder decision (supersedes the `agent` decision below): the +mint-secret actor is `human`. Coordinator review r1 (the-custodian +`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`, +finding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every +`effect: mint` admission whose actor is not `human` ("agent mint is +forbidden"), so the `agent` descriptor could never be admitted. Adapter, tests +and composer comment updated and the artifact regenerated (mint-secret +descriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`); +98 tests pass against a45cf47. The set needs an independent r2 review. + +2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own +`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2 +type, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and +fixture-only trust (stand-in adapters, nothing dispatched). It also pins that +an agent mint descriptor is refused ("agent mint is forbidden") and that a +wrong lane is refused for every step. Against the old `agent` revision +(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47 +and on current ops-warden. Fixture admission is not owner admission or a pin. + +2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`. +The kernel is unchanged. The adapter, its test and the composer were updated and +the artifact regenerated; all seven steps pass `Descriptor.decode` and +`complete_executable_descriptor_set` is true (98 tests passed). The artifact +stays `not_an_admission`, with no owner review, admission or dispatch permission. + +2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged; +combined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an +explicit version root and refuses partial, duplicate, unknown or mismatched maps. +`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked +`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now +carry and honour `parent-session` revocation, and the hand-over owner is +parameterised (`railiance-apps` for Forgejo). Six of the seven steps pass +`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside +the kernel's human/agent set, and Platform (with ops-warden, if the kernel should +change) must decide. Five steps carry `owner_confirmation_required` names. The +set is complete only after both. Owner review, the canon section 6 successor +revision and admission remain T16's. + +2026-10-17 prerequisites merged (65559ef; combined suite 78 passed): +ensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with +1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its +kernel-level fixture tests like T13's are still missing, and its `build()` +signature changed. Composition is unblocked in source. Owner confirmation of +the proposed step, lane and schema names remains before pinning. + +2026-10-17 inputs from T24–T29 that composition must resolve: T24 accepts +only 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path; +T27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names +proposed in T25, T26 and T29 need owner confirmation before pinning. + +Split from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set +through the existing explicit version-root selection over T13 and T24–T29. No +partial descriptor map, canonical 1.1 stays the default, and new descriptor bytes +need a reviewed successor revision under canon section 6. Owner review and +admission remain T16's. + +## T31 — Restart and lost-receipt fixtures against Warden's original-intent interface + +```task +id: NK-WP-0048-T31 +status: done +priority: high +state_hub_task_id: "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" +``` + +2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract. + +Split from T15 on 2026-10-17. ops-warden published the original-intent +lookup/context interface in `src/warden/case_run.py` (`OriginalIntent`, +`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`). +Build T15's restart/lost-receipt fixtures for the registration adapter against +it: the original intent is kept separate from the reconcile attempt; wrong +attempt/case/source/principal and unavailable-holder cases refuse; and a missing +or ambiguous record stays uncertain, with no add replay or undo. The interface +is Warden's own dispatch record, not owner-authenticated created-only provenance. +Selecting the actual authenticated holder stays a T15 owner gate. diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/commit-receipt.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/commit-receipt.json new file mode 100644 index 00000000..c7cc1317 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/commit-receipt.json @@ -0,0 +1,7 @@ +{ + "exit_code": 0, + "commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "base": "001a38ca2aa85155ce4840c4da22aca6e2346171", + "stdout": "[main 3236d50] docs: update published Warden original-context checkpoint\n 1 file changed, 23 insertions(+), 10 deletions(-)\n", + "stderr": "{\"blocked_blob_ids\": [], \"scanned_blobs\": 1, \"scanner_sha256\": \"88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509\", \"scanner_version\": \"8.30.1\", \"scope\": \"changed-staged-blobs\", \"status\": \"clean\"}\n" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/consistency.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/consistency.json new file mode 100644 index 00000000..c10ac7cd --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/consistency.json @@ -0,0 +1,331 @@ +{ + "repo_slug": "net-kingdom", + "repo_path": "/home/worsch/net-kingdom", + "source_revision": "3236d5000e52e0643f33906a26a7825aa0b67465", + "source_revision_reason": null, + "issues": [ + { + "severity": "WARN", + "check_id": "C-24", + "message": "Repo classification advisory: capability_tag 'security' is not a recommended family tag (allowed, check for synonym)", + "file_path": "", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-35", + "message": "Repo Manager warning: workplans/: repository uses more than one workplan prefix: ['NET-WP', 'NK-WP']", + "file_path": "workplans/", + "db_id": "", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0011' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0011-enterprise-federation-saml.md", + "db_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub", + "file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md", + "db_id": "", + "file_value": "USER-WP-0020", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub", + "file_path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md", + "db_id": "", + "file_value": "KEY-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0020' is not linked to State Hub", + "file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md", + "db_id": "", + "file_value": "USER-WP-0020", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0004' is not linked to State Hub", + "file_path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md", + "db_id": "", + "file_value": "KEY-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0021' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "USER-WP-0021", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'FLEX-WP-0009' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "FLEX-WP-0009", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'AUDIT-WP-0003' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "AUDIT-WP-0003", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'EMAIL-WP-0004' is not linked to State Hub", + "file_path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "db_id": "", + "file_value": "EMAIL-WP-0004", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'USER-WP-0022' is not linked to State Hub", + "file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "db_id": "", + "file_value": "USER-WP-0022", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'KEY-WP-0008' is not linked to State Hub", + "file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "db_id": "", + "file_value": "KEY-WP-0008", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'CSOC-WP-0003' is not linked to State Hub", + "file_path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "db_id": "", + "file_value": "CSOC-WP-0003", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0027' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0027-reef-placement-reconciliation.md", + "db_id": "965ad365-6b81-50a1-a2a3-2d0c1fcce0b4", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0031' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0031-deterministic-posture-feedback.md", + "db_id": "9d7b04f9-3803-5613-b7a5-8bd606c77f5a", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-37", + "message": "Residual workplan 'nk-wp-0040' has no origin/origin_ref provenance (not invented)", + "file_path": "workplans/NK-WP-0040-execution-attribution-receipt.md", + "db_id": "e2533f3a-aa43-59b3-bff3-8e64b6149487", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-40", + "message": "blocker satisfied, task still wait: 'NK-WP-0048-T15' in workplan 'NK-WP-0048' \u2014 every depends_on target is terminal (NK-WP-0048-T31)", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T15", + "db_id": "", + "file_value": "NK-WP-0048-T31", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-22", + "message": "Task description drift 'NK-WP-0048-T15': file content differs from DB (file wins)", + "file_path": "workplans/NK-WP-0048-security-case-types.md#NK-WP-0048-T15", + "db_id": "1b825705-4848-5edc-832f-b1622cd9bdb7", + "file_value": "2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional\npaired `holder`/`observe` capabilities and", + "db_value": "2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional\npaired `holder`/`observe` capabilities and", + "fixable": true + }, + { + "severity": "WARN", + "check_id": "C-20", + "message": "Workplan dependency target 'RAIL-PL-WP-0002' is not linked to State Hub", + "file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md", + "db_id": "", + "file_value": "RAIL-PL-WP-0002", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'nk-wp-0035@retired-20260908' (id=064c5e8b\u2026, status=archived) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "064c5e8b-7852-4d7f-a5fa-e26e406ab8e0", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-14@retired-20260827' (id=a5097121\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "a5097121-1461-59f8-81fa-e6ecef527337", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'adhoc-2026-08-23@retired-20260827' (id=e25ba5fa\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "e25ba5fa-eaae-5039-8d93-a69c6d837fc4", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'net-kingdom-adhoc-2026-06-14@retired-20260827' (id=c6f3d6bf\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "c6f3d6bf-4916-490d-96ce-092d2776d7e7", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "INFO", + "check_id": "C-08", + "message": "Retired DB workstream 'net-kingdom-adhoc-2026-07-02@retired-20260827' (id=67c1c7ac\u2026, status=finished) has no backing workplan file \u2014 projection tombstone, not live work", + "file_path": "", + "db_id": "67c1c7ac-d8b1-41dc-a81a-4e43f1afd068", + "file_value": "", + "db_value": "", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0020' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md", + "db_id": "", + "file_value": "NET-WP-0020", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0015' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260603-NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap.md", + "db_id": "", + "file_value": "NET-WP-0015", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0016' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260603-NET-WP-0016-guided-security-bootstrap-experience.md", + "db_id": "", + "file_value": "NET-WP-0016", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0018' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260604-NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md", + "db_id": "", + "file_value": "NET-WP-0018", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0017' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md", + "db_id": "", + "file_value": "NET-WP-0017", + "db_value": "NK-WP", + "fixable": false + }, + { + "severity": "WARN", + "check_id": "C-26", + "message": "Workplan id 'NET-WP-0019' uses prefix 'NET-WP' but repo canonical prefix is 'NK-WP' \u2014 new plans must conform", + "file_path": "workplans/archived/260702-NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md", + "db_id": "", + "file_value": "NET-WP-0019", + "db_value": "NK-WP", + "fixable": false + } + ], + "fixes_applied": [ + "bindings: synced 64/64", + "C-17 cleared: pushed 1 backlogged commit(s)", + "C-22 fixed: task 1b825705\u2026 description updated", + "push: pushed" + ], + "summary": { + "fail": 0, + "automation_error": 0, + "warn": 26, + "info": 5 + }, + "result": "warn" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/consistency.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/consistency.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/final_verify.py b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/final_verify.py new file mode 100644 index 00000000..cf4bdb98 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/final_verify.py @@ -0,0 +1,27 @@ +from pathlib import Path +import json,hashlib,subprocess,re,urllib.request +p=Path(__file__).resolve().parent;r=Path('/home/worsch/net-kingdom') +commit=json.loads((p/'commit-receipt.json').read_text())['commit'] +def git(*a):return subprocess.check_output(['git','-C',str(r),*a]).decode().strip() +assert git('rev-parse','HEAD')==commit +assert git('ls-remote','origin','refs/heads/main').split()[0]==commit +projection=json.loads((p/'projection.json').read_text());receipt=projection['receipt'] +assert projection['ok'] and projection['status'] in ('applied','noop') +assert receipt['expected_commit']==receipt['derived_commit']==commit +assert (receipt['instance_role'],receipt['instance_label'])==('primary','railiance01') +assert receipt['outcome']['counts']['retired']==0 +before=json.loads((p/'tasks-before.json').read_text()) +req=urllib.request.Request('http://127.0.0.1:8000/tasks/?workplan_id=e31f3f9d-0748-5137-b0a3-7b3ca46a66eb',headers={'X-StateHub-Component':'net-kingdom'}) +after=json.load(urllib.request.urlopen(req,timeout=30));(p/'tasks-after.json').write_text(json.dumps(after,indent=2)+'\n') +def identity(rows):return sorted((x['id'],x['status']) for x in rows) +assert identity(before)==identity(after) +name='workplans/NK-WP-0048-security-case-types.md';original=(p/'backup'/name).read_text();current=(r/name).read_text() +blocks=lambda text:re.findall(r'```task\n.*?```',text,re.S) +assert blocks(original)==blocks(current) +assert (r/name).read_bytes()==(p/'candidate.md').read_bytes() +t15=[b for b in blocks(current) if 'id: NK-WP-0048-T15\n' in b][0] +summary={'status':'published-and-projected','commit':commit,'only_committed_path':git('diff-tree','--no-commit-id','--name-only','-r',commit),'projection_status':projection['status'],'instance_role':receipt['instance_role'],'instance_label':receipt['instance_label'],'expected_commit':receipt['expected_commit'],'derived_commit':receipt['derived_commit'],'original_task_count':len(before),'current_task_count':len(after),'all_task_ids_and_statuses_unchanged':True,'all_source_task_blocks_unchanged':True,'T15_task_block':t15,'preservation':json.loads((p/'preservation-verification.json').read_text()),'native_or_credential_or_adapter_changes':False,'root_git_brief_work_modified':False} +(p/'verification.json').write_text(json.dumps(summary,indent=2)+'\n') +manifest={'files':[{'path':str(f.relative_to(p)),'sha256':hashlib.sha256(f.read_bytes()).hexdigest()} for f in sorted(p.rglob('*')) if f.is_file() and f.name!='manifest.json']} +(p/'manifest.json').write_text(json.dumps(manifest,indent=2)+'\n') +print(json.dumps({'status':summary['status'],'commit':commit,'task_count':len(after),'projection':projection['status'],'manifest_sha256':hashlib.sha256((p/'manifest.json').read_bytes()).hexdigest()})) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/identifier-preview.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/identifier-preview.json new file mode 100644 index 00000000..3bfb5519 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/identifier-preview.json @@ -0,0 +1,11 @@ +{ + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": false, + "repo": "net-kingdom", + "namespace": "helixforge", + "assignments": [], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/manifest.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/manifest.json new file mode 100644 index 00000000..3ab1ea15 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/manifest.json @@ -0,0 +1,332 @@ +{ + "files": [ + { + "path": "apply_commit.py", + "sha256": "ae5b4b42c44ca69e6b97ac0667401064eb81c22eae9ab2db3d579792e587b9a3" + }, + { + "path": "approved.patch", + "sha256": "4402448cac9b50b745f247aec1c550a5fc3532e702ad4ceeae0812691278118c" + }, + { + "path": "backup/.custodian-brief.md", + "sha256": "3eb7f91f2443090c9b859a716d83711310364897b6144dcf10d3254342fec3d1" + }, + { + "path": "backup/WORK-RECORDS.md", + "sha256": "a1fec217a6e380f528778d781dbea1fc5a836fdefa1dd4dd0308100fbe08112a" + }, + { + "path": "backup/workplans/ADHOC-2026-07-02.md", + "sha256": "f775fb97ce7c4d49f03f68dec86df8b2f1d0c265a6432d2b995d14852394db3d" + }, + { + "path": "backup/workplans/ADHOC-2026-08-14.md", + "sha256": "24e73b2559fecb4da7818fd2dd75af413dfc30d761887029d8dda364cd0205ab" + }, + { + "path": "backup/workplans/ADHOC-2026-08-23.md", + "sha256": "f3aabf4e36a27a4692b97d6cb7286f944681917cd3c31420dbb90d9b8a55f646" + }, + { + "path": "backup/workplans/ADHOC-2026-10-03.md", + "sha256": "0591aa5c901f6adb73aa9ecbfa89f8e1cf81919ab2f0f2d5bdb6ff4934eef81c" + }, + { + "path": "backup/workplans/ADHOC-2026-10-10.md", + "sha256": "01c42780f0299ec96febd083393d45a23da7d17674f8049374db1e4ef7dfb322" + }, + { + "path": "backup/workplans/ADHOC-2026-10-11.md", + "sha256": "cdfce94de7390b7abd2a8d2cc10d3e770b718521dd34c69fda81fa0a1f1e83fd" + }, + { + "path": "backup/workplans/ADHOC-2026-10-16.md", + "sha256": "4288f317ee4e66b332b5e3907d90878becb829d4492ccd5d86842a914667a9b8" + }, + { + "path": "backup/workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md", + "sha256": "fe8a6c450f734810c78b97eeca70dddcb7555cd16d9e3403b9b4bc8eca371ff8" + }, + { + "path": "backup/workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md", + "sha256": "1d0f5f9674b20c3fbaa76e98010f63a2d83d081b0a1c1410ac6a38d49b3fc631" + }, + { + "path": "backup/workplans/NK-WP-0011-enterprise-federation-saml.md", + "sha256": "e5692b737a1b2f0ca8c1ed9129d06929be74726e69baccea5520738b7e31a657" + }, + { + "path": "backup/workplans/NK-WP-0021-activity-core-ops-sso-operators.md", + "sha256": "24fe6fd6a2bd0bf549c24523f4562e5fc8e1a3d317c6858f701f914b33b6059a" + }, + { + "path": "backup/workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md", + "sha256": "a44265859ecab3c65d95f9ea520950e48c223b64f3a5449ab2d4663a8efce336" + }, + { + "path": "backup/workplans/NK-WP-0023-user-engine-portal-platform-integration.md", + "sha256": "9d1f43313f2b1d0d55ad3d69966397cd310e6c90d76f90c216fb75b5f7b1e053" + }, + { + "path": "backup/workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "sha256": "7899232d817fde7a0ac1be4d9511c1ba65f44428a08872befe30af0b06a3aac0" + }, + { + "path": "backup/workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "sha256": "d3570990e1c4d4ee528c72f2db9116b57f9e6bfc76bac4fa881c362b10b90c63" + }, + { + "path": "backup/workplans/NK-WP-0026-flex-auth-caller-identity-rollout.md", + "sha256": "52f0727defd43791c812e045e75eb5ebfb181183290a25f7841bccd1e250e0f4" + }, + { + "path": "backup/workplans/NK-WP-0027-reef-placement-reconciliation.md", + "sha256": "3360b2bfeedd04ea5c3854dff0c01960f9c1d2e8433a71f393fc9c8c9253aca2" + }, + { + "path": "backup/workplans/NK-WP-0028-canon-publication-and-grouping-semantics.md", + "sha256": "48548faf581e1bacb6952db0050f67b30e634830eb55d20289be85580afb6017" + }, + { + "path": "backup/workplans/NK-WP-0029-scope-and-intent-reconciliation.md", + "sha256": "f2111f2a8c6661022755824091aa20413e10975ef940d5492ff6e9564d45c372" + }, + { + "path": "backup/workplans/NK-WP-0030-deterministic-security-scenario-composition.md", + "sha256": "f82e1f33ae1ddf2305ce2efc567d2c55809669a47b91c0cabd3d0b41bd78ab3b" + }, + { + "path": "backup/workplans/NK-WP-0031-deterministic-posture-feedback.md", + "sha256": "f2b465a883ab8e28ab84f0e94d95e23a15ad6d9b6e038068d96b36dcaa313b5e" + }, + { + "path": "backup/workplans/NK-WP-0032-openbao-operator-loopback-callback.md", + "sha256": "a1955896b0ac0805c19bd97264e0e755f2d46a99687acdd2fcd6dd36947aa1bb" + }, + { + "path": "backup/workplans/NK-WP-0033-keycape-secret-exposure-rotation.md", + "sha256": "8268aa86f7fa9f2a3848adfe2dc3022bcf2e9dc9197328993445f4138a40e513" + }, + { + "path": "backup/workplans/NK-WP-0034-verification-that-verifies.md", + "sha256": "d31227ca44d697d9a3e7bef5931836ab0cc6d2c9641900ab1fa8f63307cb7c83" + }, + { + "path": "backup/workplans/NK-WP-0035-emission-cadence-security-profile.md", + "sha256": "273a9154a37a7b9efd4fb199e7375d2a3d5cb6919eaa3a2fb8532745a90f83e9" + }, + { + "path": "backup/workplans/NK-WP-0036-restore-user-portal-client-registration.md", + "sha256": "84019513b7099c39b6dc2daa286017fb48f1286b966fe7c7a142df7c914b575e" + }, + { + "path": "backup/workplans/NK-WP-0037-vergabe-company-welcome.md", + "sha256": "f4fb6d7972f59afa550d6021a9297303e9329867027229512ec9f85b61dda286" + }, + { + "path": "backup/workplans/NK-WP-0038-tenant-scoped-identity-lifecycle.md", + "sha256": "32811a635811c870795aba5d4d2fa6f3b0baecc0b3d270a7cfeb77dbba75206b" + }, + { + "path": "backup/workplans/NK-WP-0039-flex-auth-access-engine-coordinate-intake.md", + "sha256": "31a0811bf36984f1ef3bdf3c7b7e68d46413909bfb4bc0d781bd7bf49d9bbabf" + }, + { + "path": "backup/workplans/NK-WP-0040-execution-attribution-receipt.md", + "sha256": "8b0b653da2fc6ea62a2b804bdb8feb92046876f4e5e0afa96c7c53bc31945d2e" + }, + { + "path": "backup/workplans/NK-WP-0041-onboarding-journey-usability.md", + "sha256": "b05d9e14028e83cebbe16b1efcee3477f49a99ae5d6dfc408c41c6cd0b3c3a83" + }, + { + "path": "backup/workplans/NK-WP-0042-workload-mfa-step-up.md", + "sha256": "12c95fd8a2cbed6c521c1fbf5464d7cf6b7552d63c11992bc93ec88f2013c618" + }, + { + "path": "backup/workplans/NK-WP-0043-cadence-assessment-and-operating-guidance.md", + "sha256": "bf75f9fdaf7745596b79843c33d6325236a0c7de27547169464c367f71100de1" + }, + { + "path": "backup/workplans/NK-WP-0044-runbook-packs-for-runbook-tutorials.md", + "sha256": "071fe448f456b7622e4d6499dab6005af1e41e9c7cc516bc8389b2847e1c01d5" + }, + { + "path": "backup/workplans/NK-WP-0045-root-grant-custody-record.md", + "sha256": "72957387424f36b376a3b66ea10381d40665497dbcdc7b029ba54fed87c522d1" + }, + { + "path": "backup/workplans/NK-WP-0046-secrets-engine-openbao-client-registration.md", + "sha256": "ef38481d26e599802d94f8b06a4d89c7c99a83c81ca5be9bcf7422b885867479" + }, + { + "path": "backup/workplans/NK-WP-0047-platform-password-reset-mail.md", + "sha256": "78b159076121c4e53fef8eadd6287b8c9e91a725203be88816840c9f9c1c8516" + }, + { + "path": "backup/workplans/NK-WP-0048-security-case-types.md", + "sha256": "b682cd607d9ed9b232656cc7934ec09ac4e986c1c81131a44f80240342a0571c" + }, + { + "path": "backup/workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md", + "sha256": "cba3233883e9a5b429aaea1b8eacd250c6efcf5e484c4284ddc97e8e52697085" + }, + { + "path": "backup/workplans/NK-WP-0050-playwright-consumer-registration.md", + "sha256": "0be0b643409d0731d4924c95566facdef58b1c65c5c1276f7f308ebfb2271fb7" + }, + { + "path": "backup/workplans/NK-WP-0051-sign-in-diagnosis.md", + "sha256": "736a06e798d9d15715490f8c3ed2f81a8d065e8446e046e36a2bd19353a083b9" + }, + { + "path": "backup/workplans/archived/260522-NK-WP-0015-user-engine-isolated-mvp.md", + "sha256": "98e7ae719ae0282e80d541222d12805bebf2ef1abf9dca576f1e32c83d5d31bf" + }, + { + "path": "backup/workplans/archived/260522-NK-WP-0016-user-engine-multi-tenancy.md", + "sha256": "be9490920a6c8bc1652c334c6b2b1480257b18fffe55406426b04ab240ba7430" + }, + { + "path": "backup/workplans/archived/260522-NK-WP-0017-user-engine-multi-application-catalogs.md", + "sha256": "ab5c151034d6ceaf8bf6d4154944fa7406e2edb6127e6adc963429920c425270" + }, + { + "path": "backup/workplans/archived/260522-NK-WP-0018-user-engine-integrated-test-scenarios.md", + "sha256": "2b83295aeaae5a58ad6cef6b0198e9abf8f57a8a9b0216bb166afb71d4d26e28" + }, + { + "path": "backup/workplans/archived/260522-NK-WP-0019-user-engine-finalization-polish.md", + "sha256": "7db746783b2ad60110451b9b44a04471b188bb8a6b49d76c984a1cd4c72b1ae3" + }, + { + "path": "backup/workplans/archived/260603-NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap.md", + "sha256": "014252768653440c1911512972022122b4f43c3c040c7da34d0f86bba71e1204" + }, + { + "path": "backup/workplans/archived/260603-NET-WP-0016-guided-security-bootstrap-experience.md", + "sha256": "936b446483326b3dc7c1be94397f833a1e124311c98654d1877478a09b8b23e8" + }, + { + "path": "backup/workplans/archived/260603-NK-WP-0003-keycape-privacyidea-cluster-deployment.md", + "sha256": "754955fe18c25c35a1c0036b25f889d281dd8442b86e1793583c97f83f8125c2" + }, + { + "path": "backup/workplans/archived/260603-NK-WP-0004-credential-management-foundation.md", + "sha256": "62a69453f9dc5db8a208e01df83e198249074d921fa83cba9d919385208709fc" + }, + { + "path": "backup/workplans/archived/260603-NK-WP-0005-agent-driven-credential-bootstrap.md", + "sha256": "f7cebada5f6344d0baa7625f66938cafd1f156bd20fe9b00f09c994f290fb4e0" + }, + { + "path": "backup/workplans/archived/260604-NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md", + "sha256": "784b034ba22125ea2098da40c3ece39f9e910c473d4a3f0f8f3c23250aae7e23" + }, + { + "path": "backup/workplans/archived/260702-ADHOC-2026-06-14.md", + "sha256": "455b4cb77ec944551696203c79d3a229cbf9e5f2381931632ea1ce23c587e2fd" + }, + { + "path": "backup/workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md", + "sha256": "74eafd43790bde4d31728518e1b875ecc8262e6af18e1cd5f81e0882178182be" + }, + { + "path": "backup/workplans/archived/260702-NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md", + "sha256": "61cd3135222696a7b6afd74b2f3a3fd717f5e7985f8ade2ccc66e119a43a9d14" + }, + { + "path": "backup/workplans/archived/260702-NK-WP-0001-sso-mfa-platform.md", + "sha256": "c904ad4503b45ea64bc9d360c1cc8394398771cabf1a389dea41496714cde6e6" + }, + { + "path": "backup/workplans/archived/260702-NK-WP-0002-local-identity.md", + "sha256": "fc2b924da22125a8d0374e795b962612dfd8e01f583b6a0eefbb827e338127d7" + }, + { + "path": "backup/workplans/archived/260702-NK-WP-0006-recursive-platform-identity-security-architecture.md", + "sha256": "7a70d174baa28f399c63823c264cb1dfba9af69b61934ffc105cba56e4f34b47" + }, + { + "path": "backup/workplans/archived/260702-NK-WP-0007-object-storage-sts-credential-vending.md", + "sha256": "d921c3d9c015027b99b30e7723acf6c71151a29927fcdbfaa124f776dcfbd64f" + }, + { + "path": "backup/workplans/archived/260702-NK-WP-0008-it-security-architecture-patterns-infospace.md", + "sha256": "abacfb3651e56b233587492dda5fa6cbbd08e0c1fc49681a22373e156a630b36" + }, + { + "path": "backup/workplans/archived/260702-NK-WP-0010-genesis-security-pattern-completion.md", + "sha256": "6a0b13e0f82af03e7461f4f433d7876e5f8d9a76acc8888709f4e130e13fb2e4" + }, + { + "path": "backup/workplans/archived/260702-NK-WP-0012-iam-profile-specification.md", + "sha256": "1ca0aed6549d7cb2aeb490a4110fddfd0f5575832cb7e2f696724b3b4b4e79ba" + }, + { + "path": "backup/workplans/archived/260702-NK-WP-0013-playbook-capability-contract.md", + "sha256": "5e69691251391706d3419349bc38cb13c6d910d623b03ba788d31dbcd5a7e3bf" + }, + { + "path": "backup/workplans/archived/260702-NK-WP-0014-user-engine-preparation-boundary-contracts.md", + "sha256": "d4a6e9f60ccfe666645ebf37af0d49b50e692218de46539c50b8b22ec255030a" + }, + { + "path": "candidate.md", + "sha256": "1502f1f474856172d969489cab8c0642d9456d93d326f6660df3772a4716d30b" + }, + { + "path": "commit-receipt.json", + "sha256": "1ec30c07304b95ea7474c8e05d0d4770932e726d56d7a7a93356ac559058c797" + }, + { + "path": "consistency.json", + "sha256": "bf4c3946f5080d8dc878599ab73053eeb8d1ce7f25717067a8cb837be61c5aa6" + }, + { + "path": "consistency.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "final_verify.py", + "sha256": "00273ab7067452aafa200e6b5dcbb431a4041a8a6bfc1d4e3a6a69902e97bc10" + }, + { + "path": "identifier-preview.json", + "sha256": "c7df3d4f74214943d11408c147f1d4331549ac7d9764667cf75bc58b4186b3b9" + }, + { + "path": "preimage.json", + "sha256": "d223380325bc523c2226a0d4cf91be3c35b94a6db7ad42753c8ad359ecbdeb61" + }, + { + "path": "preservation-verification.json", + "sha256": "0accde4df20981a3b8732aa5143beb7a9defb333d11c3481543950e676a4674d" + }, + { + "path": "projection.json", + "sha256": "00a8832cd630b7fb42214883710cfcda6f653b1b22c417f952393d2a43876d39" + }, + { + "path": "projection.stderr.txt", + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "restore_verify.py", + "sha256": "230bed15ab4388aaf7dd3452248fbdab30a14f0ecbbf79965fb609e050816e40" + }, + { + "path": "tasks-after.json", + "sha256": "26bda42190983a07cb853c69b14635c69f4a4ec21f3bae7b4d1e72fbd3961e79" + }, + { + "path": "tasks-before.json", + "sha256": "0c79d45200ff95d67558de103ea0a75b8450fcff4cea1e871a70e66c2edfe288" + }, + { + "path": "verification.json", + "sha256": "6695016b43830abc79fb4da56697e51a59624ef7d0d345fa73afa2cd77fbd8d9" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/preimage.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/preimage.json new file mode 100644 index 00000000..6ce2cf36 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/preimage.json @@ -0,0 +1,367 @@ +{ + "head": "001a38ca2aa85155ce4840c4da22aca6e2346171", + "remote": "001a38ca2aa85155ce4840c4da22aca6e2346171", + "status": "M .custodian-brief.md\n M WORK-RECORDS.md\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json\n?? intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md", + "files": [ + { + "path": ".custodian-brief.md", + "sha256": "3eb7f91f2443090c9b859a716d83711310364897b6144dcf10d3254342fec3d1", + "index": "100644 f60f8bad019e23e6f7e0e074745da0bb2fcbb870 0\t.custodian-brief.md" + }, + { + "path": "WORK-RECORDS.md", + "sha256": "a1fec217a6e380f528778d781dbea1fc5a836fdefa1dd4dd0308100fbe08112a", + "index": "100644 76a6b7fc304c508f1b50010cb72d892826c6a831 0\tWORK-RECORDS.md" + }, + { + "path": "workplans/ADHOC-2026-07-02.md", + "sha256": "f775fb97ce7c4d49f03f68dec86df8b2f1d0c265a6432d2b995d14852394db3d", + "index": "100644 66e748e1224a1c212a071a4e3c72d0d3404ed9a9 0\tworkplans/ADHOC-2026-07-02.md" + }, + { + "path": "workplans/ADHOC-2026-08-14.md", + "sha256": "24e73b2559fecb4da7818fd2dd75af413dfc30d761887029d8dda364cd0205ab", + "index": "100644 91a3ea1a4256cf7d09d857500be99810c71009a5 0\tworkplans/ADHOC-2026-08-14.md" + }, + { + "path": "workplans/ADHOC-2026-08-23.md", + "sha256": "f3aabf4e36a27a4692b97d6cb7286f944681917cd3c31420dbb90d9b8a55f646", + "index": "100644 21f20d066843b000c457851bd44e6ec217999228 0\tworkplans/ADHOC-2026-08-23.md" + }, + { + "path": "workplans/ADHOC-2026-10-03.md", + "sha256": "0591aa5c901f6adb73aa9ecbfa89f8e1cf81919ab2f0f2d5bdb6ff4934eef81c", + "index": "100644 360faf210cc73613bf27b4f7f6655aa9cf8c511a 0\tworkplans/ADHOC-2026-10-03.md" + }, + { + "path": "workplans/ADHOC-2026-10-10.md", + "sha256": "01c42780f0299ec96febd083393d45a23da7d17674f8049374db1e4ef7dfb322", + "index": "100644 7d350c2746dce164e8fe0c43677c346a68db5cf6 0\tworkplans/ADHOC-2026-10-10.md" + }, + { + "path": "workplans/ADHOC-2026-10-11.md", + "sha256": "cdfce94de7390b7abd2a8d2cc10d3e770b718521dd34c69fda81fa0a1f1e83fd", + "index": "100644 94059eb7d549fda7a2a6550ecea896078e3a5f93 0\tworkplans/ADHOC-2026-10-11.md" + }, + { + "path": "workplans/ADHOC-2026-10-16.md", + "sha256": "4288f317ee4e66b332b5e3907d90878becb829d4492ccd5d86842a914667a9b8", + "index": "100644 a38773aed4d12e9b214988d8fac1d7061eadc6fb 0\tworkplans/ADHOC-2026-10-16.md" + }, + { + "path": "workplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md", + "sha256": "fe8a6c450f734810c78b97eeca70dddcb7555cd16d9e3403b9b4bc8eca371ff8", + "index": "100644 ffe7a7eeee1069f4bdf9511e2dd44e6eff5726f8 0\tworkplans/NET-WP-0020-openbao-unseal-custody-and-ssh-automation.md" + }, + { + "path": "workplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md", + "sha256": "1d0f5f9674b20c3fbaa76e98010f63a2d83d081b0a1c1410ac6a38d49b3fc631", + "index": "100644 6e55c842670a5f1153c2556495abbf9c78db866e 0\tworkplans/NK-WP-0009-netkingdom-security-pattern-tutorials.md" + }, + { + "path": "workplans/NK-WP-0011-enterprise-federation-saml.md", + "sha256": "e5692b737a1b2f0ca8c1ed9129d06929be74726e69baccea5520738b7e31a657", + "index": "100644 f6dc921d804a45dbb4c225527f7ce5ddb5f53dc6 0\tworkplans/NK-WP-0011-enterprise-federation-saml.md" + }, + { + "path": "workplans/NK-WP-0021-activity-core-ops-sso-operators.md", + "sha256": "24fe6fd6a2bd0bf549c24523f4562e5fc8e1a3d317c6858f701f914b33b6059a", + "index": "100644 31de057e004086584e85d14595269460896cd5ea 0\tworkplans/NK-WP-0021-activity-core-ops-sso-operators.md" + }, + { + "path": "workplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md", + "sha256": "a44265859ecab3c65d95f9ea520950e48c223b64f3a5449ab2d4663a8efce336", + "index": "100644 d0616047e35b52257647ce72914584e32222a5c7 0\tworkplans/NK-WP-0022-railiance01-identity-cutover-and-coulombcore-retirement.md" + }, + { + "path": "workplans/NK-WP-0023-user-engine-portal-platform-integration.md", + "sha256": "9d1f43313f2b1d0d55ad3d69966397cd310e6c90d76f90c216fb75b5f7b1e053", + "index": "100644 c08863a1b0cc9cd9ebf15ac09c07b29e7fbe46e1 0\tworkplans/NK-WP-0023-user-engine-portal-platform-integration.md" + }, + { + "path": "workplans/NK-WP-0024-user-engine-portal-integration-expansion.md", + "sha256": "7899232d817fde7a0ac1be4d9511c1ba65f44428a08872befe30af0b06a3aac0", + "index": "100644 0b336e80dc4ad264f6aae2340f380fc041f54df0 0\tworkplans/NK-WP-0024-user-engine-portal-integration-expansion.md" + }, + { + "path": "workplans/NK-WP-0025-public-self-registration-and-application-jit.md", + "sha256": "d3570990e1c4d4ee528c72f2db9116b57f9e6bfc76bac4fa881c362b10b90c63", + "index": "100644 9cd2e946519328b96910be0dbf87c35f7128a0ed 0\tworkplans/NK-WP-0025-public-self-registration-and-application-jit.md" + }, + { + "path": "workplans/NK-WP-0026-flex-auth-caller-identity-rollout.md", + "sha256": "52f0727defd43791c812e045e75eb5ebfb181183290a25f7841bccd1e250e0f4", + "index": "100644 5aadafc881b7f79b0f432bae3eed46800222249e 0\tworkplans/NK-WP-0026-flex-auth-caller-identity-rollout.md" + }, + { + "path": "workplans/NK-WP-0027-reef-placement-reconciliation.md", + "sha256": "3360b2bfeedd04ea5c3854dff0c01960f9c1d2e8433a71f393fc9c8c9253aca2", + "index": "100644 ea0b21e2685b8304c184103f67a013e8f7456ba9 0\tworkplans/NK-WP-0027-reef-placement-reconciliation.md" + }, + { + "path": "workplans/NK-WP-0028-canon-publication-and-grouping-semantics.md", + "sha256": "48548faf581e1bacb6952db0050f67b30e634830eb55d20289be85580afb6017", + "index": "100644 e92df0c1de0c77be975f2e375221043598f0091d 0\tworkplans/NK-WP-0028-canon-publication-and-grouping-semantics.md" + }, + { + "path": "workplans/NK-WP-0029-scope-and-intent-reconciliation.md", + "sha256": "f2111f2a8c6661022755824091aa20413e10975ef940d5492ff6e9564d45c372", + "index": "100644 fc1b60f6cafeb38dbaa403bce5e050dd631ad54e 0\tworkplans/NK-WP-0029-scope-and-intent-reconciliation.md" + }, + { + "path": "workplans/NK-WP-0030-deterministic-security-scenario-composition.md", + "sha256": "f82e1f33ae1ddf2305ce2efc567d2c55809669a47b91c0cabd3d0b41bd78ab3b", + "index": "100644 f60bdbbb04d1c79ed17f40f41cb51c800d162470 0\tworkplans/NK-WP-0030-deterministic-security-scenario-composition.md" + }, + { + "path": "workplans/NK-WP-0031-deterministic-posture-feedback.md", + "sha256": "f2b465a883ab8e28ab84f0e94d95e23a15ad6d9b6e038068d96b36dcaa313b5e", + "index": "100644 4cda818a55ff1e02f4a37a7ea24e8caa8d6190df 0\tworkplans/NK-WP-0031-deterministic-posture-feedback.md" + }, + { + "path": "workplans/NK-WP-0032-openbao-operator-loopback-callback.md", + "sha256": "a1955896b0ac0805c19bd97264e0e755f2d46a99687acdd2fcd6dd36947aa1bb", + "index": "100644 ce46547934ea494938f818a41379327ed9f627a6 0\tworkplans/NK-WP-0032-openbao-operator-loopback-callback.md" + }, + { + "path": "workplans/NK-WP-0033-keycape-secret-exposure-rotation.md", + "sha256": "8268aa86f7fa9f2a3848adfe2dc3022bcf2e9dc9197328993445f4138a40e513", + "index": "100644 1946d3221b8724907911d8b2a0f603583eb12af7 0\tworkplans/NK-WP-0033-keycape-secret-exposure-rotation.md" + }, + { + "path": "workplans/NK-WP-0034-verification-that-verifies.md", + "sha256": "d31227ca44d697d9a3e7bef5931836ab0cc6d2c9641900ab1fa8f63307cb7c83", + "index": "100644 7302cd6a053bd1e9fa59d1187ee61f7440c3ef88 0\tworkplans/NK-WP-0034-verification-that-verifies.md" + }, + { + "path": "workplans/NK-WP-0035-emission-cadence-security-profile.md", + "sha256": "273a9154a37a7b9efd4fb199e7375d2a3d5cb6919eaa3a2fb8532745a90f83e9", + "index": "100644 4c22c5376951efbf7f3877fc73cfb96a5cbf2769 0\tworkplans/NK-WP-0035-emission-cadence-security-profile.md" + }, + { + "path": "workplans/NK-WP-0036-restore-user-portal-client-registration.md", + "sha256": "84019513b7099c39b6dc2daa286017fb48f1286b966fe7c7a142df7c914b575e", + "index": "100644 87b218549c1721ad73962922866b4f119786a475 0\tworkplans/NK-WP-0036-restore-user-portal-client-registration.md" + }, + { + "path": "workplans/NK-WP-0037-vergabe-company-welcome.md", + "sha256": "f4fb6d7972f59afa550d6021a9297303e9329867027229512ec9f85b61dda286", + "index": "100644 27f964c0da1b2ff04014f954ee02ac7f5d132d4f 0\tworkplans/NK-WP-0037-vergabe-company-welcome.md" + }, + { + "path": "workplans/NK-WP-0038-tenant-scoped-identity-lifecycle.md", + "sha256": "32811a635811c870795aba5d4d2fa6f3b0baecc0b3d270a7cfeb77dbba75206b", + "index": "100644 6b274dc6d6743a0d4c0d5c7b2fea9d8c844b7cad 0\tworkplans/NK-WP-0038-tenant-scoped-identity-lifecycle.md" + }, + { + "path": "workplans/NK-WP-0039-flex-auth-access-engine-coordinate-intake.md", + "sha256": "31a0811bf36984f1ef3bdf3c7b7e68d46413909bfb4bc0d781bd7bf49d9bbabf", + "index": "100644 af85aaa2b5acc459ceb633f02465a953d0d84212 0\tworkplans/NK-WP-0039-flex-auth-access-engine-coordinate-intake.md" + }, + { + "path": "workplans/NK-WP-0040-execution-attribution-receipt.md", + "sha256": "8b0b653da2fc6ea62a2b804bdb8feb92046876f4e5e0afa96c7c53bc31945d2e", + "index": "100644 541f7f2ebcc09e4cf92ca564d377fa26133c221f 0\tworkplans/NK-WP-0040-execution-attribution-receipt.md" + }, + { + "path": "workplans/NK-WP-0041-onboarding-journey-usability.md", + "sha256": "b05d9e14028e83cebbe16b1efcee3477f49a99ae5d6dfc408c41c6cd0b3c3a83", + "index": "100644 549226d24984e4c8a6af8e9791723df4167cfc92 0\tworkplans/NK-WP-0041-onboarding-journey-usability.md" + }, + { + "path": "workplans/NK-WP-0042-workload-mfa-step-up.md", + "sha256": "12c95fd8a2cbed6c521c1fbf5464d7cf6b7552d63c11992bc93ec88f2013c618", + "index": "100644 71856e4b4b5d7fc7bd943df27d1eff9ff0647bc2 0\tworkplans/NK-WP-0042-workload-mfa-step-up.md" + }, + { + "path": "workplans/NK-WP-0043-cadence-assessment-and-operating-guidance.md", + "sha256": "bf75f9fdaf7745596b79843c33d6325236a0c7de27547169464c367f71100de1", + "index": "100644 814c11d1a3da13c2979e4b5d960ba5f72cba2e7b 0\tworkplans/NK-WP-0043-cadence-assessment-and-operating-guidance.md" + }, + { + "path": "workplans/NK-WP-0044-runbook-packs-for-runbook-tutorials.md", + "sha256": "071fe448f456b7622e4d6499dab6005af1e41e9c7cc516bc8389b2847e1c01d5", + "index": "100644 6c9117281daf8de58783018fe5c23d8c58f09d96 0\tworkplans/NK-WP-0044-runbook-packs-for-runbook-tutorials.md" + }, + { + "path": "workplans/NK-WP-0045-root-grant-custody-record.md", + "sha256": "72957387424f36b376a3b66ea10381d40665497dbcdc7b029ba54fed87c522d1", + "index": "100644 455c2fb2c64f350836b3b426acdb18bdf3ba3b37 0\tworkplans/NK-WP-0045-root-grant-custody-record.md" + }, + { + "path": "workplans/NK-WP-0046-secrets-engine-openbao-client-registration.md", + "sha256": "ef38481d26e599802d94f8b06a4d89c7c99a83c81ca5be9bcf7422b885867479", + "index": "100644 56cdc6b3580a253b38d17dfe4dad007ca00f03ed 0\tworkplans/NK-WP-0046-secrets-engine-openbao-client-registration.md" + }, + { + "path": "workplans/NK-WP-0047-platform-password-reset-mail.md", + "sha256": "78b159076121c4e53fef8eadd6287b8c9e91a725203be88816840c9f9c1c8516", + "index": "100644 bdb3c73b42cfe5c9b47c0d9ee77ee44b3a4e187e 0\tworkplans/NK-WP-0047-platform-password-reset-mail.md" + }, + { + "path": "workplans/NK-WP-0048-security-case-types.md", + "sha256": "b682cd607d9ed9b232656cc7934ec09ac4e986c1c81131a44f80240342a0571c", + "index": "100644 71cba31e0bef994557e972df18a98d1f2002fa1b 0\tworkplans/NK-WP-0048-security-case-types.md" + }, + { + "path": "workplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md", + "sha256": "cba3233883e9a5b429aaea1b8eacd250c6efcf5e484c4284ddc97e8e52697085", + "index": "100644 2078d625443a6fcafb22b96f18e9b7bb5c45cc84 0\tworkplans/NK-WP-0049-bootstrap-secret-free-recovery-handoff.md" + }, + { + "path": "workplans/NK-WP-0050-playwright-consumer-registration.md", + "sha256": "0be0b643409d0731d4924c95566facdef58b1c65c5c1276f7f308ebfb2271fb7", + "index": "100644 00d4833d270ed06f5e54ea992c175565a824d069 0\tworkplans/NK-WP-0050-playwright-consumer-registration.md" + }, + { + "path": "workplans/NK-WP-0051-sign-in-diagnosis.md", + "sha256": "736a06e798d9d15715490f8c3ed2f81a8d065e8446e046e36a2bd19353a083b9", + "index": "100644 5844807b7de5f365fc5353b565e070823cc230b2 0\tworkplans/NK-WP-0051-sign-in-diagnosis.md" + }, + { + "path": "workplans/archived/260522-NK-WP-0015-user-engine-isolated-mvp.md", + "sha256": "98e7ae719ae0282e80d541222d12805bebf2ef1abf9dca576f1e32c83d5d31bf", + "index": "100644 0fa1afc14b570ca23fd6f562ac6ae6d083231580 0\tworkplans/archived/260522-NK-WP-0015-user-engine-isolated-mvp.md" + }, + { + "path": "workplans/archived/260522-NK-WP-0016-user-engine-multi-tenancy.md", + "sha256": "be9490920a6c8bc1652c334c6b2b1480257b18fffe55406426b04ab240ba7430", + "index": "100644 419f916829f6dc6b453f0988c5b736f79ec7a365 0\tworkplans/archived/260522-NK-WP-0016-user-engine-multi-tenancy.md" + }, + { + "path": "workplans/archived/260522-NK-WP-0017-user-engine-multi-application-catalogs.md", + "sha256": "ab5c151034d6ceaf8bf6d4154944fa7406e2edb6127e6adc963429920c425270", + "index": "100644 a047efccf34c48a5900e3168f97345ef71776e4f 0\tworkplans/archived/260522-NK-WP-0017-user-engine-multi-application-catalogs.md" + }, + { + "path": "workplans/archived/260522-NK-WP-0018-user-engine-integrated-test-scenarios.md", + "sha256": "2b83295aeaae5a58ad6cef6b0198e9abf8f57a8a9b0216bb166afb71d4d26e28", + "index": "100644 2d3b99d566519a03e5bf98972ab9b355f08fd560 0\tworkplans/archived/260522-NK-WP-0018-user-engine-integrated-test-scenarios.md" + }, + { + "path": "workplans/archived/260522-NK-WP-0019-user-engine-finalization-polish.md", + "sha256": "7db746783b2ad60110451b9b44a04471b188bb8a6b49d76c984a1cd4c72b1ae3", + "index": "100644 d7135b4993398d9220e08f5c1f430220a46c114c 0\tworkplans/archived/260522-NK-WP-0019-user-engine-finalization-polish.md" + }, + { + "path": "workplans/archived/260603-NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap.md", + "sha256": "014252768653440c1911512972022122b4f43c3c040c7da34d0f86bba71e1204", + "index": "100644 ef2a569259fd1a08577c09b6b143559ecba375f0 0\tworkplans/archived/260603-NET-WP-0015-platform-root-custody-and-openbao-identity-bootstrap.md" + }, + { + "path": "workplans/archived/260603-NET-WP-0016-guided-security-bootstrap-experience.md", + "sha256": "936b446483326b3dc7c1be94397f833a1e124311c98654d1877478a09b8b23e8", + "index": "100644 b23e86009bcf6772faf6f939363897f913d45744 0\tworkplans/archived/260603-NET-WP-0016-guided-security-bootstrap-experience.md" + }, + { + "path": "workplans/archived/260603-NK-WP-0003-keycape-privacyidea-cluster-deployment.md", + "sha256": "754955fe18c25c35a1c0036b25f889d281dd8442b86e1793583c97f83f8125c2", + "index": "100644 bd9bf9662654c80fafb192a927aeb35fefe56b2b 0\tworkplans/archived/260603-NK-WP-0003-keycape-privacyidea-cluster-deployment.md" + }, + { + "path": "workplans/archived/260603-NK-WP-0004-credential-management-foundation.md", + "sha256": "62a69453f9dc5db8a208e01df83e198249074d921fa83cba9d919385208709fc", + "index": "100644 eea4583d2e6059bf9beee17083309eeb06740e78 0\tworkplans/archived/260603-NK-WP-0004-credential-management-foundation.md" + }, + { + "path": "workplans/archived/260603-NK-WP-0005-agent-driven-credential-bootstrap.md", + "sha256": "f7cebada5f6344d0baa7625f66938cafd1f156bd20fe9b00f09c994f290fb4e0", + "index": "100644 6acf610042aa48292be0a589530ab64d3e6750f2 0\tworkplans/archived/260603-NK-WP-0005-agent-driven-credential-bootstrap.md" + }, + { + "path": "workplans/archived/260604-NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md", + "sha256": "784b034ba22125ea2098da40c3ece39f9e910c473d4a3f0f8f3c23250aae7e23", + "index": "100644 a781cb44597b2f87eeac5b88117fc506b20e2fbd 0\tworkplans/archived/260604-NET-WP-0018-bootstrap-automation-and-rebuild-readiness.md" + }, + { + "path": "workplans/archived/260702-ADHOC-2026-06-14.md", + "sha256": "455b4cb77ec944551696203c79d3a229cbf9e5f2381931632ea1ce23c587e2fd", + "index": "100644 d2efa4d31679469804cf48b6eb56f14c9ccd442a 0\tworkplans/archived/260702-ADHOC-2026-06-14.md" + }, + { + "path": "workplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md", + "sha256": "74eafd43790bde4d31728518e1b875ecc8262e6af18e1cd5f81e0882178182be", + "index": "100644 7756f7dd4b7d44a30120b6339fce3e94852e08eb 0\tworkplans/archived/260702-NET-WP-0017-it-security-readiness-for-user-onboarding.md" + }, + { + "path": "workplans/archived/260702-NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md", + "sha256": "61cd3135222696a7b6afd74b2f3a3fd717f5e7985f8ade2ccc66e119a43a9d14", + "index": "100644 b4635dc00ab6a6d6fac0572495fbf6c4172384e7 0\tworkplans/archived/260702-NET-WP-0019-t06-adjacent-user-lifecycle-dry-run-polish.md" + }, + { + "path": "workplans/archived/260702-NK-WP-0001-sso-mfa-platform.md", + "sha256": "c904ad4503b45ea64bc9d360c1cc8394398771cabf1a389dea41496714cde6e6", + "index": "100644 78d58cb5be90924c102ca9a57afe0e9af6a15380 0\tworkplans/archived/260702-NK-WP-0001-sso-mfa-platform.md" + }, + { + "path": "workplans/archived/260702-NK-WP-0002-local-identity.md", + "sha256": "fc2b924da22125a8d0374e795b962612dfd8e01f583b6a0eefbb827e338127d7", + "index": "100644 a93af8cf2641144212f519bb7fbcb72ad14d2874 0\tworkplans/archived/260702-NK-WP-0002-local-identity.md" + }, + { + "path": "workplans/archived/260702-NK-WP-0006-recursive-platform-identity-security-architecture.md", + "sha256": "7a70d174baa28f399c63823c264cb1dfba9af69b61934ffc105cba56e4f34b47", + "index": "100644 886d423af68c175cedc3a5daf28814ca673fafdf 0\tworkplans/archived/260702-NK-WP-0006-recursive-platform-identity-security-architecture.md" + }, + { + "path": "workplans/archived/260702-NK-WP-0007-object-storage-sts-credential-vending.md", + "sha256": "d921c3d9c015027b99b30e7723acf6c71151a29927fcdbfaa124f776dcfbd64f", + "index": "100644 14689c43ac98730ad1ae2bd6aeb4648b87bee3eb 0\tworkplans/archived/260702-NK-WP-0007-object-storage-sts-credential-vending.md" + }, + { + "path": "workplans/archived/260702-NK-WP-0008-it-security-architecture-patterns-infospace.md", + "sha256": "abacfb3651e56b233587492dda5fa6cbbd08e0c1fc49681a22373e156a630b36", + "index": "100644 88120ba4a87ed785ec63a9edd58b88ec3db6a2bc 0\tworkplans/archived/260702-NK-WP-0008-it-security-architecture-patterns-infospace.md" + }, + { + "path": "workplans/archived/260702-NK-WP-0010-genesis-security-pattern-completion.md", + "sha256": "6a0b13e0f82af03e7461f4f433d7876e5f8d9a76acc8888709f4e130e13fb2e4", + "index": "100644 06daac6b1e143bc8505439566ae33eaed8d2fa9c 0\tworkplans/archived/260702-NK-WP-0010-genesis-security-pattern-completion.md" + }, + { + "path": "workplans/archived/260702-NK-WP-0012-iam-profile-specification.md", + "sha256": "1ca0aed6549d7cb2aeb490a4110fddfd0f5575832cb7e2f696724b3b4b4e79ba", + "index": "100644 2f80c1f72c65723633fceb45f88195d6a3a131fe 0\tworkplans/archived/260702-NK-WP-0012-iam-profile-specification.md" + }, + { + "path": "workplans/archived/260702-NK-WP-0013-playbook-capability-contract.md", + "sha256": "5e69691251391706d3419349bc38cb13c6d910d623b03ba788d31dbcd5a7e3bf", + "index": "100644 aa5edb5fcf8aa7d75220778c595cce455769c95e 0\tworkplans/archived/260702-NK-WP-0013-playbook-capability-contract.md" + }, + { + "path": "workplans/archived/260702-NK-WP-0014-user-engine-preparation-boundary-contracts.md", + "sha256": "d4a6e9f60ccfe666645ebf37af0d49b50e692218de46539c50b8b22ec255030a", + "index": "100644 ab0652790e1456957a70cee59b84fb7810c3e9c5 0\tworkplans/archived/260702-NK-WP-0014-user-engine-preparation-boundary-contracts.md" + } + ], + "untracked": [ + { + "path": "docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/capture-provenance.json", + "sha256": "b562f51c58c2114c2122286091dceebf3a107f0822b411cd2d8554d45d0555d5" + }, + { + "path": "docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/events.jsonl", + "sha256": "3664dc13a3ce3509ca87afc49c1b4c9095cc56cdfb6c37faa4c227556c7d3858" + }, + { + "path": "docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/receipts.jsonl", + "sha256": "060433115fb784eaf2e7bd7eab7a368d5b4439d925524b68aa4edf920b54b7aa" + }, + { + "path": "docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/snapshot.json", + "sha256": "210c0c5df320ce2c507eee764596b5928f59a99f938432de87811932d1896676" + }, + { + "path": "docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl", + "sha256": "6588a0795b8374e6ee36e822de9a3f17e1a6c0a7852804c5aeea9f8edc18905e" + }, + { + "path": "docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json", + "sha256": "a1ed7d07bae1813315fa7b2a166f3125a6c0195b2ad468568718429381d77153" + }, + { + "path": "intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md", + "sha256": "988a2df3639f9f17d7c8b01621bf3185850f248778d6ccf34cb23001f7f7690d" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/preservation-verification.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/preservation-verification.json new file mode 100644 index 00000000..51538f4a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/preservation-verification.json @@ -0,0 +1,7 @@ +{ + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "restored_generated_paths": [], + "unrelated_tracked_files_preserved": 65, + "untracked_files_preserved": 7, + "status": "M .custodian-brief.md\n M WORK-RECORDS.md\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json\n?? intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md" +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/projection.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/projection.json new file mode 100644 index 00000000..84bb559d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/projection.json @@ -0,0 +1,996 @@ +{ + "ok": true, + "status": "applied", + "repo_slug": "net-kingdom", + "commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "requests": 2, + "elapsed_ms": 11496.4, + "git": { + "upstream": "origin/main", + "behind": 0, + "ahead": 0 + }, + "identifiers": { + "schema": "repo-manager.identifier-assignment.v1", + "ok": true, + "executed": true, + "repo": "net-kingdom", + "namespace": "helixforge", + "assignments": [], + "historical_invalid_identifiers": [], + "historical_identity_collisions": [], + "files_changed": [] + }, + "receipt": { + "schema": "state-hub.repository-projection-reconcile.v1", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "derived_commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "outcome": { + "schema": "state-hub.projection-reset.v1", + "repo_slug": "net-kingdom", + "commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "status": "applied", + "counts": { + "created": 0, + "updated": 64, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 8, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 1, + "external_edges": 85 + }, + "created": [], + "updated": [ + "NET-WP-0015", + "NET-WP-0016", + "NET-WP-0017", + "NET-WP-0018", + "NET-WP-0019", + "NET-WP-0020", + "NK-WP-0001", + "NK-WP-0002", + "NK-WP-0003", + "NK-WP-0004", + "NK-WP-0005", + "NK-WP-0006", + "NK-WP-0007", + "NK-WP-0008", + "NK-WP-0009", + "NK-WP-0010", + "NK-WP-0011", + "NK-WP-0012", + "NK-WP-0013", + "NK-WP-0014", + "NK-WP-0015", + "NK-WP-0016", + "NK-WP-0017", + "NK-WP-0018", + "NK-WP-0019", + "NK-WP-0021", + "NK-WP-0022", + "NK-WP-0023", + "NK-WP-0024", + "NK-WP-0025", + "NK-WP-0026", + "NK-WP-0027", + "NK-WP-0028", + "NK-WP-0029", + "NK-WP-0030", + "NK-WP-0031", + "NK-WP-0032", + "NK-WP-0033", + "NK-WP-0034", + "NK-WP-0035", + "NK-WP-0036", + "NK-WP-0037", + "NK-WP-0038", + "NK-WP-0039", + "NK-WP-0040", + "NK-WP-0041", + "NK-WP-0042", + "NK-WP-0043", + "NK-WP-0044", + "NK-WP-0045", + "NK-WP-0046", + "NK-WP-0047", + "NK-WP-0048", + "NK-WP-0049", + "NK-WP-0050", + "NK-WP-0051", + "NK-WP-ADHOC-2026-06-14", + "NK-WP-ADHOC-2026-07-02", + "NK-WP-ADHOC-2026-08-14", + "NK-WP-ADHOC-2026-08-23", + "NK-WP-ADHOC-2026-10-03", + "NK-WP-ADHOC-2026-10-10", + "NK-WP-ADHOC-2026-10-11", + "NK-WP-ADHOC-2026-10-16" + ], + "retired": [], + "released": [], + "created_tasks": [], + "updated_tasks": [ + "NK-WP-0048-T24", + "NK-WP-0048-T25", + "NK-WP-0048-T26", + "NK-WP-0048-T27", + "NK-WP-0048-T28", + "NK-WP-0048-T29", + "NK-WP-0048-T30", + "NK-WP-0048-T31" + ], + "cancelled_tasks": [], + "created_edges": [], + "removed_edges": [], + "unresolved_edges": [ + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0001", + "reason": "missing" + } + ], + "external_edges": [ + { + "workplan": "NK-WP-0008", + "from": "NK-WP-0008", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0050", + "from": "NK-WP-0050-T03", + "target": "RPF-WP-0056-T03", + "relationship_type": "starts_after", + "from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377", + "from_task_id": "5a3aaf57-de5c-5a03-bc99-9bcf2fc5d95e", + "to_workplan_id": null, + "to_task_id": "a82762eb-bb6d-5600-89ca-a81ada2eba9f" + }, + { + "workplan": "NK-WP-0050", + "from": "NK-WP-0050-T03", + "target": "NK-WP-0050-T02", + "relationship_type": "starts_after", + "from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377", + "from_task_id": "5a3aaf57-de5c-5a03-bc99-9bcf2fc5d95e", + "to_workplan_id": null, + "to_task_id": "a9dadd07-f136-5dc7-9229-7a9dbb941f0d" + }, + { + "workplan": "NK-WP-0050", + "from": "NK-WP-0050-T02", + "target": "NK-WP-0050-T01", + "relationship_type": "starts_after", + "from_workplan_id": "06ba98d5-609e-5aaa-9bf7-4c4f4460e377", + "from_task_id": "a9dadd07-f136-5dc7-9229-7a9dbb941f0d", + "to_workplan_id": null, + "to_task_id": "64893d94-9717-5812-b8e3-7cabe095fcdc" + }, + { + "workplan": "NK-WP-0017", + "from": "NK-WP-0017", + "target": "NK-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "08398d26-cadf-44bc-97ee-67da790040e6", + "from_task_id": null, + "to_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011", + "target": "NK-WP-0003", + "relationship_type": "blocks", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": null, + "to_workplan_id": "f24cefd4-a09b-4fa1-9b25-94bf783b425e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T03", + "target": "NK-WP-0011-T02", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908", + "to_workplan_id": null, + "to_task_id": "2fe6f100-3a5d-563e-b033-7d7b846ae487" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T07", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T07", + "target": "NK-WP-0011-T04", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833", + "to_workplan_id": null, + "to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T07", + "target": "NK-WP-0011-T05", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833", + "to_workplan_id": null, + "to_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T04", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T06", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T06", + "target": "NK-WP-0011-T04", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446", + "to_workplan_id": null, + "to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T06", + "target": "NK-WP-0011-T05", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "9cb7fd93-c16e-5102-83ce-195b3aa87446", + "to_workplan_id": null, + "to_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T05", + "target": "NK-WP-0011-T04", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d845380d-3dbc-5e59-8f2b-5a4d1b32d89d", + "to_workplan_id": null, + "to_task_id": "6697a994-7343-5ea8-8b37-bc3b921e5a9b" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T09", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T09", + "target": "NK-WP-0011-T07", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e", + "to_workplan_id": null, + "to_task_id": "38998c68-29bf-50d2-8c8d-0c75184d5833" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T09", + "target": "NK-WP-0011-T08", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "d9dc4292-6b66-5797-9ff9-a1a4a556278e", + "to_workplan_id": null, + "to_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T08", + "target": "NK-WP-0011-T02", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d", + "to_workplan_id": null, + "to_task_id": "2fe6f100-3a5d-563e-b033-7d7b846ae487" + }, + { + "workplan": "NK-WP-0011", + "from": "NK-WP-0011-T08", + "target": "NK-WP-0011-T03", + "relationship_type": "starts_after", + "from_workplan_id": "1075448f-d533-5f9e-94b7-c3adfe151a07", + "from_task_id": "e5f44ddc-9645-5f61-b84c-da5ab9cccb6d", + "to_workplan_id": null, + "to_task_id": "32d1411b-10a4-5a8b-8f43-99ac46d83908" + }, + { + "workplan": "NET-WP-0016", + "from": "NET-WP-0016", + "target": "NET-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38", + "from_task_id": null, + "to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "to_task_id": null + }, + { + "workplan": "NET-WP-0016", + "from": "NET-WP-0016", + "target": "NK-WP-0012", + "relationship_type": "blocks", + "from_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38", + "from_task_id": null, + "to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "to_task_id": null + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T18", + "target": "NK-WP-0049-T33", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "141dd773-c1fa-56c1-a852-bbe2372e5125", + "to_workplan_id": null, + "to_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T18", + "target": "NK-WP-0049-T32", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "141dd773-c1fa-56c1-a852-bbe2372e5125", + "to_workplan_id": null, + "to_task_id": "cd78dbb7-a8f2-5754-8788-6eb1b7d97741" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T35", + "target": "NK-WP-0049-T34", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "1bfd1703-589f-5511-bad5-812d860e4a71", + "to_workplan_id": null, + "to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T35", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "1bfd1703-589f-5511-bad5-812d860e4a71" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T34", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T36", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "8b72334b-fc27-5707-a189-bad345eaf3bc" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T33", + "target": "NK-WP-0049-T32", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "56c8d469-7a10-5d50-a30e-95cbf3cd4756", + "to_workplan_id": null, + "to_task_id": "cd78dbb7-a8f2-5754-8788-6eb1b7d97741" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T36", + "target": "NK-WP-0049-T34", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "8b72334b-fc27-5707-a189-bad345eaf3bc", + "to_workplan_id": null, + "to_task_id": "821b3a56-6f36-5812-abc9-5a00e37cb2e7" + }, + { + "workplan": "NK-WP-0049", + "from": "NK-WP-0049-T03", + "target": "NK-WP-0049-T02", + "relationship_type": "starts_after", + "from_workplan_id": "17d315f0-829e-57c7-85cc-3a7914152804", + "from_task_id": "d68cd4d3-1ba3-530e-88f9-26ebb8dd6eac", + "to_workplan_id": null, + "to_task_id": "479d6970-6516-50f5-9436-bb5b56d58aad" + }, + { + "workplan": "NK-WP-0025", + "from": "NK-WP-0025", + "target": "NK-WP-0024", + "relationship_type": "blocks", + "from_workplan_id": "2412a251-d6b5-5f05-8082-3eb7697c51dc", + "from_task_id": null, + "to_workplan_id": "635a73d4-b3cb-5c38-af22-56a6a66beb0e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0016", + "from": "NK-WP-0016", + "target": "NK-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "2d592e18-e63d-4856-97a1-f8c3e019e150", + "from_task_id": null, + "to_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8", + "to_task_id": null + }, + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0005", + "relationship_type": "blocks", + "from_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "from_task_id": null, + "to_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19", + "to_task_id": null + }, + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0013", + "from": "NK-WP-0013", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "32a54d8e-8633-42a6-8ec1-104842c581c1", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0015", + "from": "NK-WP-0015", + "target": "NK-WP-0014", + "relationship_type": "blocks", + "from_workplan_id": "343e035d-41e1-4fc4-b209-6872e4a5fdc8", + "from_task_id": null, + "to_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c", + "to_task_id": null + }, + { + "workplan": "NET-WP-0017", + "from": "NET-WP-0017", + "target": "NET-WP-0016", + "relationship_type": "blocks", + "from_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "from_task_id": null, + "to_workplan_id": "16069174-6698-4855-ad9e-5092c8571f38", + "to_task_id": null + }, + { + "workplan": "NET-WP-0017", + "from": "NET-WP-0017", + "target": "NET-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "from_task_id": null, + "to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "to_task_id": null + }, + { + "workplan": "NK-WP-0007", + "from": "NK-WP-0007", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0007", + "from": "NK-WP-0007", + "target": "NK-WP-0005", + "relationship_type": "blocks", + "from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695", + "from_task_id": null, + "to_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19", + "to_task_id": null + }, + { + "workplan": "NK-WP-0007", + "from": "NK-WP-0007", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "3cbc81ec-7ad5-46cf-a4a0-fc5fe9873695", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NK-WP-0024", + "from": "NK-WP-0024", + "target": "NK-WP-0023", + "relationship_type": "blocks", + "from_workplan_id": "635a73d4-b3cb-5c38-af22-56a6a66beb0e", + "from_task_id": null, + "to_workplan_id": "fdb3deee-06c3-5d12-91ce-9c50b2ac0618", + "to_task_id": null + }, + { + "workplan": "NET-WP-0015", + "from": "NET-WP-0015", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NET-WP-0015", + "from": "NET-WP-0015", + "target": "NK-WP-0012", + "relationship_type": "blocks", + "from_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "from_task_id": null, + "to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "to_task_id": null + }, + { + "workplan": "NK-WP-0018", + "from": "NK-WP-0018", + "target": "NK-WP-0017", + "relationship_type": "blocks", + "from_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87", + "from_task_id": null, + "to_workplan_id": "08398d26-cadf-44bc-97ee-67da790040e6", + "to_task_id": null + }, + { + "workplan": "NK-WP-0018", + "from": "NK-WP-0018", + "target": "NK-WP-0016", + "relationship_type": "blocks", + "from_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87", + "from_task_id": null, + "to_workplan_id": "2d592e18-e63d-4856-97a1-f8c3e019e150", + "to_task_id": null + }, + { + "workplan": "NK-WP-0005", + "from": "NK-WP-0005", + "target": "NK-WP-0004", + "relationship_type": "blocks", + "from_workplan_id": "75bc472b-cc0a-48f2-afb6-62b896f7cc19", + "from_task_id": null, + "to_workplan_id": "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e", + "to_task_id": null + }, + { + "workplan": "NET-WP-0019", + "from": "NET-WP-0019", + "target": "NET-WP-0017", + "relationship_type": "blocks", + "from_workplan_id": "75d388b6-7ec1-4e1b-8c87-6ff44f953210", + "from_task_id": null, + "to_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "to_task_id": null + }, + { + "workplan": "NET-WP-0019", + "from": "NET-WP-0019", + "target": "NET-WP-0018", + "relationship_type": "blocks", + "from_workplan_id": "75d388b6-7ec1-4e1b-8c87-6ff44f953210", + "from_task_id": null, + "to_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698", + "to_task_id": null + }, + { + "workplan": "NET-WP-0018", + "from": "NET-WP-0018", + "target": "NET-WP-0017", + "relationship_type": "blocks", + "from_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698", + "from_task_id": null, + "to_workplan_id": "385de708-fd59-4bab-a4f4-28c1c476b3ea", + "to_task_id": null + }, + { + "workplan": "NET-WP-0018", + "from": "NET-WP-0018", + "target": "NET-WP-0015", + "relationship_type": "blocks", + "from_workplan_id": "800f9f16-bc44-4bbf-a771-58a630a3b698", + "from_task_id": null, + "to_workplan_id": "6b9c25e4-1008-429a-8de6-54361872c0dd", + "to_task_id": null + }, + { + "workplan": "NK-WP-0027", + "from": "NK-WP-0027-T03", + "target": "NK-WP-0027-T02", + "relationship_type": "starts_after", + "from_workplan_id": "965ad365-6b81-50a1-a2a3-2d0c1fcce0b4", + "from_task_id": "e3f0bb0f-0c85-5cb9-a5ab-75f668cd3447", + "to_workplan_id": null, + "to_task_id": "5b35b646-a525-55da-852a-6005613d42ea" + }, + { + "workplan": "NK-WP-0012", + "from": "NK-WP-0012", + "target": "NK-WP-0006", + "relationship_type": "blocks", + "from_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "from_task_id": null, + "to_workplan_id": "2eb8a5e0-4e33-4ed3-8996-a2eec3aad862", + "to_task_id": null + }, + { + "workplan": "NK-WP-0014", + "from": "NK-WP-0014", + "target": "NK-WP-0013", + "relationship_type": "blocks", + "from_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c", + "from_task_id": null, + "to_workplan_id": "32a54d8e-8633-42a6-8ec1-104842c581c1", + "to_task_id": null + }, + { + "workplan": "NK-WP-0014", + "from": "NK-WP-0014", + "target": "NK-WP-0012", + "relationship_type": "blocks", + "from_workplan_id": "b9bf56bc-7ef6-43eb-badc-fa0f4896c63c", + "from_task_id": null, + "to_workplan_id": "9b8e4afc-eb71-47d9-8750-799a082b320a", + "to_task_id": null + }, + { + "workplan": "NK-WP-0019", + "from": "NK-WP-0019", + "target": "NK-WP-0018", + "relationship_type": "blocks", + "from_workplan_id": "d2daa8b4-8ecf-4377-b382-492e653735f7", + "from_task_id": null, + "to_workplan_id": "6f75035a-e056-4eab-8fdb-00a18bacdf87", + "to_task_id": null + }, + { + "workplan": "NK-WP-0009", + "from": "NK-WP-0009", + "target": "NK-WP-0008", + "relationship_type": "blocks", + "from_workplan_id": "d4d02dbf-3974-502d-8b87-b776fc63e17e", + "from_task_id": null, + "to_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d", + "to_task_id": null + }, + { + "workplan": "NK-WP-0022", + "from": "NK-WP-0022", + "target": "NK-WP-0023", + "relationship_type": "blocks", + "from_workplan_id": "d76ddccc-00c8-548a-b141-2cd660fa38da", + "from_task_id": null, + "to_workplan_id": "fdb3deee-06c3-5d12-91ce-9c50b2ac0618", + "to_task_id": null + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T15", + "target": "NK-WP-0048-T31", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "1b825705-4848-5edc-832f-b1622cd9bdb7", + "to_workplan_id": null, + "to_task_id": "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T25", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "0de2a7d9-874d-578b-a076-f653fe42e30b" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T29", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "129db41d-4560-5e20-9f3b-c3c91e0a5601" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T24", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "1a3931ee-3b5f-52fc-9295-4073804f4bd2" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T28", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T30", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "6f6073ac-52dd-5b70-8874-6d76d4c072e4" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T26", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "94b3cbf0-c908-5ac6-b0db-67385c3e021c" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T16", + "target": "NK-WP-0048-T27", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "to_workplan_id": null, + "to_task_id": "e56365f8-29b5-5c89-a329-8115f9f3b2a7" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T07", + "target": "NK-WP-0048-T06", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7", + "to_workplan_id": null, + "to_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T09", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "0ee12dfc-8260-5038-bd39-cc7fb4113be0" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T15", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "1b825705-4848-5edc-832f-b1622cd9bdb7" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T16", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "200bf500-5084-5ae7-be26-3033fe1f296e" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T05", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "3d987d3d-0797-538b-86e4-51185a327ae2" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "MASON-WP-0010-T03", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "56cea6be-c613-586c-80cd-125340b374da" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T12", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T20", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T17", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "8c3e4baa-a218-5996-af9e-fe1153a46dac" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T08", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "8e519082-dc8b-5e46-b660-03d03e55dcf6" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T18", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "9c45da71-fb4c-5f20-9065-90f448f37c64" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T13", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "b264c10b-565c-5d29-b788-03cf5fbd2883" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "WARDEN-WP-0044-T03", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T06", + "target": "NK-WP-0048-T14", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "to_workplan_id": null, + "to_task_id": "fce95440-a25b-5f27-8ef3-a3d182099113" + }, + { + "workplan": "NK-WP-0048", + "from": "NK-WP-0048-T14", + "target": "NK-WP-0048-T20", + "relationship_type": "starts_after", + "from_workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "from_task_id": "fce95440-a25b-5f27-8ef3-a3d182099113", + "to_workplan_id": null, + "to_task_id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8" + }, + { + "workplan": "NK-WP-0010", + "from": "NK-WP-0010", + "target": "NK-WP-0008", + "relationship_type": "blocks", + "from_workplan_id": "f4faf8b4-ae57-40cf-a881-6fe66ca6ad74", + "from_task_id": null, + "to_workplan_id": "053c6d96-9396-40c9-a2e5-c36531e7810d", + "to_task_id": null + } + ], + "refused": [], + "notes": [] + } + } +} diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/projection.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/projection.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/restore_verify.py b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/restore_verify.py new file mode 100644 index 00000000..0a75afd4 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/restore_verify.py @@ -0,0 +1,23 @@ +from pathlib import Path +import json,hashlib,subprocess +p=Path(__file__).resolve().parent;r=Path('/home/worsch/net-kingdom') +pre=json.loads((p/'preimage.json').read_text());head=json.loads((p/'commit-receipt.json').read_text())['commit'] +def git(*a):return subprocess.check_output(['git','-C',str(r),*a]).decode().strip() +assert git('rev-parse','HEAD')==head,'HEAD changed concurrently; stop' +restored=[] +for row in pre['files']: + name=row['path'] + if name=='workplans/NK-WP-0048-security-case-types.md': + assert (r/name).read_bytes()==(p/'candidate.md').read_bytes();continue + if name in ('.custodian-brief.md','WORK-RECORDS.md'): + if (r/name).read_bytes()!=(p/'backup'/name).read_bytes(): + (p/(name.lstrip('.')+'.after-consistency')).write_bytes((r/name).read_bytes()) + (r/name).write_bytes((p/'backup'/name).read_bytes());restored.append(name) + if git('ls-files','--stage','--',name)!=row['index']: + mode,oid,stage,path=row['index'].split();assert stage=='0';git('update-index','--cacheinfo',mode,oid,path) + assert hashlib.sha256((r/name).read_bytes()).hexdigest()==row['sha256'],name + assert git('ls-files','--stage','--',name)==row['index'],name +for row in pre['untracked']: + assert hashlib.sha256((r/row['path']).read_bytes()).hexdigest()==row['sha256'],row['path'] +(p/'preservation-verification.json').write_text(json.dumps({'head':head,'restored_generated_paths':restored,'unrelated_tracked_files_preserved':len(pre['files'])-1,'untracked_files_preserved':len(pre['untracked']),'status':git('status','--short')},indent=2)+'\n') +print(json.dumps({'head':head,'restored':restored,'preserved':True})) diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/tasks-after.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/tasks-after.json new file mode 100644 index 00000000..1c51b79a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/tasks-after.json @@ -0,0 +1,715 @@ +[ + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "930ac3e9-20ec-5473-98cd-3fe8fddb730f", + "record_id": "NK-WP-0048-T01", + "flavor": null, + "title": "Tasks", + "description": "### T01 \u2014 Accept the terminology and the canon\n\nFounder review of `canon/standards/security-case-types_v0.1.md`, including its\nsection 10 open questions. On acceptance the canon status goes from `proposed`\nto `accepted`.\n\n2026-10-08: accepted by the founder (in session). Canon status `accepted`;\nsection 10's open questions are carried forward to the first type (T04, T05).", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.533372Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "19c25d96-a55c-58f0-9168-3fdbc4955fc2", + "record_id": "NK-WP-0048-T02", + "flavor": null, + "title": "T01 \u2014 Accept the terminology and the canon", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.572717Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "e487975b-73b1-516c-971d-da4cbbb93063", + "record_id": "NK-WP-0048-T03", + "flavor": null, + "title": "T02 \u2014 Inventory the de-facto types already built by hand", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.614667Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "2fffe112-7d55-5680-9952-78d74f44f87f", + "record_id": "NK-WP-0048-T04", + "flavor": null, + "title": "T03 \u2014 Schema, template and validator", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.724272Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "3d987d3d-0797-538b-86e4-51185a327ae2", + "record_id": "NK-WP-0048-T05", + "flavor": null, + "title": "T04 \u2014 Define the first type: browser OIDC relying party", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.833385Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "record_id": "NK-WP-0048-T06", + "flavor": null, + "title": "T05 \u2014 Acknowledge the type through informed-decision", + "description": null, + "status": "wait", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status.", + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": "external", + "created_at": "2026-10-08T20:17:41.935191Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7", + "record_id": "NK-WP-0048-T07", + "flavor": null, + "title": "T06 \u2014 First KnownSecurityCase: Forgejo", + "description": null, + "status": "wait", + "priority": "medium", + "assignee": null, + "due_date": null, + "blocking_reason": "The next types follow once the first one is proven end to end.", + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": "external", + "created_at": "2026-10-08T20:17:42.037158Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "8e519082-dc8b-5e46-b660-03d03e55dcf6", + "record_id": "NK-WP-0048-T08", + "flavor": null, + "title": "T07 \u2014 Queue the next types", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:46:05.696360Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "0ee12dfc-8260-5038-bd39-cc7fb4113be0", + "record_id": "NK-WP-0048-T09", + "flavor": null, + "title": "T08 \u2014 identity-provisioner: ensure service-access groups", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T21:03:35.764154Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "4b06ca97-c1e4-5680-bf22-ab75ab1646a4", + "record_id": "NK-WP-0048-T10", + "flavor": null, + "title": "T10 \u2014 Source compatibility checker for Mason and Warden evidence", + "description": "Completed 2026-10-16 in published source `c31d21ba`. The read-only\n`check-evidence` command checks the actual Mason structural request and Warden\njournal source protocols against an exact version/render. Independent source\nreview passed 103 tests at that checkpoint. It always reports native completion\nfalse and execution prohibited. Fixtures and local journal fingerprints do not\nprovide authenticated owner/native evidence. This records completed source scope\npreviously described under T06; it does not duplicate native acceptance credit.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:29:35.408457Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "ef2a0119-f59e-5e45-9519-115da9df7b85", + "record_id": "NK-WP-0048-T11", + "flavor": null, + "title": "T11 \u2014 Validate the optional machine-descriptor pin contract", + "description": "Completed 2026-10-16. Reproduced the JSON schema rejecting a valid\n`execution_descriptors` map while the Python validator accepted malformed maps.\nThe schema now supports the existing Warden optional exact-byte pin map; semantic\nvalidation binds keys to declared steps, bounds the map and checks digest shape.\nThe direct evidence API refuses malformed maps before accessing them. Twenty-one\nnew checks and all 127 affected source checks passed. No current type/case bytes,\nstatus, owner descriptors or native acceptance changed. Source evidence and\npublication receipt: Custodian\n`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:29:35.501110Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd", + "record_id": "NK-WP-0048-T12", + "flavor": null, + "title": "T12 \u2014 Prepare NetKingdom-owned attended step adapters", + "description": "Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env,\nconfig verification and restart/readiness steps around the existing helpers.\nNext action: identify exact current entry points and their revision/readback\ncontracts, then implement fixed source-pinned callbacks or receipt-only attended\nsteps. Completion requires positive/refusal checks, exact target/source pins and\nuncertainty behavior without acquiring user-engine runtime identity or exposing\nregistry/client secrets. Existing caller/session, cluster/issuer, CAS/readback\nand status-only output boundaries remain mandatory.\n\n2026-10-16 task refinement: the original broad adapter scope is split into T13\n(completed registration source candidate), T14 (group creation provenance), T15\n(durable registration recovery) and T16 (descriptor/artifact selection and owner\nreview). This preserves T12's task ID and Hub UUID while giving each remaining\noutcome an owner and completion gate. T06 now names every source dependency;\nits native/reference lifecycle acceptance scope remains unchanged. Completion\nof any source task supplies no Warden, Platform or Apps assent and no native\nexecution authority.", + "status": "todo", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:29:35.603696Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "b264c10b-565c-5d29-b788-03cf5fbd2883", + "record_id": "NK-WP-0048-T13", + "flavor": null, + "title": "T13 \u2014 Prepare the fixed registration adapter source candidate", + "description": "Completed 2026-10-16, source implementation and synthetic fixture proof only.\nAdded an injection-only adapter around actual `keycape-client.run`: fixed\ncase/principal/human/lane/source/acceptance binding, existing CAS/readback,\nmetadata-only owner evidence and per-attempt parent-session revocation boundary.\nThe descriptor source composition pins the exact checker executable digest\nalongside adapter/helper source. Creation-only rollback authenticates its\ncreation receipt and refuses changed postcreation UID/resourceVersion;\npre-existing clients confer no rollback authority. Missing readback, evidence,\nrevocation or process-local creation provenance cannot become retry-safe success.\n\nEvidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/`\nR3 candidate, explicit patch and postimages, retained R1 rejected custody finding\nand R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks\nuse actual Warden kernel admission/dispatch/rollback with published Warden\n`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust,\ndescriptor/type/case and Kubernetes objects remain synthetic fixtures. This\nrecords a completed source deliverable, not independent review, publication,\ntype acknowledgment, exercised procedure, identity admission or native browser\nacceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority.\n\nSource publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently\nreviewed by the Custodian coordinator before integration. The required initial\nprojection and ordinary UUID writeback both completed with zero failures and\nzero automation errors. Generated-only brief commits were refused by the normal\nhook; exact pre-existing staged/working brief bytes were restored. The separate\ncanonical integration receipt retains those refusals and projection evidence.\nNative admission and all other-owner acceptance remain open.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:47:17.544924Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "fce95440-a25b-5f27-8ef3-a3d182099113", + "record_id": "NK-WP-0048-T14", + "flavor": null, + "title": "T14 \u2014 Admit governed group rollback against native owner boundaries", + "description": "Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to\nan admitted dedicated CNPG consumer through the existing `rapp-postgres`\ndatabase-per-consumer contract. Identify the actual deployment/package and\ncustody owner, admitted placement/capacity and role/renewable lease/TLS target;\nimplement the independently retained exact content checkpoint and authoritative\ntenant/case/source/principal callbacks before enabling governed group operations.\nThe injection-only backend and fixture callbacks do not supply production scope\nor authorization. Existing service tokens, caller-supplied JSON and provenance\nreceipts confer no new grant. The default production endpoint remains disabled\nwithout the trusted integration. SQLite remains local/disposable only; any\nproduction exception requires explicit scoped governance under ADR-0015.\n\n2026-10-17 owner-integration refinement: T23 records the completed source/package\nplacement review, distinct from T21's source/disposable-SQL proof. Its dedicated\n`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted\npackage or existing repository. The full six-consumer rapp-postgres renderer\naccepts the inert candidate on platform-pg-2 as its third of four declared\nconsumers; first-cell placement is refused as a fifth. Neither the estimated\nfour-consumer ceiling nor source requests establish native headroom or backup.\n\nNext independent owner contracts: NetKingdom/package/substrate owners accept\nexact deployment/rollback fate; rapp-postgres admits the dedicated consumer,\nroles, five-connection budget and actual backup/restore; Platform admits exact\nrenewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL,\nbroker and checkpoint paths without assuming a narrow policy subtracts additive\nsame-namespace access. Protected caller owners must provide authoritative\nGroupJournalBinding construction and current authorization; an independent\nexisting holder must authenticate and atomically advance the exact content\ncheckpoint, not a counter or ordinary Audit receipt. Directory/runtime owners\nmust select and prove either an exclusive all-writer maintenance barrier or\nprovider-supported CAS/generation, including established sessions, lifecycle\ncascades and remove/readd ABA. No source review establishes those owner facts.\n\nThe default PostgreSQL activation and destructive native rollback remain refused.\nStorage/lease acceptance can be proposed separately from LDAP mutation only after\nexact owner source/target/custody/verification/recovery and cost/risk review;\nState Hub guard approval grants none of it. Source-only integration review and\nnative acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's\nexisting status, UUID, dependencies and native completion gate are retained.\n\n2026-10-17 refinement: T20 retains the historical durable SQLite source and\nactual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and\nbounded real disposable SQL compatibility proof. T14 remains `progress` for\nnative CNPG admission/recovery, protected owner integration and exclusion of all\nexternal LDAP writers. Separate source publication/projection and independent\ncleanup receipts belong to coordinator follow-through; no missing native assent\nis represented as received.\n\nCompletion still requires independently reviewed/admitted ADR-0015-conforming\nCNPG storage (or an explicit governed exception), backup/lost-store recovery and\nmigration of legacy requests; a native\nwriter-isolation/fencing or provider CAS/version contract that closes external\nwriter races and remove/re-add ABA; exact case/source/principal and current\nnative object bindings for protected callbacks; and actual per-attempt evidence,\nrevocation and readback/refusal verification through the existing owner paths.\nOnly independently retained confirmed creations may be undone. Every\npre-existing/shared group and membership must remain preserved; unresolved\nintents never expire into retry authority. The disposable fixture supplies none\nof these native custody, authorization, owner-acceptance or production claims.", + "status": "progress", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:47:17.614347Z", + "updated_at": "2026-10-17T02:17:04.606534Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "1b825705-4848-5edc-832f-b1622cd9bdb7", + "record_id": "NK-WP-0048-T15", + "flavor": null, + "title": "T15 \u2014 Define authenticated recovery for lost registration process state", + "description": "2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional\npaired `holder`/`observe` capabilities and installs `reconcile_original`; the T31\nfixtures now run through the adapter's own path. Rollback after restart is still\nrefused, and there is no add replay. This changes `keycape-registration-adapter.py`,\nso the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json`\nis superseded. The new revision has no independent review yet and must not be\npinned on the strength of the old one.\n\n2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner\nselects the real authenticated original-attempt holder, and the T13 adapter\ngains the holder capability, `reconcile_original` and reconcile-aware `verify`\ndescribed in T31.\n\nOwner: NetKingdom for registration state, with Warden owning kernel evidence\nadmission. T22 records the completed source review and exact existing-seam gaps;\nT15 retains implementation and authenticated restart/lost-receipt acceptance.\nNext action: select and admit the actual owner publisher/authenticator and bounded\noriginal-attempt readback contract for the merged holder/observe adapter seam.\nWarden's original-intent persistence/interface (`a45cf475`) and additive validation\n(`117fec9241e7b985dfb187b992e173d0a5dc3700`) are now published and projected.\nThe local Journal retains admitted nonsecret original context, validates original\nactor/attempt and metadata linkage to the original running receipt, and supplies\nit separately from the new reconciliation attempt. This local consistency is not\nindependent original-holder authorization or creation provenance; neither the\njournal nor a current matching registry entry reconstructs creation authority.\n\nThe owner contract must bind the original Invocation, case/type/descriptor/catalog/\nacceptance pins, source, principal and admitted tenant/realm provenance, separately\nfrom the new reconciliation attempt and its independently revoked parent session.\nUse an existing authenticated holder/readback; Audit Core's custody API is a\ncandidate only until its actual registration source/class, load-bearing emission\ncontract and separate scoped reader are admitted. No new broker or receipt trust\nroot is proposed. A missing original record, ambiguous publication, NO_CHANGE,\nchanged native revision or lost readback stays uncertain; no add replay or undo.\n\nCompletion requires restart/lost-receipt fixtures against the selected actual owner\ncontract: independently authenticated original created-only provenance, original\nexact client entry and unchanged current postcreation UID/resourceVersion before\nany undo, with wrong attempt/case/source/principal and unavailable-holder refusals.\nThe merged T13 adapter exposes reconciliation only through paired injected\nholder/observe capabilities and still refuses post-restart rollback. Existing\nsource review, JSON receipts and synthetic trust do not satisfy independent\nholder authorization/provenance or native admission, or authorize attended recovery.\nExact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json.\n\n2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface\nis not published \u2014 ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03\ncommit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt\nfixture or readback seam was added this session; the work is deferred behind\nT16's decomposition under the same token budget. Remains todo, gated on Warden\npublishing the reviewed lookup/context interface and on owner selection of the\nauthenticated original-attempt holder.\n\n2026-10-17 current-source correction: the earlier same-day `d97652a` lookup\ncheckpoint and T31 adapter-next-action note above are historical and superseded\nby the published Warden interface/validation and merged `061461e` adapter seam.\nThe 54 focused Warden checks establish source behavior only. Current admission\nleaves tenant/realm empty; no admitted tenant/realm provenance is inferred.\nT15 remains wait for actual independently authenticated owner-holder/readback\nselection, independent review of the changed adapter, and native restart/lost-\nreceipt acceptance. Existing task metadata, owner gates and historical reviews\nare unchanged.", + "status": "wait", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source.", + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": "external", + "created_at": "2026-10-16T19:47:17.734492Z", + "updated_at": "2026-10-17T11:34:33.270999Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "record_id": "NK-WP-0048-T16", + "flavor": null, + "title": "T16 \u2014 Select and review the actual machine descriptor composition", + "description": "2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no\nfindings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose\noption A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`):\n1.3 with execution descriptors for all seven steps. Owner reviews for A are open\nin the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom,\nrailiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet\nsupersedes the r1 composition pointer: T16's exact composition is now\n`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`.\n\n2026-10-17 successor candidate prepared (source only, `proposed`):\n`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus\nan `execution_descriptors` map of the seven composed descriptors, with examples\npinned to `@1.3` and no acknowledgment. ensure-groups, hand-over,\ndeliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c).\n`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type:\nthe proposed type is refused; an acknowledged copy is refused because ops-warden\npins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to\n1.2's fingerprint) all seven are admitted. This is a major change under canon\nsection 6. Next: r4 source review of the changed adapters and the map, then owner\nreviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human\nquestion and the owner-run steps question, then the founder's Decision Memo.\n\nOwner: NetKingdom for descriptor/type source and exact checker artifact; Warden\nindependently owns global provenance/admission. T22 records the completed seven-step\nsource1.2 inventory and regenerated register-client descriptor using the delivered\nchecker and current sealed-snapshot source; T16 retains exact owner review,\ncomplete descriptor composition and admission. Next action: bind actual native\npublisher/authenticator, original/current session verification and revocation,\nexecutable storage/custody and config pins, then obtain the six missing fixed\nowner descriptors/adapters for ensure-groups, mint-secret, deliver-secret,\nbind-env, deliver-consumer-secret and hand-over. The inventory identifies each\nexisting owner seam, exact missing contract and created-only rollback requirement;\nit is not an executable bundle. T14 and T15 remain independent prerequisites.\n\nReview the dedicated consumer source1.2 composition only through the existing\nexplicit version-root selection. Keep canonical1.1/default and its accepted case\nunchanged, and the source-only acknowledged1.2 reference case proposed without\nacceptance fields. New descriptor bytes require a reviewed exact successor\nrevision/version under canon section6; they do not inherit historical source\nacknowledgment. Changed authentication/materialization/trust needs the prescribed\ninformed-decision acknowledgment. Do not insert a partial descriptor map or use\n--allow-unimplemented preparation as ordinary case/native admission. The registry\nhelper reports restart_required; separately review quiet-window restart/readiness\nand actual consumer verification rather than infer activation from registry CAS.\nExact composition: Custodian native-registration-r1/descriptor-composition.json.\nCompletion requires separate owner source review of exact machine descriptors,\nartifact/source/config pins, normal type/version admission and independent\nevidence-verification boundaries. Warden d97652a refreshes producer validator\npins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any\nmachine descriptor into an acknowledged type is a separately reviewed source\nchange. Platform owns structural/mint/broker/delivery evidence; Apps owns actual\nconsumer login/group enforcement. Neither their assent nor native acceptance is\nsupplied by this task.\n\n2026-10-16 source checkpoint: T19 below extracts the completed per-attempt\nexecutable snapshot implementation and reviewed fixture proof. T16 retains\nnative executable storage/custody, exact owner evidence/session callbacks,\ncomplete descriptor composition and ordinary type/version admission. The\nsuccessor descriptor/source pins are proposed only; the earlier descriptor\nhistory is preserved. T14 group provenance and T15 durable restart/recovery\nremain independent open requirements. No native acceptance is counted here.\n\n2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The\nT13 registration adapter alone is 217 source and 317 test lines plus an\nindependent review; six further owner descriptors/adapters cannot be completed\nwithin one per-task token budget (soft 8000 / hard 10000), so the task was\nstopped below 50% and is decomposed rather than pushed through. Proposed split,\none source-candidate task each with positive/refusal fixtures, canonical1.1\ndefault unchanged and no --allow-unimplemented admission: (a) ensure-groups,\nbound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret,\nPlatform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret;\n(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition\nover the reviewed set. Each candidate needs its owner's source review before\npinning; none is authored here as owner-accepted. Status stays todo.", + "status": "todo", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:47:17.849814Z", + "updated_at": "2026-10-17T09:41:04.077127Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "8c3e4baa-a218-5996-af9e-fe1153a46dac", + "record_id": "NK-WP-0048-T17", + "flavor": null, + "title": "T17 \u2014 Build and exercise the exact offline config-checker artifact", + "description": "Completed 2026-10-16, isolated artifact/source fixture proof only. Built the\nexisting `key-cape/src/cmd/keycape` command from published clean source revision\n`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree\n`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0,\nCGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline\nread-only module selection. The full retained closure pins 108 source files,\n9876 toolchain files and ten used module source directories. Three isolated\nbuild locations produced identical 13633409-byte artifacts:\n`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`.\n\nEight disposable checks use the actual executable and maintained helper/kernel,\nwith complete synthetic configs and a minimal environment: add/created-only\nremoval, normal pinned producer admission, rollback, lost-readback uncertainty,\nstale revisions, substituted checker refusal, invalid startup config and missing\nsecret-env refusal. No checker stub or producer-pin override remains. The\nartifact-bound descriptor proposal is\n`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`.\nEvidence: Custodian\n`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`.\nThe first two build attempts succeeded but their closure collection failed;\nboth failures are retained, as is refusal of the older incomplete synthetic\nfixture. No production config, credentials or cluster mutation was used.\n\nThis extracts completed build/fixture scope from T16 without claiming KeyCape\nowner endorsement, native checker selection/storage custody, acknowledged type\nintegration, native evidence/session callbacks or attended browser acceptance.\nT16 retains exact owner review and composition/admission scope. The attended\nregistration procedure remains unexercised.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T20:30:03.492101Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "9c45da71-fb4c-5f20-9065-90f448f37c64", + "record_id": "NK-WP-0048-T18", + "flavor": null, + "title": "T18 \u2014 Make checker input and executable selection deterministic", + "description": "Completed 2026-10-16, isolated source correction and real-artifact fixture proof\nonly; independent integration/publication remain separately recorded. KeyCape\nsource review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and\n`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper\ninherited ambient environment, allowing valid supplied configuration to be\nrejected or invalid supplied URL configuration to be admitted. A bare relative\nchecker filename could also be replaced by PATH lookup after the adapter had\nhashed the selected local file. All three counterexamples are retained against\nthe unchanged predecessor.\n\nThe existing helper now resolves the selected checker file to an absolute path\nand passes an explicit empty subprocess environment. Three opt-in real-checker\nregressions verify both URL-verdict directions and relative-file selection under\na non-checker PATH decoy. All 39 affected checks pass with the exact supplied\nartifact, as do 11 real-artifact helper/kernel scenarios. Source evidence:\nCustodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`.\nHistorical descriptors remain retained; the helper correction changes successor\nsource-composition pins and requires their separate review/admission. An absolute\npath is not immutable custody and does not eliminate file replacement races;\nT16 retains native executable storage/custody and callback/type admission.\nNo production configuration, credentials or cluster mutation was used.\n\nSource integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent\nCustodian source review in browser-checker-environment-r2/coordinator-review.json.\nThe ordinary protected projection pass assigned T17/T18 UUIDs with zero failures\nand zero automation errors; generated-only brief commit refusal is retained.\nThe exact prior staged/working brief bytes were restored. Final source/publication/\nprojection evidence is retained in Custodian browser-checker-environment-r2/\ncanonical-integration.json. These source receipts do not admit native executable\ncustody, owner callbacks, acknowledged type changes or browser identities.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T20:30:03.587086Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "f50fdcd1-00be-5c3c-be0c-345aa2126032", + "record_id": "NK-WP-0048-T19", + "flavor": null, + "title": "T19 \u2014 Bind checker execution to verified immutable attempt bytes", + "description": "Completed 2026-10-16, independently reviewed source implementation and synthetic\nfixture proof only. The registration adapter now opens a bounded regular checker\nartifact without following its terminal symlink, verifies the reviewed digest,\nand snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux\nmemfd before helper access. Live and proposed config checks execute that same fd\nthrough procfs with the existing empty environment; the fd closes on completion\nor failure. Unsupported snapshot/seal/exec policy refuses through the existing\nparent-session revocation and uncertainty boundaries. The earlier unbounded\npre-snapshot path read was removed during independent review; actual adapter\nsymlink/FIFO/oversize refusal tests verify helper access never starts.\n\nEvidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`,\nincluding exact base/preimages/postimages/patch, proposed descriptor, coordinator\nsource review and canonical integration receipt. Independent Custodian checks\npassed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios;\nthe sets overlap and are not separate acceptance counts. Existing published\nWarden d97652a producer pins remain unchanged. The exact KeyCape artifact remains\n`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`.\nThe predecessor path-replacement refusal and the first expanded reopened-fd test\nfailure are retained; the latter fixture was corrected to restore write mode\nbefore independently proving seals block reopened-fd writes and shared mmap.\n\nThis source task adds no native owner custody, callback, evidence trust, type\nacknowledgment, credential access, consumer sign-in or rollback acceptance.\nExecutable memfds, Linux seals and usable procfs are explicit runtime dependencies\nand still require native admission. This is an extracted source outcome from\nT16, not completion of T16; T12/T14/T15 and all external-owner gates remain open.\n\n\nPublication verified by the coordinator after the source worker usage limit:\n`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision.\nAll five canonical source hashes match the reviewed packet. Normal synchronization\ncompleted with zero failures/automation errors; all 19 task statuses match the\nprojection. T19's assigned UUID is retained. The original staged and working brief\nbytes were restored and independently compared. The exact source, projection and\npublication checks are in Custodian browser-resume-r1/canonical-integration.json.\nNative acceptance remains open under T16 and the other named owner tasks.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T21:23:29.761635Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8", + "record_id": "NK-WP-0048-T20", + "flavor": null, + "title": "T20 \u2014 Prepare durable group provenance and prove disposable LLDAP compatibility", + "description": "Completed 2026-10-17, source candidate preparation and bounded synthetic\ncompatibility proof only. This extracts evidenced implementation/fixture scope\nfrom T14; T14 retains native storage, writer isolation, protected callback and\nowner-acceptance gates. Source integration/publication and State Hub projection\nare pending the coordinator's separate receipts; no revision or task UUID is\ninvented here.\n\nThe existing identity-provisioner group ensure/revoke interface now has a\nreviewed candidate for a bounded owner-private SQLite journal, proven only in\nlocal/disposable use. The initial storage instructions wrongly suggested a native\npersistent-volume path; source review against accepted ADR-0015 corrected them\nbefore publication. Production SQLite-on-a-PVC is not admitted: T14 retains\nCNPG/rapp-postgres storage admission or a separately governed exception.\nThe local journal saves\ncomplete prestate and intent before each attempted directory mutation, inventories\nonly confirmed new groups/memberships, binds original ensure idempotency and\ncorrelation to the exact request digest, and refuses unsigned caller inventories\nor legacy broad revoke requests. Replays never recreate removed state. Missing,\nreplaced or untrusted storage and missing intent rows stop execution; definite\nbefore-mutation refusals retain evidence without stranding unrelated valid work.\nInterrupted writes/readback remain unresolved across reopening. Revoke validates\nall targeted native ids and complete member postimages before destructive work,\nremoves only confirmed creations and never deletes a preexisting empty group.\nServer/configuration wiring, image-copy closure and owner storage/request\nmigration instructions are included. The existing endpoint's caller\nauthentication remains the existing boundary, not a new grant.\n\nThe candidate was prepared against NetKingdom\n`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest\n`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`.\nThe independent Custodian review passed all 65 provisioner checks. The earlier\n62-check candidate, two independent-review findings and subsequent fixes remain\nretained rather than overwritten. Seven additional fixture-plan checks exercise\nthe preparation boundary; these counts overlap the same candidate and do not\nmeasure task-completion percentage or independent acceptance.\n\nA separately reviewed bounded fixture then exercised the exact candidate against\ncached pinned disposable LLDAP and Python images, through actual GraphQL. All 14\nclosed compatibility/containment oracles passed: exact creation inventory,\nseparate-process SQLite reopening/replay/revoke, complete preexisting directory\npreservation including empty/shared groups and memberships, refusal of shared\nmembership drift, retained uncertainty after a lost response following a real\naddition, and refusal of unknown/cross-case receipt bindings. The fixture used\nonly newly generated synthetic users/passwords/key material in private tmpfs,\nwith a network-none anchor and shared loopback helper, no host ports/mounts,\nno image pulls and no production or actual credential contact. The coordinator's\nsafe receipt reports both exact owned containers absent after cleanup; final\nindependent cleanup verification remains the coordinator's integration duty.\n\nEvidence: Custodian\n`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact\nsource base/pre/postimages, patch/manifest, independent review and retained\nrefinements. Native fixture manifest\n`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`;\nretained safe result `native-fixture/execution-result.json` (original\n`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies\nno native owner assent, production rollout, protected session/evidence callbacks,\nordinary type admission or consumer login proof. LLDAP has no conditional\nmembership mutation or generation token here: exact readback cannot exclude\nABA or a concurrent external writer, so T14 remains open.\n\n2026-10-17 verified source follow-through: source commit `4f65e41` passed normal\nlocal scanning and all 65 canonical provisioner tests. The actual disposable\nLLDAP receipt passed all 14 oracles; independent verification on the pinned\ndaemon found both exact names and the run label absent. Source integration\nincludes the ADR-0015 documentation correction; the journal remains local-only.\n\nT14 source preparation is underway. Twenty-three pinned public sources identify\nNetKingdom implementation/reconciliation, Platform custody/database governance\nand rapp-postgres consumer provisioning; the inspected runtime binding does not\nassign identity-provisioner a package. The existing User Engine runtime token\nand pod route are not a protected-case caller lane. The native proposal preserves\nthe same journal seam on a dedicated CNPG consumer and requires authoritative\nscope binding, admitted role/lease/placement/recovery and all LDAP writers fenced.\nThe declared exclusive maintenance window is not mechanical exclusion of WebUI,\nhelper or lifecycle writers. Capacity placement and EUR cost/risk remain owner\ndecisions. No package, database, credential, rollout or owner assent was created.\nCustodian evidence: group-provenance-r1/native-storage-proposal-r1; final source,\nprojection and cleanup review: resume-integration-r1/publication-verification.json.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T22:35:33.755778Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "afe78e4d-408a-516a-ab92-e212e8a2d11d", + "record_id": "NK-WP-0048-T21", + "flavor": null, + "title": "T21 \u2014 Implement the PostgreSQL journal and prove disposable SQL compatibility", + "description": "Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL\ncompatibility only. T14 retains native storage, custody, callbacks, authorization,\nall-writer isolation and production admission. Coordinator source publication and\nprojection remain separate follow-through; no source revision or task UUID is\ninvented here. T20 remains the historical local SQLite/actual LLDAP proof.\n\nThe eight-path source candidate adds an injection-only PostgreSQL implementation\nbehind the existing owner journal seam, with explicit trusted tenant/directory/\ncase/source/principal/epoch binding and independent continuity callbacks. It\npersists bounded intent/created-only receipts, exact-one version updates, durable\nactive fences, and a content-bound journal chain. SQL commits precede independent\nexact-marker acknowledgment; lost replies/checkpoints poison continuation and\nretain uncertainty. A counter-only checkpoint is explicitly insufficient for\ndivergent clones. Migration does not create database/schema/credential, admit an\nepoch or clear quarantine; production HTTP activation is absent pending trusted\ncallbacks. Pinned psycopg package/image-copy closure is included, without claiming\nan image build or native dependency admission.\n\nCandidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch\n`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`.\nAll 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two\nsource-backed snapshot regressions. Counts overlap the same source and fixture;\nthey are not additive native acceptance or completion percentages.\n\nThe independently reviewed R5 fixture ran the actual SQL migration/backend and\nexisting provisioner path against cached pinned disposable PostgreSQL. Directory\noperations deliberately used the existing synthetic LLDAP test double. All\nmanifest-declared phase oracles passed: creation-only inventory and DDL refusal;\nseparate-process replay/revoke with complete preexisting/shared visible-state\npreservation; foreign-context refusal; concurrent exactly-once reservation and\nstale version refusal; actual database fork with divergent bodies at the same\ncounter and fresh-object rejection of the losing exact checkpoint; retained\nuncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/\npg_restore rollback refusal against the independently retained fixture checkpoint.\nThe checkpoint is fixture-only, not an admitted native owner evidence holder.\n\nOnly generated credentials/state were used, with two nonroot/read-only containers\non private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts,\nno pulls and no production contact. The safe receipt reports both exact owned\ncontainers absent, with no daemon uncertainty. Independent final pinned-daemon\ncleanup verification remains coordinator follow-through.\n\nFailed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure\nsubphase is unknown; R3 exposed a real backend bug where replacing nested receipt\nobjects lost the created ID held by ensure, fixed by acknowledging only the backend\nversion after commit/checkpoint. R4 verified that fix through actual SQL bootstrap,\nthen exposed fixture snapshot aliasing and empty deleted-group backing entries;\nsource-backed regressions corrected the snapshot without weakening complete\nvisible-prestate equality, and reject nonempty orphan membership. These refinements\nbelong to one trial, not extra native acceptance successes.\n\nCustodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`;\nexact source/pre/postimages and retained predecessors; native R5 manifest\n`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`;\n`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt\n(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native\nstorage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected\ncallbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production\nendpoint activation is claimed. Those remain live T14 work.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T23:38:06.135472Z", + "updated_at": "2026-10-16T23:38:23.460790Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1", + "record_id": "NK-WP-0048-T22", + "flavor": null, + "title": "T22 \u2014 Establish registration recovery source gaps and complete descriptor review inventory", + "description": "Completed 2026-10-17: source-only owner-contract review and complete review\ninventory. Inspected the actual registration adapter, Warden Journal/Executor,\nNetKingdom evidence compatibility checker and Audit Core custody/read surfaces.\nNo existing admitted holder/readback supplies authenticated original-attempt\nrecovery: payload/creation are process-local, persisted evidence references are\nhashed, and reconcile receives a new attempt without original context. The exact\nfour-owner contract requirements and non-replayable missing/ambiguous cases are\nrecorded; no recovery callback, publisher, credential grant or native trust root\nwas installed. T15 retains implementation and actual acceptance.\n\nThe complete source1.2 inventory maps all seven follow-through steps to their\nexisting owners, missing exact contracts and created-only rollback requirements.\nThe actual maintained adapter regenerated the sole existing register-client\nmachine descriptor against the delivered 13,633,409-byte checker and current\nsealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode\nround-trip passed; six owner descriptors remain absent. Actual source commands\nrender canonical1.1 and explicitly selected1.2 with execution.permitted false;\nordinary1.2 case admission still refuses its acknowledged/unimplemented type.\nCanonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were\nadded or rerun, no native configuration or effect occurred, and no owner assent\nor complete machine bundle is claimed. T16 retains native executable custody,\ncomplete exact descriptors, independent source review and normal version/type\nadmission. This extracted outcome supplies no native gate completion credit.\n\nEvidence in the-custodian:\n`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially\noriginal-attempt-review.json, descriptor-composition.json, source-verification.json,\nsource-manifest.json and manifest.json. The prior sixteen-gate audit remains\nimmutable. Publication, normal task UUID allocation and projection are separate\ncoordinator follow-through; no Hub UUID is assigned by this source record.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T02:17:04.847833Z", + "updated_at": "2026-10-17T02:17:23.962938Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee", + "record_id": "NK-WP-0048-T23", + "flavor": null, + "title": "T23 \u2014 Review native journal package assignment and storage integration boundaries", + "description": "Completed 2026-10-17: source/package placement and protected integration review\nonly, separate from T21's completed PostgreSQL source and disposable-SQL proof.\nThe owner review recommends a dedicated independently rolled-back\n`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a\nschema-valid draft declaration and no reef binding. No repository, registry\nentry, native package assignment or owner assent was created. Existing\nrapp-user-engine ownership/token delivery does not assign it the provisioner.\n\nThe complete current rapp-postgres catalog has six declared consumers: four on\nplatform-pg and two on platform-pg-2. The inert renderer-valid provisioner\ncandidate fits platform-pg-2 at three of four, requesting one migration plus\nfour runtime connections; placing it on the first cell is refused as a fifth.\nNormative package schema and full-catalog capacity/render checks passed. The\nfour-consumer ceiling is estimated, not measured native headroom, and no current\nSQL session, backup or restore acceptance is claimed.\n\nThe exact review identifies remaining package/substrate, SQL role/lease/TLS,\nnetwork and backup/recovery contracts; independently authenticated protected\nscope callbacks; an external content-bound checkpoint holder with atomic\nadvance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation,\nincluding lifecycle cascades and remove/readd ABA. Generic Audit custody is not\nthat checkpoint service. A database-only fence, final reread or one replica\ncannot exclude foreign directory writers. The native acceptance/recovery matrix\nkeeps storage/lease admission separate from separately approved directory\nmutation; the production activation remains disabled pending trusted integration.\n\nEvidence in the-custodian:\n`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`,\nsource-validation.json and hash-bound source-references.json, plus the inert\nproposed declaration/consumer and renderer outputs. No native resource, lease,\ncredential, deployment, destructive effect, independent holder or native owner\nacceptance was delivered. T14 retains these live gates with its original UUID\nand progress status. This completed review adds no duplicate T21 acceptance\ncredit; publication, task UUID allocation and projection remain coordinator\nfollow-through and no UUID is hand-assigned here.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T02:17:04.908357Z", + "updated_at": "2026-10-17T02:17:23.962938Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "1a3931ee-3b5f-52fc-9295-4073804f4bd2", + "record_id": "NK-WP-0048-T24", + "flavor": null, + "title": "T24 \u2014 Ensure-groups adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. NetKingdom (directory) owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:09.929764Z", + "updated_at": "2026-10-17T11:36:09.946486Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "0de2a7d9-874d-578b-a076-f653fe42e30b", + "record_id": "NK-WP-0048-T25", + "flavor": null, + "title": "T25 \u2014 Mint-secret adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. Platform owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.059698Z", + "updated_at": "2026-10-17T11:36:09.946486Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "94b3cbf0-c908-5ac6-b0db-67385c3e021c", + "record_id": "NK-WP-0048-T26", + "flavor": null, + "title": "T26 \u2014 Deliver-secret adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. Platform owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.191369Z", + "updated_at": "2026-10-17T11:36:09.946486Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "e56365f8-29b5-5c89-a329-8115f9f3b2a7", + "record_id": "NK-WP-0048-T27", + "flavor": null, + "title": "T27 \u2014 Bind-env adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. NetKingdom runtime owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.323775Z", + "updated_at": "2026-10-17T11:36:09.946486Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f", + "record_id": "NK-WP-0048-T28", + "flavor": null, + "title": "T28 \u2014 Deliver-consumer-secret adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. Platform and Apps owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.460128Z", + "updated_at": "2026-10-17T11:36:09.946486Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "129db41d-4560-5e20-9f3b-c3c91e0a5601", + "record_id": "NK-WP-0048-T29", + "flavor": null, + "title": "T29 \u2014 Hand-over adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. Apps owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.588379Z", + "updated_at": "2026-10-17T11:36:09.946486Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "6f6073ac-52dd-5b70-8874-6d76d4c072e4", + "record_id": "NK-WP-0048-T30", + "flavor": null, + "title": "T30 \u2014 Compose the descriptor set over the reviewed candidates", + "description": "2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`,\nfd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent\nreconstruction of 117 passing and no open findings. This is a source review only:\nit grants no owner admission, pin or native authority. Owner review and admission\ncontinue under T16, which also takes the r2 observations, including the\n`warden_executes: false` steps that carry execution descriptors.\n\n2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`)\napproved five files and rejected three, plus the artifact. The fixes are merged in\n2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant\nindependently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and\nderives its Secret and custody path, with no caller reference (640292c); R2-F3,\nbind-env pins its five-file closure, and a new closure-completeness test checks\nevery adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is\nregenerated, and the full suite including kernel admission passed 117. Next is\nthe r3 review. Open for owner review under T16: the type marks four steps\n`warden_executes: false`, yet each has an execution descriptor.\n\n2026-10-17 founder confirmed every proposed lane and schema name for T25-T29\nas-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17),\nand no step is still `owner_confirmation_required`. Next: independent source\nreview of the seven adapters.\n\n2026-10-17 founder decision (supersedes the `agent` decision below): the\nmint-secret actor is `human`. Coordinator review r1 (the-custodian\n`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`,\nfinding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every\n`effect: mint` admission whose actor is not `human` (\"agent mint is\nforbidden\"), so the `agent` descriptor could never be admitted. Adapter, tests\nand composer comment updated and the artifact regenerated (mint-secret\ndescriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`);\n98 tests pass against a45cf47. The set needs an independent r2 review.\n\n2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own\n`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2\ntype, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and\nfixture-only trust (stand-in adapters, nothing dispatched). It also pins that\nan agent mint descriptor is refused (\"agent mint is forbidden\") and that a\nwrong lane is refused for every step. Against the old `agent` revision\n(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47\nand on current ops-warden. Fixture admission is not owner admission or a pin.\n\n2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`.\nThe kernel is unchanged. The adapter, its test and the composer were updated and\nthe artifact regenerated; all seven steps pass `Descriptor.decode` and\n`complete_executable_descriptor_set` is true (98 tests passed). The artifact\nstays `not_an_admission`, with no owner review, admission or dispatch permission.\n\n2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged;\ncombined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an\nexplicit version root and refuses partial, duplicate, unknown or mismatched maps.\n`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked\n`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now\ncarry and honour `parent-session` revocation, and the hand-over owner is\nparameterised (`railiance-apps` for Forgejo). Six of the seven steps pass\n`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside\nthe kernel's human/agent set, and Platform (with ops-warden, if the kernel should\nchange) must decide. Five steps carry `owner_confirmation_required` names. The\nset is complete only after both. Owner review, the canon section 6 successor\nrevision and admission remain T16's.\n\n2026-10-17 prerequisites merged (65559ef; combined suite 78 passed):\nensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with\n1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its\nkernel-level fixture tests like T13's are still missing, and its `build()`\nsignature changed. Composition is unblocked in source. Owner confirmation of\nthe proposed step, lane and schema names remains before pinning.\n\n2026-10-17 inputs from T24\u2013T29 that composition must resolve: T24 accepts\nonly 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path;\nT27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names\nproposed in T25, T26 and T29 need owner confirmation before pinning.\n\nSplit from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set\nthrough the existing explicit version-root selection over T13 and T24\u2013T29. No\npartial descriptor map, canonical 1.1 stays the default, and new descriptor bytes\nneed a reviewed successor revision under canon section 6. Owner review and\nadmission remain T16's.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.655538Z", + "updated_at": "2026-10-17T11:36:09.946486Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a", + "record_id": "NK-WP-0048-T31", + "flavor": null, + "title": "T31 \u2014 Restart and lost-receipt fixtures against Warden's original-intent interface", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract.\n\nSplit from T15 on 2026-10-17. ops-warden published the original-intent\nlookup/context interface in `src/warden/case_run.py` (`OriginalIntent`,\n`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`).\nBuild T15's restart/lost-receipt fixtures for the registration adapter against\nit: the original intent is kept separate from the reconcile attempt; wrong\nattempt/case/source/principal and unavailable-holder cases refuse; and a missing\nor ambiguous record stays uncertain, with no add replay or undo. The interface\nis Warden's own dispatch record, not owner-authenticated created-only provenance.\nSelecting the actual authenticated holder stays a T15 owner gate.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.819590Z", + "updated_at": "2026-10-17T11:36:09.946486Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/tasks-before.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/tasks-before.json new file mode 100644 index 00000000..c3c5d5ba --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/tasks-before.json @@ -0,0 +1,715 @@ +[ + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "930ac3e9-20ec-5473-98cd-3fe8fddb730f", + "record_id": "NK-WP-0048-T01", + "flavor": null, + "title": "Tasks", + "description": "### T01 \u2014 Accept the terminology and the canon\n\nFounder review of `canon/standards/security-case-types_v0.1.md`, including its\nsection 10 open questions. On acceptance the canon status goes from `proposed`\nto `accepted`.\n\n2026-10-08: accepted by the founder (in session). Canon status `accepted`;\nsection 10's open questions are carried forward to the first type (T04, T05).", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.533372Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "19c25d96-a55c-58f0-9168-3fdbc4955fc2", + "record_id": "NK-WP-0048-T02", + "flavor": null, + "title": "T01 \u2014 Accept the terminology and the canon", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.572717Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "e487975b-73b1-516c-971d-da4cbbb93063", + "record_id": "NK-WP-0048-T03", + "flavor": null, + "title": "T02 \u2014 Inventory the de-facto types already built by hand", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.614667Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "2fffe112-7d55-5680-9952-78d74f44f87f", + "record_id": "NK-WP-0048-T04", + "flavor": null, + "title": "T03 \u2014 Schema, template and validator", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.724272Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "3d987d3d-0797-538b-86e4-51185a327ae2", + "record_id": "NK-WP-0048-T05", + "flavor": null, + "title": "T04 \u2014 Define the first type: browser OIDC relying party", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:17:41.833385Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "d9b26bcf-a7c6-59a2-92a0-f913d76a203c", + "record_id": "NK-WP-0048-T06", + "flavor": null, + "title": "T05 \u2014 Acknowledge the type through informed-decision", + "description": null, + "status": "wait", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": "Type1.2 architecture has a confirmed source-only founder acknowledgment; canonical1.1 remains unchanged/default and the1.2 reference case stays proposed. Needs explicit version selection/reference-source integration without normal admission bypass, closed owner adapters plus global/native receipt verifier (WARDEN-WP-0044-T03), independently verified six-object structural materialization and CCR42 claims/created-only provenance, exact broker/case/source reconciliation, then separately authorized native mint/delivery and attended registry/consumer verification. Source acknowledgment, a bundle or local tests do not prove implemented/materialized/live status.", + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": "external", + "created_at": "2026-10-08T20:17:41.935191Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "c3c9c3ae-4f23-5ba7-9e6f-e63bcf29b9f7", + "record_id": "NK-WP-0048-T07", + "flavor": null, + "title": "T06 \u2014 First KnownSecurityCase: Forgejo", + "description": null, + "status": "wait", + "priority": "medium", + "assignee": null, + "due_date": null, + "blocking_reason": "The next types follow once the first one is proven end to end.", + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": "external", + "created_at": "2026-10-08T20:17:42.037158Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "8e519082-dc8b-5e46-b660-03d03e55dcf6", + "record_id": "NK-WP-0048-T08", + "flavor": null, + "title": "T07 \u2014 Queue the next types", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T20:46:05.696360Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "0ee12dfc-8260-5038-bd39-cc7fb4113be0", + "record_id": "NK-WP-0048-T09", + "flavor": null, + "title": "T08 \u2014 identity-provisioner: ensure service-access groups", + "description": null, + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-08T21:03:35.764154Z", + "updated_at": "2026-10-14T00:11:44.904244Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "4b06ca97-c1e4-5680-bf22-ab75ab1646a4", + "record_id": "NK-WP-0048-T10", + "flavor": null, + "title": "T10 \u2014 Source compatibility checker for Mason and Warden evidence", + "description": "Completed 2026-10-16 in published source `c31d21ba`. The read-only\n`check-evidence` command checks the actual Mason structural request and Warden\njournal source protocols against an exact version/render. Independent source\nreview passed 103 tests at that checkpoint. It always reports native completion\nfalse and execution prohibited. Fixtures and local journal fingerprints do not\nprovide authenticated owner/native evidence. This records completed source scope\npreviously described under T06; it does not duplicate native acceptance credit.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:29:35.408457Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "ef2a0119-f59e-5e45-9519-115da9df7b85", + "record_id": "NK-WP-0048-T11", + "flavor": null, + "title": "T11 \u2014 Validate the optional machine-descriptor pin contract", + "description": "Completed 2026-10-16. Reproduced the JSON schema rejecting a valid\n`execution_descriptors` map while the Python validator accepted malformed maps.\nThe schema now supports the existing Warden optional exact-byte pin map; semantic\nvalidation binds keys to declared steps, bounds the map and checks digest shape.\nThe direct evidence API refuses malformed maps before accessing them. Twenty-one\nnew checks and all 127 affected source checks passed. No current type/case bytes,\nstatus, owner descriptors or native acceptance changed. Source evidence and\npublication receipt: Custodian\n`docs/changes/netkingdom-target-2026-10-16/browser-acceptance-next/`.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:29:35.501110Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "7ce4eee3-cd47-55a9-bfa5-4f1835d4d6fd", + "record_id": "NK-WP-0048-T12", + "flavor": null, + "title": "T12 \u2014 Prepare NetKingdom-owned attended step adapters", + "description": "Owner: NetKingdom. Retained scope: prepare bounded attended Deployment env,\nconfig verification and restart/readiness steps around the existing helpers.\nNext action: identify exact current entry points and their revision/readback\ncontracts, then implement fixed source-pinned callbacks or receipt-only attended\nsteps. Completion requires positive/refusal checks, exact target/source pins and\nuncertainty behavior without acquiring user-engine runtime identity or exposing\nregistry/client secrets. Existing caller/session, cluster/issuer, CAS/readback\nand status-only output boundaries remain mandatory.\n\n2026-10-16 task refinement: the original broad adapter scope is split into T13\n(completed registration source candidate), T14 (group creation provenance), T15\n(durable registration recovery) and T16 (descriptor/artifact selection and owner\nreview). This preserves T12's task ID and Hub UUID while giving each remaining\noutcome an owner and completion gate. T06 now names every source dependency;\nits native/reference lifecycle acceptance scope remains unchanged. Completion\nof any source task supplies no Warden, Platform or Apps assent and no native\nexecution authority.", + "status": "todo", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:29:35.603696Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "b264c10b-565c-5d29-b788-03cf5fbd2883", + "record_id": "NK-WP-0048-T13", + "flavor": null, + "title": "T13 \u2014 Prepare the fixed registration adapter source candidate", + "description": "Completed 2026-10-16, source implementation and synthetic fixture proof only.\nAdded an injection-only adapter around actual `keycape-client.run`: fixed\ncase/principal/human/lane/source/acceptance binding, existing CAS/readback,\nmetadata-only owner evidence and per-attempt parent-session revocation boundary.\nThe descriptor source composition pins the exact checker executable digest\nalongside adapter/helper source. Creation-only rollback authenticates its\ncreation receipt and refuses changed postcreation UID/resourceVersion;\npre-existing clients confer no rollback authority. Missing readback, evidence,\nrevocation or process-local creation provenance cannot become retry-safe success.\n\nEvidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-adapter-next/`\nR3 candidate, explicit patch and postimages, retained R1 rejected custody finding\nand R2 revision, and 36 passing source checks (16 new, 20 existing). Three checks\nuse actual Warden kernel admission/dispatch/rollback with published Warden\n`d97652a3bb5fa733b063d71a1b74068efcccf572` producer pins unchanged. Owner trust,\ndescriptor/type/case and Kubernetes objects remain synthetic fixtures. This\nrecords a completed source deliverable, not independent review, publication,\ntype acknowledgment, exercised procedure, identity admission or native browser\nacceptance. Independent root source review: Custodian browser-adapter-next/coordinator-review-r3.json. Source integration and normal publication/synchronization receipts: Custodian browser-adapter-next/canonical-integration.json. These receipts supply no other-owner assent or native authority.\n\nSource publication: `0775c2bfa41af236301155bb5a69bbf088bdf823`, independently\nreviewed by the Custodian coordinator before integration. The required initial\nprojection and ordinary UUID writeback both completed with zero failures and\nzero automation errors. Generated-only brief commits were refused by the normal\nhook; exact pre-existing staged/working brief bytes were restored. The separate\ncanonical integration receipt retains those refusals and projection evidence.\nNative admission and all other-owner acceptance remain open.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:47:17.544924Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "fce95440-a25b-5f27-8ef3-a3d182099113", + "record_id": "NK-WP-0048-T14", + "flavor": null, + "title": "T14 \u2014 Admit governed group rollback against native owner boundaries", + "description": "Owner: NetKingdom. Next action: bind the completed T21 PostgreSQL journal to\nan admitted dedicated CNPG consumer through the existing `rapp-postgres`\ndatabase-per-consumer contract. Identify the actual deployment/package and\ncustody owner, admitted placement/capacity and role/renewable lease/TLS target;\nimplement the independently retained exact content checkpoint and authoritative\ntenant/case/source/principal callbacks before enabling governed group operations.\nThe injection-only backend and fixture callbacks do not supply production scope\nor authorization. Existing service tokens, caller-supplied JSON and provenance\nreceipts confer no new grant. The default production endpoint remains disabled\nwithout the trusted integration. SQLite remains local/disposable only; any\nproduction exception requires explicit scoped governance under ADR-0015.\n\n2026-10-17 owner-integration refinement: T23 records the completed source/package\nplacement review, distinct from T21's source/disposable-SQL proof. Its dedicated\n`rapp-identity-provisioner` recommendation is draft and unbound, not an accepted\npackage or existing repository. The full six-consumer rapp-postgres renderer\naccepts the inert candidate on platform-pg-2 as its third of four declared\nconsumers; first-cell placement is refused as a fifth. Neither the estimated\nfour-consumer ceiling nor source requests establish native headroom or backup.\n\nNext independent owner contracts: NetKingdom/package/substrate owners accept\nexact deployment/rollback fate; rapp-postgres admits the dedicated consumer,\nroles, five-connection budget and actual backup/restore; Platform admits exact\nrenewable runtime/migration delivery and TLS/CA/SAN; network owners scope SQL,\nbroker and checkpoint paths without assuming a narrow policy subtracts additive\nsame-namespace access. Protected caller owners must provide authoritative\nGroupJournalBinding construction and current authorization; an independent\nexisting holder must authenticate and atomically advance the exact content\ncheckpoint, not a counter or ordinary Audit receipt. Directory/runtime owners\nmust select and prove either an exclusive all-writer maintenance barrier or\nprovider-supported CAS/generation, including established sessions, lifecycle\ncascades and remove/readd ABA. No source review establishes those owner facts.\n\nThe default PostgreSQL activation and destructive native rollback remain refused.\nStorage/lease acceptance can be proposed separately from LDAP mutation only after\nexact owner source/target/custody/verification/recovery and cost/risk review;\nState Hub guard approval grants none of it. Source-only integration review and\nnative acceptance matrix: Custodian native-storage-r1/OWNER-REVIEW.md. T14's\nexisting status, UUID, dependencies and native completion gate are retained.\n\n2026-10-17 refinement: T20 retains the historical durable SQLite source and\nactual disposable LLDAP proof. T21 extracts the completed PostgreSQL source and\nbounded real disposable SQL compatibility proof. T14 remains `progress` for\nnative CNPG admission/recovery, protected owner integration and exclusion of all\nexternal LDAP writers. Separate source publication/projection and independent\ncleanup receipts belong to coordinator follow-through; no missing native assent\nis represented as received.\n\nCompletion still requires independently reviewed/admitted ADR-0015-conforming\nCNPG storage (or an explicit governed exception), backup/lost-store recovery and\nmigration of legacy requests; a native\nwriter-isolation/fencing or provider CAS/version contract that closes external\nwriter races and remove/re-add ABA; exact case/source/principal and current\nnative object bindings for protected callbacks; and actual per-attempt evidence,\nrevocation and readback/refusal verification through the existing owner paths.\nOnly independently retained confirmed creations may be undone. Every\npre-existing/shared group and membership must remain preserved; unresolved\nintents never expire into retry authority. The disposable fixture supplies none\nof these native custody, authorization, owner-acceptance or production claims.", + "status": "progress", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:47:17.614347Z", + "updated_at": "2026-10-17T02:17:04.606534Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "1b825705-4848-5edc-832f-b1622cd9bdb7", + "record_id": "NK-WP-0048-T15", + "flavor": null, + "title": "T15 \u2014 Define authenticated recovery for lost registration process state", + "description": "2026-10-17 source part merged (061461e via 65559ef). `build()` takes optional\npaired `holder`/`observe` capabilities and installs `reconcile_original`; the T31\nfixtures now run through the adapter's own path. Rollback after restart is still\nrefused, and there is no add replay. This changes `keycape-registration-adapter.py`,\nso the revision reviewed under Custodian `browser-adapter-next/coordinator-review-r3.json`\nis superseded. The new revision has no independent review yet and must not be\npinned on the strength of the old one.\n\n2026-10-17: T31 fixtures done (74adcd7). What remains for T15: the owner\nselects the real authenticated original-attempt holder, and the T13 adapter\ngains the holder capability, `reconcile_original` and reconcile-aware `verify`\ndescribed in T31.\n\nOwner: NetKingdom for registration state, with Warden owning kernel evidence\nadmission. T22 records the completed source review and exact existing-seam gaps;\nT15 retains implementation and authenticated restart/lost-receipt acceptance.\nNext action: identify the actual owner publisher/authenticator and bounded\noriginal-attempt readback contract, and obtain Warden's reviewed original-intent\nlookup/context interface through existing WARDEN-WP-0044-T03. The current Journal\nretains attempt identity in historical rows but only hashes evidence references;\nload drops that context and reconcile passes a fresh attempt to the callback.\nThe adapter's original payload and creation record remain process-local. Neither\nthat journal nor a current matching registry entry reconstructs creation authority.\n\nThe owner contract must bind the original Invocation, case/type/descriptor/catalog/\nacceptance pins, source, principal and admitted tenant/realm provenance, separately\nfrom the new reconciliation attempt and its independently revoked parent session.\nUse an existing authenticated holder/readback; Audit Core's custody API is a\ncandidate only until its actual registration source/class, load-bearing emission\ncontract and separate scoped reader are admitted. No new broker or receipt trust\nroot is proposed. A missing original record, ambiguous publication, NO_CHANGE,\nchanged native revision or lost readback stays uncertain; no add replay or undo.\n\nCompletion requires restart/lost-receipt fixtures against the selected actual owner\ncontract: independently authenticated original created-only provenance, original\nexact client entry and unchanged current postcreation UID/resourceVersion before\nany undo, with wrong attempt/case/source/principal and unavailable-holder refusals.\nThe T13 adapter deliberately refuses post-restart rollback and installs no\nreconciliation callback. Existing source review, JSON receipts and synthetic\ntrust do not satisfy native holder acceptance or authorize attended recovery.\nExact remaining owner facts: Custodian native-registration-r1/original-attempt-review.json.\n\n2026-10-17 check (net-kingdom-worker): Warden's original-intent lookup interface\nis not published \u2014 ops-warden head is `d97652a`, with no WARDEN-WP-0044-T03\ncommit, and the net-kingdom inbox holds no announcement. No restart/lost-receipt\nfixture or readback seam was added this session; the work is deferred behind\nT16's decomposition under the same token budget. Remains todo, gated on Warden\npublishing the reviewed lookup/context interface and on owner selection of the\nauthenticated original-attempt holder.", + "status": "wait", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": "Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source.", + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": "external", + "created_at": "2026-10-16T19:47:17.734492Z", + "updated_at": "2026-10-17T06:04:41.033307Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "200bf500-5084-5ae7-be26-3033fe1f296e", + "record_id": "NK-WP-0048-T16", + "flavor": null, + "title": "T16 \u2014 Select and review the actual machine descriptor composition", + "description": "2026-10-17 coordinator review r4 approved the 1.3 candidate at 1920b13 with no\nfindings (the-custodian `nk48-adapters-1.2-review/coordinator-review-r4.json`). The founder chose\noption A of the T16 owner-review packet (`nk48-t16-owner-review/founder-option-decision.md`):\n1.3 with execution descriptors for all seven steps. Owner reviews for A are open\nin the-custodian `docs/changes/netkingdom-target-2026-10-17/nk48-1.3-owner-review/` (net-kingdom,\nrailiance-platform, railiance-apps, ops-warden, key-cape). The owner-review packet\nsupersedes the r1 composition pointer: T16's exact composition is now\n`sso-mfa/k8s/keycape/descriptor-composition-1.3.candidate.json`.\n\n2026-10-17 successor candidate prepared (source only, `proposed`):\n`security-case-type-revisions/1.3/oidc-browser-relying-party/` is the 1.2 package plus\nan `execution_descriptors` map of the seven composed descriptors, with examples\npinned to `@1.3` and no acknowledgment. ensure-groups, hand-over,\ndeliver-consumer-secret and the composer accept 1.3 (835680c, merged acda26c).\n`test_admit_1_3_type.py` runs Warden's real admission against the committed 1.3 type:\nthe proposed type is refused; an acknowledged copy is refused because ops-warden\npins parameter fingerprints only for 1.1 and 1.2; and with only a 1.3 pin (equal to\n1.2's fingerprint) all seven are admitted. This is a major change under canon\nsection 6. Next: r4 source review of the changed adapters and the map, then owner\nreviews, including the ops-warden 1.3 pin, the mint-secret warden_executes/human\nquestion and the owner-run steps question, then the founder's Decision Memo.\n\nOwner: NetKingdom for descriptor/type source and exact checker artifact; Warden\nindependently owns global provenance/admission. T22 records the completed seven-step\nsource1.2 inventory and regenerated register-client descriptor using the delivered\nchecker and current sealed-snapshot source; T16 retains exact owner review,\ncomplete descriptor composition and admission. Next action: bind actual native\npublisher/authenticator, original/current session verification and revocation,\nexecutable storage/custody and config pins, then obtain the six missing fixed\nowner descriptors/adapters for ensure-groups, mint-secret, deliver-secret,\nbind-env, deliver-consumer-secret and hand-over. The inventory identifies each\nexisting owner seam, exact missing contract and created-only rollback requirement;\nit is not an executable bundle. T14 and T15 remain independent prerequisites.\n\nReview the dedicated consumer source1.2 composition only through the existing\nexplicit version-root selection. Keep canonical1.1/default and its accepted case\nunchanged, and the source-only acknowledged1.2 reference case proposed without\nacceptance fields. New descriptor bytes require a reviewed exact successor\nrevision/version under canon section6; they do not inherit historical source\nacknowledgment. Changed authentication/materialization/trust needs the prescribed\ninformed-decision acknowledgment. Do not insert a partial descriptor map or use\n--allow-unimplemented preparation as ordinary case/native admission. The registry\nhelper reports restart_required; separately review quiet-window restart/readiness\nand actual consumer verification rather than infer activation from registry CAS.\nExact composition: Custodian native-registration-r1/descriptor-composition.json.\nCompletion requires separate owner source review of exact machine descriptors,\nartifact/source/config pins, normal type/version admission and independent\nevidence-verification boundaries. Warden d97652a refreshes producer validator\npins for NetKingdom 015694ee; it does not attest T13's new adapter. Pinning any\nmachine descriptor into an acknowledged type is a separately reviewed source\nchange. Platform owns structural/mint/broker/delivery evidence; Apps owns actual\nconsumer login/group enforcement. Neither their assent nor native acceptance is\nsupplied by this task.\n\n2026-10-16 source checkpoint: T19 below extracts the completed per-attempt\nexecutable snapshot implementation and reviewed fixture proof. T16 retains\nnative executable storage/custody, exact owner evidence/session callbacks,\ncomplete descriptor composition and ordinary type/version admission. The\nsuccessor descriptor/source pins are proposed only; the earlier descriptor\nhistory is preserved. T14 group provenance and T15 durable restart/recovery\nremain independent open requirements. No native acceptance is counted here.\n\n2026-10-17 budget checkpoint (net-kingdom-worker): no source change made. The\nT13 registration adapter alone is 217 source and 317 test lines plus an\nindependent review; six further owner descriptors/adapters cannot be completed\nwithin one per-task token budget (soft 8000 / hard 10000), so the task was\nstopped below 50% and is decomposed rather than pushed through. Proposed split,\none source-candidate task each with positive/refusal fixtures, canonical1.1\ndefault unchanged and no --allow-unimplemented admission: (a) ensure-groups,\nbound to T14's journal/provenance seam; (b) mint-secret and (c) deliver-secret,\nPlatform-owned evidence seams; (d) bind-env and (e) deliver-consumer-secret;\n(f) hand-over, Apps-owned consumer verification; then (g) descriptor composition\nover the reviewed set. Each candidate needs its owner's source review before\npinning; none is authored here as owner-accepted. Status stays todo.", + "status": "todo", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T19:47:17.849814Z", + "updated_at": "2026-10-17T09:41:04.077127Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "8c3e4baa-a218-5996-af9e-fe1153a46dac", + "record_id": "NK-WP-0048-T17", + "flavor": null, + "title": "T17 \u2014 Build and exercise the exact offline config-checker artifact", + "description": "Completed 2026-10-16, isolated artifact/source fixture proof only. Built the\nexisting `key-cape/src/cmd/keycape` command from published clean source revision\n`3c71226a1a7b8a2bdb710e9c497e1a1f7f8a3987`, source tree\n`f0010b9cab17dbc3a787628a29657d98b900320f`, with cached Go 1.23.0,\nCGO disabled, Linux/amd64 v1, trimmed paths, disabled VCS stamping and offline\nread-only module selection. The full retained closure pins 108 source files,\n9876 toolchain files and ten used module source directories. Three isolated\nbuild locations produced identical 13633409-byte artifacts:\n`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`.\n\nEight disposable checks use the actual executable and maintained helper/kernel,\nwith complete synthetic configs and a minimal environment: add/created-only\nremoval, normal pinned producer admission, rollback, lost-readback uncertainty,\nstale revisions, substituted checker refusal, invalid startup config and missing\nsecret-env refusal. No checker stub or producer-pin override remains. The\nartifact-bound descriptor proposal is\n`sha256:76b1153d188062ece53fcd5e8d35290fdbfd02a69dd79d4b001fc42781afcdc3`.\nEvidence: Custodian\n`docs/changes/netkingdom-target-2026-10-16/browser-checker-artifact-next/`.\nThe first two build attempts succeeded but their closure collection failed;\nboth failures are retained, as is refusal of the older incomplete synthetic\nfixture. No production config, credentials or cluster mutation was used.\n\nThis extracts completed build/fixture scope from T16 without claiming KeyCape\nowner endorsement, native checker selection/storage custody, acknowledged type\nintegration, native evidence/session callbacks or attended browser acceptance.\nT16 retains exact owner review and composition/admission scope. The attended\nregistration procedure remains unexercised.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T20:30:03.492101Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "9c45da71-fb4c-5f20-9065-90f448f37c64", + "record_id": "NK-WP-0048-T18", + "flavor": null, + "title": "T18 \u2014 Make checker input and executable selection deterministic", + "description": "Completed 2026-10-16, isolated source correction and real-artifact fixture proof\nonly; independent integration/publication remain separately recorded. KeyCape\nsource review identified non-secret `KEYCAPE_ACCOUNT_PORTAL_URL` and\n`KEYCAPE_BROWSER_LOGOUT_URL` overrides in config.Load. The original helper\ninherited ambient environment, allowing valid supplied configuration to be\nrejected or invalid supplied URL configuration to be admitted. A bare relative\nchecker filename could also be replaced by PATH lookup after the adapter had\nhashed the selected local file. All three counterexamples are retained against\nthe unchanged predecessor.\n\nThe existing helper now resolves the selected checker file to an absolute path\nand passes an explicit empty subprocess environment. Three opt-in real-checker\nregressions verify both URL-verdict directions and relative-file selection under\na non-checker PATH decoy. All 39 affected checks pass with the exact supplied\nartifact, as do 11 real-artifact helper/kernel scenarios. Source evidence:\nCustodian `docs/changes/netkingdom-target-2026-10-16/browser-checker-environment-r2/`.\nHistorical descriptors remain retained; the helper correction changes successor\nsource-composition pins and requires their separate review/admission. An absolute\npath is not immutable custody and does not eliminate file replacement races;\nT16 retains native executable storage/custody and callback/type admission.\nNo production configuration, credentials or cluster mutation was used.\n\nSource integration and publication: `916ba4b33e03179274d03b78bd898a4843e8653f`, following independent\nCustodian source review in browser-checker-environment-r2/coordinator-review.json.\nThe ordinary protected projection pass assigned T17/T18 UUIDs with zero failures\nand zero automation errors; generated-only brief commit refusal is retained.\nThe exact prior staged/working brief bytes were restored. Final source/publication/\nprojection evidence is retained in Custodian browser-checker-environment-r2/\ncanonical-integration.json. These source receipts do not admit native executable\ncustody, owner callbacks, acknowledged type changes or browser identities.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T20:30:03.587086Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "f50fdcd1-00be-5c3c-be0c-345aa2126032", + "record_id": "NK-WP-0048-T19", + "flavor": null, + "title": "T19 \u2014 Bind checker execution to verified immutable attempt bytes", + "description": "Completed 2026-10-16, independently reviewed source implementation and synthetic\nfixture proof only. The registration adapter now opens a bounded regular checker\nartifact without following its terminal symlink, verifies the reviewed digest,\nand snapshots the exact bytes into a write/grow/shrink/seal-removal-sealed Linux\nmemfd before helper access. Live and proposed config checks execute that same fd\nthrough procfs with the existing empty environment; the fd closes on completion\nor failure. Unsupported snapshot/seal/exec policy refuses through the existing\nparent-session revocation and uncertainty boundaries. The earlier unbounded\npre-snapshot path read was removed during independent review; actual adapter\nsymlink/FIFO/oversize refusal tests verify helper access never starts.\n\nEvidence: Custodian `docs/changes/netkingdom-target-2026-10-16/browser-resume-r1/`,\nincluding exact base/preimages/postimages/patch, proposed descriptor, coordinator\nsource review and canonical integration receipt. Independent Custodian checks\npassed 49 affected source tests and 12 actual Go checker/helper/kernel scenarios;\nthe sets overlap and are not separate acceptance counts. Existing published\nWarden d97652a producer pins remain unchanged. The exact KeyCape artifact remains\n`sha256:be37738fe1c08259fb4f9ca0daf0f9301bfffc237e5bb38435525dfbf11ccff5`.\nThe predecessor path-replacement refusal and the first expanded reopened-fd test\nfailure are retained; the latter fixture was corrected to restore write mode\nbefore independently proving seals block reopened-fd writes and shared mmap.\n\nThis source task adds no native owner custody, callback, evidence trust, type\nacknowledgment, credential access, consumer sign-in or rollback acceptance.\nExecutable memfds, Linux seals and usable procfs are explicit runtime dependencies\nand still require native admission. This is an extracted source outcome from\nT16, not completion of T16; T12/T14/T15 and all external-owner gates remain open.\n\n\nPublication verified by the coordinator after the source worker usage limit:\n`8790dd18ae83c4651ef3d1d923312a118a5b436b` is the actual remote main revision.\nAll five canonical source hashes match the reviewed packet. Normal synchronization\ncompleted with zero failures/automation errors; all 19 task statuses match the\nprojection. T19's assigned UUID is retained. The original staged and working brief\nbytes were restored and independently compared. The exact source, projection and\npublication checks are in Custodian browser-resume-r1/canonical-integration.json.\nNative acceptance remains open under T16 and the other named owner tasks.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T21:23:29.761635Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "863fcbdb-e246-51ba-87ca-a2f44d01d6f8", + "record_id": "NK-WP-0048-T20", + "flavor": null, + "title": "T20 \u2014 Prepare durable group provenance and prove disposable LLDAP compatibility", + "description": "Completed 2026-10-17, source candidate preparation and bounded synthetic\ncompatibility proof only. This extracts evidenced implementation/fixture scope\nfrom T14; T14 retains native storage, writer isolation, protected callback and\nowner-acceptance gates. Source integration/publication and State Hub projection\nare pending the coordinator's separate receipts; no revision or task UUID is\ninvented here.\n\nThe existing identity-provisioner group ensure/revoke interface now has a\nreviewed candidate for a bounded owner-private SQLite journal, proven only in\nlocal/disposable use. The initial storage instructions wrongly suggested a native\npersistent-volume path; source review against accepted ADR-0015 corrected them\nbefore publication. Production SQLite-on-a-PVC is not admitted: T14 retains\nCNPG/rapp-postgres storage admission or a separately governed exception.\nThe local journal saves\ncomplete prestate and intent before each attempted directory mutation, inventories\nonly confirmed new groups/memberships, binds original ensure idempotency and\ncorrelation to the exact request digest, and refuses unsigned caller inventories\nor legacy broad revoke requests. Replays never recreate removed state. Missing,\nreplaced or untrusted storage and missing intent rows stop execution; definite\nbefore-mutation refusals retain evidence without stranding unrelated valid work.\nInterrupted writes/readback remain unresolved across reopening. Revoke validates\nall targeted native ids and complete member postimages before destructive work,\nremoves only confirmed creations and never deletes a preexisting empty group.\nServer/configuration wiring, image-copy closure and owner storage/request\nmigration instructions are included. The existing endpoint's caller\nauthentication remains the existing boundary, not a new grant.\n\nThe candidate was prepared against NetKingdom\n`878abc93588e12b6da1bac055cfbb3abb8d7adf1`; exact source patch digest\n`sha256:1f3f3ca94c46dadac8f24896053d4d23179de795e50a24298a0cfbcc46cca30e`.\nThe independent Custodian review passed all 65 provisioner checks. The earlier\n62-check candidate, two independent-review findings and subsequent fixes remain\nretained rather than overwritten. Seven additional fixture-plan checks exercise\nthe preparation boundary; these counts overlap the same candidate and do not\nmeasure task-completion percentage or independent acceptance.\n\nA separately reviewed bounded fixture then exercised the exact candidate against\ncached pinned disposable LLDAP and Python images, through actual GraphQL. All 14\nclosed compatibility/containment oracles passed: exact creation inventory,\nseparate-process SQLite reopening/replay/revoke, complete preexisting directory\npreservation including empty/shared groups and memberships, refusal of shared\nmembership drift, retained uncertainty after a lost response following a real\naddition, and refusal of unknown/cross-case receipt bindings. The fixture used\nonly newly generated synthetic users/passwords/key material in private tmpfs,\nwith a network-none anchor and shared loopback helper, no host ports/mounts,\nno image pulls and no production or actual credential contact. The coordinator's\nsafe receipt reports both exact owned containers absent after cleanup; final\nindependent cleanup verification remains the coordinator's integration duty.\n\nEvidence: Custodian\n`docs/changes/netkingdom-target-2026-10-16/group-provenance-r1/`, including exact\nsource base/pre/postimages, patch/manifest, independent review and retained\nrefinements. Native fixture manifest\n`sha256:8a0514f5f14137355972400cd4852f48c892316d28e3abd3c86635d7cb81eef2`;\nretained safe result `native-fixture/execution-result.json` (original\n`/tmp/nk48-groups-native-review-5pywb19z/result.json`). The source task supplies\nno native owner assent, production rollout, protected session/evidence callbacks,\nordinary type admission or consumer login proof. LLDAP has no conditional\nmembership mutation or generation token here: exact readback cannot exclude\nABA or a concurrent external writer, so T14 remains open.\n\n2026-10-17 verified source follow-through: source commit `4f65e41` passed normal\nlocal scanning and all 65 canonical provisioner tests. The actual disposable\nLLDAP receipt passed all 14 oracles; independent verification on the pinned\ndaemon found both exact names and the run label absent. Source integration\nincludes the ADR-0015 documentation correction; the journal remains local-only.\n\nT14 source preparation is underway. Twenty-three pinned public sources identify\nNetKingdom implementation/reconciliation, Platform custody/database governance\nand rapp-postgres consumer provisioning; the inspected runtime binding does not\nassign identity-provisioner a package. The existing User Engine runtime token\nand pod route are not a protected-case caller lane. The native proposal preserves\nthe same journal seam on a dedicated CNPG consumer and requires authoritative\nscope binding, admitted role/lease/placement/recovery and all LDAP writers fenced.\nThe declared exclusive maintenance window is not mechanical exclusion of WebUI,\nhelper or lifecycle writers. Capacity placement and EUR cost/risk remain owner\ndecisions. No package, database, credential, rollout or owner assent was created.\nCustodian evidence: group-provenance-r1/native-storage-proposal-r1; final source,\nprojection and cleanup review: resume-integration-r1/publication-verification.json.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T22:35:33.755778Z", + "updated_at": "2026-10-16T22:45:46.537377Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "afe78e4d-408a-516a-ab92-e212e8a2d11d", + "record_id": "NK-WP-0048-T21", + "flavor": null, + "title": "T21 \u2014 Implement the PostgreSQL journal and prove disposable SQL compatibility", + "description": "Completed 2026-10-17: bounded source preparation and actual disposable PostgreSQL\ncompatibility only. T14 retains native storage, custody, callbacks, authorization,\nall-writer isolation and production admission. Coordinator source publication and\nprojection remain separate follow-through; no source revision or task UUID is\ninvented here. T20 remains the historical local SQLite/actual LLDAP proof.\n\nThe eight-path source candidate adds an injection-only PostgreSQL implementation\nbehind the existing owner journal seam, with explicit trusted tenant/directory/\ncase/source/principal/epoch binding and independent continuity callbacks. It\npersists bounded intent/created-only receipts, exact-one version updates, durable\nactive fences, and a content-bound journal chain. SQL commits precede independent\nexact-marker acknowledgment; lost replies/checkpoints poison continuation and\nretain uncertainty. A counter-only checkpoint is explicitly insufficient for\ndivergent clones. Migration does not create database/schema/credential, admit an\nepoch or clear quarantine; production HTTP activation is absent pending trusted\ncallbacks. Pinned psycopg package/image-copy closure is included, without claiming\nan image build or native dependency admission.\n\nCandidate base `6cb43c0cc55212363a5406b02db046e2384a7c40`; final eight-path patch\n`sha256:5549d2982b61ff70aba4b3928fecd5639db0f1a732ab2f8630f678e7acf4d34d`.\nAll 82 source tests pass, alongside 13 fixture plan/diagnostic checks and two\nsource-backed snapshot regressions. Counts overlap the same source and fixture;\nthey are not additive native acceptance or completion percentages.\n\nThe independently reviewed R5 fixture ran the actual SQL migration/backend and\nexisting provisioner path against cached pinned disposable PostgreSQL. Directory\noperations deliberately used the existing synthetic LLDAP test double. All\nmanifest-declared phase oracles passed: creation-only inventory and DDL refusal;\nseparate-process replay/revoke with complete preexisting/shared visible-state\npreservation; foreign-context refusal; concurrent exactly-once reservation and\nstale version refusal; actual database fork with divergent bodies at the same\ncounter and fresh-object rejection of the losing exact checkpoint; retained\nuncertainty after checkpoint/directory/SQL response loss; and actual pg_dump/\npg_restore rollback refusal against the independently retained fixture checkpoint.\nThe checkpoint is fixture-only, not an admitted native owner evidence holder.\n\nOnly generated credentials/state were used, with two nonroot/read-only containers\non private loopback, bounded tmpfs/CPU/memory/PIDs/lifetimes, no host ports/mounts,\nno pulls and no production contact. The safe receipt reports both exact owned\ncontainers absent, with no daemon uncertainty. Independent final pinned-daemon\ncleanup verification remains coordinator follow-through.\n\nFailed R2/R3/R4 receipts and source revisions remain retained. R2's helper failure\nsubphase is unknown; R3 exposed a real backend bug where replacing nested receipt\nobjects lost the created ID held by ensure, fixed by acknowledging only the backend\nversion after commit/checkpoint. R4 verified that fix through actual SQL bootstrap,\nthen exposed fixture snapshot aliasing and empty deleted-group backing entries;\nsource-backed regressions corrected the snapshot without weakening complete\nvisible-prestate equality, and reject nonempty orphan membership. These refinements\nbelong to one trial, not extra native acceptance successes.\n\nCustodian evidence: `docs/changes/netkingdom-target-2026-10-16/group-postgres-r1/`;\nexact source/pre/postimages and retained predecessors; native R5 manifest\n`sha256:d51f90816616848b93bcdc482e10a98d07061deb01eeed9e50700fc6e68b6051`;\n`final-integration-r1/typed-execution-evidence.json` and byte-exact safe receipt\n(original `/tmp/nk48-postgres-native-review-sguj7t8p/result.json`). No CNPG native\nstorage/HA/backup/lease/TLS/capacity/restore admission, authoritative protected\ncallbacks, real LDAP writer fencing/CAS/ABA exclusion, owner assent or production\nendpoint activation is claimed. Those remain live T14 work.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-16T23:38:06.135472Z", + "updated_at": "2026-10-16T23:38:23.460790Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "ea50d93b-0d04-55d2-98c4-9b6c3fe925b1", + "record_id": "NK-WP-0048-T22", + "flavor": null, + "title": "T22 \u2014 Establish registration recovery source gaps and complete descriptor review inventory", + "description": "Completed 2026-10-17: source-only owner-contract review and complete review\ninventory. Inspected the actual registration adapter, Warden Journal/Executor,\nNetKingdom evidence compatibility checker and Audit Core custody/read surfaces.\nNo existing admitted holder/readback supplies authenticated original-attempt\nrecovery: payload/creation are process-local, persisted evidence references are\nhashed, and reconcile receives a new attempt without original context. The exact\nfour-owner contract requirements and non-replayable missing/ambiguous cases are\nrecorded; no recovery callback, publisher, credential grant or native trust root\nwas installed. T15 retains implementation and actual acceptance.\n\nThe complete source1.2 inventory maps all seven follow-through steps to their\nexisting owners, missing exact contracts and created-only rollback requirements.\nThe actual maintained adapter regenerated the sole existing register-client\nmachine descriptor against the delivered 13,633,409-byte checker and current\nsealed-memfd snapshot source. The checker rehash and Warden Descriptor.decode\nround-trip passed; six owner descriptors remain absent. Actual source commands\nrender canonical1.1 and explicitly selected1.2 with execution.permitted false;\nordinary1.2 case admission still refuses its acknowledged/unimplemented type.\nCanonical type/case/acknowledgment bytes were hash-checked unchanged. No tests were\nadded or rerun, no native configuration or effect occurred, and no owner assent\nor complete machine bundle is claimed. T16 retains native executable custody,\ncomplete exact descriptors, independent source review and normal version/type\nadmission. This extracted outcome supplies no native gate completion credit.\n\nEvidence in the-custodian:\n`docs/changes/netkingdom-target-2026-10-17/native-registration-r1/`, especially\noriginal-attempt-review.json, descriptor-composition.json, source-verification.json,\nsource-manifest.json and manifest.json. The prior sixteen-gate audit remains\nimmutable. Publication, normal task UUID allocation and projection are separate\ncoordinator follow-through; no Hub UUID is assigned by this source record.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T02:17:04.847833Z", + "updated_at": "2026-10-17T02:17:23.962938Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "6e7cd483-c7af-53a2-9d0a-3d76b6fb49ee", + "record_id": "NK-WP-0048-T23", + "flavor": null, + "title": "T23 \u2014 Review native journal package assignment and storage integration boundaries", + "description": "Completed 2026-10-17: source/package placement and protected integration review\nonly, separate from T21's completed PostgreSQL source and disposable-SQL proof.\nThe owner review recommends a dedicated independently rolled-back\n`rapp-identity-provisioner`, owned by NetKingdom on rail-kubernetes, with a\nschema-valid draft declaration and no reef binding. No repository, registry\nentry, native package assignment or owner assent was created. Existing\nrapp-user-engine ownership/token delivery does not assign it the provisioner.\n\nThe complete current rapp-postgres catalog has six declared consumers: four on\nplatform-pg and two on platform-pg-2. The inert renderer-valid provisioner\ncandidate fits platform-pg-2 at three of four, requesting one migration plus\nfour runtime connections; placing it on the first cell is refused as a fifth.\nNormative package schema and full-catalog capacity/render checks passed. The\nfour-consumer ceiling is estimated, not measured native headroom, and no current\nSQL session, backup or restore acceptance is claimed.\n\nThe exact review identifies remaining package/substrate, SQL role/lease/TLS,\nnetwork and backup/recovery contracts; independently authenticated protected\nscope callbacks; an external content-bound checkpoint holder with atomic\nadvance/loss handling; and all-LDAP-writer exclusion or provider CAS/generation,\nincluding lifecycle cascades and remove/readd ABA. Generic Audit custody is not\nthat checkpoint service. A database-only fence, final reread or one replica\ncannot exclude foreign directory writers. The native acceptance/recovery matrix\nkeeps storage/lease admission separate from separately approved directory\nmutation; the production activation remains disabled pending trusted integration.\n\nEvidence in the-custodian:\n`docs/changes/netkingdom-target-2026-10-17/native-storage-r1/OWNER-REVIEW.md`,\nsource-validation.json and hash-bound source-references.json, plus the inert\nproposed declaration/consumer and renderer outputs. No native resource, lease,\ncredential, deployment, destructive effect, independent holder or native owner\nacceptance was delivered. T14 retains these live gates with its original UUID\nand progress status. This completed review adds no duplicate T21 acceptance\ncredit; publication, task UUID allocation and projection remain coordinator\nfollow-through and no UUID is hand-assigned here.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T02:17:04.908357Z", + "updated_at": "2026-10-17T02:17:23.962938Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "1a3931ee-3b5f-52fc-9295-4073804f4bd2", + "record_id": null, + "flavor": null, + "title": "T24 \u2014 Ensure-groups adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): ensure-groups-adapter.py, 8 tests (commit 5a2e7d6). Binds to the T14 PostgreSQL journal and refuses SQLite or a foreign binding. It accepts only canonical 1.1 cases, so T30 must add an explicit 1.2 path. It calls the provisioner's private `_group_binding`; a public helper is the cleaner shared edit.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind group creation to T14's journal/provenance seam; refuse without trusted journal callbacks.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. NetKingdom (directory) owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:09.929764Z", + "updated_at": "2026-10-17T05:59:49.841928Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "0de2a7d9-874d-578b-a076-f653fe42e30b", + "record_id": null, + "flavor": null, + "title": "T25 \u2014 Mint-secret adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): mint-secret-adapter.py, 9 tests (commit 558c5bc). Value-free receipts only. The receipt schema `railiance-platform.custody-mint.v1` and the mint/destroy capability interface are proposals that Platform must confirm.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned custody evidence seam: the adapter records value-free mint evidence only and never handles secret values.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. Platform owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.059698Z", + "updated_at": "2026-10-17T05:59:50.089628Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "94b3cbf0-c908-5ac6-b0db-67385c3e021c", + "record_id": null, + "flavor": null, + "title": "T26 \u2014 Deliver-secret adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-secret-adapter.py, 8 tests (commit 1b60f6e). The lane `platform-secret-delivery` and the schema `netkingdom.deliver-secret.v1` are proposals that Platform must confirm.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Platform-owned delivery evidence seam (ESO/custody), value-free.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. Platform owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.191369Z", + "updated_at": "2026-10-17T05:59:50.848214Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "e56365f8-29b5-5c89-a329-8115f9f3b2a7", + "record_id": null, + "flavor": null, + "title": "T27 \u2014 Bind-env adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): bind-env-adapter.py, 5 tests (commit 6fbce73). Binds references only. To fit the budget it does not yet wrap a full Warden `Adapter` like T13 (source-digest pin, authenticated evidence, parent-session revocation); it returns `(reference, run, rollback)`. That wrapping is a prerequisite for T30.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Bind the issuer Deployment env reference to the delivered secret reference, without values.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. NetKingdom runtime owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.323775Z", + "updated_at": "2026-10-17T05:59:51.139982Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f", + "record_id": null, + "flavor": null, + "title": "T28 \u2014 Deliver-consumer-secret adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): deliver-consumer-secret-adapter.py, 5 tests (commit 650c1ac). Type 1.2 only. It assumes the owner has validated ESO TokenRequest and the controller-RBAC admission; that stays an owner gate.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Consumer-side delivery evidence seam, value-free.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. Platform and Apps owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.460128Z", + "updated_at": "2026-10-17T05:59:51.404991Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "129db41d-4560-5e20-9f3b-c3c91e0a5601", + "record_id": null, + "flavor": null, + "title": "T29 \u2014 Hand-over adapter source candidate", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): handover-adapter.py, 8 tests (commit ec63539). The owner `apps` and lane `apps-consumer-handover` are proposals that the Apps owner must confirm. It has no reconcile path; restart handling follows T15.\n\nSplit from T16 on 2026-10-17 so each candidate fits one per-task budget. Apps-owned consumer verification and hand-over receipt seam.\nSource candidate only, modelled on the T13 registration adapter\n(`sso-mfa/k8s/keycape/keycape-registration-adapter.py`, its tests and\n`registration-adapter.md`). Requires positive and refusal fixtures and\ncreated-only rollback. Keeps canonical 1.1 as the default and uses no\n`--allow-unimplemented` admission. Done means the candidate and its tests are\ncommitted. Apps owner source review before pinning, and any native action,\nstay separate gates and are not supplied by this task.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.588379Z", + "updated_at": "2026-10-17T05:59:51.753233Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "6f6073ac-52dd-5b70-8874-6d76d4c072e4", + "record_id": null, + "flavor": null, + "title": "T30 \u2014 Compose the descriptor set over the reviewed candidates", + "description": "2026-10-17 done: coordinator review r3 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r3.json`,\nfd07eaa) approved all nine files at ff8f597, with R2-F1..F3 resolved, an independent\nreconstruction of 117 passing and no open findings. This is a source review only:\nit grants no owner admission, pin or native authority. Owner review and admission\ncontinue under T16, which also takes the r2 observations, including the\n`warden_executes: false` steps that carry execution descriptors.\n\n2026-10-17 coordinator review r2 (the-custodian `nk48-adapters-1.2-review/coordinator-review-r2.json`)\napproved five files and rejected three, plus the artifact. The fixes are merged in\n2ee4ed0: R2-F1, mint-secret revokes the session and the broker grant\nindependently (eeed8e1); R2-F2, deliver-secret binds to the admitted case and\nderives its Secret and custody path, with no caller reference (640292c); R2-F3,\nbind-env pins its five-file closure, and a new closure-completeness test checks\nevery adapter (d16db30). Each regression test failed on 68f4bbb. The artifact is\nregenerated, and the full suite including kernel admission passed 117. Next is\nthe r3 review. Open for owner review under T16: the type marks four steps\n`warden_executes: false`, yet each has an execution descriptor.\n\n2026-10-17 founder confirmed every proposed lane and schema name for T25-T29\nas-is. The artifact records them as `names_confirmed` (by founder, 2026-10-17),\nand no step is still `owner_confirmation_required`. Next: independent source\nreview of the seven adapters.\n\n2026-10-17 founder decision (supersedes the `agent` decision below): the\nmint-secret actor is `human`. Coordinator review r1 (the-custodian\n`docs/changes/netkingdom-target-2026-10-17/nk48-adapters-1.2-review/coordinator-review-r1.json`,\nfinding NK48-R1-F1) showed that unchanged kernel a45cf47 refuses every\n`effect: mint` admission whose actor is not `human` (\"agent mint is\nforbidden\"), so the `agent` descriptor could never be admitted. Adapter, tests\nand composer comment updated and the artifact regenerated (mint-secret\ndescriptor digest now `sha256:8c40c2884f8ba15e8cb6c98f33419c395e54fce8ef33d6e1a34782cf3f1fcfa9`);\n98 tests pass against a45cf47. The set needs an independent r2 review.\n\n2026-10-17 `test_admit_1_2_adapter_descriptors.py` runs Warden's own\n`Executor.admit` on all seven composed 1.2 descriptors, with the real 1.2\ntype, the 1.2 forgejo example case, the reviewed `BrowserInputs` validator and\nfixture-only trust (stand-in adapters, nothing dispatched). It also pins that\nan agent mint descriptor is refused (\"agent mint is forbidden\") and that a\nwrong lane is refused for every step. Against the old `agent` revision\n(582ab9f) it fails as expected. Combined suite: 102 passed on ops-warden a45cf47\nand on current ops-warden. Fixture admission is not owner admission or a pin.\n\n2026-10-17 founder decision: the mint-secret actor is `agent`, not `warden`.\nThe kernel is unchanged. The adapter, its test and the composer were updated and\nthe artifact regenerated; all seven steps pass `Descriptor.decode` and\n`complete_executable_descriptor_set` is true (98 tests passed). The artifact\nstays `not_an_admission`, with no owner review, admission or dispatch permission.\n\n2026-10-17 composer merged (60b41a1 via 3c62bec) with follow-up 376b44b (merged;\ncombined suite 98 passed). `compose-1.2-descriptors.py` loads 1.2 only through an\nexplicit version root and refuses partial, duplicate, unknown or mismatched maps.\n`descriptor-composition-1.2.candidate.json` is a reviewable candidate, marked\n`not_an_admission`, with canonical 1.1 unchanged. ensure-groups and hand-over now\ncarry and honour `parent-session` revocation, and the hand-over owner is\nparameterised (`railiance-apps` for Forgejo). Six of the seven steps pass\n`Descriptor.decode`. mint-secret is still refused: its actor `warden` is outside\nthe kernel's human/agent set, and Platform (with ops-warden, if the kernel should\nchange) must decide. Five steps carry `owner_confirmation_required` names. The\nset is complete only after both. Owner review, the canon section 6 successor\nrevision and admission remain T16's.\n\n2026-10-17 prerequisites merged (65559ef; combined suite 78 passed):\nensure-groups accepts 1.2 via an explicit `version_root='1.2'` (531151b), with\n1.1 still the default. bind-env is now a full Warden v1 `Adapter` (7442d21); its\nkernel-level fixture tests like T13's are still missing, and its `build()`\nsignature changed. Composition is unblocked in source. Owner confirmation of\nthe proposed step, lane and schema names remains before pinning.\n\n2026-10-17 inputs from T24\u2013T29 that composition must resolve: T24 accepts\nonly 1.1 while T28 accepts only 1.2, so add an explicit 1.2 ensure-groups path;\nT27 still needs full Warden `Adapter` wrapping; the lane, schema and owner names\nproposed in T25, T26 and T29 need owner confirmation before pinning.\n\nSplit from T16 on 2026-10-17. Compose the seven-step source 1.2 descriptor set\nthrough the existing explicit version-root selection over T13 and T24\u2013T29. No\npartial descriptor map, canonical 1.1 stays the default, and new descriptor bytes\nneed a reviewed successor revision under canon section 6. Owner review and\nadmission remain T16's.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": "Seven-step 1.2 candidate composes completely and its names are confirmed. Waiting on independent source review of the seven adapters; owner review and admission follow under T16.", + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.655538Z", + "updated_at": "2026-10-17T09:16:33.265303Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb", + "id": "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a", + "record_id": null, + "flavor": null, + "title": "T31 \u2014 Restart and lost-receipt fixtures against Warden's original-intent interface", + "description": "2026-10-17 done (source candidate only; merged in 7dbe75f; combined suite 69 passed): test_registration_original_intent_reconcile.py, 8 tests (commit 74adcd7), runs against the unmodified ops-warden a45cf47. The holder is a test-only in-memory store. For real T15 support the T13 adapter's `build()` needs an injected holder/readback capability, a `reconcile_original` callback, and a `verify` that accepts reconcile results. Rollback after restart stays refused until there is an owner contract.\n\nSplit from T15 on 2026-10-17. ops-warden published the original-intent\nlookup/context interface in `src/warden/case_run.py` (`OriginalIntent`,\n`Journal.original_intent`, `Adapter.reconcile_original`; commit `a45cf47`).\nBuild T15's restart/lost-receipt fixtures for the registration adapter against\nit: the original intent is kept separate from the reconcile attempt; wrong\nattempt/case/source/principal and unavailable-holder cases refuse; and a missing\nor ambiguous record stays uncertain, with no add replay or undo. The interface\nis Warden's own dispatch record, not owner-authenticated created-only provenance.\nSelecting the actual authenticated holder stays a T15 owner gate.", + "status": "done", + "priority": "high", + "assignee": null, + "due_date": null, + "blocking_reason": null, + "needs_human": false, + "intervention_note": null, + "decision_id": null, + "blocked_on": null, + "wait_until": null, + "parent_task_id": null, + "wait_kind": null, + "created_at": "2026-10-17T05:53:10.819590Z", + "updated_at": "2026-10-17T05:59:54.309209Z", + "workstream_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + } +] diff --git a/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/verification.json b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/verification.json new file mode 100644 index 00000000..539f4f0a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/nk48-t15-source-publication-r2/verification.json @@ -0,0 +1,96 @@ +{ + "status": "published-and-projected", + "commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "only_committed_path": "workplans/NK-WP-0048-security-case-types.md", + "projection_status": "applied", + "instance_role": "primary", + "instance_label": "railiance01", + "expected_commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "derived_commit": "3236d5000e52e0643f33906a26a7825aa0b67465", + "original_task_count": 31, + "current_task_count": 31, + "all_task_ids_and_statuses_unchanged": true, + "all_source_task_blocks_unchanged": true, + "T15_task_block": "```task\nid: NK-WP-0048-T15\nstatus: wait\ndepends_on: [NK-WP-0048-T31]\nblocking_reason: \"Source candidate 061461e is merged. Waiting on the owner to select the real authenticated original-attempt holder and native readback, and on independent re-review of the changed T13 adapter source.\"\npriority: high\nstate_hub_task_id: \"1b825705-4848-5edc-832f-b1622cd9bdb7\"\n```", + "preservation": { + "head": "3236d5000e52e0643f33906a26a7825aa0b67465", + "restored_generated_paths": [], + "unrelated_tracked_files_preserved": 65, + "untracked_files_preserved": 7, + "status": "M .custodian-brief.md\n M WORK-RECORDS.md\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-capture/\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-inventory.jsonl\n?? docs/evidence/2026-10-15-cadence-adoption/hourly-observation-monitor.json\n?? intakes/NET-IN-0005-single-use-setup-link-mail-handoff.md" + }, + "native_or_credential_or_adapter_changes": false, + "root_git_brief_work_modified": false, + "canonical_source_record_id_convergence_T24_T31": [ + { + "id": "1a3931ee-3b5f-52fc-9295-4073804f4bd2", + "record_id": "NK-WP-0048-T24", + "status": "done", + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "id": "0de2a7d9-874d-578b-a076-f653fe42e30b", + "record_id": "NK-WP-0048-T25", + "status": "done", + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "id": "94b3cbf0-c908-5ac6-b0db-67385c3e021c", + "record_id": "NK-WP-0048-T26", + "status": "done", + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "id": "e56365f8-29b5-5c89-a329-8115f9f3b2a7", + "record_id": "NK-WP-0048-T27", + "status": "done", + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "id": "6dab281c-ea9a-5a17-a83f-e8e9a4d4484f", + "record_id": "NK-WP-0048-T28", + "status": "done", + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "id": "129db41d-4560-5e20-9f3b-c3c91e0a5601", + "record_id": "NK-WP-0048-T29", + "status": "done", + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "id": "6f6073ac-52dd-5b70-8874-6d76d4c072e4", + "record_id": "NK-WP-0048-T30", + "status": "done", + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + }, + { + "id": "ed1ccd87-89a0-5e69-885c-0eaf5ab0bb5a", + "record_id": "NK-WP-0048-T31", + "status": "done", + "workplan_id": "e31f3f9d-0748-5137-b0a3-7b3ca46a66eb" + } + ], + "canonical_counts": { + "created": 0, + "updated": 64, + "retired": 0, + "refused": 0, + "released": 0, + "created_tasks": 0, + "updated_tasks": 8, + "cancelled_tasks": 0, + "created_edges": 0, + "removed_edges": 0, + "unresolved_edges": 1, + "external_edges": 85 + }, + "reported_unresolved_edges": [ + { + "workplan": "NK-WP-0006", + "from": "NK-WP-0006", + "target": "NK-WP-0001", + "reason": "missing" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/comparison-review.json b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/comparison-review.json new file mode 100644 index 00000000..7a32ed0a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/comparison-review.json @@ -0,0 +1,164 @@ +{ + "schema": "custodian.warden-concurrent-original-context-comparison.v1", + "observed_at": "2026-10-17T05:56:50.251443+00:00", + "verdict": "Concurrent OriginalIntent variant solves local original-dispatch persistence, not independent original-creation authentication. Preserve its additive architecture; apply no older fingerprint-table replacement. Minimal artifact delta addresses concrete local consistency/privacy-contract defects only after root ownership/review.", + "base_state": { + "observed_at": "2026-10-17T05:53:20.364253+00:00", + "owner": "ops-warden", + "canonical_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "sources": [ + { + "path": "src/warden/case_run.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:0574036ca7b74bdb9124f6af4888e63ec231ee60f5c9f34cbe2fb1acee39f671", + "worktree_sha256": "sha256:0574036ca7b74bdb9124f6af4888e63ec231ee60f5c9f34cbe2fb1acee39f671", + "dirty": false + }, + { + "path": "src/warden/case_receipt_state.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc", + "worktree_sha256": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc", + "dirty": false + }, + { + "path": "src/warden/case_inputs.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "worktree_sha256": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "dirty": false + }, + { + "path": "src/warden/__init__.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a", + "worktree_sha256": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a", + "dirty": false + }, + { + "path": "tests/test_case_run.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:bc7264848e76e3576821523fec0c4925c4446eaf470f859fe245c98ea072d139", + "worktree_sha256": "sha256:bc7264848e76e3576821523fec0c4925c4446eaf470f859fe245c98ea072d139", + "dirty": false + }, + { + "path": "docs/security-case-execution-kernel-v1.md", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:0eb563dbaea739fba928d04708c2c2bdac592783bbe68e1271041b0d1b1994cb", + "worktree_sha256": "sha256:0eb563dbaea739fba928d04708c2c2bdac592783bbe68e1271041b0d1b1994cb", + "dirty": false + }, + { + "path": "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:44686df53dac792e46791878c3ccf8c187bb14e2de33eeac411be2005f7f2aa1", + "worktree_sha256": "sha256:44686df53dac792e46791878c3ccf8c187bb14e2de33eeac411be2005f7f2aa1", + "dirty": false + } + ], + "canonical_edits_performed": false + }, + "current_owner_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "current_owner_sources_match_snapshot": true, + "canonical_writes_or_user_tree_tests_performed": false, + "probes": { + "snapshot_diagnostic_probes": 13, + "scope": "Exact copied a45cf475 snapshot; probes passing for documented insecure behaviors mean finding reproduced, not secure/native acceptance.", + "minimal_delta_cases": 54, + "delta_case_scope": "43 existing kernel cases plus11 new metadata/context consistency checks; not additive to prior suites. Source-only generated data and existing synthetic owner trust.", + "lint": "Owner-installed Ruff check and format check pass after removing two unused test imports; no dependency installation.", + "extra_earlier_delta_cases": 56, + "extra_earlier_scope": "Retained diagnostic variant included tenant/realm and missing-original-purpose observations; excluded those from canonical regression proposal to avoid encoding missing native proof as a desired control." + }, + "comparison": [ + { + "topic": "Actual persistence and restart", + "concurrent": "OriginalIntent and running receipt share one SQLite transaction. Actual distinct interpreter retrieves original run identity after lost response. This solves the original local memory-loss/context seam.", + "fingerprint_candidate": "Atomic receipt-linked invocation fingerprint also survives actual distinct interpreter; avoids raw field storage but introduces a second table and a changed reconcile callback.", + "disposition": "Keep existing concurrent intents/reconcile_original surfaces; no duplicate context subsystem." + }, + { + "topic": "Privacy and closed field contract", + "concurrent": "Normal Executor.admit produces bounded declared metadata strings plus empty tenant/realm, not arbitrary owner response payload. No observed real credential leak. OriginalIntent.payload/decode themselves accept arbitrary JSON-valued target and dynamically serialize all Invocation fields; a direct generated probe reproduces this missing public API invariant.", + "fingerprint_candidate": "Stores only a fixed validated projection fingerprint; safer raw-data retention but not necessary to replace the concurrent architecture unilaterally.", + "disposition": "Minimal delta validates exact Invocation/Pins and explicit field set/types/lengths/operations before payload/save and bounded duplicate-free decode. Keep raw identifiers only as the existing declared operational source contract; do not claim tenant/realm provenance from serialization." + }, + { + "topic": "Actor consistency", + "concurrent": "original_intent compares principal/lane/tenant/realm but omits actor. A stored actor=agent is returned against current actor=human.", + "fingerprint_candidate": "Fixed exact invocation fingerprint covers actor.", + "disposition": "Add actor to existing equality comparison; no new permission or migration." + }, + { + "topic": "Original attempt/intent lineage", + "concurrent": "Query reads only payload. Altered payload attempt_id can differ from intents.attempt_id and be returned; removing original running receipt leaves accepted orphan intent. Both reproduced in temporary SQLite fixtures.", + "fingerprint_candidate": "Intent sequence/fingerprint links its context to an original running receipt; no authority inference.", + "disposition": "Minimal delta checks SQL attempt_id equals payload and matches a bounded existing original running receipt with exact step/owner/pins/binding. Save validates intent, invocation and running state agree before atomic write." + }, + { + "topic": "Principal, tenant, realm and current admission", + "concurrent": "Stored mismatched principal/tenant/realm is refused. However Executor.admit has no tenant/realm input or independent provenance; normal admitted values remain empty. Tests replacing the current Invocation fail at admission and do not prove authentic tenant claims.", + "fingerprint_candidate": "Introduces no new tenant/realm fields or migration. Exact current readmission/pins are retained.", + "disposition": "Keep tenant/realm explicitly unknown/reserved; do not add caller-settable claims or relax run-sheet matching. If future nonempty coordinates are needed, require existing authoritative claim/session owner contract and review." + }, + { + "topic": "Independent original proof versus local correspondence", + "concurrent": "ReconcileContext reaches an owner callback, but Adapter.verify and evidence_verifier authenticate the fresh OwnerResult/current attempt and current revocations. No original-context-specific verifier purpose is emitted. Intent is not independent creation evidence and cannot itself establish created-only undo.", + "fingerprint_candidate": "Adds explicit original/fresh-context proof purpose; no actual native verifier/holder supplied.", + "disposition": "This is an unresolved native owner adapter/readback contract, not proof of a live bypass. Define exact independent original payload authentication in the admitted owner adapter/verification contract before native integration. Do not add an unadmitted second verifier/holder or treat callback context/JSON as assent. Minimal consistency delta intentionally adds no new native proof purpose." + }, + { + "topic": "Legacy and API compatibility", + "concurrent": "Existing one-argument Adapter.reconcile remains usable; new reconcile_original opts into original-intent lookup and refuses missing records. Existing positional Adapter construction and Invocation defaults remain callable. v1 receipt JSON stays unchanged. Invocation.asdict now includes two extra default fields, so registration attempt payload/source consumers require explicit review before native admission.", + "fingerprint_candidate": "Changes reconcile callback signature to two arguments; existing injected one-argument callbacks fail closed.", + "disposition": "Retain additive callback compatibility. Missing legacy original intent is never backfilled for new original-aware path. No installed actual native adapters/verifier/CLI are found in inspected current sources." + }, + { + "topic": "Parent sessions and unknown outcomes", + "concurrent": "Current caller/session checks and independent fresh parent-session/grant revocation remain enforced; withholding fresh parent revocation refuses. Original session proof is not retrieved/authenticated from intent; actual original holder must supply it. Lost reply and interrupted undo stay uncertainty gates.", + "fingerprint_candidate": "Same current checks plus explicit contextual proof purpose; no real original/session holder.", + "disposition": "Keep native gate closed until independently authenticated original result/revocation and current native UID/version checks exist. Changed session/run-sheet/caller pins cannot inherit source state automatically." + } + ], + "minimal_delta": { + "path": "minimal-integration.patch", + "base_commit": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "changes": [ + { + "path": "src/warden/case_run.py", + "before_sha256": "sha256:0574036ca7b74bdb9124f6af4888e63ec231ee60f5c9f34cbe2fb1acee39f671", + "after_sha256": "sha256:0cc62a33dbb3b90276b291f77fa5d0a906f19296874d68636957763f576d1ae2" + }, + { + "path": "tests/test_case_original_context_consistency.py", + "before_sha256": null, + "after_sha256": "sha256:8483bb5915701e8bd0749e85097e11076ffd539084ca68e9ec1c565babc157d1" + } + ], + "snapshot_patch_check": 0, + "scope": "Only original-intent validation and matching + new focused tests; keeps intents schema, Invocation fields and reconcile_original API. No owner adapter, credential, native source activation, original-proof grant, task UUID/status or production mutation.", + "apply_rule": "Root must recheck exact current hashes and coordinate implementation owner. This artifact is not an instruction to overwrite concurrent changes." + }, + "actual_consumers_inspected": [ + { + "path": "ops-warden/src/warden/case_run.py", + "finding": "No installed default adapters/verifier/CLI; interfaces are injected fixed owner source." + }, + { + "path": "ops-warden/tests/test_case_run.py", + "finding": "Only inspected reconcile_original consumer is synthetic fixture by_original, which delegates to old.reconcile(ctx.attempt); it does not independently authenticate original custody." + }, + { + "path": "net-kingdom/sso-mfa/k8s/keycape/keycape-registration-adapter.py", + "finding": "Maintained registration adapter builds existing six positional Adapter fields and has no reconcile callback. Process-local creation and post-restart undo refusal remain." + }, + { + "path": "net-kingdom/tools/security-case-types/security_case_evidence.py", + "finding": "Reads unchanged v1 receipts only; native_authenticated remains false. It is no original context/owner authenticator." + } + ], + "next_native_gate": "Unchanged NK48T15/T16/WARDEN44T03: actual independent original holder/readback, exact source/tenant/principal binding, original/current session proof and native revision/created-only custody remain absent. No gate closure or owner assent inferred.", + "older_candidate_disposition": "original-context-r1 remains retained for design/failure history only. Its replacement table/API patch must not be applied over a45cf475; minimal-integration.patch is the non-overwriting proposal on this actual variant.", + "failures_retained": "Initial minimal-delta lint found two unused test imports; removed. Snapshot diagnostic and changed-fixture probe results remain recorded separately." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/docs/security-case-execution-kernel-v1.md b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/docs/security-case-execution-kernel-v1.md new file mode 100644 index 00000000..35289d77 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/docs/security-case-execution-kernel-v1.md @@ -0,0 +1,127 @@ +# Closed case execution kernel v1 — 2026-10-11 + +Owner implementation record for WARDEN-WP-0044-T03. The architectural proposal +at SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was accepted through memo `infd-20261010-security-case-execution`, version 1, +approval e46b149b-ec3c-4b3b-98a7-dcb4934448ca, confirmed disposition +disp-cf44dacb-2117-4de7-8f90-cfa99f84f325 at 2026-10-10T23:23:31Z. The original +approved proposal remains byte-identical. That decision accepts source design; +it grants no execution session, credential or deployment authority. + +`src/warden/case_run.py` advances the pure reducer into an executable injected +owner harness with private durable receipts. Descriptor version +`warden.case.execution.v1` is a closed local implementation seam, not an accepted +NetKingdom type schema. It contains fixed owner/adapter/source digest, actor, +lane, target parameter, effect, reviewed created-object identifiers declared +retry safety and explicit required revocation scopes (parent-session and broker-grant). +A mint descriptor cannot omit either scope. Unknown fields, free shell text and secret parameters are refused. +An acknowledged type must additionally pin the descriptor bytes under its +`execution_descriptors` map and independent owner evidence must authenticate +that exact machine-seam revision. Existing descriptive types lack this map and +remain plan-only. Their acknowledged labels do not authorize this new seam. + +There are no installed owner adapters, native provenance verifier or dispatch CLI +in this change. Callables are injected only through a fixed adapter registry, +with immutable owner reference/source pins. A real integration must supply +independent owner/native verification or explicit attended manual evidence; +a bare JSON receipt identifier is insufficient. Fixture verifiers in tests +are deliberately fake and attest no native case. Caller sessions are independently +verified for exact principal/actor/lane at admission and again before dispatch; +input actor labels alone are never sufficient. Agent mint is refused, including +when WARDEN_AGENT_ID appears after initial human admission. + +Each admission pins exact case/type/descriptor/catalog bytes, acceptance reference +and run-sheet identity. Actual ops-mason materialization evidence is required +against the case source, separately from its status label. Case parameters supply +an exact metadata target. Source/session changes cannot inherit journal success. +Owner results have a closed metadata shape, result evidence fingerprint and +separate independently authenticated grant/parent-session revocation evidence +fingerprints. An aggregate revoked boolean alone cannot satisfy either scope. Unknown result payloads, unverified evidence, absent revocation and +created objects outside the reviewed scope become uncertain, never success. +Python and OS stdout/stderr, including inherited child-process output, are +discarded without buffering or retention under a serialized fd containment +context. Independent provenance verification uses the same containment and +sanitizes exceptions. All source inputs are capped at 256 KiB before parsing. +Error text is not copied to the journal. KeyboardInterrupt/SystemExit and other +owner interruptions are rethrown with a fixed message while retaining running +intent for crash recovery. + +The journal uses an owner-private 0700 directory and 0600 files, refuses symlinks +and insecure modes, uses SQLite FULL synchronization and commits running intent +before calling the adapter. A separate process lock prevents concurrent mutation. +A crash between dispatch and final receipt leaves running; the next lock holder +persists uncertain before refusing replay. Both intent and outcome remain in +history. Each persisted attempt has a unique identity bound to all exact pins, +operation (run/reconcile/rollback), sequence and nonce. Adapter results and both +revocation proof purposes carry that identity. Prior-attempt evidence cannot +verify a safe retry, even if a caller rewrites result labels. Descriptor-created +objects are capped at 32 and histories at 128 transitions, with dispatch stopped +before archival is required. A throw or lost response is not failed-before-change. A safe retry needs +verified failed-before-change, unchanged pins, descriptor retry-safe and explicit +retry. Success and no-change cannot replay. + +Reconciliation is an explicit fixed owner callback, never the mutation callback. +It may verify an already-present native CAS result as no-change/success, or prove +failed-before-change; it never tells Warden to regenerate a credential. Uncertain +or failed-after-change requires this owner-native inspection before any retry. +Owner proof and revocation remain mandatory. Rollback visits only objects in a +verified creation receipt, in reverse order; no-change has no undo and shared +objects are excluded. Unknown/lost undo blocks another rollback. + +The kernel operates individual admitted steps. Full ordered run-sheet orchestration, +verification inventory, contained login integration, native owner adapters and +CLI activation remain WARDEN-WP-0044-T03; no overall native-complete result is +implemented. Current Forgejo source is not materialized and has no acknowledged +machine descriptors. Its first native run remains an acceptance requirement, +with real materialization, separately authenticated broker-grant and parent-session +revocation receipts, session/owner receipts and consumer enforcement. This +implementation neither promotes a type nor changes any case status. + +Validation: 37 new kernel tests plus 14 reducer tests pass (51 total). Tests open +an independent SQLite connection inside dispatch to prove running was committed, +crash after mutation and reopen a fresh journal to prove no duplicate invocation, +lose a response then reconcile without mint, require explicit retry, check exact +pins/session expiry, withhold malicious output and unconfirmed revocation, and +prove created-only reverse undo plus uncertain-undo refusal. Ruff formatting and +checks pass. No live token or target action occurred. + +## Browser input projection — 2026-10-16 source preparation + +An explicitly configured `BrowserInputs(netkingdom_root)` now validates browser +cases before admission. It calls the real NetKingdom type/case validator over +verified producer bytes in a disposable Python process (`-I`, empty environment, +timeout, no inherited output). The validator, composer, capability contract and lazy evidence +modules have exact source pins; missing or changed code refuses. There is no +automatic discovery or download. A producer upgrade needs reviewed pin changes. + +Only the exact reviewed 1.1/1.2 browser parameter definitions qualify. A digest +of each complete parameter definition list prevents reclassifying secrets as +metadata, loosening constraints, or adding fields under a recognized version. +The complete case passes producer validation, including required fields, +constraints and credential-shape checks. Explicit fixed/default overrides, +unknown fields and secret references refuse. Security-sensitive browser metadata +can therefore be present without being passed to an owner adapter: the only +projection is its descriptor's exact public/operational target identifier. +No raw parameter values are added to invocations or persisted receipts. + +`require_implemented=False` validates the acknowledged type's source, not its +execution authority. Existing descriptor/type/source/catalog/caller provenance +gates stay mandatory. Neither historical browser type contains the required +exact machine-descriptor map, and neither is promoted by this change. There are +still no installed native owner adapters, authenticated provenance verifier, +contained session/ordered run orchestration or live CLI dispatch. A journal digest +is a fingerprint and cannot authenticate native evidence. Completion still needs +owner-admitted descriptors and actual structural, mint/delivery, consumer and +revocation/rollback evidence. Local fixture success is not native acceptance. + +Tests use the actual producer checkout (override sibling location with +`WARDEN_TEST_NETKINGDOM_ROOT`), the real Executor, SQLite journal and injected +test-only owner adapter/evidence verifier. Their descriptor extensions and case +materialized labels exist only in memory. Canonical case/type bytes stay intact. + +Producer integration refinement: the accepted NetKingdom browser-case patch +`d32a7f51cbf3012bee3e4bb329f4259dea867eff1c59a2b01a65d72774488d05` +is now the tested producer revision. Its evidence module is imported lazily only +by `check-evidence`, outside this admission path; Warden nevertheless pins and +copies it with the complete reviewed producer set. Missing or modified bytes in +any of the four modules refuse admission. The test covers every dependency. diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/pytest.ini b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/pytest.ini new file mode 100644 index 00000000..fcccae19 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +pythonpath = src diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/__init__.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/__init__.py new file mode 100644 index 00000000..4525f1e2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/__init__.py @@ -0,0 +1,3 @@ +"""OpsWarden — SSH CA and certificate lifecycle manager.""" + +__version__ = "0.1.0" diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_inputs.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_inputs.py new file mode 100644 index 00000000..9c876d79 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_inputs.py @@ -0,0 +1,98 @@ +"""Reviewed browser metadata projection; no native trust or command dispatch. + +The producer validator and all reviewed producer modules are pinned below. +Their verified bytes run in a disposable tree, without caller environment or +ambient Python imports. Type parameters must match the exact reviewed version. +Machine descriptor extensions still require Executor's independent admission. +""" + +import hashlib +import json +from pathlib import Path +import subprocess +import sys +import tempfile + +from .case_receipt_state import Refused + +# Reviewed producer: net-kingdom 015694ee414c802dc2ef1fa0120dfed610086e69. +# Review: docs/reviews/2026-10-16-browser-producer-pin-refresh.md +SOURCE_PINS = { + "tools/security-case-types/security_case_evidence.py": "da8efa59c52ad73283397ebe639ae259d0cb9c6348b5cb829135c24230dcdc75", + "tools/security-case-types/security_case_validator.py": "1bcc62e9bc374c711363295549f3cd5bb0b32952671ed4b08fddc7f74742959b", + "tools/security-scenario-composer/security_scenario_composer.py": "9f0cc69930aa8ee2cc3fb92236e6a99728df17b8c0fe36e1250241c38fc01c4f", + "tools/playbook-capability-contract/playbook_contract_validator.py": "2e1e0c809d4bc9d64c2d974cc23fecd1c5636785154c2554edd9191852fd926d", +} +PARAMETER_PINS = { + "1.1": "9ec2d6113bf70dbd35cfc247412363ff187e792bc82e24159a6f78e090aa3e4c", + "1.2": "c04385d176f5f70257fb0fa6fef7e48ab26ed7cf26f6b1c7f14b16237b0ebea6", +} + +_VALIDATE = """ +import json, sys +from pathlib import Path +sys.path.insert(0, str(Path(sys.argv[1]) / 'tools/security-case-types')) +import security_case_validator as v +case, typ = json.load(sys.stdin) +issues = v.validate_type(typ) + v.validate_case(case, typ, require_implemented=False) +print('valid' if not v.has_errors(issues) else 'invalid') +""" + + +class BrowserInputs: + """Explicit owner configuration, never inferred from a case-supplied path.""" + + def __init__(self, netkingdom_root: Path): + self.root = Path(netkingdom_root) + + def target(self, case: dict, typ: dict, target_parameter: str) -> str: + try: + spec = typ["spec"] + parameters = spec["parameters"] + fingerprint = hashlib.sha256( + json.dumps(parameters, sort_keys=True, separators=(",", ":")).encode() + ).hexdigest() + if ( + spec["id"] != "oidc-browser-relying-party" + or PARAMETER_PINS.get(spec["version"]) != fingerprint + ): + raise ValueError + declared = {item["name"]: item for item in parameters} + definition = declared.get(target_parameter, {}) + if ( + definition.get("sensitivity") not in ("public", "operational") + or definition.get("case_settable") is not True + ): + raise ValueError + # No secret references or newly classified metadata are grandfathered. + for name in case["spec"]["parameters"]: + item = declared.get(name, {}) + if item.get("case_settable") is not True or item.get( + "sensitivity" + ) not in ("public", "operational", "security_sensitive"): + raise ValueError + with tempfile.TemporaryDirectory( + prefix="warden-case-validator-" + ) as directory: + for relative, expected in SOURCE_PINS.items(): + source = (self.root / relative).read_bytes() + if hashlib.sha256(source).hexdigest() != expected: + raise ValueError + dest = Path(directory) / relative + dest.parent.mkdir(parents=True, exist_ok=True) + dest.write_bytes(source) + result = subprocess.run( + [sys.executable, "-I", "-c", _VALIDATE, directory], + input=json.dumps([case, typ]), + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + env={}, + timeout=10, + check=True, + ) + if result.stdout != "valid\n": + raise ValueError + return case["spec"]["parameters"][target_parameter] + except (OSError, ValueError, TypeError, KeyError, subprocess.SubprocessError): + raise Refused("browser metadata failed exact-version validation") from None diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_receipt_state.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_receipt_state.py new file mode 100644 index 00000000..03a53e99 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_receipt_state.py @@ -0,0 +1,146 @@ +"""Proposed, pure case receipt reducer for WARDEN-WP-0044-T03. + +This prototype executes nothing and implements no accepted owner adapter API. +Callers MUST durably persist a returned running receipt before a side effect. +Only metadata identifiers/digests enter this model; no command output is accepted. +""" +from __future__ import annotations + +from dataclasses import dataclass, replace +from enum import Enum +import re + +_ID = re.compile(r"[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}\Z") +_DIGEST = re.compile(r"sha256:[0-9a-f]{64}\Z") + + +class Refused(ValueError): + """A transition requires an independent owner reconciliation or new review.""" + + +class Outcome(str, Enum): + RUNNING = "running" + SUCCESS = "verified-success" + NO_CHANGE = "verified-no-change" + FAILED_BEFORE = "failed-before-change" + FAILED_AFTER = "failed-after-change" + UNCERTAIN = "uncertain" + ROLLBACK_RUNNING = "rollback-running" + ROLLED_BACK = "verified-rolled-back" + ROLLBACK_UNCERTAIN = "rollback-uncertain" + + +@dataclass(frozen=True) +class Pins: + """Exact reviewed case/type/adapter/catalog/acceptance/run-sheet digests.""" + + case: str + case_type: str + adapter: str + catalog: str + acceptance: str + run_sheet: str + + def __post_init__(self) -> None: + if not all(_DIGEST.fullmatch(value) for value in vars(self).values()): + raise Refused("pins require sha256 digests") + + +@dataclass(frozen=True) +class Receipt: + attempt: int + outcome: Outcome + created: tuple[str, ...] = () + + +@dataclass(frozen=True) +class StepState: + step_id: str + owner: str + pins: Pins + allowed_created: tuple[str, ...] = () + retry_safe: bool = False + receipts: tuple[Receipt, ...] = () + + def __post_init__(self) -> None: + if not all(_ID.fullmatch(v) for v in (self.step_id, self.owner, *self.allowed_created)): + raise Refused("only metadata identifiers are supported") + if len(set(self.allowed_created)) != len(self.allowed_created): + raise Refused("duplicate reviewed object identifier") + + @property + def last(self) -> Receipt | None: + return self.receipts[-1] if self.receipts else None + + +def _same_pins(state: StepState, pins: Pins) -> None: + if state.pins != pins: + raise Refused("pins changed; a newly reviewed sheet is required") + + +def _append(state: StepState, outcome: Outcome, created: tuple[str, ...] = ()) -> StepState: + receipt = Receipt(len(state.receipts) + 1, outcome, created) + return replace(state, receipts=(*state.receipts, receipt)) + + +def begin(state: StepState, pins: Pins, *, retry: bool = False) -> StepState: + """Return intent to dispatch. Persist before calling an owner adapter.""" + _same_pins(state, pins) + last = state.last + if last is None: + if retry: + raise Refused("no failed attempt to retry") + elif not (last.outcome is Outcome.FAILED_BEFORE and state.retry_safe and retry): + raise Refused("step cannot dispatch or replay without owner reconciliation") + return _append(state, Outcome.RUNNING) + + +def finish(state: StepState, pins: Pins, outcome: Outcome, + *, created: tuple[str, ...] = ()) -> StepState: + """Accept only an adapter's verified, closed metadata result, never output.""" + _same_pins(state, pins) + if not state.last or state.last.outcome is not Outcome.RUNNING: + raise Refused("no running side effect") + if not isinstance(outcome, Outcome) or outcome not in (Outcome.SUCCESS, Outcome.NO_CHANGE, Outcome.FAILED_BEFORE, + Outcome.FAILED_AFTER, Outcome.UNCERTAIN): + raise Refused("unsupported adapter result") + if len(set(created)) != len(created) or not set(created) <= set(state.allowed_created): + raise Refused("created objects are outside the reviewed case") + if created and outcome not in (Outcome.SUCCESS, Outcome.FAILED_AFTER): + raise Refused("this result cannot claim created objects") + return _append(state, outcome, created) + + +def recover(state: StepState) -> StepState: + """Lost result after persisted dispatch is uncertain, never safe to retry.""" + if state.last and state.last.outcome is Outcome.RUNNING: + return _append(state, Outcome.UNCERTAIN) + if state.last and state.last.outcome is Outcome.ROLLBACK_RUNNING: + return _append(state, Outcome.ROLLBACK_UNCERTAIN, state.last.created) + return state + + +def rollback_objects(state: StepState, pins: Pins) -> tuple[str, ...]: + """Reviewed identifiers only, reverse created order; no shared-object undo.""" + _same_pins(state, pins) + last = state.last + if last is None or last.outcome is Outcome.NO_CHANGE: + return () + if last.outcome is not Outcome.SUCCESS: + raise Refused("rollback needs a verified successful creation receipt") + return tuple(reversed(last.created)) + + +def begin_rollback(state: StepState, pins: Pins) -> StepState: + objects = rollback_objects(state, pins) + if not objects: + raise Refused("no case-created objects to undo") + return _append(state, Outcome.ROLLBACK_RUNNING, objects) + + +def finish_rollback(state: StepState, pins: Pins, *, verified: bool) -> StepState: + _same_pins(state, pins) + if not state.last or state.last.outcome is not Outcome.ROLLBACK_RUNNING: + raise Refused("no running rollback") + outcome = Outcome.ROLLED_BACK if verified else Outcome.ROLLBACK_UNCERTAIN + return _append(state, outcome, state.last.created) diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_run.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_run.py new file mode 100644 index 00000000..73c7d023 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/src/warden/case_run.py @@ -0,0 +1,981 @@ +"""Closed local execution kernel v1; no installed adapters or live CLI dispatch. + +An owner must acknowledge each descriptor and provide an independent provenance +verifier. Descriptive case types remain plan-only. Nothing executes shell prose. +""" + +from __future__ import annotations + +from contextlib import contextmanager, redirect_stderr, redirect_stdout +from dataclasses import asdict, dataclass, replace +import fcntl +import hashlib +import json +import os +from pathlib import Path +import re +import sqlite3 +import threading +import uuid +from typing import Callable + +import yaml +from .case_inputs import BrowserInputs +from .case_receipt_state import ( + Outcome, + Pins, + Receipt, + Refused, + StepState, + begin, + finish, + recover, + begin_rollback, + finish_rollback, +) + +IDENTIFIER = re.compile(r"[a-zA-Z0-9][a-zA-Z0-9_.:/@-]{0,191}\Z") +MAX_SOURCE_BYTES = 262144 +MAX_HISTORY = 128 +OUTPUT_LOCK = threading.RLock() + + +def bounded_source(value: bytes): + if type(value) is not bytes or len(value) > MAX_SOURCE_BYTES: + raise Refused("source exceeds bounded input contract") + + +@contextmanager +def contained_output(): + """Discard Python and inherited OS stdout/stderr; serialize process fd changes.""" + with OUTPUT_LOCK: + saved = [] + null = os.open(os.devnull, os.O_WRONLY) + try: + for target in (1, 2): + saved.append((target, os.dup(target))) + os.dup2(null, target) + with redirect_stdout(_Discard()), redirect_stderr(_Discard()): + try: + yield + except Exception: + raise Refused("owner callback failed") from None + except BaseException: + raise OwnerInterrupted() from None + finally: + for target, original in saved: + os.dup2(original, target) + os.close(original) + os.close(null) + + +DIGEST = re.compile(r"sha256:[0-9a-f]{64}\Z") + + +def digest(value: bytes) -> str: + return "sha256:" + hashlib.sha256(value).hexdigest() + + +@dataclass(frozen=True, slots=True) +class Descriptor: + version: str + step: str + owner: str + adapter: str + adapter_source: str + actor: str + lane: str + target_parameter: str + effect: str + allowed_created: tuple[str, ...] + retry_safe: bool + revocation_scopes: tuple[str, ...] + + @classmethod + def decode(cls, source: bytes) -> Descriptor: + bounded_source(source) + try: + obj = json.loads(source) + if set(obj) != set(cls.__dataclass_fields__): + raise ValueError + if not isinstance(obj["allowed_created"], list): + raise ValueError + obj["allowed_created"] = tuple(obj["allowed_created"]) + if not isinstance(obj["revocation_scopes"], list): + raise ValueError + obj["revocation_scopes"] = tuple(obj["revocation_scopes"]) + result = cls(**obj) + identifiers = ( + result.step, + result.owner, + result.adapter, + result.lane, + result.target_parameter, + *result.allowed_created, + ) + if result.version != "warden.case.execution.v1" or not all( + isinstance(v, str) and IDENTIFIER.fullmatch(v) for v in identifiers + ): + raise ValueError + if not DIGEST.fullmatch(result.adapter_source) or result.actor not in ( + "human", + "agent", + ): + raise ValueError + if ( + result.effect not in ("read", "change", "mint") + or type(result.retry_safe) is not bool + ): + raise ValueError + scopes = set(result.revocation_scopes) + if ( + len(scopes) != len(result.revocation_scopes) + or not scopes <= {"parent-session", "broker-grant"} + or "parent-session" not in scopes + ): + raise ValueError + if result.effect == "mint" and "broker-grant" not in scopes: + raise ValueError + if len(result.allowed_created) > 32: + raise ValueError + if len(set(result.allowed_created)) != len(result.allowed_created): + raise ValueError + return result + except (ValueError, TypeError, KeyError): + raise Refused("invalid closed execution descriptor") from None + + +@dataclass(frozen=True, slots=True) +class OwnerResult: + outcome: Outcome + evidence: str + created: tuple[str, ...] = () + revoked: bool = False + session_revocation_evidence: str | None = None + grant_revocation_evidence: str | None = None + attempt_id: str = "" + operation: str = "" + + +@dataclass(frozen=True, slots=True) +class Invocation: + step: str + owner: str + target: str + actor: str + principal: str + lane: str + pins: Pins + attempt_id: str = "" + operation: str = "admission" + tenant: str = "" + realm: str = "" + + +INTENT_SCHEMA = "warden.case.original-intent.v1" + + +@dataclass(frozen=True, slots=True) +class OriginalIntent: + """Durable record of the original mutating dispatch, written before dispatch. + + Binds the original Invocation (attempt, step, owner, target, actor, principal, + lane), all pins, the admitted adapter source and the tenant/realm provenance + the caller was admitted with. It is evidence of what Warden dispatched, not + owner proof of creation: owners still authenticate created-only provenance. + """ + + binding: str + invocation: Invocation + source: str + + def validate(self) -> None: + """Fixed bounded metadata only; tenant/realm are not authority claims. + + Current admit leaves tenant/realm empty. Nonempty future coordinates need + separate owner provenance; serializability alone must never supply it. + """ + if ( + type(self.invocation) is not Invocation + or type(self.invocation.pins) is not Pins + ): + raise Refused("exact original invocation required") + i = self.invocation + fields = (i.step, i.owner, i.target, i.principal, i.lane, i.attempt_id) + if ( + not all( + type(value) is str and IDENTIFIER.fullmatch(value) for value in fields + ) + or type(i.actor) is not str + or i.actor not in ("human", "agent") + or type(i.operation) is not str + or i.operation not in ("run", "rollback") + or not all( + type(value) is str and (not value or IDENTIFIER.fullmatch(value)) + for value in (i.tenant, i.realm) + ) + or type(self.source) is not str + or not DIGEST.fullmatch(self.source) + or type(self.binding) is not str + or self.binding != f"{i.step}:{i.target}" + ): + raise Refused("invalid original intent metadata") + if set(Invocation.__dataclass_fields__) != { + "step", + "owner", + "target", + "actor", + "principal", + "lane", + "pins", + "attempt_id", + "operation", + "tenant", + "realm", + }: + raise Refused("original invocation field contract changed") + if not all( + type(v) is str and DIGEST.fullmatch(v) + for v in ( + i.pins.case, + i.pins.case_type, + i.pins.adapter, + i.pins.catalog, + i.pins.acceptance, + i.pins.run_sheet, + ) + ): + raise Refused("invalid original intent pins") + + def payload(self) -> str: + self.validate() + return json.dumps( + {"schema": INTENT_SCHEMA, "binding": self.binding, "source": self.source} + | asdict(self.invocation), + separators=(",", ":"), + sort_keys=True, + ) + + @classmethod + def decode(cls, raw: str) -> OriginalIntent: + def unique(pairs): + result = {} + for key, value in pairs: + if key in result: + raise Refused("duplicate original intent field") + result[key] = value + return result + + try: + if type(raw) is not str or len(raw.encode()) > MAX_SOURCE_BYTES: + raise Refused("original intent input exceeds bound") + obj = json.loads(raw, object_pairs_hook=unique) + fields = { + "step", + "owner", + "target", + "actor", + "principal", + "lane", + "pins", + "attempt_id", + "operation", + "tenant", + "realm", + } + if ( + type(obj) is not dict + or set(obj) != fields | {"schema", "binding", "source"} + or obj["schema"] != INTENT_SCHEMA + ): + raise Refused("unknown original intent schema") + inv = {k: obj[k] for k in fields} + inv["pins"] = Pins(**inv["pins"]) + result = cls(obj["binding"], Invocation(**inv), obj["source"]) + result.validate() + return result + except (TypeError, ValueError, KeyError): + raise Refused("invalid original intent record") from None + + +@dataclass(frozen=True, slots=True) +class ReconcileContext: + """Original intent and the new reconciliation attempt, kept separate.""" + + original: OriginalIntent + attempt: Invocation + + +@dataclass(frozen=True, slots=True) +class Adapter: + reference: str + source: str + invoke: Callable[[Invocation], OwnerResult] + verify: Callable[[Invocation, OwnerResult], bool] + rollback: Callable[[Invocation, tuple[str, ...]], OwnerResult] | None = None + reconcile: Callable[[Invocation], OwnerResult] | None = None + reconcile_original: Callable[[ReconcileContext], OwnerResult] | None = None + + +class Journal: + """Private SQLite journal plus process lock; transaction commits before dispatch.""" + + def __init__(self, directory: Path): + directory.mkdir(mode=0o700, parents=True, exist_ok=True) + if ( + directory.is_symlink() + or directory.stat().st_uid != os.getuid() + or directory.stat().st_mode & 0o077 + ): + raise Refused("journal directory is not private") + self.directory = directory + for name in ("journal.sqlite", "execution.lock"): + path = directory / name + fd = os.open(path, os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600) + try: + if os.fstat(fd).st_uid != os.getuid() or os.fstat(fd).st_mode & 0o077: + raise Refused("journal file is not private") + finally: + os.close(fd) + self.db = sqlite3.connect(directory / "journal.sqlite", isolation_level=None) + self.db.execute("PRAGMA synchronous=FULL") + self.db.execute( + "CREATE TABLE IF NOT EXISTS receipts (seq INTEGER PRIMARY KEY, binding TEXT NOT NULL, payload TEXT NOT NULL)" + ) + self.db.execute( + "CREATE TABLE IF NOT EXISTS intents (seq INTEGER PRIMARY KEY, attempt_id TEXT NOT NULL UNIQUE, binding TEXT NOT NULL, payload TEXT NOT NULL)" + ) + + def original_intent( + self, binding: str, current: Invocation, source: str + ) -> OriginalIntent: + """Latest original run intent for binding, refused unless it matches exactly. + + Missing or mismatched records refuse; the step stays uncertain. + """ + row = self.db.execute( + "SELECT attempt_id,payload FROM intents WHERE binding=? ORDER BY seq DESC LIMIT 1", + (binding,), + ).fetchone() + if row is None: + raise Refused("original intent record missing; step stays uncertain") + intent = OriginalIntent.decode(row[1]) + o = intent.invocation + if row[0] != o.attempt_id: + raise Refused("original intent attempt identity differs") + if intent.binding != binding or o.operation != "run": + raise Refused("original intent binding mismatch") + if o.pins != current.pins: + raise Refused("original intent pins mismatch") + if intent.source != source: + raise Refused("original intent source mismatch") + for name in ( + "step", + "owner", + "target", + "actor", + "principal", + "lane", + "tenant", + "realm", + ): + if getattr(o, name) != getattr(current, name): + raise Refused(f"original intent {name} mismatch") + # Match the persisted original running receipt. An orphan context or a + # payload from another attempt cannot become the current lookup key. + receipts = self.db.execute( + "SELECT payload FROM receipts WHERE binding=? ORDER BY seq DESC LIMIT ?", + (binding, MAX_HISTORY + 1), + ).fetchall() + if len(receipts) > MAX_HISTORY: + raise Refused("original intent receipt history exceeds bound") + matched = False + try: + for (raw,) in receipts: + if type(raw) is not str or len(raw.encode()) > MAX_SOURCE_BYTES: + raise Refused("original receipt input exceeds bound") + payload = json.loads(raw) + if ( + payload.get("attempt_id") != o.attempt_id + or payload.get("operation") != "run" + ): + continue + last = payload.get("receipts", [])[-1] + if last.get("outcome") != "running": + continue + if ( + payload.get("journal_schema") != "warden.case.step-journal.v1" + or payload.get("step_id") != o.step + or payload.get("owner") != o.owner + or payload.get("pins") != asdict(o.pins) + ): + raise Refused("original running receipt binding differs") + matched = True + break + except (TypeError, ValueError, KeyError, IndexError, AttributeError): + raise Refused("invalid original running receipt") from None + if not matched: + raise Refused("original running receipt missing; no backfill") + return intent + + @contextmanager + def locked(self): + with (self.directory / "execution.lock").open("rb") as fh: + try: + fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + raise Refused("case execution already active") from None + try: + yield + finally: + fcntl.flock(fh, fcntl.LOCK_UN) + + def load(self, binding: str, initial: StepState) -> StepState: + row = self.db.execute( + "SELECT payload FROM receipts WHERE binding=? ORDER BY seq DESC LIMIT 1", + (binding,), + ).fetchone() + if row is None: + return initial + obj = json.loads(row[0]) + expected = { + "step_id", + "owner", + "pins", + "allowed_created", + "retry_safe", + "receipts", + "journal_schema", + "evidence_digest", + "session_revoked", + "session_revocation_digest", + "grant_revocation_digest", + "attempt_id", + "operation", + } + if ( + set(obj) != expected + or obj["journal_schema"] != "warden.case.step-journal.v1" + ): + raise Refused("unknown journal receipt schema") + state = StepState( + obj["step_id"], + obj["owner"], + Pins(**obj["pins"]), + tuple(obj["allowed_created"]), + obj["retry_safe"], + tuple( + Receipt(r["attempt"], Outcome(r["outcome"]), tuple(r["created"])) + for r in obj["receipts"] + ), + ) + if ( + state.step_id, + state.owner, + state.pins, + state.allowed_created, + state.retry_safe, + ) != ( + initial.step_id, + initial.owner, + initial.pins, + initial.allowed_created, + initial.retry_safe, + ): + raise Refused("journal pins changed") + return state + + def save( + self, + binding: str, + state: StepState, + evidence: OwnerResult | None = None, + invocation: Invocation | None = None, + intent: OriginalIntent | None = None, + ): + if intent is not None: + if type(intent) is not OriginalIntent: + raise Refused("exact original intent required") + intent.validate() + expected = ( + Outcome.RUNNING + if intent.invocation.operation == "run" + else Outcome.ROLLBACK_RUNNING + ) + if ( + intent.binding != binding + or intent.invocation != invocation + or intent.invocation.pins != state.pins + or not state.last + or state.last.outcome is not expected + ): + raise Refused("original intent and running receipt differ") + self.db.execute("BEGIN IMMEDIATE") + try: + if intent is not None: + self.db.execute( + "INSERT INTO intents(attempt_id,binding,payload) VALUES (?,?,?)", + (intent.invocation.attempt_id, binding, intent.payload()), + ) + self.db.execute( + "INSERT INTO receipts(binding,payload) VALUES (?,?)", + ( + binding, + json.dumps( + asdict(state) + | { + "journal_schema": "warden.case.step-journal.v1", + "attempt_id": invocation.attempt_id if invocation else None, + "operation": invocation.operation if invocation else None, + "evidence_digest": digest(evidence.evidence.encode()) + if evidence + else None, + "session_revoked": evidence.revoked if evidence else None, + "session_revocation_digest": digest( + evidence.session_revocation_evidence.encode() + ) + if evidence and evidence.session_revocation_evidence + else None, + "grant_revocation_digest": digest( + evidence.grant_revocation_evidence.encode() + ) + if evidence and evidence.grant_revocation_evidence + else None, + }, + separators=(",", ":"), + ), + ), + ) + self.db.execute("COMMIT") + except BaseException: + self.db.execute("ROLLBACK") + raise + + +class Executor: + """Injected fixed owner adapters and provenance verifier; none ship by default. + + evidence verifier must authenticate owner custody/provenance or explicitly + attest attended manual evidence; a bare JSON reference is never sufficient. + It receives purpose, exact source digest, and named evidence reference. + """ + + def __init__( + self, + journal: Journal, + adapters: dict[str, Adapter], + evidence_verifier: Callable[[str, str, str], bool], + *, + browser_inputs: BrowserInputs | None = None, + ): + self.journal, self.adapters, self.evidence_verifier = ( + journal, + dict(adapters), + evidence_verifier, + ) + self._admitted = {} + self.browser_inputs = browser_inputs + + def admit( + self, + case_source: bytes, + type_source: bytes, + descriptor_source: bytes, + catalog_source: bytes, + *, + principal: str, + actor: str, + lane: str, + acceptance_evidence: str, + materialization_evidence: str, + descriptor_evidence: str, + session_evidence: str, + ) -> tuple[Descriptor, Invocation, StepState]: + for source in (case_source, type_source, descriptor_source, catalog_source): + bounded_source(source) + descriptor = Descriptor.decode(descriptor_source) + try: + case = yaml.safe_load(case_source)["spec"] + case_type = yaml.safe_load(type_source)["spec"] + if not isinstance(case, dict) or not isinstance(case_type, dict): + raise ValueError + except (yaml.YAMLError, KeyError, TypeError, ValueError): + raise Refused("invalid case or type source shape") from None + if case.get("status") != "materialized" or not case.get("acceptance"): + raise Refused("case is not accepted and structurally materialized") + if case_type.get("status") not in ("acknowledged", "implemented"): + raise Refused("type is not acknowledged") + if case.get("type") != f"{case_type['id']}@{case_type['version']}": + raise Refused("case type pin mismatch") + if case_type.get("execution_descriptors", {}).get(descriptor.step) != digest( + descriptor_source + ): + raise Refused( + "type has no exact acknowledged machine descriptor; plan only" + ) + for purpose, source, evidence in ( + ("case-acceptance", digest(case_source), acceptance_evidence), + ( + "ops-mason-materialization", + digest(case_source), + materialization_evidence, + ), + ( + "owner-descriptor-acceptance:" + descriptor.owner, + digest(descriptor_source), + descriptor_evidence, + ), + ("type-machine-seam-acceptance", digest(type_source), descriptor_evidence), + ("catalog-routing-acceptance", digest(catalog_source), descriptor_evidence), + ( + f"caller-session:{actor}:{principal}:{lane}", + digest(case_source), + session_evidence, + ), + ): + if ( + not isinstance(evidence, str) + or not IDENTIFIER.fullmatch(evidence) + or self._verify_evidence(purpose, source, evidence) is not True + ): + raise Refused("independent owner evidence not verified") + if ( + not IDENTIFIER.fullmatch(principal) + or actor != descriptor.actor + or lane != descriptor.lane + ): + raise Refused("principal actor or lane mismatch") + if descriptor.effect == "mint" and ( + actor != "human" or os.getenv("WARDEN_AGENT_ID") + ): + raise Refused("agent mint is forbidden") + parameters = case.get("parameters", {}) + declared = { + item["name"]: item + for item in case_type.get("parameters", []) + if isinstance(item, dict) and isinstance(item.get("name"), str) + } + if ( + not isinstance(parameters, dict) + or descriptor.target_parameter not in declared + ): + raise Refused("target parameter is not declared by acknowledged type") + if case_type.get("id") == "oidc-browser-relying-party": + if type(self.browser_inputs) is not BrowserInputs: + raise Refused("browser metadata validator is not configured") + target = self.browser_inputs.target( + yaml.safe_load(case_source), + yaml.safe_load(type_source), + descriptor.target_parameter, + ) + else: + for name in parameters: + definition = declared.get(name) + if ( + definition is None + or definition.get("sensitivity") not in ("public", "operational") + or definition.get("case_settable") is False + ): + raise Refused("unknown, fixed or secret-sensitivity case parameter") + target = parameters.get(descriptor.target_parameter) + if not isinstance(target, str) or not IDENTIFIER.fullmatch(target): + raise Refused("target must be exact case metadata") + adapter = self.adapters.get(descriptor.adapter) + if adapter is None or (adapter.reference, adapter.source) != ( + descriptor.adapter, + descriptor.adapter_source, + ): + raise Refused("unknown adapter or owner source pin") + sheet = json.dumps( + [ + descriptor.step, + descriptor.owner, + target, + actor, + principal, + lane, + acceptance_evidence, + materialization_evidence, + descriptor_evidence, + session_evidence, + ], + separators=(",", ":"), + ).encode() + pins = Pins( + digest(case_source), + digest(type_source), + digest(descriptor_source), + digest(catalog_source), + digest(acceptance_evidence.encode()), + digest(sheet), + ) + invocation = Invocation( + descriptor.step, descriptor.owner, target, actor, principal, lane, pins + ) + initial = StepState( + descriptor.step, + descriptor.owner, + pins, + descriptor.allowed_created, + descriptor.retry_safe, + ) + self._admitted[invocation] = (descriptor, initial, session_evidence) + return descriptor, invocation, initial + + def _result( + self, adapter: Adapter, invocation: Invocation, result: OwnerResult + ) -> OwnerResult: + if type(result) is not OwnerResult or not isinstance(result.outcome, Outcome): + raise Refused("unknown owner result shape") + if type(result.created) is not tuple or not all( + isinstance(v, str) and IDENTIFIER.fullmatch(v) for v in result.created + ): + raise Refused("owner created-object result invalid") + if not isinstance(result.evidence, str) or not IDENTIFIER.fullmatch( + result.evidence + ): + raise Refused("owner evidence identifier invalid") + if adapter.verify(invocation, result) is not True or result.revoked is not True: + raise Refused("owner result or session revocation unverified") + if (result.attempt_id, result.operation) != ( + invocation.attempt_id, + invocation.operation, + ): + raise Refused("owner receipt belongs to another attempt") + descriptor = self._admitted[ + replace(invocation, attempt_id="", operation="admission") + ][0] + proof_fields = { + "parent-session": ( + f"session-revocation:{invocation.lane}:{invocation.operation}:{invocation.attempt_id}", + result.session_revocation_evidence, + ), + "broker-grant": ( + f"grant-revocation:{invocation.lane}:{invocation.operation}:{invocation.attempt_id}", + result.grant_revocation_evidence, + ), + } + proofs = [proof_fields[scope] for scope in descriptor.revocation_scopes] + for purpose, evidence in proofs: + if ( + not isinstance(evidence, str) + or not IDENTIFIER.fullmatch(evidence) + or self._verify_evidence(purpose, invocation.pins.case, evidence) + is not True + ): + raise Refused("independent grant or parent-session revocation absent") + return result + + def _verify_evidence(self, purpose, source, evidence): + try: + with contained_output(): + return self.evidence_verifier(purpose, source, evidence) is True + except Exception: + raise Refused("independent evidence verification failed") from None + + def _admission( + self, descriptor: Descriptor, invocation: Invocation, initial: StepState + ): + if self._admitted.get(invocation, ())[:2] != (descriptor, initial): + raise Refused("execution requires verified admission on this executor") + adapter = self.adapters.get(descriptor.adapter) + if adapter is None or (adapter.reference, adapter.source) != ( + descriptor.adapter, + descriptor.adapter_source, + ): + raise Refused("adapter changed after admission") + session = self._admitted[invocation][2] + if ( + self._verify_evidence( + f"caller-session:{invocation.actor}:{invocation.principal}:{invocation.lane}", + invocation.pins.case, + session, + ) + is not True + ): + raise Refused("caller session no longer verified") + if descriptor.effect == "mint" and os.getenv("WARDEN_AGENT_ID"): + raise Refused("agent mint is forbidden") + return adapter + + def _attempt(self, invocation: Invocation, state: StepState, operation: str): + identity = json.dumps( + [asdict(invocation.pins), operation, state.last.attempt, uuid.uuid4().hex], + separators=(",", ":"), + ).encode() + return replace( + invocation, + attempt_id="attempt-" + hashlib.sha256(identity).hexdigest(), + operation=operation, + ) + + def run( + self, + descriptor: Descriptor, + invocation: Invocation, + initial: StepState, + *, + retry=False, + ) -> StepState: + adapter = self._admission(descriptor, invocation, initial) + with self.journal.locked(): + binding = f"{invocation.step}:{invocation.target}" + state = self.journal.load(binding, initial) + recovered = recover(state) + if recovered != state: + self.journal.save(binding, recovered) + state = recovered + if len(state.receipts) > MAX_HISTORY - 4: + raise Refused("bounded journal history requires owner archival") + running = begin(state, invocation.pins, retry=retry) + dispatched = self._attempt(invocation, running, "run") + self.journal.save( + binding, + running, + invocation=dispatched, + intent=OriginalIntent(binding, dispatched, adapter.source), + ) + evidence = None + try: + with contained_output(): + result = self._result( + adapter, dispatched, adapter.invoke(dispatched) + ) + completed = finish( + running, invocation.pins, result.outcome, created=result.created + ) + evidence = result + except Exception: + completed = finish(running, invocation.pins, Outcome.UNCERTAIN) + self.journal.save(binding, completed, evidence, dispatched) + return completed + + def rollback( + self, descriptor: Descriptor, invocation: Invocation, initial: StepState + ) -> StepState: + adapter = self._admission(descriptor, invocation, initial) + with self.journal.locked(): + binding = f"{invocation.step}:{invocation.target}" + state = self.journal.load(binding, initial) + if recover(state) != state: + self.journal.save(binding, recover(state)) + raise Refused("lost rollback result requires owner reconciliation") + if adapter.rollback is None: + raise Refused("owner has no reviewed undo adapter") + if len(state.receipts) > MAX_HISTORY - 4: + raise Refused("bounded journal history requires owner archival") + running = begin_rollback(state, invocation.pins) + dispatched = self._attempt(invocation, running, "rollback") + self.journal.save( + binding, + running, + invocation=dispatched, + intent=OriginalIntent(binding, dispatched, adapter.source), + ) + evidence = None + try: + with contained_output(): + result = self._result( + adapter, + dispatched, + adapter.rollback(dispatched, running.last.created), + ) + verified = result.outcome is Outcome.ROLLED_BACK and not result.created + evidence = result if verified else None + except Exception: + verified = False + completed = finish_rollback(running, invocation.pins, verified=verified) + self.journal.save(binding, completed, evidence, dispatched) + return completed + + def reconcile( + self, descriptor: Descriptor, invocation: Invocation, initial: StepState + ) -> StepState: + """Explicit owner-native state inspection; never redispatches the mutation.""" + adapter = self._admission(descriptor, invocation, initial) + if adapter.reconcile is None and adapter.reconcile_original is None: + raise Refused("owner reconciliation adapter absent") + with self.journal.locked(): + binding = f"{invocation.step}:{invocation.target}" + state = recover(self.journal.load(binding, initial)) + if not state.last or state.last.outcome not in ( + Outcome.UNCERTAIN, + Outcome.FAILED_AFTER, + ): + raise Refused("no uncertain mutation to reconcile") + if len(state.receipts) > MAX_HISTORY - 4: + raise Refused("bounded journal history requires owner archival") + state = replace( + state, + receipts=( + *state.receipts, + Receipt(len(state.receipts) + 1, Outcome.UNCERTAIN), + ), + ) + original = ( + self.journal.original_intent(binding, invocation, adapter.source) + if adapter.reconcile_original is not None + else None + ) + dispatched = self._attempt(invocation, state, "reconcile") + self.journal.save(binding, state, invocation=dispatched) + with contained_output(): + result = self._result( + adapter, + dispatched, + adapter.reconcile_original(ReconcileContext(original, dispatched)) + if original is not None + else adapter.reconcile(dispatched), + ) + if result.outcome not in ( + Outcome.NO_CHANGE, + Outcome.SUCCESS, + Outcome.FAILED_BEFORE, + ): + raise Refused("owner did not reconcile target state") + # Validate created-object scope using the same closed result reducer. + finish( + begin( + StepState( + state.step_id, + state.owner, + state.pins, + state.allowed_created, + state.retry_safe, + ), + state.pins, + ), + state.pins, + result.outcome, + created=result.created, + ) + reconciled = StepState( + state.step_id, + state.owner, + state.pins, + state.allowed_created, + state.retry_safe, + ( + *state.receipts, + Receipt(len(state.receipts) + 1, result.outcome, result.created), + ), + ) + self.journal.save(binding, reconciled, result, dispatched) + return reconciled + + +class OwnerInterrupted(KeyboardInterrupt): + """Sanitized interruption preserves persisted running/crash recovery semantics.""" + + def __init__(self): + super().__init__("owner execution interrupted") + + +class _Discard: + """Never retain or forward adapter output, including unbounded malicious text.""" + + def write(self, text): + return len(text) + + def flush(self): + pass diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_case_original_context_consistency.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_case_original_context_consistency.py new file mode 100644 index 00000000..574fcc4a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_case_original_context_consistency.py @@ -0,0 +1,198 @@ +"""Original-intent consistency checks with generated metadata only. + +Reuse the existing synthetic owner rig. These checks do not provide a native +holder or independently authenticate original creation from local SQL/JSON. +""" + +from dataclasses import asdict, replace +import json +from pathlib import Path +import sqlite3 +import subprocess +import sys +import pytest +from test_case_run import rig as rig +from warden.case_receipt_state import Outcome, Refused +from warden.case_run import OriginalIntent + + +def interrupted(rig): + executor, args, calls, sources, kwargs = rig + old = executor.adapters[args[0].adapter] + seen = [] + + def lost(inv): + calls.append(inv) + raise TimeoutError("generated lost response") + + def original(ctx): + seen.append(ctx) + return old.reconcile(ctx.attempt) + + executor.adapters[old.reference] = replace( + old, invoke=lost, reconcile=None, reconcile_original=original + ) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + return executor, args, calls, old, seen + + +def alter_payload(executor, **changes): + row = executor.journal.db.execute( + "SELECT seq,payload FROM intents ORDER BY seq DESC LIMIT 1" + ).fetchone() + obj = json.loads(row[1]) + obj.update(changes) + executor.journal.db.execute( + "UPDATE intents SET payload=? WHERE seq=?", (json.dumps(obj), row[0]) + ) + + +def test_original_actor_mismatch_is_refused(rig): + executor, args, calls, old, _ = interrupted(rig) + alter_payload(executor, actor="agent") + with pytest.raises(Refused): + executor.journal.original_intent( + "mint-secret:fixture-client", args[1], old.source + ) + + +def test_payload_attempt_id_must_match_sql_column_and_original_receipt(rig): + executor, args, calls, old, _ = interrupted(rig) + actual = calls[0].attempt_id + alter_payload(executor, attempt_id="attempt-unrelated") + with pytest.raises(Refused): + executor.journal.original_intent( + "mint-secret:fixture-client", args[1], old.source + ) + assert ( + executor.journal.db.execute("SELECT attempt_id FROM intents").fetchone()[0] + == actual + ) + + +def test_orphan_intent_without_original_running_receipt_is_refused(rig): + executor, args, calls, old, _ = interrupted(rig) + executor.journal.db.execute( + "DELETE FROM receipts WHERE seq=(SELECT MIN(seq) FROM receipts)" + ) + assert ( + executor.journal.load("mint-secret:fixture-client", args[2]).last.outcome + is Outcome.UNCERTAIN + ) + with pytest.raises(Refused): + executor.journal.original_intent( + "mint-secret:fixture-client", args[1], old.source + ) + + +def test_serializer_refuses_arbitrary_values_outside_normal_admit(rig): + _, args, _, _, _ = rig + inv = replace( + args[1], + target={"unexpected": "synthetic-owner-payload"}, + operation="run", + attempt_id="attempt-fixture", + ) + record = OriginalIntent("mint-secret:fixture-client", inv, "sha256:" + "1" * 64) + with pytest.raises(Refused): + OriginalIntent.decode(record.payload()) + # This is a direct serialization API gap, not an observed normal-admit leak. + + # Owner trust is the existing synthetic fixture; this proves call shape only. + + +@pytest.mark.parametrize( + "field,value", + [ + ("principal", "another-person"), + ("tenant", "another-tenant"), + ("realm", "another-realm"), + ], +) +def test_existing_lookup_does_refuse_stored_principal_tenant_realm_mismatch( + rig, field, value +): + executor, args, _, old, _ = interrupted(rig) + alter_payload(executor, **{field: value}) + with pytest.raises(Refused): + executor.journal.original_intent( + "mint-secret:fixture-client", args[1], old.source + ) + + +def test_existing_atomic_insert_failure_prevents_dispatch_and_both_records(rig): + executor, args, calls, _, _ = rig + executor.journal.db.execute( + "CREATE TRIGGER fail_receipt BEFORE INSERT ON receipts BEGIN SELECT RAISE(ABORT,'fixture'); END" + ) + with pytest.raises(sqlite3.IntegrityError): + executor.run(*args) + assert not calls + assert executor.journal.db.execute("SELECT COUNT(*) FROM intents").fetchone() == ( + 0, + ) + assert executor.journal.db.execute("SELECT COUNT(*) FROM receipts").fetchone() == ( + 0, + ) + + +def test_existing_missing_original_refuses_before_callback(rig): + executor, args, _, _, seen = interrupted(rig) + executor.journal.db.execute("DELETE FROM intents") + with pytest.raises(Refused): + executor.reconcile(*args) + assert not seen + assert ( + executor.journal.load("mint-secret:fixture-client", args[2]).last.outcome + is Outcome.UNCERTAIN + ) + + +def test_existing_current_parent_revocation_remains_mandatory(rig): + executor, args, _, _, _ = interrupted(rig) + old = executor.evidence_verifier + + def refuse_session(purpose, source, evidence): + return ( + False + if purpose.startswith("session-revocation:") + else old(purpose, source, evidence) + ) + + executor.evidence_verifier = refuse_session + with pytest.raises(Refused): + executor.reconcile(*args) + assert ( + executor.journal.load("mint-secret:fixture-client", args[2]).last.outcome + is Outcome.UNCERTAIN + ) + + +def test_existing_intent_survives_actual_distinct_interpreter(rig): + executor, args, calls, old, _ = interrupted(rig) + code = """import json,sys +from pathlib import Path +from warden.case_run import Journal,Invocation +from warden.case_receipt_state import Pins +obj=json.load(sys.stdin);obj['pins']=Pins(**obj['pins']);j=Journal(Path(sys.argv[1])) +with j.locked(): + i=j.original_intent('mint-secret:fixture-client',Invocation(**obj),sys.argv[2]) + print(json.dumps({'attempt':i.invocation.attempt_id,'operation':i.invocation.operation})) +""" + result = subprocess.run( + [sys.executable, "-B", "-c", code, str(executor.journal.directory), old.source], + input=json.dumps(asdict(args[1])), + text=True, + capture_output=True, + timeout=15, + env={ + "PATH": "/usr/bin:/bin", + "PYTHONPATH": str(Path(__file__).parents[1] / "src"), + "PYTHONDONTWRITEBYTECODE": "1", + }, + check=True, + ) + assert json.loads(result.stdout) == { + "attempt": calls[0].attempt_id, + "operation": "run", + } diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_case_run.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_case_run.py new file mode 100644 index 00000000..0032e5eb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_case_run.py @@ -0,0 +1,700 @@ +"""Injected owner harness; these receipts are not native owner attestations.""" + +from dataclasses import asdict, replace +import json +import os +import subprocess +import sys +import sqlite3 +import pytest +import yaml +from warden.case_receipt_state import Outcome, Refused +from warden.case_run import Adapter, Descriptor, Executor, Journal, OwnerResult, digest + + +@pytest.fixture +def rig(tmp_path): + desc = Descriptor( + "warden.case.execution.v1", + "mint-secret", + "owner", + "owner/mint@v1", + "sha256:" + "1" * 64, + "human", + "issuer-lane", + "client_id", + "mint", + ("case-policy", "case-role"), + True, + ("parent-session", "broker-grant"), + ) + descriptor = json.dumps(asdict(desc)).encode() + typ = yaml.safe_dump( + { + "spec": { + "id": "test", + "version": "1", + "status": "acknowledged", + "parameters": [ + { + "name": "client_id", + "sensitivity": "operational", + "case_settable": True, + } + ], + "execution_descriptors": {"mint-secret": digest(descriptor)}, + } + } + ).encode() + case = yaml.safe_dump( + { + "spec": { + "id": "case-test", + "type": "test@1", + "status": "materialized", + "acceptance": "acceptance", + "parameters": {"client_id": "fixture-client"}, + } + } + ).encode() + calls = [] + + def invoke(inv): + calls.append(inv) + return bound( + inv, + OwnerResult( + Outcome.SUCCESS, + "owner-receipt", + ("case-policy", "case-role"), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ) + + adapter = Adapter( + "owner/mint@v1", + desc.adapter_source, + invoke, + lambda inv, result: result.evidence == "owner-receipt", + lambda inv, objects: bound( + inv, + OwnerResult( + Outcome.ROLLED_BACK, + "owner-receipt", + (), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ), + lambda inv: bound( + inv, + OwnerResult( + Outcome.NO_CHANGE, + "owner-receipt", + (), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ), + ) + verified = { + ("case-acceptance", digest(case), "acceptance"), + ("ops-mason-materialization", digest(case), "materialization"), + ("owner-descriptor-acceptance:owner", digest(descriptor), "descriptor"), + ("type-machine-seam-acceptance", digest(typ), "descriptor"), + ("catalog-routing-acceptance", digest(b"catalog"), "descriptor"), + ("caller-session:human:operator:issuer-lane", digest(case), "session"), + ("session-revocation:issuer-lane", digest(case), "session-revoked"), + ("grant-revocation:issuer-lane", digest(case), "grant-revoked"), + } + + def verifier(purpose, source, evidence): + if purpose.startswith(("session-revocation:", "grant-revocation:")): + scope, lane, operation, attempt = purpose.split(":") + return ( + source == digest(case) + and lane == "issuer-lane" + and operation in ("run", "rollback", "reconcile") + and evidence + == ("session-" if scope == "session-revocation" else "grant-") + attempt + ) + return (purpose, source, evidence) in verified + + executor = Executor( + Journal(tmp_path / "private"), {"owner/mint@v1": adapter}, verifier + ) + kwargs = dict( + principal="operator", + actor="human", + lane="issuer-lane", + acceptance_evidence="acceptance", + materialization_evidence="materialization", + descriptor_evidence="descriptor", + session_evidence="session", + ) + admitted = executor.admit(case, typ, descriptor, b"catalog", **kwargs) + return (executor, admitted, calls, (case, typ, descriptor, b"catalog"), kwargs) + + +def test_commit_running_is_visible_from_separate_connection_before_dispatch(rig): + executor, args, calls, _, _ = rig + old = executor.adapters[args[0].adapter] + + def inspect(inv): + other = sqlite3.connect(executor.journal.directory / "journal.sqlite") + payload = json.loads( + other.execute( + "SELECT payload FROM receipts ORDER BY seq DESC LIMIT 1" + ).fetchone()[0] + ) + assert payload["receipts"][-1]["outcome"] == "running" + return old.invoke(inv) + + executor.adapters[old.reference] = replace(old, invoke=inspect) + assert executor.run(*args).last.outcome is Outcome.SUCCESS + with pytest.raises(Refused): + executor.run(*args) + assert len(calls) == 1 + + +def test_crash_and_fresh_process_resume_never_duplicates_effect(rig): + executor, args, calls, sources, kwargs = rig + old = executor.adapters[args[0].adapter] + + def crash(inv): + calls.append(inv) + raise KeyboardInterrupt + + executor.adapters[old.reference] = replace(old, invoke=crash) + with pytest.raises(KeyboardInterrupt): + executor.run(*args) + fresh = Executor( + Journal(executor.journal.directory), + {old.reference: old}, + executor.evidence_verifier, + ) + admitted = fresh.admit(*sources, **kwargs) + with pytest.raises(Refused): + fresh.run(*admitted, retry=True) + state = fresh.journal.load("mint-secret:fixture-client", admitted[2]) + assert state.last.outcome is Outcome.UNCERTAIN + assert len(calls) == 1 + + +def test_lost_result_preserved_and_cas_reconciliation_does_not_mint(rig): + executor, args, calls, _, _ = rig + old = executor.adapters[args[0].adapter] + + def lost(inv): + calls.append(inv) + raise TimeoutError("SECRET-HIDDEN") + + executor.adapters[old.reference] = replace(old, invoke=lost) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + assert executor.reconcile(*args).last.outcome is Outcome.NO_CHANGE + with pytest.raises(Refused): + executor.run(*args, retry=True) + assert len(calls) == 1 + assert ( + b"SECRET-HIDDEN" + not in (executor.journal.directory / "journal.sqlite").read_bytes() + ) + + +def test_failed_before_change_requires_explicit_retry_and_safe_owner(rig): + executor, args, calls, _, _ = rig + old = executor.adapters[args[0].adapter] + + def no_change(inv): + calls.append(inv) + return bound( + inv, + OwnerResult( + Outcome.FAILED_BEFORE, + "owner-receipt", + (), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ) + + executor.adapters[old.reference] = replace(old, invoke=no_change) + assert executor.run(*args).last.outcome is Outcome.FAILED_BEFORE + with pytest.raises(Refused): + executor.run(*args) + assert executor.run(*args, retry=True).last.outcome is Outcome.FAILED_BEFORE + assert len(calls) == 2 + + +@pytest.mark.parametrize( + "result", + [ + OwnerResult( + Outcome.SUCCESS, + "owner-receipt", + ("shared-policy",), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + OwnerResult( + Outcome.SUCCESS, + "owner-receipt", + (), + False, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + OwnerResult( + Outcome.SUCCESS, + "unknown-receipt", + (), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + {"outcome": "verified-success", "stdout": "SECRET-HIDDEN"}, + ], +) +def test_unknown_secret_payload_created_scope_or_missing_revocation_never_succeeds( + rig, result, capsys +): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + + def malicious(inv): + print("SECRET-HIDDEN" * 1000) + return result + + executor.adapters[old.reference] = replace(old, invoke=malicious) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + assert capsys.readouterr().out == "" + assert ( + b"SECRET-HIDDEN" + not in (executor.journal.directory / "journal.sqlite").read_bytes() + ) + + +def test_rollback_reverses_only_verified_case_created_objects(rig): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + undos = [] + + def undo(inv, objects): + undos.append(objects) + return old.rollback(inv, objects) + + executor.adapters[old.reference] = replace(old, rollback=undo) + executor.run(*args) + assert executor.rollback(*args).last.outcome is Outcome.ROLLED_BACK + assert undos == [("case-role", "case-policy")] + assert "shared-policy" not in undos[0] + + +def test_uncertain_rollback_cannot_replay(rig): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + executor.run(*args) + executor.adapters[old.reference] = replace( + old, + rollback=lambda inv, objs: bound( + inv, + OwnerResult( + Outcome.ROLLED_BACK, + "owner-receipt", + (), + False, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ), + ) + assert executor.rollback(*args).last.outcome is Outcome.ROLLBACK_UNCERTAIN + with pytest.raises(Refused): + executor.rollback(*args) + + +@pytest.mark.parametrize( + "change", ["materialization", "type-prose", "actor", "lane", "source", "target"] +) +def test_exact_admission_requires_owner_evidence_and_closed_source_pins(rig, change): + executor, _, _, sources, kwargs = rig + case, typ, descriptor, catalog = sources + kwargs = dict(kwargs) + if change == "materialization": + kwargs["materialization_evidence"] = "bare-json" + if change == "type-prose": + obj = yaml.safe_load(typ) + obj["spec"].pop("execution_descriptors") + typ = yaml.safe_dump(obj).encode() + if change == "actor": + kwargs["actor"] = "agent" + if change == "lane": + kwargs["lane"] = "other-lane" + if change == "source": + obj = json.loads(descriptor) + obj["adapter_source"] = "sha256:" + "2" * 64 + descriptor = json.dumps(obj).encode() + if change == "target": + obj = yaml.safe_load(case) + obj["spec"]["parameters"]["client_id"] = "other-target" + case = yaml.safe_dump(obj).encode() + with pytest.raises(Refused): + executor.admit(case, typ, descriptor, catalog, **kwargs) + + +def test_agent_env_cannot_dispatch_after_human_admission(rig, monkeypatch): + executor, args, calls, _, _ = rig + monkeypatch.setenv("WARDEN_AGENT_ID", "test-agent") + with pytest.raises(Refused): + executor.run(*args) + assert calls == [] + + +def test_forged_invocation_cannot_bypass_admission(rig): + executor, args, calls, _, _ = rig + with pytest.raises(Refused): + executor.run(args[0], replace(args[1], target="other-target"), args[2]) + assert calls == [] + + +def test_journal_rejects_unsafe_directory_and_symlink(tmp_path): + unsafe = tmp_path / "unsafe" + unsafe.mkdir(mode=493) + with pytest.raises(Refused): + Journal(unsafe) + private = tmp_path / "private" + private.mkdir(mode=448) + (private / "journal.sqlite").symlink_to(tmp_path / "outside") + with pytest.raises(OSError): + Journal(private) + + +def test_process_lock_blocks_parallel_dispatch(rig): + executor, args, calls, _, _ = rig + with executor.journal.locked(): + with pytest.raises(Refused): + executor.run(*args) + assert calls == [] + + +def test_changed_catalog_pins_cannot_inherit_old_success(rig): + executor, args, calls, sources, kwargs = rig + executor.run(*args) + with pytest.raises(Refused): + executor.admit(*sources[:3], b"changed-catalog", **kwargs) + assert len(calls) == 1 + + +def test_private_journal_does_not_retain_owner_payload_or_accept_unknown_schema(rig): + executor, args, _, _, _ = rig + executor.run(*args) + row = executor.journal.db.execute( + "SELECT seq,payload FROM receipts ORDER BY seq DESC LIMIT 1" + ).fetchone() + payload = json.loads(row[1]) + assert payload["journal_schema"] == "warden.case.step-journal.v1" + assert payload["evidence_digest"] == digest(b"owner-receipt") + assert payload["session_revoked"] is True + assert "owner-receipt" not in row[1] + payload["stdout"] = "UNTRUSTED-OUTPUT" + executor.journal.db.execute( + "UPDATE receipts SET payload=? WHERE seq=?", (json.dumps(payload), row[0]) + ) + with pytest.raises(Refused): + executor.run(*args) + + +def test_expired_caller_session_cannot_use_cached_admission(rig): + executor, args, calls, _, _ = rig + old = executor.evidence_verifier + executor.evidence_verifier = lambda purpose, source, evidence: ( + False + if purpose.startswith("caller-session:") + else old(purpose, source, evidence) + ) + with pytest.raises(Refused): + executor.run(*args) + assert calls == [] + + +@pytest.mark.parametrize("index", [0, 1, 2, 3]) +def test_source_sizes_are_bounded_before_parsing(rig, index): + executor, _, _, sources, kwargs = rig + oversized = list(sources) + oversized[index] = b"x" * 262145 + with pytest.raises(Refused, match="bounded"): + executor.admit(*oversized, **kwargs) + + +def test_owner_fd_and_inherited_subprocess_output_is_contained(rig, capfd): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + + def leaking(inv): + os.write(1, b"SYNTHETIC-SECRET") + os.write(2, b"SYNTHETIC-SECRET") + subprocess.run([sys.executable, "-c", 'print("SYNTHETIC-SECRET")'], check=True) + return old.invoke(inv) + + executor.adapters[old.reference] = replace(old, invoke=leaking) + assert executor.run(*args).last.outcome is Outcome.SUCCESS + observed = capfd.readouterr() + assert "SYNTHETIC-SECRET" not in observed.out + observed.err + + +def test_evidence_provider_output_and_error_never_escape(rig, capfd): + executor, _, calls, sources, kwargs = rig + + def broken(*args): + print("SYNTHETIC-SECRET") + os.write(2, b"SYNTHETIC-SECRET") + raise RuntimeError("SYNTHETIC-SECRET") + + executor.evidence_verifier = broken + with pytest.raises(Refused) as caught: + executor.admit(*sources, **kwargs) + assert "SYNTHETIC-SECRET" not in str(caught.value) + observed = capfd.readouterr() + assert "SYNTHETIC-SECRET" not in observed.out + observed.err + assert calls == [] + + +@pytest.mark.parametrize("missing", ["session", "grant"]) +def test_broker_grant_and_parent_session_have_separate_required_evidence(rig, missing): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + result = old.invoke(args[1]) + if missing == "session": + result = replace(result, session_revocation_evidence=None) + else: + result = replace(result, grant_revocation_evidence=None) + executor.adapters[old.reference] = replace(old, invoke=lambda inv: result) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + + +def test_mint_descriptor_cannot_omit_broker_grant_scope(rig): + descriptor = json.loads(rig[3][2]) + descriptor["revocation_scopes"] = ["parent-session"] + with pytest.raises(Refused): + Descriptor.decode(json.dumps(descriptor).encode()) + + +def test_unsafe_owner_step_cannot_retry_even_after_verified_failed_before( + rig, tmp_path +): + executor, args, _, sources, kwargs = rig + descriptor = replace(args[0], retry_safe=False) + descriptor_source = json.dumps(asdict(descriptor)).encode() + type_doc = yaml.safe_load(sources[1]) + type_doc["spec"]["execution_descriptors"][descriptor.step] = digest( + descriptor_source + ) + type_source = yaml.safe_dump(type_doc).encode() + old_verifier = executor.evidence_verifier + + def verifier(purpose, source, evidence): + if purpose == "owner-descriptor-acceptance:owner": + return source == digest(descriptor_source) and evidence == "descriptor" + if purpose == "type-machine-seam-acceptance": + return source == digest(type_source) and evidence == "descriptor" + return old_verifier(purpose, source, evidence) + + adapter = executor.adapters[descriptor.adapter] + + def failed(inv): + return bound( + inv, + OwnerResult( + Outcome.FAILED_BEFORE, + "owner-receipt", + (), + True, + "session-revoked", + "grant-revoked", + ), + ) + + fresh = Executor( + Journal(tmp_path / "unsafe-retry"), + {adapter.reference: replace(adapter, invoke=failed)}, + verifier, + ) + admitted = fresh.admit( + sources[0], type_source, descriptor_source, sources[3], **kwargs + ) + assert fresh.run(*admitted).last.outcome is Outcome.FAILED_BEFORE + with pytest.raises(Refused): + fresh.run(*admitted, retry=True) + + +def bound(inv, result): + return replace( + result, + attempt_id=inv.attempt_id, + operation=inv.operation, + session_revocation_evidence=("session-" + inv.attempt_id) + if result.session_revocation_evidence + else None, + grant_revocation_evidence=("grant-" + inv.attempt_id) + if result.grant_revocation_evidence + else None, + ) + + +def test_old_grant_and_session_proofs_cannot_verify_a_new_retry(rig): + executor, args, _, _, _ = rig + adapter = executor.adapters[args[0].adapter] + retained = [] + + def attempts(inv): + if not retained: + result = bound( + inv, + OwnerResult( + Outcome.FAILED_BEFORE, + "owner-receipt", + (), + True, + "session-revoked", + "grant-revoked", + ), + ) + retained.append(result) + return result + # Even a caller rewriting labels cannot authenticate prior revocation + # evidence for the newly persisted attempt identity. + return replace(retained[0], attempt_id=inv.attempt_id, operation=inv.operation) + + executor.adapters[adapter.reference] = replace(adapter, invoke=attempts) + assert executor.run(*args).last.outcome is Outcome.FAILED_BEFORE + assert executor.run(*args, retry=True).last.outcome is Outcome.UNCERTAIN + + +def test_owner_interrupt_payload_is_sanitized_with_running_intent_retained(rig, capfd): + executor, args, _, _, _ = rig + adapter = executor.adapters[args[0].adapter] + + def interrupted(inv): + os.write(2, b"SYNTHETIC-SECRET") + raise KeyboardInterrupt("SYNTHETIC-SECRET") + + executor.adapters[adapter.reference] = replace(adapter, invoke=interrupted) + with pytest.raises(KeyboardInterrupt) as caught: + executor.run(*args) + assert "SYNTHETIC-SECRET" not in str(caught.value) + assert "SYNTHETIC-SECRET" not in "".join(capfd.readouterr()) + assert ( + executor.journal.load("mint-secret:fixture-client", args[2]).last.outcome + is Outcome.RUNNING + ) + + +def test_attempt_identity_changes_with_exact_catalog_revision_and_new_journal( + rig, tmp_path +): + executor, args, calls, sources, kwargs = rig + executor.run(*args) + original_attempt = calls[-1].attempt_id + new_catalog = b"independently-reviewed-catalog-v2" + old_verifier = executor.evidence_verifier + + def verifier(purpose, source, evidence): + if purpose == "catalog-routing-acceptance": + return source == digest(new_catalog) and evidence == "descriptor" + return old_verifier(purpose, source, evidence) + + fresh = Executor(Journal(tmp_path / "new-revision"), executor.adapters, verifier) + revised = fresh.admit(*sources[:3], new_catalog, **kwargs) + assert fresh.run(*revised).last.outcome is Outcome.SUCCESS + assert calls[-1].attempt_id != original_attempt + assert calls[-1].pins.catalog != calls[0].pins.catalog + + +def test_secret_sensitivity_parameter_cannot_reach_admission_or_journal(rig): + executor, _, _, sources, kwargs = rig + type_doc = yaml.safe_load(sources[1]) + type_doc["spec"]["parameters"][0]["sensitivity"] = "secret" + source = yaml.safe_dump(type_doc).encode() + old = executor.evidence_verifier + executor.evidence_verifier = lambda purpose, digest_value, evidence: ( + True + if purpose == "type-machine-seam-acceptance" + else old(purpose, digest_value, evidence) + ) + with pytest.raises(Refused, match="secret-sensitivity"): + executor.admit(sources[0], source, sources[2], sources[3], **kwargs) + + +def _lose_result(executor, args, calls): + old = executor.adapters[args[0].adapter] + + def lost(inv): + calls.append(inv) + raise TimeoutError("lost") + + seen = [] + + def by_original(ctx): + seen.append(ctx) + return old.reconcile(ctx.attempt) + + executor.adapters[old.reference] = replace( + old, invoke=lost, reconcile=None, reconcile_original=by_original + ) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + return seen + + +def test_original_intent_survives_restart_separate_from_reconcile_attempt(rig): + executor, args, calls, _, _ = rig + seen = _lose_result(executor, args, calls) + executor.journal = Journal(executor.journal.directory) # restart: fresh handle + assert executor.reconcile(*args).last.outcome is Outcome.NO_CHANGE + (ctx,) = seen + original = ctx.original.invocation + assert original.attempt_id == calls[0].attempt_id + assert original.operation == "run" and ctx.attempt.operation == "reconcile" + assert ctx.attempt.attempt_id != original.attempt_id + assert original.pins == args[1].pins + assert (original.principal, original.tenant, original.realm) == ( + args[1].principal, + args[1].tenant, + args[1].realm, + ) + assert ctx.original.source == executor.adapters[args[0].adapter].source + + +def test_missing_original_intent_refuses_and_stays_uncertain(rig): + executor, args, calls, _, _ = rig + seen = _lose_result(executor, args, calls) + executor.journal.db.execute("DELETE FROM intents") + with pytest.raises(Refused, match="original intent record missing"): + executor.reconcile(*args) + assert seen == [] + binding = f"{args[1].step}:{args[1].target}" + assert executor.journal.load(binding, args[2]).last.outcome is Outcome.UNCERTAIN + + +@pytest.mark.parametrize( + "change", [{"tenant": "other-tenant"}, {"realm": "other-realm"}, {"lane": "x"}] +) +def test_original_intent_mismatch_refuses(rig, change): + executor, args, calls, _, _ = rig + seen = _lose_result(executor, args, calls) + with pytest.raises(Refused): + executor.reconcile(args[0], replace(args[1], **change), args[2]) + assert seen == [] + + +def test_original_intent_source_mismatch_refuses(rig): + executor, args, calls, _, _ = rig + _lose_result(executor, args, calls) + binding = f"{args[1].step}:{args[1].target}" + with pytest.raises(Refused, match="source mismatch"): + executor.journal.original_intent(binding, calls[0], "other-source") diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_context_compare_probes.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_context_compare_probes.py new file mode 100644 index 00000000..2a18bac7 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/tests/test_context_compare_probes.py @@ -0,0 +1,131 @@ +"""Observed source behavior of exact concurrent snapshot, not secure acceptance. + +Counterexamples use generated metadata and the owner's existing synthetic rig. +Passing a counterexample means the documented gap reproduced; it is not a native +holder, an owner attestation or permission to trust local SQL/JSON. +""" +from dataclasses import asdict, replace +import inspect +import json +from pathlib import Path +import sqlite3 +import subprocess +import sys +import pytest +from test_case_run import rig as rig +from warden.case_receipt_state import Outcome, Refused +from warden.case_run import Executor, Journal, OriginalIntent + + +def interrupted(rig): + executor,args,calls,sources,kwargs=rig + old=executor.adapters[args[0].adapter];seen=[] + def lost(inv):calls.append(inv);raise TimeoutError('generated lost response') + def original(ctx):seen.append(ctx);return old.reconcile(ctx.attempt) + executor.adapters[old.reference]=replace(old,invoke=lost,reconcile=None,reconcile_original=original) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + return executor,args,calls,old,seen + + +def alter_payload(executor,**changes): + row=executor.journal.db.execute('SELECT seq,payload FROM intents ORDER BY seq DESC LIMIT 1').fetchone() + obj=json.loads(row[1]);obj.update(changes) + executor.journal.db.execute('UPDATE intents SET payload=? WHERE seq=?',(json.dumps(obj),row[0])) + + +def test_observed_original_actor_mismatch_is_accepted_by_lookup(rig): + executor,args,calls,old,_=interrupted(rig) + alter_payload(executor,actor='agent') + with pytest.raises(Refused): + executor.journal.original_intent('mint-secret:fixture-client',args[1],old.source) + + +def test_observed_payload_attempt_id_is_not_checked_against_sql_column_or_receipt(rig): + executor,args,calls,old,_=interrupted(rig) + actual=calls[0].attempt_id;alter_payload(executor,attempt_id='attempt-unrelated') + with pytest.raises(Refused): + executor.journal.original_intent('mint-secret:fixture-client',args[1],old.source) + assert executor.journal.db.execute('SELECT attempt_id FROM intents').fetchone()[0]==actual + + +def test_observed_orphan_intent_without_original_running_receipt_is_accepted(rig): + executor,args,calls,old,_=interrupted(rig) + executor.journal.db.execute('DELETE FROM receipts WHERE seq=(SELECT MIN(seq) FROM receipts)') + assert executor.journal.load('mint-secret:fixture-client',args[2]).last.outcome is Outcome.UNCERTAIN + with pytest.raises(Refused): + executor.journal.original_intent('mint-secret:fixture-client',args[1],old.source) + + +def test_observed_serializer_accepts_arbitrary_values_outside_normal_admit(rig): + _,args,_,_,_=rig + inv=replace(args[1],target={'unexpected':'synthetic-owner-payload'},operation='run',attempt_id='attempt-fixture') + record=OriginalIntent('mint-secret:fixture-client',inv,'sha256:'+'1'*64) + with pytest.raises(Refused): + OriginalIntent.decode(record.payload()) + # This is a direct serialization API gap, not an observed normal-admit leak. + + +def test_observed_tenant_realm_not_admitted_and_remain_empty(rig): + _,args,_,_,_=rig + params=inspect.signature(Executor.admit).parameters + assert 'tenant' not in params and 'realm' not in params + assert args[1].tenant==args[1].realm=='' + + +def test_observed_no_original_context_purpose_is_requested(rig): + executor,args,_,_,seen=interrupted(rig);old=executor.evidence_verifier;purposes=[] + def trace(purpose,source,evidence):purposes.append(purpose);return old(purpose,source,evidence) + executor.evidence_verifier=trace + assert executor.reconcile(*args).last.outcome is Outcome.NO_CHANGE + assert seen and any(p.startswith('session-revocation:') for p in purposes) + assert not any('original' in p for p in purposes) + # Owner trust is the existing synthetic fixture; this proves call shape only. + + +@pytest.mark.parametrize('field,value',[('principal','another-person'),('tenant','another-tenant'),('realm','another-realm')]) +def test_existing_lookup_does_refuse_stored_principal_tenant_realm_mismatch(rig,field,value): + executor,args,_,old,_=interrupted(rig);alter_payload(executor,**{field:value}) + with pytest.raises(Refused):executor.journal.original_intent('mint-secret:fixture-client',args[1],old.source) + + +def test_existing_atomic_insert_failure_prevents_dispatch_and_both_records(rig): + executor,args,calls,_,_=rig + executor.journal.db.execute("CREATE TRIGGER fail_receipt BEFORE INSERT ON receipts BEGIN SELECT RAISE(ABORT,'fixture'); END") + with pytest.raises(sqlite3.IntegrityError):executor.run(*args) + assert not calls + assert executor.journal.db.execute('SELECT COUNT(*) FROM intents').fetchone()==(0,) + assert executor.journal.db.execute('SELECT COUNT(*) FROM receipts').fetchone()==(0,) + + +def test_existing_missing_original_refuses_before_callback(rig): + executor,args,_,_,seen=interrupted(rig) + executor.journal.db.execute('DELETE FROM intents') + with pytest.raises(Refused):executor.reconcile(*args) + assert not seen + assert executor.journal.load('mint-secret:fixture-client',args[2]).last.outcome is Outcome.UNCERTAIN + + +def test_existing_current_parent_revocation_remains_mandatory(rig): + executor,args,_,_,_=interrupted(rig);old=executor.evidence_verifier + def refuse_session(purpose,source,evidence): + return False if purpose.startswith('session-revocation:') else old(purpose,source,evidence) + executor.evidence_verifier=refuse_session + with pytest.raises(Refused):executor.reconcile(*args) + assert executor.journal.load('mint-secret:fixture-client',args[2]).last.outcome is Outcome.UNCERTAIN + + +def test_existing_intent_survives_actual_distinct_interpreter(rig): + executor,args,calls,old,_=interrupted(rig) + code='''import json,sys +from pathlib import Path +from warden.case_run import Journal,Invocation +from warden.case_receipt_state import Pins +obj=json.load(sys.stdin);obj['pins']=Pins(**obj['pins']);j=Journal(Path(sys.argv[1])) +with j.locked(): + i=j.original_intent('mint-secret:fixture-client',Invocation(**obj),sys.argv[2]) + print(json.dumps({'attempt':i.invocation.attempt_id,'operation':i.invocation.operation})) +''' + result=subprocess.run([sys.executable,'-B','-c',code,str(executor.journal.directory),old.source], + input=json.dumps(asdict(args[1])),text=True,capture_output=True,timeout=15, + env={'PATH':'/usr/bin:/bin','PYTHONPATH':str(Path(__file__).parents[1]/'src'),'PYTHONDONTWRITEBYTECODE':'1'},check=True) + assert json.loads(result.stdout)=={'attempt':calls[0].attempt_id,'operation':'run'} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/workplans/WARDEN-WP-0044-security-case-follow-through.md b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/workplans/WARDEN-WP-0044-security-case-follow-through.md new file mode 100644 index 00000000..40f1b5b5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-fixture/workplans/WARDEN-WP-0044-security-case-follow-through.md @@ -0,0 +1,289 @@ +--- +id: WARDEN-WP-0044 +type: workplan +title: "Follow through on KnownSecurityCases along their type's route" +domain: infotech +repo: ops-warden +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-11" +depends_on: [] +related_repos: + - net-kingdom + - ops-mason + - key-cape + - railiance-platform +state_hub_workstream_id: "f7e81013-6868-5d99-8528-ba75a442eca9" +--- + +# WARDEN-WP-0044 — Follow through on KnownSecurityCases along their type's route + +## Why + +NetKingdom is introducing SecurityCaseTypes (net-kingdom `NK-WP-0048`, canon +`security-case-types_v0.1`, proposed). Once a case of an implemented type is +accepted and ops-mason has materialized its structure (`MASON-WP-0010`), +ops-warden carries out the remaining steps. These are the owners' declared entry +points, run on the authentication path the type defines, followed by the type's +verification. + +Today ops-warden routes needs and, for `exec_capable` lanes, proxies fetches as +the caller. It does not yet know about cases, and each new integration ends with +a founder running a hand-written script (for example the proposed KEY-WP-0038-T02 +rollout). This plan gives ops-warden the follow-through role, without widening +what it may do: each step still runs under its owner's entry point and the +caller's identity. + +## Tasks + +```task +id: WARDEN-WP-0044-T01 +status: done +depends_on: [NK-WP-0048-T03] +priority: high +state_hub_task_id: "ef0920fc-418e-52cb-acff-9a5c0c728bf5" +``` + +### T01 — Define the type's follow-through section + +Propose the `follow_through` part of the SecurityCaseType schema together with +net-kingdom. For each step it names the owner entry point, `warden_executes`, +the auth method and lane, whether an attended human login is unavoidable (with +the type's justification), and the verification command. Write it for the first +type, `oidc-browser-relying-party`, covering key-cape client registration, +custody minting and LLDAP group membership. + +2026-10-08: NK-WP-0048-T03/T04 landed with a drafted section. Review is in +`history/2026-10-08-oidc-rp-follow-through-review.md`. It requires three +changes before T02: a per-step `lane`, a narrow `warden_executes`, and +`value_handling`. It also lists four refinements. This task is done when +net-kingdom acknowledges the review (NK-WP-0048-T05). + +2026-10-09: R4 was withdrawn in favour of ops-mason's per-case +`{consuming_service}-keycape-client`, with the entry body templated in the type +(R4′, see the addendum in the review). + +2026-10-09: the revised 1.0 type and canon now carry R1–R3, R4′, R5–R7. +`warden case check-type ` (`src/warden/case_type.py`, tests in +`tests/test_case_type.py`) checks a type's follow-through mechanically: the +live type is schema-conformant, with one attended session +(`key-cape-oidc-login`). It is not yet plannable, because two lanes are not +in the catalog: `identity-provisioner-groups` (net-kingdom) and +`keycape-client-secret-generate` (railiance-platform). These gate T02, not +T01. T01 still closes on net-kingdom's acknowledgement (NK-WP-0048-T05). + +2026-10-09: ops-warden cannot add `identity-provisioner-groups` as the type +declares it. The provisioner accepts only user-engine's runtime token and +admits only user-engine pods, so no caller-identity path exists. R8 (review +addendum) asks net-kingdom either to move `ensure-groups` into the existing +attended session (recommended) or to give the provisioner an operator +credential. `keycape-client-secret-generate` needs a generate-in-custody entry +point from railiance-platform, and the broker does not have one yet. Both were +sent to their owners. + +2026-10-09: done. net-kingdom took R8 option 1, so `ensure-groups` runs attended +in the `register-client` session and `identity-provisioner-groups` is dropped +(net-kingdom `0e86f95`). The founder acknowledged the type with memo r4 (hub +decision `e418263e`, NK-WP-0048-T05 done). + +```task +id: WARDEN-WP-0044-T02 +status: done +depends_on: [WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "5f68c991-f490-58f8-a121-92f74073d272" +``` + +### T02 — Plan a case run + +`warden case plan ` checks that the case is accepted and materialized, +resolves each step to its catalog entry and entry point, and prints the run: +which steps warden executes, which run as the attended caller, what the +verification is. No secret values; no execution. + +Tests: refuse an unaccepted or unmaterialized case; refuse a step whose catalog +entry is missing or not reviewed; show attended steps explicitly. + +2026-10-09: done. `warden case plan [--type] [--json]` +(`src/warden/case_plan.py`, 14 tests in `tests/test_case_plan.py`). It refuses: +- a case that is not `materialized`, or has no acceptance record +- a type that is mismatched or not acknowledged +- any `case check-type` finding, including a missing or unreviewed lane +- undeclared, type-fixed or secret-sensitivity parameters +- a placeholder the case does not set, or an unsupported `when` + +It fills type defaults (`issuer`), evaluates ` == ` conditions, +renders owner, entry point, auth and ready_check, groups attended steps per +lane, and lists the applicable verification. + +The live Forgejo case is correctly refused: it is `accepted`, not +materialized. A materialized copy, with a stand-in lane for +`keycape-client-secret-generate`, renders six steps: two attended steps plus +`register-client` in one `key-cape-oidc-login` session, one warden step +(`mint-secret`), and two owner steps. Seven checks follow. + +Observation for net-kingdom (not blocking): `bind-env` runs before +`register-client`, in type order. Its entry carries a literal +`KEYCAPE__CLIENT_SECRET`, which is not a `{param}` placeholder, so it +reaches the plan unrendered. + +2026-10-09 follow-up: +- **Format specs.** `_render` supports net-kingdom's `{param:env}` spec (upper + case, `-` as `_`, the same as `keycape-client.py env_name`) and refuses any + other spec or `!conversion`. +- **Case reference.** The plan prints `case-ref: @sha256:` over the + case file's exact bytes. This is the reference proposed to railiance-platform + for `credential.py generate --case-ref` (RPF-WP-0054-T01); the broker + re-derives it from the file. +- 18 tests now. + +```task +id: WARDEN-WP-0044-T03 +status: progress +depends_on: [WARDEN-WP-0044-T02, MASON-WP-0010-T03] +blocking_reason: "Local durable execution kernel and injected failure harness implemented. Native acceptance still requires authenticated owner machine descriptors/adapters, actual Forgejo materialization receipt, contained session integration, ordered verification and first native run." +priority: high +state_hub_task_id: "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89" +``` + +### T03 — Run and verify a case + +`warden case run ` executes the plan. Owner entry points run through +the existing proxy as the caller; steps that need an attended login use the +existing contained login envelope (silent command, session revoked afterwards), +grouped into as few attended sessions as possible. Then it runs the type's +verification and writes a receipt without values. `warden case rollback` +reverses exactly this case's steps. + +Acceptance: the Forgejo reference case (NK-WP-0048-T06) reaches `live` through +this path, with no case-specific script. + +2026-10-17: original-intent lookup/context interface for NK-WP-0048-T15, source +and synthetic tests only. `src/warden/case_run.py`: `Executor.run` writes an +`OriginalIntent` (schema `warden.case.original-intent.v1`: the dispatched run +Invocation — attempt, step, owner, target, actor, principal, lane, all six pins, +tenant, realm — plus admitted adapter source) in the same SQLite transaction as +the pre-dispatch journal row. `Journal.original_intent(binding, current, source)` +returns it after restart and refuses on a missing record or any +pins/source/step/owner/target/principal/lane/tenant/realm mismatch, before any +journal write, so the step stays uncertain. An adapter's +`reconcile_original(ReconcileContext(original, attempt))` receives the original +intent separately from the new reconcile attempt. This is Warden's record of what +it dispatched, not owner created-only provenance; tenant/realm are as supplied at +admission. Owner descriptors/adapters, contained session integration, Forgejo +materialization and the native run remain open. + +Agreed with railiance-platform (RPF-WP-0054-T01, 2026-10-09): +- `mint-secret` runs `credential.py generate --grant keycape/client-secret-generate + --client-id --case-ref --case-file ` as the caller's + child process. The broker re-derives the case-ref and checks status and + client_id. +- warden refuses that step when `WARDEN_AGENT_ID` is set. + +2026-10-10: railiance-platform implemented the command (RPF-WP-0054-T03). It is +catalogued as draft `keycape-client-secret-generate`, with `exec_owner` +railiance-platform and `exec_capable: false`. `warden case check-type` now +reports the lane as LANE-UNREVIEWED rather than LANE-MISSING. Before it can be +activated: +- the founder binds the issuer policy to the operator OIDC role +- key-cape confirms the secret format +- the RPF-WP-0054-T05 probe passes + +2026-10-10 (later): +- **Broker lane.** The issuer role is bound (CCR-2026-0043) and the T05 probe + passed (railiance-platform `21b8af3`). Only key-cape's format confirmation is + left. The playbook now includes the issuer-role login, a 15m token with + unlimited uses that `warden case run` must group with the attended session. +- **Env spec.** net-kingdom adopted `{client_id:env}` as a 1.0 patch + (`9a9b8e3`). The plan renders `KEYCAPE_FORGEJO_CLIENT_SECRET`. +- **Forgejo case.** Rendered and built by ops-mason. It is not yet + `materialized`: MASON-WP-0010-T03 waits on the railiance-platform CCRs and + their founder apply. + +2026-10-10: `keycape-client-secret-generate` activated on railiance-platform's +go-ahead (message 20973a9c) after key-cape confirmed the format (4e0213b3). +`warden case check-type` on the live type now reports `plannable=True`. The +only thing T03 still waits on is the Forgejo case reaching `materialized` +(MASON-WP-0010-T03). + +2026-10-10: net-kingdom moved the type to 1.1 (`e2174ce`) and re-pinned the +Forgejo case. 1.1 adds the verification `consumer-enforces-groups` and requires +the hand-over to enforce `access_groups`. `check-type` on 1.1 reports +`plannable=True`, and a materialized copy renders six steps and the new check. +The case-ref digest covers the whole case file, including `status`, so it +changes when the case becomes `materialized`. `warden case run` therefore takes +it from the file at run time, and the broker re-derives it from the same file. + +```task +id: WARDEN-WP-0044-T04 +status: wait +depends_on: [WARDEN-WP-0044-T03] +blocking_reason: "Catalog integration follows a proven run." +priority: medium +state_hub_task_id: "4a0fa4a7-5ca9-518a-ab92-a79a5e2cf93e" +``` + +### T04 — Catalog entries from types + +Let a type contribute reviewed catalog entries once, so that every case of that +type routes without a new catalog edit. Cases add parameter values, not routes. +`warden route find` should be able to answer "which SecurityCaseType covers this +need?" before anyone builds by hand. + + +2026-10-10 consolidation source prototype (not an accepted owner adapter API): +`src/warden/case_receipt_state.py` provides a pure immutable receipt reducer, +with exact case/type/adapter/catalog/acceptance/run-sheet pins, running intent +before dispatch, uncertain recovery after a lost result, no success replay, +explicit retry-safe failed-before-change retry and created-only reverse undo. +It executes nothing and accepts no command output. The caller must durably +persist running before any future side effect; storage/authenticated owner +receipts/live adapters are not implemented. Focused offline tests cover changed +pins, lost receipts, retry/refusal, uncertain rollback and shared-object +preservation. Source proposal: `docs/security-case-execution-proposal.md`. + +T03 remains open: typed owner execution/receipt contracts require review, the +Forgejo structural materialization needs owner receipts, and CLI dispatch, +contained sessions, native verification and provenance remain unimplemented. +There is a circularity to resolve with net-kingdom/ops-mason: materialization +cannot require mint-secret if mint-secret is itself a follow-through step whose +planning requires materialized. No case status is changed by this prototype. + + +2026-10-11: the exact execution-design memo version1 has confirmed native human +acceptance (approval e46b149b-ec3c-4b3b-98a7-dcb4934448ca). The approved proposal +bytes remain unchanged. `src/warden/case_run.py` now implements a closed v1 local +execution seam, independent admission/evidence callbacks, private transactional +SQLite receipt storage, process locking, running-before-dispatch, crash recovery +to uncertainty, explicit safe retry, separately required broker-grant and parent-session revocation +proofs, native owner reconciliation and created-only +reverse rollback. Thirty-seven kernel tests and fourteen reducer tests pass; +ruff checks pass. See `docs/security-case-execution-kernel-v1.md` for exact scope +and receipt provenance. No installed native adapter or live CLI execution is +claimed. Current prose types stay plan-only; acknowledged descriptor/type machine +seams and authenticated owner/caller receipts are still required. T03 progresses +for implementation but cannot finish before ordered native Forgejo acceptance. + +2026-10-16 source preparation (isolated checkout; no native acceptance): +T03's browser parameter mismatch now has a producer-validator-backed projection +in `case_inputs.py`, explicitly configured on the real Executor. Exact 1.1/1.2 +parameter definitions and all four reviewed producer modules are pinned; full case +validation admits reviewed security-sensitive metadata while unknown/fixed/secret +inputs and type/producer drift refuse. Only the reviewed descriptor target +reaches the owner callback; no parameter journal payload is added. Actual +Executor/producer tests cover positive versions and refusal/containment paths. +Acknowledged machine descriptors, authenticated owner evidence, real Forgejo +materialization and contained native sessions remain mandatory and unimplemented; +T03 stays progress, T04 stays wait. No type or case status changed. + +2026-10-16 producer source refresh: independently reviewed NetKingdom +`015694ee414c802dc2ef1fa0120dfed610086e69` optional descriptor-map repair. +Only the validator/evidence source pins change; exact parameter and transitive +source identity remain enforced. Actual canonical producer, Executor and reducer +suite passes 87 checks, including 14 new malformed-map integration cases; all +four missing/changed modules still refuse. Review: +`docs/reviews/2026-10-16-browser-producer-pin-refresh.md`. No adapter attestation, +native acceptance, credential use or privileged dispatch. T03 remains progress. diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probe-results.json b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probe-results.json new file mode 100644 index 00000000..069d864c --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probe-results.json @@ -0,0 +1,17 @@ +{ + "argv": [ + "python3", + "-B", + "-m", + "pytest", + "-q", + "-c", + "pytest.ini", + "tests/test_case_run.py", + "tests/test_context_compare_probes.py" + ], + "exit_code": 0, + "stdout": "........................................................ [100%]\n56 passed in 2.18s\n", + "stderr": "", + "scope": "Changed concurrent source snapshot only; no canonical mutation or independent owner proof claimed." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probes.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probes.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probes.stdout.txt b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probes.stdout.txt new file mode 100644 index 00000000..7679360e --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/delta-probes.stdout.txt @@ -0,0 +1,2 @@ +........................................................ [100%] +56 passed in 2.18s diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/manifest.json b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/manifest.json new file mode 100644 index 00000000..56b6230b --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/manifest.json @@ -0,0 +1,124 @@ +{ + "files": [ + { + "path": "comparison-review.json", + "sha256": "sha256:2f1763a685f1ce1da2f42421d3a1cdd2d3a7253be1fb39c6b48194ff29f1ed85" + }, + { + "path": "delta-fixture/docs/security-case-execution-kernel-v1.md", + "sha256": "sha256:0eb563dbaea739fba928d04708c2c2bdac592783bbe68e1271041b0d1b1994cb" + }, + { + "path": "delta-fixture/pytest.ini", + "sha256": "sha256:363381771127e3479fe49773659cc2a8dc2b2f727459ebd057ac39e79e98a94e" + }, + { + "path": "delta-fixture/src/warden/__init__.py", + "sha256": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a" + }, + { + "path": "delta-fixture/src/warden/case_inputs.py", + "sha256": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7" + }, + { + "path": "delta-fixture/src/warden/case_receipt_state.py", + "sha256": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc" + }, + { + "path": "delta-fixture/src/warden/case_run.py", + "sha256": "sha256:0cc62a33dbb3b90276b291f77fa5d0a906f19296874d68636957763f576d1ae2" + }, + { + "path": "delta-fixture/tests/test_case_original_context_consistency.py", + "sha256": "sha256:8483bb5915701e8bd0749e85097e11076ffd539084ca68e9ec1c565babc157d1" + }, + { + "path": "delta-fixture/tests/test_case_run.py", + "sha256": "sha256:bc7264848e76e3576821523fec0c4925c4446eaf470f859fe245c98ea072d139" + }, + { + "path": "delta-fixture/tests/test_context_compare_probes.py", + "sha256": "sha256:ce64b52ba98d559f4e13f382f7c204a38d6779e0958f37e215b43ec4cfe5fc39" + }, + { + "path": "delta-fixture/workplans/WARDEN-WP-0044-security-case-follow-through.md", + "sha256": "sha256:44686df53dac792e46791878c3ccf8c187bb14e2de33eeac411be2005f7f2aa1" + }, + { + "path": "delta-probe-results.json", + "sha256": "sha256:b5cc1e7fc2f4bba33ce9256d4780149c5c3f8b850b7d666f5de15e582ebad774" + }, + { + "path": "delta-probes.stderr.txt", + "sha256": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "delta-probes.stdout.txt", + "sha256": "sha256:3f7dcab165b24572fff8cfcb6aa0fbe74509c434f163475bf1fbe87c051c6b8d" + }, + { + "path": "minimal-delta-tests.json", + "sha256": "sha256:0016cc87173f7485a94b2695c48c7df4fd699743b19b530164229bbc2c4ffd18" + }, + { + "path": "minimal-integration.patch", + "sha256": "sha256:d058f7f5da015d9dc3fe406982e83f52d0c1b2acb33f0e41861a7bd0fdf8f02e" + }, + { + "path": "patch-check.json", + "sha256": "sha256:7eb832e50261c31f86d01e7e45bfc30197647417086d927e3f72a397e09e11c4" + }, + { + "path": "probe-results.json", + "sha256": "sha256:d6dc95750038ce5f26fa4bdb1f7954959270eb74b4bb90b0939677896480fec5" + }, + { + "path": "probes.stderr.txt", + "sha256": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "probes.stdout.txt", + "sha256": "sha256:6bf1b3ccd554890c5dc39fed048dbb793b2b82ca9135cd4376303c55798d6139" + }, + { + "path": "snapshot/docs/security-case-execution-kernel-v1.md", + "sha256": "sha256:0eb563dbaea739fba928d04708c2c2bdac592783bbe68e1271041b0d1b1994cb" + }, + { + "path": "snapshot/pytest.ini", + "sha256": "sha256:363381771127e3479fe49773659cc2a8dc2b2f727459ebd057ac39e79e98a94e" + }, + { + "path": "snapshot/src/warden/__init__.py", + "sha256": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a" + }, + { + "path": "snapshot/src/warden/case_inputs.py", + "sha256": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7" + }, + { + "path": "snapshot/src/warden/case_receipt_state.py", + "sha256": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc" + }, + { + "path": "snapshot/src/warden/case_run.py", + "sha256": "sha256:0574036ca7b74bdb9124f6af4888e63ec231ee60f5c9f34cbe2fb1acee39f671" + }, + { + "path": "snapshot/tests/test_case_run.py", + "sha256": "sha256:bc7264848e76e3576821523fec0c4925c4446eaf470f859fe245c98ea072d139" + }, + { + "path": "snapshot/tests/test_context_compare_probes.py", + "sha256": "sha256:85dd7b7ea80c5c2f29fd7125e067c1fa765c99741d15d0a543a238dac2bc524a" + }, + { + "path": "snapshot/workplans/WARDEN-WP-0044-security-case-follow-through.md", + "sha256": "sha256:44686df53dac792e46791878c3ccf8c187bb14e2de33eeac411be2005f7f2aa1" + }, + { + "path": "source-state.json", + "sha256": "sha256:1d0df4438451b1d0b688d66f09cf924f0b009ce326535c31af30dcdf2d466443" + } + ] +} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/minimal-delta-tests.json b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/minimal-delta-tests.json new file mode 100644 index 00000000..d57339fb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/minimal-delta-tests.json @@ -0,0 +1,17 @@ +{ + "argv": [ + "python3", + "-B", + "-m", + "pytest", + "-q", + "-c", + "pytest.ini", + "tests/test_case_run.py", + "tests/test_case_original_context_consistency.py" + ], + "exit_code": 0, + "stdout": "...................................................... [100%]\n54 passed in 1.98s\n", + "stderr": "", + "scope": "Exact modified temporary snapshot only; no canonical or native invocation." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/minimal-integration.patch b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/minimal-integration.patch new file mode 100644 index 00000000..a5319724 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/minimal-integration.patch @@ -0,0 +1,427 @@ +--- a/src/warden/case_run.py ++++ b/src/warden/case_run.py +@@ -189,7 +189,66 @@ + invocation: Invocation + source: str + ++ def validate(self) -> None: ++ """Fixed bounded metadata only; tenant/realm are not authority claims. ++ ++ Current admit leaves tenant/realm empty. Nonempty future coordinates need ++ separate owner provenance; serializability alone must never supply it. ++ """ ++ if ( ++ type(self.invocation) is not Invocation ++ or type(self.invocation.pins) is not Pins ++ ): ++ raise Refused("exact original invocation required") ++ i = self.invocation ++ fields = (i.step, i.owner, i.target, i.principal, i.lane, i.attempt_id) ++ if ( ++ not all( ++ type(value) is str and IDENTIFIER.fullmatch(value) for value in fields ++ ) ++ or type(i.actor) is not str ++ or i.actor not in ("human", "agent") ++ or type(i.operation) is not str ++ or i.operation not in ("run", "rollback") ++ or not all( ++ type(value) is str and (not value or IDENTIFIER.fullmatch(value)) ++ for value in (i.tenant, i.realm) ++ ) ++ or type(self.source) is not str ++ or not DIGEST.fullmatch(self.source) ++ or type(self.binding) is not str ++ or self.binding != f"{i.step}:{i.target}" ++ ): ++ raise Refused("invalid original intent metadata") ++ if set(Invocation.__dataclass_fields__) != { ++ "step", ++ "owner", ++ "target", ++ "actor", ++ "principal", ++ "lane", ++ "pins", ++ "attempt_id", ++ "operation", ++ "tenant", ++ "realm", ++ }: ++ raise Refused("original invocation field contract changed") ++ if not all( ++ type(v) is str and DIGEST.fullmatch(v) ++ for v in ( ++ i.pins.case, ++ i.pins.case_type, ++ i.pins.adapter, ++ i.pins.catalog, ++ i.pins.acceptance, ++ i.pins.run_sheet, ++ ) ++ ): ++ raise Refused("invalid original intent pins") ++ + def payload(self) -> str: ++ self.validate() + return json.dumps( + {"schema": INTENT_SCHEMA, "binding": self.binding, "source": self.source} + | asdict(self.invocation), +@@ -199,13 +258,44 @@ + + @classmethod + def decode(cls, raw: str) -> OriginalIntent: +- obj = json.loads(raw) +- fields = {f for f in Invocation.__dataclass_fields__} +- if set(obj) != fields | {"schema", "binding", "source"} or obj["schema"] != INTENT_SCHEMA: +- raise Refused("unknown original intent schema") +- inv = {k: obj[k] for k in fields} +- inv["pins"] = Pins(**inv["pins"]) +- return cls(obj["binding"], Invocation(**inv), obj["source"]) ++ def unique(pairs): ++ result = {} ++ for key, value in pairs: ++ if key in result: ++ raise Refused("duplicate original intent field") ++ result[key] = value ++ return result ++ ++ try: ++ if type(raw) is not str or len(raw.encode()) > MAX_SOURCE_BYTES: ++ raise Refused("original intent input exceeds bound") ++ obj = json.loads(raw, object_pairs_hook=unique) ++ fields = { ++ "step", ++ "owner", ++ "target", ++ "actor", ++ "principal", ++ "lane", ++ "pins", ++ "attempt_id", ++ "operation", ++ "tenant", ++ "realm", ++ } ++ if ( ++ type(obj) is not dict ++ or set(obj) != fields | {"schema", "binding", "source"} ++ or obj["schema"] != INTENT_SCHEMA ++ ): ++ raise Refused("unknown original intent schema") ++ inv = {k: obj[k] for k in fields} ++ inv["pins"] = Pins(**inv["pins"]) ++ result = cls(obj["binding"], Invocation(**inv), obj["source"]) ++ result.validate() ++ return result ++ except (TypeError, ValueError, KeyError): ++ raise Refused("invalid original intent record") from None + + + @dataclass(frozen=True, slots=True) +@@ -256,28 +346,76 @@ + "CREATE TABLE IF NOT EXISTS intents (seq INTEGER PRIMARY KEY, attempt_id TEXT NOT NULL UNIQUE, binding TEXT NOT NULL, payload TEXT NOT NULL)" + ) + +- def original_intent(self, binding: str, current: Invocation, source: str) -> OriginalIntent: ++ def original_intent( ++ self, binding: str, current: Invocation, source: str ++ ) -> OriginalIntent: + """Latest original run intent for binding, refused unless it matches exactly. + + Missing or mismatched records refuse; the step stays uncertain. + """ + row = self.db.execute( +- "SELECT payload FROM intents WHERE binding=? ORDER BY seq DESC LIMIT 1", ++ "SELECT attempt_id,payload FROM intents WHERE binding=? ORDER BY seq DESC LIMIT 1", + (binding,), + ).fetchone() + if row is None: + raise Refused("original intent record missing; step stays uncertain") +- intent = OriginalIntent.decode(row[0]) ++ intent = OriginalIntent.decode(row[1]) + o = intent.invocation ++ if row[0] != o.attempt_id: ++ raise Refused("original intent attempt identity differs") + if intent.binding != binding or o.operation != "run": + raise Refused("original intent binding mismatch") + if o.pins != current.pins: + raise Refused("original intent pins mismatch") + if intent.source != source: + raise Refused("original intent source mismatch") +- for name in ("step", "owner", "target", "principal", "lane", "tenant", "realm"): ++ for name in ( ++ "step", ++ "owner", ++ "target", ++ "actor", ++ "principal", ++ "lane", ++ "tenant", ++ "realm", ++ ): + if getattr(o, name) != getattr(current, name): + raise Refused(f"original intent {name} mismatch") ++ # Match the persisted original running receipt. An orphan context or a ++ # payload from another attempt cannot become the current lookup key. ++ receipts = self.db.execute( ++ "SELECT payload FROM receipts WHERE binding=? ORDER BY seq DESC LIMIT ?", ++ (binding, MAX_HISTORY + 1), ++ ).fetchall() ++ if len(receipts) > MAX_HISTORY: ++ raise Refused("original intent receipt history exceeds bound") ++ matched = False ++ try: ++ for (raw,) in receipts: ++ if type(raw) is not str or len(raw.encode()) > MAX_SOURCE_BYTES: ++ raise Refused("original receipt input exceeds bound") ++ payload = json.loads(raw) ++ if ( ++ payload.get("attempt_id") != o.attempt_id ++ or payload.get("operation") != "run" ++ ): ++ continue ++ last = payload.get("receipts", [])[-1] ++ if last.get("outcome") != "running": ++ continue ++ if ( ++ payload.get("journal_schema") != "warden.case.step-journal.v1" ++ or payload.get("step_id") != o.step ++ or payload.get("owner") != o.owner ++ or payload.get("pins") != asdict(o.pins) ++ ): ++ raise Refused("original running receipt binding differs") ++ matched = True ++ break ++ except (TypeError, ValueError, KeyError, IndexError, AttributeError): ++ raise Refused("invalid original running receipt") from None ++ if not matched: ++ raise Refused("original running receipt missing; no backfill") + return intent + + @contextmanager +@@ -355,6 +493,23 @@ + invocation: Invocation | None = None, + intent: OriginalIntent | None = None, + ): ++ if intent is not None: ++ if type(intent) is not OriginalIntent: ++ raise Refused("exact original intent required") ++ intent.validate() ++ expected = ( ++ Outcome.RUNNING ++ if intent.invocation.operation == "run" ++ else Outcome.ROLLBACK_RUNNING ++ ) ++ if ( ++ intent.binding != binding ++ or intent.invocation != invocation ++ or intent.invocation.pins != state.pins ++ or not state.last ++ or state.last.outcome is not expected ++ ): ++ raise Refused("original intent and running receipt differ") + self.db.execute("BEGIN IMMEDIATE") + try: + if intent is not None: +--- /dev/null ++++ b/tests/test_case_original_context_consistency.py +@@ -0,0 +1,198 @@ ++"""Original-intent consistency checks with generated metadata only. ++ ++Reuse the existing synthetic owner rig. These checks do not provide a native ++holder or independently authenticate original creation from local SQL/JSON. ++""" ++ ++from dataclasses import asdict, replace ++import json ++from pathlib import Path ++import sqlite3 ++import subprocess ++import sys ++import pytest ++from test_case_run import rig as rig ++from warden.case_receipt_state import Outcome, Refused ++from warden.case_run import OriginalIntent ++ ++ ++def interrupted(rig): ++ executor, args, calls, sources, kwargs = rig ++ old = executor.adapters[args[0].adapter] ++ seen = [] ++ ++ def lost(inv): ++ calls.append(inv) ++ raise TimeoutError("generated lost response") ++ ++ def original(ctx): ++ seen.append(ctx) ++ return old.reconcile(ctx.attempt) ++ ++ executor.adapters[old.reference] = replace( ++ old, invoke=lost, reconcile=None, reconcile_original=original ++ ) ++ assert executor.run(*args).last.outcome is Outcome.UNCERTAIN ++ return executor, args, calls, old, seen ++ ++ ++def alter_payload(executor, **changes): ++ row = executor.journal.db.execute( ++ "SELECT seq,payload FROM intents ORDER BY seq DESC LIMIT 1" ++ ).fetchone() ++ obj = json.loads(row[1]) ++ obj.update(changes) ++ executor.journal.db.execute( ++ "UPDATE intents SET payload=? WHERE seq=?", (json.dumps(obj), row[0]) ++ ) ++ ++ ++def test_original_actor_mismatch_is_refused(rig): ++ executor, args, calls, old, _ = interrupted(rig) ++ alter_payload(executor, actor="agent") ++ with pytest.raises(Refused): ++ executor.journal.original_intent( ++ "mint-secret:fixture-client", args[1], old.source ++ ) ++ ++ ++def test_payload_attempt_id_must_match_sql_column_and_original_receipt(rig): ++ executor, args, calls, old, _ = interrupted(rig) ++ actual = calls[0].attempt_id ++ alter_payload(executor, attempt_id="attempt-unrelated") ++ with pytest.raises(Refused): ++ executor.journal.original_intent( ++ "mint-secret:fixture-client", args[1], old.source ++ ) ++ assert ( ++ executor.journal.db.execute("SELECT attempt_id FROM intents").fetchone()[0] ++ == actual ++ ) ++ ++ ++def test_orphan_intent_without_original_running_receipt_is_refused(rig): ++ executor, args, calls, old, _ = interrupted(rig) ++ executor.journal.db.execute( ++ "DELETE FROM receipts WHERE seq=(SELECT MIN(seq) FROM receipts)" ++ ) ++ assert ( ++ executor.journal.load("mint-secret:fixture-client", args[2]).last.outcome ++ is Outcome.UNCERTAIN ++ ) ++ with pytest.raises(Refused): ++ executor.journal.original_intent( ++ "mint-secret:fixture-client", args[1], old.source ++ ) ++ ++ ++def test_serializer_refuses_arbitrary_values_outside_normal_admit(rig): ++ _, args, _, _, _ = rig ++ inv = replace( ++ args[1], ++ target={"unexpected": "synthetic-owner-payload"}, ++ operation="run", ++ attempt_id="attempt-fixture", ++ ) ++ record = OriginalIntent("mint-secret:fixture-client", inv, "sha256:" + "1" * 64) ++ with pytest.raises(Refused): ++ OriginalIntent.decode(record.payload()) ++ # This is a direct serialization API gap, not an observed normal-admit leak. ++ ++ # Owner trust is the existing synthetic fixture; this proves call shape only. ++ ++ ++@pytest.mark.parametrize( ++ "field,value", ++ [ ++ ("principal", "another-person"), ++ ("tenant", "another-tenant"), ++ ("realm", "another-realm"), ++ ], ++) ++def test_existing_lookup_does_refuse_stored_principal_tenant_realm_mismatch( ++ rig, field, value ++): ++ executor, args, _, old, _ = interrupted(rig) ++ alter_payload(executor, **{field: value}) ++ with pytest.raises(Refused): ++ executor.journal.original_intent( ++ "mint-secret:fixture-client", args[1], old.source ++ ) ++ ++ ++def test_existing_atomic_insert_failure_prevents_dispatch_and_both_records(rig): ++ executor, args, calls, _, _ = rig ++ executor.journal.db.execute( ++ "CREATE TRIGGER fail_receipt BEFORE INSERT ON receipts BEGIN SELECT RAISE(ABORT,'fixture'); END" ++ ) ++ with pytest.raises(sqlite3.IntegrityError): ++ executor.run(*args) ++ assert not calls ++ assert executor.journal.db.execute("SELECT COUNT(*) FROM intents").fetchone() == ( ++ 0, ++ ) ++ assert executor.journal.db.execute("SELECT COUNT(*) FROM receipts").fetchone() == ( ++ 0, ++ ) ++ ++ ++def test_existing_missing_original_refuses_before_callback(rig): ++ executor, args, _, _, seen = interrupted(rig) ++ executor.journal.db.execute("DELETE FROM intents") ++ with pytest.raises(Refused): ++ executor.reconcile(*args) ++ assert not seen ++ assert ( ++ executor.journal.load("mint-secret:fixture-client", args[2]).last.outcome ++ is Outcome.UNCERTAIN ++ ) ++ ++ ++def test_existing_current_parent_revocation_remains_mandatory(rig): ++ executor, args, _, _, _ = interrupted(rig) ++ old = executor.evidence_verifier ++ ++ def refuse_session(purpose, source, evidence): ++ return ( ++ False ++ if purpose.startswith("session-revocation:") ++ else old(purpose, source, evidence) ++ ) ++ ++ executor.evidence_verifier = refuse_session ++ with pytest.raises(Refused): ++ executor.reconcile(*args) ++ assert ( ++ executor.journal.load("mint-secret:fixture-client", args[2]).last.outcome ++ is Outcome.UNCERTAIN ++ ) ++ ++ ++def test_existing_intent_survives_actual_distinct_interpreter(rig): ++ executor, args, calls, old, _ = interrupted(rig) ++ code = """import json,sys ++from pathlib import Path ++from warden.case_run import Journal,Invocation ++from warden.case_receipt_state import Pins ++obj=json.load(sys.stdin);obj['pins']=Pins(**obj['pins']);j=Journal(Path(sys.argv[1])) ++with j.locked(): ++ i=j.original_intent('mint-secret:fixture-client',Invocation(**obj),sys.argv[2]) ++ print(json.dumps({'attempt':i.invocation.attempt_id,'operation':i.invocation.operation})) ++""" ++ result = subprocess.run( ++ [sys.executable, "-B", "-c", code, str(executor.journal.directory), old.source], ++ input=json.dumps(asdict(args[1])), ++ text=True, ++ capture_output=True, ++ timeout=15, ++ env={ ++ "PATH": "/usr/bin:/bin", ++ "PYTHONPATH": str(Path(__file__).parents[1] / "src"), ++ "PYTHONDONTWRITEBYTECODE": "1", ++ }, ++ check=True, ++ ) ++ assert json.loads(result.stdout) == { ++ "attempt": calls[0].attempt_id, ++ "operation": "run", ++ } diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/patch-check.json b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/patch-check.json new file mode 100644 index 00000000..37eccb2d --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/patch-check.json @@ -0,0 +1,7 @@ +{ + "base": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "exit_code": 0, + "stdout": "", + "stderr": "", + "current_source_unchanged": true +} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probe-results.json b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probe-results.json new file mode 100644 index 00000000..5a377b18 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probe-results.json @@ -0,0 +1,17 @@ +{ + "observed_at": "2026-10-17T05:53:57.282196+00:00", + "argv": [ + "python3", + "-B", + "-m", + "pytest", + "-q", + "-c", + "pytest.ini", + "tests/test_context_compare_probes.py" + ], + "exit_code": 0, + "stdout": "............. [100%]\n13 passed in 0.71s\n", + "stderr": "", + "scope": "Only exact snapshot; no tests against canonical user tree. Passing counterexample probes reproduce documented source gaps, not security/native acceptance." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probes.stderr.txt b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probes.stderr.txt new file mode 100644 index 00000000..e69de29b diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probes.stdout.txt b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probes.stdout.txt new file mode 100644 index 00000000..ec8b5d42 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/probes.stdout.txt @@ -0,0 +1,2 @@ +............. [100%] +13 passed in 0.71s diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/docs/security-case-execution-kernel-v1.md b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/docs/security-case-execution-kernel-v1.md new file mode 100644 index 00000000..35289d77 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/docs/security-case-execution-kernel-v1.md @@ -0,0 +1,127 @@ +# Closed case execution kernel v1 — 2026-10-11 + +Owner implementation record for WARDEN-WP-0044-T03. The architectural proposal +at SHA256 78ab1439e4c5beb205aeccf15b1b2e706511be55ab195c5a3aa682f326c7c77c +was accepted through memo `infd-20261010-security-case-execution`, version 1, +approval e46b149b-ec3c-4b3b-98a7-dcb4934448ca, confirmed disposition +disp-cf44dacb-2117-4de7-8f90-cfa99f84f325 at 2026-10-10T23:23:31Z. The original +approved proposal remains byte-identical. That decision accepts source design; +it grants no execution session, credential or deployment authority. + +`src/warden/case_run.py` advances the pure reducer into an executable injected +owner harness with private durable receipts. Descriptor version +`warden.case.execution.v1` is a closed local implementation seam, not an accepted +NetKingdom type schema. It contains fixed owner/adapter/source digest, actor, +lane, target parameter, effect, reviewed created-object identifiers declared +retry safety and explicit required revocation scopes (parent-session and broker-grant). +A mint descriptor cannot omit either scope. Unknown fields, free shell text and secret parameters are refused. +An acknowledged type must additionally pin the descriptor bytes under its +`execution_descriptors` map and independent owner evidence must authenticate +that exact machine-seam revision. Existing descriptive types lack this map and +remain plan-only. Their acknowledged labels do not authorize this new seam. + +There are no installed owner adapters, native provenance verifier or dispatch CLI +in this change. Callables are injected only through a fixed adapter registry, +with immutable owner reference/source pins. A real integration must supply +independent owner/native verification or explicit attended manual evidence; +a bare JSON receipt identifier is insufficient. Fixture verifiers in tests +are deliberately fake and attest no native case. Caller sessions are independently +verified for exact principal/actor/lane at admission and again before dispatch; +input actor labels alone are never sufficient. Agent mint is refused, including +when WARDEN_AGENT_ID appears after initial human admission. + +Each admission pins exact case/type/descriptor/catalog bytes, acceptance reference +and run-sheet identity. Actual ops-mason materialization evidence is required +against the case source, separately from its status label. Case parameters supply +an exact metadata target. Source/session changes cannot inherit journal success. +Owner results have a closed metadata shape, result evidence fingerprint and +separate independently authenticated grant/parent-session revocation evidence +fingerprints. An aggregate revoked boolean alone cannot satisfy either scope. Unknown result payloads, unverified evidence, absent revocation and +created objects outside the reviewed scope become uncertain, never success. +Python and OS stdout/stderr, including inherited child-process output, are +discarded without buffering or retention under a serialized fd containment +context. Independent provenance verification uses the same containment and +sanitizes exceptions. All source inputs are capped at 256 KiB before parsing. +Error text is not copied to the journal. KeyboardInterrupt/SystemExit and other +owner interruptions are rethrown with a fixed message while retaining running +intent for crash recovery. + +The journal uses an owner-private 0700 directory and 0600 files, refuses symlinks +and insecure modes, uses SQLite FULL synchronization and commits running intent +before calling the adapter. A separate process lock prevents concurrent mutation. +A crash between dispatch and final receipt leaves running; the next lock holder +persists uncertain before refusing replay. Both intent and outcome remain in +history. Each persisted attempt has a unique identity bound to all exact pins, +operation (run/reconcile/rollback), sequence and nonce. Adapter results and both +revocation proof purposes carry that identity. Prior-attempt evidence cannot +verify a safe retry, even if a caller rewrites result labels. Descriptor-created +objects are capped at 32 and histories at 128 transitions, with dispatch stopped +before archival is required. A throw or lost response is not failed-before-change. A safe retry needs +verified failed-before-change, unchanged pins, descriptor retry-safe and explicit +retry. Success and no-change cannot replay. + +Reconciliation is an explicit fixed owner callback, never the mutation callback. +It may verify an already-present native CAS result as no-change/success, or prove +failed-before-change; it never tells Warden to regenerate a credential. Uncertain +or failed-after-change requires this owner-native inspection before any retry. +Owner proof and revocation remain mandatory. Rollback visits only objects in a +verified creation receipt, in reverse order; no-change has no undo and shared +objects are excluded. Unknown/lost undo blocks another rollback. + +The kernel operates individual admitted steps. Full ordered run-sheet orchestration, +verification inventory, contained login integration, native owner adapters and +CLI activation remain WARDEN-WP-0044-T03; no overall native-complete result is +implemented. Current Forgejo source is not materialized and has no acknowledged +machine descriptors. Its first native run remains an acceptance requirement, +with real materialization, separately authenticated broker-grant and parent-session +revocation receipts, session/owner receipts and consumer enforcement. This +implementation neither promotes a type nor changes any case status. + +Validation: 37 new kernel tests plus 14 reducer tests pass (51 total). Tests open +an independent SQLite connection inside dispatch to prove running was committed, +crash after mutation and reopen a fresh journal to prove no duplicate invocation, +lose a response then reconcile without mint, require explicit retry, check exact +pins/session expiry, withhold malicious output and unconfirmed revocation, and +prove created-only reverse undo plus uncertain-undo refusal. Ruff formatting and +checks pass. No live token or target action occurred. + +## Browser input projection — 2026-10-16 source preparation + +An explicitly configured `BrowserInputs(netkingdom_root)` now validates browser +cases before admission. It calls the real NetKingdom type/case validator over +verified producer bytes in a disposable Python process (`-I`, empty environment, +timeout, no inherited output). The validator, composer, capability contract and lazy evidence +modules have exact source pins; missing or changed code refuses. There is no +automatic discovery or download. A producer upgrade needs reviewed pin changes. + +Only the exact reviewed 1.1/1.2 browser parameter definitions qualify. A digest +of each complete parameter definition list prevents reclassifying secrets as +metadata, loosening constraints, or adding fields under a recognized version. +The complete case passes producer validation, including required fields, +constraints and credential-shape checks. Explicit fixed/default overrides, +unknown fields and secret references refuse. Security-sensitive browser metadata +can therefore be present without being passed to an owner adapter: the only +projection is its descriptor's exact public/operational target identifier. +No raw parameter values are added to invocations or persisted receipts. + +`require_implemented=False` validates the acknowledged type's source, not its +execution authority. Existing descriptor/type/source/catalog/caller provenance +gates stay mandatory. Neither historical browser type contains the required +exact machine-descriptor map, and neither is promoted by this change. There are +still no installed native owner adapters, authenticated provenance verifier, +contained session/ordered run orchestration or live CLI dispatch. A journal digest +is a fingerprint and cannot authenticate native evidence. Completion still needs +owner-admitted descriptors and actual structural, mint/delivery, consumer and +revocation/rollback evidence. Local fixture success is not native acceptance. + +Tests use the actual producer checkout (override sibling location with +`WARDEN_TEST_NETKINGDOM_ROOT`), the real Executor, SQLite journal and injected +test-only owner adapter/evidence verifier. Their descriptor extensions and case +materialized labels exist only in memory. Canonical case/type bytes stay intact. + +Producer integration refinement: the accepted NetKingdom browser-case patch +`d32a7f51cbf3012bee3e4bb329f4259dea867eff1c59a2b01a65d72774488d05` +is now the tested producer revision. Its evidence module is imported lazily only +by `check-evidence`, outside this admission path; Warden nevertheless pins and +copies it with the complete reviewed producer set. Missing or modified bytes in +any of the four modules refuse admission. The test covers every dependency. diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/pytest.ini b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/pytest.ini new file mode 100644 index 00000000..fcccae19 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +pythonpath = src diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/__init__.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/__init__.py new file mode 100644 index 00000000..4525f1e2 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/__init__.py @@ -0,0 +1,3 @@ +"""OpsWarden — SSH CA and certificate lifecycle manager.""" + +__version__ = "0.1.0" diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_inputs.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_inputs.py new file mode 100644 index 00000000..9c876d79 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_inputs.py @@ -0,0 +1,98 @@ +"""Reviewed browser metadata projection; no native trust or command dispatch. + +The producer validator and all reviewed producer modules are pinned below. +Their verified bytes run in a disposable tree, without caller environment or +ambient Python imports. Type parameters must match the exact reviewed version. +Machine descriptor extensions still require Executor's independent admission. +""" + +import hashlib +import json +from pathlib import Path +import subprocess +import sys +import tempfile + +from .case_receipt_state import Refused + +# Reviewed producer: net-kingdom 015694ee414c802dc2ef1fa0120dfed610086e69. +# Review: docs/reviews/2026-10-16-browser-producer-pin-refresh.md +SOURCE_PINS = { + "tools/security-case-types/security_case_evidence.py": "da8efa59c52ad73283397ebe639ae259d0cb9c6348b5cb829135c24230dcdc75", + "tools/security-case-types/security_case_validator.py": "1bcc62e9bc374c711363295549f3cd5bb0b32952671ed4b08fddc7f74742959b", + "tools/security-scenario-composer/security_scenario_composer.py": "9f0cc69930aa8ee2cc3fb92236e6a99728df17b8c0fe36e1250241c38fc01c4f", + "tools/playbook-capability-contract/playbook_contract_validator.py": "2e1e0c809d4bc9d64c2d974cc23fecd1c5636785154c2554edd9191852fd926d", +} +PARAMETER_PINS = { + "1.1": "9ec2d6113bf70dbd35cfc247412363ff187e792bc82e24159a6f78e090aa3e4c", + "1.2": "c04385d176f5f70257fb0fa6fef7e48ab26ed7cf26f6b1c7f14b16237b0ebea6", +} + +_VALIDATE = """ +import json, sys +from pathlib import Path +sys.path.insert(0, str(Path(sys.argv[1]) / 'tools/security-case-types')) +import security_case_validator as v +case, typ = json.load(sys.stdin) +issues = v.validate_type(typ) + v.validate_case(case, typ, require_implemented=False) +print('valid' if not v.has_errors(issues) else 'invalid') +""" + + +class BrowserInputs: + """Explicit owner configuration, never inferred from a case-supplied path.""" + + def __init__(self, netkingdom_root: Path): + self.root = Path(netkingdom_root) + + def target(self, case: dict, typ: dict, target_parameter: str) -> str: + try: + spec = typ["spec"] + parameters = spec["parameters"] + fingerprint = hashlib.sha256( + json.dumps(parameters, sort_keys=True, separators=(",", ":")).encode() + ).hexdigest() + if ( + spec["id"] != "oidc-browser-relying-party" + or PARAMETER_PINS.get(spec["version"]) != fingerprint + ): + raise ValueError + declared = {item["name"]: item for item in parameters} + definition = declared.get(target_parameter, {}) + if ( + definition.get("sensitivity") not in ("public", "operational") + or definition.get("case_settable") is not True + ): + raise ValueError + # No secret references or newly classified metadata are grandfathered. + for name in case["spec"]["parameters"]: + item = declared.get(name, {}) + if item.get("case_settable") is not True or item.get( + "sensitivity" + ) not in ("public", "operational", "security_sensitive"): + raise ValueError + with tempfile.TemporaryDirectory( + prefix="warden-case-validator-" + ) as directory: + for relative, expected in SOURCE_PINS.items(): + source = (self.root / relative).read_bytes() + if hashlib.sha256(source).hexdigest() != expected: + raise ValueError + dest = Path(directory) / relative + dest.parent.mkdir(parents=True, exist_ok=True) + dest.write_bytes(source) + result = subprocess.run( + [sys.executable, "-I", "-c", _VALIDATE, directory], + input=json.dumps([case, typ]), + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + env={}, + timeout=10, + check=True, + ) + if result.stdout != "valid\n": + raise ValueError + return case["spec"]["parameters"][target_parameter] + except (OSError, ValueError, TypeError, KeyError, subprocess.SubprocessError): + raise Refused("browser metadata failed exact-version validation") from None diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_receipt_state.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_receipt_state.py new file mode 100644 index 00000000..03a53e99 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_receipt_state.py @@ -0,0 +1,146 @@ +"""Proposed, pure case receipt reducer for WARDEN-WP-0044-T03. + +This prototype executes nothing and implements no accepted owner adapter API. +Callers MUST durably persist a returned running receipt before a side effect. +Only metadata identifiers/digests enter this model; no command output is accepted. +""" +from __future__ import annotations + +from dataclasses import dataclass, replace +from enum import Enum +import re + +_ID = re.compile(r"[a-zA-Z0-9][a-zA-Z0-9_.:-]{0,127}\Z") +_DIGEST = re.compile(r"sha256:[0-9a-f]{64}\Z") + + +class Refused(ValueError): + """A transition requires an independent owner reconciliation or new review.""" + + +class Outcome(str, Enum): + RUNNING = "running" + SUCCESS = "verified-success" + NO_CHANGE = "verified-no-change" + FAILED_BEFORE = "failed-before-change" + FAILED_AFTER = "failed-after-change" + UNCERTAIN = "uncertain" + ROLLBACK_RUNNING = "rollback-running" + ROLLED_BACK = "verified-rolled-back" + ROLLBACK_UNCERTAIN = "rollback-uncertain" + + +@dataclass(frozen=True) +class Pins: + """Exact reviewed case/type/adapter/catalog/acceptance/run-sheet digests.""" + + case: str + case_type: str + adapter: str + catalog: str + acceptance: str + run_sheet: str + + def __post_init__(self) -> None: + if not all(_DIGEST.fullmatch(value) for value in vars(self).values()): + raise Refused("pins require sha256 digests") + + +@dataclass(frozen=True) +class Receipt: + attempt: int + outcome: Outcome + created: tuple[str, ...] = () + + +@dataclass(frozen=True) +class StepState: + step_id: str + owner: str + pins: Pins + allowed_created: tuple[str, ...] = () + retry_safe: bool = False + receipts: tuple[Receipt, ...] = () + + def __post_init__(self) -> None: + if not all(_ID.fullmatch(v) for v in (self.step_id, self.owner, *self.allowed_created)): + raise Refused("only metadata identifiers are supported") + if len(set(self.allowed_created)) != len(self.allowed_created): + raise Refused("duplicate reviewed object identifier") + + @property + def last(self) -> Receipt | None: + return self.receipts[-1] if self.receipts else None + + +def _same_pins(state: StepState, pins: Pins) -> None: + if state.pins != pins: + raise Refused("pins changed; a newly reviewed sheet is required") + + +def _append(state: StepState, outcome: Outcome, created: tuple[str, ...] = ()) -> StepState: + receipt = Receipt(len(state.receipts) + 1, outcome, created) + return replace(state, receipts=(*state.receipts, receipt)) + + +def begin(state: StepState, pins: Pins, *, retry: bool = False) -> StepState: + """Return intent to dispatch. Persist before calling an owner adapter.""" + _same_pins(state, pins) + last = state.last + if last is None: + if retry: + raise Refused("no failed attempt to retry") + elif not (last.outcome is Outcome.FAILED_BEFORE and state.retry_safe and retry): + raise Refused("step cannot dispatch or replay without owner reconciliation") + return _append(state, Outcome.RUNNING) + + +def finish(state: StepState, pins: Pins, outcome: Outcome, + *, created: tuple[str, ...] = ()) -> StepState: + """Accept only an adapter's verified, closed metadata result, never output.""" + _same_pins(state, pins) + if not state.last or state.last.outcome is not Outcome.RUNNING: + raise Refused("no running side effect") + if not isinstance(outcome, Outcome) or outcome not in (Outcome.SUCCESS, Outcome.NO_CHANGE, Outcome.FAILED_BEFORE, + Outcome.FAILED_AFTER, Outcome.UNCERTAIN): + raise Refused("unsupported adapter result") + if len(set(created)) != len(created) or not set(created) <= set(state.allowed_created): + raise Refused("created objects are outside the reviewed case") + if created and outcome not in (Outcome.SUCCESS, Outcome.FAILED_AFTER): + raise Refused("this result cannot claim created objects") + return _append(state, outcome, created) + + +def recover(state: StepState) -> StepState: + """Lost result after persisted dispatch is uncertain, never safe to retry.""" + if state.last and state.last.outcome is Outcome.RUNNING: + return _append(state, Outcome.UNCERTAIN) + if state.last and state.last.outcome is Outcome.ROLLBACK_RUNNING: + return _append(state, Outcome.ROLLBACK_UNCERTAIN, state.last.created) + return state + + +def rollback_objects(state: StepState, pins: Pins) -> tuple[str, ...]: + """Reviewed identifiers only, reverse created order; no shared-object undo.""" + _same_pins(state, pins) + last = state.last + if last is None or last.outcome is Outcome.NO_CHANGE: + return () + if last.outcome is not Outcome.SUCCESS: + raise Refused("rollback needs a verified successful creation receipt") + return tuple(reversed(last.created)) + + +def begin_rollback(state: StepState, pins: Pins) -> StepState: + objects = rollback_objects(state, pins) + if not objects: + raise Refused("no case-created objects to undo") + return _append(state, Outcome.ROLLBACK_RUNNING, objects) + + +def finish_rollback(state: StepState, pins: Pins, *, verified: bool) -> StepState: + _same_pins(state, pins) + if not state.last or state.last.outcome is not Outcome.ROLLBACK_RUNNING: + raise Refused("no running rollback") + outcome = Outcome.ROLLED_BACK if verified else Outcome.ROLLBACK_UNCERTAIN + return _append(state, outcome, state.last.created) diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_run.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_run.py new file mode 100644 index 00000000..331ce11a --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/src/warden/case_run.py @@ -0,0 +1,826 @@ +"""Closed local execution kernel v1; no installed adapters or live CLI dispatch. + +An owner must acknowledge each descriptor and provide an independent provenance +verifier. Descriptive case types remain plan-only. Nothing executes shell prose. +""" + +from __future__ import annotations + +from contextlib import contextmanager, redirect_stderr, redirect_stdout +from dataclasses import asdict, dataclass, replace +import fcntl +import hashlib +import json +import os +from pathlib import Path +import re +import sqlite3 +import threading +import uuid +from typing import Callable + +import yaml +from .case_inputs import BrowserInputs +from .case_receipt_state import ( + Outcome, + Pins, + Receipt, + Refused, + StepState, + begin, + finish, + recover, + begin_rollback, + finish_rollback, +) + +IDENTIFIER = re.compile(r"[a-zA-Z0-9][a-zA-Z0-9_.:/@-]{0,191}\Z") +MAX_SOURCE_BYTES = 262144 +MAX_HISTORY = 128 +OUTPUT_LOCK = threading.RLock() + + +def bounded_source(value: bytes): + if type(value) is not bytes or len(value) > MAX_SOURCE_BYTES: + raise Refused("source exceeds bounded input contract") + + +@contextmanager +def contained_output(): + """Discard Python and inherited OS stdout/stderr; serialize process fd changes.""" + with OUTPUT_LOCK: + saved = [] + null = os.open(os.devnull, os.O_WRONLY) + try: + for target in (1, 2): + saved.append((target, os.dup(target))) + os.dup2(null, target) + with redirect_stdout(_Discard()), redirect_stderr(_Discard()): + try: + yield + except Exception: + raise Refused("owner callback failed") from None + except BaseException: + raise OwnerInterrupted() from None + finally: + for target, original in saved: + os.dup2(original, target) + os.close(original) + os.close(null) + + +DIGEST = re.compile(r"sha256:[0-9a-f]{64}\Z") + + +def digest(value: bytes) -> str: + return "sha256:" + hashlib.sha256(value).hexdigest() + + +@dataclass(frozen=True, slots=True) +class Descriptor: + version: str + step: str + owner: str + adapter: str + adapter_source: str + actor: str + lane: str + target_parameter: str + effect: str + allowed_created: tuple[str, ...] + retry_safe: bool + revocation_scopes: tuple[str, ...] + + @classmethod + def decode(cls, source: bytes) -> Descriptor: + bounded_source(source) + try: + obj = json.loads(source) + if set(obj) != set(cls.__dataclass_fields__): + raise ValueError + if not isinstance(obj["allowed_created"], list): + raise ValueError + obj["allowed_created"] = tuple(obj["allowed_created"]) + if not isinstance(obj["revocation_scopes"], list): + raise ValueError + obj["revocation_scopes"] = tuple(obj["revocation_scopes"]) + result = cls(**obj) + identifiers = ( + result.step, + result.owner, + result.adapter, + result.lane, + result.target_parameter, + *result.allowed_created, + ) + if result.version != "warden.case.execution.v1" or not all( + isinstance(v, str) and IDENTIFIER.fullmatch(v) for v in identifiers + ): + raise ValueError + if not DIGEST.fullmatch(result.adapter_source) or result.actor not in ( + "human", + "agent", + ): + raise ValueError + if ( + result.effect not in ("read", "change", "mint") + or type(result.retry_safe) is not bool + ): + raise ValueError + scopes = set(result.revocation_scopes) + if ( + len(scopes) != len(result.revocation_scopes) + or not scopes <= {"parent-session", "broker-grant"} + or "parent-session" not in scopes + ): + raise ValueError + if result.effect == "mint" and "broker-grant" not in scopes: + raise ValueError + if len(result.allowed_created) > 32: + raise ValueError + if len(set(result.allowed_created)) != len(result.allowed_created): + raise ValueError + return result + except (ValueError, TypeError, KeyError): + raise Refused("invalid closed execution descriptor") from None + + +@dataclass(frozen=True, slots=True) +class OwnerResult: + outcome: Outcome + evidence: str + created: tuple[str, ...] = () + revoked: bool = False + session_revocation_evidence: str | None = None + grant_revocation_evidence: str | None = None + attempt_id: str = "" + operation: str = "" + + +@dataclass(frozen=True, slots=True) +class Invocation: + step: str + owner: str + target: str + actor: str + principal: str + lane: str + pins: Pins + attempt_id: str = "" + operation: str = "admission" + tenant: str = "" + realm: str = "" + + +INTENT_SCHEMA = "warden.case.original-intent.v1" + + +@dataclass(frozen=True, slots=True) +class OriginalIntent: + """Durable record of the original mutating dispatch, written before dispatch. + + Binds the original Invocation (attempt, step, owner, target, actor, principal, + lane), all pins, the admitted adapter source and the tenant/realm provenance + the caller was admitted with. It is evidence of what Warden dispatched, not + owner proof of creation: owners still authenticate created-only provenance. + """ + + binding: str + invocation: Invocation + source: str + + def payload(self) -> str: + return json.dumps( + {"schema": INTENT_SCHEMA, "binding": self.binding, "source": self.source} + | asdict(self.invocation), + separators=(",", ":"), + sort_keys=True, + ) + + @classmethod + def decode(cls, raw: str) -> OriginalIntent: + obj = json.loads(raw) + fields = {f for f in Invocation.__dataclass_fields__} + if set(obj) != fields | {"schema", "binding", "source"} or obj["schema"] != INTENT_SCHEMA: + raise Refused("unknown original intent schema") + inv = {k: obj[k] for k in fields} + inv["pins"] = Pins(**inv["pins"]) + return cls(obj["binding"], Invocation(**inv), obj["source"]) + + +@dataclass(frozen=True, slots=True) +class ReconcileContext: + """Original intent and the new reconciliation attempt, kept separate.""" + + original: OriginalIntent + attempt: Invocation + + +@dataclass(frozen=True, slots=True) +class Adapter: + reference: str + source: str + invoke: Callable[[Invocation], OwnerResult] + verify: Callable[[Invocation, OwnerResult], bool] + rollback: Callable[[Invocation, tuple[str, ...]], OwnerResult] | None = None + reconcile: Callable[[Invocation], OwnerResult] | None = None + reconcile_original: Callable[[ReconcileContext], OwnerResult] | None = None + + +class Journal: + """Private SQLite journal plus process lock; transaction commits before dispatch.""" + + def __init__(self, directory: Path): + directory.mkdir(mode=0o700, parents=True, exist_ok=True) + if ( + directory.is_symlink() + or directory.stat().st_uid != os.getuid() + or directory.stat().st_mode & 0o077 + ): + raise Refused("journal directory is not private") + self.directory = directory + for name in ("journal.sqlite", "execution.lock"): + path = directory / name + fd = os.open(path, os.O_CREAT | os.O_RDWR | os.O_NOFOLLOW, 0o600) + try: + if os.fstat(fd).st_uid != os.getuid() or os.fstat(fd).st_mode & 0o077: + raise Refused("journal file is not private") + finally: + os.close(fd) + self.db = sqlite3.connect(directory / "journal.sqlite", isolation_level=None) + self.db.execute("PRAGMA synchronous=FULL") + self.db.execute( + "CREATE TABLE IF NOT EXISTS receipts (seq INTEGER PRIMARY KEY, binding TEXT NOT NULL, payload TEXT NOT NULL)" + ) + self.db.execute( + "CREATE TABLE IF NOT EXISTS intents (seq INTEGER PRIMARY KEY, attempt_id TEXT NOT NULL UNIQUE, binding TEXT NOT NULL, payload TEXT NOT NULL)" + ) + + def original_intent(self, binding: str, current: Invocation, source: str) -> OriginalIntent: + """Latest original run intent for binding, refused unless it matches exactly. + + Missing or mismatched records refuse; the step stays uncertain. + """ + row = self.db.execute( + "SELECT payload FROM intents WHERE binding=? ORDER BY seq DESC LIMIT 1", + (binding,), + ).fetchone() + if row is None: + raise Refused("original intent record missing; step stays uncertain") + intent = OriginalIntent.decode(row[0]) + o = intent.invocation + if intent.binding != binding or o.operation != "run": + raise Refused("original intent binding mismatch") + if o.pins != current.pins: + raise Refused("original intent pins mismatch") + if intent.source != source: + raise Refused("original intent source mismatch") + for name in ("step", "owner", "target", "principal", "lane", "tenant", "realm"): + if getattr(o, name) != getattr(current, name): + raise Refused(f"original intent {name} mismatch") + return intent + + @contextmanager + def locked(self): + with (self.directory / "execution.lock").open("rb") as fh: + try: + fcntl.flock(fh, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + raise Refused("case execution already active") from None + try: + yield + finally: + fcntl.flock(fh, fcntl.LOCK_UN) + + def load(self, binding: str, initial: StepState) -> StepState: + row = self.db.execute( + "SELECT payload FROM receipts WHERE binding=? ORDER BY seq DESC LIMIT 1", + (binding,), + ).fetchone() + if row is None: + return initial + obj = json.loads(row[0]) + expected = { + "step_id", + "owner", + "pins", + "allowed_created", + "retry_safe", + "receipts", + "journal_schema", + "evidence_digest", + "session_revoked", + "session_revocation_digest", + "grant_revocation_digest", + "attempt_id", + "operation", + } + if ( + set(obj) != expected + or obj["journal_schema"] != "warden.case.step-journal.v1" + ): + raise Refused("unknown journal receipt schema") + state = StepState( + obj["step_id"], + obj["owner"], + Pins(**obj["pins"]), + tuple(obj["allowed_created"]), + obj["retry_safe"], + tuple( + Receipt(r["attempt"], Outcome(r["outcome"]), tuple(r["created"])) + for r in obj["receipts"] + ), + ) + if ( + state.step_id, + state.owner, + state.pins, + state.allowed_created, + state.retry_safe, + ) != ( + initial.step_id, + initial.owner, + initial.pins, + initial.allowed_created, + initial.retry_safe, + ): + raise Refused("journal pins changed") + return state + + def save( + self, + binding: str, + state: StepState, + evidence: OwnerResult | None = None, + invocation: Invocation | None = None, + intent: OriginalIntent | None = None, + ): + self.db.execute("BEGIN IMMEDIATE") + try: + if intent is not None: + self.db.execute( + "INSERT INTO intents(attempt_id,binding,payload) VALUES (?,?,?)", + (intent.invocation.attempt_id, binding, intent.payload()), + ) + self.db.execute( + "INSERT INTO receipts(binding,payload) VALUES (?,?)", + ( + binding, + json.dumps( + asdict(state) + | { + "journal_schema": "warden.case.step-journal.v1", + "attempt_id": invocation.attempt_id if invocation else None, + "operation": invocation.operation if invocation else None, + "evidence_digest": digest(evidence.evidence.encode()) + if evidence + else None, + "session_revoked": evidence.revoked if evidence else None, + "session_revocation_digest": digest( + evidence.session_revocation_evidence.encode() + ) + if evidence and evidence.session_revocation_evidence + else None, + "grant_revocation_digest": digest( + evidence.grant_revocation_evidence.encode() + ) + if evidence and evidence.grant_revocation_evidence + else None, + }, + separators=(",", ":"), + ), + ), + ) + self.db.execute("COMMIT") + except BaseException: + self.db.execute("ROLLBACK") + raise + + +class Executor: + """Injected fixed owner adapters and provenance verifier; none ship by default. + + evidence verifier must authenticate owner custody/provenance or explicitly + attest attended manual evidence; a bare JSON reference is never sufficient. + It receives purpose, exact source digest, and named evidence reference. + """ + + def __init__( + self, + journal: Journal, + adapters: dict[str, Adapter], + evidence_verifier: Callable[[str, str, str], bool], + *, + browser_inputs: BrowserInputs | None = None, + ): + self.journal, self.adapters, self.evidence_verifier = ( + journal, + dict(adapters), + evidence_verifier, + ) + self._admitted = {} + self.browser_inputs = browser_inputs + + def admit( + self, + case_source: bytes, + type_source: bytes, + descriptor_source: bytes, + catalog_source: bytes, + *, + principal: str, + actor: str, + lane: str, + acceptance_evidence: str, + materialization_evidence: str, + descriptor_evidence: str, + session_evidence: str, + ) -> tuple[Descriptor, Invocation, StepState]: + for source in (case_source, type_source, descriptor_source, catalog_source): + bounded_source(source) + descriptor = Descriptor.decode(descriptor_source) + try: + case = yaml.safe_load(case_source)["spec"] + case_type = yaml.safe_load(type_source)["spec"] + if not isinstance(case, dict) or not isinstance(case_type, dict): + raise ValueError + except (yaml.YAMLError, KeyError, TypeError, ValueError): + raise Refused("invalid case or type source shape") from None + if case.get("status") != "materialized" or not case.get("acceptance"): + raise Refused("case is not accepted and structurally materialized") + if case_type.get("status") not in ("acknowledged", "implemented"): + raise Refused("type is not acknowledged") + if case.get("type") != f"{case_type['id']}@{case_type['version']}": + raise Refused("case type pin mismatch") + if case_type.get("execution_descriptors", {}).get(descriptor.step) != digest( + descriptor_source + ): + raise Refused( + "type has no exact acknowledged machine descriptor; plan only" + ) + for purpose, source, evidence in ( + ("case-acceptance", digest(case_source), acceptance_evidence), + ( + "ops-mason-materialization", + digest(case_source), + materialization_evidence, + ), + ( + "owner-descriptor-acceptance:" + descriptor.owner, + digest(descriptor_source), + descriptor_evidence, + ), + ("type-machine-seam-acceptance", digest(type_source), descriptor_evidence), + ("catalog-routing-acceptance", digest(catalog_source), descriptor_evidence), + ( + f"caller-session:{actor}:{principal}:{lane}", + digest(case_source), + session_evidence, + ), + ): + if ( + not isinstance(evidence, str) + or not IDENTIFIER.fullmatch(evidence) + or self._verify_evidence(purpose, source, evidence) is not True + ): + raise Refused("independent owner evidence not verified") + if ( + not IDENTIFIER.fullmatch(principal) + or actor != descriptor.actor + or lane != descriptor.lane + ): + raise Refused("principal actor or lane mismatch") + if descriptor.effect == "mint" and ( + actor != "human" or os.getenv("WARDEN_AGENT_ID") + ): + raise Refused("agent mint is forbidden") + parameters = case.get("parameters", {}) + declared = { + item["name"]: item + for item in case_type.get("parameters", []) + if isinstance(item, dict) and isinstance(item.get("name"), str) + } + if ( + not isinstance(parameters, dict) + or descriptor.target_parameter not in declared + ): + raise Refused("target parameter is not declared by acknowledged type") + if case_type.get("id") == "oidc-browser-relying-party": + if type(self.browser_inputs) is not BrowserInputs: + raise Refused("browser metadata validator is not configured") + target = self.browser_inputs.target( + yaml.safe_load(case_source), + yaml.safe_load(type_source), + descriptor.target_parameter, + ) + else: + for name in parameters: + definition = declared.get(name) + if ( + definition is None + or definition.get("sensitivity") not in ("public", "operational") + or definition.get("case_settable") is False + ): + raise Refused("unknown, fixed or secret-sensitivity case parameter") + target = parameters.get(descriptor.target_parameter) + if not isinstance(target, str) or not IDENTIFIER.fullmatch(target): + raise Refused("target must be exact case metadata") + adapter = self.adapters.get(descriptor.adapter) + if adapter is None or (adapter.reference, adapter.source) != ( + descriptor.adapter, + descriptor.adapter_source, + ): + raise Refused("unknown adapter or owner source pin") + sheet = json.dumps( + [ + descriptor.step, + descriptor.owner, + target, + actor, + principal, + lane, + acceptance_evidence, + materialization_evidence, + descriptor_evidence, + session_evidence, + ], + separators=(",", ":"), + ).encode() + pins = Pins( + digest(case_source), + digest(type_source), + digest(descriptor_source), + digest(catalog_source), + digest(acceptance_evidence.encode()), + digest(sheet), + ) + invocation = Invocation( + descriptor.step, descriptor.owner, target, actor, principal, lane, pins + ) + initial = StepState( + descriptor.step, + descriptor.owner, + pins, + descriptor.allowed_created, + descriptor.retry_safe, + ) + self._admitted[invocation] = (descriptor, initial, session_evidence) + return descriptor, invocation, initial + + def _result( + self, adapter: Adapter, invocation: Invocation, result: OwnerResult + ) -> OwnerResult: + if type(result) is not OwnerResult or not isinstance(result.outcome, Outcome): + raise Refused("unknown owner result shape") + if type(result.created) is not tuple or not all( + isinstance(v, str) and IDENTIFIER.fullmatch(v) for v in result.created + ): + raise Refused("owner created-object result invalid") + if not isinstance(result.evidence, str) or not IDENTIFIER.fullmatch( + result.evidence + ): + raise Refused("owner evidence identifier invalid") + if adapter.verify(invocation, result) is not True or result.revoked is not True: + raise Refused("owner result or session revocation unverified") + if (result.attempt_id, result.operation) != ( + invocation.attempt_id, + invocation.operation, + ): + raise Refused("owner receipt belongs to another attempt") + descriptor = self._admitted[ + replace(invocation, attempt_id="", operation="admission") + ][0] + proof_fields = { + "parent-session": ( + f"session-revocation:{invocation.lane}:{invocation.operation}:{invocation.attempt_id}", + result.session_revocation_evidence, + ), + "broker-grant": ( + f"grant-revocation:{invocation.lane}:{invocation.operation}:{invocation.attempt_id}", + result.grant_revocation_evidence, + ), + } + proofs = [proof_fields[scope] for scope in descriptor.revocation_scopes] + for purpose, evidence in proofs: + if ( + not isinstance(evidence, str) + or not IDENTIFIER.fullmatch(evidence) + or self._verify_evidence(purpose, invocation.pins.case, evidence) + is not True + ): + raise Refused("independent grant or parent-session revocation absent") + return result + + def _verify_evidence(self, purpose, source, evidence): + try: + with contained_output(): + return self.evidence_verifier(purpose, source, evidence) is True + except Exception: + raise Refused("independent evidence verification failed") from None + + def _admission( + self, descriptor: Descriptor, invocation: Invocation, initial: StepState + ): + if self._admitted.get(invocation, ())[:2] != (descriptor, initial): + raise Refused("execution requires verified admission on this executor") + adapter = self.adapters.get(descriptor.adapter) + if adapter is None or (adapter.reference, adapter.source) != ( + descriptor.adapter, + descriptor.adapter_source, + ): + raise Refused("adapter changed after admission") + session = self._admitted[invocation][2] + if ( + self._verify_evidence( + f"caller-session:{invocation.actor}:{invocation.principal}:{invocation.lane}", + invocation.pins.case, + session, + ) + is not True + ): + raise Refused("caller session no longer verified") + if descriptor.effect == "mint" and os.getenv("WARDEN_AGENT_ID"): + raise Refused("agent mint is forbidden") + return adapter + + def _attempt(self, invocation: Invocation, state: StepState, operation: str): + identity = json.dumps( + [asdict(invocation.pins), operation, state.last.attempt, uuid.uuid4().hex], + separators=(",", ":"), + ).encode() + return replace( + invocation, + attempt_id="attempt-" + hashlib.sha256(identity).hexdigest(), + operation=operation, + ) + + def run( + self, + descriptor: Descriptor, + invocation: Invocation, + initial: StepState, + *, + retry=False, + ) -> StepState: + adapter = self._admission(descriptor, invocation, initial) + with self.journal.locked(): + binding = f"{invocation.step}:{invocation.target}" + state = self.journal.load(binding, initial) + recovered = recover(state) + if recovered != state: + self.journal.save(binding, recovered) + state = recovered + if len(state.receipts) > MAX_HISTORY - 4: + raise Refused("bounded journal history requires owner archival") + running = begin(state, invocation.pins, retry=retry) + dispatched = self._attempt(invocation, running, "run") + self.journal.save( + binding, + running, + invocation=dispatched, + intent=OriginalIntent(binding, dispatched, adapter.source), + ) + evidence = None + try: + with contained_output(): + result = self._result( + adapter, dispatched, adapter.invoke(dispatched) + ) + completed = finish( + running, invocation.pins, result.outcome, created=result.created + ) + evidence = result + except Exception: + completed = finish(running, invocation.pins, Outcome.UNCERTAIN) + self.journal.save(binding, completed, evidence, dispatched) + return completed + + def rollback( + self, descriptor: Descriptor, invocation: Invocation, initial: StepState + ) -> StepState: + adapter = self._admission(descriptor, invocation, initial) + with self.journal.locked(): + binding = f"{invocation.step}:{invocation.target}" + state = self.journal.load(binding, initial) + if recover(state) != state: + self.journal.save(binding, recover(state)) + raise Refused("lost rollback result requires owner reconciliation") + if adapter.rollback is None: + raise Refused("owner has no reviewed undo adapter") + if len(state.receipts) > MAX_HISTORY - 4: + raise Refused("bounded journal history requires owner archival") + running = begin_rollback(state, invocation.pins) + dispatched = self._attempt(invocation, running, "rollback") + self.journal.save( + binding, + running, + invocation=dispatched, + intent=OriginalIntent(binding, dispatched, adapter.source), + ) + evidence = None + try: + with contained_output(): + result = self._result( + adapter, + dispatched, + adapter.rollback(dispatched, running.last.created), + ) + verified = result.outcome is Outcome.ROLLED_BACK and not result.created + evidence = result if verified else None + except Exception: + verified = False + completed = finish_rollback(running, invocation.pins, verified=verified) + self.journal.save(binding, completed, evidence, dispatched) + return completed + + def reconcile( + self, descriptor: Descriptor, invocation: Invocation, initial: StepState + ) -> StepState: + """Explicit owner-native state inspection; never redispatches the mutation.""" + adapter = self._admission(descriptor, invocation, initial) + if adapter.reconcile is None and adapter.reconcile_original is None: + raise Refused("owner reconciliation adapter absent") + with self.journal.locked(): + binding = f"{invocation.step}:{invocation.target}" + state = recover(self.journal.load(binding, initial)) + if not state.last or state.last.outcome not in ( + Outcome.UNCERTAIN, + Outcome.FAILED_AFTER, + ): + raise Refused("no uncertain mutation to reconcile") + if len(state.receipts) > MAX_HISTORY - 4: + raise Refused("bounded journal history requires owner archival") + state = replace( + state, + receipts=( + *state.receipts, + Receipt(len(state.receipts) + 1, Outcome.UNCERTAIN), + ), + ) + original = ( + self.journal.original_intent(binding, invocation, adapter.source) + if adapter.reconcile_original is not None + else None + ) + dispatched = self._attempt(invocation, state, "reconcile") + self.journal.save(binding, state, invocation=dispatched) + with contained_output(): + result = self._result( + adapter, + dispatched, + adapter.reconcile_original(ReconcileContext(original, dispatched)) + if original is not None + else adapter.reconcile(dispatched), + ) + if result.outcome not in ( + Outcome.NO_CHANGE, + Outcome.SUCCESS, + Outcome.FAILED_BEFORE, + ): + raise Refused("owner did not reconcile target state") + # Validate created-object scope using the same closed result reducer. + finish( + begin( + StepState( + state.step_id, + state.owner, + state.pins, + state.allowed_created, + state.retry_safe, + ), + state.pins, + ), + state.pins, + result.outcome, + created=result.created, + ) + reconciled = StepState( + state.step_id, + state.owner, + state.pins, + state.allowed_created, + state.retry_safe, + ( + *state.receipts, + Receipt(len(state.receipts) + 1, result.outcome, result.created), + ), + ) + self.journal.save(binding, reconciled, result, dispatched) + return reconciled + + +class OwnerInterrupted(KeyboardInterrupt): + """Sanitized interruption preserves persisted running/crash recovery semantics.""" + + def __init__(self): + super().__init__("owner execution interrupted") + + +class _Discard: + """Never retain or forward adapter output, including unbounded malicious text.""" + + def write(self, text): + return len(text) + + def flush(self): + pass diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/tests/test_case_run.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/tests/test_case_run.py new file mode 100644 index 00000000..0032e5eb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/tests/test_case_run.py @@ -0,0 +1,700 @@ +"""Injected owner harness; these receipts are not native owner attestations.""" + +from dataclasses import asdict, replace +import json +import os +import subprocess +import sys +import sqlite3 +import pytest +import yaml +from warden.case_receipt_state import Outcome, Refused +from warden.case_run import Adapter, Descriptor, Executor, Journal, OwnerResult, digest + + +@pytest.fixture +def rig(tmp_path): + desc = Descriptor( + "warden.case.execution.v1", + "mint-secret", + "owner", + "owner/mint@v1", + "sha256:" + "1" * 64, + "human", + "issuer-lane", + "client_id", + "mint", + ("case-policy", "case-role"), + True, + ("parent-session", "broker-grant"), + ) + descriptor = json.dumps(asdict(desc)).encode() + typ = yaml.safe_dump( + { + "spec": { + "id": "test", + "version": "1", + "status": "acknowledged", + "parameters": [ + { + "name": "client_id", + "sensitivity": "operational", + "case_settable": True, + } + ], + "execution_descriptors": {"mint-secret": digest(descriptor)}, + } + } + ).encode() + case = yaml.safe_dump( + { + "spec": { + "id": "case-test", + "type": "test@1", + "status": "materialized", + "acceptance": "acceptance", + "parameters": {"client_id": "fixture-client"}, + } + } + ).encode() + calls = [] + + def invoke(inv): + calls.append(inv) + return bound( + inv, + OwnerResult( + Outcome.SUCCESS, + "owner-receipt", + ("case-policy", "case-role"), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ) + + adapter = Adapter( + "owner/mint@v1", + desc.adapter_source, + invoke, + lambda inv, result: result.evidence == "owner-receipt", + lambda inv, objects: bound( + inv, + OwnerResult( + Outcome.ROLLED_BACK, + "owner-receipt", + (), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ), + lambda inv: bound( + inv, + OwnerResult( + Outcome.NO_CHANGE, + "owner-receipt", + (), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ), + ) + verified = { + ("case-acceptance", digest(case), "acceptance"), + ("ops-mason-materialization", digest(case), "materialization"), + ("owner-descriptor-acceptance:owner", digest(descriptor), "descriptor"), + ("type-machine-seam-acceptance", digest(typ), "descriptor"), + ("catalog-routing-acceptance", digest(b"catalog"), "descriptor"), + ("caller-session:human:operator:issuer-lane", digest(case), "session"), + ("session-revocation:issuer-lane", digest(case), "session-revoked"), + ("grant-revocation:issuer-lane", digest(case), "grant-revoked"), + } + + def verifier(purpose, source, evidence): + if purpose.startswith(("session-revocation:", "grant-revocation:")): + scope, lane, operation, attempt = purpose.split(":") + return ( + source == digest(case) + and lane == "issuer-lane" + and operation in ("run", "rollback", "reconcile") + and evidence + == ("session-" if scope == "session-revocation" else "grant-") + attempt + ) + return (purpose, source, evidence) in verified + + executor = Executor( + Journal(tmp_path / "private"), {"owner/mint@v1": adapter}, verifier + ) + kwargs = dict( + principal="operator", + actor="human", + lane="issuer-lane", + acceptance_evidence="acceptance", + materialization_evidence="materialization", + descriptor_evidence="descriptor", + session_evidence="session", + ) + admitted = executor.admit(case, typ, descriptor, b"catalog", **kwargs) + return (executor, admitted, calls, (case, typ, descriptor, b"catalog"), kwargs) + + +def test_commit_running_is_visible_from_separate_connection_before_dispatch(rig): + executor, args, calls, _, _ = rig + old = executor.adapters[args[0].adapter] + + def inspect(inv): + other = sqlite3.connect(executor.journal.directory / "journal.sqlite") + payload = json.loads( + other.execute( + "SELECT payload FROM receipts ORDER BY seq DESC LIMIT 1" + ).fetchone()[0] + ) + assert payload["receipts"][-1]["outcome"] == "running" + return old.invoke(inv) + + executor.adapters[old.reference] = replace(old, invoke=inspect) + assert executor.run(*args).last.outcome is Outcome.SUCCESS + with pytest.raises(Refused): + executor.run(*args) + assert len(calls) == 1 + + +def test_crash_and_fresh_process_resume_never_duplicates_effect(rig): + executor, args, calls, sources, kwargs = rig + old = executor.adapters[args[0].adapter] + + def crash(inv): + calls.append(inv) + raise KeyboardInterrupt + + executor.adapters[old.reference] = replace(old, invoke=crash) + with pytest.raises(KeyboardInterrupt): + executor.run(*args) + fresh = Executor( + Journal(executor.journal.directory), + {old.reference: old}, + executor.evidence_verifier, + ) + admitted = fresh.admit(*sources, **kwargs) + with pytest.raises(Refused): + fresh.run(*admitted, retry=True) + state = fresh.journal.load("mint-secret:fixture-client", admitted[2]) + assert state.last.outcome is Outcome.UNCERTAIN + assert len(calls) == 1 + + +def test_lost_result_preserved_and_cas_reconciliation_does_not_mint(rig): + executor, args, calls, _, _ = rig + old = executor.adapters[args[0].adapter] + + def lost(inv): + calls.append(inv) + raise TimeoutError("SECRET-HIDDEN") + + executor.adapters[old.reference] = replace(old, invoke=lost) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + assert executor.reconcile(*args).last.outcome is Outcome.NO_CHANGE + with pytest.raises(Refused): + executor.run(*args, retry=True) + assert len(calls) == 1 + assert ( + b"SECRET-HIDDEN" + not in (executor.journal.directory / "journal.sqlite").read_bytes() + ) + + +def test_failed_before_change_requires_explicit_retry_and_safe_owner(rig): + executor, args, calls, _, _ = rig + old = executor.adapters[args[0].adapter] + + def no_change(inv): + calls.append(inv) + return bound( + inv, + OwnerResult( + Outcome.FAILED_BEFORE, + "owner-receipt", + (), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ) + + executor.adapters[old.reference] = replace(old, invoke=no_change) + assert executor.run(*args).last.outcome is Outcome.FAILED_BEFORE + with pytest.raises(Refused): + executor.run(*args) + assert executor.run(*args, retry=True).last.outcome is Outcome.FAILED_BEFORE + assert len(calls) == 2 + + +@pytest.mark.parametrize( + "result", + [ + OwnerResult( + Outcome.SUCCESS, + "owner-receipt", + ("shared-policy",), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + OwnerResult( + Outcome.SUCCESS, + "owner-receipt", + (), + False, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + OwnerResult( + Outcome.SUCCESS, + "unknown-receipt", + (), + True, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + {"outcome": "verified-success", "stdout": "SECRET-HIDDEN"}, + ], +) +def test_unknown_secret_payload_created_scope_or_missing_revocation_never_succeeds( + rig, result, capsys +): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + + def malicious(inv): + print("SECRET-HIDDEN" * 1000) + return result + + executor.adapters[old.reference] = replace(old, invoke=malicious) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + assert capsys.readouterr().out == "" + assert ( + b"SECRET-HIDDEN" + not in (executor.journal.directory / "journal.sqlite").read_bytes() + ) + + +def test_rollback_reverses_only_verified_case_created_objects(rig): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + undos = [] + + def undo(inv, objects): + undos.append(objects) + return old.rollback(inv, objects) + + executor.adapters[old.reference] = replace(old, rollback=undo) + executor.run(*args) + assert executor.rollback(*args).last.outcome is Outcome.ROLLED_BACK + assert undos == [("case-role", "case-policy")] + assert "shared-policy" not in undos[0] + + +def test_uncertain_rollback_cannot_replay(rig): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + executor.run(*args) + executor.adapters[old.reference] = replace( + old, + rollback=lambda inv, objs: bound( + inv, + OwnerResult( + Outcome.ROLLED_BACK, + "owner-receipt", + (), + False, + session_revocation_evidence="session-revoked", + grant_revocation_evidence="grant-revoked", + ), + ), + ) + assert executor.rollback(*args).last.outcome is Outcome.ROLLBACK_UNCERTAIN + with pytest.raises(Refused): + executor.rollback(*args) + + +@pytest.mark.parametrize( + "change", ["materialization", "type-prose", "actor", "lane", "source", "target"] +) +def test_exact_admission_requires_owner_evidence_and_closed_source_pins(rig, change): + executor, _, _, sources, kwargs = rig + case, typ, descriptor, catalog = sources + kwargs = dict(kwargs) + if change == "materialization": + kwargs["materialization_evidence"] = "bare-json" + if change == "type-prose": + obj = yaml.safe_load(typ) + obj["spec"].pop("execution_descriptors") + typ = yaml.safe_dump(obj).encode() + if change == "actor": + kwargs["actor"] = "agent" + if change == "lane": + kwargs["lane"] = "other-lane" + if change == "source": + obj = json.loads(descriptor) + obj["adapter_source"] = "sha256:" + "2" * 64 + descriptor = json.dumps(obj).encode() + if change == "target": + obj = yaml.safe_load(case) + obj["spec"]["parameters"]["client_id"] = "other-target" + case = yaml.safe_dump(obj).encode() + with pytest.raises(Refused): + executor.admit(case, typ, descriptor, catalog, **kwargs) + + +def test_agent_env_cannot_dispatch_after_human_admission(rig, monkeypatch): + executor, args, calls, _, _ = rig + monkeypatch.setenv("WARDEN_AGENT_ID", "test-agent") + with pytest.raises(Refused): + executor.run(*args) + assert calls == [] + + +def test_forged_invocation_cannot_bypass_admission(rig): + executor, args, calls, _, _ = rig + with pytest.raises(Refused): + executor.run(args[0], replace(args[1], target="other-target"), args[2]) + assert calls == [] + + +def test_journal_rejects_unsafe_directory_and_symlink(tmp_path): + unsafe = tmp_path / "unsafe" + unsafe.mkdir(mode=493) + with pytest.raises(Refused): + Journal(unsafe) + private = tmp_path / "private" + private.mkdir(mode=448) + (private / "journal.sqlite").symlink_to(tmp_path / "outside") + with pytest.raises(OSError): + Journal(private) + + +def test_process_lock_blocks_parallel_dispatch(rig): + executor, args, calls, _, _ = rig + with executor.journal.locked(): + with pytest.raises(Refused): + executor.run(*args) + assert calls == [] + + +def test_changed_catalog_pins_cannot_inherit_old_success(rig): + executor, args, calls, sources, kwargs = rig + executor.run(*args) + with pytest.raises(Refused): + executor.admit(*sources[:3], b"changed-catalog", **kwargs) + assert len(calls) == 1 + + +def test_private_journal_does_not_retain_owner_payload_or_accept_unknown_schema(rig): + executor, args, _, _, _ = rig + executor.run(*args) + row = executor.journal.db.execute( + "SELECT seq,payload FROM receipts ORDER BY seq DESC LIMIT 1" + ).fetchone() + payload = json.loads(row[1]) + assert payload["journal_schema"] == "warden.case.step-journal.v1" + assert payload["evidence_digest"] == digest(b"owner-receipt") + assert payload["session_revoked"] is True + assert "owner-receipt" not in row[1] + payload["stdout"] = "UNTRUSTED-OUTPUT" + executor.journal.db.execute( + "UPDATE receipts SET payload=? WHERE seq=?", (json.dumps(payload), row[0]) + ) + with pytest.raises(Refused): + executor.run(*args) + + +def test_expired_caller_session_cannot_use_cached_admission(rig): + executor, args, calls, _, _ = rig + old = executor.evidence_verifier + executor.evidence_verifier = lambda purpose, source, evidence: ( + False + if purpose.startswith("caller-session:") + else old(purpose, source, evidence) + ) + with pytest.raises(Refused): + executor.run(*args) + assert calls == [] + + +@pytest.mark.parametrize("index", [0, 1, 2, 3]) +def test_source_sizes_are_bounded_before_parsing(rig, index): + executor, _, _, sources, kwargs = rig + oversized = list(sources) + oversized[index] = b"x" * 262145 + with pytest.raises(Refused, match="bounded"): + executor.admit(*oversized, **kwargs) + + +def test_owner_fd_and_inherited_subprocess_output_is_contained(rig, capfd): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + + def leaking(inv): + os.write(1, b"SYNTHETIC-SECRET") + os.write(2, b"SYNTHETIC-SECRET") + subprocess.run([sys.executable, "-c", 'print("SYNTHETIC-SECRET")'], check=True) + return old.invoke(inv) + + executor.adapters[old.reference] = replace(old, invoke=leaking) + assert executor.run(*args).last.outcome is Outcome.SUCCESS + observed = capfd.readouterr() + assert "SYNTHETIC-SECRET" not in observed.out + observed.err + + +def test_evidence_provider_output_and_error_never_escape(rig, capfd): + executor, _, calls, sources, kwargs = rig + + def broken(*args): + print("SYNTHETIC-SECRET") + os.write(2, b"SYNTHETIC-SECRET") + raise RuntimeError("SYNTHETIC-SECRET") + + executor.evidence_verifier = broken + with pytest.raises(Refused) as caught: + executor.admit(*sources, **kwargs) + assert "SYNTHETIC-SECRET" not in str(caught.value) + observed = capfd.readouterr() + assert "SYNTHETIC-SECRET" not in observed.out + observed.err + assert calls == [] + + +@pytest.mark.parametrize("missing", ["session", "grant"]) +def test_broker_grant_and_parent_session_have_separate_required_evidence(rig, missing): + executor, args, _, _, _ = rig + old = executor.adapters[args[0].adapter] + result = old.invoke(args[1]) + if missing == "session": + result = replace(result, session_revocation_evidence=None) + else: + result = replace(result, grant_revocation_evidence=None) + executor.adapters[old.reference] = replace(old, invoke=lambda inv: result) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + + +def test_mint_descriptor_cannot_omit_broker_grant_scope(rig): + descriptor = json.loads(rig[3][2]) + descriptor["revocation_scopes"] = ["parent-session"] + with pytest.raises(Refused): + Descriptor.decode(json.dumps(descriptor).encode()) + + +def test_unsafe_owner_step_cannot_retry_even_after_verified_failed_before( + rig, tmp_path +): + executor, args, _, sources, kwargs = rig + descriptor = replace(args[0], retry_safe=False) + descriptor_source = json.dumps(asdict(descriptor)).encode() + type_doc = yaml.safe_load(sources[1]) + type_doc["spec"]["execution_descriptors"][descriptor.step] = digest( + descriptor_source + ) + type_source = yaml.safe_dump(type_doc).encode() + old_verifier = executor.evidence_verifier + + def verifier(purpose, source, evidence): + if purpose == "owner-descriptor-acceptance:owner": + return source == digest(descriptor_source) and evidence == "descriptor" + if purpose == "type-machine-seam-acceptance": + return source == digest(type_source) and evidence == "descriptor" + return old_verifier(purpose, source, evidence) + + adapter = executor.adapters[descriptor.adapter] + + def failed(inv): + return bound( + inv, + OwnerResult( + Outcome.FAILED_BEFORE, + "owner-receipt", + (), + True, + "session-revoked", + "grant-revoked", + ), + ) + + fresh = Executor( + Journal(tmp_path / "unsafe-retry"), + {adapter.reference: replace(adapter, invoke=failed)}, + verifier, + ) + admitted = fresh.admit( + sources[0], type_source, descriptor_source, sources[3], **kwargs + ) + assert fresh.run(*admitted).last.outcome is Outcome.FAILED_BEFORE + with pytest.raises(Refused): + fresh.run(*admitted, retry=True) + + +def bound(inv, result): + return replace( + result, + attempt_id=inv.attempt_id, + operation=inv.operation, + session_revocation_evidence=("session-" + inv.attempt_id) + if result.session_revocation_evidence + else None, + grant_revocation_evidence=("grant-" + inv.attempt_id) + if result.grant_revocation_evidence + else None, + ) + + +def test_old_grant_and_session_proofs_cannot_verify_a_new_retry(rig): + executor, args, _, _, _ = rig + adapter = executor.adapters[args[0].adapter] + retained = [] + + def attempts(inv): + if not retained: + result = bound( + inv, + OwnerResult( + Outcome.FAILED_BEFORE, + "owner-receipt", + (), + True, + "session-revoked", + "grant-revoked", + ), + ) + retained.append(result) + return result + # Even a caller rewriting labels cannot authenticate prior revocation + # evidence for the newly persisted attempt identity. + return replace(retained[0], attempt_id=inv.attempt_id, operation=inv.operation) + + executor.adapters[adapter.reference] = replace(adapter, invoke=attempts) + assert executor.run(*args).last.outcome is Outcome.FAILED_BEFORE + assert executor.run(*args, retry=True).last.outcome is Outcome.UNCERTAIN + + +def test_owner_interrupt_payload_is_sanitized_with_running_intent_retained(rig, capfd): + executor, args, _, _, _ = rig + adapter = executor.adapters[args[0].adapter] + + def interrupted(inv): + os.write(2, b"SYNTHETIC-SECRET") + raise KeyboardInterrupt("SYNTHETIC-SECRET") + + executor.adapters[adapter.reference] = replace(adapter, invoke=interrupted) + with pytest.raises(KeyboardInterrupt) as caught: + executor.run(*args) + assert "SYNTHETIC-SECRET" not in str(caught.value) + assert "SYNTHETIC-SECRET" not in "".join(capfd.readouterr()) + assert ( + executor.journal.load("mint-secret:fixture-client", args[2]).last.outcome + is Outcome.RUNNING + ) + + +def test_attempt_identity_changes_with_exact_catalog_revision_and_new_journal( + rig, tmp_path +): + executor, args, calls, sources, kwargs = rig + executor.run(*args) + original_attempt = calls[-1].attempt_id + new_catalog = b"independently-reviewed-catalog-v2" + old_verifier = executor.evidence_verifier + + def verifier(purpose, source, evidence): + if purpose == "catalog-routing-acceptance": + return source == digest(new_catalog) and evidence == "descriptor" + return old_verifier(purpose, source, evidence) + + fresh = Executor(Journal(tmp_path / "new-revision"), executor.adapters, verifier) + revised = fresh.admit(*sources[:3], new_catalog, **kwargs) + assert fresh.run(*revised).last.outcome is Outcome.SUCCESS + assert calls[-1].attempt_id != original_attempt + assert calls[-1].pins.catalog != calls[0].pins.catalog + + +def test_secret_sensitivity_parameter_cannot_reach_admission_or_journal(rig): + executor, _, _, sources, kwargs = rig + type_doc = yaml.safe_load(sources[1]) + type_doc["spec"]["parameters"][0]["sensitivity"] = "secret" + source = yaml.safe_dump(type_doc).encode() + old = executor.evidence_verifier + executor.evidence_verifier = lambda purpose, digest_value, evidence: ( + True + if purpose == "type-machine-seam-acceptance" + else old(purpose, digest_value, evidence) + ) + with pytest.raises(Refused, match="secret-sensitivity"): + executor.admit(sources[0], source, sources[2], sources[3], **kwargs) + + +def _lose_result(executor, args, calls): + old = executor.adapters[args[0].adapter] + + def lost(inv): + calls.append(inv) + raise TimeoutError("lost") + + seen = [] + + def by_original(ctx): + seen.append(ctx) + return old.reconcile(ctx.attempt) + + executor.adapters[old.reference] = replace( + old, invoke=lost, reconcile=None, reconcile_original=by_original + ) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + return seen + + +def test_original_intent_survives_restart_separate_from_reconcile_attempt(rig): + executor, args, calls, _, _ = rig + seen = _lose_result(executor, args, calls) + executor.journal = Journal(executor.journal.directory) # restart: fresh handle + assert executor.reconcile(*args).last.outcome is Outcome.NO_CHANGE + (ctx,) = seen + original = ctx.original.invocation + assert original.attempt_id == calls[0].attempt_id + assert original.operation == "run" and ctx.attempt.operation == "reconcile" + assert ctx.attempt.attempt_id != original.attempt_id + assert original.pins == args[1].pins + assert (original.principal, original.tenant, original.realm) == ( + args[1].principal, + args[1].tenant, + args[1].realm, + ) + assert ctx.original.source == executor.adapters[args[0].adapter].source + + +def test_missing_original_intent_refuses_and_stays_uncertain(rig): + executor, args, calls, _, _ = rig + seen = _lose_result(executor, args, calls) + executor.journal.db.execute("DELETE FROM intents") + with pytest.raises(Refused, match="original intent record missing"): + executor.reconcile(*args) + assert seen == [] + binding = f"{args[1].step}:{args[1].target}" + assert executor.journal.load(binding, args[2]).last.outcome is Outcome.UNCERTAIN + + +@pytest.mark.parametrize( + "change", [{"tenant": "other-tenant"}, {"realm": "other-realm"}, {"lane": "x"}] +) +def test_original_intent_mismatch_refuses(rig, change): + executor, args, calls, _, _ = rig + seen = _lose_result(executor, args, calls) + with pytest.raises(Refused): + executor.reconcile(args[0], replace(args[1], **change), args[2]) + assert seen == [] + + +def test_original_intent_source_mismatch_refuses(rig): + executor, args, calls, _, _ = rig + _lose_result(executor, args, calls) + binding = f"{args[1].step}:{args[1].target}" + with pytest.raises(Refused, match="source mismatch"): + executor.journal.original_intent(binding, calls[0], "other-source") diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/tests/test_context_compare_probes.py b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/tests/test_context_compare_probes.py new file mode 100644 index 00000000..9f17e6a0 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/tests/test_context_compare_probes.py @@ -0,0 +1,131 @@ +"""Observed source behavior of exact concurrent snapshot, not secure acceptance. + +Counterexamples use generated metadata and the owner's existing synthetic rig. +Passing a counterexample means the documented gap reproduced; it is not a native +holder, an owner attestation or permission to trust local SQL/JSON. +""" +from dataclasses import asdict, replace +import inspect +import json +from pathlib import Path +import sqlite3 +import subprocess +import sys +import pytest +from test_case_run import rig as rig +from warden.case_receipt_state import Outcome, Refused +from warden.case_run import Executor, Journal, OriginalIntent + + +def interrupted(rig): + executor,args,calls,sources,kwargs=rig + old=executor.adapters[args[0].adapter];seen=[] + def lost(inv):calls.append(inv);raise TimeoutError('generated lost response') + def original(ctx):seen.append(ctx);return old.reconcile(ctx.attempt) + executor.adapters[old.reference]=replace(old,invoke=lost,reconcile=None,reconcile_original=original) + assert executor.run(*args).last.outcome is Outcome.UNCERTAIN + return executor,args,calls,old,seen + + +def alter_payload(executor,**changes): + row=executor.journal.db.execute('SELECT seq,payload FROM intents ORDER BY seq DESC LIMIT 1').fetchone() + obj=json.loads(row[1]);obj.update(changes) + executor.journal.db.execute('UPDATE intents SET payload=? WHERE seq=?',(json.dumps(obj),row[0])) + + +def test_observed_original_actor_mismatch_is_accepted_by_lookup(rig): + executor,args,calls,old,_=interrupted(rig) + alter_payload(executor,actor='agent') + result=executor.journal.original_intent('mint-secret:fixture-client',args[1],old.source) + assert result.invocation.actor=='agent' and args[1].actor=='human' + + +def test_observed_payload_attempt_id_is_not_checked_against_sql_column_or_receipt(rig): + executor,args,calls,old,_=interrupted(rig) + actual=calls[0].attempt_id;alter_payload(executor,attempt_id='attempt-unrelated') + result=executor.journal.original_intent('mint-secret:fixture-client',args[1],old.source) + sql_id=executor.journal.db.execute('SELECT attempt_id FROM intents').fetchone()[0] + assert sql_id==actual and result.invocation.attempt_id=='attempt-unrelated' + + +def test_observed_orphan_intent_without_original_running_receipt_is_accepted(rig): + executor,args,calls,old,_=interrupted(rig) + executor.journal.db.execute('DELETE FROM receipts WHERE seq=(SELECT MIN(seq) FROM receipts)') + assert executor.journal.load('mint-secret:fixture-client',args[2]).last.outcome is Outcome.UNCERTAIN + result=executor.journal.original_intent('mint-secret:fixture-client',args[1],old.source) + assert result.invocation.attempt_id==calls[0].attempt_id + + +def test_observed_serializer_accepts_arbitrary_values_outside_normal_admit(rig): + _,args,_,_,_=rig + inv=replace(args[1],target={'unexpected':'synthetic-owner-payload'},operation='run',attempt_id='attempt-fixture') + record=OriginalIntent('mint-secret:fixture-client',inv,'sha256:'+'1'*64) + decoded=OriginalIntent.decode(record.payload()) + assert decoded.invocation.target=={'unexpected':'synthetic-owner-payload'} + # This is a direct serialization API gap, not an observed normal-admit leak. + + +def test_observed_tenant_realm_not_admitted_and_remain_empty(rig): + _,args,_,_,_=rig + params=inspect.signature(Executor.admit).parameters + assert 'tenant' not in params and 'realm' not in params + assert args[1].tenant==args[1].realm=='' + + +def test_observed_no_original_context_purpose_is_requested(rig): + executor,args,_,_,seen=interrupted(rig);old=executor.evidence_verifier;purposes=[] + def trace(purpose,source,evidence):purposes.append(purpose);return old(purpose,source,evidence) + executor.evidence_verifier=trace + assert executor.reconcile(*args).last.outcome is Outcome.NO_CHANGE + assert seen and any(p.startswith('session-revocation:') for p in purposes) + assert not any('original' in p for p in purposes) + # Owner trust is the existing synthetic fixture; this proves call shape only. + + +@pytest.mark.parametrize('field,value',[('principal','another-person'),('tenant','another-tenant'),('realm','another-realm')]) +def test_existing_lookup_does_refuse_stored_principal_tenant_realm_mismatch(rig,field,value): + executor,args,_,old,_=interrupted(rig);alter_payload(executor,**{field:value}) + with pytest.raises(Refused):executor.journal.original_intent('mint-secret:fixture-client',args[1],old.source) + + +def test_existing_atomic_insert_failure_prevents_dispatch_and_both_records(rig): + executor,args,calls,_,_=rig + executor.journal.db.execute("CREATE TRIGGER fail_receipt BEFORE INSERT ON receipts BEGIN SELECT RAISE(ABORT,'fixture'); END") + with pytest.raises(sqlite3.IntegrityError):executor.run(*args) + assert not calls + assert executor.journal.db.execute('SELECT COUNT(*) FROM intents').fetchone()==(0,) + assert executor.journal.db.execute('SELECT COUNT(*) FROM receipts').fetchone()==(0,) + + +def test_existing_missing_original_refuses_before_callback(rig): + executor,args,_,_,seen=interrupted(rig) + executor.journal.db.execute('DELETE FROM intents') + with pytest.raises(Refused):executor.reconcile(*args) + assert not seen + assert executor.journal.load('mint-secret:fixture-client',args[2]).last.outcome is Outcome.UNCERTAIN + + +def test_existing_current_parent_revocation_remains_mandatory(rig): + executor,args,_,_,_=interrupted(rig);old=executor.evidence_verifier + def refuse_session(purpose,source,evidence): + return False if purpose.startswith('session-revocation:') else old(purpose,source,evidence) + executor.evidence_verifier=refuse_session + with pytest.raises(Refused):executor.reconcile(*args) + assert executor.journal.load('mint-secret:fixture-client',args[2]).last.outcome is Outcome.UNCERTAIN + + +def test_existing_intent_survives_actual_distinct_interpreter(rig): + executor,args,calls,old,_=interrupted(rig) + code='''import json,sys +from pathlib import Path +from warden.case_run import Journal,Invocation +from warden.case_receipt_state import Pins +obj=json.load(sys.stdin);obj['pins']=Pins(**obj['pins']);j=Journal(Path(sys.argv[1])) +with j.locked(): + i=j.original_intent('mint-secret:fixture-client',Invocation(**obj),sys.argv[2]) + print(json.dumps({'attempt':i.invocation.attempt_id,'operation':i.invocation.operation})) +''' + result=subprocess.run([sys.executable,'-B','-c',code,str(executor.journal.directory),old.source], + input=json.dumps(asdict(args[1])),text=True,capture_output=True,timeout=15, + env={'PATH':'/usr/bin:/bin','PYTHONPATH':str(Path(__file__).parents[1]/'src'),'PYTHONDONTWRITEBYTECODE':'1'},check=True) + assert json.loads(result.stdout)=={'attempt':calls[0].attempt_id,'operation':'run'} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/workplans/WARDEN-WP-0044-security-case-follow-through.md b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/workplans/WARDEN-WP-0044-security-case-follow-through.md new file mode 100644 index 00000000..40f1b5b5 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/snapshot/workplans/WARDEN-WP-0044-security-case-follow-through.md @@ -0,0 +1,289 @@ +--- +id: WARDEN-WP-0044 +type: workplan +title: "Follow through on KnownSecurityCases along their type's route" +domain: infotech +repo: ops-warden +status: active +flavor: implementation +owner: claude-code +topic_slug: netkingdom +created: "2026-10-08" +updated: "2026-10-11" +depends_on: [] +related_repos: + - net-kingdom + - ops-mason + - key-cape + - railiance-platform +state_hub_workstream_id: "f7e81013-6868-5d99-8528-ba75a442eca9" +--- + +# WARDEN-WP-0044 — Follow through on KnownSecurityCases along their type's route + +## Why + +NetKingdom is introducing SecurityCaseTypes (net-kingdom `NK-WP-0048`, canon +`security-case-types_v0.1`, proposed). Once a case of an implemented type is +accepted and ops-mason has materialized its structure (`MASON-WP-0010`), +ops-warden carries out the remaining steps. These are the owners' declared entry +points, run on the authentication path the type defines, followed by the type's +verification. + +Today ops-warden routes needs and, for `exec_capable` lanes, proxies fetches as +the caller. It does not yet know about cases, and each new integration ends with +a founder running a hand-written script (for example the proposed KEY-WP-0038-T02 +rollout). This plan gives ops-warden the follow-through role, without widening +what it may do: each step still runs under its owner's entry point and the +caller's identity. + +## Tasks + +```task +id: WARDEN-WP-0044-T01 +status: done +depends_on: [NK-WP-0048-T03] +priority: high +state_hub_task_id: "ef0920fc-418e-52cb-acff-9a5c0c728bf5" +``` + +### T01 — Define the type's follow-through section + +Propose the `follow_through` part of the SecurityCaseType schema together with +net-kingdom. For each step it names the owner entry point, `warden_executes`, +the auth method and lane, whether an attended human login is unavoidable (with +the type's justification), and the verification command. Write it for the first +type, `oidc-browser-relying-party`, covering key-cape client registration, +custody minting and LLDAP group membership. + +2026-10-08: NK-WP-0048-T03/T04 landed with a drafted section. Review is in +`history/2026-10-08-oidc-rp-follow-through-review.md`. It requires three +changes before T02: a per-step `lane`, a narrow `warden_executes`, and +`value_handling`. It also lists four refinements. This task is done when +net-kingdom acknowledges the review (NK-WP-0048-T05). + +2026-10-09: R4 was withdrawn in favour of ops-mason's per-case +`{consuming_service}-keycape-client`, with the entry body templated in the type +(R4′, see the addendum in the review). + +2026-10-09: the revised 1.0 type and canon now carry R1–R3, R4′, R5–R7. +`warden case check-type ` (`src/warden/case_type.py`, tests in +`tests/test_case_type.py`) checks a type's follow-through mechanically: the +live type is schema-conformant, with one attended session +(`key-cape-oidc-login`). It is not yet plannable, because two lanes are not +in the catalog: `identity-provisioner-groups` (net-kingdom) and +`keycape-client-secret-generate` (railiance-platform). These gate T02, not +T01. T01 still closes on net-kingdom's acknowledgement (NK-WP-0048-T05). + +2026-10-09: ops-warden cannot add `identity-provisioner-groups` as the type +declares it. The provisioner accepts only user-engine's runtime token and +admits only user-engine pods, so no caller-identity path exists. R8 (review +addendum) asks net-kingdom either to move `ensure-groups` into the existing +attended session (recommended) or to give the provisioner an operator +credential. `keycape-client-secret-generate` needs a generate-in-custody entry +point from railiance-platform, and the broker does not have one yet. Both were +sent to their owners. + +2026-10-09: done. net-kingdom took R8 option 1, so `ensure-groups` runs attended +in the `register-client` session and `identity-provisioner-groups` is dropped +(net-kingdom `0e86f95`). The founder acknowledged the type with memo r4 (hub +decision `e418263e`, NK-WP-0048-T05 done). + +```task +id: WARDEN-WP-0044-T02 +status: done +depends_on: [WARDEN-WP-0044-T01] +priority: high +state_hub_task_id: "5f68c991-f490-58f8-a121-92f74073d272" +``` + +### T02 — Plan a case run + +`warden case plan ` checks that the case is accepted and materialized, +resolves each step to its catalog entry and entry point, and prints the run: +which steps warden executes, which run as the attended caller, what the +verification is. No secret values; no execution. + +Tests: refuse an unaccepted or unmaterialized case; refuse a step whose catalog +entry is missing or not reviewed; show attended steps explicitly. + +2026-10-09: done. `warden case plan [--type] [--json]` +(`src/warden/case_plan.py`, 14 tests in `tests/test_case_plan.py`). It refuses: +- a case that is not `materialized`, or has no acceptance record +- a type that is mismatched or not acknowledged +- any `case check-type` finding, including a missing or unreviewed lane +- undeclared, type-fixed or secret-sensitivity parameters +- a placeholder the case does not set, or an unsupported `when` + +It fills type defaults (`issuer`), evaluates ` == ` conditions, +renders owner, entry point, auth and ready_check, groups attended steps per +lane, and lists the applicable verification. + +The live Forgejo case is correctly refused: it is `accepted`, not +materialized. A materialized copy, with a stand-in lane for +`keycape-client-secret-generate`, renders six steps: two attended steps plus +`register-client` in one `key-cape-oidc-login` session, one warden step +(`mint-secret`), and two owner steps. Seven checks follow. + +Observation for net-kingdom (not blocking): `bind-env` runs before +`register-client`, in type order. Its entry carries a literal +`KEYCAPE__CLIENT_SECRET`, which is not a `{param}` placeholder, so it +reaches the plan unrendered. + +2026-10-09 follow-up: +- **Format specs.** `_render` supports net-kingdom's `{param:env}` spec (upper + case, `-` as `_`, the same as `keycape-client.py env_name`) and refuses any + other spec or `!conversion`. +- **Case reference.** The plan prints `case-ref: @sha256:` over the + case file's exact bytes. This is the reference proposed to railiance-platform + for `credential.py generate --case-ref` (RPF-WP-0054-T01); the broker + re-derives it from the file. +- 18 tests now. + +```task +id: WARDEN-WP-0044-T03 +status: progress +depends_on: [WARDEN-WP-0044-T02, MASON-WP-0010-T03] +blocking_reason: "Local durable execution kernel and injected failure harness implemented. Native acceptance still requires authenticated owner machine descriptors/adapters, actual Forgejo materialization receipt, contained session integration, ordered verification and first native run." +priority: high +state_hub_task_id: "bcbf6c91-2c04-5c16-ae8c-5d6c06fd6b89" +``` + +### T03 — Run and verify a case + +`warden case run ` executes the plan. Owner entry points run through +the existing proxy as the caller; steps that need an attended login use the +existing contained login envelope (silent command, session revoked afterwards), +grouped into as few attended sessions as possible. Then it runs the type's +verification and writes a receipt without values. `warden case rollback` +reverses exactly this case's steps. + +Acceptance: the Forgejo reference case (NK-WP-0048-T06) reaches `live` through +this path, with no case-specific script. + +2026-10-17: original-intent lookup/context interface for NK-WP-0048-T15, source +and synthetic tests only. `src/warden/case_run.py`: `Executor.run` writes an +`OriginalIntent` (schema `warden.case.original-intent.v1`: the dispatched run +Invocation — attempt, step, owner, target, actor, principal, lane, all six pins, +tenant, realm — plus admitted adapter source) in the same SQLite transaction as +the pre-dispatch journal row. `Journal.original_intent(binding, current, source)` +returns it after restart and refuses on a missing record or any +pins/source/step/owner/target/principal/lane/tenant/realm mismatch, before any +journal write, so the step stays uncertain. An adapter's +`reconcile_original(ReconcileContext(original, attempt))` receives the original +intent separately from the new reconcile attempt. This is Warden's record of what +it dispatched, not owner created-only provenance; tenant/realm are as supplied at +admission. Owner descriptors/adapters, contained session integration, Forgejo +materialization and the native run remain open. + +Agreed with railiance-platform (RPF-WP-0054-T01, 2026-10-09): +- `mint-secret` runs `credential.py generate --grant keycape/client-secret-generate + --client-id --case-ref --case-file ` as the caller's + child process. The broker re-derives the case-ref and checks status and + client_id. +- warden refuses that step when `WARDEN_AGENT_ID` is set. + +2026-10-10: railiance-platform implemented the command (RPF-WP-0054-T03). It is +catalogued as draft `keycape-client-secret-generate`, with `exec_owner` +railiance-platform and `exec_capable: false`. `warden case check-type` now +reports the lane as LANE-UNREVIEWED rather than LANE-MISSING. Before it can be +activated: +- the founder binds the issuer policy to the operator OIDC role +- key-cape confirms the secret format +- the RPF-WP-0054-T05 probe passes + +2026-10-10 (later): +- **Broker lane.** The issuer role is bound (CCR-2026-0043) and the T05 probe + passed (railiance-platform `21b8af3`). Only key-cape's format confirmation is + left. The playbook now includes the issuer-role login, a 15m token with + unlimited uses that `warden case run` must group with the attended session. +- **Env spec.** net-kingdom adopted `{client_id:env}` as a 1.0 patch + (`9a9b8e3`). The plan renders `KEYCAPE_FORGEJO_CLIENT_SECRET`. +- **Forgejo case.** Rendered and built by ops-mason. It is not yet + `materialized`: MASON-WP-0010-T03 waits on the railiance-platform CCRs and + their founder apply. + +2026-10-10: `keycape-client-secret-generate` activated on railiance-platform's +go-ahead (message 20973a9c) after key-cape confirmed the format (4e0213b3). +`warden case check-type` on the live type now reports `plannable=True`. The +only thing T03 still waits on is the Forgejo case reaching `materialized` +(MASON-WP-0010-T03). + +2026-10-10: net-kingdom moved the type to 1.1 (`e2174ce`) and re-pinned the +Forgejo case. 1.1 adds the verification `consumer-enforces-groups` and requires +the hand-over to enforce `access_groups`. `check-type` on 1.1 reports +`plannable=True`, and a materialized copy renders six steps and the new check. +The case-ref digest covers the whole case file, including `status`, so it +changes when the case becomes `materialized`. `warden case run` therefore takes +it from the file at run time, and the broker re-derives it from the same file. + +```task +id: WARDEN-WP-0044-T04 +status: wait +depends_on: [WARDEN-WP-0044-T03] +blocking_reason: "Catalog integration follows a proven run." +priority: medium +state_hub_task_id: "4a0fa4a7-5ca9-518a-ab92-a79a5e2cf93e" +``` + +### T04 — Catalog entries from types + +Let a type contribute reviewed catalog entries once, so that every case of that +type routes without a new catalog edit. Cases add parameter values, not routes. +`warden route find` should be able to answer "which SecurityCaseType covers this +need?" before anyone builds by hand. + + +2026-10-10 consolidation source prototype (not an accepted owner adapter API): +`src/warden/case_receipt_state.py` provides a pure immutable receipt reducer, +with exact case/type/adapter/catalog/acceptance/run-sheet pins, running intent +before dispatch, uncertain recovery after a lost result, no success replay, +explicit retry-safe failed-before-change retry and created-only reverse undo. +It executes nothing and accepts no command output. The caller must durably +persist running before any future side effect; storage/authenticated owner +receipts/live adapters are not implemented. Focused offline tests cover changed +pins, lost receipts, retry/refusal, uncertain rollback and shared-object +preservation. Source proposal: `docs/security-case-execution-proposal.md`. + +T03 remains open: typed owner execution/receipt contracts require review, the +Forgejo structural materialization needs owner receipts, and CLI dispatch, +contained sessions, native verification and provenance remain unimplemented. +There is a circularity to resolve with net-kingdom/ops-mason: materialization +cannot require mint-secret if mint-secret is itself a follow-through step whose +planning requires materialized. No case status is changed by this prototype. + + +2026-10-11: the exact execution-design memo version1 has confirmed native human +acceptance (approval e46b149b-ec3c-4b3b-98a7-dcb4934448ca). The approved proposal +bytes remain unchanged. `src/warden/case_run.py` now implements a closed v1 local +execution seam, independent admission/evidence callbacks, private transactional +SQLite receipt storage, process locking, running-before-dispatch, crash recovery +to uncertainty, explicit safe retry, separately required broker-grant and parent-session revocation +proofs, native owner reconciliation and created-only +reverse rollback. Thirty-seven kernel tests and fourteen reducer tests pass; +ruff checks pass. See `docs/security-case-execution-kernel-v1.md` for exact scope +and receipt provenance. No installed native adapter or live CLI execution is +claimed. Current prose types stay plan-only; acknowledged descriptor/type machine +seams and authenticated owner/caller receipts are still required. T03 progresses +for implementation but cannot finish before ordered native Forgejo acceptance. + +2026-10-16 source preparation (isolated checkout; no native acceptance): +T03's browser parameter mismatch now has a producer-validator-backed projection +in `case_inputs.py`, explicitly configured on the real Executor. Exact 1.1/1.2 +parameter definitions and all four reviewed producer modules are pinned; full case +validation admits reviewed security-sensitive metadata while unknown/fixed/secret +inputs and type/producer drift refuse. Only the reviewed descriptor target +reaches the owner callback; no parameter journal payload is added. Actual +Executor/producer tests cover positive versions and refusal/containment paths. +Acknowledged machine descriptors, authenticated owner evidence, real Forgejo +materialization and contained native sessions remain mandatory and unimplemented; +T03 stays progress, T04 stays wait. No type or case status changed. + +2026-10-16 producer source refresh: independently reviewed NetKingdom +`015694ee414c802dc2ef1fa0120dfed610086e69` optional descriptor-map repair. +Only the validator/evidence source pins change; exact parameter and transitive +source identity remain enforced. Actual canonical producer, Executor and reducer +suite passes 87 checks, including 14 new malformed-map integration cases; all +four missing/changed modules still refuse. Review: +`docs/reviews/2026-10-16-browser-producer-pin-refresh.md`. No adapter attestation, +native acceptance, credential use or privileged dispatch. T03 remains progress. diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/source-state.json b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/source-state.json new file mode 100644 index 00000000..b25e9fd9 --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-compare-r2/source-state.json @@ -0,0 +1,57 @@ +{ + "observed_at": "2026-10-17T05:53:20.364253+00:00", + "owner": "ops-warden", + "canonical_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "sources": [ + { + "path": "src/warden/case_run.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:0574036ca7b74bdb9124f6af4888e63ec231ee60f5c9f34cbe2fb1acee39f671", + "worktree_sha256": "sha256:0574036ca7b74bdb9124f6af4888e63ec231ee60f5c9f34cbe2fb1acee39f671", + "dirty": false + }, + { + "path": "src/warden/case_receipt_state.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc", + "worktree_sha256": "sha256:a2cda7f187e77bd76d4d209505638de44a587fae0bc033ec75c23ee9ea9f73bc", + "dirty": false + }, + { + "path": "src/warden/case_inputs.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "worktree_sha256": "sha256:1334b0cd9897e763cfea2d306e997c659f3f284ecc3f9612140c2104835da7c7", + "dirty": false + }, + { + "path": "src/warden/__init__.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a", + "worktree_sha256": "sha256:047e0feba479474235f0707a207655ecfddfb33cd4f54069134419b8ca4ea24a", + "dirty": false + }, + { + "path": "tests/test_case_run.py", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:bc7264848e76e3576821523fec0c4925c4446eaf470f859fe245c98ea072d139", + "worktree_sha256": "sha256:bc7264848e76e3576821523fec0c4925c4446eaf470f859fe245c98ea072d139", + "dirty": false + }, + { + "path": "docs/security-case-execution-kernel-v1.md", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:0eb563dbaea739fba928d04708c2c2bdac592783bbe68e1271041b0d1b1994cb", + "worktree_sha256": "sha256:0eb563dbaea739fba928d04708c2c2bdac592783bbe68e1271041b0d1b1994cb", + "dirty": false + }, + { + "path": "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "base_sha256": "sha256:44686df53dac792e46791878c3ccf8c187bb14e2de33eeac411be2005f7f2aa1", + "worktree_sha256": "sha256:44686df53dac792e46791878c3ccf8c187bb14e2de33eeac411be2005f7f2aa1", + "dirty": false + } + ], + "canonical_edits_performed": false +} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-owner-followthrough-r3/after.json b/docs/changes/netkingdom-target-2026-10-17/original-context-owner-followthrough-r3/after.json new file mode 100644 index 00000000..837758bb --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-owner-followthrough-r3/after.json @@ -0,0 +1,37 @@ +{ + "observed_at": "2026-10-17T06:50:09.029792+00:00", + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "origin_main": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "changed_paths": [ + { + "path": "src/warden/case_run.py", + "before_sha256": "sha256:0574036ca7b74bdb9124f6af4888e63ec231ee60f5c9f34cbe2fb1acee39f671", + "after_sha256": "sha256:77e8117a1fb4e039815b555797778f7180f11e30f64cebe51b350b3eb7e46768" + }, + { + "path": "tests/test_case_original_context_consistency.py", + "before_sha256": null, + "after_sha256": "sha256:10c5187cbc49222017319249ccb408816497ec6e006859d23003f3b2b375445b" + }, + { + "path": "docs/security-case-execution-kernel-v1.md", + "before_sha256": "sha256:0eb563dbaea739fba928d04708c2c2bdac592783bbe68e1271041b0d1b1994cb", + "after_sha256": "sha256:e5570f542ec3ef4e6c11332cfe48fea16bf90cde5b9a5d926f5a068b7cafdbc7" + }, + { + "path": "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "before_sha256": "sha256:44686df53dac792e46791878c3ccf8c187bb14e2de33eeac411be2005f7f2aa1", + "after_sha256": "sha256:721a90b2ed8c896257c34a1451eb642cb8824c8263e37c483fb61b36adc54088" + } + ], + "all_task_blocks_byte_identical": true, + "task_blocks": 4, + "unrelated_WORK_RECORDS_worktree_and_index_unchanged": true, + "diff_check": 0, + "status": " M WORK-RECORDS.md\n M docs/security-case-execution-kernel-v1.md\n M src/warden/case_run.py\n M workplans/WARDEN-WP-0044-security-case-follow-through.md\n?? tests/test_case_original_context_consistency.py\n", + "native_actions": false, + "commit_or_sync": false, + "git_metadata_edited": false, + "retained_packets_unchanged": true, + "refinement": "New test fixture import corrected for canonical tests package after retained failed collection; no source control was weakened." +} diff --git a/docs/changes/netkingdom-target-2026-10-17/original-context-owner-followthrough-r3/before.json b/docs/changes/netkingdom-target-2026-10-17/original-context-owner-followthrough-r3/before.json new file mode 100644 index 00000000..35f871cf --- /dev/null +++ b/docs/changes/netkingdom-target-2026-10-17/original-context-owner-followthrough-r3/before.json @@ -0,0 +1,29 @@ +{ + "base_head": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "origin_main": "a45cf475bc4dbc929e0eecbc1f806a9229d934e2", + "paths": [ + { + "path": "src/warden/case_run.py", + "existed": true, + "before_sha256": "sha256:0574036ca7b74bdb9124f6af4888e63ec231ee60f5c9f34cbe2fb1acee39f671" + }, + { + "path": "tests/test_case_original_context_consistency.py", + "existed": false, + "before_sha256": null + }, + { + "path": "docs/security-case-execution-kernel-v1.md", + "existed": true, + "before_sha256": "sha256:0eb563dbaea739fba928d04708c2c2bdac592783bbe68e1271041b0d1b1994cb" + }, + { + "path": "workplans/WARDEN-WP-0044-security-case-follow-through.md", + "existed": true, + "before_sha256": "sha256:44686df53dac792e46791878c3ccf8c187bb14e2de33eeac411be2005f7f2aa1" + } + ], + "status": " M WORK-RECORDS.md\n", + "unrelated_worktree_sha256": "sha256:b6af9b1cb21e3a64b2ef8a5e732bc35c39cdeaef6af7a18f24824e8ec9fb5a76", + "unrelated_index_sha256": "sha256:c61591efad763a3dcb86751f5bf46f03fec8a162bfe06c4272032fea15d2988f" +}