Declare in source what each in-process agent may do, and give the status page a name - #764
Merged
Conversation
…tus page a name Groundwork for moving CORA's background runtimes onto the in-process door. Neither half changes what the running application does. ## The grant table `cora.api.in_process_grants.IN_PROCESS_GRANTS` names, per principal, the exact commands that principal issues through the back door: 20 principals, 28 distinct commands, 44 grant pairs. The cross-product a pre-#762 rulebook would have granted is 560, so 92% of it was fiction. It lives in the composition root, not in `cora.trust`, because `tach.toml` restricts Trust to infrastructure, shared, and its own aggregates: a table there importing agent-id constants from `cora.agent` does not build. The composition root is also where it belongs, since which agents a deployment runs is wiring rather than domain. Inert by construction, which is the load-bearing property. Nothing in `src/` imports it; only the fitness test (which AST-parses rather than imports) and `tools/gen_policy_grants.py`, which emits the `POST /policies` body an operator pipes into the API. If the running app read this table and defined a Policy from it, code would be granting itself its own authority, and a merged commit would be the only thing between an edit here and live authority. A human still posts it; nothing arms itself. The fitness test extends #750's registry, so every command name here must appear in the real wire surface. That closes the gap #750 opened but could not fill: it guards hand-typed command lists in the repo, while the two rulebooks actually armed at 2-BM were typed into a curl body and live only as events in a database CI cannot see. Generating the body from a CI-checked table is what turns "these names were correct on the day I checked" into a property. ## StatusPublisher `_status_push.py` acts as `SYSTEM_PRINCIPAL_ID`, which is also the fallback identity an unauthenticated HTTP request receives. Safe, since the back door demands a surface no HTTP request can claim, but the verdict log would read "nobody in particular" about a thousand times an hour. It now has a seeded Agent of its own, following `seed_run_witness.py`'s shape. `_status_push.py` itself is untouched: it still issues every read as SYSTEM until the call-site sweep. This commit creates the identity that sweep will use. ## Notes The eight synthetic names (`CampaignWatcherTick` and siblings) turned out NOT to need the allow-list the brief anticipated. They are `command_name=` values on `to_new_event(...)` envelopes, audit labels on a Decision record, and never reach `authorize()`. Verified at every site before omitting them. Two principals in the table, RunDebriefer and CautionDrafter, are overridable by env var and are overridden at 2-BM to deployment-specific Agent ids. The table names the source-code constants; a deployment that overrides them must grant the ids it actually runs. Co-Authored-By: Claude Sonnet 5 (1M context) <noreply@anthropic.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Coverage reportClick to see where and how coverage changed
This report was generated by python-coverage-comment-action |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
xmap
added a commit
that referenced
this pull request
Sep 1, 2026
…#765) Thirty-five call sites across fifteen files still passed NIL_SENTINEL_ID as their arrival Surface. That resolves to the FRONT policy, which is bound to the HTTP door, so every one of them strict-denied at the surface check. On the live 2-BM deployment those were the overwhelming majority of shadow-mode near-misses, and they are the reason POLICY_POSTURE=enforce could not be turned on: enforcing today would darken the status page and stop the durable-copy sweep from registering finished scans. They now pass SYSTEM_IN_PROCESS_SURFACE_ID. conduit_id stays nil at the same sites deliberately, since that one resolves through trust_conduit_id and is a different axis. The status page also stops signing as SYSTEM_PRINCIPAL_ID, the fallback identity an unauthenticated HTTP request receives, and acts as the StatusPublisher agent #764 seeded for it. Twelve reads an interval that used to be attributed to nobody in particular now name the runtime that issued them. A new architecture test keeps it swept. It is an AST walk over `surface_id=` keyword bindings, not a grep: a grep would match the docstring in `in_process_grants` that quotes the very command used to find these sites, and the natural response to that misfire would be an exclusion that quietly weakens the check. Its allowlist is empty, and a second test asserts the file enumerator actually found files, so the parametrized case cannot pass vacuously over an empty list. ## What the sweep broke, and why that was the point `test_run_initiator_with_start_grant_starts_run` failed: its fixture policy takes seed_policy's default nil surface, so once the initiator moved doors the grant no longer matched and no Run was created. The test was right and the fixture was stale; both policies in that file now bind the in-process surface, exactly as the deployment's generated back-door rulebook does. Its companion `..._without_start_grant_is_denied_no_run` did NOT fail, and that was worse. Post-sweep it was refused at the surface check before the command check was ever reached, so it would have passed just as well with StartRun granted. A test that has silently stopped testing its own name is the failure this repo keeps re-learning. Binding its surface restores the refusal to the command grant, verified by granting StartRun and watching it fail. Co-authored-by: xmap <16776958+xmap@users.noreply.github.com> Co-authored-by: Claude Sonnet 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Parts 2 and 3 of the plan behind #762. Groundwork for moving CORA's background runtimes onto the in-process door. Neither half changes what the running application does.
The grant table
cora.api.in_process_grants.IN_PROCESS_GRANTSnames, per principal, the exact commands that principal issues through the back door.It lives in the composition root, not
cora.trust.tach.tomlrestricts Trust to infrastructure, shared, and its own aggregates, so a table there importing agent-id constants fromcora.agentdoes not build. That turned out to be the right home anyway: which agents a deployment runs is wiring, not domain.Inert by construction, and that is the load-bearing property. Nothing in
src/imports it. Only the fitness test (which AST-parses rather than imports) andtools/gen_policy_grants.py, which emits thePOST /policiesbody an operator pipes into the API. If the running app read this table and defined a Policy from it, code would be granting itself its own authority, and a merged commit would be the only thing standing between an edit here and live authority. A human still posts it; nothing arms itself.The fitness test extends #750's registry, so every command name here must appear in the real wire surface. That closes the gap #750 opened but could not fill: it guards hand-typed lists in the repo, while the two rulebooks actually armed at 2-BM were typed into a curl body and live only as events in a database CI cannot see. Generating the body from a CI-checked table is what turns "these names were correct the day I checked" into a property.
StatusPublisher
_status_push.pyacts asSYSTEM_PRINCIPAL_ID, which is also the fallback identity an unauthenticated HTTP request receives. Safe, since the back door demands a surface no HTTP request can claim, but the verdict log would read "nobody in particular" about a thousand times an hour. It now has a seeded Agent of its own, followingseed_run_witness.py's shape._status_push.pyis untouched: it still issues every read as SYSTEM until the call-site sweep. This creates the identity that sweep will use.Two things the brief got wrong
The eight synthetic names did not need an allow-list. I expected
CampaignWatcherTickand its siblings to need explicit exemption from the fitness test. They arecommand_name=values onto_new_event(...)envelopes — audit labels on a Decision record — and never reachauthorize(). Verified at every site.My site inventory was stale within hours. The plan said 33 nil-sentinel call sites; #763 pushed it to 35 while the plan was being written. The table was derived fresh from the call sites rather than from the plan.
Verification
49,189 unit + architecture green.
ruff,pyright,tachclean.Independently re-verified rather than taken on trust:
ExperimentSteerer's two,StatusPublisher's eleven, and theread_commandvalues the flag-watcher helper receives from its callers). All matched.src/imports the table.DefinePolicyRequestschema and round-trips to a 44-pair command bound to the in-process surface and the local conduit.Deployment note
Nothing to deploy here. The table is inert, and the new agent seeds itself at boot (
seed_agentemits bothAgentDefinedandAgentVersioned, so it arrives ready rather than needing promotion). arcturus is unaffected until the sweep lands and an operator posts a generated rulebook.Process
Designed by Opus, implemented by a Sonnet subagent from a scoped brief, then independently verified above. The subagent reported both places the brief's premises were wrong instead of silently working around them, which is what the brief asked for.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com