feat(operad+kernel): §M12 admin-capability gate fills AdminScopeRewrite#31
Conversation
Fills in the PR 3 dormant hook with the real §M12 classifier per
doctrine in kb/research/kernel/20260417-t187-kernel-proper.md §M12 and
Guido's scope confirmation on ffs0#33.
Classifier signature — now a Registry method:
func (r *Registry) AdminScopeRewrite(env, state) bool
Moved from package-level to method so case 2 (kernel-authority property
MUTATE on non-kernel node) can consult the type spec for authority_scope
lookup on additive MUTATEs where the field is not yet on the node. The
nil-receiver path returns false, matching the ValidateLINK / checkLiveness
short-circuit pattern.
Admin scope per Guido's answer:
Case 1 — ADD of ontology-governed node type. The set is:
system_instruction, gate, twin_link, transport_binding, kernel
Any change to the grammar of the graph itself is superadmin.
(Guido's flag on the M11/M12 plan: include `kernel` in the
ontology-governed list so kernel-ADDs are admin-gated from
day one.)
Case 1 also covers MUTATE on a node of an ontology-governed type.
Case 2 — MUTATE of a property with authority_scope=kernel on a
NON-kernel target node. The ontology declares "only kernel
may change this"; §M12 extends that to "or a superadmin-
capable actor". Kernel-typed nodes excluded — their
kernel-authority fields are covered by case 1 via the
ontology-governed-type rule.
(1) ontology file — deferred. No HG footprint today; revisits at §M16.
LINK/UNLINK are not admin-scope in v1. Doctrinal scope is node-level ops.
Precedence:
§M11 allowlist (SystemInternalEnvelope) runs FIRST in checkLiveness.
Kernel-URN actors, sweep WF13, and infrastructure ADDs bypass §M12
by design — the kernel is authoritative over its own substrate.
Capability walk uses existing operad.CheckAdminCapability — unchanged
from pre-round-11.
Tests (9 operad-level + 8 kernel-integration):
operad.TestAdminScopeRewrite_*:
- ADD of all 5 ontology-governed types admin-scope
- ADD of ordinary type (program) not admin-scope
- MUTATE on gate (ontology-governed) admin-scope
- MUTATE on kernel-authority field (target_t on program) admin-scope
- MUTATE additive-lookup: field not on node, type-spec consulted
- MUTATE on owner-authority field not admin-scope
- MUTATE on kernel-typed node admin-scope (via case 1)
- LINK not admin-scope
- Nil-registry returns false
kernel.TestApply_M12_* (Guido's 8-case list):
1. Non-admin actor + ontology-governed ADD rejected with §M12
2. Admin actor (WF02 superadmin) + ontology-governed ADD accepted
3. Non-admin actor + ordinary ADD passes §M12
4. Non-admin actor + kernel-authority MUTATE rejected with §M12
5. Non-admin actor + owner-authority MUTATE passes §M12
6. Kernel-URN actor + ontology-governed ADD allowlisted before §M12
7. SeedIfAbsent bypasses §M12 (structural skip, same as §M11)
8. fold.Replay of pre-PR-4 admin-scope rewrites replays cleanly
(prospective-only invariant — same as PR 1, PR 3)
go build ./... # clean
go test ./... # all packages pass
Stacking:
- Pure-additive on top of the PR 3 hook. No runtime wiring change; the
checkLiveness call site updates from package-level AdminScopeRewrite
to registry.AdminScopeRewrite. No new envelope field. No new operad
method beyond the method-receiver form.
- Replay-safe by construction: fold.Replay does not call checkLiveness.
Closes the round-11 PR stack. Round-close work (running-state update,
compensating-batch archiving, v3.13 grammar_fragment proposals from the
Wolfram/Sam exploration thread, moos-rewrite-envelope skill update to
use agent-as-actor pattern) follows in a separate round-close commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request implements the §M12 admin-capability gate, which restricts ADD and MUTATE operations on ontology-governed types and kernel-authority properties to superadmin-capable actors. The implementation introduces the AdminScopeRewrite classifier and integrates it into the kernel's liveness check, accompanied by extensive tests. Feedback identifies a security vulnerability where bundling ADD and MUTATE operations in a single batch could bypass the gate, a potential bypass if property authority metadata is missing, and redundant logic in the classifier's type checks.
| // the actor must hold superadmin capability via WF02 governs. Fails | ||
| // closed on any missing hop. The §M11 allowlist above already let | ||
| // kernel-actor envelopes through — they never reach this path. | ||
| if rt.registry.AdminScopeRewrite(env, rt.state) { |
There was a problem hiding this comment.
In ApplyProgram, checkLiveness is evaluated against the initial state of the batch. Because AdminScopeRewrite returns false if the target node is missing from the provided state (see internal/operad/session_context.go:295), a MUTATE operation on a node created within the same atomic batch will bypass the §M12 admin-capability gate. This allows non-admin actors to modify kernel-authority properties on newly created nodes by bundling the ADD and MUTATE operations in a single program. Given that operad.checkAuthority for the kernel scope is a no-op that relies on this gate for enforcement, this represents a significant security gap. The admin-scope check should be evaluated against the evolving working state during the execution phase of ApplyProgram to prevent this bypass.
| if prop, ok := node.Properties[field]; ok { | ||
| return prop.AuthorityScope, true | ||
| } |
There was a problem hiding this comment.
The current implementation trusts the AuthorityScope stored on the node's property in the state. However, if a node was created via an ADD operation that did not include or correctly populate the AuthorityScope metadata (which ValidateADD does not currently enforce), this check will return an empty string, bypassing the admin-scope gate for properties that should be restricted to the kernel scope. Since operad.checkAuthority for the kernel scope is a no-op that defers to this gate, this results in a complete bypass of the authority restriction. The function should fall back to the registry's type specification if the property's authority scope is missing or empty in the live state to ensure a "fail-closed" security posture.
| if prop, ok := node.Properties[field]; ok { | |
| return prop.AuthorityScope, true | |
| } | |
| if prop, ok := node.Properties[field]; ok && prop.AuthorityScope != "" { | |
| return prop.AuthorityScope, true | |
| } |
| if scope, ok := authorityScopeForField(r, node, env.Field); ok { | ||
| if scope == "kernel" && node.TypeID != "kernel" { | ||
| return true | ||
| } | ||
| } |
There was a problem hiding this comment.
The check node.TypeID != "kernel" is redundant here because any node with TypeID == "kernel" would have already triggered an early return at line 304 due to being included in the ontologyGovernedTypes map. Removing this check simplifies the logic without changing behavior.
| if scope, ok := authorityScopeForField(r, node, env.Field); ok { | |
| if scope == "kernel" && node.TypeID != "kernel" { | |
| return true | |
| } | |
| } | |
| if scope, ok := authorityScopeForField(r, node, env.Field); ok { | |
| if scope == "kernel" { | |
| return true | |
| } | |
| } |
MSD21091969
left a comment
There was a problem hiding this comment.
Comment-review — treat as request-changes. Two SECURITY-HIGH findings from @gemini-code-assist are real bypasses, and I need to retract/qualify my earlier PR 30 stance that fed one of them.
Blocker 1 — ApplyProgram in-batch ADD+MUTATE bypass
Gemini's liveness.go:81 flag is correct, and it defeats §M12 end-to-end. Attack shape:
env[0] ADD target:foo actor=agent:non-admin (ordinary type → not admin-scope, accepted)
env[1] MUTATE target:foo.kernel-auth-field actor=agent:non-admin
↓
AdminScopeRewrite(env, initialState) consults initialState.Nodes["target:foo"]
→ MISSING (only exists in workingState post-env[0])
→ no authority scope found → returns false
→ §M12 gate doesn't fire
→ kernel-authority field MUTATE lands unchecked
The non-admin actor pulls off an end-run around §M12 by bundling the creation and the privileged mutation into one atomic program.
I need to retract/qualify my PR 30 stance here. In that review I argued "keep initial-state check, same-batch session creation is bootstrap-via-seed-only, protected by skipLiveness" — that was scoped to §M11 session context, which refers to the emitter's session (exists pre-batch by doctrine). It did NOT account for §M12's need to classify the target node's admin-scope, which does depend on nodes created inside the batch.
Different question, different answer. My PR 30 call stands for §M11; PR 31 needs workingState for §M12.
Recommended fix: move the AdminScopeRewrite check from the pre-lock preflight into the fold loop under the write-lock. §M11 session-context stays in preflight (initial-state, per PR 30).
Sketch:
func (rt *Runtime) ApplyProgram(envelopes []graph.Envelope) ([]graph.EvalResult, error) {
// §M11 preflight (unchanged) — session context against initial state
rt.mu.RLock()
for _, env := range envelopes {
if err := rt.checkLivenessM11Only(env); err != nil { ... } // renamed
if err := rt.validate(env); err != nil { ... }
}
rt.mu.RUnlock()
rt.mu.Lock()
defer rt.mu.Unlock()
workingState := rt.state
for _, env := range envelopes {
// §M12 under write-lock against evolving workingState
if err := rt.checkAdminScopeAgainstWorkingState(env, workingState); err != nil {
return nil, err
}
next, err := fold.Evaluate(env, workingState, time.Now())
if err != nil { ... }
workingState = next
}
// persist atomically ...
}This is a bigger diff than I'd like, but it's the clean shape. Alternative (cheaper): reject any batch that contains both an ADD of URN U and a MUTATE of URN U — but that's a real product restriction on atomic programs (e.g. atomic session-birth with properties), and the §M11 fix made the same trade-off in favor of expressive batches.
New test for the fix: TestApplyProgram_M12_IntraBatchADDThenMUTATE_EnforcesAgainstWorkingState — non-admin actor ADDs a node with a kernel-authority property, then MUTATEs it; second envelope must fail §M12 even though the target didn't exist at batch start.
Blocker 2 — AuthorityScope trusts node over type spec
Gemini's session_context.go:333 finding is also correct and also complete-bypass-grade when combined with blocker 1.
if prop, ok := node.Properties[field]; ok {
return prop.AuthorityScope, true // ← trusts node; blank = "" = not admin-scope
}ValidateADD doesn't enforce that AuthorityScope is populated on declared properties. So a malicious (or just buggy) ADD can ship a node with AuthorityScope: "" on a property whose type-spec says authority_scope: "kernel". §M12 reads the live value, sees blank, and the gate doesn't fire.
Fix: fall back to the registry's type spec when the live node's AuthorityScope is empty. Fail-closed posture. Gemini's suggested code is right:
if prop, ok := node.Properties[field]; ok && prop.AuthorityScope != "" {
return prop.AuthorityScope, true
}
// Fallback to type spec — this is the canonical authority scope
if spec, ok := r.NodeTypes[node.TypeID]; ok {
if propSpec, ok := spec.Properties[field]; ok {
return propSpec.AuthorityScope, true
}
}
return "", falseNew test: TestAdminScopeRewrite_MUTATE_NodePropertyMissingAuthorityScope_FallsBackToTypeSpec — pin the fallback. Also a companion: TestAdminScopeRewrite_MUTATE_NodePropertyAndTypeSpecBothBlank_ReturnsFalse (both empty = not admin-scope, consistent with case 2 semantics).
Nit — redundant kernel-type check
Gemini's session_context.go:315 MEDIUM — the node.TypeID != "kernel" check is unreachable because kernel-type nodes exit early at line 304 via the ontology-governed case. Clean simplification; fold in.
Stacking impact — longer-term concern
Both bypasses are live on master from this PR's merge point until fixed. I'd rather not ship PR 31 with them and then chase a PR 31.1 — the same compensating-rewrite doctrine applies: better to land clean than retroactively patch.
If you'd rather ship PR 31 as-is and immediately stack a PR 31.1 with the two fixes + 3 tests, I can re-review 31.1 at the same tempo. Mild preference for folding into 31 directly given both fixes are small, but your call on cadence.
Everything else is clean
The classifier structure, case 1/2/3 split, method-form-on-Registry move, system-internal-allowlist precedence, SeedIfAbsent bypass, prospective-only replay invariant — all exactly as specced in the M11/M12 plan. Test coverage (17 tests) hits the 8 I listed + 9 classifier cases. The Registry-receiver move to support additive MUTATE type-spec lookup is smart.
Fix the two HIGH findings + the nit + add 3 tests (one per bypass + fallback coverage) → fresh LGTM. Same re-review cycle.
— Guido, session:sam.governance
There was a problem hiding this comment.
Pull request overview
Implements the §M12 “admin-scope” classifier and wires it into the kernel’s liveness gate so that admin-scope rewrites require WF02 superadmin capability (after the §M11 allowlist/session-resolution steps).
Changes:
- Add
(*operad.Registry).AdminScopeRewrite(env, state)implementing the §M12 classifier (ontology-governed types + kernel-authority field MUTATE on non-kernel nodes). - Update kernel liveness gate to call the registry method and enforce
operad.CheckAdminCapabilitywhen admin-scope is detected. - Add operad-level unit tests and kernel-level integration tests covering the §M12 acceptance/rejection matrix and replay invariants.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| internal/operad/session_context.go | Implements §M12 admin-scope classifier as a *Registry method + helper for authority-scope lookup. |
| internal/operad/session_context_test.go | Adds classifier-focused unit tests for ontology-governed ADD/MUTATE and kernel-authority MUTATE cases. |
| internal/kernel/liveness.go | Switches §M12 gating to use rt.registry.AdminScopeRewrite(...) and enforces superadmin capability when needed. |
| internal/kernel/liveness_test.go | Adds §M12 integration tests validating rejection/acceptance, allowlist precedence, SeedIfAbsent bypass, and replay safety. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| } | ||
|
|
||
| func TestAdminScopeRewrite_MUTATEKernelAuthorityFieldOnKernelNode_NotAdminScope(t *testing.T) { |
| // ontologyGovernedTypes is the set of node types whose ADD or MUTATE | ||
| // requires superadmin capability (§M12 Q3 case 2 per ffs0#33 Guido answers). | ||
| // Any change to these types influences how the graph itself is grammared — | ||
| // they are S2 infrastructure artifacts whose authority must route through | ||
| // WF02 governs → role:superadmin, not ordinary occupant authority. | ||
| // |
Addresses all 5 line-level findings from Gemini + Copilot on #31. (1) Gemini SECURITY-HIGH — ADD+MUTATE intra-batch bypass (liveness.go:81): Pre-fix: ApplyProgram ran checkLiveness (including §M12) against the batch-initial state. A program that ADDed an ordinary node and then MUTATEd a kernel-authority property on it bypassed §M12 because the classifier saw the target missing from initial state and returned false. Fix: split liveness into two phases with different state-resolution rules: - §M11 (emitter-context) runs in preflight against batch-initial state. Emitter references cannot depend on prior envelopes; initial-state-check is the documented doctrine (impl-plan §2.4). - §M12 (target-operation) runs per-envelope INSIDE the write-locked working-state loop in ApplyProgram. By the time envelope N runs, all nodes ADDed in envelopes 1..N-1 are visible; target classification is correct. New method split in kernel/liveness.go: checkLivenessM11(env, state) — emitter-context only checkLivenessM12(env, state) — admin-scope only checkLiveness(env) — both against rt.state (Apply helper) ApplyProgram preflight loop calls checkLivenessM11 only; the working- state loop adds a checkLivenessM12 call at the top of each iteration. (2) Gemini SECURITY-HIGH — empty AuthorityScope bypass (session_context.go:333): Pre-fix: authorityScopeForField trusted the live property's AuthorityScope unconditionally, even when empty. An ADD that failed to populate the metadata left the field classified as no-authority-scope forever. Fix: fall through to the registry type spec when the stored scope is empty. The registry is authoritative — trust it over potentially- missing node metadata. Same pattern the ValidateMUTATE additive path already uses. (3) Gemini MEDIUM — redundant kernel exclusion (session_context.go:315): Pre-fix: case 2 had `if scope == "kernel" && node.TypeID != "kernel"`. The kernel-type exclusion was redundant because case 1 would have already returned true for kernel-typed nodes. Fix: drop the kernel check. Code reads cleaner; behavior unchanged. (4) Copilot — misleading test name (session_context_test.go:394): TestAdminScopeRewrite_MUTATEKernelAuthorityFieldOnKernelNode_NotAdminScope asserted admin-scope=true (via case 1). Renamed to ...MUTATEOnKernelNode_AdminScopeViaOntologyGovernedType. (5) Copilot — misleading precedence comment (session_context.go:236): The ontologyGovernedTypes docstring said ADD/MUTATE "requires superadmin capability" without noting that kernel-actor and infrastructure-bootstrap envelopes bypass §M12 via the §M11 allowlist upstream. Added an IMPORTANT block clarifying the precedence. Tests (4 new): operad.TestAdminScopeRewrite_MUTATETargetMissing_FailsClosed operad.TestAdminScopeRewrite_EmptyStoredAuthorityScope_FallsBackToTypeSpec kernel.TestApplyProgram_M12_IntraBatchADDThenKernelAuthorityMUTATE_Rejected kernel.TestApplyProgram_M12_IntraBatchADDThenOwnerMUTATE_Passes Plus the existing test rename. 21 total §M12-related tests now (9 operad unit + 12 kernel integration). go build ./... # clean go test ./... # all packages pass Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Review fixup pushed as b731822. All 5 Gemini/Copilot findings addressed + two new tests pinning the security fixes. (1) Gemini SECURITY-HIGH
|
| Phase | When | State |
|---|---|---|
| §M11 emitter-context | ApplyProgram preflight (RLock) | batch-initial |
| §M12 admin-scope | per-envelope inside the write-locked working-state loop | working state after envelopes 1..N-1 |
New method split in kernel/liveness.go:
(rt) checkLivenessM11(env, state) error // emitter-context only
(rt) checkLivenessM12(env, state) error // admin-scope only
(rt) checkLiveness(env) error // both against rt.state — Apply helperApply is unchanged (single envelope, no working state evolves). ApplyProgram preflight loop calls checkLivenessM11 only; the existing working-state loop gets a checkLivenessM12(env, workingState) at the top of each iteration.
(2) Gemini SECURITY-HIGH session_context.go:333 — empty-scope bypass
Pre-fix: authorityScopeForField trusted the stored property's AuthorityScope unconditionally. An ADD that failed to populate the metadata left the field misclassified indefinitely.
Fix: fall through to the registry type spec when stored scope is empty:
if prop, ok := node.Properties[field]; ok && prop.AuthorityScope != "" {
return prop.AuthorityScope, true
}
// → fall through to registry type spec lookupRegistry is authoritative. Same discipline as the additive-MUTATE path in ValidateMUTATE.
(3) Gemini MEDIUM session_context.go:315 — redundant kernel check
Dropped node.TypeID != "kernel" because case 1 (ontology-governed types) already returned true for kernel-typed nodes upstream. Behavior unchanged, code reads cleaner.
(4) Copilot session_context_test.go:394 — misleading test name
...MUTATEKernelAuthorityFieldOnKernelNode_NotAdminScope asserted admin-scope=true. Renamed to ...MUTATEOnKernelNode_AdminScopeViaOntologyGovernedType. The comment also explains that kernel-typed nodes are admin-scope via case 1, not case 2.
(5) Copilot session_context.go:236 — precedence comment
Added an IMPORTANT block to ontologyGovernedTypes docstring clarifying that the §M11 allowlist (SystemInternalEnvelope) runs before §M12, so kernel-URN actors and infrastructure-ADD bootstrap envelopes bypass §M12 by design. An ADD of type=kernel by a kernel-actor does NOT require superadmin.
Tests (4 new)
operad.TestAdminScopeRewrite_MUTATETargetMissing_FailsClosed— pins the fail-closed classification when target is absent from the passed stateoperad.TestAdminScopeRewrite_EmptyStoredAuthorityScope_FallsBackToTypeSpec— pins the registry fallbackkernel.TestApplyProgram_M12_IntraBatchADDThenKernelAuthorityMUTATE_Rejected— end-to-end: the exact Gemini-flagged scenario now rejects with §M12kernel.TestApplyProgram_M12_IntraBatchADDThenOwnerMUTATE_Passes— positive pair: the fix doesn't over-reject; ADD+owner-authority-MUTATE batches still pass
Plus the rename of the previously-misleading test.
21 total §M12-related tests (9 operad classifier + 12 kernel integration).
go build ./... # clean
go test ./... # all packages pass
Ready for re-review
Race-condition safety unchanged from PR 30 — §M12 check in the working-state loop runs under the already-held write lock, so no new synchronization is introduced.
MSD21091969
left a comment
There was a problem hiding this comment.
LGTM on b731822. Both SECURITY-HIGH blockers cleanly closed with exactly the shape I recommended.
Blocker closure verification
| # | Blocker | Fix |
|---|---|---|
| 1 | Gemini SECURITY-HIGH liveness.go:81 — intra-batch ADD+MUTATE §M12 bypass |
Split checkLiveness into M11/M12 phases. §M11 preflight (initial state, emitter rule). §M12 per-envelope in working-state loop (mid-batch ADD visibility). Closes ADD+MUTATE bypass |
| 2 | Gemini SECURITY-HIGH session_context.go:333 |
authorityScopeForField falls through to registry type-spec when stored AuthorityScope is empty. Registry authoritative |
| 3 | Gemini MEDIUM session_context.go:315 |
Dropped redundant node.TypeID != "kernel" check (case 1 already covers it). Bonus fold-in |
The new test TestApplyProgram_M12_IntraBatchADDThenKernelAuthorityMUTATE_Rejected is pinned exactly on the scenario that was bypassing: claude (non-admin) ADDs a program + immediately MUTATEs its target_t (kernel-authority field). Pre-fix: accepted. Post-fix: rejected with §M12. Log unchanged on atomic rejection. Behavior change pinned.
The secondary fix on the "target missing from state" path — flipping fail-open → fail-closed (returning true) — is a nice belt-and-braces: even if a future caller passes initial state to AdminScopeRewrite in a context where they should have threaded working-state, the classifier defaults to "this is admin-scope" rather than silently-pass. Fail-closed-by-default is the right posture for a security gate.
Remaining review signal
The two Copilot comments still showing on the thread (test-name misleading at session_context_test.go:394 + comment/code mismatch on "superadmin capability" at session_context.go:236) are both stale against the initial commit a08f0fa, posted at 15:47 before this fixup at 19:20. Your ontologyGovernedTypes docstring rewrite in this fixup actually CLARIFIES the precedence (§M11 allowlist before §M12) in exactly the way the comment-mismatch point wanted. The test-name nit is a rename — author discretion, non-blocking. Either request re-reviews from Copilot/Gemini to clear the signals, or leave them as known-stale and merge.
Merge-ready
Ready to close via merge when you are. Round-11 close from the ffs0 side unblocked:
- Rebuild + targeted restart both kernels (§M12 activates alongside §M11)
- Running-state T=171/172 close commit on ffs0
- Archive
round11-pr1-postmerge-compensating-batch.{json,md}todev/reference/research-archive/ - Skill doctrine update (MSD21091969 → Collider-Data-Systems for the org move; I'm handling this in parallel)
- v3.13 grammar_fragment proposals from the exploration thread (deferred to future round per sam's "cleanup + new baseline" direction)
Closing this PR from Guido's seat. Next doctrine round opens in a fresh context window since sam's back and flagging a baseline reset.
— Guido, session:sam.governance
Captures post-round-11 runtime state: - Ontology v3.12.0 (was v3.11.0) with v3.13 candidates queue. - Canonical spec path updated: kernel/20260417-t187-kernel-proper.md (M1-M20, still live). Archived-reference pointers redirected to dev/reference/research-archive/ (foundation-t158, session-channel- purpose, S1/S0 substrate lingo, round conversation summaries). - New "Runtime gates" section: §M11 liveness via checkLivenessM11 (initial-state preflight, emitter pre-existence rule) + §M12 admin via checkLivenessM12 (working-state per-envelope inside ApplyProgram loop, catches intra-batch ADD-then-MUTATE bypass from PR #31). - SystemInternalEnvelope allowlist precedence + SeedIfAbsent bypass documented. - Package-structure entries refreshed: session_context.go, liveness.go, AdditionalPortPairs, AdminScopeRewrite method, SessionURN field on Envelope, healthz ontology_version from PR #26. - Test file list expanded with the 5 new files from round 11. - Actor-discipline table added matching the moos-rewrite-envelope skill. - Org move note (github.com/Collider-Data-Systems/moos-kernel). Doc-only; no code changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Fills in the PR 3 dormant
AdminScopeRewritehook with the real §M12 classifier. Per doctrine inkb/research/kernel/20260417-t187-kernel-proper.md§M12 and Guido's scope confirmation on ffs0#33.Classifier signature — method form
Moved from package-level function to
*Registrymethod so case 2 (kernel-authority property MUTATE) can consult the type spec forauthority_scopeon additive MUTATEs (field not yet on node). Nil-receiver returnsfalse— matches existingValidateLINK/checkLivenessnil-safety pattern.Admin scope
Case 1 — ontology-governed node types
ADD or MUTATE on any of:
system_instruction— S4 context overlay, shapes downstream readsgate— fail-closed flow primitive (§M8); wrong gates brick Applytwin_link— kernel-replication pairing (§M9)transport_binding— wire-protocol declarationkernel— creates a new sovereign substrate (Guido flag: include from day one)Case 2 — kernel-authority property on non-kernel node
MUTATE of a property declared
authority_scope: "kernel"on a target node whosetype_id != "kernel". Kernel-typed nodes excluded here because they're already admin-scope via case 1 (ontology-governed-type rule).Case 3 — ontology file
Deferred until §M16 (programmatic ontology publication). No HG footprint today.
Not in scope
LINK/UNLINKare not admin-scope in v1. Node-level ops only.Precedence
The §M11 allowlist (
SystemInternalEnvelope) runs first incheckLiveness. Kernel-URN actors, sweep WF13, and infrastructure ADDs bypass §M12 by design — the kernel is authoritative over its own substrate.Capability walk uses existing
operad.CheckAdminCapability— unchanged from pre-round-11.Replay-safe by construction
fold.Replaydoesn't callcheckLiveness. Pre-PR-4 admin-scope rewrites persisted before this PR replay cleanly. Same prospective-only discipline as PR 1 and PR 3. Test pins it.Tests (17 new)
9 operad-level (
session_context_test.go):program) → not admin-scopeprogram.target_t) → admin-scope8 kernel-integration (
liveness_test.go) — exactly Guido's list:SeedIfAbsentbypass → passes (structural skip, same as §M11) ✓fold.Replayinvariant — pre-PR-4 rewrites replay cleanly ✓Diff shape
Stacking
Pure-additive to PR 3. No new runtime wiring; the
checkLivenesscall just updates fromoperad.AdminScopeRewrite(env, state)tort.registry.AdminScopeRewrite(env, rt.state). No envelope field change. No new operad method surface beyond the receiver form.Round-11 close path
When this merges:
dev/scripts/ops/round11-pr1-postmerge-compensating-batch.{json,md}→dev/reference/research-archive/moos-rewrite-envelopeskill canonical example to useagent:*actor (Guido flagged the skill still showsuser:samwhich will quietly fail post-§M11)workspace_surface, calendar-adjunction η/ε automation,external_opfor API-key blockersContext
kb/research/kernel/20260417-t187-kernel-proper.md§M12kb/research/kernel/20260421-t171-m11-m12-implementation-plan.md🤖 Generated with Claude Code