Files
stack/docs/plans/2026-09-06_foundation-inspector-charter.md
T
jason.woltje 8ebddd6f93 feat(foundation): offline synthetic scope/permission inspector (FI-FILBERT-8 APPROVED r6)
Rocko-authored, Filbert-reviewed inspector (r6 manifest
a4a44930...) with full review/build/verdict evidence under
docs/plans/reviews. 43/0 selftests, oracle zero-disagreement,
foundation checker PASS. Owner A9 acceptance recorded separately.
2026-09-07 14:06:35 -05:00

417 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Foundation synthetic inspector — frozen charter candidate 3
Issue: #53. Author/integrator: darkwing. Feasibility contributor: rocko.
Independent reviewer: filbert, pending exact-candidate review.
Status: proposed implementation contract, NOT permission to implement.
Jason authorized continuation of planning after accepting the reviewed map.
No source changes, migration, publication, push or live fleet activity authorized.
## 1. Bound inputs and interpretation
Accepted map/handoff: commit 7345f330fc6bfae5aa1d896c78cfb7cbe62efbae.
Source/plan baseline: d4696d09eb1b5dcf1028f30db2cd63735f51cb16.
Map verdict: FM-FILBERT-1, SHA-256
6b08c6fac0718d3db527cf9ffbfab49407e7b289d09782f5d1d0e26493eaabb3.
That approval does not cover this new charter.
Detailed incorporated specification: sections 513 of
reviews/2026-09-06_foundation-inspector-rocko-feasibility-r2.md,
SHA-256 f2f47fcfe22dca79f10f885b83d87a2f846fdb560425a4e20705c40ce4a123e1.
The explicit corrections/decisions below override that note. Its other sections
are contributor rationale/history, not normative repository facts. Original r1
and the FI-ROCKO-2 corrections remain history, not alternate specifications.
Any ambiguity or contradiction discovered at implementation is a charter defect
to resolve, not permission for a coder to guess or weaken refusal rules.
Candidate records.schema.json SHA-256:
05774aaf6943cb69c113e39ff1c29676a2a230ca7bf665c50dbcaa8049672af6.
Candidate check.py SHA-256:
82564a7d3200afcdda0850a9454cac6e6cd6a76687d2162c13cf214d7eac4607.
Both resolve under docs/plans/foundation-v1-candidate at d4696d09.
## 2. Deliverable and non-goals
One local CLI over an explicitly supplied synthetic bundle, with deterministic
text and JSON previews of selected agent/project/workspace permission. No cwd
inference of scope, live registry, config bootstrap, account lookup, engine launch,
assignment mutation or real permission grant. Every result, including parser/I/O
failures, says SYNTHETIC PREVIEW — NO LIVE EFFECTS and authentication: not-modelled.
Implemented operations: work.read (null target), file.read/file.change (logical
workspace-relative target), and assignment.change as a negative/unresolved check
only. The other 25 known catalog names return unsupported-capability, exit 2.
Unknown names return invalid-request, exit 2. No assignment-change success path.
No execution.stop operation is invented; the catalog contains execution.control.
No general RPC, sandbox, authentication, command audit, stopping, durability,
legacy adoption, OAuth refresh or extension integration is delivered.
Operator supplies synthetic files only. The CLI reads just that explicit input,
never discovers credentials/config. It cannot know whether arbitrary operator-
selected file bytes contain secrets before reading them; this is not a general
credential-read prevention boundary. Never pass credentials or live records to it.
Tests and agents are not authorized to access such files.
## 3. Input, identity and graph rules
Use the closed bundle/selection/registry/artifact/authorization shapes in r2 §5,
subject to the proposal and output overrides below. All fields shown there are
required except message. proposal is explicitly null outside assignment.change;
assignment.change requires non-null proposal and null operation.target. The
subjectAssignmentRef must exactly equal selection.assignmentRef; selection agent,
project and workspace must match that assignment. Never infer a different subject.
Eight supported record kinds: agent-definition, project, workspace, registration,
mission, task, assignment and decision. Other candidate kinds refuse unsupported-
kind. Candidate record shapes remain unchanged; simulation-specific fields exist
only in the enclosing bundle, never appended to accepted records.
Record references are kind/id/scope/revision, WITHOUT digest. Registry references
include registry/id/revision/digest. Artifact references and authorization IDs
match declared simulation inputs; their presence proves neither publication
permission, authentication, authentic evidence nor current real-world authority.
Decision approvals are evaluated as synthetic fixture facts, not verified signatures.
Missing declarations refuse; no flag can turn them into actual authenticated facts.
Reject duplicate record identities, duplicate registry identities even with different
digests, duplicate artifact identities (runId/artifactId) even with different digests,
and duplicate authorization IDs. Do not choose one of ambiguous registrations:
more than one current active registration for the same agent/scope refuses ambiguity.
Revision rules: complete contiguous history from 1, or a single revision-1 record.
Structural references on every revision must resolve. Apply head/current-admission
checks only to head records used for this calculation and their consulted chain;
historical records do not fail merely because their then-current refs are now old.
Explicit historical supersedes/decision subject references may name existing old
revisions. Current selected intent/task/mission/registration/delegation/proposal
refs must name heads; no invented predecessor, repair or fallback.
Cycle checks cover task.dependencies, mission.parentMissionRef and supersedes as
separate edge relations. Before tests are counted, show that each fixture reaches
the intended rule rather than failing an earlier shape error. Workspace ownership,
mission parent scope and registration parent match the accepted schema/README.
Dependencies grant no ownership/access. A selected task's same-project dependencies
must be accepted to permit file.change; another project's dependency never lends
permission. Broader dependency execution semantics are unsupported in this slice.
## 4. Mandatory policy and assignment calculation
Adopt r2 §7 L1L9: enabled agent ceiling, both active registrations each bounded by
its mandatory scope-role ceiling, required project and workspace policy, exact
assignment/task/intent checks, applicable task/mission narrowing and an explicit
simulation execution layer. Workspace policy never falls back to a scope role.
Null optional restrictions mean no additional narrowing; missing required ceiling
never means unrestricted. Empty operations/path grants mean none. Exactly one
selected assignment contributes; no union across assignments.
Selected project/workspace must be active. Assignment is selected, belongs to the
selected agent and workspace, and binds the selected task. Task is ready/active,
with approved current plan-approval intent whose subjects include that exact task.
Assignment intent is an approved current plan-approval, delegation or assignment-
change decision covering the task/assignment. An unrelated approved acceptance
or owner-checkpoint decision is not a substitute. Mission chain must be active.
Taskless work.read/file.read skips assignment/task/mission only when assignmentRef
is explicitly null. Supplying an invalid assignment never falls back to taskless.
Paths use logical workspace roots and component-prefix intersection, never raw
string prefixes or physical filesystem traversal. Network restrictions are parsed
and intersected but no network operation is implemented or performed.
## 5. Reassignment negative check — tightened requester context
Extend r2 §5 proposal with required requesterContexts, a closed object:
- original: {assignmentRef, execution}
- target: {assignmentRef, execution}
Each assignmentRef is non-null recordRef; execution uses the same mandatory
selection.execution shape. No extra keys. Original scope derives from the subject
assignment; target scope derives from targetTaskRef. Both requester contexts belong
to requesterAgentId and the corresponding scope, not the subject agent.
Apply L1L9 to the requester in BOTH scopes for assignment.change, using those
contexts. Role membership alone is insufficient; task/mission/intent and execution
limits also narrow the request. Require original subject and target task current
intent/status checks. Apply r2 §8 recipient/subject-bounded delegation and recorded
change checks, with approved outcomes and exact current references. Artifact basis
is evidence only. Delegations are direct declared simulation assumptions as specified in §10;
this inspector cannot detect chains hidden in artifacts or omitted from the bundle.
No message authorizes a change. Missing delegation with a message yields the
message-is-not-authority rule once structural validation succeeds. Other failures
use fixed deterministic original-scope, target-scope, delegation, change, intent
ordering. If all simulation checks pass, return unresolved/unknown-effects with
runtime-reconciliation-required, NOT allowed. No supplied runtime observations
field exists. Echo the original selection unchanged on every admitted proposal.
This is a negative preview, not a real assignment engine or proof of stopping.
## 6. Canonicalization and precise limits
Adopt separately named inspector-content-digest/1 for mock registry content only:
sorted keys, compact encoding, ASCII string domain, safe integer values, array order
preserved. No launch fingerprint or full JCS claim. V1V3 in r2 were independently
recomputed and match. Use 1.5 (not 1.0) for the nonintegral-number V4 test: JavaScript
cannot distinguish numeric 1.0 from 1 after parsing. Reject negative zero and unsafe
integer values. Strict bundle number tokens use integer notation only: reject
fraction/exponent forms and negative zero, bound integer tokens exactly before
Number conversion. This prevents precision loss from hiding unsupported values.
This lexical restriction is an inspector profile rule, not a change to candidate
JSON Schema integer semantics; the differential record validator tests shapes
separately from this strict-input profile.
Non-ASCII mock content refuses unsupported-capability; candidate record and target
paths still use the candidate Unicode/path formats. Validation tests separate schema
validity from the stricter inspector input/canonicalization domain.
Bounds: file <=1 MiB; each top-level inventory <=256; arrays <=1024; nesting <=32;
strings <=4096 UTF-8 bytes subject to stricter candidate limits. Duplicate JSON keys,
malformed UTF-8, invalid calendar dates, forbidden path characters/segments and
unknown fields refuse. Parser object construction must not invoke prototype setters;
__proto__/constructor keys are ordinary unknown keys, never executable behavior.
Never normalize an invalid path into a permitted one.
Linux input open: require available O_RDONLY, O_NOFOLLOW and O_NONBLOCK; open the
final component without following its symlink, fstat regular type and bounded size,
read within limit with an extra-byte/truncation check, close in finally. Parent-path
containment is NOT claimed. Missing/symlink/FIFO/nonregular input refuses exit 4.
Do not OR an undefined O_CLOEXEC constant: host Node v26.8.1 exposes none (measured).
No child spawning or close-on-exec guarantee is claimed. The CLI itself never spawns;
verification harness processes are separate test effects. Same-size concurrent file
mutation is not ruled out by size checks: operator/test inputs must remain quiescent.
## 7. Output and exits — replaces r2 §9 shape
Closed result fields, emitted on every outcome:
- disclaimer: fixed SYNTHETIC PREVIEW — NO LIVE EFFECTS
- preview: fixed preview: no live registrations or permission grants
- bundleVersion: 1
- authentication: fixed not-modelled
- declarations: fixed unverified-simulation
- result: allowed | refused | unresolved | invalid
- reason: allowed | invalid-request | missing-state | stale-revision |
not-authorized | unsupported-capability | unsupported-kind | unknown-effects | io-failure
- rule: fixed documented rule identifier or null; never input-derived prose
- selection: null before safe structural validation, otherwise exactly the selected
{agentId, projectId, workspaceId, assignmentRef} supplied by the caller
- operation: null before safe validation, otherwise the validated {name, target}
- proposal: null outside a valid assignment.change input, otherwise
{result, reason, rule, selectedAssignmentRef}, with no allowed result
- diagnostic: null or {byteOffset, inputPath}, each member nullable; offset only for
parse failure, original argv path only for I/O failure, never file contents
No consulted inventory, global counts, unrelated identities/payloads or raw parser
messages. Text and JSON derive from the same result. JSON is ordinary valid JSON
with the disclaimer field, not a non-JSON preamble. Text escapes control characters
in echoed arguments/targets so diagnostics cannot inject terminal controls.
No time/random data. No output claims authentic publication or real grants.
Exits: 0 permitted permission preview; 2 malformed/structurally incoherent or
unsupported input; 3 simulated permission refusal or unresolved admission; 4 I/O.
Missing required JSON keys (including execution) are structural: 2 invalid-request.
Absent referenced ordinary records are incoherent: 2 missing-state. Absent declared
mandatory policy/artifact/authorization inputs: 3 missing-state. Existing historical
reference used for current admission: 3 stale-revision. Cycles/duplicates: 2 invalid-
request. These override inconsistent example exits in r2. Refusal is never repaired.
## 8. Physical paths, toolchain and verification
Proposed implementation allowlist only:
- scripts/foundation-inspect.mjs (CLI; eventual packages/mosaic)
- scripts/foundation/{strict-json,canonical,resolve}.mjs (pure; eventual packages/config)
- scripts/foundation/validate-record.mjs (test-only differential bridge)
- scripts/foundation/verify-schema.py (verification only)
- scripts/foundation/*.test.mjs and scripts/foundation/fixtures/**
- scripts/test-foundation.sh
This is not yet a code-write authorization. No root/package-manager files, packages/
scaffolds, duplicate runtime sources, .pi/extensions, Dewey scripts, roles or config
changes. Pure modules never import live config, filesystem/process/network APIs or
read environment/clock. CLI handles input/output only, with no ambient config lookup.
Node ESM, zero new runtime dependencies. Record exact tested Node version; proposed
minimum 22 is not evidence of testing every supported version. Host measured 26.8.1;
container source uses node:24-bookworm-slim, not the first CLI's test environment.
No new repository engines pin is inferred. No automatic installation.
Mandatory verification uses existing explicitly selected Python/jsonschema 4.26.0,
checking pinned schema/checker hashes first. Missing dependency fails, never skips.
The differential bridge compares field-level validation over supported candidate
fixtures plus deterministic mutations, not invocation of check.py as a library.
Keep record-schema verdict distinct from unsupported-kind/profile/graph refusal.
Use calendar/path format semantics from the pinned checker, not string-only tests.
Unit/CLI tests cover r2 §12 with these overrides. Separate observed fixture/home/cwd/
data-root roots from harness output. Compare before/after path/type/size/mode/owner/
inode/content inventories; canaries and static import checks are bounded evidence,
not a sandbox or proof of no reads. No native synchronization or live fleet tests.
Required five repository suites and foundation author checks must remain green.
## 9. Acceptance and gates
A1. Positive read and assigned file.change permission previews with coherent records;
deterministic text/JSON, explicit disclaimers and no effects.
A2. Unregistered second workspace/cross-project selection refuses without fallback
or unrelated payload disclosure.
A3. Ownership/reference/duplicate/revision/cycle negatives hit the intended rules.
A4. Required role ceilings and all applicable layers narrow; empty/missing authority,
cross-assignment union and unassigned change refuse.
A5. Encoding, duplicate keys, calendar, path, bounds and unknown-field tests refuse
safely with the documented schema/profile distinction.
A6. Input/observed roots unchanged; no bootstrap, ambient config/credential lookup,
engine, network, installation or migration action.
A7. Cross-lane proposal preserves original selection; requester limits in both
scopes enforced; even fully supplied mock evidence cannot yield allowed.
A8. All output/exit combinations match the closed result contract, including parse
and I/O failure. No raw contents, unrelated metadata or terminal injection.
A9. Jason runs the positive and negative demo and understands the limitation before
any dependent live-registry/runtime increment begins.
First: Filbert independently reviews the frozen charter and incorporated note at
exact hashes without authorship or edits. Resolve substantive findings as a new
candidate. Then present the reviewed build scope to Jason for implementation
approval; prior map acceptance does not supply it. After authorized implementation,
retain separate author/reviewer roles, serialize shared index use, run all required
checks and obtain owner demo acceptance before dependent work. No automatic push,
issue closure, implementation or next-phase advancement follows from this charter.
## 10. Candidate 3 corrections — normative overrides for FI-FILBERT-2
These rules resolve findings 15 and override any remaining inconsistent shorthand
in this charter or the incorporated note. Candidate 2 and its NOT APPROVED verdict
are preserved separately; approval of this revision is not yet established.
### 10.1 Registration delegation and explicit mock issuer bounds (finding 1)
Add required top-level `delegationInputs`, an array of at most 256 closed objects:
`{decisionRef, mode, issuerCeiling}`. decisionRef is a candidate recordRef of kind
decision; mode is exactly `direct-declared`; issuerCeiling is candidate restrictions.
No extra fields. Duplicate exact decisionRef entries are structural invalid-request,
exit 2. This is another explicit simulation input, not an accepted record-schema
change or a runtime authority record. Empty array is allowed when none is consulted.
Its ceiling is an unverified stand-in for the referenced decision author's reviewed
delegation ceiling; it proves neither authorship nor actual permission to delegate.
For each consulted current L2/L3 registration:
1. Check current registration identity, active status, correct agent/owning scope
and required parent registration. Revoked registration: exit 3 not-authorized,
rule registration-revoked. Resolve mandatory scope-role and policy ceilings.
2. delegationRef null explicitly models owner-origin registration under the
declared authorization ID. That is a fixture assumption, not verified owner
issuance. Other mandatory checks/ceilings still apply; null is not a grant.
3. Non-null delegationRef must name a current head decision. Old decision reference:
exit 3 stale-revision, registration-delegation-stale. Its decisionKind must be
delegation and outcome approved; otherwise exit 3 not-authorized,
registration-delegation-not-approved. No new revoked field is invented: changing
the decision head to rejected blocks admission, as does using its old revision.
4. Supported scope is exactly the registration's scope. A broader/other scope is
exit 3 unsupported-capability, registration-delegation-scope-not-modelled.
subjectRefs must be exactly the current agent-definition reference for that
registration's agent and the exact current registration reference. A wrong
recipient/registration is exit 3 not-authorized, registration-delegation-bounds;
additional subject forms are exit 3 unsupported-capability,
registration-delegation-subject-form. No ambiguous pairing of multiple recipients.
5. Require the exact decision's delegationInputs entry. Missing: exit 3 missing-state,
delegation-input-missing. Every delegatedOperation must be in issuerCeiling's
operations; otherwise exit 3 not-authorized, delegation-exceeds-issuer-ceiling.
6. Intersect the role ceiling, optional registration restrictions, delegated
operation set AND the issuerCeiling restrictions (operations, paths, network and
endpoints), then all other L1L9 layers. No union or skipping the role ceiling.
Current consulted scope-role/agent-policy/project-policy references must name the
highest declared revision of that registry/id; an older ceiling is exit 3
stale-revision, policy-ceiling-stale. Historical records may retain exact old refs.
This is currentness within the explicit mock inventory, never live-current proof.
Apply the same exact delegationInputs/issuer bound check to the proposal's explicit
delegation after its approved kind/outcome/recipient/subject tests. Its issuer
restrictions additionally narrow the requester's calculations in both scopes.
No source of declared authority overrides any required role, policy or assignment.
Required coherent tests: owner-origin null; approved applicable direct delegation
with file.read permitted; rejected delegation; unrelated plan-approval/acceptance;
wrong recipient; wrong registration; unsupported scope; role allows file.change
but delegated operations/issuer paths do not; missing issuer input; active registration
pointing to an old approved decision whose newer head rejects it; reference updated
to that rejected head; and revoked registration. Each must reach its named gate.
### 10.2 Structural ownership versus consulted work access (finding 2)
Structural graph validation uses EACH record's own scope, never the global selected
project. Every task mission must belong to the task's project (project-level mission
or a workspace mission in that project). A mismatch is exit 2 invalid-request,
mission-owning-project-mismatch. Workspace mission parents must be project missions
in their own project; project missions have no parent. Dependencies may structurally
reference tasks in other scopes/projects; they confer no permission or ownership.
Unselected P2 tasks with P2 missions remain valid when the caller selects P1.
For each actual calculation context (main selection, or requester original/target):
- Consulted tasks/mission/dependency references may target the SAME workspace or
the parent project only. A different workspace of the same project is exit 3
unsupported-capability, cross-workspace-work-reference-not-modelled. Another
project is exit 3 unsupported-capability, cross-project-work-reference-not-modelled.
Apply these refusals even if that agent has another registration there. The first
slice deliberately does not model additional cross-scope visibility contexts.
Do not inspect that referenced work's status/restrictions for admission or silently
omit the reference from readiness checks. Structural existence/type checks are
distinct and operate on the supplied synthetic inventory, not live private data.
- References within the supported relation require work.read in the caller's full
applicable L1L9 intersection, as well as the requested operation. Otherwise exit 3
not-authorized, consulted-work-not-readable. The parent-project registration and
policy are already mandatory layers, as are the acting workspace restrictions.
The calculator may combine supplied mock restrictions to derive this intersection;
it returns no referenced bodies and does not claim authentic private-record access.
- Dependencies of a task used for file.change or proposal requester authority must
be accepted. Otherwise exit 3 not-authorized, dependency-not-accepted. A reference
rejected by the scope guard cannot be skipped because its status says accepted.
Taskless work.read/file.read does not traverse unrelated tasks or their dependencies.
- Apply the same reference-access rules when the proposal checks its subject/target
work: original requester context for the subject; target requester context for
the target. Dependency refs never become extra assignments or execution authority.
Tests: local workspace mission/dependency permitted with work.read; parent project
mission permitted; work.read removed while file.change remains; W1 task referring
to W2 mission or accepted dependency while agent registered only W1; same case even
with W2 registration (still unsupported in this slice); cross-project dependency;
and independent P2 task/mission inventory under a P1 selection, which must not fail
merely because it is unselected. All outputs remain body-free.
### 10.3 Direct assumptions, not hidden-chain detection (finding 3)
The model supports only the direct-declared delegationInputs form in §10.1. There is
NO claim to detect a chain concealed behind an artifact, a declaration or an omitted
record. A decision subject reference to another decision is not automatically a
parent-delegation edge. Historical subject references retain their defined meaning.
No artifact body is read and artifact presence is not authentic authority.
An input adding parentDelegationRef or another undocumented field fails the closed
shape rule: exit 2 invalid-request. A mode other than direct-declared likewise fails
shape; it is not a successfully detected hidden chain. A valid direct declaration
with opaque artifact pointers is checked as a declared assumption and cannot prove
absence of chaining. Test these three distinctions explicitly. Proposal success
remains unreachable, even when all direct simulation checks pass.
### 10.4 Fixed graph sub-order and fixture reachability (finding 4)
Global order: input bounds/lexical parsing; closed shapes/profile/supported kinds;
identity index and duplicate checks; structural reference existence/type; cycles
(task.dependencies, then mission.parentMissionRef, then supersedes); revision-chain
continuity; per-record ownership/parent-scope checks; supported operation selection;
then context binding/current-head, work-scope guards and ordered admission checks.
Within a stage use stable kind/id/scope/revision and field ordering, not input order.
Use the documented missing-declaration/current-admission exit exceptions in §7.
Schema-valid workspace mission self-parent and revision-2 self-supersedes fixtures
must hit their cycle rule BEFORE parent-scope or predecessor-continuity refusal.
Use separate detector unit tests and CLI-precedence tests. Never count an earlier
schema/ownership rejection as demonstrated cycle-detector coverage. Missing required
policy/artifact/authorization declarations are admission failures at their documented
stage, not swallowed by the ordinary-record structural missing-reference rule.
### 10.5 Differential corpus and platform boundary additions (finding 5)
Add scalar type substitution; every null-versus-absent branch; integer min/max,
boolean-versus-integer and numeric 1.0 schema cases; nested object-order-insensitive
uniqueItems comparisons; BMP/non-BMP code-point length boundaries; UTF-8 byte-path
boundaries; and escaped surrogate cases. Schema verdict and strict bundle/profile
verdict are separate expected columns. Strict bundle strings reject unpaired escaped
surrogates as profile-invalid; this is not an assertion about unformatted candidate
JSON Schema strings. A passing finite corpus is compatibility evidence, not proof
of complete schema equivalence. Actual Node versions tested must be reported.
O_RDONLY availability checks use presence/type, not truthiness: its valid value is 0.