Files
stack/docs/plans/reviews/2026-09-06_foundation-inspector-charter-candidate2.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

271 lines
17 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 2
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. Unmodelled delegation chains refuse unsupported-capability.
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.