docs: preserve reviewed NetKingdom session artifacts
This commit is contained in:
parent
b77ad96bd5
commit
3add9f8790
45 changed files with 16252 additions and 0 deletions
|
|
@ -0,0 +1,46 @@
|
|||
## 2026-10-16 workload-only candidate lock and actual-manager preparation
|
||||
|
||||
Prepared under existing REINAH-WP-0008-T07 / GLAS-WP-0012 acceptance work.
|
||||
The combined-runtime lock still selects Glas `30be8180`, so the rebuilt
|
||||
`818b1b7c` artifact cannot include published workload-only delivery `7357115c`.
|
||||
The prepared lock-only patch advances that dependency; llm-connect `37436bb5`
|
||||
and sandboxer `221126cb` remain pinned to the prior admitted source set. Rein
|
||||
source is `be5ef400`; an isolated preparation commit `c811fc43` contains the
|
||||
patch. This isolated commit is not an owner publication.
|
||||
|
||||
The existing owner builder produced candidate runtime
|
||||
`634e65fee5517ebee0d1080805fd84c81bd46af26a2b38ec1ab14bd2be17bf8f`, using the
|
||||
same native Claude 2.1.266 binary (`19842705…`), frozen uv.lock and source-content
|
||||
verification. The runtime is only in `/tmp/netkingdom-runtime-next-20261016/runtime`.
|
||||
Canonical owner repositories, retained artifact pins and native owner
|
||||
configuration were not changed by this preparation.
|
||||
|
||||
All 141 Glas tests and seven rein runtime-lock/rehearsal tests pass. The complete
|
||||
runtime digest recomputed after all proofs remains `634e65fee5517…`. Using the candidate's installed packages, the actual
|
||||
SandboxManager and Glas gateway execute the unchanged
|
||||
`harness.agent-dev-local@1.1.1` profile (still `unverified`): real rein/Glas CLI
|
||||
startup, Claude --version, read-only artifact, absent synthetic host file,
|
||||
inherited environment and child-process denial, exact task identity/private
|
||||
state, deterministic Git effect in the disposable workspace, task removal and
|
||||
manager destruction pass. A separately labelled synthetic route fixture proves
|
||||
only the explicit workload receives its synthetic provider value, output is
|
||||
redacted, every housekeeping command has empty routes and the following command
|
||||
is clean. Real manager refusals for wrong actor/project/run, expired execution,
|
||||
expiry cleanup, explicit cancellation and post-cancellation execution also pass.
|
||||
No model, provider request, real credential read or spend occurred.
|
||||
|
||||
First proof attempt correctly encountered PermissionError on wrong consumer but
|
||||
the proof runner expected only ValueError/RuntimeError. Its finally cleanup ran;
|
||||
the runner was amended, with the failed attempt preserved in proof-attempts.json.
|
||||
The artifact was unchanged. This is a refined test attempt, not a new unchanged
|
||||
execution success.
|
||||
|
||||
Owner review/integration of the lock patch must produce an owner source revision
|
||||
and a rebuilt artifact whose complete digest is verified again. Candidate
|
||||
preparation does not authorize native installation or admission. T07's queued
|
||||
spend memo, native artifact installation/rehearsal, renewal and file-pin updates,
|
||||
required approvals and bounded real-model/tool proof remain open. GLAS native
|
||||
acceptance and actual interactive assignment migration, denied external tool
|
||||
paths, bounded delegation and revocation remain open; no readiness or supervision
|
||||
promotion is inferred. Older `818b1b7c`, `ee3d0a53`, installed `b6e4e8a4` and their
|
||||
failed/unknown outcomes remain historical evidence.
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"attempts": [
|
||||
{
|
||||
"attempt": 1,
|
||||
"outcome": "proof-runner-exception",
|
||||
"gateway_proof_completed": true,
|
||||
"failure": "Manager correctly refused mismatched consumer with PermissionError; runner expected ValueError or RuntimeError",
|
||||
"recovery": "Fixture finally destroyed sandbox. Runner catch amended to include PermissionError. Artifact unchanged.",
|
||||
"unchanged_success": false
|
||||
},
|
||||
{
|
||||
"attempt": 2,
|
||||
"outcome": "passed",
|
||||
"scope": "exact unmodified1.1.1 gateway + manager identity/expiry/cancellation",
|
||||
"artifact_unchanged": true
|
||||
},
|
||||
{
|
||||
"attempt": 3,
|
||||
"outcome": "passed",
|
||||
"scope": "same exact profile + separately labelled synthetic credential route gateway fixture + manager lifecycle",
|
||||
"artifact_unchanged": true
|
||||
},
|
||||
{
|
||||
"attempt": 4,
|
||||
"outcome": "passed",
|
||||
"scope": "canonical published rein9b88cad artifact f59959b6, unchanged1.1.1 gateway and separate synthetic route fixture plus manager lifecycle",
|
||||
"refinement_from_attempt1_retained": true,
|
||||
"native_acceptance": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
diff --git a/deploy/runtime-contract-lock.json b/deploy/runtime-contract-lock.json
|
||||
index ee804ca..2ef3087 100644
|
||||
--- a/deploy/runtime-contract-lock.json
|
||||
+++ b/deploy/runtime-contract-lock.json
|
||||
@@ -13,7 +13,7 @@
|
||||
"version": "0.1.0"
|
||||
},
|
||||
{
|
||||
- "commit": "30be81808140a3a3d137e731d1fe9aed59ad8581",
|
||||
+ "commit": "7357115c8311e55ee3d495f5266f501a17ec3c89",
|
||||
"distribution": "glas-harness",
|
||||
"source": "../glas-harness",
|
||||
"version": "0.1.0"
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
....... [100%]
|
||||
7 passed in 0.59s
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"dependencies": [
|
||||
{
|
||||
"clean": true,
|
||||
"commit": "37436bb562a3f20abc3b88defc5bae6508c32e7e",
|
||||
"distribution": "llm-connect",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
{
|
||||
"clean": true,
|
||||
"commit": "7357115c8311e55ee3d495f5266f501a17ec3c89",
|
||||
"distribution": "glas-harness",
|
||||
"version": "0.1.0"
|
||||
},
|
||||
{
|
||||
"clean": true,
|
||||
"commit": "221126cb77ea41ec283598a23d628f760a51bdea",
|
||||
"distribution": "sandboxer",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
],
|
||||
"ok": true,
|
||||
"schema_version": "1"
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"rein-aharness": "be5ef400861a5e31a31f29d173990c7a143fb189",
|
||||
"glas-harness": "7357115c8311e55ee3d495f5266f501a17ec3c89",
|
||||
"llm-connect": "37436bb562a3f20abc3b88defc5bae6508c32e7e",
|
||||
"sand-boxer": "221126cb77ea41ec283598a23d628f760a51bdea",
|
||||
"candidate_rein": "c811fc430f80e214b43cfcca5dc9e7f786d5f559"
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# Glas owner implementation — 16 October 2026
|
||||
|
||||
Prepared a concrete source patch in an isolated copy of current tracked Glas
|
||||
files, retaining working-tree contents. Canonical Glas/Sand-boxer/rein sources,
|
||||
profiles, native policies, installed artifacts and supervision authority were
|
||||
not modified. Dirty Glas `.custodian-brief.md` and `WORK-RECORDS.md` are preserved.
|
||||
|
||||
The gateway previously requested profile credential routes for every owner
|
||||
command, including executable lookup, task writers/removers, Git helpers and
|
||||
artifact probes. The patch gives housekeeping an empty-route binding and gives
|
||||
only explicit rein workload execution the profile routes. Both concrete adapters
|
||||
use the explicit path. Missing workload binding refuses; an SSH selection with
|
||||
credential routes refuses instead of silently bypassing owner delivery. This
|
||||
narrows trusted gateway behavior, not arbitrary caller authorization.
|
||||
|
||||
Selected `harness.agent-dev-local@1.1.1` already has empty credential refs and
|
||||
uses the rein's task-scoped MessagesOwner route outside bwrap. This patch does
|
||||
not revoke that route or claim its scoped capability is an admin credential.
|
||||
It prevents future/raw provider route selections from granting housekeeping
|
||||
provider values. No generic shell broker or supervisor service was added.
|
||||
|
||||
Validation:
|
||||
|
||||
- All 141 Glas tests pass; regression checks cover exact create-time identity,
|
||||
timeout and stdin, empty housekeeping routes, explicit workload routes,
|
||||
missing binding refusal and remote credential refusal.
|
||||
- Actual disposable bwrap through Glas transport proves a synthetic host
|
||||
credential file absent, inherited BAO/VAULT/AWS/SSH variables denied including
|
||||
a child subprocess, provider value only in the explicit workload, output
|
||||
redaction, no subsequent-command value, and workspace destruction.
|
||||
- `git apply --check` passes against canonical Glas. No provider/model call,
|
||||
real credential read, remote live execution or spend occurred.
|
||||
|
||||
Artifacts: [patch](glas-harness.patch), [source hashes](source-manifest.json),
|
||||
[test result](tests.json), [actual bwrap proof](bwrap-proof.json),
|
||||
[patch check](patch-check.json). Patch SHA-256:
|
||||
`83eeeb0017603aabae0ff49a23f96248f677f6c9f3e2c01b22f6d0416fca69c7`.
|
||||
The reproducible proof source is included in the patch. The isolated source
|
||||
remains `/tmp/netkingdom-agent-isolation-20261016/glas-harness`.
|
||||
|
||||
## Exact native gates retained
|
||||
|
||||
1. Review/apply this owner patch and reconcile its resulting runtime/source pins.
|
||||
Do not reuse an admitted artifact digest for changed contents. The patch adds
|
||||
a dated preparation note to GLAS-WP-0012 without completing a native task.
|
||||
2. Latest dependency sources supersede historical missing-key/lane accounts:
|
||||
September 27 native lanes were applied, verified and revoked. The corrected
|
||||
owner runtime `818b1b7c…` was rebuilt October 15 and passes no-credential
|
||||
startup; it is not installed/admitted on Railiance (still `b6e4e8a4…`).
|
||||
3. REINAH-WP-0008-T07 retains queued spend memo lifecycle acceptance (workspace
|
||||
`2ccb4bda-e318-51d2-a8dd-709db768494f`, review
|
||||
`review-e2d2e98f-4701-45b0-abc1-4fe9f00bd1a8`), owner artifact installation,
|
||||
`metered-once --rehearse`, owner renewal and Secrets Engine file-pin updates,
|
||||
required native approvals, then successful bounded model/tool/commit proof.
|
||||
Previously consumed approvals are historical evidence, not reusable authority.
|
||||
4. GLAS-WP-0012-T02–T05 remain subject to accepted runtime/profile and combined
|
||||
actual rein/model/task cleanup proof. The standalone bwrap proof establishes
|
||||
none of those model/provider outcomes.
|
||||
5. CUST-WP-0073-T02/T04 and TSA-06 additionally require admission of this actual
|
||||
interactive agent assignment: its host files/mounts/env/subprocesses and
|
||||
external tool paths must lose admin authority, with scoped distinguishable
|
||||
task identity, revocation/expiry/cancellation and bounded delegation evidence.
|
||||
The current interactive runtime has not moved into this disposable sandbox.
|
||||
The assignment supervision record remains supervised, authority_granted false;
|
||||
this owner worker does not approve a privileged path or promote the agent.
|
||||
|
||||
No task status, profile readiness, CUST-WP-0073 completion or TSA-06 acceptance
|
||||
is inferred from the patch or synthetic proof. The stated owner/native gates
|
||||
remain live in existing workplans; source delivery is separate from admission.
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"exit_code": 0,
|
||||
"result": {
|
||||
"scope": "disposable bwrap via Glas transport; not interactive assignment",
|
||||
"model_calls": 0,
|
||||
"real_credentials_read": false,
|
||||
"host_file_denied": true,
|
||||
"inherited_env_and_subprocess_denied": true,
|
||||
"credential_routes_by_command": [
|
||||
[],
|
||||
[
|
||||
"synthetic"
|
||||
],
|
||||
[]
|
||||
],
|
||||
"provider_output_redacted": true,
|
||||
"subsequent_command_clean": true,
|
||||
"workspace_removed": true
|
||||
},
|
||||
"stderr": ""
|
||||
}
|
||||
|
|
@ -0,0 +1,464 @@
|
|||
--- a/src/glas_harness/contract.py
|
||||
+++ b/src/glas_harness/contract.py
|
||||
@@ -117,6 +117,8 @@
|
||||
class SandboxHandle(ContractModel):
|
||||
# Process-local owner binding; never serialized as reachability or evidence.
|
||||
_owner_execute: Any = PrivateAttr(default=None)
|
||||
+ _owner_workload_execute: Any = PrivateAttr(default=None)
|
||||
+ _credential_route_refs: list[str] = PrivateAttr(default_factory=list)
|
||||
_timeout_seconds: int = PrivateAttr(default=600)
|
||||
|
||||
sandbox_id: str
|
||||
--- a/src/glas_harness/gateway.py
|
||||
+++ b/src/glas_harness/gateway.py
|
||||
@@ -7,6 +7,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
+from functools import partial
|
||||
from collections.abc import Callable
|
||||
import uuid
|
||||
from datetime import UTC, datetime
|
||||
@@ -145,19 +146,23 @@
|
||||
reachability=reachability,
|
||||
)
|
||||
|
||||
- def owner_execute(argv, input_text, timeout):
|
||||
+ def owner_execute(argv, input_text, timeout, *, credential_routes=()):
|
||||
return manager.execute(
|
||||
sandbox.sandbox_id,
|
||||
SandboxExecRequest(
|
||||
command=list(argv),
|
||||
consumer=consumer,
|
||||
- credential_route_refs=profile.credential_route_refs,
|
||||
+ credential_route_refs=list(credential_routes),
|
||||
stdin_text=input_text,
|
||||
timeout_seconds=timeout,
|
||||
),
|
||||
)
|
||||
|
||||
sandbox._owner_execute = owner_execute
|
||||
+ sandbox._owner_workload_execute = partial(
|
||||
+ owner_execute, credential_routes=tuple(profile.credential_route_refs)
|
||||
+ )
|
||||
+ sandbox._credential_route_refs = list(profile.credential_route_refs)
|
||||
sandbox._timeout_seconds = profile.limits.timeout_seconds or 600
|
||||
|
||||
session = selected_rein.start_session(
|
||||
--- a/src/glas_harness/transport.py
|
||||
+++ b/src/glas_harness/transport.py
|
||||
@@ -13,7 +13,7 @@
|
||||
import shlex
|
||||
import subprocess
|
||||
import uuid
|
||||
-from dataclasses import dataclass
|
||||
+from dataclasses import dataclass, replace
|
||||
from pathlib import Path, PurePosixPath
|
||||
from typing import Any, Callable, Literal, Sequence
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
pid: int | None = None
|
||||
ssh_target: str | None = None
|
||||
owner_execute: Callable | None = None
|
||||
+ owner_workload_execute: Callable | None = None
|
||||
timeout_seconds: int = 600
|
||||
|
||||
def command(self, argv: Sequence[str]) -> list[str]:
|
||||
@@ -94,6 +95,26 @@
|
||||
timeout=bounded_timeout,
|
||||
)
|
||||
|
||||
+ def run_workload(
|
||||
+ self,
|
||||
+ argv: Sequence[str],
|
||||
+ *,
|
||||
+ input_text: str | None = None,
|
||||
+ timeout: float | None = None,
|
||||
+ ) -> subprocess.CompletedProcess[str]:
|
||||
+ """Run the selected rein; only this path requests provider routes.
|
||||
+
|
||||
+ The gateway is trusted control-plane code. This separation narrows its
|
||||
+ credential delivery; it is not authentication of arbitrary Python callers.
|
||||
+ """
|
||||
+ if self.kind == "local_namespace":
|
||||
+ if self.owner_workload_execute is None:
|
||||
+ raise TransportError("workload requires an owner workload binding")
|
||||
+ return replace(self, owner_execute=self.owner_workload_execute).run(
|
||||
+ argv, input_text=input_text, timeout=timeout
|
||||
+ )
|
||||
+ return self.run(argv, input_text=input_text, timeout=timeout)
|
||||
+
|
||||
def resolve_executable(self, name: str, *, timeout: float = 15.0) -> str:
|
||||
if not name or "/" in name:
|
||||
raise TransportError("rein executable must be a command name, not a host path")
|
||||
@@ -169,9 +190,12 @@
|
||||
return ExecutionTransport(
|
||||
kind="local_namespace", workspace=str(workspace), pid=parsed_pid,
|
||||
owner_execute=sandbox._owner_execute,
|
||||
+ owner_workload_execute=sandbox._owner_workload_execute,
|
||||
timeout_seconds=sandbox._timeout_seconds,
|
||||
)
|
||||
if has_remote:
|
||||
+ if sandbox._credential_route_refs:
|
||||
+ raise TransportError("SSH transport has no owner credential delivery contract")
|
||||
if not ssh_target or not remote_dir:
|
||||
raise TransportError(
|
||||
"incomplete remote sandbox reachability: ssh and remote_dir are required"
|
||||
--- a/src/glas_harness/reins/rein_aharness.py
|
||||
+++ b/src/glas_harness/reins/rein_aharness.py
|
||||
@@ -109,7 +109,7 @@
|
||||
argv += ["--tool-profile", self.tool_profile]
|
||||
if self.budget_tokens:
|
||||
argv += ["--budget-tokens", str(self.budget_tokens)]
|
||||
- proc = transport.run(argv, timeout=session["timeout_seconds"])
|
||||
+ proc = transport.run_workload(argv, timeout=session["timeout_seconds"])
|
||||
ok = proc.returncode == 0
|
||||
output, events = self._split_stream_events(proc.stdout)
|
||||
last_result = session["last_result"] = parse_json_object(output)
|
||||
--- a/src/glas_harness/reins/rein_openweights.py
|
||||
+++ b/src/glas_harness/reins/rein_openweights.py
|
||||
@@ -91,7 +91,7 @@
|
||||
argv += ["--budget-tokens", str(self.budget_tokens)]
|
||||
if self.tool_profile:
|
||||
argv += ["--tool-profile", self.tool_profile]
|
||||
- proc = transport.run(argv, timeout=session["timeout_seconds"])
|
||||
+ proc = transport.run_workload(argv, timeout=session["timeout_seconds"])
|
||||
ok = proc.returncode == 0
|
||||
last_result = session["last_result"] = parse_json_object(proc.stdout)
|
||||
return ToolResult(
|
||||
--- a/tests/test_gateway.py
|
||||
+++ b/tests/test_gateway.py
|
||||
@@ -150,6 +150,7 @@
|
||||
assert "_owner_execute" not in sandbox.model_dump()
|
||||
transport = transport_from_sandbox(sandbox)
|
||||
transport.run(["printf", "%s", "non-secret"], timeout=30)
|
||||
+ transport.run_workload(["rein-aharness", "run"], timeout=30)
|
||||
path = transport.write_task_file({"title": "private prompt"})
|
||||
transport.remove_file(path)
|
||||
return super().start_session(profile, inputs, sandbox)
|
||||
@@ -164,14 +165,14 @@
|
||||
assert create_consumer.project == "exact-project"
|
||||
assert create_consumer.run_id == result.evidence.request_id
|
||||
assert create_consumer.session_id is None
|
||||
- assert manager.execute.call_count == 3
|
||||
+ assert manager.execute.call_count == 4
|
||||
for call in manager.execute.call_args_list:
|
||||
sandbox_id, command = call.args
|
||||
assert sandbox_id == "sbx1"
|
||||
assert command.consumer == create_consumer
|
||||
- assert command.credential_route_refs == refs
|
||||
+ assert command.credential_route_refs == (refs if command.command[0] == "rein-aharness" else [])
|
||||
assert command.timeout_seconds == 7
|
||||
- writer = manager.execute.call_args_list[1].args[1]
|
||||
+ writer = manager.execute.call_args_list[2].args[1]
|
||||
assert writer.stdin_text == '{"title": "private prompt"}'
|
||||
assert "private prompt" not in str(writer.command)
|
||||
manager.destroy.assert_called_once_with("sbx1")
|
||||
--- a/tests/test_transport.py
|
||||
+++ b/tests/test_transport.py
|
||||
@@ -173,3 +173,16 @@
|
||||
):
|
||||
with pytest.raises(TransportError, match="inside the selected sandbox"):
|
||||
transport.git_head()
|
||||
+
|
||||
+
|
||||
+def test_workload_refuses_missing_dedicated_owner_binding():
|
||||
+ transport = ExecutionTransport(kind="local_namespace", workspace="/work", owner_execute=lambda *args: None)
|
||||
+ with pytest.raises(TransportError, match="workload binding"):
|
||||
+ transport.run_workload(["true"])
|
||||
+
|
||||
+
|
||||
+def test_remote_transport_refuses_requested_credential_routes():
|
||||
+ sandbox = SandboxHandle(sandbox_id="remote", host="remote", reachability={"ssh":"agent@host", "remote_dir":"/work"})
|
||||
+ sandbox._credential_route_refs = ["provider-route"]
|
||||
+ with pytest.raises(TransportError, match="credential delivery"):
|
||||
+ transport_from_sandbox(sandbox)
|
||||
--- a/tests/test_rein_aharness.py
|
||||
+++ b/tests/test_rein_aharness.py
|
||||
@@ -80,18 +80,18 @@
|
||||
budget_tokens=1234,
|
||||
)
|
||||
transport = MagicMock(spec=ExecutionTransport)
|
||||
- transport.run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
|
||||
- session = {"task_file": "/tmp/task.json", "transport": transport, "timeout_seconds": 30}
|
||||
-
|
||||
- with patch.object(rein, "_bin", return_value="/usr/bin/agent-harness"):
|
||||
- result = rein.dispatch_tool(session, ToolCall(name="run_task"))
|
||||
-
|
||||
- argv = transport.run.call_args.args[0]
|
||||
+ transport.run_workload.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
|
||||
+ session = {"task_file": "/tmp/task.json", "transport": transport, "timeout_seconds": 30}
|
||||
+
|
||||
+ with patch.object(rein, "_bin", return_value="/usr/bin/agent-harness"):
|
||||
+ result = rein.dispatch_tool(session, ToolCall(name="run_task"))
|
||||
+
|
||||
+ argv = transport.run_workload.call_args.args[0]
|
||||
assert argv[:3] == ["/usr/bin/agent-harness", "run", "--task-file"]
|
||||
assert argv[argv.index("--model") + 1] == "claude-sonnet-4-6"
|
||||
assert argv[argv.index("--tool-profile") + 1] == "green-commit-only"
|
||||
assert argv[argv.index("--budget-tokens") + 1] == "1234"
|
||||
- assert transport.run.call_args.kwargs["timeout"] == 30
|
||||
+ assert transport.run_workload.call_args.kwargs["timeout"] == 30
|
||||
assert result.ok is True
|
||||
assert result.output == "ok"
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
def test_dispatch_tool_reports_failure() -> None:
|
||||
rein = ReinAharness()
|
||||
transport = MagicMock(spec=ExecutionTransport)
|
||||
- transport.run.return_value = MagicMock(returncode=1, stdout="", stderr="boom")
|
||||
+ transport.run_workload.return_value = MagicMock(returncode=1, stdout="", stderr="boom")
|
||||
session = {"task_file": "/tmp/task.json", "transport": transport, "timeout_seconds": 30}
|
||||
|
||||
with patch.object(rein, "_bin", return_value="/usr/bin/agent-harness"):
|
||||
@@ -167,14 +167,14 @@
|
||||
json.dumps({"stream_event": {"type": "system", "subtype": "hook_started"}}),
|
||||
json.dumps({"ok": True, "committed": True}, indent=2),
|
||||
]
|
||||
- transport.run.return_value = MagicMock(
|
||||
+ transport.run_workload.return_value = MagicMock(
|
||||
returncode=0, stdout="\n".join(stdout_lines), stderr=""
|
||||
)
|
||||
|
||||
with patch.object(rein, "_bin", return_value="/usr/bin/agent-harness"):
|
||||
result = rein.dispatch_tool(session, ToolCall(name="run_task"))
|
||||
|
||||
- argv = transport.run.call_args.args[0]
|
||||
+ argv = transport.run_workload.call_args.args[0]
|
||||
assert "--stream-tool-events" in argv
|
||||
assert len(result.events) == 2
|
||||
assert result.events[0]["message"]["content"][0]["name"] == "Read"
|
||||
@@ -186,13 +186,13 @@
|
||||
def test_dispatch_tool_no_stream_flag_by_default() -> None:
|
||||
rein = ReinAharness()
|
||||
transport = MagicMock(spec=ExecutionTransport)
|
||||
- transport.run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
|
||||
- session = {"task_file": "/tmp/task.json", "transport": transport, "timeout_seconds": 30}
|
||||
-
|
||||
- with patch.object(rein, "_bin", return_value="/usr/bin/agent-harness"):
|
||||
- result = rein.dispatch_tool(session, ToolCall(name="run_task"))
|
||||
-
|
||||
- argv = transport.run.call_args.args[0]
|
||||
+ transport.run_workload.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
|
||||
+ session = {"task_file": "/tmp/task.json", "transport": transport, "timeout_seconds": 30}
|
||||
+
|
||||
+ with patch.object(rein, "_bin", return_value="/usr/bin/agent-harness"):
|
||||
+ result = rein.dispatch_tool(session, ToolCall(name="run_task"))
|
||||
+
|
||||
+ argv = transport.run_workload.call_args.args[0]
|
||||
assert "--stream-tool-events" not in argv
|
||||
assert result.events == []
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
payload = {"ok": True}
|
||||
if reported is not None:
|
||||
payload["model"] = reported
|
||||
- transport.run.return_value = MagicMock(returncode=0, stdout=json.dumps(payload), stderr="")
|
||||
+ transport.run_workload.return_value = MagicMock(returncode=0, stdout=json.dumps(payload), stderr="")
|
||||
transport.git_head.return_value = "new-head"
|
||||
session = {"task_file": "/tmp/task.json", "transport": transport,
|
||||
"timeout_seconds": 30, "head_before": "old-head"}
|
||||
--- a/tests/test_rein_openweights.py
|
||||
+++ b/tests/test_rein_openweights.py
|
||||
@@ -76,13 +76,13 @@
|
||||
def test_dispatch_tool_invokes_rein_openweights_cli() -> None:
|
||||
rein = ReinOpenWeights()
|
||||
transport = MagicMock(spec=ExecutionTransport)
|
||||
- transport.run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
|
||||
+ transport.run_workload.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
|
||||
session = {"task_file": "/tmp/task.json", "transport": transport, "timeout_seconds": 30}
|
||||
|
||||
with patch.object(rein, "_bin", return_value="/usr/bin/rein-openweights"):
|
||||
result = rein.dispatch_tool(session, ToolCall(name="run_task"))
|
||||
|
||||
- argv = transport.run.call_args.args[0]
|
||||
+ argv = transport.run_workload.call_args.args[0]
|
||||
assert argv[:3] == ["/usr/bin/rein-openweights", "run", "--task-file"]
|
||||
assert "--no-hub" in argv
|
||||
assert result.ok is True
|
||||
@@ -97,13 +97,13 @@
|
||||
tool_profile="green-commit-only",
|
||||
)
|
||||
transport = MagicMock(spec=ExecutionTransport)
|
||||
- transport.run.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
|
||||
+ transport.run_workload.return_value = MagicMock(returncode=0, stdout="ok", stderr="")
|
||||
session = {"task_file": "/tmp/task.json", "transport": transport, "timeout_seconds": 30}
|
||||
|
||||
with patch.object(rein, "_bin", return_value="/usr/bin/rein-openweights"):
|
||||
rein.dispatch_tool(session, ToolCall(name="run_task"))
|
||||
|
||||
- argv = transport.run.call_args.args[0]
|
||||
+ argv = transport.run_workload.call_args.args[0]
|
||||
assert "--model" in argv
|
||||
assert "meta-llama/llama-3.1-70b-instruct" in argv
|
||||
assert argv[argv.index("--max-turns") + 1] == "8"
|
||||
@@ -114,7 +114,7 @@
|
||||
def test_dispatch_tool_reports_failure() -> None:
|
||||
rein = ReinOpenWeights()
|
||||
transport = MagicMock(spec=ExecutionTransport)
|
||||
- transport.run.return_value = MagicMock(returncode=1, stdout="", stderr="boom")
|
||||
+ transport.run_workload.return_value = MagicMock(returncode=1, stdout="", stderr="boom")
|
||||
session = {"task_file": "/tmp/task.json", "transport": transport, "timeout_seconds": 30}
|
||||
|
||||
with patch.object(rein, "_bin", return_value="/usr/bin/rein-openweights"):
|
||||
@@ -127,7 +127,7 @@
|
||||
def test_dispatch_tool_enforces_outer_timeout() -> None:
|
||||
rein = ReinOpenWeights()
|
||||
transport = MagicMock(spec=ExecutionTransport)
|
||||
- transport.run.side_effect = subprocess.TimeoutExpired("rein-openweights", 30)
|
||||
+ transport.run_workload.side_effect = subprocess.TimeoutExpired("rein-openweights", 30)
|
||||
session = {
|
||||
"task_file": "/tmp/task.json",
|
||||
"transport": transport,
|
||||
@@ -138,7 +138,7 @@
|
||||
with pytest.raises(subprocess.TimeoutExpired):
|
||||
rein.dispatch_tool(session, ToolCall(name="run_task"))
|
||||
|
||||
- assert transport.run.call_args.kwargs["timeout"] == 30
|
||||
+ assert transport.run_workload.call_args.kwargs["timeout"] == 30
|
||||
|
||||
|
||||
def test_end_session_detects_new_commit(tmp_path) -> None:
|
||||
@@ -183,7 +183,7 @@
|
||||
payload = {"ok": True}
|
||||
if reported is not None:
|
||||
payload["model"] = reported
|
||||
- transport.run.return_value = MagicMock(returncode=0, stdout=json.dumps(payload), stderr="")
|
||||
+ transport.run_workload.return_value = MagicMock(returncode=0, stdout=json.dumps(payload), stderr="")
|
||||
transport.git_head.return_value = "new-head"
|
||||
session = {"task_file": "/tmp/task.json", "transport": transport,
|
||||
"timeout_seconds": 30, "head_before": "old-head"}
|
||||
--- a/tests/test_rein_session_isolation.py
|
||||
+++ b/tests/test_rein_session_isolation.py
|
||||
@@ -19,7 +19,7 @@
|
||||
transport = MagicMock(spec=ExecutionTransport)
|
||||
transport.resolve_executable.return_value = "/runtime/rein"
|
||||
transport.git_head.return_value = f"head-{index}"
|
||||
- transport.run.return_value = MagicMock(returncode=0 if ok else 1, stderr="",
|
||||
+ transport.run_workload.return_value = MagicMock(returncode=0 if ok else 1, stderr="",
|
||||
stdout=json.dumps({"ok": ok, "tokens_spent": 10 + index,
|
||||
"model": f"model-{index}", "reason": None if ok else "failed"}))
|
||||
sessions.append({"transport": transport, "task_file": f"/workspace-{index}/task",
|
||||
--- /dev/null
|
||||
+++ b/scripts/prove-credential-command-scope.py
|
||||
@@ -0,0 +1,55 @@
|
||||
+"""No model/provider: actual bwrap transport proof with synthetic credentials."""
|
||||
+import json
|
||||
+import os
|
||||
+import sys
|
||||
+import tempfile
|
||||
+from pathlib import Path
|
||||
+from sandboxer.extensions.bwrap import BwrapExtension
|
||||
+from sandboxer.models import Profile
|
||||
+from glas_harness.contract import SandboxHandle
|
||||
+from glas_harness.transport import transport_from_sandbox
|
||||
+
|
||||
+
|
||||
+def main():
|
||||
+ with tempfile.TemporaryDirectory(prefix="glas-command-scope-") as tmp:
|
||||
+ root = Path(tmp)
|
||||
+ sentinel = root / "admin-sentinel"
|
||||
+ sentinel.write_text("synthetic-file-only")
|
||||
+ provider = root / "provider.py"
|
||||
+ provider.write_text("import os,subprocess,sys\nenv=os.environ.copy()\nenv['ANTHROPIC_API_KEY']='synthetic-provider-only'\nraise SystemExit(subprocess.call(sys.argv[sys.argv.index('--')+1:],env=env))\n")
|
||||
+ ext = BwrapExtension({"base_dir": str(root / "sandboxes"), "credential_routes": {
|
||||
+ "synthetic": {"profiles":["profile.scope-proof"], "projects":["scope-proof"], "actors":["agt"], "exec_argv":[sys.executable,str(provider),"--"]}}})
|
||||
+ context = {"actor":"agt", "project":"scope-proof", "run_id":"scope-proof-1", "profile_id":"profile.scope-proof"}
|
||||
+ # Inherited values are synthetic. Real owner credentials are never read.
|
||||
+ before = {key:os.environ.get(key) for key in ("BAO_TOKEN","VAULT_TOKEN","AWS_SECRET_ACCESS_KEY","SSH_AUTH_SOCK")}
|
||||
+ for key in before:
|
||||
+ os.environ[key] = "synthetic-inherited-only"
|
||||
+ handle = None
|
||||
+ try:
|
||||
+ handle = ext.provision(Profile(id="profile.scope-proof",version="1",extension="ext.bwrap"), {}, "localhost")
|
||||
+ ext.wait_ready(handle)
|
||||
+ sandbox = SandboxHandle(sandbox_id=handle["sandbox_id"],host="localhost",reachability={"pid":handle["pid"],"workspace_dir":handle["workspace_dir"]})
|
||||
+ calls=[]
|
||||
+ def execute(argv, input_text, timeout, routes=()):
|
||||
+ calls.append(list(routes))
|
||||
+ raw=ext.execute(handle,argv,credential_route_refs=list(routes),execution_context=context,timeout_seconds=timeout,max_output_bytes=4096,stdin_text=input_text)
|
||||
+ return type("Result",(),raw)()
|
||||
+ sandbox._owner_execute=execute
|
||||
+ sandbox._owner_workload_execute=lambda argv,stdin,timeout: execute(argv,stdin,timeout,["synthetic"])
|
||||
+ transport=transport_from_sandbox(sandbox)
|
||||
+ probe = "import os,pathlib,subprocess; assert not pathlib.Path("+repr(str(sentinel))+").exists(); assert all(k not in os.environ for k in "+repr(list(before))+"); assert subprocess.check_output(['python3','-c',\"import os; print(os.environ.get('BAO_TOKEN','absent'))\"]).strip()==b'absent'; "
|
||||
+ for workload in (False,True,False):
|
||||
+ check = probe + ("assert os.environ['ANTHROPIC_API_KEY']=='synthetic-provider-only'; print(os.environ['ANTHROPIC_API_KEY'])" if workload else "assert 'ANTHROPIC_API_KEY' not in os.environ; print('clean')")
|
||||
+ result=(transport.run_workload if workload else transport.run)(["python3","-c",check],timeout=10)
|
||||
+ assert result.returncode==0, result.stderr
|
||||
+ assert result.stdout == ("[REDACTED]\n" if workload else "clean\n")
|
||||
+ assert calls==[[],["synthetic"],[]]
|
||||
+ finally:
|
||||
+ teardown=ext.teardown(handle) if handle else {}
|
||||
+ for key,value in before.items():
|
||||
+ if value is None: os.environ.pop(key,None)
|
||||
+ else: os.environ[key]=value
|
||||
+ assert teardown.get("workspace_removed")=="True"
|
||||
+ print(json.dumps({"scope":"disposable bwrap via Glas transport; not interactive assignment", "model_calls":0,"real_credentials_read":False,"host_file_denied":True,"inherited_env_and_subprocess_denied":True,"credential_routes_by_command":calls,"provider_output_redacted":True,"subsequent_command_clean":True,"workspace_removed":True}))
|
||||
+
|
||||
+if __name__ == "__main__": main()
|
||||
--- a/docs/harness-contract.md
|
||||
+++ b/docs/harness-contract.md
|
||||
@@ -80,9 +80,17 @@
|
||||
not treated as sandbox availability.
|
||||
|
||||
The gateway binds local execution to the exact create-time consumer: actor,
|
||||
-project, and resolved request id as `run_id`. Every owner command forwards the
|
||||
-selected profile's value-free `credential_route_refs` and a timeout capped by
|
||||
-its limit. The binding is process-local and excluded from serialized handles.
|
||||
+project, and resolved request id as `run_id`. Housekeeping `run` commands
|
||||
+(executable lookup, task creation/removal, Git inspection and artifact capture)
|
||||
+request no credential routes. Only the selected rein's explicit `run_workload`
|
||||
+command forwards the selected profile's value-free `credential_route_refs`.
|
||||
+Both bindings cap timeouts by the profile limit and remain process-local,
|
||||
+excluded from serialized handles. An absent workload binding refuses rather
|
||||
+than using the housekeeping binding. SSH profiles with credential routes refuse
|
||||
+because SSH has no owner-mediated credential delivery contract. These bindings
|
||||
+narrow trusted gateway behavior; they do not authenticate arbitrary Python
|
||||
+callers, remove task-scoped MessagesOwner routes or establish isolation of
|
||||
+the caller's interactive runtime.
|
||||
An unbound local descriptor refuses execution; no consumer namespace entry or
|
||||
host execution fallback exists. Owner timeouts and truncated output fail closed.
|
||||
Generated task specs travel over stdin to an exclusive mode-0600 writer inside
|
||||
@@ -187,3 +195,10 @@
|
||||
Unknown remains null, including failure before provider execution. A reported
|
||||
alias does not attest an exact provider revision or conversation identifier.
|
||||
See `docs/evidence/2026-10-03-emergent-session-readiness-review.md`.
|
||||
+
|
||||
+
|
||||
+`scripts/prove-credential-command-scope.py` exercises the Glas transport against
|
||||
+a disposable real bwrap namespace with synthetic material. It checks host-file
|
||||
+absence, inherited credential-variable denial through a subprocess, explicit
|
||||
+workload-only provider delivery, output redaction, subsequent-command absence
|
||||
+and teardown. It uses no provider or model and cannot promote a runtime profile.
|
||||
--- a/workplans/GLAS-WP-0012-first-local-profile-production-proof.md
|
||||
+++ b/workplans/GLAS-WP-0012-first-local-profile-production-proof.md
|
||||
@@ -423,3 +423,28 @@
|
||||
available model/session metadata. The current lifecycle exposes no continuity
|
||||
event API. Existing owner-delivery blockers and all task/readiness states remain;
|
||||
local deterministic tests are not governed provider or isolation acceptance.
|
||||
+
|
||||
+
|
||||
+## 2026-10-16 NetKingdom isolated integration preparation
|
||||
+
|
||||
+Prepared, without owner deployment or readiness promotion, a Glas change that
|
||||
+separates credential-free housekeeping commands from explicit rein workload
|
||||
+execution. Both concrete adapters use the workload path; SSH selections with
|
||||
+credential routes refuse because that transport has no owner delivery contract.
|
||||
+Full regression: 141 passed. Real disposable bwrap/Glas transport proof passes
|
||||
+synthetic host-file denial, inherited credential environment and subprocess
|
||||
+denial, workload-only delivery, redaction, subsequent-command absence and
|
||||
+teardown. No provider/model call or real credential read occurred.
|
||||
+
|
||||
+Latest owner dependency sources supersede the stale missing-lane account:
|
||||
+September 27 native lanes were activated and revoked; corrected immutable
|
||||
+owner runtime is `818b1b7c…` (October 15 startup proof), not yet installed or
|
||||
+admitted on Railiance. REINAH-WP-0008-T07 retains memo lifecycle acceptance,
|
||||
+installation, rehearsal, renewal/pin updates, native approvals and bounded
|
||||
+successful model/tool/commit acceptance. Any subsequent build containing this
|
||||
+patch has a different source/artifact pin requiring owner admission.
|
||||
+
|
||||
+This is a reviewable patch in Custodian's NetKingdom change bundle; no canonical
|
||||
+source synchronization or workplan task completion is implied. CUST-WP-0073
|
||||
+continues to require admission of the actual interactive assignment, including
|
||||
+its existing host credential and external tool paths, not a disposable fixture.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"exit_code": 0,
|
||||
"stdout": "",
|
||||
"stderr": ""
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
{
|
||||
"owner": "glas-harness",
|
||||
"canonical_source_modified": false,
|
||||
"isolated_tree": "/tmp/netkingdom-agent-isolation-20261016/glas-harness",
|
||||
"sources": [
|
||||
{
|
||||
"repo": "glas-harness",
|
||||
"head": "dc88f302044c7c2b41795958527e64be8a83e3ab",
|
||||
"status": "M .custodian-brief.md\n M WORK-RECORDS.md\n"
|
||||
},
|
||||
{
|
||||
"repo": "sand-boxer",
|
||||
"head": "53f800c4f7a9c91469834d04ef6f58e906b0bd49",
|
||||
"status": ""
|
||||
},
|
||||
{
|
||||
"repo": "rein-aharness",
|
||||
"head": "be5ef400861a5e31a31f29d173990c7a143fb189",
|
||||
"status": ""
|
||||
}
|
||||
],
|
||||
"changes": [
|
||||
{
|
||||
"path": "src/glas_harness/contract.py",
|
||||
"baseline_sha256": "ad5f63b4a22ff65a7e9bf956ea65b04d55fe3d7268bbbdf6956aabd118ce4238",
|
||||
"candidate_sha256": "db9a3540221229de9e4a6ec04216f7bea9e99136f26931418a994c37b6eb21df"
|
||||
},
|
||||
{
|
||||
"path": "src/glas_harness/gateway.py",
|
||||
"baseline_sha256": "d6c7636b7d1fb1d16fa66f7105ff18a769414eb4c99f21e62557c8b04c3fe9a8",
|
||||
"candidate_sha256": "5e998bfa53ade5dafbae799dc04b77031b6414e2af49797f45727d59d7397d42"
|
||||
},
|
||||
{
|
||||
"path": "src/glas_harness/transport.py",
|
||||
"baseline_sha256": "91f5a5adeb7a27d0a828eb0c801fd2381f7d272a521cd5f563d04d5fa022d0f2",
|
||||
"candidate_sha256": "fa1686298b7403390396fa2786a2b6711609980a4a0fca5f2e38f250ae640b5c"
|
||||
},
|
||||
{
|
||||
"path": "src/glas_harness/reins/rein_aharness.py",
|
||||
"baseline_sha256": "67bf8369501c96227cc5f89158ad4c41cf2c86bd7f4235f162e7da8cadcc8f05",
|
||||
"candidate_sha256": "2f725a4dbda311bf2626959dd439ab9d70e82021496fecf61d9319091873c7a7"
|
||||
},
|
||||
{
|
||||
"path": "src/glas_harness/reins/rein_openweights.py",
|
||||
"baseline_sha256": "7da551d0bfc42588897ecae6145bdd90813dd64611ff818a03ea5144ebd8dd90",
|
||||
"candidate_sha256": "e0f7261cafed7f4fe67d34bf27cf431dfeca18536c1cbd84626fa25017cd44ad"
|
||||
},
|
||||
{
|
||||
"path": "tests/test_gateway.py",
|
||||
"baseline_sha256": "0197543fda862a1c6cb1e2c974ef4ea7969017fe8ca3f212e5c0d7e8d13308de",
|
||||
"candidate_sha256": "8ecae8010b1c6182591b12b335af8f2f6e6e08366b646eb138a4ef08679b6634"
|
||||
},
|
||||
{
|
||||
"path": "tests/test_transport.py",
|
||||
"baseline_sha256": "27b3d0e2d78e128e2265467835d92d7ab9dcc06637e4ffc236bdc791488703c9",
|
||||
"candidate_sha256": "50c86170d9dc9cecbef49886a733f6ca78b5a8be5d67701b521d98bd81dfaabe"
|
||||
},
|
||||
{
|
||||
"path": "tests/test_rein_aharness.py",
|
||||
"baseline_sha256": "3d34a6dfdb8962263d254e5f40f64147af7da15f6d1ac377854ff688cd7a9032",
|
||||
"candidate_sha256": "5dc802830f902d4e4a502c981e7626a8282b6dbd4e9829659f9efedfcf1dd16e"
|
||||
},
|
||||
{
|
||||
"path": "tests/test_rein_openweights.py",
|
||||
"baseline_sha256": "1b0c4d16e0c885f6dd0f25dac3de3372a93ab3d65c1432b0eebac045625b4665",
|
||||
"candidate_sha256": "c71c66976982e5375dd2513e2f392e8884305be1690856a37fa5a78075aee222"
|
||||
},
|
||||
{
|
||||
"path": "tests/test_rein_session_isolation.py",
|
||||
"baseline_sha256": "0c6a875cd8529b3f084a3b055a1edd86af0703489ced4669671dff817c296a84",
|
||||
"candidate_sha256": "699b554f5889df5afee2b556e50602876f849ef7fde1dd612408a7acfc597dc1"
|
||||
},
|
||||
{
|
||||
"path": "scripts/prove-credential-command-scope.py",
|
||||
"baseline_sha256": null,
|
||||
"candidate_sha256": "acc7ca6e29b3ace36c734ac3478f48ba901805e9a9acca98bd1ee8820f6fa1a8"
|
||||
},
|
||||
{
|
||||
"path": "docs/harness-contract.md",
|
||||
"baseline_sha256": "96e274ea547844e5062eab96c05231bc4d9ffb81306f1e9c5e3cc1ee2c8c87f9",
|
||||
"candidate_sha256": "0bc3a43ff9a1b72c4668d6ca8e936163f865c2b302c920f7ede6c5e00d59a39d"
|
||||
},
|
||||
{
|
||||
"path": "workplans/GLAS-WP-0012-first-local-profile-production-proof.md",
|
||||
"baseline_sha256": "83978dc6cfdc836cca7b5cd12b03b878c944d67598f9505a196701fa534f6cfc",
|
||||
"candidate_sha256": "881ab0f5ba3bb43f5404c8f4cd23f9628e014992ba88fc13f541f0fa8b7b1eaa"
|
||||
}
|
||||
],
|
||||
"patch_sha256": "83eeeb0017603aabae0ff49a23f96248f677f6c9f3e2c01b22f6d0416fca69c7",
|
||||
"dependency_sources": [
|
||||
{
|
||||
"path": "/home/worsch/sand-boxer/src/sandboxer/extensions/bwrap.py",
|
||||
"sha256": "eff580a498f3609d3e07e41440f926016fd46a7b0c6273c37445921ea6e03cb0"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/sand-boxer/src/sandboxer/extensions/bwrap_runner.py",
|
||||
"sha256": "c83cdd2758bae58cd5d5694f5d6752736f59d479cfc6e9f0f8841c1f5596cba6"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/sand-boxer/workplans/SAND-WP-0015-bwrap-runtime-and-private-state.md",
|
||||
"sha256": "230d1ef37aa9c2023335a617492a72678c4671a8bc82d5577e9d7a7cea6e8fe4"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/rein-aharness/rein_aharness/adapter.py",
|
||||
"sha256": "de55aec9a19c5e17c59febb7e0481400ab4a47880f54bf8ca46b6dd8ec8fcf17"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/rein-aharness/rein_aharness/messages_owner.py",
|
||||
"sha256": "14129bc0ae9611d5b2e04e277a292b2ee09b32f18d896b94741038fdab867abd"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/rein-aharness/workplans/REINAH-WP-0008-simpler-metered-retry.md",
|
||||
"sha256": "3f46cf1f278e35be994772cb2d7e6d95f570fb5bfba28c6b55dc89b663d25f89"
|
||||
},
|
||||
{
|
||||
"path": "/home/worsch/the-custodian/.kaizen/agents/custodian-codex/supervision.json",
|
||||
"sha256": "407340493ffe584e9a245911d01f60e8f9ec1478f84478388f0210fd0c8b1a6d"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"command": "PYTHONPATH=/tmp/netkingdom-agent-isolation-20261016/glas-harness/src:/tmp/netkingdom-agent-isolation-20261016/sand-boxer/src /home/worsch/glas-harness/.venv/bin/python -m pytest /tmp/netkingdom-agent-isolation-20261016/glas-harness/tests -q",
|
||||
"exit_code": 0,
|
||||
"stdout": "141 passed in 10.89s",
|
||||
"python": "/home/worsch/glas-harness/.venv/bin/python",
|
||||
"native_provider_requests": 0
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Approval exact-binding confirmation repair
|
||||
|
||||
Approval owner source preparation from clean source commit
|
||||
`477c683991fda3c01e2af98a18f5a8719ee9dbc9`, in an isolated temporary checkout.
|
||||
Pending canonical WP0004 closure changes, generated UUIDs and unrelated dirt
|
||||
remain untouched. `owner-dirty-baseline.patch` preserves that observed state;
|
||||
`implementation.patch` applies cleanly on top of it.
|
||||
|
||||
Reproduction on unchanged production client source: canonical binding dict
|
||||
equality accepts nested True/1, False/0, 1/1.0 and 1/True substitutions. Missing,
|
||||
wrong and boolean served native digests also went unchecked. Seven regression
|
||||
cases failed their expected refusal, while a valid nested/key-order case passed.
|
||||
This is client confirmation ambiguity, not proof of a production exploit or
|
||||
native acceptance failure.
|
||||
|
||||
The minimal fix reuses Approval's existing native `binding_digest`: recompute
|
||||
the served five-field act digest, compare it with the expected act digest, and
|
||||
require the served digest field to match. No new digest, API schema, authority,
|
||||
service or approval contract is introduced. Native binding, approval-free PDP
|
||||
and full consumed request digests retain their separate meanings.
|
||||
|
||||
Twelve added cases cover four Python-equal/JSON-distinct substitutions with
|
||||
stale and recomputed served digests, three missing/invalid digest cases, and valid
|
||||
nested types plus reordered keys. The final confirmation suite passes 36 tests.
|
||||
Final focused command including consume provenance and the unavailable-server
|
||||
case passes 44 tests. A broader earlier run passed 101 and failed one existing
|
||||
200ms transport test because isolated child startup reached the total deadline
|
||||
instead of returning its expected `unreachable` text; both outcomes fail closed.
|
||||
Its focused retry passed. That transient failure is retained in validation.json.
|
||||
|
||||
New bounded low-risk source task: APPROVAL-WP-ADHOC-2026-10-16-T01. WP0004 closure
|
||||
is not reopened, and existing WP0002 native residuals remain open. This source
|
||||
preparation is not formal delegated-owner assent, signed approval or native
|
||||
acceptance. No live credential, approval service, OpenBao or protected operation
|
||||
was used. Coordinator review/integration/publication/sync remain pending.
|
||||
|
||||
After integration, Platform must pin the actual integrated confirmation client
|
||||
source. Prepared `approval_engine/execution_confirmation.py` SHA256 is
|
||||
`afd4383d6785b12be9650d1cb3ef84ccbb1b57cd271d38d55e75875c46e85a79`.
|
||||
Its actual mediator must retain independently authenticated provider transport,
|
||||
exact fresh claim/signed decision/body/operation checks and the same live one-shot
|
||||
journal admission. This historical confirmation fact never grants execution or
|
||||
retries uncertain protected work. Native validation must use the final pinned
|
||||
client, and prior disposable proof must state which earlier client it exercised.
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
diff --git a/approval_engine/execution_confirmation.py b/approval_engine/execution_confirmation.py
|
||||
index 2fef8b9..fa7daaa 100644
|
||||
--- a/approval_engine/execution_confirmation.py
|
||||
+++ b/approval_engine/execution_confirmation.py
|
||||
@@ -8,7 +8,7 @@ from datetime import datetime
|
||||
from typing import Any
|
||||
from urllib.parse import quote
|
||||
|
||||
-from .binding import DIGEST_RE, canonical_binding
|
||||
+from .binding import DIGEST_RE, binding_digest
|
||||
from .pep import ApprovalHTTPClient, ApprovalProtocolError, _ID_RE
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ class ExecutionConfirmationClient(ApprovalHTTPClient):
|
||||
if not isinstance(expected_pdp_digest, str) or not DIGEST_RE.fullmatch(expected_pdp_digest):
|
||||
raise ApprovalProtocolError("exact approval-free PDP digest is required")
|
||||
try:
|
||||
- expected = canonical_binding(expected_binding)
|
||||
+ expected = binding_digest(expected_binding)
|
||||
except (TypeError, ValueError, KeyError):
|
||||
raise ApprovalProtocolError("expected act binding is invalid") from None
|
||||
obj = self._request("GET", f"/v1/approvals/{quote(approval_id, safe='')}")
|
||||
@@ -81,7 +81,11 @@ class ExecutionConfirmationClient(ApprovalHTTPClient):
|
||||
raise ApprovalProtocolError("consumption object is not a decision")
|
||||
served = obj.get("binding")
|
||||
try:
|
||||
- same_act = isinstance(served, dict) and canonical_binding(served) == expected
|
||||
+ same_act = (
|
||||
+ isinstance(served, dict)
|
||||
+ and binding_digest(served) == expected
|
||||
+ and served.get("digest") == expected
|
||||
+ )
|
||||
except (TypeError, ValueError, KeyError):
|
||||
same_act = False
|
||||
if not same_act or served.get("pdp_digest") != expected_pdp_digest:
|
||||
diff --git a/tests/test_execution_confirmation.py b/tests/test_execution_confirmation.py
|
||||
index fed3f83..9892ff3 100644
|
||||
--- a/tests/test_execution_confirmation.py
|
||||
+++ b/tests/test_execution_confirmation.py
|
||||
@@ -127,3 +127,50 @@ def test_denied_read_never_represents_confirmation(engine, app, tmp_path):
|
||||
raise HTTPError(req.full_url, status, "refused", {}, None)
|
||||
with pytest.raises(ApprovalProtocolError, match="unauthorized"):
|
||||
confirmer(client(tmp_path, opener), obj)
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize("original,substitute", [(True, 1), (False, 0), (1, 1.0), (1, True)])
|
||||
+@pytest.mark.parametrize("recompute_served_digest", [False, True])
|
||||
+def test_confirmation_refuses_python_equal_json_distinct_target(
|
||||
+ engine, tmp_path, original, substitute, recompute_served_digest
|
||||
+):
|
||||
+ expected = binding(target={"nested": [{"value": original}]})
|
||||
+ obj = engine.create(expected, validity(), pdp_digest=FREE, pdp_path=True,
|
||||
+ human_control=True)
|
||||
+ engine.add_entry(obj.id, "human:founder", principal_type="human")
|
||||
+ engine.consume(obj.id, FULL, decision_id="decision:original")
|
||||
+ result = engine.get(obj.id).as_dict()
|
||||
+ result["binding"]["target"] = {"nested": [{"value": substitute}]}
|
||||
+ if recompute_served_digest:
|
||||
+ from approval_engine.binding import binding_digest
|
||||
+ result["binding"]["digest"] = binding_digest(result["binding"])
|
||||
+ assert result["binding"]["target"] == expected["target"]
|
||||
+ with pytest.raises(ApprovalProtocolError, match="binding does not match"):
|
||||
+ client(tmp_path, lambda *_a, **_k: Response(result)).confirm_consumption(
|
||||
+ obj.id, FULL, "decision:original", expected_binding=expected,
|
||||
+ expected_pdp_digest=FREE)
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize("digest", [None, "sha256:" + "00" * 32, True])
|
||||
+def test_confirmation_requires_served_native_digest_correspondence(engine, tmp_path, digest):
|
||||
+ obj = consumed(engine)
|
||||
+ result = engine.get(obj.id).as_dict()
|
||||
+ if digest is None:
|
||||
+ result["binding"].pop("digest")
|
||||
+ else:
|
||||
+ result["binding"]["digest"] = digest
|
||||
+ with pytest.raises(ApprovalProtocolError, match="binding does not match"):
|
||||
+ confirmer(client(tmp_path, lambda *_a, **_k: Response(result)), obj)
|
||||
+
|
||||
+
|
||||
+def test_confirmation_accepts_nested_json_types_and_key_order(engine, tmp_path):
|
||||
+ expected = binding(target={"b": [True, False, 1, 1.0], "a": {"unicode": "ä"}})
|
||||
+ obj = engine.create(expected, validity(), pdp_digest=FREE, pdp_path=True,
|
||||
+ human_control=True)
|
||||
+ engine.add_entry(obj.id, "human:founder", principal_type="human")
|
||||
+ engine.consume(obj.id, FULL, decision_id="decision:original")
|
||||
+ result = engine.get(obj.id).as_dict()
|
||||
+ result["binding"]["target"] = dict(reversed(list(expected["target"].items())))
|
||||
+ assert client(tmp_path, lambda *_a, **_k: Response(result)).confirm_consumption(
|
||||
+ obj.id, FULL, "decision:original", expected_binding=expected,
|
||||
+ expected_pdp_digest=FREE)["approval_id"] == obj.id
|
||||
diff --git a/workplans/ADHOC-2026-10-16.md b/workplans/ADHOC-2026-10-16.md
|
||||
new file mode 100644
|
||||
index 0000000..cd19170
|
||||
--- /dev/null
|
||||
+++ b/workplans/ADHOC-2026-10-16.md
|
||||
@@ -0,0 +1,44 @@
|
||||
+---
|
||||
+id: APPROVAL-WP-ADHOC-2026-10-16
|
||||
+type: workplan
|
||||
+title: "Repair exact consume-confirmation native binding comparison"
|
||||
+domain: infotech
|
||||
+repo: approval-engine
|
||||
+status: finished
|
||||
+flavor: implementation
|
||||
+owner: codex
|
||||
+topic_slug: netkingdom
|
||||
+created: "2026-10-16"
|
||||
+updated: "2026-10-16"
|
||||
+---
|
||||
+
|
||||
+## Refuse Python-equal JSON-distinct consumed act bindings
|
||||
+
|
||||
+```task
|
||||
+id: APPROVAL-WP-ADHOC-2026-10-16-T01
|
||||
+status: done
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Bounded source repair demanded by the CUST-WP-0080 strict execution-seam proof.
|
||||
+INTENT basis: exact binding and authenticated consumption evidence; no authority
|
||||
+or native grant is added. Existing canonical native-five-field digest is reused.
|
||||
+ExecutionConfirmationClient now recomputes the served and expected digests and
|
||||
+requires the served digest field to correspond, replacing Python dict equality
|
||||
+which accepted nested True/1, False/0 and integer/float substitutions.
|
||||
+
|
||||
+Before repair seven regression cases wrongly confirmed (four target type
|
||||
+substitutions and three absent/wrong/type-invalid served digest fields); valid
|
||||
+nested types with reordered keys passed. The repair retains this failure history.
|
||||
+Twelve added regression cases cover exact native JSON correspondence, including
|
||||
+substitutions with both retained and recomputed served digests, without
|
||||
+changing the native digest algorithm, request/PDP digest contract or API schema.
|
||||
+Canonical integration/publication and the Platform mediator's final provider
|
||||
+client source pin remain with the coordinator; preparation does not claim them.
|
||||
+
|
||||
+Existing APPROVAL-WP-0004 closure and generated identifiers are untouched.
|
||||
+APPROVAL-WP-0002-T01/T04/T05 remain existing live native acceptance obligations;
|
||||
+none is promoted or completed here. No production auth, approval mutation,
|
||||
+credential, deployment or backend operation was used. This completed low-risk
|
||||
+local source repair introduces no residual native obligation beyond those
|
||||
+existing records and the coordinator's integration of this exact artifact.
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
diff --git a/.custodian-brief.md b/.custodian-brief.md
|
||||
index 4e8f8d3..ee8ca96 100644
|
||||
--- a/.custodian-brief.md
|
||||
+++ b/.custodian-brief.md
|
||||
@@ -2,7 +2,7 @@
|
||||
# Custodian Brief — approval-engine
|
||||
|
||||
**Domain:** infotech
|
||||
-**Last synced:** 2026-10-11 13:46 UTC
|
||||
+**Last synced:** 2026-10-16 06:27 UTC
|
||||
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
|
||||
|
||||
## Active Workstreams
|
||||
@@ -20,7 +20,7 @@ Progress: 3/6 done | workplan_id: `4fa25ad5-f5d0-5592-aa59-085f8ee3edaf`
|
||||
|
||||
## Inbox Hygiene
|
||||
|
||||
-**Stale unread:** 10 message(s) older than 3 day(s) — triage at session start.
|
||||
+**Stale unread:** 11 message(s) older than 3 day(s) — triage at session start.
|
||||
**Missing thread_id:** 9 unread message(s) lack supersession chains.
|
||||
- ! informed-decision: INFD-WP-0005: service-requested approvals from informed-decision intake (two small asks) `59c5ee82`
|
||||
- ! flex-auth: FLEX-WP-0020 handoff request (consumer) `2523510b`
|
||||
diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md
|
||||
index 2110857..f7cd8df 100644
|
||||
--- a/WORK-RECORDS.md
|
||||
+++ b/WORK-RECORDS.md
|
||||
@@ -13,6 +13,7 @@
|
||||
| workplan | APPROVAL-WP-0001 | finished | — | workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md |
|
||||
| workplan | APPROVAL-WP-0002 | blocked | — | workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md |
|
||||
| workplan | APPROVAL-WP-0003 | finished | — | workplans/APPROVAL-WP-0003-reissue-expired-approvals.md |
|
||||
+| workplan | APPROVAL-WP-0004 | finished | — | workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md |
|
||||
| task | APPROVAL-WP-ADHOC-2026-10-10-T01 | done | — | workplans/ADHOC-2026-10-10.md |
|
||||
| task | APPROVAL-WP-ADHOC-2026-10-11-T01 | done | — | workplans/ADHOC-2026-10-11.md |
|
||||
| task | APPROVAL-WP-ADHOC-2026-10-11-T02 | done | — | workplans/ADHOC-2026-10-11.md |
|
||||
@@ -32,6 +33,8 @@
|
||||
| task | APPROVAL-WP-0002-T05 | wait | — | workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md |
|
||||
| task | APPROVAL-WP-0002-T06 | done | — | workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md |
|
||||
| task | APPROVAL-WP-0003-T01 | done | — | workplans/APPROVAL-WP-0003-reissue-expired-approvals.md |
|
||||
+| task | APPROVAL-WP-0004-T01 | done | — | workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md |
|
||||
+| task | APPROVAL-WP-0004-T02 | done | — | workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md |
|
||||
| intake | APPROVAL-IN-0001 | open | blue | intakes/intakes.md |
|
||||
| intake | APPROVAL-IN-0002 | closed | blue | intakes/intakes.md |
|
||||
| intake | APPROVAL-IN-0003 | closed | — | intakes/intakes.md |
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"exit_code": 0,
|
||||
"stderr": "",
|
||||
"pending_WP0004_closure_and_UUIDs_preserved": true
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"workplan_id": "4fa25ad5-f5d0-5592-aa59-085f8ee3edaf",
|
||||
"id": "03ffe09e-b794-43c8-8010-9a55425a4351",
|
||||
"topic_id": null,
|
||||
"task_id": null,
|
||||
"decision_id": null,
|
||||
"event_type": "note",
|
||||
"summary": "Approval owner bounded exact-confirmation source repair: reproduced Python-equal JSON-distinct target acceptance and unchecked served native digest, then reused native binding_digest recomputation and exact field correspondence.12 new cases;36confirmation tests and44focused tests passed. Intermediate200ms transport timing failure retained, focused retry passed. Patch/manifest under approval-confirmation; pending canonical integration/final Platform pin. Existing WP0004 closure/UUIDs and native WP0002 obligations unchanged; no live auth/effect.",
|
||||
"detail": null,
|
||||
"author": "codex",
|
||||
"session_id": null,
|
||||
"created_at": "2026-10-16T06:29:09.227024Z",
|
||||
"workstream_id": "4fa25ad5-f5d0-5592-aa59-085f8ee3edaf"
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
# Thin Access Approval fact reader — owner source contract proposal
|
||||
|
||||
Owner scope: Approval Engine / PIP. Work: APPROVAL-WP-0005-T01, selected source
|
||||
subset of APPROVAL-WP-0002-T01/T05 under CUST-WP-0080. This interprets the existing
|
||||
claim schema 0.1, not a new API/schema/signature or policy grant. Access's B2
|
||||
implementation stays DENY until its separately reviewed complete successor.
|
||||
|
||||
## Authoritative source and guarantee
|
||||
|
||||
Only server-installed, independently admitted provider origin and transport may
|
||||
GET `/v1/approvals/{id}/claim`, with least-scope `approval:read` credential and
|
||||
exact admitted store tenant. No caller URL/opener/key/verified flag, redirects,
|
||||
environment proxy, caller JSON or public same-channel key bootstrap can supply
|
||||
this provenance. Owner transport must enforce total deadline/size, exact 200
|
||||
application/json UTF-8, no encoding, duplicate-key/nonfinite/malformed JSON
|
||||
refusal, no retry and bounded errors without bearer/body leakage. Provider
|
||||
unavailability refuses this source join. Never invoke a credential-carrying
|
||||
Python subprocess as the Go adapter; reuse Go's existing transport boundary.
|
||||
|
||||
At the provider observation, `valid_now: true` AND `binding.human_control: true`
|
||||
means the configured required count of distinct entries is met and every entry
|
||||
has verified `principal_type=human`. `/entries` obtains identities/types only from
|
||||
its authenticator, rejects non-human binds for this declaration, and persists
|
||||
UNIQUE(approval_id, subject_id); `_evaluate` repeats the threshold/human check.
|
||||
It does NOT mean required_count is two, requester/actor/principal is human,
|
||||
directory-derived tenant membership, a particular MFA/assurance level, policy
|
||||
eligibility, uncompromised source, native issuer admission, or continuing
|
||||
validity after the observation. Consumer trusts this owner evaluation instead
|
||||
of fetching identities/counting approvers or revalidating human JWTs. Native
|
||||
issuer/profile acceptance is still T01, not established by synthetic vectors.
|
||||
|
||||
## Minimal typed fact validation
|
||||
|
||||
The submitted claim and fetched observation must each satisfy existing schema
|
||||
0.1 with exact JSON types; `human_control` must be present and exactly true on
|
||||
this lane despite being historically optional in the generic schema. Require
|
||||
schema_version=0.1, kind=approval-claim, issuer=approval-engine, exact expected
|
||||
approval UUID, state=valid, valid_now=true, consumed=false, reason_code=ok,
|
||||
pdp_path=true and non-null lowercase sha256 pdp_digest. Reject decision-shaped
|
||||
keys and unknown schema members (except optional informational yields_to).
|
||||
The signed/requesting service must never become a human approver by inference.
|
||||
|
||||
At one captured UTC validation instant now, require zoned RFC3339 timestamps,
|
||||
positive integral ttl_seconds, observed_at<=now<not_after and
|
||||
not_after=observed_at+ttl_seconds. Require not_before<=now<expires_at. Do not
|
||||
invent skew or extend TTL; a separately admitted clock policy is needed to
|
||||
relax the zero-skew default. The provider default TTL is 30 seconds but source
|
||||
configuration may differ; an Access-owned maximum may only shorten it.
|
||||
|
||||
Compare exact five-field act against the owner-derived normalized proposed act,
|
||||
including all nested target values; no action vocabulary translation is owned
|
||||
here. Recompute native binding.digest from those five fields and require exact
|
||||
served and expected digest correspondence. Match pdp_digest to Access's actual
|
||||
ApprovalBindingDigest over the exact normalized original request (excludes only
|
||||
top-level context.approval). Preserve execution_contract and all other context.
|
||||
Never substitute full request_digest, native act digest or a guessed Go digest.
|
||||
The PDP digest proves correspondence, not decision authenticity or approval
|
||||
source authenticity. Registry/normalization changes must fail or reissue.
|
||||
|
||||
## Original request is immutable; fresh observation stays out-of-band
|
||||
|
||||
The original submitted context.approval must remain byte/value-equivalent under
|
||||
Access's existing canonical request rules. Do not replace it, update freshness,
|
||||
change binding/declarations, or manufacture an evaluated refreshed request.
|
||||
Validate its own original freshness/validity too; a newly fetched observation
|
||||
cannot rescue a stale original claim. Require all non-freshness authority facts
|
||||
to match between the submitted and fetched claims: schema/kind/issuer/id,
|
||||
state/valid_now/consumed/reason_code, binding and validity. Optional yields_to
|
||||
is informational only. Each native binding is independently recomputed.
|
||||
Freshness is validated separately on both observations; a later observation
|
||||
may have a later freshness window without changing the signed request.
|
||||
|
||||
Return a package-private/server-created typed observation handle only after
|
||||
these checks. Access may bind its source identity/revision, observed_at,
|
||||
not_after, fetched-claim content digest and validation time into owner-produced
|
||||
signed provenance separately from unchanged request_digest and
|
||||
approval_binding_digest. This does not require changing the Approval schema;
|
||||
Access owns the minimal existing provenance representation and signer contract.
|
||||
An HTTP caller cannot instantiate this handle or set its verified state.
|
||||
|
||||
Suggested Go boundary, for Access to implement in its existing process:
|
||||
`ApprovalFactSource.Observe(ctx, approvalID) (Observation, error)` reads only
|
||||
provider bytes plus source provenance; a separate package-private validator
|
||||
joins Observation with the immutable normalized request, submitted claim and
|
||||
captured validation time. The handle retains detached raw provider bytes and
|
||||
strict parsed numbers (json.Number), not caller-installable validation flags.
|
||||
Neither this reader nor its validator evaluates policy. Access's existing
|
||||
single policy evaluator consumes the verified factual handle without rewriting
|
||||
the request. Callers may not supply a ready-made Observation on the HTTP body.
|
||||
|
||||
Effective input deadline is the minimum of ORIGINAL submitted freshness,
|
||||
fetched freshness, approval expiry, authenticated caller expiry and operation/
|
||||
decision deadlines. No equality at an expiry boundary is accepted. Refetching
|
||||
does not renew the original full request. Revocation/use may race a read; the
|
||||
PEP still performs one original exact consume CAS before effect and retains
|
||||
the same live Platform journal admission, deadlines and unknown-outcome rules.
|
||||
Access never consumes or executes. Historical confirm_consumption cannot
|
||||
substitute for this fresh predecision source validation.
|
||||
|
||||
## Go canonicalization and golden vectors
|
||||
|
||||
`examples/access-claim-reader-vectors.json` contains producer-generated native
|
||||
canonical UTF-8 bytes/digests and source lifecycle observations, plus explicitly
|
||||
mutated refusal fixtures. Python dict equality and Go decoding numbers into
|
||||
float64 lose authority distinctions: True/1, 1/1.0 and integers above 2^53 differ.
|
||||
Go default json.Marshal additionally escapes HTML/U+2028 differently. Retain
|
||||
number tokens with UseNumber and implement/test compatibility with Approval's
|
||||
existing Python json.dumps(sort_keys=True,separators=(',', ':'),ensure_ascii=False)
|
||||
format; do not silently normalize types or hash whole binding/freshness.
|
||||
UseNumber preserves number tokens but is NOT a canonicalizer: producer parsing
|
||||
of `1e-7` emits `1e-07`, `1e20` emits `1e+20`, `1.0` remains distinct from `1`,
|
||||
and negative floating zero emits `-0.0`. A signed typed Go envelope alone does
|
||||
not preserve or authenticate Python's original numeric representation.
|
||||
|
||||
These finite vectors are a minimum, not proof of a complete Python float/string
|
||||
port. A thin pilot may fail closed on unsupported numeric/string domains only
|
||||
if its actual owner-compiled operation contract excludes those values and Access
|
||||
declares/tests that boundary. It may not silently change the global Approval
|
||||
algorithm or accept a digest without checking the proposed act. Required vectors
|
||||
cover bool/int/float distinction, large integer, Unicode/HTML/U+2028, reordered
|
||||
keys, nested containers, exponent formatting and negative floating zero. These
|
||||
are authoritative producer vectors for the domains shown, not arbitrary new
|
||||
restrictions on current pilot metadata. No currently authoritative narrow numeric
|
||||
restriction was established by this review, so it cannot be presumed. If a
|
||||
small shared producer-owned Go canonicalization helper becomes necessary, scope
|
||||
it explicitly to the actual consumer need and test against these producer bytes;
|
||||
do not invent a second unverified serialization algorithm in the adapter. This
|
||||
review provides vectors only, not that runtime implementation. The Go owner must
|
||||
return exact accepted/refused vector results and transport negatives
|
||||
(redirect/proxy/duplicate/size/deadline).
|
||||
|
||||
The superseded service-http-join-proposal-r1 B3 refreshed-evaluation wording is
|
||||
changes-requested for this immutable-context path. B2's source reader can proceed
|
||||
against this proposal after Access's own design/conformance review. No foreign
|
||||
owner assent, policy activation, live credential or native acceptance is implied.
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"reviewer": "approval-engine-owner (agent approval-engine, codex)",
|
||||
"decision": "changes_requested",
|
||||
"comment": "INTENT Purpose and exact binding/claim boundary: B2 can reuse existing GET claim schema0.1 and verified human-control predicate without republishing approver identities. Superseded B3 refreshed evaluated-request/context design cannot satisfy the exact original strict handoff; retain original context/full and approval-free digests and verify fresh provider observation out-of-band. Work APPROVAL-WP-0005-T01; source base eece796b8bbee19551f6aa07ad389cbf83cabcdb. No policy activation, issuer admission, foreign owner assent or native acceptance.",
|
||||
"reviewed_artifact": "/home/worsch/the-custodian/docs/changes/netkingdom-target-2026-10-16/access-native-source/service-http-join-proposal-r1.json",
|
||||
"reviewed_sha256": "e0f8f12c48faf2331cf366591028577beffe617250d7005dcaf2a8b7396535fc",
|
||||
"owner_contract": "docs/access-claim-fact-reader.md",
|
||||
"owner_contract_sha256": "3dfb7987a4d9fe461b917cfa5bae7beef61bc4d9a4561fdded3e90128f5966de",
|
||||
"required_returns": [
|
||||
"Access own B2 contract/conformance review and exact native canonicalization parity; original submitted context/digests retained",
|
||||
"Owner-admitted read identity/store tenant and trusted provider transport separate from source fixture tests",
|
||||
"Actual runtime source integration and original consumer/journal/cas path before any policy eligibility successor"
|
||||
],
|
||||
"native_accepted": false
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"workplan_id": "4fa25ad5-f5d0-5592-aa59-085f8ee3edaf",
|
||||
"id": "c60f783b-47af-4d98-9495-686b59bc6619",
|
||||
"topic_id": null,
|
||||
"task_id": null,
|
||||
"decision_id": null,
|
||||
"event_type": "note",
|
||||
"summary": "Approval owner reviewed authoritative Access B2 claim contract under scoped WP0002 T01/T05 demand, prepared WP0005 source review and producer vectors. Original submitted context and freshness remain intact; fresh source checked out-of-band. Distinct verified human guarantee and native issuer limits documented. 44 source tests passed; 24 claim cases, 14 native Python canonical vectors and 7 transport refusal vectors delivered; Go conformance and canonical integration pending. No live auth, policy activation or native acceptance. Artifacts approval-fact-contract.",
|
||||
"detail": null,
|
||||
"author": "codex",
|
||||
"session_id": null,
|
||||
"created_at": "2026-10-16T07:44:18.335488Z",
|
||||
"workstream_id": "4fa25ad5-f5d0-5592-aa59-085f8ee3edaf"
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"command": "python3 -m pytest -q tests/test_access_claim_vectors.py tests/test_human_control.py tests/test_claim_contract.py tests/test_tenant_provenance.py",
|
||||
"passed": 44,
|
||||
"claim_cases": 24,
|
||||
"native_canonical_vectors": 14,
|
||||
"transport_refusal_vectors": 7,
|
||||
"native_canonical_vector_test": "actual existing producer bytes/digests verified",
|
||||
"claim_vector_test": "positive differential observations reproduced against actual Engine; signed human-control/provenance regressions passed",
|
||||
"consumer_go_conformance": "pending Access implementation; negative fixture expectations are contract, not executed Go acceptance",
|
||||
"patch_check_exit": 0,
|
||||
"canonical_source_mutated": false,
|
||||
"native_accepted": false
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# Approval-owned Go fact data adapter
|
||||
|
||||
Source proposal under APPROVAL-WP-0005-T03. Base owner revision:
|
||||
`eece796b8bbee19551f6aa07ad389cbf83cabcdb`. Worktree:
|
||||
`/tmp/netkingdom-approval-go-fact-20261016`. Canonical source/dirt is untouched.
|
||||
|
||||
Access agreed the exact module/API before implementation:
|
||||
`github.com/netkingdom/approval-engine/adapters/go/approvalfact` exports
|
||||
CanonicalBinding(raw JSON), ValidateClaim(raw JSON, Expected, now), and
|
||||
MatchObserved(original, fetched). Fact exposes detached bytes, observation and
|
||||
deadline/native digest only; it is validated data, never authenticated origin.
|
||||
No credential transport, subprocess runtime, evaluator, consume or service was
|
||||
added. Existing Access Go1.26.7 is used.
|
||||
|
||||
The Python owner primitive remains authoritative. The Go adapter reproduces
|
||||
its native five-field JSON bytes, including bool/int/float identity, finite
|
||||
float exponent thresholds/padding, negative floating zero, arbitrary integer
|
||||
magnitudes, sorted Unicode keys, HTML and U+2028/U+2029 without Go escaping.
|
||||
Strict parsing rejects duplicates, invalid Unicode/surrogates and nonfinite
|
||||
values. Explicit 1MiB/128-depth/nanosecond-time bounds fail closed; no numeric
|
||||
domain restriction was invented to simplify conformance.
|
||||
|
||||
Validation: 14 native vectors and 24 claim cases pass; Go race and vet pass.
|
||||
The actual Python source differential corpus matches 14,114 exact canonical
|
||||
byte/digest results, including random finite IEEE patterns, float boundaries,
|
||||
subnormals, extrema, integer and Unicode cases. Existing Python human-control,
|
||||
claim and provenance checks: 44 passed. This is tested source correspondence,
|
||||
not exhaustive formal proof, authenticated origin or native acceptance.
|
||||
Initial toolchain-selection/cache failures are retained in validation.json.
|
||||
|
||||
Two patch forms are provided because the initial fact-contract package was
|
||||
still pending integration when this source was prepared:
|
||||
|
||||
- `implementation.patch` contains the pending fact-contract package plus this
|
||||
Go adapter, and applies directly to the current canonical owner revision.
|
||||
- `incremental-after-fact-contract.patch` contains only this next source slice;
|
||||
apply after approval-fact-contract/implementation.patch. Do not apply both.
|
||||
|
||||
Both apply-checks pass; exact hashes and every source file hash are in
|
||||
manifest.json. The local pending-contract commit is only a temporary preparation
|
||||
anchor, not a published owner commit. Workplan T02 remains open for coordinator
|
||||
review/integration/handoff, and native WP0002 T01/T04/T05 gates remain unchanged.
|
||||
|
||||
Access owns source transport authentication and the wrapper inaccessible to HTTP
|
||||
callers. Expected native raw act must come from authoritative owner/compiler and
|
||||
actual normalization. Access's existing float64-decoded maps cannot supply it.
|
||||
The owner identified a current pilot route preserving raw request.resource only
|
||||
when it proves no active target registry enrichment and applies exact existing
|
||||
tenant normalization. Future overrides need authoritative raw enrichment source;
|
||||
unsupported correspondence refuses. Helper success must not activate policy or
|
||||
substitute a supplied digest for recomputation. Independently review and pin the
|
||||
final module sources before Access reader/engine integration and native proof.
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"chunk_id": "1fd0b5",
|
||||
"wall_time_seconds": 0.000015916,
|
||||
"exit_code": 0,
|
||||
"original_token_count": 858,
|
||||
"output": "WARN: git commit failed for writeback: Generated-only commits rejected: staged\n{\n \"repo_slug\": \"approval-engine\",\n \"repo_path\": \"/home/worsch/approval-engine\",\n \"source_revision\": null,\n \"source_revision_reason\": \"dirty_workplans\",\n \"issues\": [\n {\n \"severity\": \"WARN\",\n \"check_id\": \"C-33\",\n \"message\": \"WORK-RECORDS.md is missing or stale \\u2014 regenerate via fix-consistency (CUST-WP-0061-T04 generated work-record index)\",\n \"file_path\": \"WORK-RECORDS.md\",\n \"db_id\": \"\",\n \"file_value\": \"\",\n \"db_value\": \"\",\n \"fixable\": true\n },\n {\n \"severity\": \"WARN\",\n \"check_id\": \"C-11\",\n \"message\": \"Task 'APPROVAL-WP-ADHOC-2026-10-11-T02' has no state_hub_task_id\",\n \"file_path\": \"workplans/ADHOC-2026-10-11.md#APPROVAL-WP-ADHOC-2026-10-11-T02\",\n \"db_id\": \"\",\n \"file_value\": \"\",\n \"db_value\": \"\",\n \"fixable\": true\n },\n {\n \"severity\": \"WARN\",\n \"check_id\": \"C-06\",\n \"message\": \"Workplan has no state_hub_workstream_id \\u2014 not indexed in DB\",\n \"file_path\": \"workplans/APPROVAL-WP-0005-access-claim-fact-contract.md\",\n \"db_id\": \"\",\n \"file_value\": \"Approval fact contract and reusable Go data adapter\",\n \"db_value\": \"\",\n \"fixable\": true\n },\n {\n \"severity\": \"WARN\",\n \"check_id\": \"C-28\",\n \"message\": \"11 unread inbox message(s) older than 3 day(s): key-cape:ffe5aff4, informed-decision:59c5ee82, the-custodian:23172cb2, access-engine:9b8795d2, the-custodian:51e9eace, ...\",\n \"file_path\": \"\",\n \"db_id\": \"\",\n \"file_value\": \"\",\n \"db_value\": \"\",\n \"fixable\": false\n },\n {\n \"severity\": \"WARN\",\n \"check_id\": \"C-28\",\n \"message\": \"9 unread message(s) lack thread_id for supersession tracking: key-cape:ffe5aff4, informed-decision:59c5ee82, the-custodian:23172cb2, access-engine:9b8795d2, the-custodian:51e9eace\",\n \"file_path\": \"\",\n \"db_id\": \"\",\n \"file_value\": \"\",\n \"db_value\": \"\",\n \"fixable\": false\n },\n {\n \"severity\": \"WARN\",\n \"check_id\": \"C-29\",\n \"message\": \"Unread work request from informed-decision (59c5ee82) may need a workplan file: INFD-WP-0005: service-requested approvals from informed-decision intake (two small asks)\",\n \"file_path\": \"\",\n \"db_id\": \"\",\n \"file_value\": \"\",\n \"db_value\": \"\",\n \"fixable\": false\n },\n {\n \"severity\": \"WARN\",\n \"check_id\": \"C-29\",\n \"message\": \"Unread work request from flex-auth (2523510b) may need a workplan file: FLEX-WP-0020 handoff request (consumer)\",\n \"file_path\": \"\",\n \"db_id\": \"\",\n \"file_value\": \"\",\n \"db_value\": \"\",\n \"fixable\": false\n }\n ],\n \"fixes_applied\": [\n \"bindings: synced 7/7\",\n \"C-17 cleared: pushed 1 backlogged commit(s)\",\n \"C-33 fixed: regenerated WORK-RECORDS.md\",\n \"C-11 skipped: task 'APPROVAL-WP-ADHOC-2026-10-11-T02' in finished workstream \\u2014 not created\",\n \"C-06 fixed: created workstream 777b17de\\u2026 for APPROVAL-WP-0005, wrote ID to APPROVAL-WP-0005-access-claim-fact-contract.md\",\n \" + task APPROVAL-WP-0005-T01 \\u2192 24bf384a\\u2026\",\n \" + task APPROVAL-WP-0005-T02 \\u2192 3275354d\\u2026\",\n \" + task APPROVAL-WP-0005-T03 \\u2192 362bfe75\\u2026\",\n \"brief: .custodian-brief.md updated\",\n \"push: pushed\"\n ],\n \"summary\": {\n \"fail\": 0,\n \"automation_error\": 0,\n \"warn\": 7,\n \"info\": 0\n },\n \"result\": \"warn\"\n}\n"
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"go_race": {
|
||||
"command": "go test -race -count=1 ./...",
|
||||
"module": "adapters/go/approvalfact",
|
||||
"result": "passed"
|
||||
},
|
||||
"go_vet": "passed",
|
||||
"producer_golden_native_vectors": 14,
|
||||
"producer_claim_cases": 24,
|
||||
"transport": "only duplicate/nonfinite bodies tested here; HTTP transport negatives remain Access owner",
|
||||
"python_differential": {
|
||||
"command": "python3 scripts/check_go_approvalfact.py",
|
||||
"matched_exact_canonical_bytes_and_digest": 14114,
|
||||
"seed": 20261016,
|
||||
"result": "passed"
|
||||
},
|
||||
"python_regressions": {
|
||||
"command": "python3 -m pytest -q tests/test_access_claim_vectors.py tests/test_human_control.py tests/test_claim_contract.py tests/test_tenant_provenance.py",
|
||||
"passed": 44
|
||||
},
|
||||
"retained_preparation_failures": [
|
||||
"Initial go1.23 module selector could not find toolchain; module aligned to existing Access Go1.26.7, no added runtime",
|
||||
"Shared default Go cache sandbox write refusal; authorized source test retry completed"
|
||||
],
|
||||
"combined_patch_check_exit": 0,
|
||||
"incremental_patch_check_exit": 0,
|
||||
"native_accepted": false
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
# Approval exact-consume source provenance
|
||||
|
||||
Prepared by the approval-engine worker, with its AGENTS.md and INTENT.md loaded,
|
||||
in `/tmp/netkingdom-approval-reference-20261016`. Canonical owner source remained
|
||||
unchanged and clean at `9491d1d3a7b6554fa04f8dc06f65d50e3d6ee4e1`.
|
||||
This is source preparation and owner technical judgement, not a formal delegated
|
||||
review, native approval or independently accepted deployment. The new owner work
|
||||
record must be integrated before a formal review is recorded.
|
||||
|
||||
The existing authoritative consume CAS, durable transactional outbox,
|
||||
ExecutionConfirmationClient and hardened fixed-origin transport already supply
|
||||
the factual consumer boundary. A signed portable consume receipt is absent,
|
||||
but adding another signer/service is unnecessary for the independently
|
||||
authenticated mediator. The reachable source gap was discarded consume caller
|
||||
identity and insufficient standalone act/PDP correspondence in the use event.
|
||||
|
||||
The patch passes HTTP-verified Identity to consume and atomically retains bounded
|
||||
consumer metadata, native binding, PDP digest/path alongside existing full
|
||||
request/decision and threshold evidence. Top-level actor remains the approved
|
||||
initiator. Caller JSON cannot replace verified consumer provenance. Internal
|
||||
calls retain null, retries preserve the first event, and historical rows remain
|
||||
unchanged. Existing response/claim fields and schema version are unchanged.
|
||||
INTENT basis: authenticated consumable object, exact binding and local atomic
|
||||
evidence. It supplies facts and creates no new permission decision.
|
||||
|
||||
`implementation.patch`, `manifest.json`, `validation.json` and `patch-check.json`
|
||||
pin the exact deliverable. Patch SHA256:
|
||||
`a6bf77fa8b6916f423db7832f475e38747b828dd632a88f9631131e28e3230ee`.
|
||||
44 focused tests passed. Full suite: 277 passed and one failed deployment-evidence
|
||||
image pin check; the identical failure was reproduced on an untouched baseline.
|
||||
Historical release/native evidence was preserved. No real token, approval,
|
||||
OpenBao, native side effect, deployment or migration was used.
|
||||
|
||||
The scoped demand from CUST-WP-0080 is reconciled into WP0002 without promoting
|
||||
all residuals. New WP0004 tracks the bounded source delivery and pending canonical
|
||||
review/integration. The coordinator handles canonical sync after independent
|
||||
review. Hub progress logging has its request/receipt here; no new workplan was
|
||||
registered from the temporary checkout.
|
||||
|
||||
Native acceptance requires the existing owner interfaces:
|
||||
|
||||
- Approval T01: exact KeyCape issuer/audience/scope/tenant provenance receipt and
|
||||
human-authorized release/migration with native verified entry/readback. Current
|
||||
source identity tests do not prove issuer admission or deployment.
|
||||
- Approval T04 / Audit: admit the refined source payload, drain actual lifecycle
|
||||
rows and independently read receiver event ID/content; reconcile counts,
|
||||
duplicate/lag/boundary handling, silence and scoped bearer revocation. Existing
|
||||
local atomicity cannot prove omission resistance under source compromise.
|
||||
- Approval T05 / Platform / Secrets: name actual served PEP revision/route and
|
||||
actual independently authenticated read/consume client; establish trusted server
|
||||
transport/custody/ingress. Validate fresh exact claim and signed Access decision,
|
||||
approved native act, approval-free/full request bindings and immutable operation
|
||||
plan before CAS. Preserve live one-shot journal admission, deadlines, unknown
|
||||
outcomes, independently confirmed cleanup and approved recovery.
|
||||
- Secrets and Access retain strict downstream consumption correspondence,
|
||||
published policy/action/initiating-principal representation admission and
|
||||
producer-compatible signed-decision trust. The outbox row does not validate
|
||||
that a supplied decision ID exists or authorize a protected operation.
|
||||
|
||||
The native proof must bind exact source/image/config, identity, operation/body,
|
||||
CAS evidence, actual protected effect, event/source/receiver correspondence and
|
||||
cleanup. Wrong actor/tenant/body/decision, expiry, replay, unavailability and
|
||||
consume-before-effect failure are required refusals. Fixture success is never
|
||||
reported as native acceptance. The existing deployment pin/evidence discrepancy
|
||||
must be reconciled by its owner from real release evidence before rollout.
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
id: APPROVAL-WP-0004
|
||||
type: workplan
|
||||
title: "Retain exact authenticated consume source provenance"
|
||||
domain: infotech
|
||||
repo: approval-engine
|
||||
status: active
|
||||
flavor: implementation
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-10-16"
|
||||
updated: "2026-10-16"
|
||||
depends_on: []
|
||||
state_hub_workstream_id: "688edc60-a179-5618-90f1-468aadc2af58"
|
||||
---
|
||||
|
||||
Explicit NetKingdom target demand under CUST-WP-0080 selects the source evidence
|
||||
subset of APPROVAL-WP-0002-T01/T04/T05. INTENT basis: authenticated consumable
|
||||
object, exact binding and atomic local evidence. Existing authoritative CAS,
|
||||
confirmation transport and audit adapter are reused. No second approval/PDP
|
||||
service, portable signer, foreign owner review or live action is added.
|
||||
|
||||
## Preserve verified consumer and approved operation in the use source row
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0004-T01
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "b0b0a5a9-f7e7-508b-96d0-4b02457b16fc"
|
||||
```
|
||||
|
||||
Source implementation supplies the HTTP-verified Identity to consume and writes
|
||||
its bounded metadata plus native act/PDP bindings into the existing atomic use
|
||||
outbox. Body-provided provenance is ignored, internal calls retain null, original
|
||||
idempotent evidence is immutable, and historical rows remain untouched. No DB
|
||||
schema change, response/claim widening or identity scope change is needed.
|
||||
Seven new source tests cover signature-verified HTTP consumption, restart,
|
||||
adapter preservation, spoofed fields, denied identities, retry immutability and
|
||||
rollback. Source checks do not establish native acceptance.
|
||||
|
||||
## Integrate independently reviewed source and synchronize work records
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0004-T02
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "2b932ef8-e143-5bb7-856e-4cba4fb22c09"
|
||||
```
|
||||
|
||||
The Custodian coordinator independently reviews the exact patch/manifest, then
|
||||
integrates and synchronizes the canonical owner record. Temporary-checkout work
|
||||
is not a canonical synchronized record or formal delegated-owner assent.
|
||||
|
||||
Existing native delivery obligations remain live under APPROVAL-WP-0002:
|
||||
T01 exact issuer/profile and release/migration, T04 admitted lifecycle source
|
||||
delivery/reconciliation, T05 named served PEP consumption before protected effect.
|
||||
Platform owns mediator transport/identity/custody, the one-shot journal and native
|
||||
effect; Secrets owns its actual strict handoff and task-scoped backend. Access
|
||||
owns the signed decision and exact request/action/representation binding. No
|
||||
claim that an isolated source test closes any of these gates is made.
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
diff --git a/approval_engine/api.py b/approval_engine/api.py
|
||||
index 607ff19..440e4ac 100644
|
||||
--- a/approval_engine/api.py
|
||||
+++ b/approval_engine/api.py
|
||||
@@ -176,6 +176,7 @@ class App:
|
||||
approval_id,
|
||||
data.get("request_digest"),
|
||||
decision_id=data.get("decision_id"),
|
||||
+ consumer=identity,
|
||||
)
|
||||
if "check" in path or path.endswith("/authorize"):
|
||||
return 404, {"error": "not_found", "message": "no such surface"}
|
||||
diff --git a/approval_engine/store.py b/approval_engine/store.py
|
||||
index c30decc..eb02a24 100644
|
||||
--- a/approval_engine/store.py
|
||||
+++ b/approval_engine/store.py
|
||||
@@ -18,6 +18,7 @@ from pathlib import Path
|
||||
from typing import Any, Callable
|
||||
from uuid import uuid4
|
||||
|
||||
+from .auth import Identity
|
||||
from .binding import binding_digest, canonical_binding, require_digest
|
||||
from .errors import (
|
||||
Conflict,
|
||||
@@ -750,6 +751,7 @@ class Engine:
|
||||
request_digest: str | None,
|
||||
*,
|
||||
decision_id: str | None = None,
|
||||
+ consumer: Identity | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Atomically spend an approval for one decision-bound request.
|
||||
|
||||
@@ -819,6 +821,19 @@ class Engine:
|
||||
actor=row["actor"],
|
||||
extra={
|
||||
"binding_digest": row["binding_digest"],
|
||||
+ "binding": json.loads(row["binding_json"]),
|
||||
+ "pdp_digest": row["pdp_digest"],
|
||||
+ "pdp_path": bool(row["pdp_path"]),
|
||||
+ # Only the HTTP boundary supplies an authenticated identity.
|
||||
+ # Internal/legacy calls explicitly leave provenance unknown.
|
||||
+ "consumer": ({
|
||||
+ "subject_id": consumer.subject,
|
||||
+ "issuer": consumer.issuer,
|
||||
+ "principal_type": consumer.principal_type,
|
||||
+ "tenant": consumer.tenant,
|
||||
+ "tenant_source": consumer.tenant_source,
|
||||
+ "evidence_ref": consumer.evidence_ref,
|
||||
+ } if consumer is not None else None),
|
||||
"request_digest": digest,
|
||||
**({"decision_id": decision_id} if decision_id else {}),
|
||||
"threshold": self._threshold_evidence(
|
||||
diff --git a/docs/outbox-contract.md b/docs/outbox-contract.md
|
||||
index a15bdc8..14295a3 100644
|
||||
--- a/docs/outbox-contract.md
|
||||
+++ b/docs/outbox-contract.md
|
||||
@@ -130,3 +130,32 @@ where the identities are the point. A consumer that wants the threshold reads
|
||||
Distinctness itself is a storage invariant rather than a recomputation:
|
||||
`entries` is UNIQUE on `(approval_id, subject_id)`, so a repeat approver is
|
||||
refused at insert.
|
||||
+
|
||||
+## Exact use provenance (source refinement, 2026-10-16)
|
||||
+
|
||||
+New `approval.use` rows additionally retain `details.binding` (the native
|
||||
+five-field act), its existing `binding_digest`, nullable `pdp_digest` and
|
||||
+explicit `pdp_path`. `request_digest` remains the full decision-bound request,
|
||||
+and `decision_id` remains caller-supplied correlation, not a verified PDP verdict.
|
||||
+These distinct digests must never be substituted for each other.
|
||||
+
|
||||
+`details.consumer` records the authenticated consume caller's `subject_id`,
|
||||
+`issuer`, `principal_type`, exact `tenant`, nullable verified `tenant_source` and
|
||||
+token-fingerprint `evidence_ref`. It comes solely from the HTTP authenticator;
|
||||
+body fields cannot override it. Top-level `actor` retains its existing meaning:
|
||||
+the actor captured on the approved act. Consumer provenance distinguishes the
|
||||
+service spending the object from that initiating actor. Bearer values, scopes,
|
||||
+roles and assurance are not copied into this record.
|
||||
+
|
||||
+In-process calls without an authenticated caller record `consumer: null`.
|
||||
+Existing outbox rows remain unchanged; missing/null consumer evidence cannot be
|
||||
+backfilled as authenticated. Same-digest retries preserve the first use row and
|
||||
+never replace its original consumer or decision correspondence. All fields are
|
||||
+inserted atomically with the consumption CAS, survive restart, and pass unchanged
|
||||
+through the existing asynchronous audit adapter. No schema migration is needed.
|
||||
+
|
||||
+This source evidence is not a portable signed receipt, execution grant or
|
||||
+server-identity proof. Consumer confirmation still uses the existing trusted
|
||||
+authenticated read/consume transport and a separate live execution journal.
|
||||
+Audit source admission, deployed issuer/profile, lifecycle readback and actual
|
||||
+served-PEP proof remain APPROVAL-WP-0002-T01/T04/T05 native gates.
|
||||
diff --git a/tests/test_consume_provenance.py b/tests/test_consume_provenance.py
|
||||
new file mode 100644
|
||||
index 0000000..f2b5d6a
|
||||
--- /dev/null
|
||||
+++ b/tests/test_consume_provenance.py
|
||||
@@ -0,0 +1,110 @@
|
||||
+"""Source evidence binds authenticated consumer, approved act and PDP digest."""
|
||||
+import json
|
||||
+
|
||||
+import pytest
|
||||
+
|
||||
+from approval_engine.api import App, call
|
||||
+from approval_engine.audit import audit_envelope
|
||||
+from approval_engine.store import Engine
|
||||
+from tests.conftest import FROZEN, binding, validity
|
||||
+from tests.test_auth import _jwt
|
||||
+from tests.test_tenant_provenance import authenticator
|
||||
+
|
||||
+FULL = "sha256:" + "ab" * 32
|
||||
+FREE = "sha256:" + "cd" * 32
|
||||
+
|
||||
+
|
||||
+def approved(engine):
|
||||
+ obj = engine.create(binding(), validity(), pdp_digest=FREE, pdp_path=True,
|
||||
+ human_control=True)
|
||||
+ engine.add_entry(obj.id, "human:founder", principal_type="human")
|
||||
+ return obj
|
||||
+
|
||||
+
|
||||
+def token(private, **overrides):
|
||||
+ claims = dict(sub="service:mediator", tenant="tenant:platform",
|
||||
+ principal_type="service", tenant_source="registration",
|
||||
+ scope="approval:consume")
|
||||
+ claims.update(overrides)
|
||||
+ return _jwt(private, **claims)
|
||||
+
|
||||
+
|
||||
+def consume(app, obj, bearer, **body):
|
||||
+ return call(app, "POST", f"/v1/approvals/{obj.id}/consume",
|
||||
+ {"request_digest": FULL, "decision_id": "decision:original", **body},
|
||||
+ authorization="Bearer " + bearer)
|
||||
+
|
||||
+
|
||||
+def uses(engine):
|
||||
+ return [r for r in engine.undrained() if r["class"] == "use"]
|
||||
+
|
||||
+
|
||||
+def test_authenticated_consumer_and_exact_binding_survive_restart_and_drain(tmp_path):
|
||||
+ private, auth = authenticator()
|
||||
+ path = tmp_path / "approval.sqlite"
|
||||
+ engine = Engine(path, clock=lambda: FROZEN)
|
||||
+ obj = approved(engine)
|
||||
+ bearer = token(private)
|
||||
+ status, result = consume(App(engine, auth), obj, bearer,
|
||||
+ consumer={"subject_id": "spoofed"}, pdp_digest="spoofed")
|
||||
+ assert status == 200 and result["idempotent"] is False
|
||||
+ event = uses(engine)[0]
|
||||
+ details = event["payload"]["details"]
|
||||
+ assert event["payload"]["actor"] == binding()["actor"]
|
||||
+ assert details["binding"] == binding()
|
||||
+ assert details["binding_digest"] == obj.binding_digest
|
||||
+ assert details["pdp_digest"] == FREE and details["pdp_path"] is True
|
||||
+ assert details["request_digest"] == FULL
|
||||
+ assert details["decision_id"] == "decision:original"
|
||||
+ assert details["consumer"] == {
|
||||
+ "subject_id": "service:mediator", "issuer": "https://keycape.example",
|
||||
+ "principal_type": "service", "tenant": "tenant:platform",
|
||||
+ "tenant_source": "registration", "evidence_ref": auth.authenticate("Bearer " + bearer).evidence_ref,
|
||||
+ }
|
||||
+ assert bearer not in json.dumps(event)
|
||||
+ assert "consumer" not in json.dumps(engine.claim(obj.id))
|
||||
+ engine.close()
|
||||
+ engine = Engine(path, clock=lambda: FROZEN, auto_migrate=False)
|
||||
+ assert uses(engine) == [event]
|
||||
+ # A second authenticated caller cannot rewrite the first consumer evidence.
|
||||
+ status, retry = consume(App(engine, auth), obj, token(private, sub="service:retry"),
|
||||
+ decision_id="decision:other")
|
||||
+ assert status == 200 and retry["idempotent"] is True
|
||||
+ assert retry["decision_id"] == "decision:original"
|
||||
+ assert uses(engine) == [event]
|
||||
+ delivered = []
|
||||
+ engine.drain(lambda payload: delivered.append(audit_envelope(payload)))
|
||||
+ use = next(e for e in delivered if e["type"] == "approval.use")
|
||||
+ assert use["id"] == event["event_id"]
|
||||
+ assert use["data"]["details"] == details
|
||||
+ engine.close()
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize("overrides,expected", [
|
||||
+ ({"tenant": "tenant:foreign"}, 403),
|
||||
+ ({"principal_type": "human"}, 403),
|
||||
+ ({"scope": "approval:read"}, 403),
|
||||
+ ({"tenant_source": "spoofed"}, 401),
|
||||
+])
|
||||
+def test_unadmitted_consumer_cannot_create_evidence(engine, overrides, expected):
|
||||
+ private, auth = authenticator()
|
||||
+ obj = approved(engine)
|
||||
+ assert consume(App(engine, auth), obj, token(private, **overrides))[0] == expected
|
||||
+ assert uses(engine) == [] and engine.get(obj.id).status == "approved"
|
||||
+
|
||||
+
|
||||
+def test_outbox_failure_rolls_back_consumer_and_spend(engine):
|
||||
+ private, auth = authenticator()
|
||||
+ obj = approved(engine)
|
||||
+ engine.fail_outbox = True
|
||||
+ assert consume(App(engine, auth), obj, token(private))[0] == 503
|
||||
+ assert engine.get(obj.id).status == "approved" and uses(engine) == []
|
||||
+ engine.fail_outbox = False
|
||||
+ assert consume(App(engine, auth), obj, token(private, sub="service:recovered"))[0] == 200
|
||||
+ assert uses(engine)[0]["payload"]["details"]["consumer"]["subject_id"] == "service:recovered"
|
||||
+
|
||||
+
|
||||
+def test_internal_calls_do_not_invent_authenticated_consumer(engine):
|
||||
+ obj = approved(engine)
|
||||
+ engine.consume(obj.id, FULL)
|
||||
+ assert uses(engine)[0]["payload"]["details"]["consumer"] is None
|
||||
diff --git a/workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md b/workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md
|
||||
index e3ff5ca..856ebc7 100644
|
||||
--- a/workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md
|
||||
+++ b/workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md
|
||||
@@ -34,6 +34,15 @@ Current status: blocked after the 2026-10-04 dependency review. T02, T03 and
|
||||
T06 are complete. T01, T04 and T05 use `wait` for blocked task status; their
|
||||
remaining owner receipts are specified in `docs/production-dependencies.md`.
|
||||
|
||||
+2026-10-16 target-architecture demand reconciliation: CUST-WP-0080 explicitly
|
||||
+selects necessary authenticated-consume/provenance/audit and exact served-PEP
|
||||
+interfaces from T01/T04/T05. APPROVAL-WP-0004 implements only the reachable
|
||||
+source use-evidence gap: verified consuming identity and approved act/PDP binding
|
||||
+in the existing transactional outbox. This scoped implementation demand does
|
||||
+not activate unrelated residuals or authorize credentials, migration, rollout,
|
||||
+production approvals or native effects. T01/T04/T05 remain wait for their
|
||||
+existing independently owned native receipts; this workplan remains residual.
|
||||
+
|
||||
Review completed 2026-09-01. The plan is active. Production mode will verify
|
||||
KeyCape JWT signatures and exact issuer/audience/scopes; local caller-supplied
|
||||
identity never becomes authenticated evidence. SQLite remains the first
|
||||
diff --git a/workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md b/workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md
|
||||
new file mode 100644
|
||||
index 0000000..b9bc3fc
|
||||
--- /dev/null
|
||||
+++ b/workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md
|
||||
@@ -0,0 +1,57 @@
|
||||
+---
|
||||
+id: APPROVAL-WP-0004
|
||||
+type: workplan
|
||||
+title: "Retain exact authenticated consume source provenance"
|
||||
+domain: infotech
|
||||
+repo: approval-engine
|
||||
+status: active
|
||||
+flavor: implementation
|
||||
+owner: codex
|
||||
+topic_slug: netkingdom
|
||||
+created: "2026-10-16"
|
||||
+updated: "2026-10-16"
|
||||
+depends_on: []
|
||||
+---
|
||||
+
|
||||
+Explicit NetKingdom target demand under CUST-WP-0080 selects the source evidence
|
||||
+subset of APPROVAL-WP-0002-T01/T04/T05. INTENT basis: authenticated consumable
|
||||
+object, exact binding and atomic local evidence. Existing authoritative CAS,
|
||||
+confirmation transport and audit adapter are reused. No second approval/PDP
|
||||
+service, portable signer, foreign owner review or live action is added.
|
||||
+
|
||||
+## Preserve verified consumer and approved operation in the use source row
|
||||
+
|
||||
+```task
|
||||
+id: APPROVAL-WP-0004-T01
|
||||
+status: done
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Source implementation supplies the HTTP-verified Identity to consume and writes
|
||||
+its bounded metadata plus native act/PDP bindings into the existing atomic use
|
||||
+outbox. Body-provided provenance is ignored, internal calls retain null, original
|
||||
+idempotent evidence is immutable, and historical rows remain untouched. No DB
|
||||
+schema change, response/claim widening or identity scope change is needed.
|
||||
+Seven new source tests cover signature-verified HTTP consumption, restart,
|
||||
+adapter preservation, spoofed fields, denied identities, retry immutability and
|
||||
+rollback. Source checks do not establish native acceptance.
|
||||
+
|
||||
+## Integrate independently reviewed source and synchronize work records
|
||||
+
|
||||
+```task
|
||||
+id: APPROVAL-WP-0004-T02
|
||||
+status: todo
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+The Custodian coordinator independently reviews the exact patch/manifest, then
|
||||
+integrates and synchronizes the canonical owner record. Temporary-checkout work
|
||||
+is not a canonical synchronized record or formal delegated-owner assent.
|
||||
+
|
||||
+Existing native delivery obligations remain live under APPROVAL-WP-0002:
|
||||
+T01 exact issuer/profile and release/migration, T04 admitted lifecycle source
|
||||
+delivery/reconciliation, T05 named served PEP consumption before protected effect.
|
||||
+Platform owns mediator transport/identity/custody, the one-shot journal and native
|
||||
+effect; Secrets owns its actual strict handoff and task-scoped backend. Access
|
||||
+owns the signed decision and exact request/action/representation binding. No
|
||||
+claim that an isolated source test closes any of these gates is made.
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"exit_code": 0,
|
||||
"stdout": "",
|
||||
"stderr": ""
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"workplan_id":"4fa25ad5-f5d0-5592-aa59-085f8ee3edaf","id":"c6bddc94-a264-4c82-8b28-6507e2019624","topic_id":null,"task_id":null,"decision_id":null,"event_type":"note","summary":"approval-engine owner source preparation under CUST-WP-0080: exact authenticated consuming identity and approved act/PDP binding retained atomically in existing approval.use outbox. Isolated patch/manifest in docs/changes/netkingdom-target-2026-10-16/approval-reference. 44 focused passed; full 277 passed, one unchanged baseline deployment evidence pin mismatch reproduced. Canonical review/integration/sync pending coordinator; APPROVAL-WP-0002 native gates remain open; no credential/deploy/native action.","detail":null,"author":"codex","session_id":null,"created_at":"2026-10-16T00:06:37.193148Z","workstream_id":"4fa25ad5-f5d0-5592-aa59-085f8ee3edaf"}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"summary": "approval-engine owner source preparation under CUST-WP-0080: exact authenticated consuming identity and approved act/PDP binding retained atomically in existing approval.use outbox. Isolated patch/manifest in docs/changes/netkingdom-target-2026-10-16/approval-reference. 44 focused passed; full 277 passed, one unchanged baseline deployment evidence pin mismatch reproduced. Canonical review/integration/sync pending coordinator; APPROVAL-WP-0002 native gates remain open; no credential/deploy/native action.", "event_type": "note", "author": "codex", "workplan_id": "4fa25ad5-f5d0-5592-aa59-085f8ee3edaf"}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
--- a/workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md
|
||||
+++ b/workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md
|
||||
@@ -4,7 +4,7 @@
|
||||
title: "Retain exact authenticated consume source provenance"
|
||||
domain: infotech
|
||||
repo: approval-engine
|
||||
-status: active
|
||||
+status: finished
|
||||
flavor: implementation
|
||||
owner: codex
|
||||
topic_slug: netkingdom
|
||||
@@ -42,19 +42,40 @@
|
||||
|
||||
```task
|
||||
id: APPROVAL-WP-0004-T02
|
||||
-status: todo
|
||||
+status: done
|
||||
priority: high
|
||||
state_hub_task_id: "2b932ef8-e143-5bb7-856e-4cba4fb22c09"
|
||||
```
|
||||
|
||||
-The Custodian coordinator independently reviews the exact patch/manifest, then
|
||||
-integrates and synchronizes the canonical owner record. Temporary-checkout work
|
||||
-is not a canonical synchronized record or formal delegated-owner assent.
|
||||
+The Custodian coordinator reviewed and integrated the exact source patch
|
||||
+`a6bf77fa8b6916f423db7832f475e38747b828dd632a88f9631131e28e3230ee` as commit
|
||||
+`477c683991fda3c01e2af98a18f5a8719ee9dbc9`. Canonical source hashes and integration
|
||||
+are recorded in the-custodian's
|
||||
+`docs/changes/netkingdom-target-2026-10-16/approval-reference/canonical-integration.json`.
|
||||
+Normal State Hub sync/publication is recorded in the same change directory's
|
||||
+`sync-session-61857.json`: bindings synchronized 5/5, source commit pushed, zero
|
||||
+failures, zero automation errors and nine warnings. The generated-only writeback
|
||||
+commit refusal remains recorded in that receipt; generated identifiers are
|
||||
+retained. Published origin HEAD was independently read back as the exact commit
|
||||
+above on 2026-10-16. This completes scoped source integration/publication, not
|
||||
+formal delegated-owner assent or native acceptance. The closure-file update
|
||||
+itself still follows the coordinator's normal review and synchronization.
|
||||
|
||||
Existing native delivery obligations remain live under APPROVAL-WP-0002:
|
||||
T01 exact issuer/profile and release/migration, T04 admitted lifecycle source
|
||||
delivery/reconciliation, T05 named served PEP consumption before protected effect.
|
||||
Platform owns mediator transport/identity/custody, the one-shot journal and native
|
||||
-effect; Secrets owns its actual strict handoff and task-scoped backend. Access
|
||||
+effect; Secrets owns implementation of the strict handoff and task-scoped
|
||||
+backend. The strict task handoff is not implemented at Secrets canonical
|
||||
+`c80a944105a00220cd3c1a007709e72d830dd08d`
|
||||
+and remains its next implementation work. Access
|
||||
owns the signed decision and exact request/action/representation binding. No
|
||||
claim that an isolated source test closes any of these gates is made.
|
||||
+
|
||||
+No scoped source work remains in APPROVAL-WP-0004. The native obligations above
|
||||
+were live in APPROVAL-WP-0002 before this work and remain there under their
|
||||
+existing identifiers and wait statuses; no new residual record is needed and
|
||||
+no residual implementation is promoted by this closure. Further implementation
|
||||
+of a necessary WP0002 residual must be explicitly selected under the continuing
|
||||
+CUST-WP-0080 target demand and recorded with its precise owner/action scope;
|
||||
+credential, rollout and native effect steps retain their separate authority gates.
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"focused": {
|
||||
"command": "python3 -m pytest -q tests/test_consume_provenance.py tests/test_outbox.py tests/test_execution_confirmation.py",
|
||||
"passed": 44
|
||||
},
|
||||
"full": {
|
||||
"command": "python3 -m pytest -q",
|
||||
"passed": 277,
|
||||
"failed": 1,
|
||||
"failure": "tests/test_deploy_manifest.py::test_pinned_image_schema_drift_is_acknowledged",
|
||||
"reason": "manifest image digest sha256:16e5d222d58e179f4836f649e8f8128321a8b644d29dc426b4d21a65ff97845d differs from historical release evidence digest sha256:251941a5cb2724b57cc32cff6b693b1ab0be695bee4f56f02d51961189c0fa49"
|
||||
},
|
||||
"baseline_failure_reproduced": {
|
||||
"checkout": "/tmp/netkingdom-approval-baseline-20261016",
|
||||
"command": "python3 -m pytest -q tests/test_deploy_manifest.py::test_pinned_image_schema_drift_is_acknowledged",
|
||||
"failed": 1,
|
||||
"same_mismatch": true
|
||||
},
|
||||
"diff_check": "passed"
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Approval authoritative timestamp precision repair
|
||||
|
||||
Frozen source repair under APPROVAL-WP-0006-T01, base
|
||||
`3485415b823faf4f3b682079195e186666b0ed2b`. Isolated source:
|
||||
`/tmp/netkingdom-approval-timestamp-precision-20261016`.
|
||||
Canonical source, prior WP0005 closure and registrar-generated identifiers remain
|
||||
untouched. Exact observed owner dirt is preserved in owner-dirty-baseline.patch.
|
||||
|
||||
The coordinator counterexample demonstrates a real validity defect: clock .750
|
||||
was rounded to .000 and consumed an approval expiring at .500. The same rounding
|
||||
made legitimate immediate consumption evidence appear before Access's precise
|
||||
signed NotBefore. The repair retains actual aware UTC microseconds in utc_now
|
||||
and ISO serialization. First consumption samples exactly once after SQLite's
|
||||
write lock is acquired and uses that one instant for validity, immutable receipt,
|
||||
updated_at and the use outbox's occurrence/creation time. No Access backdating,
|
||||
consumer tolerance, caller sleep, schema change or new approval protocol exists.
|
||||
|
||||
Historical whole-second rows/readback and same-digest idempotent receipts retain
|
||||
their original timestamps/decision IDs without fabricated precision. All new
|
||||
source timestamps, including claims/freshness, preserve clock precision. The
|
||||
existing Go helper already accepts microseconds; its code is unchanged, and only
|
||||
its README's old whole-second producer statement is corrected.
|
||||
|
||||
Validation: 89 focused tests pass, including actual SQLite lock waits crossing
|
||||
expiry, immediate fractional receipt/readback/restart, one sampled clock and
|
||||
identical receipt/outbox time, unchanged old evidence and failed-outbox rollback.
|
||||
Full source suite: 298 pass, one known historical release-evidence/image-pin
|
||||
mismatch remains. Original regressions failed five cases before repair. An
|
||||
initial repaired test tried issuing an already expired approval and correctly
|
||||
failed; its fixture now issues before expiry and then advances. These outcomes
|
||||
remain recorded instead of being rewritten into success.
|
||||
|
||||
`implementation.patch` SHA256:
|
||||
`74eb0ff67d14508c2a2cee14d055790321ee171a96d5290ad6d037ce8f61dbcd`.
|
||||
`approval_engine/store.py` SHA256:
|
||||
`866b275ed2d844d0aa9090ce9899ce955c6bfe32a493c2645c86e72efa343e86`.
|
||||
The original standalone repair is frozen independently of later temporal review.
|
||||
`owner-review-record.patch` applies after it, recording T02 progress and the
|
||||
Approval-owned formal review in the owner repo; no canonical write is performed.
|
||||
|
||||
The supplementary review approves Approval facts/time only for exact Access R3
|
||||
full patch `46bfeb706d3a88bf6054309cc9303eb360a91e57dda3b0fcbb7b80d551a75db6`.
|
||||
All 31 frozen source hashes match; independent temporal/retained-registry HTTP
|
||||
race checks pass. Signed claim-free DENY supplies authenticated normalized facts
|
||||
before issue; final source join compares that approved act/PDP binding without
|
||||
rewriting the original claim. Exact operation lifetime must fit original/fresh
|
||||
fact bounds. Default freshness30 cannot cover backend30 plus cleanup5;
|
||||
fixture freshness120 is explicit synthetic configuration, never a native grant.
|
||||
|
||||
Prior R1 review and WP0005 closure remain exact historical records. Immediate
|
||||
strict CAS and whole-path remeasurement use the final separately pinned repaired
|
||||
Approval source, Access R3 and Secrets successor; their result is not assumed
|
||||
here. Native issuer/profile, source/custody/config/image admission, actual audit
|
||||
receiver and served PEP/CAS/journal/effect/cleanup remain the existing WP0002
|
||||
T01/T04/T05 and other-owner obligations. No authority promotion is recorded.
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"patch": "review-sha256-prefix.patch",
|
||||
"patch_sha256": "sha256:7ff222f5a0968d8b580382d21246c987f03990aa152aec6f61edeb3a367963be",
|
||||
"original_git_blob": "deefaac30c988de61ee26a08f29482ae76b39a0b",
|
||||
"formatted_git_blob": "4a0b3e3404a36544f7e90e974a76bd0aab946783",
|
||||
"files_sha256": {
|
||||
"updated-review.json": "sha256:2bfc6122456737918f5cf5f2c5f62eb30886b7ea911b601589ab08fc6225e98c",
|
||||
"owner-reaffirmation.json": "sha256:bdaa2bd086d0d93e3d799cab6f7f6b59447572a88418003999fc2aa481cfa4a5",
|
||||
"review-sha256-prefix.patch": "sha256:7ff222f5a0968d8b580382d21246c987f03990aa152aec6f61edeb3a367963be",
|
||||
"validation.json": "sha256:ee50e4ca1a5e060e9d227bbe3f4868396e05719286890c75965086de30bac1b6"
|
||||
},
|
||||
"canonical_mutated_by_owner": false
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"reviewer": "approval-engine-owner (agent approval-engine, codex)",
|
||||
"decision": "approved",
|
||||
"comment": "APPROVAL-WP-0006-T02 supplemental source review formatting only. Seven explicit sha256: prefixes label existing public digest metadata in values and prose. Removing only that prefix restores the exact original review bytes; digest values, technical judgement, reviewed source revisions and authority exclusions are unchanged. The owner reaffirms the original Approval-only fact/time review and no native authority is promoted. This is a new narrow formatting delta; original frozen repair/review artifacts remain historical and immutable.",
|
||||
"target": "docs/reviews/2026-10-16-fractional-consumption-temporal-approval-owner-review.json",
|
||||
"original_review_sha256": "sha256:fd5a00c498940fcfeaf188bded42de2f55b2fd04d0f78f294cc00610f5af3a6b",
|
||||
"formatted_review_sha256": "sha256:2bfc6122456737918f5cf5f2c5f62eb30886b7ea911b601589ab08fc6225e98c",
|
||||
"format_patch_sha256": "sha256:7ff222f5a0968d8b580382d21246c987f03990aa152aec6f61edeb3a367963be",
|
||||
"normal_scanner_result": "clean after explicit public digest labels; original blocked blob independently reproduced with unchanged pinned scanner",
|
||||
"source_implementation_changed": false,
|
||||
"canonical_mutated_by_owner": false,
|
||||
"native_accepted": false
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"reviewer": "approval-engine-owner (agent approval-engine, codex)",
|
||||
"decision": "approved",
|
||||
"comment": "INTENT Purpose (valid right now), The Claim Shape and Consumption Ordering: approved for bounded Approval source time precision repair and Approval-owned fact/temporal correspondence in exact Access R3 source. Work APPROVAL-WP-0006-T01/T02. Repair patch SHA256 sha256:74eb0ff67d14508c2a2cee14d055790321ee171a96d5290ad6d037ce8f61dbcd against 3485415b823faf4f3b682079195e186666b0ed2b; store.py SHA256 sha256:866b275ed2d844d0aa9090ce9899ce955c6bfe32a493c2645c86e72efa343e86. Access R3 full patch SHA256 sha256:46bfeb706d3a88bf6054309cc9303eb360a91e57dda3b0fcbb7b80d551a75db6, delta sha256:17ecf399c50c07845c545b31cad4022200eb20dfb760595aa76a271e9cee7521; all 31 frozen source hashes matched. This review does not assent to Access policy/other-owner behavior, native credentials/trust, deployment or effects.",
|
||||
"reviewed_repair_patch_sha256": "sha256:74eb0ff67d14508c2a2cee14d055790321ee171a96d5290ad6d037ce8f61dbcd",
|
||||
"reviewed_access_full_r3_sha256": "sha256:46bfeb706d3a88bf6054309cc9303eb360a91e57dda3b0fcbb7b80d551a75db6",
|
||||
"reviewed_access_r2_to_r3_sha256": "sha256:17ecf399c50c07845c545b31cad4022200eb20dfb760595aa76a271e9cee7521",
|
||||
"approval_time_findings": [
|
||||
"utc_now and iso preserve actual aware microseconds; no schema or binding algorithm change",
|
||||
"First consume samples one authoritative time only after BEGINIMMEDIATE obtains the lock; identical instant checks validity, persists receipt/update and creates use outbox timestamp",
|
||||
"Expiry remains exclusive; actual clock .750 refuses expiry .500; post-lock expiry cannot borrow pre-lock admission",
|
||||
"Whole-second old rows, readback and idempotent original decision/timestamp are preserved without fabricated precision; no unconsume, retry authority or tolerance",
|
||||
"No Access NotBefore backdating, caller wait/sleep acceptance or consumer timing waiver is needed"
|
||||
],
|
||||
"access_fact_temporal_findings": [
|
||||
"Signed claim-free DENY normalization receipt supplies existing producer-evaluated native act and approval-free digest before issue, never permission; requester consumer independently authenticates it",
|
||||
"Actual retained registry normalization is source-owned and number-preserving; final join compares the approved act/PDP digest and refuses drift instead of refreshing old context",
|
||||
"Original and newly fetched facts are validated separately; no mutation of original claim/context or either request digest",
|
||||
"Exact immutable operation deadline must fit caller, original/fetched freshness and approval validity; decision is precise post-evaluation NotBefore to exact operation deadline",
|
||||
"Entire remaining interval must cover declared backendTTL plus 5-second cleanup reserve and source maximum 120; this review does not approve that policy or native budget",
|
||||
"Default Approval freshness 30 cannot satisfy minimum 30+5; fixture freshness 120 is explicit synthetic configuration, not a producer default flip or native grant"
|
||||
],
|
||||
"validation": {
|
||||
"repair_focused_passed": 89,
|
||||
"repair_full_passed": 298,
|
||||
"repair_full_failed_preexisting": 1,
|
||||
"baseline_counterexamples_failed": 5,
|
||||
"access_independent_command": "go test -race -count=1 ./internal/decision ./cmd/access-engine -run TestTrusted",
|
||||
"access_independent_result": "passed"
|
||||
},
|
||||
"retained_failures": [
|
||||
"coordinator-counterexample.json documents canonical expired fractional approval consumed",
|
||||
"Initial repair expiry test attempted already-expired issuance; repaired engine correctly refused; fixture corrected to issue before expiry then advance",
|
||||
"Existing deployment pin/historical release evidence mismatch remains unchanged"
|
||||
],
|
||||
"prior_evidence": "WP0005 closure and6079edR1review remain exact historical evidence; this is a new precision task and new exactR3 review",
|
||||
"remaining_gates": [
|
||||
"Coordinator independent source integration/publication/synchronization at exact repair and Access/Secrets successor source pins",
|
||||
"Actual immediate strict CAS/whole-path remeasurement must use unchanged precise signed NotBefore and no sleep/waiver",
|
||||
"Native issuer/profile, CA/source/read credential/store tenant admission and schema/image/config rollout remain existing WP0002T01",
|
||||
"Actual use event/audit receiver reconciliation remains WP0002T04",
|
||||
"Served PEP original decision/request CAS before effect, live one-shot journal, deadlines and confirmed cleanup remain WP0002T05 and existing Secrets/Platform work"
|
||||
],
|
||||
"native_accepted": false,
|
||||
"authority_promotion": false
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"scanner_command": "python3 /home/worsch/repo-manager/src/repo_manager/secret_scan.py --path /tmp/approval-review-formatting-20261016 --binary /home/worsch/.local/lib/repo-manager/gitleaks/8.30.1/gitleaks",
|
||||
"scanner_version": "8.30.1",
|
||||
"scanner_sha256": "sha256:88f91962aa2f93ac6ab281d553b9e125f5197bbbce38f9f2437f7299c32e5509",
|
||||
"before": {
|
||||
"status": "blocked",
|
||||
"scanned_blobs": 1,
|
||||
"blocked_blob_ids": [
|
||||
"deefaac30c988de61ee26a08f29482ae76b39a0b"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"status": "clean",
|
||||
"scanned_blobs": 1,
|
||||
"blocked_blob_ids": [],
|
||||
"formatted_blob_id": "4a0b3e3404a36544f7e90e974a76bd0aab946783"
|
||||
},
|
||||
"prefixes_added": 7,
|
||||
"all_digest_bytes_preserved": true,
|
||||
"all_other_bytes_preserved": true,
|
||||
"JSON_valid": true,
|
||||
"scanner_config_ignores_bypassed": false,
|
||||
"canonical_mutated_by_owner": false
|
||||
}
|
||||
|
|
@ -0,0 +1,303 @@
|
|||
diff --git a/adapters/go/approvalfact/README.md b/adapters/go/approvalfact/README.md
|
||||
index fdffd1f..1b67b64 100644
|
||||
--- a/adapters/go/approvalfact/README.md
|
||||
+++ b/adapters/go/approvalfact/README.md
|
||||
@@ -36,7 +36,8 @@ Canonical float/string formatting follows Python source, including negative
|
||||
floating zero, exponent switching/padding, HTML and U+2028/U+2029. Duplicate
|
||||
object keys, nonfinite/overflow numbers, invalid UTF-8 and lone surrogates refuse.
|
||||
Inputs are bounded to 1 MiB and 128 container levels. Timestamp precision is at
|
||||
-most nanoseconds with strict zoned RFC3339; current producer emits whole seconds.
|
||||
+most nanoseconds with strict zoned RFC3339; the precision-repaired Python
|
||||
+producer preserves microseconds, while historical whole-second strings remain valid.
|
||||
These explicit parser/resource limits fail closed, never normalize authority.
|
||||
|
||||
`go test -race ./...` checks the portable producer vectors, typed fact pairing,
|
||||
diff --git a/approval_engine/store.py b/approval_engine/store.py
|
||||
index eb02a24..e9444d9 100644
|
||||
--- a/approval_engine/store.py
|
||||
+++ b/approval_engine/store.py
|
||||
@@ -103,11 +103,11 @@ def utc_now(clock: Callable[[], datetime] | None = None) -> datetime:
|
||||
now = datetime.now(timezone.utc)
|
||||
if now.tzinfo is None:
|
||||
now = now.replace(tzinfo=timezone.utc)
|
||||
- return now.astimezone(timezone.utc).replace(microsecond=0)
|
||||
+ return now.astimezone(timezone.utc)
|
||||
|
||||
|
||||
def iso(ts: datetime) -> str:
|
||||
- return ts.astimezone(timezone.utc).replace(microsecond=0).isoformat()
|
||||
+ return ts.astimezone(timezone.utc).isoformat()
|
||||
|
||||
|
||||
def parse_iso(value: str) -> datetime:
|
||||
@@ -768,7 +768,6 @@ class Engine:
|
||||
):
|
||||
raise Unprocessable("decision_id must be a non-empty string")
|
||||
conn = self._conn()
|
||||
- now = iso(self.now())
|
||||
idempotent = False
|
||||
try:
|
||||
conn.execute("BEGIN IMMEDIATE")
|
||||
@@ -794,7 +793,11 @@ class Engine:
|
||||
if row["status"] != "approved":
|
||||
conn.rollback()
|
||||
raise Conflict(f"cannot consume from status {row['status']}")
|
||||
- if self.now() < parse_iso(row["not_before"]) or self.now() >= parse_iso(row["expires_at"]):
|
||||
+ # Lock acquisition can wait past a validity boundary. One post-lock
|
||||
+ # instant is authoritative for both the check and its durable proof.
|
||||
+ consumed_at = self.now()
|
||||
+ now = iso(consumed_at)
|
||||
+ if consumed_at < parse_iso(row["not_before"]) or consumed_at >= parse_iso(row["expires_at"]):
|
||||
conn.rollback()
|
||||
raise Conflict("cannot consume outside validity window")
|
||||
if row["human_control"]:
|
||||
@@ -819,6 +822,7 @@ class Engine:
|
||||
"use",
|
||||
approval_id,
|
||||
actor=row["actor"],
|
||||
+ observed_at=consumed_at,
|
||||
extra={
|
||||
"binding_digest": row["binding_digest"],
|
||||
"binding": json.loads(row["binding_json"]),
|
||||
@@ -911,13 +915,14 @@ class Engine:
|
||||
*,
|
||||
actor: str | None,
|
||||
extra: dict[str, Any] | None = None,
|
||||
+ observed_at: datetime | None = None,
|
||||
) -> str:
|
||||
if self.fail_outbox:
|
||||
raise StoreUnavailable("outbox insert failed")
|
||||
if event_class not in EVENT_CLASSES:
|
||||
raise Unprocessable(f"unknown event class {event_class}")
|
||||
event_id = str(uuid4())
|
||||
- created = iso(self.now())
|
||||
+ created = iso(observed_at if observed_at is not None else self.now())
|
||||
resource = f"approval:{approval_id}" if approval_id else "approval-engine:heartbeat"
|
||||
details = {"class": event_class, **(extra or {})}
|
||||
if approval_id:
|
||||
diff --git a/docs/approval-consumption.md b/docs/approval-consumption.md
|
||||
index f85ab28..45cbb96 100644
|
||||
--- a/docs/approval-consumption.md
|
||||
+++ b/docs/approval-consumption.md
|
||||
@@ -42,6 +42,24 @@ after consumption, the approval remains spent and a retry needs a new approval.
|
||||
The response is mutation evidence, not a permission decision. It contains no
|
||||
`effect`, `allow`, `deny`, or decision result.
|
||||
|
||||
+## Authoritative time and fractional precision
|
||||
+
|
||||
+New source preserves the actual aware UTC clock's microseconds rather than
|
||||
+rounding down to whole seconds. The first consume samples one instant after
|
||||
+`BEGIN IMMEDIATE` acquires its write lock, then uses that instant for the
|
||||
+not-before/inclusive and expiry/exclusive check, persisted `consumed_at` and
|
||||
+`updated_at`, and use-event occurrence/creation time. Lock wait cannot borrow
|
||||
+an earlier validity instant or stamp evidence before actual admission.
|
||||
+
|
||||
+Existing whole-second ISO strings remain valid and are never backfilled with
|
||||
+invented fractions. Idempotent responses and object readback retain the original
|
||||
+recorded timestamp/decision exactly, even after expiry; they do not renew
|
||||
+execution authority. The store schema, API keys, CAS/digest correspondence and
|
||||
+no-unconsume rule are unchanged. Claim observation/freshness and other new
|
||||
+source timestamps also retain actual clock precision. Consumers must parse
|
||||
+aware ISO/RFC3339 timestamps including fractional seconds instead of assuming
|
||||
+a fixed-width whole-second string. No clock skew or early-use tolerance is added.
|
||||
+
|
||||
## The claim response is not an `ActionAuthorization`
|
||||
|
||||
`GET /v1/approvals/{id}/claim` serves the **approval-claim** envelope defined in
|
||||
diff --git a/tests/test_timestamp_precision.py b/tests/test_timestamp_precision.py
|
||||
new file mode 100644
|
||||
index 0000000..42fb5bc
|
||||
--- /dev/null
|
||||
+++ b/tests/test_timestamp_precision.py
|
||||
@@ -0,0 +1,130 @@
|
||||
+"""Actual source clock, locking and original immutable receipt precision."""
|
||||
+from datetime import timedelta, timezone
|
||||
+import sqlite3
|
||||
+import threading
|
||||
+from concurrent.futures import ThreadPoolExecutor
|
||||
+
|
||||
+import pytest
|
||||
+
|
||||
+from approval_engine.errors import Conflict, StoreUnavailable
|
||||
+from approval_engine.store import Engine, iso, utc_now
|
||||
+from tests.conftest import FROZEN, binding
|
||||
+
|
||||
+DIGEST = "sha256:" + "ab" * 32
|
||||
+
|
||||
+
|
||||
+def issue(engine, start, end):
|
||||
+ obj = engine.create(binding(), {"not_before": start.isoformat(), "expires_at": end.isoformat()})
|
||||
+ return engine.add_entry(obj.id, "human", principal_type="human")
|
||||
+
|
||||
+
|
||||
+def uses(engine):
|
||||
+ return [row for row in engine.undrained() if row["class"] == "use"]
|
||||
+
|
||||
+
|
||||
+def test_clock_and_iso_preserve_aware_precision_and_legacy_seconds():
|
||||
+ now = FROZEN.replace(microsecond=750123)
|
||||
+ offset = now.astimezone(timezone(timedelta(hours=2)))
|
||||
+ assert utc_now(lambda: offset) == now
|
||||
+ assert iso(offset) == "2026-08-29T12:00:00.750123+00:00"
|
||||
+ assert iso(FROZEN) == "2026-08-29T12:00:00+00:00"
|
||||
+
|
||||
+
|
||||
+def test_fractional_expiry_refuses_actual_canonical_counterexample():
|
||||
+ engine = Engine(clock=lambda: FROZEN)
|
||||
+ obj = issue(engine, FROZEN-timedelta(seconds=1), FROZEN.replace(microsecond=500000))
|
||||
+ engine.clock = lambda: FROZEN.replace(microsecond=750000)
|
||||
+ assert engine.claim(obj.id)["valid_now"] is False
|
||||
+ with pytest.raises(Conflict):
|
||||
+ engine.consume(obj.id, DIGEST, decision_id="decision:original")
|
||||
+ assert uses(engine) == []
|
||||
+
|
||||
+
|
||||
+def test_immediate_fractional_receipt_restart_and_outbox_use_one_instant(tmp_path):
|
||||
+ current = [FROZEN.replace(microsecond=750123)]
|
||||
+ path = tmp_path/"approval.sqlite"
|
||||
+ engine = Engine(path, clock=lambda: current[0])
|
||||
+ obj = issue(engine, FROZEN.replace(microsecond=700000), FROZEN+timedelta(seconds=1))
|
||||
+ samples = []
|
||||
+ def clock():
|
||||
+ value = current[0] + timedelta(microseconds=len(samples))
|
||||
+ samples.append(value)
|
||||
+ return value
|
||||
+ engine.clock = clock
|
||||
+ result = engine.consume(obj.id, DIGEST, decision_id="decision:original")
|
||||
+ assert samples == [current[0]]
|
||||
+ assert result["consumed_at"] == current[0].isoformat()
|
||||
+ assert result["consumed_at"] >= FROZEN.replace(microsecond=700001).isoformat()
|
||||
+ event = uses(engine)[0]
|
||||
+ assert event["created_at"] == event["payload"]["observed_at"] == result["consumed_at"]
|
||||
+ engine.close()
|
||||
+ engine = Engine(path, clock=lambda: FROZEN+timedelta(days=1), auto_migrate=False)
|
||||
+ assert engine.get(obj.id).as_dict()["consumption"]["consumed_at"] == result["consumed_at"]
|
||||
+ retry = engine.consume(obj.id, DIGEST, decision_id="decision:new")
|
||||
+ assert retry == {**result, "idempotent": True}
|
||||
+ assert uses(engine) == [event]
|
||||
+ engine.close()
|
||||
+
|
||||
+
|
||||
+@pytest.mark.parametrize("after,accepted", [(450000, True), (750000, False)])
|
||||
+def test_consume_samples_after_real_sqlite_lock_wait(tmp_path, after, accepted):
|
||||
+ current = [FROZEN.replace(microsecond=250000)]
|
||||
+ path = tmp_path/"approval.sqlite"
|
||||
+ engine = Engine(path, clock=lambda: current[0])
|
||||
+ obj = issue(engine, FROZEN-timedelta(seconds=1), FROZEN.replace(microsecond=500000))
|
||||
+ entered = threading.Event()
|
||||
+ initialized = threading.Event()
|
||||
+ start = threading.Event()
|
||||
+ samples = []
|
||||
+ def consume():
|
||||
+ conn = engine._conn()
|
||||
+ conn.set_trace_callback(lambda statement: entered.set() if statement == "BEGIN IMMEDIATE" else None)
|
||||
+ initialized.set()
|
||||
+ assert start.wait(2)
|
||||
+ def clock():
|
||||
+ samples.append(current[0])
|
||||
+ return current[0]
|
||||
+ engine.clock = clock
|
||||
+ try:
|
||||
+ return engine.consume(obj.id, DIGEST, decision_id="decision:original")
|
||||
+ finally:
|
||||
+ engine.close()
|
||||
+ with ThreadPoolExecutor(max_workers=1) as pool:
|
||||
+ future = pool.submit(consume)
|
||||
+ assert initialized.wait(2)
|
||||
+ lock = sqlite3.connect(path)
|
||||
+ lock.execute("BEGIN IMMEDIATE")
|
||||
+ start.set()
|
||||
+ assert entered.wait(2)
|
||||
+ current[0] = FROZEN.replace(microsecond=after)
|
||||
+ lock.commit()
|
||||
+ lock.close()
|
||||
+ if accepted:
|
||||
+ assert future.result(timeout=2)["consumed_at"] == current[0].isoformat()
|
||||
+ else:
|
||||
+ with pytest.raises(Conflict):
|
||||
+ future.result(timeout=2)
|
||||
+ assert samples == [current[0]]
|
||||
+ assert len(uses(engine)) == int(accepted)
|
||||
+ engine.close()
|
||||
+
|
||||
+
|
||||
+def test_old_second_precision_receipt_and_payload_are_never_backfilled(tmp_path):
|
||||
+ engine = Engine(tmp_path/"old.sqlite", clock=lambda: FROZEN)
|
||||
+ obj = issue(engine, FROZEN-timedelta(seconds=1), FROZEN+timedelta(seconds=1))
|
||||
+ original = engine.consume(obj.id, DIGEST, decision_id="decision:old")
|
||||
+ event = uses(engine)[0]
|
||||
+ assert original["consumed_at"] == "2026-08-29T12:00:00+00:00"
|
||||
+ engine.clock = lambda: FROZEN + timedelta(days=1, microseconds=123456)
|
||||
+ assert engine.consume(obj.id, DIGEST, decision_id="decision:new") == {**original, "idempotent": True}
|
||||
+ assert uses(engine) == [event]
|
||||
+
|
||||
+
|
||||
+def test_fractional_use_outbox_failure_rolls_back(engine):
|
||||
+ engine.clock = lambda: FROZEN.replace(microsecond=123456)
|
||||
+ obj = issue(engine, FROZEN, FROZEN+timedelta(seconds=1))
|
||||
+ engine.fail_outbox = True
|
||||
+ with pytest.raises(StoreUnavailable):
|
||||
+ engine.consume(obj.id, DIGEST)
|
||||
+ assert engine.get(obj.id).consumed_at is None
|
||||
+ assert uses(engine) == []
|
||||
diff --git a/workplans/APPROVAL-WP-0006-fractional-consumption-time.md b/workplans/APPROVAL-WP-0006-fractional-consumption-time.md
|
||||
new file mode 100644
|
||||
index 0000000..be9ab93
|
||||
--- /dev/null
|
||||
+++ b/workplans/APPROVAL-WP-0006-fractional-consumption-time.md
|
||||
@@ -0,0 +1,55 @@
|
||||
+---
|
||||
+id: APPROVAL-WP-0006
|
||||
+type: workplan
|
||||
+title: "Preserve fractional authoritative approval and consumption time"
|
||||
+domain: infotech
|
||||
+repo: approval-engine
|
||||
+status: active
|
||||
+flavor: implementation
|
||||
+owner: codex
|
||||
+topic_slug: netkingdom
|
||||
+created: "2026-10-16"
|
||||
+updated: "2026-10-16"
|
||||
+depends_on: []
|
||||
+---
|
||||
+
|
||||
+Explicit CUST-WP-0080 precision defect demand: actual clock .750 was rounded to
|
||||
+.000, accepting consume beyond a .500 expiry and recording evidence earlier than
|
||||
+the actual signed decision. INTENT basis: valid right now, exact-bound atomic
|
||||
+consumption and faithful source evidence. Fix source time, not Access backdating
|
||||
+or a consumer waiver. This does not promote native issuer/deployment authority.
|
||||
+
|
||||
+## Preserve clock precision and one post-lock consumption instant
|
||||
+
|
||||
+```task
|
||||
+id: APPROVAL-WP-0006-T01
|
||||
+status: done
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Preserve aware UTC microseconds through clock/ISO formatting. After acquisition
|
||||
+of the SQLite consume lock, sample one time for validity, stored consumption
|
||||
+receipt/update and use-event occurrence. Preserve old second-precision rows,
|
||||
+original idempotent receipt/decision and local transactional atomicity.
|
||||
+
|
||||
+Seven added regressions: baseline five failed/two passed; repaired 89 focused
|
||||
+checks pass. Full source suite: 298 passed, one pre-existing deployment pin vs
|
||||
+historical release-evidence mismatch retained. One initial test attempted issue
|
||||
+after expiry and was corrected to issue before expiry, then advance the clock;
|
||||
+the repaired source correctly refused that expired setup. Source change needs no
|
||||
+schema migration and preserves old rows/idempotent timestamps exactly.
|
||||
+
|
||||
+## Review temporal source successor and hand off independently pinned repair
|
||||
+
|
||||
+```task
|
||||
+id: APPROVAL-WP-0006-T02
|
||||
+status: todo
|
||||
+priority: high
|
||||
+```
|
||||
+
|
||||
+Supply exact patch/test/source review evidence for coordinator integration and
|
||||
+Access/Secrets immediate strict CAS remeasurement. Review only Approval facts,
|
||||
+normalized preapproval correspondence and temporal bounds on the exact successor
|
||||
+source. Existing WP0005 closure/R1 review remain historical; native issuer,
|
||||
+audit receiver, served-PEP/CAS/effect and cleanup gates remain WP0002 T01/T04/T05
|
||||
+and the existing Access/Secrets/Platform records. No native gate is closed here.
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"owner": "approval-engine",
|
||||
"source_revision": "3485415b823faf4f3b682079195e186666b0ed2b",
|
||||
"work_item": "APPROVAL-WP-0006-T01",
|
||||
"patch_sha256": "74eb0ff67d14508c2a2cee14d055790321ee171a96d5290ad6d037ce8f61dbcd",
|
||||
"owner_dirty_baseline_sha256": "d076c2525b659279bddf1c56c037a77cb67bc0ad59b746cc1c377b4f1a6949e9",
|
||||
"files": {
|
||||
"adapters/go/approvalfact/README.md": "ac8f85d83c50a59c74a0d6ffa5f2bba9a414b2aab4e11a49c3dcc16e1b5201fc",
|
||||
"approval_engine/store.py": "866b275ed2d844d0aa9090ce9899ce955c6bfe32a493c2645c86e72efa343e86",
|
||||
"docs/approval-consumption.md": "31eee8a1a975bc1ad6a77ca0973208b2fc636c68cb0c748cb8aa666d5618be85",
|
||||
"tests/test_timestamp_precision.py": "cbffa9265546a685b802702f5baf4e9d9ee822aaacab83c9a15c6c7316ee989b",
|
||||
"workplans/APPROVAL-WP-0006-fractional-consumption-time.md": "7fe8fddb06894e87a89236eda3a3292d1d1495404b1b6a368bbfb0159d8c17ce"
|
||||
},
|
||||
"canonical_written": false,
|
||||
"native_accepted": false,
|
||||
"status": "standalone precision repair source frozen; exact Access R3 Approval fact/temporal review delivered separately",
|
||||
"supplementary_owner_review_sha256": "fd5a00c498940fcfeaf188bded42de2f55b2fd04d0f78f294cc00610f5af3a6b",
|
||||
"supplementary_owner_review_record_patch_sha256": "fd69252bfa12825158eb2333d4d3816a1cdde5f67b58e937dd4f8bd321627073",
|
||||
"owner_review_record_applies_after": "implementation.patch"
|
||||
}
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
diff --git a/.custodian-brief.md b/.custodian-brief.md
|
||||
index 4e8f8d3..cbefd12 100644
|
||||
--- a/.custodian-brief.md
|
||||
+++ b/.custodian-brief.md
|
||||
@@ -2,11 +2,17 @@
|
||||
# Custodian Brief — approval-engine
|
||||
|
||||
**Domain:** infotech
|
||||
-**Last synced:** 2026-10-11 13:46 UTC
|
||||
+**Last synced:** 2026-10-16 14:28 UTC
|
||||
**State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)*
|
||||
|
||||
## Active Workstreams
|
||||
|
||||
+### Approval fact contract and reusable Go data adapter
|
||||
+Progress: 2/3 done | workplan_id: `777b17de-2c1e-5ce5-aa82-c99e5137e956`
|
||||
+
|
||||
+**Open tasks:**
|
||||
+- · Independently integrate owner contract and coordinate Access reader conformance `3275354d`
|
||||
+
|
||||
### Production readiness and consumer adoption
|
||||
Progress: 3/6 done | workplan_id: `4fa25ad5-f5d0-5592-aa59-085f8ee3edaf`
|
||||
|
||||
@@ -20,7 +26,7 @@ Progress: 3/6 done | workplan_id: `4fa25ad5-f5d0-5592-aa59-085f8ee3edaf`
|
||||
|
||||
## Inbox Hygiene
|
||||
|
||||
-**Stale unread:** 10 message(s) older than 3 day(s) — triage at session start.
|
||||
+**Stale unread:** 11 message(s) older than 3 day(s) — triage at session start.
|
||||
**Missing thread_id:** 9 unread message(s) lack supersession chains.
|
||||
- ! informed-decision: INFD-WP-0005: service-requested approvals from informed-decision intake (two small asks) `59c5ee82`
|
||||
- ! flex-auth: FLEX-WP-0020 handoff request (consumer) `2523510b`
|
||||
diff --git a/WORK-RECORDS.md b/WORK-RECORDS.md
|
||||
index 2110857..11d89d3 100644
|
||||
--- a/WORK-RECORDS.md
|
||||
+++ b/WORK-RECORDS.md
|
||||
@@ -10,12 +10,16 @@
|
||||
| --- | --- | --- | --- | --- |
|
||||
| workplan | APPROVAL-WP-ADHOC-2026-10-10 | finished | — | workplans/ADHOC-2026-10-10.md |
|
||||
| workplan | APPROVAL-WP-ADHOC-2026-10-11 | finished | — | workplans/ADHOC-2026-10-11.md |
|
||||
+| workplan | APPROVAL-WP-ADHOC-2026-10-16 | finished | — | workplans/ADHOC-2026-10-16.md |
|
||||
| workplan | APPROVAL-WP-0001 | finished | — | workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md |
|
||||
| workplan | APPROVAL-WP-0002 | blocked | — | workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md |
|
||||
| workplan | APPROVAL-WP-0003 | finished | — | workplans/APPROVAL-WP-0003-reissue-expired-approvals.md |
|
||||
+| workplan | APPROVAL-WP-0004 | finished | — | workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md |
|
||||
+| workplan | APPROVAL-WP-0005 | active | — | workplans/APPROVAL-WP-0005-access-claim-fact-contract.md |
|
||||
| task | APPROVAL-WP-ADHOC-2026-10-10-T01 | done | — | workplans/ADHOC-2026-10-10.md |
|
||||
| task | APPROVAL-WP-ADHOC-2026-10-11-T01 | done | — | workplans/ADHOC-2026-10-11.md |
|
||||
| task | APPROVAL-WP-ADHOC-2026-10-11-T02 | done | — | workplans/ADHOC-2026-10-11.md |
|
||||
+| task | APPROVAL-WP-ADHOC-2026-10-16-T01 | done | — | workplans/ADHOC-2026-10-16.md |
|
||||
| task | APPROVAL-WP-0001-T01 | done | — | workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md |
|
||||
| task | APPROVAL-WP-0001-T02 | done | — | workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md |
|
||||
| task | APPROVAL-WP-0001-T03 | done | — | workplans/APPROVAL-WP-0001-v07-alignment-and-engine-spine.md |
|
||||
@@ -32,6 +36,11 @@
|
||||
| task | APPROVAL-WP-0002-T05 | wait | — | workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md |
|
||||
| task | APPROVAL-WP-0002-T06 | done | — | workplans/APPROVAL-WP-0002-production-readiness-and-consumer-adoption.md |
|
||||
| task | APPROVAL-WP-0003-T01 | done | — | workplans/APPROVAL-WP-0003-reissue-expired-approvals.md |
|
||||
+| task | APPROVAL-WP-0004-T01 | done | — | workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md |
|
||||
+| task | APPROVAL-WP-0004-T02 | done | — | workplans/APPROVAL-WP-0004-exact-consume-source-provenance.md |
|
||||
+| task | APPROVAL-WP-0005-T01 | done | — | workplans/APPROVAL-WP-0005-access-claim-fact-contract.md |
|
||||
+| task | APPROVAL-WP-0005-T02 | todo | — | workplans/APPROVAL-WP-0005-access-claim-fact-contract.md |
|
||||
+| task | APPROVAL-WP-0005-T03 | done | — | workplans/APPROVAL-WP-0005-access-claim-fact-contract.md |
|
||||
| intake | APPROVAL-IN-0001 | open | blue | intakes/intakes.md |
|
||||
| intake | APPROVAL-IN-0002 | closed | blue | intakes/intakes.md |
|
||||
| intake | APPROVAL-IN-0003 | closed | — | intakes/intakes.md |
|
||||
diff --git a/workplans/ADHOC-2026-10-16.md b/workplans/ADHOC-2026-10-16.md
|
||||
index cd19170..8cda495 100644
|
||||
--- a/workplans/ADHOC-2026-10-16.md
|
||||
+++ b/workplans/ADHOC-2026-10-16.md
|
||||
@@ -10,6 +10,7 @@ owner: codex
|
||||
topic_slug: netkingdom
|
||||
created: "2026-10-16"
|
||||
updated: "2026-10-16"
|
||||
+state_hub_workstream_id: "51585baa-f3c3-5203-9f32-3f0a3c94f889"
|
||||
---
|
||||
|
||||
## Refuse Python-equal JSON-distinct consumed act bindings
|
||||
@@ -18,6 +19,7 @@ updated: "2026-10-16"
|
||||
id: APPROVAL-WP-ADHOC-2026-10-16-T01
|
||||
status: done
|
||||
priority: high
|
||||
+state_hub_task_id: "ff45b583-a021-5a06-bb8d-3ba09674d1f8"
|
||||
```
|
||||
|
||||
Bounded source repair demanded by the CUST-WP-0080 strict execution-seam proof.
|
||||
diff --git a/workplans/APPROVAL-WP-0005-access-claim-fact-contract.md b/workplans/APPROVAL-WP-0005-access-claim-fact-contract.md
|
||||
index abdb945..ba0b9af 100644
|
||||
--- a/workplans/APPROVAL-WP-0005-access-claim-fact-contract.md
|
||||
+++ b/workplans/APPROVAL-WP-0005-access-claim-fact-contract.md
|
||||
@@ -11,6 +11,7 @@ topic_slug: netkingdom
|
||||
created: "2026-10-16"
|
||||
updated: "2026-10-16"
|
||||
depends_on: []
|
||||
+state_hub_workstream_id: "777b17de-2c1e-5ce5-aa82-c99e5137e956"
|
||||
---
|
||||
|
||||
CUST-WP-0080 explicitly selects the necessary Approval-WP-0002-T01/T05 source
|
||||
@@ -26,6 +27,7 @@ policy eligibility or live action is included.
|
||||
id: APPROVAL-WP-0005-T01
|
||||
status: done
|
||||
priority: high
|
||||
+state_hub_task_id: "24bf384a-fdd0-566d-a1d3-5e89023a7521"
|
||||
```
|
||||
|
||||
INTENT basis: factual exact-bound approval claims supplied to Access, never a
|
||||
@@ -41,6 +43,7 @@ request-refresh design requires change; fresh facts are verified out-of-band.
|
||||
id: APPROVAL-WP-0005-T02
|
||||
status: todo
|
||||
priority: high
|
||||
+state_hub_task_id: "3275354d-d781-53cf-aec6-975c0d4a601a"
|
||||
```
|
||||
|
||||
Coordinator reviews/integrates these source records. Access owns the thin Go
|
||||
@@ -56,6 +59,7 @@ is created, promoted or marked complete by these source-only fixtures.
|
||||
id: APPROVAL-WP-0005-T03
|
||||
status: done
|
||||
priority: high
|
||||
+state_hub_task_id: "362bfe75-1cd7-5081-971b-b55ce483c802"
|
||||
```
|
||||
|
||||
Explicit bounded implementation demand after Access confirmed the exact module
|
||||
Loading…
Add table
Add a link
Reference in a new issue