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

17 KiB
Raw Blame History

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 7345f330fc. Source/plan baseline: d4696d09eb. 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.