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.
28 KiB
FI-ROCKO-1 — Foundation synthetic inspector: measured feasibility note
Author: rocko (Claude Fable 5.1, Archify lane author seat). Requested by: darkwing (FI-ROCKO-1, 2026-09-06). Issue: #53. Status: planning evidence only. This note proposes; it does not approve, implement, or bind. Every choice below is a recommendation for darkwing to accept, alter, or reject in the frozen charter.
0. Admission
FI-ROCKO-1 is admitted. No blocker.
- Compatibility: my current owner goal (Archify C1 rev 3, HELD for Jason's T2/T3 rulings) is a waiting state with no lane action due; this note displaces nothing. I take no supervisory authority over darkwing, filbert, or Jason's queue; Jason may veto or discard this note at any time.
- Authority basis observed, not assumed:
docs/plans/CURRENT.md(working tree) records Jason's acceptance of the map and "continue with filbert/rocko"; the charter draft restates it. I did not verify this with Jason directly. - Scope honoured: this file is the only repository write. No commit, push,
checkout, reset, live/runtime test, credential read, or
~/.mosaicinvestigation was performed. Dewey'sextensions/**,.pi/**, and scripts were not touched. Filbert's verdict files were not opened for editing.
1. Pinned inputs
| Input | Identity | How read |
|---|---|---|
Accepted map docs/plans/2026-09-06_agent-project-workspace-foundation.md |
commit 7345f330fc6bfae5aa1d896c78cfb7cbe62efbae |
git show 7345f33:… |
| Source/plan baseline (candidate schema, semantic-model.py, check.py, README, REVIEW, RUNTIME, phase-2 contract, ROADMAP, foundation plan) | commit d4696d09eb1b5dcf1028f30db2cd63735f51cb16 |
git show d4696d09:… |
Charter draft 1 docs/plans/2026-09-06_foundation-inspector-charter.md |
untracked; SHA-256 fdcdf4df8bf30b9fbfff960f9b1d4160ce7e6bbc4b8bb601a93cda13b56c8996 at write time |
working tree |
| Filbert map verdict (FM-FILBERT-1) | SHA-256 6b08c6fac0718d3db527cf9ffbfab49407e7b289d09782f5d1d0e26493eaabb3 (as cited in the charter) |
read only |
The charter is a moving draft: an earlier read in this session hashed to
9c065ef1…. Recommendations below target the fdcdf4df… text. If the frozen
charter differs materially, this note must be re-checked, not assumed valid.
Citations use commit:path:line. cand/ abbreviates
docs/plans/foundation-v1-candidate/.
2. Repository facts that constrain the design
- Host toolchain: Node v26.8.1 with
node:testavailable; Python 3.12.8 withjsonschema4.26.0 importable.package.jsondeclares one dependency (pi 0.84.4); nonode_modulesis present in the tree.RELEASE= 0.0.12. scripts/is flat; nopackages/tree exists yet. ROADMAP places the package layout under M20 (d4696d09:docs/plans/ROADMAP.md:114) with the layout doctrine and phase-1 sequencing at:142-145and the target tree at:152-159(packages/configat:159).- The map's recommended increment forbids npm or Turbo changes
(
7345f33:docs/plans/2026-09-06_agent-project-workspace-foundation.md:278) and proposes exit classes 0/2/3/4 (:275). - Existing validation idiom is hand-rolled and closed:
scripts/mosaic-task.mjs(rejectUnknownKeys,fail(2, …)).scripts/mosaic-config.mjsreads$MOSAIC_CONFIG(:42-43) and refuses symlinks (:54); importing it runs config resolution, which the charter forbids for the inspector. - Existing suite style:
scripts/test-config.shuses amktempsandbox,check/expect_exithelpers, andNO_COLOR. cand/FINDINGS.mdrecords anajv/fast-uriaudit concern; the candidate's own checker uses Pythonjsonschema(cand/check.py).- The candidate's fingerprint rule is restricted-domain canonicalization, not
full RFC 8785 (
d4696d09:cand/README.md:265-288, ASCII-only vectors:281-282;check.pysorts keys, uses compact separators, ASCII-only, sorts skills by id, digest ="sha256:" + hex(SHA-256(UTF-8))).
3. Language and dependency pin (recommendation)
Recommend: Node ESM (.mjs), zero new dependencies, hand-rolled closed
validation. Pin: the repository's existing Node engine (v26.8.1 on host); no
package.json change; no ajv, no jsonschema at runtime.
Why:
- Matches the map (
:278, no npm/Turbo change) and the repo's validation idiom. - Avoids the
ajv/fast-uriaudit issue and avoids adding a runtime dependency for a preview tool that must be reproducible offline. - Node gives
TextDecoder('utf-8', { fatal: true })for malformed-UTF-8 refusal,node:cryptofor SHA-256, andnode:testfor units, all built in. - A hand-rolled closed validator is smaller than the candidate schema but must
be derived from
cand/records.schema.jsonfield-by-field. To keep it honest, the test suite should run the Python candidate checker over every fixture bundle's records as a cross-check whenpython3 -c "import jsonschema"succeeds, and skip with a visible notice otherwise (see §9).
Alternative (viable, not preferred): Python 3.12 stdlib only, reusing
check.py's canonicalizer verbatim. Rejected because the CLI surface of the
stack is Node/bash and because the charter says not to promote the author-only
Python model into the inspector. This is unresolved decision D-A.
Explicitly not recommended: JSON Schema at runtime via ajv (audit issue,
dependency) or via Python jsonschema in a Node CLI (two runtimes).
4. Physical source paths (recommendation, M20-consistent)
No packages/ scaffolding in this increment. Files land in scripts/, which
is the existing flat layout, with a comment header naming the logical owner so
M20 phase 1 can move them without rewriting.
| Path | Logical owner (M20 target) | Contents |
|---|---|---|
scripts/foundation-inspect.mjs |
packages/mosaic (CLI presentation) |
argv parsing, file read, output rendering, exit classes |
scripts/foundation/strict-json.mjs |
packages/config |
strict JSON reader: fatal UTF-8, duplicate-key refusal, depth/size limits, byte-offset-only errors |
scripts/foundation/canonical.mjs |
packages/config |
restricted-domain canonicalizer + sha256: digest, out-of-domain refusal |
scripts/foundation/resolve.mjs |
packages/config |
bundle validation, reference resolution, graph checks, permission intersection, proposal evaluation. Pure: no I/O, no env, no clock |
scripts/foundation/fixtures/*.json |
test data | demo bundle plus negative fixtures |
scripts/test-foundation.sh |
suite | sandboxed CLI/privacy/non-effect suite, invokes node --test scripts/foundation/*.test.mjs |
scripts/foundation/*.test.mjs |
unit tests | node:test units for the three pure modules |
Rules:
resolve.mjs,canonical.mjs,strict-json.mjsimport only each other andnode:crypto. Nevermosaic-config.mjs, neverprocess.env, neverDate.foundation-inspect.mjsis the only file that touchesprocess.argv,fs,stdout,stderr, andprocess.exit. It readsprocess.envonly to honourNO_COLOR; it never readsMOSAIC_CONFIG,HOME, or the data root.- Root stays bootstrap-only (AGENTS.md invariant 1): nothing is added to root.
- The command name
mosaic foundation inspect <bundle.json>from REVIEW (d4696d09:cand/REVIEW.md:123) is the eventualpackages/mosaicsurface. In this increment the wrapper isscripts/foundation-inspect.mjs; afoundation-inspect.shshim is unnecessary. Naming is decision D-H.
5. Bundle contract (exact fields, proposed to freeze)
One file, one closed object. Every object is closed: unknown keys refuse.
Duplicate keys refuse at parse time. All strings must be ASCII printable when
they participate in digests (§6); identifiers are ^[a-z0-9][a-z0-9-]{0,63}$.
{
"bundleVersion": 1,
"kind": "foundation-inspector-bundle",
"simulation": true, // must be literally true; anything else refuses
"records": [ <record>, ... ], // candidate envelope records, ≤256
"registries": [ <registryEntry>, ... ], // ≤256
"selection": {
"agentId": "<id>",
"projectId": "<id>",
"workspaceId": "<id>",
"assignmentRef": <recordRef> | null,
"launchRestrictions": <restrictions> | null // simulated execution layer, D-F
},
"operation": { "name": "<one of the 29 operation names>", "target": <target> | null },
"proposal": null | {
"kind": "assignment-change",
"requesterAgentId": "<id>",
"subjectAssignmentRef": <recordRef>,
"targetTaskRef": <recordRef>,
"delegationRef": <recordRef> | null, // decision, decisionKind delegation
"changeDecisionRef": <recordRef> | null, // decision, decisionKind assignment-change
"message": "<string>" | absent, // informational only; never authority
"runtimeObservations": {
"originalExecutionStopped": true|false|null,
"effectsReconciled": true|false|null
}
}
}
<record> is exactly the candidate envelope (d4696d09:cand/records.schema.json):
{schemaVersion:1, kind, id, scope, revision, createdAt, createdBy, supersedes, authorizationRef, payload}, closed. createdAt is carried and validated as a
string but never used in calculations (charter: no timestamps in results).
<recordRef> = {kind, id, scope, revision}; <registryRef> =
{registry, id, revision, digest}; <restrictions> = {operations[], readPaths[], writePaths[], network, endpointRefs[]} with pathGrant
{root:"workspace", path|null}; all as defined in the candidate schema.
<target> for file operations: {root:"workspace", path:"<relative>"}. For
record operations: a <recordRef>. For operations that take no target: null.
An operation whose target shape does not match its name refuses invalid-request.
<registryEntry> has two shapes:
- Declared:
{registry, id, revision, digest}for harness, settings, context-content, endpoint. Existence and exact-match only. - Content-bearing:
{registry, id, revision, digest, content}forscope-role,agent-policy,project-policy.contentis{restrictions: <restrictions>};digestmust equal the canonical digest ofcontent(§6) or the bundle refusesinvalid-request. These are the "separately identified mock policy inputs" the charter requires. The candidate does not define registry content shapes; this is decision D-E.
Bounds (refuse invalid-request before any calculation):
| Bound | Value |
|---|---|
| File size | ≤ 1 MiB (aligns with RUNTIME frame limit d4696d09:cand/RUNTIME.md:41 and contract …phase2-contract.md:116) |
records |
≤ 256 |
registries |
≤ 256 |
| Any array | ≤ 1024 items (contract :116) |
| Nesting depth | ≤ 32 |
| Any string | ≤ 4096 bytes; path strings ≤ 4096 bytes |
| Input file | regular file only; symlink, directory, FIFO, device refuse invalid-request (matches mosaic-config.mjs:54 doctrine) |
| Path argument | exactly one positional bundle path; relative paths resolved against cwd but never searched; no defaults |
Explicitly absent by design: no $ref, no includes, no URLs, no env, no
credentials, no authenticated/authorized booleans anywhere. Presence of
any of those keys is an unknown-key refusal, not a warning.
6. Reference and digest algorithm
6.1 Canonical digest (restricted domain)
Implement canonical.mjs as the candidate's rule, not JCS:
- Domain: objects, arrays, strings, booleans, null, and integers within
Number.isSafeInteger. Floats,-0, non-ASCII strings, control characters other than those JSON must escape, and values outside the domain refuse withunsupported-capability. This is stricter than JCS and is the documented restricted domain (d4696d09:cand/README.md:265-288). - Object keys sorted by UTF-16 code unit order (same as
sort_keys=Trueon ASCII keys), compact separators, no whitespace. - Set-valued arrays are sorted by a stable key before serialization:
skillRefsbyid,instructionRefsbyid,operationslexically,readPaths/writePathsbypathwithnullfirst,endpointRefsbyid. Sequence-valued arrays (dependencies,subjectRefs,evidenceRefs) keep order. The list of set-valued fields is enumerated in code and in the frozen charter; anything not listed is a sequence. - Digest =
"sha256:" + lowercase hex SHA-256 of the UTF-8 bytes. - Test vectors: reproduce the README's vectors (
:281-282) and add one out-of-domain vector per rejected class.
No claim of RFC 8785 conformance is made in code, docs, or output.
6.2 Reference resolution (bounded resolver, resolve.mjs)
Index build:
- Key
(kind, id, scope)→ list of revisions. Two records with the same(kind, id, scope, revision)refuseinvalid-request(duplicate identity, A3). supersedeschains must be contiguous from revision 1: revision n must supersede exactly(kind, id, scope, n-1); gaps, forks (two records superseding the same predecessor), or a supersedes pointing outside the identity refuseinvalid-request. The bundle may carry only the head revision if it carries no history at all for that identity (D-J).idmust agree withscopeper README:64; mismatch refuses.
Reference check (every recordRef and registryRef in every payload):
- Exact match on all fields;
digestcompared byte-for-byte after §6.1 recomputation for content-bearing entries. A ref to a superseded revision refusesstale-revision. A ref to an absent identity refusesmissing-state. Kind mismatch (e.g.policyRefnot pointing atagent-policy) refusesinvalid-request.
Graph rules (all refuse without repair; the violated rule name is emitted):
workspace.payloadmust reference exactly one activeproject(README:65; charter "exactly one project owns a workspace").- A workspace
registrationfor agent X must reference, viaprojectRegistrationRef, an active projectregistrationfor the same agent X in the owning project (README:65). Missing or revoked → refuse. mission.parentMissionRefmust resolve to a mission in the same project; a task'smissionRefmust resolve to a mission in the selected project.assignmentmust bind one task and one agent;intentRefmust resolve to adecisionwithdecisionKind ∈ {plan-approval, delegation, assignment-change}whosesubjectRefsinclude the task. Otherwisenot-authorized.- Cycle checks (A3, "document which edges participate"): DFS over exactly three
edge sets, evaluated independently: (i)
task.dependencies, (ii)mission.parentMissionRef, (iii)supersedes. A cycle in any of them refusesinvalid-request. Edges deliberately excluded from cycle checks:intentRef,authorizationRef,evidenceRefs,subjectRefs, allregistryRefs (they point outside the record graph). - Cross-project execution selection refuses (README
:71).
7. Permission algorithm
Inputs: the resolved graph, selection, operation. Output: allowed or
refused + reason code + violated rule + the identities consulted. Never the
payload bodies.
Taskless set: {work.read, file.read} (from semantic-model.py). Every other
operation requires selection.assignmentRef to be non-null (D-D asks whether
work.propose belongs in the taskless set; recommendation: no, keep the
candidate's set).
Layers, in fixed order, each producing a <restrictions> view or a refusal:
| # | Layer | Source | Missing → |
|---|---|---|---|
| L1 | agent policy | agent-definition.policyRef → registry agent-policy.content.restrictions |
refuse missing-state |
| L2 | project registration | active registration for agent in projectId, restrictions |
refuse not-authorized (A2) |
| L3 | workspace registration | active registration for agent in workspaceId whose projectRegistrationRef is L2's record |
refuse not-authorized (A2) |
| L4 | project policy | project.policyRef → registry project-policy |
refuse missing-state |
| L5 | workspace policy | workspace.policyRef if present in candidate payload; else scope-role of L3 |
refuse missing-state |
| L6 | assignment | selection.assignmentRef; must be status: selected, agent = agentId, task in projectId |
non-taskless op → refuse not-authorized |
| L7 | task | task.restrictions |
null = no narrowing |
| L8 | mission chain | each mission up the parentMissionRef chain, restrictions |
null = no narrowing |
| L9 | simulated execution layer | selection.launchRestrictions |
null = no narrowing; when present, narrows only (D-F) |
Intersection semantics (README :96-142, contract :158-193):
operations: set intersection. An explicit empty array is none (:101). Anullrestrictions object at L7–L9 means "no narrowing"; at L1–L5 anullrefuses (required layer,:133).readPaths/writePaths: a grant{root:"workspace", path:null}is the whole root (:102). Intersection of two grant lists = the set of pairs where one is a component-prefix of the other, keeping the narrower. Component-prefix means path segments, not string prefix (a/bcoversa/b/c, nota/bc). Target paths must be relative, normalized, contain no.., no empty segment, no leading/, no\, no NUL; otherwise refuseinvalid-requestbefore any permission result (A5).network:none∧ anything =none;approved-endpoints∧approved-endpoints= intersection ofendpointRefsby exact ref.- Never union across assignments (contract
:172); exactly one assignment is consulted (README:134). A secondselectedassignment for the same agent+task in the bundle is not an error, but only the selected one is used. - Result for the operation: allowed iff
operation.name ∈ operationsand, for file operations, the target path is covered by the correspondingreadPaths/writePathsafter intersection, and for network operations the endpoint is covered.
Refusal precedence (deterministic): parse/shape errors (exit 2) → graph
incoherence (exit 2, D-C) → missing required layer (exit 3, missing-state)
→ registration absent (exit 3, not-authorized) → operation not in
intersection (exit 3, not-authorized). The first failing check ends
evaluation; the output names only that rule.
8. Proposal mode (cross-lane reassignment, map :290-314)
Evaluated only when proposal !== null, after the base permission result:
requesterAgentIdmust hold an active registration in the target task's project (authority in target scope) — else refusenot-authorized.delegationRefmust resolve to adecisionwithdecisionKind: delegation, outcome approved,delegatedOperationsincludingassignment.change, andsubjectRefscovering the subject assignment or its task — else refusenot-authorized. Amessagewith no delegation refuses with the same code and the violated rule "message-is-not-authority" (A7).changeDecisionRefmust resolve to adecisionwithdecisionKind: assignment-changewhosesubjectRefsinclude both the subject assignment and the target task and whosebasisRefis the delegation decision — else refusenot-authorized.runtimeObservations: if either field isnull, result is unresolved with reasonunknown-effects(charter: report reconciliation-required rather than simulate success). IforiginalExecutionStoppedisfalseoreffectsReconciledisfalse, refuseunsafe-replacement.- On any outcome, the output's
selection.assignmentRefis echoed unchanged and the bundle bytes are never written (A6, A7).
Exit class for unresolved: 3 (it is a refusal to affirm), with
result: "unresolved" in JSON and text so it cannot be read as a permission
refusal. Requester model in increment 1 is agent-only (D-G).
9. Output and exit classes
- Text (default) and
--jsonmust be generated from the same result object; the text renderer is a pure function of the JSON. First line of both, always:SYNTHETIC PREVIEW — NO LIVE EFFECTS(text) /"disclaimer": "SYNTHETIC PREVIEW — NO LIVE EFFECTS"(JSON), including on every refusal and on exit 2/4. REVIEW's phrasing "preview: no live registrations or permission grants" (d4696d09:cand/REVIEW.md:126) is emitted as the second line. - JSON result shape (closed):
{disclaimer, bundleVersion, result: allowed | refused | unresolved, reason: <code>, rule: <rule-name>, selection: {agentId, projectId, workspaceId, assignmentRef}, operation, consulted: [<recordRef | registryRef>], proposal: null | {result, reason, rule, selectedAssignmentRef}}. No payload bodies, no paths other than the requested target, no input snippets, no timestamps, no random ids, keys sorted. - Reason codes: the RUNTIME closed set (
d4696d09:cand/RUNTIME.md:97: allowed, invalid-request, not-authorized, stale-revision, missing-state, already-active, control-conflict, retired, unsupported-capability, audit-unavailable, unknown-effects, unsafe-replacement, request-id-conflict, expired) plus inspector-only codesunsupported-kind,unresolved,io-failure. Inspector-only codes are documented as not being runtime API codes (charter). - Exit classes: 0 allowed; 2 malformed input (parse, shape, bounds, graph incoherence, unsupported kind/operation); 3 simulated refusal or unresolved; 4 I/O failure (missing file, unreadable, not a regular file). Exit 4 output names the path as given on argv and nothing else; no bootstrap, no config read, no directory creation (A8).
- Errors from
strict-json.mjsreport byte offset and error class only, never the surrounding bytes.
10. Supported record kinds and operations
Supported kinds in increment 1: agent-definition, project, workspace,
registration, mission, task, assignment, decision. The remaining
candidate kinds refuse unsupported-kind (exit 2) when present in records,
rather than being ignored. Operation vocabulary is the candidate's 29-name enum
(d4696d09:cand/RUNTIME.md:55); a name outside it refuses invalid-request.
Operations whose semantics need runtime state the inspector cannot model
(stop, durability, audit) are accepted as names for permission intersection
only; the output states "scope": "permission-only" for them so a 0 is not
read as an executability claim.
11. Fixtures and the first demonstration
scripts/foundation/fixtures/demo.bundle.json implements the charter's
scenario: project P1 with workspaces W1 and W2; project P2; agent A registered
in P1 and W1 only; one mission, one task in W1, one selected assignment for A;
agent B registered in P2 with a delegation decision that does not cover
assignment.change. Derived bundles (same records, different selection or
proposal) cover:
| Fixture | Expectation |
|---|---|
demo-read-w1 |
exit 0, allowed, file.read on docs/notes.md |
demo-read-w2 |
exit 3, not-authorized, rule workspace-registration-missing, no W2 payloads in output |
demo-cross-project-exec |
exit 3, not-authorized, cross-project selection |
demo-reassign-message-only |
exit 3, not-authorized, message-is-not-authority, selection unchanged |
demo-reassign-no-observations |
exit 3, unresolved, unknown-effects |
neg-duplicate-identity, neg-stale-ref, neg-missing-parent, neg-cycle-task, neg-cycle-mission, neg-fork-supersedes |
exit 2, named rule |
neg-unknown-key, neg-dup-json-key, neg-bad-utf8.bin, neg-bad-path |
exit 2, before any permission text |
neg-empty-ops-layer |
exit 3, explicit empty = none |
neg-union-two-assignments |
exit 3, second assignment does not widen |
Fixture digests are generated by a checked-in helper
(node scripts/foundation/canonical.mjs --digest <file>) and the suite fails
if a stored digest disagrees with a recomputed one, so fixtures cannot drift
silently.
12. Test harness (isolated, non-effect, privacy)
scripts/test-foundation.sh, same shape as scripts/test-config.sh:
- Runs under
env -i PATH=… HOME=<mktemp> NO_COLOR=1 node …;MOSAIC_CONFIGunset; no~/.config/mosaic-devor data root exists in the sandbox. A canaryHOME/.config/mosaic-dev/config.jsonis absent and the test asserts it stays absent (no bootstrap on missing input, A8). - Non-effect: before and after every CLI invocation,
find <sandbox> -newer <stamp>must be empty andsha256sumof each bundle must be unchanged (A6). Also asserts no.pi,roles, orstatedirectory appears under the sandbox. - Privacy: fixture payloads contain a canary string
(
CANARY-DO-NOT-PRINT-<n>) in every unrelated record body and in W2's payload; every stdout/stderr capture is grepped for it and must be clean (A2, charter diagnostics rule). Exit-2 parse errors are checked for absence of any input bytes beyond the byte offset. - Determinism: each fixture runs twice; outputs must be byte-identical;
--jsonand text must agree onresult/reason/rule(A8). - Golden files:
scripts/foundation/fixtures/expected/<name>.{txt,json}, compared exactly. - Units (
node --test scripts/foundation/*.test.mjs): canonicalizer vectors and out-of-domain refusals; strict-json duplicate key, depth, size, fatal UTF-8; resolver graph rules one per test; permission intersection tables (empty=none, null=whole root, component-prefix, no union); proposal rules. - Cross-check (optional, skipped visibly when Python
jsonschemais absent):python3 cand/check.pyover each fixture'srecordsto confirm the hand-rolled validator has not drifted from the candidate schema. - The suite does not touch Dewey's extension installation,
~/.mosaic, the network, or any engine/process launch. It states this in its header. The existing required suites remain untouched and must stay green.
13. Unresolved decisions for the frozen charter
| Id | Decision | Recommendation |
|---|---|---|
| D-A | Language: Node ESM vs Python stdlib | Node ESM, zero deps (§3) |
| D-B | Directory layout before M20 | scripts/foundation/ with owner headers, no packages/ (§4) |
| D-C | Exit class for graph incoherence (cycle, stale, duplicate) | 2 (malformed input), not 3, so it cannot be mistaken for a policy refusal |
| D-D | Taskless operation set | {work.read, file.read} exactly; work.propose stays assigned-only |
| D-E | Registry content shapes for scope-role / agent-policy / project-policy | {restrictions} only, declared simulation-only in the charter; candidate schema does not define these |
| D-F | Simulated execution layer | selection.launchRestrictions, narrowing-only, default null |
| D-G | Requester model in proposal mode | agent-only (agentId + registration); no human/operator principal in increment 1 |
| D-H | Wrapper naming | scripts/foundation-inspect.mjs, no .sh shim |
| D-I | Delegation enforcement depth | require delegatedOperations ∋ assignment.change and subject coverage; do not model delegation chains |
| D-J | Whether bundles carry revision history | permit either head-only or contiguous full history per identity; forbid partial history |
One further point the charter should settle explicitly: whether a 0 result
for stop/durability/audit-class operations is acceptable at all in increment 1,
or whether those names should refuse unsupported-capability outright. My
recommendation is the latter for execution.stop-class names, to keep A9's
"understands the limitation" honest.
14. Feasibility verdict
Feasible as a bounded increment with no new dependencies, no package migration, and no runtime coupling. Estimated size: three pure modules of a few hundred lines each, one CLI wrapper, roughly twenty fixtures, one bash suite, and unit tests. The main risk is drift between the hand-rolled validator and the candidate schema; the Python cross-check in §12 and the enumerated set-valued field list in §6.1 are the mitigations. Nothing here weakens the accepted semantics; where the candidate is silent (D-E, D-F), the proposal is narrowing-only and simulation-labelled.
This note is evidence for darkwing's charter. It is not approval, and it authorizes no implementation.