Proposal 091: File-backed Configuration and Explainable Composition¶
Based on:
- Challenge 091: Predictable Operator Configuration
- Ontological Basis
- Core Values
- Vision
- Constitution
- Node
- Node UI
- Arca
- Middleware
- Temporal Storage Convention
- Proposal 019: Supervised Local Middleware
- Proposal 029: Workflow Template Catalog
- Proposal 033: Workflow Fan-Out and Temporal Orchestration
- Proposal 049: JSON-e Middleware
- Proposal 071: Sensorium Workbench
- Proposal 072: Capability Registry
- Proposal 080: Multiplexed Middleware Channel
- Proposal 085: Operator-Sovereign Extensibility
- Operator's Manuals
node:DEV-GUIDELINES.md
Related: Proposal 090: Inference Execution Provenance. Configuration explains selected behavior; P090 describes evidence about a realized inference. Neither descriptor substitutes for the other.
Status¶
Proposed. The operator requested this design direction on 2026-09-06. Existing mechanisms below are antecedents, not evidence that this horizontal contract is implemented. The dated audit is retained in Node. New schema and CLI names in examples are design candidates, not registered contracts or available commands. Implementation remains in the tracker. The operator resolved OQ-01/02/03 on 2026-09-06 as A/A/B respectively, with a component-name resolution contract and common-file fallback for OQ-03. These adopted design choices do not change the proposal's implementation status. The operator also selected retention of the existing middleware on/off implementation behind the shared P091 control contract; P091-007a owns the wrapper and call-site integration, not a replacement lifecycle engine.
Date¶
2026-09-06
Executive Summary¶
Adopt the operator-facing rule:
Every durable setting that controls Node behavior has an unambiguous key, a declaration in local configuration files, and explicit composition rules. GUI and CLI edit that same configuration. Every effective value can explain its sources, scope and derivation.
The objective is cognitive predictability, not the fewest settings. An operator should know where to look even when a new module adds a new configurable behavior. One interface must support global, module-instance and workflow-scoped settings, without making their meanings or authority interchangeable.
This proposal adds a common configuration address space, source-aware resolution, an inspectable change/apply lifecycle and shared CLI/GUI operations. It preserves JSON files, small domain-owned contracts and current package boundaries. It does not require a new configuration language, universal policy engine, or monolithic configuration service before delivering a useful first slice.
The reuse audit sharpens this direction: extend the existing Rust JSON primitive with source-aware configuration contracts, retain domain resolvers, and expose the same values to Python through the existing host capability/channel boundary. A shared programmer interface does not require two independent policy engines. Process startup must also work before that bridge is ready, using an explicitly bound configuration snapshot rather than a second live filesystem resolver.
Context and Problem Statement¶
Reuse baseline and design consequences¶
The dated Node Configuration Reuse Audit (node:docs/CONFIGURATION-REUSE-AUDIT.md)
retains the 2026-09-06 source/workflow tables, errata and scoped evidence.
It is input to P091-001a/001b, not exhaustive inventory or runtime completion.
Reuse json-utils for mechanical JSON composition; keep domain interpretation
with existing resolvers. Separate daemon bootstrap/materialization and the raw
loader's retirement-policy check from the new read-only path. Python consumes the
same Rust-owned contract via P080/retained host bridges or explicit snapshots.
Workflow scope reuses definition/handler and executor-instance bindings; Arca
already provides a non-JSON-e-Flow case.
Preserve known legacy source-selection/error profiles, toggle precedence and factory identity naming until explicit migration. Reuse sealed-registry CAS, tri-state admission, bounded carriers, field-wise disclosure and temporal storage as design idioms, not as interchangeable domain types. Decisions below own their configuration meaning; later code-audit updates belong in Node.
Affected documents and integration boundaries¶
This map identifies the configuration surfaces affected by P091, not new implementation claims or dependencies on completing every linked proposal. The linked solutions retain component ownership; each proposal links back to the planned integration. First-slice and wider-inventory work remain distinct.
| Surface | Related proposals | Owning solutions | P091 impact / boundary |
|---|---|---|---|
| Host configuration and operator UI | P052: Node UI desktop host | S000: Node, S001: Node UI | Shared addresses, source resolution, predicted-value plans and apply receipts; P091-005/006/008. Desktop-specific durable settings join the wider inventory, not the initial four-case acceptance. |
| Supervised modules, Python clients and on/off | P019: lifecycle antecedent, P020: bundled modules, P080: current channel executor | S019: Middleware, S004: Dator | Read-only acquisition, pre-channel snapshots, thin Python consumption and retained on/off wrapper; P091-003/005/007a/010/011. No revival of the retired supervised HTTP executor. |
| Workflow definitions, templates and parameters | P029: template catalog, P033: fan-out and temporal orchestration, P049: JSON-e / Flow | S003: Arca, S019: Middleware | Owner-bound durable declarations and source migration; P091-009a/009/010. Run state, template publication and orchestration semantics do not become configuration or a universal Flow language. |
| Separate-process configuration readers | P035: Agora, P067: shared offer catalog | S008: Agora, S033: Shared Offer Catalog | Inventory legacy selection/error profiles, consolidate readers and prove scoped consumption; P091-001a/001b/010a/011/015. No change to record, offer or relay authority. |
| Sensorium connector configuration | P045: Sensorium, P048: OS connector | S030: Sensorium | Wider module-local source/loader adoption in P091-001b/010a/015; observation/directive admission and signed action catalogs stay domain-owned. |
| Workbench configuration and sidecars | P071: Workbench | S042: Sensorium Workbench | Owner/stem resolution and signed-sidecar separation in P091-004/006/010/015; file edits cannot substitute for approval, revocation or actuation authority. |
| Inquirium profiles and adapters | P063: Inquirium, P064: implementation guidance | S044: Inquirium | Existing profile derivations feed shared explanation/consumption; P091-004/011/014. Inference semantics and provider execution remain in their existing layers. |
| Extension descriptors and resource envelopes | P085: operator-sovereign extensibility | S048: Operator-Sovereign Extensibility | Descriptor admission/withdrawal and existing limit algebra constrain composition; P091-002/004/006/014. File order is not signer authority or a replacement for an envelope. |
| Federation-derived runtime configuration | P076: federation identity and selector | S041: Federation Root | Preserve root-derived values as explicit domain derivations during inventory/resolution/migration; P091-001a/004/010/015. Root activation and authority remain separate, including restart-only application. |
| Discovery and API description | P072: capability registry, P068: API projection | S037: Capability Registry, S034: API Surface Projection | Register five operations with distinct read-dispatch/operator-control flags and project implemented routes/DTOs; P091-002/005a/015. Discoverability grants no authority and does not advertise unimplemented routes. |
| Commit/application history and recovery | P062: temporal storage convention | S028: Temporal Storage Convention | P091-006/007 consume append-only facts and rebuildable projections; the file/journal ordering protocol is P091 work, not atomicity already supplied by S028. |
P090: Inference Execution Provenance is a related reuse and semantic-boundary reference, not a configuration consumer changed by this proposal. Its carrier, projection, registry-CAS and inventory idioms inform P091; configuration never proves realized locality or egress. Likewise, the Corpus examples in the Node audit are precedents, not a requirement to change Corpus or Room contracts for the first configuration slice.
P091-001b/015 must extend this map and the reciprocal proposal links when the remaining inventory identifies another concrete integration owner. Do not link every domain merely because it has settings, or infer proposal-wide readiness from a reference or a completed configuration fixture.
Upstream intent and receiving-layer responsibility¶
User sovereignty, inspectability, explicit trade-offs and local-first operation motivate this work. The Ontological Basis is a critical lens, not a deduction of a particular directory layout. This proposal makes the engineering choice concrete: operators can locate decisions, inspect their derivation and change them without depending on one interface or memorizing hidden channels.
The receiving layer adds stable setting addresses, source and activation records, and testable change semantics. It does not create authority or rewrite the Constitution, operator-binding, consent, package-admission or revocation contracts.
Identity and revision vocabulary¶
The following names are normative within P091. Do not use an unqualified "generation" for different identities or derive one counter from another.
| Field | Identifies / owns change | Equality and fencing |
|---|---|---|
source-set/revision |
An immutable, context-selected source snapshot, including membership, selection rules, order, absent-target markers and content revisions | A canonical digest, not a counter; changes when any bound input changes. A relevant plan subset has its own explicit membership and revision. |
descriptor-set/generation |
Revision counter owned by one sealed, admitted descriptor-set artifact | Monotonic JSON-safe integer 1..9007199254740991, paired with descriptor-set/ref and descriptor-set/digest; expected-digest CAS, checked increment, exhaustion refuses. Never compare counters across different sets. |
resolution/id |
Immutable resolved view binding source-set/revision, exact descriptor-set identity, context, constraint snapshot and resolver/normalization revisions |
Content-bound identity, not an activation counter; equal values alone do not imply equal resolutions. Retained evidence allows replay. |
activation/generation |
Durable, per-component-instance application sequence binding the selected resolution/id and consumption receipt |
Owner-local monotonic JSON-safe counter with the same nonzero range; restart must not reset/reuse it. Pending attempts do not manufacture acknowledged consumption. |
descriptor-set/generation follows InferenceProviderRegistry's sealed-artifact
CAS idiom, not a global allocator. Registry admission may reuse
SemanticRegistryBinding.activation_generation as an existing domain field;
that field is not P091's activation/generation. Source revisions and resolution
identities are not aliases for either counter. Host persistence serializes CAS;
a pure checked increment is not cross-process synchronization.
Exact digests belong to authorized internal bindings. Redacted exports may replace sensitive identities with audience-scoped opaque handles; a public digest must not become an oracle for low-entropy settings or secrets. Such a projection is not a new editable source or an unredacted CAS credential.
Proposed Model / Decisions¶
1. A setting has an address, not an incidental storage location¶
A durable behavioral setting is an operator- or user-declared value intended to control future behavior until changed. Its address consists of:
- an admitted scope, such as node, module instance or workflow definition;
- a canonical key/path owned by the responsible component;
- the relevant definition or contract revision when meanings can evolve.
The physical file is an editable source for that address, not its identity. Use
JSON Pointer for exact tree selection, including escaping namespaced keys: the
literal key classify/labels-max occupies the segment classify~1labels-max.
Human aliases may exist, but must resolve to one address or report ambiguity.
Domain owners expose setting descriptors with type/schema, default source, supported scopes, composition rule, validation owner, sensitivity, editable targets, application mode and manual reference. Share the descriptor mechanism, not a hard-coded closed list of all components, workflows or profile meanings. Installed modules may contribute descriptors through host-admitted package data; configuration cannot invent a new executable merge operation or authority root.
Known active settings reject misspelled keys and unsupported scopes. Uninstalled or inactive module declarations may be retained as explicitly unvalidated source material, but must not silently take effect or count as validated coverage.
Descriptor admission, source precedence and runtime consumption are independent.
Use SemanticEntryHeader/sealing and SemanticEntryProvenance as reference idioms
for descriptor origin, and the registry's explicit unresolved ceilings for
admission constraints. Distribution/operator-enabled inputs are concrete sets in
that precedent; not every axis is already tri-state. Configuration descriptors
specialize the header idiom as CorpusThematicProfile does, without importing
Corpus semantics or making the semantic registry a JSON value merger.
For example, uninstalling middleware may withdraw its descriptor while leaving
50-<config_key>.json unchanged. Then descriptor-set/generation changes and
source-set/revision can stay equal; the new resolution/id cannot reuse the
old validation result. Retain the old resolution for historical explanation.
Report descriptor-invalidated for an affected prior application binding;
re-evaluate current-use authority and the domain's stop/degrade rules before new
work. Neither retained bytes nor an old consumption acknowledgement re-admits the
descriptor. Withdrawal, replacement and re-admission require explicit host facts.
2. File-backed declarations are the source of durable behavioral intent¶
Ordinary durable settings must not exist only in a GUI database, browser storage, process environment or service manager argument. GUI, CLI and text editing target the same declared files. A database may index configuration, record transactions or retain activation history; it must not become a competing hidden source of ordinary configuration values.
Distinguish the following classes:
| Class | File-facing contract | Independent boundary |
|---|---|---|
| Ordinary durable setting | Editable declaration or inherited default with an editable target | Component validation and application policy |
| Secret | Configured reference to an approved secret source, not mandatory plaintext | Secret-store access and disclosure policy |
| Signed consent, resource envelope, passport or revocation | Inspectable reference/projection and an explicit admission operation | Existing signer, operator-binding, scope, expiry and revocation facts |
| Runtime observation, conversation, counter, result or lease | Read model linked where relevant, not an editable setting | Owning runtime/domain state machine |
| One execution's input or temporary narrowing | Explicit invocation/context source, not silently persisted as a global preference | Caller authority and domain limits |
A source file can request a policy or reference a signed artifact; editing it does not manufacture approval. P071's approval ledger and derived sidecars retain their roles. Deleting a projected file is not revocation, and restoring an old file is not permission to restore an expired grant.
3. Preserve recognizable files and make source selection explicit¶
The initial compatibility baseline is the existing node configuration directory, module-local configuration and admitted package fragments. Ordinary operator JSON fragments already fit the existing loader; automatic target selection and the new diagnostic/recovery contracts remain P091 work. The selected V1 layout preserves existing workflow declarations and uses per-component operator fragments with a common fallback (resolved OQ-02/OQ-03):
<data-dir>/
config/
10-base.json
50-<config_key>.json
80-operator.json
90-operator-<component-name>.json
middleware/
<module-id>/config/
middleware-packages/
<package-id>/config/
storage/
<configuration-owner-store>/
- Keep existing
10-base.jsonand module fragments readable during migration. 90-operator-<component-name>.jsonis the default for sparse interactive changes;80-operator.jsonis the fallback defined below. GUI and CLI share selection and display its reason. Files are created only on an authorized commit, not inspection.- For the first JSON-e Flow case, use an existing instance entry in an
operator fragment, addressed by its admitted identity and revision. For example,
the inspected typed map is
middleware_json_e_flow_services; its instance-locallimitsremain owned by the Flow contract. The host binds a workflow context to that entry; a filename or display label is not workflow identity or authority. - The earlier
config/workflows/<workflow-file>.jsonandnode-scoped-config.v1sketch was not selected for V1. Reuse the declaring owner's current mechanism; this applies to other workflows too, not just JSON-e Flow (Decision 13). - Package configuration remains read-only factory input. Runtime-home files must declare whether they are operator inputs or generated projections; a generated projection must never be reloaded as a new independent declaration.
- Follow Solution 028 for persistence. Commit intent/outcome and application facts
use the existing storage/temporal-log boundary (logical streams such as
trace/config/commits/<target-ref>andfact/config/activation/<component-ref>); active views are rebuildable projections. Stream refs use validated opaque bindings, not client paths. Do not create separatecontrol/config-transactions/orcontrol/config-active/semantic stores. The storage owner chooses the physical backend; ordinary readers create neither journals nor projections. Transaction-local staging beside a target file is not a second source tree.
Source discovery records explicit file bindings, order and content revisions. Avoid arbitrary recursive includes or ambient searches through the home directory. The initial loader preserves documented compatibility order, while reporting legacy inputs and conflicts. New source kinds require bounded host-owned loaders.
Factory defaults must be inspectable by version and key, including defaults compiled into domain types. A generated reference file/catalog is acceptable; copying every default into active operator config is not required. Full expansion is an explicit export/pinning operation, distinct from sparse materialization.
Component name and write-target resolution¶
Reuse the existing factory inventory and admitted runtime/descriptor ownership
bindings. In identity_store.rs, middleware_seed_fragment_path builds
50-<config_key>.json; bundled_middleware_module_id reads module_id with a
config_key fallback. middleware_settings.rs already correlates config_key,
executor and module identity with a component. Consolidate these lookups behind
one configuration-owner binding; do not derive identity from module_name, a
directory basename or the text of a runtime component_id.
There is also a domain-owned filename precedent:
node:operator-storage/src/lib.rs exposes
operator_storage_profile_config_filename() for
90-operator-storage-profiles.json, consumed by
node:daemon/src/operator_storage_host.rs. Preserve this explicit source binding;
the new default convention must not rename it or capture it for another owner.
For a validated setting address, select the target in this order:
- Honor an explicitly selected, authorized source binding or an existing domain-owned write target. Do not accept an arbitrary client-supplied path.
- Otherwise resolve exactly one configuration owner. For admitted middleware,
reuse the existing
module_id, or its existingconfig_keyfallback whenmodule_idis absent. A host-admitted explicit file stem may override filename presentation without changing that identity. Native components use an explicit owner binding rather than masquerading as middleware. - A filename stem must match
[a-z0-9]+(?:-[a-z0-9]+)*and be at most 96 ASCII bytes. A matching resolved module identifier can be used unchanged. Do not lossy-normalize underscores, case, Unicode, slashes or dots into hyphens. A valid but non-filename-safe identity needs an admitted stem or the common fallback; an invalid explicit stem is an error, not a fallback trigger. - A subcomponent without its own admitted filename binding shares the owning
component's file. Keep its exact key path and instance identity inside the
declaration. A separate subcomponent stem needs an explicit unique binding;
do not split
component/middleware/..., dotted IDs or execution trace paths to invent hierarchy. Multiple instances may share one owner file while their declarations remain distinctly scoped. - When a known setting has no component-specific filename binding (for example
a node-wide setting), select
80-operator.jsonif it is an allowed target and report the fallback reason. Missing component files are created on commit; their absence alone does not trigger fallback. Unknown setting/owner, ambiguous ownership, reserved-name collision, path escape, permission denial or a failed write refuses; none redirects the write into the common file.
| Existing identity/source | Selected default or retained target |
|---|---|
config_key=arca, module_id=arca |
90-operator-arca.json, retaining the arca key |
config_key=sensorium_workbench, module_id=sensorium-workbench |
90-operator-sensorium-workbench.json, retaining the sensorium_workbench key |
Subcomponent owned by sensorium-workbench, no separate file binding |
The same Workbench file, with its own nested key/scope |
| Known node-wide setting without a component file binding | 80-operator.json, with an explicit fallback reason |
| Existing operator-storage profile writer | Retain 90-operator-storage-profiles.json through its domain-owned binding |
Validate uniqueness of owner/stem and reserved target bindings before admission.
Do not choose a winner by discovery order, auto-number a collision, or rename
existing 50-*.json files. A changed owner/stem is an explicit source migration.
The common fallback file may hold several owners; authorization stays per setting,
not per filename. Group multi-owner patches by their selected targets and preview
each group; do not silently collapse them into the fallback to obtain one write.
Retain existing deterministic filename ordering: 80-operator.json naturally
precedes 90-operator-<component-name>.json. No special comparator, source-group
priority or new filename parser is needed. Specific ordinary values may override
common ones; neither prefix bypasses domain constraints or scoped authorization.
An existing 90-operator.json, if present, remains an explicit legacy source until
an authorized migration previews its move/merge into 80-operator.json and proves
the intended effective values. Do not maintain two fallback files or silently
discard a later legacy override. Other source classes retain their declared order;
explanations and source-set checks bind actual order and owner-mapping revisions.
Multiple sources may declare the same address: that is layering, not an ownership
error. For every affected address, plan must predict the effective value after
the proposed commit under the pinned resolution inputs. Editing 80-operator.json
while 90-operator-arca.json still wins yields an unchanged predicted value and
a derived shadowed diagnostic. An explicitly confirmed shadowed commit is
allowed; its receipt says no resolved-value change for those addresses, not
"applied". It does not assert equality with an already different active runtime.
Reset removes only the selected declaration. If the shared file becomes {},
retain that empty object and its file permissions/identity; it contributes no
setting values. Removing the empty file is a separate authorized cleanup, not an
implicit side effect of reset or inspection.
4. Scope, precedence and authority are separate axes¶
Select sources for an exact context before composing their values. A typical ordinary setting sees distribution/package defaults, applicable operator files, and an admitted workflow overlay. File order is deterministic within each source class; scope selection must not silently change source authority.
There is no universal rule that the most specific scope wins. A workflow may replace an ordinary preference where allowed, but cannot widen a host safety ceiling merely by being more specific. A module cannot replace another module's namespace or an operator restriction through a late-sorting filename.
For every selected source retain its actual class: a copied factory value is not proof of signed operator approval, and peer policy is not local operator policy. Federation-root activation and other post-load domain transformations must remain explicit derivations rather than being mislabeled as a file override.
Bootstrap needs a small exception: CLI/environment can locate the node data/config root before those files can be read. Secret-source mechanisms are explicit too. Every other legacy behavioral CLI/environment input receives an inventory entry, a canonical setting address and a migration path. Do not add new environment-only behavioral settings. Existing invocation overrides remain visible as ephemeral sources until migrated; they never silently write themselves into durable intent. An active diagnostic snapshot records their non-secret contribution so online inspection can explain behavior which offline file inspection alone cannot know.
Authority context must be supplied by its admitting owner and pinned explicitly;
file presence, a package name or a copied factory value is not that context.
CorpusReasoningAnswerV2::bind_unknown is an analogous boundary rule: it requires
an explicit owner-supplied processing boundary instead of inferring it from the
respondent. P091 reuses this discipline, not Corpus's inference DTO.
5. Composition is deterministic and remembers its derivation¶
Separate source acquisition from a pure operation of the following shape:
resolve(retained_inputs, detail = values-only | with-derivation)
-> resolution + diagnostics + optional derivation_index
read(resolution_id, admitted_address) -> immutable value_or_refusal
explain(resolution_id, admitted_selection, projection) -> bounded explanation
The pure resolver performs no filesystem, network, secret-store, process or activation I/O. Host adapters acquire and authorize bounded snapshots; existing domain resolvers own policy meaning and current-use revalidation.
Default composition rules:
| Value/contract | Rule |
|---|---|
| Ordinary object | Recursively compose declared keys |
| Ordinary scalar or array | Later applicable source replaces the earlier value |
| Identified collection | Domain-declared replacement, keyed composition or append-only rule; never guessed from JSON shape |
| Safety limit or permission set | Domain-owned min, max, intersection or another registered relation |
| Signed approval/profile | Existing admission, revision, validity and domain composition rules |
| Incompatible values or unsupported operation | Typed refusal, not silent coercion or arbitrary selection |
Omission means inherit. An explicit value remains an explicit declaration even
when equal to today's default; this does not falsely claim that it narrowed a
P085 limit. null retains schema-defined meaning and is not a universal delete
operator. Removing a declaration from a chosen writable source restores
inheritance from that source; it does not remove another source's constraint.
If a domain supports tombstones, they are a separately declared operation.
Separate retention from materialization. Before a resolution can be provisioned or acknowledged, retain bounded replay inputs: selected source values/revisions, the exact descriptor artifacts and admission context, constraints, scope, resolver/domain-port versions, and normalization inputs/record. Secret values are not copied into public history; retain protected evidence or explicitly mark a historical explanation unavailable when it can no longer be reconstructed.
The startup values-only path need not construct contributor lists. A later
explanation deterministically replays these pinned inputs and lazily builds an
index by JSON Pointer, never reconstructing provenance from the final JSON or
today's files/descriptors. A merge/normalization log captured during resolution
is valid source evidence. The indexed view, not an unstructured event list, is
the query contract. Cache it in memory; authorized host materialization may persist
a rebuildable artifact keyed by resolution/id. Ordinary inspection still writes
nothing. Both modes must return identical values and validation/refusal decisions.
A subtree has per-pointer contributors, not one guessed owner. Preserve decisive, equal, shadowed and rejected inputs where needed to explain intersections, replacement of arrays/ancestors, empty objects and domain-derived paths. Do not promise one winner for an intersection, or allocate only for declared leaves while losing implicit defaults, structural replacement or derived values.
6. Declared, resolved, active and observed are distinct¶
| View | Meaning |
|---|---|
declared |
Exact declarations in selected source revisions, with validation status |
resolved |
Candidate values validated for the pinned inputs of one resolution/id |
active |
Resolution acknowledged as consumed by the owning component instance, bound to activation/generation |
| Runtime observation | What happened during an operation; linked separately, never inferred from a save |
Use separate closed, kebab-case types for application state and operation refusal.
ConfigApplicationState contains provisioned, active, pending-reload,
pending-restart, approval-required, application-rejected,
descriptor-invalidated and unknown. A reason code and owner binding accompany
every non-active result. ConfigRefusal is an operation failure, not one of these
states: missing-key, denied, unavailable-context, host-unavailable,
unsupported-contract, invalid-source, source-conflict,
descriptor-conflict, constraint-conflict, resolution-unavailable,
invalid-target, limit-exceeded, persistence-failed or
recovery-required. Refusal detail codes are versioned and bounded, not raw
error text. Serialization uses #[serde(rename_all = "kebab-case")].
provisioned means the host delivered the named resolution but has no consumption
acknowledgement. It must not be reported as active or collapsed into an
unexplained unknown. Component unavailability is a separate observation;
retain a last-known acknowledgement with freshness rather than claiming current
liveness. An apply failure preserves any prior acknowledgement and reports the
failed target separately. Descriptor withdrawal invalidates current admission,
not the historical fact that an older resolution was consumed.
P091-002 freezes a table-driven transition/receipt contract: provision precedes
acknowledgement, pending/approval states do not imply delivery, refusal is not
activation, and descriptor invalidation prevents automatic re-admission. An active
query uses retained inputs for the acknowledged resolution/id, not current files.
Multi-component queries return per-instance activation/generation and mismatch;
there is no invented atomically active whole-node counter.
Historical replay does not renew time-limited authority¶
Replay evaluates the retained inputs at their recorded validation time. It may
correctly reproduce a historical admission that is no longer usable. Keep that
preimage and resolution/id unchanged; current-use admission is a separate
evaluation bound to that resolution, phase, current constraint evidence and a
fresh host-owned time context. Record its evaluated/at, outcome and evidence
refs separately, not as a replacement historical verdict.
commit rechecks the plan's commit deadline and applicable authority windows.
Every apply, including queued dispatch, restart recovery and retry, rechecks
time-dependent authority/constraints immediately before applying behavior. A
commit receipt or provisioned snapshot is not a lease extending their validity.
Use the owning domain's interval semantics (an exclusive valid/until refuses
at or after its boundary); client-supplied replay time cannot establish current
validity. Unavailable/unreliable time evidence refuses as unavailable-context
with time-context-unavailable, rather than assuming the recorded time is current.
Expired constraints return ConfigRefusal::ConstraintConflict (wire
constraint-conflict) with reason constraint-expired; a not-yet-valid window
uses constraint-not-yet-valid. The attempted application is
application-rejected, with that reason and no new consumption acknowledgement.
Preserve the durable save and any prior historical acknowledgement. Renewal or
fallback that changes resolved values or admitted inputs requires a fresh
resolution and applicable approval, not silent recomposition during apply.
No additional application-state variant is needed for expiry.
Retain config-time-expiry-between-phases: plan at T under an envelope valid
until T+1h, commit at T+50m, apply at T+70m must refuse without lifecycle effects,
while historical replay still reproduces the original verdict. Controlled-clock
fixtures also cover pre-commit expiry, the exact expiry boundary, not-yet-valid
constraints, unavailable current time, and expiry during queued/recovered apply.
In-flight work retains its admitted binding, subject to expiry, immediate revocation and safety checks. Replay cannot bypass current-use authority or rewrite operation evidence. P090 locality and egress observations cannot be inferred from an active configuration. Atomic activation groups require a separate owner/protocol.
7. Explain any key or subtree through one read contract¶
The proposed CLI spelling is illustrative:
orbiplex-node-daemon explain-config /inquirium/resource_profile --workflow research --view resolved
orbiplex-node-daemon explain-config /inquirium/resource_profile --workflow research --view active --format json
The same bounded host read contract serves CLI and GUI. An explanation includes:
- exact address, context and requested view;
- value or explicit unavailable/redacted status;
- source ref, source class, revision/digest, file and JSON Pointer where applicable;
- the per-key derivation rule and participating, shadowed or rejected declarations;
- descriptor and resolver revision, and the bound constraint context;
- requested
resolution/id, acknowledgedresolution/id, per-instanceactivation/generationand application status; - writable targets, permission/approval requirements and a manual reference.
Read access is scoped independently of edit access. The host checks the caller and requested scope before acquiring sources; a key query must not become an arbitrary-file reader or reveal another participant's private configuration. An unavailable workflow, hidden source or denied scope produces a bounded refusal without leaking its contents. Source paths are resolved only through admitted bindings and confined loaders, not directly from a caller-supplied path string.
For example, a hypothetical classify/labels-max value of 8 can explain
min(distribution: 32, node: 16, workflow: 8). Another key in the same queried map
may inherit only its distribution value. An equal-valued declaration remains
visible without changing the existing domain resolver's narrowing semantics.
Offline declared/resolved inspection reads the selected files and available
versioned descriptors without starting middleware, fetching remote packages,
materializing defaults or retrieving secret values. Missing authority/context
produces an explicit unresolved constraint, not an optimistic approximation.
For package-supplied descriptors, offline validation is explicitly unresolved
unless a pinned export supplies the admitted descriptor set and its owner-issued
admission/constraint context. Use the semantics of
SemanticRegistryCeiling::Unresolved, never infer admission from installed files.
A historical export proves only resolution at that bound context, not current
package approval, revocation status or permission to execute.
Offline inspection does not claim active; a retained acknowledgement is shown
only as last-known with its resolution/id, activation/generation and freshness.
Authenticated CLI-to-host transport is compatible with read-only online
inspection; the prohibition concerns implicit source fetching and producer I/O,
not transport of the inspection request itself.
Separate a local diagnostic projection from a redacted export using per-field
Retain | Redact choices for values, source identities, paths and secret refs.
These choices request disclosure; they do not grant it. Redaction must not turn
an unresolved/refused result into an apparently validated one. Secret values must
not leak through losing declarations, diffs, diagnostics, hashes or error text.
Use opaque secret refs/revisions, not public hashes of low-entropy secret values.
Absolute local paths may be shown in authorized local inspection, but are not
written into committed documentation or portable/durable audit exports.
Bound source count, bytes, depth, derivation fan-out, subtree size, output size and
history retention. At a fan-out bound, require an authorized aggregate reference
or return limit-exceeded with a conservative summary; never silently drop a
constraint, refusal or contributor. Details may be unavailable, not falsely
complete. Compose diagnostic summaries through an explicit conservative decision
table (admit/warn/deny idiom), keeping reasons distinct.
8. GUI, CLI and file editing share the change semantics¶
GUI/CLI submit a source-targeted patch, expected source-set/revision, pinned
descriptor-set identity and constraint context, and intended application mode.
The host:
- checks caller authority, target ownership and path confinement before acquisition;
- acquires bounded inputs and binds the plan to a base
resolution/id; - patches the declaration, not the expanded effective tree;
- resolves/validates the candidate through the same domain ports as inspection;
- returns the source diff and predicted effective values for every affected address, including derived dependents, with shadowing, impact and approvals;
- rechecks all plan preconditions, the commit deadline and current authority windows under writer coordination;
- persists intent, atomically replaces the target, records durable outcome, then separately requests and records application with Decision 6's fresh time gate.
"Relevant source set" means every input that contributes to, or could shadow, a touched address or its dependent result: ancestor/object/array replacements, defaults, ephemeral overrides, and membership/order/selection rules too. Bind directory/source-selection membership so a newly appearing fragment conflicts even if no formerly selected file changed. If a domain cannot safely narrow this dependency closure, bind the full selected set. Descriptor-set and constraint preconditions are separate: unchanged source bytes do not preserve validation after withdrawal. Any changed bound input requires a new plan; diagnostics name the changed class without leaking unauthorized data.
A narrowed source precondition must not authorize activating stale, untouched values from a broader old resolution. Bind the complete application unit's inputs when that unit is replaced; otherwise require its owner's explicit scoped-apply contract. V1 may conservatively use the full selected set.
Keep source-conflict as the refusal family, with a closed, bounded reason:
affected-input-changed, unrelated-source-content-changed, or
source-change-unclassified. The second reason requires host proof that only
content outside the affected dependency closure changed, with source selection,
order, descriptor bindings and constraints unchanged. An ancestor replacement or
an unknown dependency is not an unrelated edit. Classify from retained/current
inputs, not merely the final value; disclose no unauthorized paths or values.
For example, an edit of owner A's independent address in 80-operator.json can
invalidate owner B's plan through whole-file fencing. The host may identify that
as unrelated; it still refuses the stale commit. A client may request a bounded
automatic replan preserving B's exact patch and target, but receives a new plan
with fresh preconditions and predicted values. This is not permission to reuse
the stale plan, silently commit/apply, or transfer its approval. Existing explicit
automation authority must independently cover any subsequent write. If safe
classification or disclosure is unavailable, use source-change-unclassified.
Host seeding, including middleware_seed_fragment_path writes, participates in
the same serialization protocol. Bootstrap materialization and pending recovery
finish before operator commits are admitted; later seed/package source updates
take the same host lock and invalidate affected plans. Reads never seed. Manual
non-cooperating writers remain a documented filesystem limitation, not an excuse
to omit coordination between the host's own writers.
GUI must expose the affected key and file, allow authorized target selection, and support explicit inheritance restoration. A later source that shadows the write must be derived and reported before saving; a saved-but-shadowed change is not an applied value. Commit may accept that explicit plan, with a receipt indicating no resolved-value change for the affected addresses. Redacted callers receive authorized projections of predictions, not leaked secret values or fingerprints. Persist only requested deltas, preserving unrelated declarations.
Manual editing remains a first-class path. A read/validate/apply operation sees
file edits through the same loader and validators without requiring GUI state.
For pre-existing or externally edited files, startup/explicit apply records an
admitted durable source snapshot and its resolution/id, not a fictitious GUI
commit or retroactive intent. This observed-source admission is separate from
the intent/replace/outcome protocol for P091-owned writes; both feed the same
application boundary. File access does not bypass approval of activation. Simultaneous
GUI/CLI edits use conflict detection rather than silent last-writer wins. External
editors should participate in the documented lock/edit protocol; do not claim
atomic compare-and-swap against arbitrary non-cooperating filesystem writers.
Successful configuration commit does not automatically authorize a separate external effect. Reload, restart, signed approval and irreversible operations retain their own contracts. Partial activation or an unavailable component is reported and reconciled, not disguised as successful whole-node application.
Existing middleware on/off remains behind the shared interface¶
Retain the existing middleware on/off implementation. P091 adds a thin host-owned wrapper around its settings and application operations, not another supervisor or a second implementation of toggle semantics. This follows P019's component control boundary and P080's dependency-aware lifecycle contract.
The shared P091 contract becomes the single programmatic control path for durable
enabled changes: GUI, CLI and retained middleware-settings endpoints use the
same source selection, validation, commit and application accounting. Existing
entry points remain compatibility adapters; they must not retain an independent
writer that bypasses the shared contract. Manual file edits remain first-class
declarations consumed through the same resolution and explicit apply path.
One control contract does not mean one physical configuration file.
Keep toggle eligibility, protected-component checks and runtime application with
their existing host/domain owners. Preserve dependency ordering, bounded shutdown,
operator_stopped, readiness and current authority checks. Operational start,
stop and restart remain distinct from changing durable enabled; the wrapper
must not silently persist those commands as configuration. A successful save is
not proof that the component is running or ready, and a shadowed requested value
must not be applied as though it were the resolved configuration.
The current control/middleware-settings.json may remain an explicitly declared
legacy source behind the wrapper until Decision 9's migration. Preserving the
implementation does not freeze its hard-coded file target or mutating read path:
small extractions and call-site changes are required to join the shared contract.
Do not create a permanent mirror between that file and operator fragments.
9. Compatibility and migration preserve intent¶
Migration is an explicit operation with a source inventory, preview, recoverable
backup, exact before/after resolution comparison and post-restart verification.
No automatic migration runs as a side effect of explain-config or validation.
The initial migration must handle:
- middleware toggle files, naming drift and their current precedence;
- factory seeding, generated runtime sections and copied defaults;
- module-local versus node-wide loading, including Python consumers;
- legacy file-selection behavior and filename ordering;
- standalone Node UI arguments and non-bootstrap environment/CLI settings;
- existing signed artifacts and domain-specific sidecar projections.
Do not guess whether an old materialized enabled was intentional. Preserve its
effective behavior as an explicit legacy declaration with uncertain intent, then
let the operator choose inheritance. A dry run must show which old sources remain
active; obsolete sources are retired only through an explicit transition, without
maintaining permanent bidirectional mirrors.
Restoring a previous configuration is a new validated change against current contracts and authority. Retain a protected pre-migration backup plus the source, descriptor, constraint and resolver inputs needed to establish its effective-value baseline. A restore fixture must prove equal effective values at all covered addresses, not just identical bytes. With incompatible current descriptors or authority, report incompatibility/refuse instead of passing a byte-only test or silently re-admitting an obsolete descriptor. Compare historical values using the pinned baseline, then independently validate the proposed restoration for current use. Counters must not roll back.
Restore does not reverse external effects, resurrect revoked permissions or justify fallback after integrity failure. Fresh startup with invalid inputs refuses or enters a named safe degraded mode. A running component may retain its last admitted resolution only under its domain's current-use rules.
10. Documentation is part of the interface¶
Add a shared Node configuration manual describing source locations, ordering, scope selection, the three configuration views, inheritance and change lifecycle. Component manuals retain the meaning of their settings and link to that common model. Their configuration tables should expose exact keys, types, default sources, allowed scopes, composition rules, sensitivity and reload behavior.
Reuse descriptor data for mechanical tables and CLI help where practical; domain rationale remains human-authored. Ship task-oriented HOWTOs for bootstrap, locating a value, editing a workflow, resolving shadowing and restoring inheritance. Every interactive setting should lead to its key, explanation and manual.
11. A bounded first slice, then an explicit coverage gate¶
The first retained vertical contains four cases:
- An ordinary daemon setting, such as
logging.level. - A middleware
enabledsetting edited through GUI and a file. - One workflow-scoped configurable parameter, initially demonstrated with an existing JSON-e Flow instance. An Arca non-Flow contract fixture must also demonstrate that the common scope is not specific to this executor.
- An Inquirium resource limit using P085's actual narrowing/envelope semantics.
Each case must support locate, explain, edit, validate, save, apply, inspect after restart, and restore inheritance. A full subtree case must combine inherited and overridden leaves, including a value constrained by multiple sources.
This vertical does not prove coverage of every component. P091-015 closes the remaining inventoried durable settings and introduces a gate for newly added ones. Completion of this proposal requires that wider coverage, not just a useful demo.
12. One programmer contract, existing host bridges, explicit standalone mode¶
Expose a small data-oriented interface rather than requiring each consumer to know directory order or reconstruct host policy. The following operation names describe candidate contracts, not registered capabilities:
| Operation | Input | Result / boundary |
|---|---|---|
| Describe | Admitted scope and bounded key selection | Domain-owned descriptors, supported operations and source targets visible to this caller |
| Read / explain | Admitted scope, JSON Pointer, view and resolution/id (active also binds component receipt) |
Immutable value/subtree, validation status and bounded derivation; no implicit reload |
| Plan change | Source binding, sparse patch/reset, base resolution/id and expected input identities |
Candidate resolution, predicted effective values and semantic impact; no writes or activation |
| Commit | Authorized plan and expected source/descriptor/constraint bindings | Durable save receipt or typed conflict/refusal; no implicit application |
| Apply / inspect application | Durably admitted resolution/id, admitted component instance and expected activation/generation where present |
Existing lifecycle operation and separately queryable application receipt |
The port has the same value, refusal and identity semantics in Rust and Python,
not necessarily identical function signatures. Distinguish missing key, denied
scope, unavailable context/host, unsupported contract, invalid source, source-set
conflict and pending/rejected application. A getter must not silently turn any of
these into a local default; defaults belong to admitted descriptors/sources.
Acquire/resolve once, then query an immutable handle explicitly carrying
resolution/id. The high-level CLI may acquire that handle for the operator;
low-level read(resolution_id, address) must not rescan files per key. Unknown,
expired or evicted identities return resolution-unavailable, not a fresh view
under an old ID. Reading a newer view never activates it.
Recommended reference implementation:
- Rust consumers use the pure configuration contract and domain ports directly.
Extend/reuse
json-utilsfor ordinary mechanical merging; keep source identity, configuration validation and domain policy out of that utility crate. - Supervised Python uses a small non-UI configuration client. Once attached, it
invokes the same host operations through P080
host-capability.invoke, usingChannelJsonRuntime.invoke_host_capability. Retained HTTP deployments use the existing/v1/host/capabilities/<capability-id>family. Both terminate in one handler/contract; transport is not a second semantic implementation. - Before process launch, the host can supply a bounded, immutable, module-scoped
snapshot bound to component/instance, schema and
resolution/id. Python startup decodes that snapshot instead of depending on a not-yet-ready channel or merging mutable directory contents again. Reuse supervisor provisioning and admitted runtime-home projections; the snapshot is derived, not another editable source. Read-only access and source binding are enforced by the host, not inferred from a module's claim about a file digest. A component reports successful consumption separately from the host merely making the snapshot available (provisioned). Use a digest/size/inline-or-ref carrier; 16 KiB is the initial inline ceiling, with smaller transport limits allowed and finite total limits frozen in P091-002. Above it, reuse the admitted object store. Before channel readiness, the supervisor provides a verified scoped file/handle; startup cannot require a capability call to retrieve the snapshot that initializes the channel. Process environment carries a locator/binding, not the whole configuration. Content addressing alone grants no access. - A Python unit test or standalone library may receive an explicit value snapshot by dependency injection. Offline operator resolution can use the same Rust core through a one-shot CLI adapter without starting the daemon. A Python-only installation can consume an exported snapshot; full live resolution without a Rust helper is outside selected V1 scope (resolved OQ-01), not an implicit fallback.
This follows inference.policy.evaluate and service.order.result.prepare: Python
packages requests and checks bound responses; Rust owns shared semantics. It does
not require all configuration clients to depend on the UI helper package. Reuse
its environment/HTTP mechanics through a non-UI seam where justified.
Read access is separately admitted by scope/key, including for a module reading its own configuration. Operator plan/commit/apply stays on the operator control boundary; a middleware host token or self-asserted component id is not a daemon control credential. Any delegated write needs a separately scoped grant. Register all five operations in one discoverable P072 inventory, with the following proposed policy. They remain unimplemented candidates until P091-002 registers their exact contracts:
| Candidate operation | dispatchable |
host-route |
docs.human-registry |
Invocation boundary |
|---|---|---|---|---|
config.setting.describe |
true |
true |
true |
Scoped host read |
config.value.explain |
true |
true |
true |
Scoped host read, including value-only selection |
config.change.plan |
false |
true |
false |
Authorized operator control |
config.change.commit |
false |
true |
false |
Authorized operator control |
config.activation.apply |
false |
true |
false |
Authorized operator control |
docs.human-registry denotes docs: {"human-registry": ...}, separate from
flags. It selects the curated CAPABILITY-REGISTRY.md / .pl.md tables;
it is neither a visibility permission nor a general "documented" flag. Include
the two common read contracts there, following inference.policy.evaluate;
keep the three operator-control entries out of that table, following
operator.extension.lifecycle. All five still belong in the machine registry,
appropriate API projection and operator documentation. P091-002 synchronizes the
two human tables and passes make check-capability-registry on registration.
All use surfaces: [host-local], advertisable: false,
federated-discovery: false and passport/eligible: false. Do not invent a
signing domain for an unsigned local operation (signing-domain: false). Registration/discovery is not a
grant: generic module dispatch must refuse the three non-dispatchable operations,
even when it knows their schemas. Host routing independently checks operator
credentials and target scope. Discoverable descriptors are projected only within
the caller's authorized scope; no unrestricted enumeration.
Do not send the complete node configuration or secret values to every module. Secret references retain their independent access rules; a configuration snapshot does not issue or renew a grant.
No silent switching is allowed from the host-managed mode to ambient file/env loading after timeout, denial, version mismatch or channel failure. Retaining an already admitted snapshot is a domain-governed action, never evidence of current authority. Migration may retain an explicitly selected legacy loader with visible coverage limits; it must not claim P091 conformance or mix old and new sources.
13. Workflow configuration is an owner-bound scope, not an executor synonym¶
In P091, workflow settings are durable declarations controlling a named workflow's future behavior: for example parameters, dependency/selection policies and limits supported by that workflow's owning contract. This is broader than JSON-e Flow and independent of the plan language or implementation language. One run's inputs, results, retries already performed and progress remain invocation data or facts, not editable configuration. A template example is not active operator intent until explicitly selected and instantiated/admitted.
Reuse existing owner-specific definitions and configuration mechanisms (OQ-02 A), with a thin configuration binding containing:
- the admitted owner/handler identity and definition or instance identity;
- the definition/source revision and relevant plan/setting contract revision;
- an exact setting path, editable source binding and domain-owned validation;
- the bound run context when inspecting an invocation's effective values.
workflow_kind selects a handler family, not a unique workflow, filename or grant.
Resolve it through the existing supervisor contract and bind the actual owner;
missing or ambiguous handlers do not become JSON-e Flow or another default engine.
For JSON-e Flow, the port uses the existing executor/instance binding instead;
do not invent a workflow-kind registration merely to expose its configuration.
CLI workflow aliases must resolve to these identities, not just match a label.
Arca's arca.workflow configuration and JSON-e Flow's existing instance fragments
provide two concrete source adapters. Their parameter schemas, normalization,
merge constraints and apply rules remain domain-owned. Future workflow handlers
can supply the same small descriptor/resolution port without a central enum of
workflow meanings or a mandatory JSON-e representation. P091 is not a replacement
orchestration engine and does not claim that every declaration is executable by
every handler.
The generic definition CRUD currently stores open plan values in append-only
records. P091-001a classifies fields needed for first-slice bindings; P091-001b
classifies the remaining fields. Neither exempts durable behavioral controls
merely because they are stored as domain resources. Such controls need file-backed
sources and owner-mediated projection/admission to claim P091 coverage. Retain
definition/run identity and historical records; migrate selected controls with
explicit source/revision bindings rather than adding an unsynchronized JSON copy
of the whole workflow store. Authoring or editing a file never triggers a run.
Non-file-backed controls remain a named migration gap until P091-010/P091-015
cover them. This preserves 2A without pretending that all current workflow CRUD
already satisfies the file-backed rule.
The first live acceptance can remain JSON-e Flow. P091-009a requires an early Arca contract fixture for scope, source selection and refusal; P091-015 owns the remaining actual consumers. This demonstrates an open boundary without making all of P029/P033 or every orchestration extension a prerequisite of the first slice.
Implementation Recommendations¶
Ownership and minimal reusable core¶
Follow node:DEV-GUIDELINES.md: immutable data, domain-owned semantics, validation
at boundaries, pure transitions, and refusal/replay evidence before promotion.
Use configuration-core, not config (an existing data directory). Extract
configuration-host only when multiple host consumers justify that seam; do not
create an empty architectural layer. Add dependency-boundary checks with every
current higher-layer consumer listed, including provider/runtime adapter crates.
| Responsibility | Recommended owner | Must not absorb |
|---|---|---|
| Mechanical JSON value transformations | Existing orbiplex-node-json-utils; keep its public value behavior compatible |
Configuration source order, provenance, I/O and policy |
| Setting addresses, source records, source-aware composition and derivation index | configuration-core, over existing json-utils; usable by daemon, standalone Rust and offline helper |
Filesystem, daemon lifecycle, UI, inference-domain types or domain authority |
| Domain descriptors, constraints and semantic derivations | Existing component core/host contracts | Other domains' policy meanings |
| Bounded source acquisition, target authorization, durable writes and activation coordination | Node host/daemon adapters | A second implementation of domain merge rules |
| CLI and GUI | Clients of shared read/change/apply contracts | Hidden settings stores or client-side policy authority |
| Signed approvals and revocation | Existing P071/P085 and signing owners | Ordinary configuration becoming a signing bypass |
| Materialized module inputs and Python access | Supervisor-provisioned scoped snapshots plus a thin client of the shared host contract | Independent ad-hoc Python/Rust precedence rules, broad config access or a bootstrap RPC cycle |
Do not repurpose config-sidecar-core as a generic overwrite engine: its
append-only conflict contract is intentional. Reuse Inquirium's existing profile
resolver and retain its digest encoding and source semantics. P091 may wrap or
reference that result; it must not silently redefine existing signed bytes.
Extract shared owner lookup and a validated filename builder from the current
factory/seed helpers rather than creating another component-name registry. Keep
legacy 50-<config_key>.json spelling unchanged, including underscores. Apply the
new safe-stem contract to newly selected operator targets only. Reuse explicit
domain filename functions as admitted target bindings; a prefix match alone does
not enroll an existing file into the new ownership model.
Data contracts before endpoints¶
The following are proposed V1 shapes, not schemas already shipped. P091-002 owns their complete request/response bindings and executable constraints.
| Candidate artifact | Required semantics / representative fields |
|---|---|
config-setting-descriptor.v1 |
setting/address (scope, pointer, contract revision), value/schema-ref, scopes/supported, composition/rule, validator/ref, sensitivity, application/mode, targets/editable, manual/ref, descriptor provenance and canonical digest |
config-source-binding.v1 |
source/ref, source/class, content source/revision, optional file/pointer, order/index, explicit admitted/by; absent targets and selector membership represented in the source-set snapshot |
config-resolution.v1 |
resolution/id, source-set/revision, descriptor-set identity, pinned inputs/constraints/context, resolver/normalization revisions and resolved/at; activation identity belongs only to the separate receipt, never this preimage |
config-derivation.v1 |
derivation/digest, derivation/size-bytes, inline or derivation/ref; per-pointer contributors (source/ref, role, rule), completeness and aggregate/ref when required |
config-describe.request.v1 / config-describe.response.v1 |
Admitted bounded selection and projected setting descriptors; refusal before acquisition |
config-explain.request.v1 / config-explain.response.v1 |
view: declared \| resolved \| active, exact binding, detail selection and field-wise disclosure policy for values/sources/paths/secret refs; validated value or explicit unavailable/refused result |
config-change-plan.v1 |
Source-targeted patch/reset, base and candidate resolution/id, explicit expected/source-set-revision, expected descriptor-set identity/constraints, predicted values/impact and expiry |
config-commit-receipt.v1 |
Transaction/plan refs, verified source outcome, candidate resolution/id, per-address resolved-value impact; never an application acknowledgement |
config-activation-receipt.v1 |
Component-instance binding, resolution/id, activation/generation where allocated, ConfigApplicationState, reason and consumption/freshness evidence |
Freeze separate closed enums for ConfigView, source classes and contributor
roles (decisive | shadowed | equal | rejected); domain refusal codes extend only
through a versioned contract. Descriptor provenance follows
SemanticEntryProvenance (distribution-default | operator-package); it is not
the value source-class enum. That enum must also represent compiled defaults,
module/node/operator declarations, invocation overrides and admitted constraints
identified by P091-001a. A generated projection is never an independent source.
Use typed ConfigApplicationState and ConfigRefusal from Decision 6.
Reuse JCS through CanonicalJsonProfile::JcsV1 and
sha256_base64url_canonical_json_prefixed, with a distinct declared domain per
artifact. Keep display labels/manual text out of semantic identity using a
specified canonical preimage, not arbitrary field deletion. Source content
revisions bind the admitted source content; commit/recovery additionally binds
exact file bytes. Freeze these distinct preimages. Resolution identity excludes
presentation and bookkeeping timestamps such as resolved/at, but includes any
time/expiry context that actually affects historical validation. Decision 6's
current-use time evaluation is separate and cannot renew this preimage. Full
internal digests may be sensitive; projected handles/digests must not expose a
hidden preimage.
When signatures are needed, use existing DomainTag / apply_domain_wrap;
do not change another contract's signed bytes.
Reuse bounded carrier and projection idioms from P090, not its inference DTOs.
Index derivation by JSON Pointer (for example BTreeMap<JsonPointer, ...>),
including structural and derived nodes needed to explain a declared selection.
Bound contributor count independently of pointer count: a map alone is not an
O(number-of-declarations) guarantee. Lazy materialization reduces startup work,
not mandatory retention or validation. Digest/ref/size and aggregate consistency
need negative fixtures; refusing excessive detail must preserve the decision.
DTOs crossing P080/HTTP or export/import are middleware contracts: use Schema Gate
with the appropriate Ingress/Egress/Import/Export checks at the actual
boundaries. Editable local configuration files may use a direct local JSON Schema
validator as allowed by DEV-GUIDELINES; document the local-only rationale. This is
not a requirement to turn every source file into a middleware DTO.
JSON Schema validates shape; pure core/domain ports enforce relational rules and
authority. Every conditional if carries the discriminating required fields.
New schemas carry x-dia-workflow, x-dia-status and x-dia-basis naming
doc/project/40-proposals/091-file-backed-configuration-and-explainable-composition.md.
Register plan/commit/apply request/response bindings as well as their plan/receipt
values; use the five-operation route matrix in Decision 12. Synchronize schema
mirrors, generated index and COVERAGE only when those artifacts actually exist.
Reuse ports and transport without copying semantics¶
Keep transport-neutral request/result DTOs and pure source/derivation operations
outside daemon internals. Do not introduce FFI, PyO3, another long-running process
or a custom RPC protocol merely to let Python read configuration. Existing P080
channel and host dispatch already provide that seam. Register new operations
through P072 and Schema Gate; use the existing operator control family for writes
and lifecycle. Under the retained-on/off decision, existing
/v1/middleware/settings endpoints become compatibility adapters to those
operations, not a second write path.
When derivation is requested, capture contributors during composition/replay, including equal and shadowed declarations. Startup may retain only Decision 5's complete replay inputs. Differential fixtures cover both modes; the final merged object alone is never provenance. Domain ports return their existing result plus explainable references, not instructions for Python to repeat domain policy.
The Python facade belongs with shared middleware helpers, not under an Inquirium
provider adapter or UI-only dependency. Inject a snapshot reader/host-call function
so domain tests do not need a daemon. Verify response schema, selected scope/key,
resolution/id, receipt bindings and declared completeness before exposing values.
Bound request/response bytes, depth, deadlines and queues on both sides; the
inspected generic HTTP
HostClient uses an unbounded response.read(), so it is a transport starting
point, not proof of P091 resource safety. Bind caller identity from the admitted
session/token and host mapping, not a caller-supplied header or JSON field.
At bootstrap, project only the configuration needed by the admitted component;
keep snapshots out of directories read as editable config layers. Missing or
mismatched launch snapshots refuse startup in managed mode. After startup,
reads target named resolution/id values; apply additionally fences
activation/generation. Reading a newer resolution does not activate it. Pin an
offline one-shot Rust helper through the existing installation/launch contract;
use bounded
structured I/O without shell interpolation, and report missing binary or contract
mismatch. Never download a helper or fall back to a different resolver implicitly.
Wrap existing middleware toggles and repoint callers¶
P091-007a integrates the shared contract with the existing implementation:
- Reuse the domain checks in
node:daemon/src/middleware_settings.rsand the application behavior ofapply_middleware_enabled_runtimeinnode:daemon/src/middleware_supervisor_host.rs. Extract narrow helpers where the current operations combine validation, materialization, persistence and snapshot reads; do not copy their domain rules into the generic resolver. - Repoint the middleware-settings route handlers and their Node UI/CLI callers to the shared use case. The one-way call chain is client or compatibility endpoint -> P091 host wrapper -> retained domain/persistence/application primitives. The wrapper must not call a compatibility endpoint back into itself.
- Route the selected source update through P091-006's commit contract, reusing
compatible low-level persistence code without a second legacy-file write.
Bind runtime application to the validated
resolution/idthrough P091-007. Existing live start/stop stays available; do not replace it with an unconditional restart requirement. Separate explicit bootstrap materialization from reads, as required by P091-003. - Retain regression fixtures for both compatibility and new entry points: identical source/value resolution, one declaration commit per change, no duplicate lifecycle dispatch on retry, protected/unauthorized refusal, provider dependency closure and operator-stopped behavior. File edits, shadowed writes, live application, save-success/apply-failure and post-restart inspection must preserve the distinction between desired, active and observed states.
This is a bounded integration and call-site refactoring task, not a middleware rewrite. P091-010 separately owns physical source migration and retirement of obsolete inputs; wrapping alone does not establish migration completion.
Read path and derivation retention¶
Separate discover/read, resolve, plan, commit and apply functions. Remove
seeding and toggle-file writes from the read path; keep explicit bootstrap and
materialization commands. Preserve evidence of defaults and later normalizations
before typed decoding or projection discards their origin.
Use immutable bounded replay inputs (Decision 5). Online active inspection binds the component's acknowledged resolution, while offline resolution uses explicit admission evidence or an unresolved constraint. Configuration acquisition must detect changes during reads and return a consistent snapshot or conflict; even one source changing cannot produce a mixed-revision answer. Pin the descriptor-set artifacts as well as source content. Retain them before provision/acknowledgement, without granting a historical export present-day authority. Expired/evicted evidence is explicitly unavailable, never silently reconstructed from current inputs.
Keep legacy acquisition profiles explicit until migration: selected suffixes, ignored files, root shape, missing-directory behavior, read/parse failures and module/node ordering. New admitted P091 sources fail on malformed or unreadable selected input. This is a visible compatibility change for Agora's skip behavior, not something to "preserve" by reporting a partial configuration as fully valid.
Write, activation and recovery boundaries¶
V1 commits one target file. Use Solution 028's append-only facts plus rebuildable
read projections; reuse StorageWrite::append, StorageRead::read_stream and
the temporal-log helpers as appropriate to the owning storage adapter.
append_corpus_inference_evaluation is a precedent for decision-before-transition,
not an existing atomic file/journal implementation.
The ordering contract under the shared writer lock is:
- Revalidate plan, authority, source selection/membership and descriptor/constraint
bindings. Persist a durable
intentwith transaction/plan ID, confined target, old byte digest or explicit absence, intended new byte digest, expected identities and a protected recovery payload/reference. No mutation before durable intent. - Prepare a unique target-directory temporary file, preserve required permissions, validate bytes, flush it and atomically replace the target. Apply platform directory/file durability primitives. The atomic rename protects target-file shape, not atomicity with the journal or non-cooperating editors.
- Append a durable
outcomebound to that intent and verified replacement. Return save success only after that outcome. If outcome persistence fails after replacement, return recovery-required with the known save uncertainty, never "unchanged" and never start application. - Apply is a separate authorized operation after a reconciled committed outcome. Re-evaluate time-dependent constraints with fresh host time at the application boundary, including deferred/recovered attempts, as required by Decision 6; expiry refuses without lifecycle effects. Append per-instance application and consumption facts. Rebuild active projections from those facts, not from the currently editable file.
Recovery scans bounded unresolved intents under the same coordination. If the target equals the old binding (including expected absence), record not-committed; if it equals the intended new binding, record a recovered committed source outcome, then independently revalidate for any requested application. Equal old/new digests permit a no-op outcome but do not prove that this process performed a replacement. A third digest, unexpected absence, invalid bytes or unreadable target requires an explicit conflict/recovery result; never overwrite it from an old intent. Digest comparison proves content equality, not which writer produced it. It cannot establish causality against arbitrary outside writers or restore their lost intent.
Retain idempotency keys and bounded recovery inputs; a retry cannot duplicate declaration commits, application dispatch or external effects. An uncertain external effect requires owner reconciliation, not a blind retry or an exactly-once claim. Test interruptions before/after intent, rename and outcome, plus disk-full, retention eviction, permission failure and journal corruption. Garbage collection cannot discard an unresolved intent or its required recovery evidence. No cross-file atomicity is claimed by V1; multi-target plans expose separate commits and visible partial completion rather than silently collapsing targets.
A restart-only setting is acceptable when its owner cannot reload safely. Neither recovery nor restart may weaken current-use revocation, roll counters back, rewrite historic operation evidence or label last-known consumption as current liveness. New file bytes and new resolution identities do not create permission to execute.
Reuse middleware supervisor start/stop and daemon control operations for application. Inventory their actual capabilities before labeling a setting reloadable or restart-only. V1 requires an explicit apply action (which may be explicitly combined with save), not a new filesystem watcher that silently activates every file edit.
Conformance and drift checks¶
Maintain node:docs/configuration-inventory.v1.json, mapping each behavior to its
address, sources/writers, validator, resolver, application mode, manual, evidence
commands and retirement owner. Reuse the structural/status/verification idiom of
check-inference-provenance-inventory.py and the middleware listener inventory,
not their domain-specific field enums. P091-001a delivers the initial artifact
and node:tools/check-configuration-inventory.py; CI runs structural checks and
--verify-current in node:.github/workflows/docs.yml from the first day.
Only done/partial entries assert current executable evidence.
planned/pending remain visible gaps; an empty verification set is not
completion. Bound verification count (initial maximum 64), execute only
repository-reviewed commands, and use --require ID for scoped checks.
P091-015's --promote rejects unfinished required coverage.
Track non-settings and legacy exceptions explicitly. Add
node:tools/check-config-source-boundaries.py for configuration acquisition,
behavioral environment reads and duplicate configuration merging outside approved
seams. Use a reviewed source inventory/allow-list with ownership and narrow
exemptions for bootstrap, explicit secret sources, transport plumbing and tests.
Do not ban all std::env, env::var or JSON transformations. In particular,
Node UI's explicit password_env is not an undeclared setting fallback.
New durable env-only behavior and unregistered configuration loaders fail the gate.
Named legacy acquisition profiles may remain visibly incomplete during migration.
Test ordinary merge order with reference/golden cases; it is not generally commutative or associative across type replacement. Property-test only laws valid for the specific domain algebra, such as monotonic narrowing or intersection. For all covered settings, test determinism for fixed inputs, equivalent CLI/GUI/file changes, exact replay of acknowledged resolutions and no mutation by inspection.
Retain shared golden fixtures for Rust direct calls, the Python facade and each
supported transport/offline mode: nested maps, arrays, null versus missing,
scalar/object replacement, equal-valued layers, escaped JSON Pointers and mixed
subtree origins. Parsing fixtures include duplicate keys, non-finite numbers and
numeric representation limits; freeze a common admitted JSON profile rather than
inheriting Python's permissive json.loads behavior. Test invalid selected files,
source ordering, defaults isolation, source/descriptor/resolution/activation mismatches, unauthorized namespace,
bounded response refusal and absence of fallback after host denial/unavailability.
Under OQ-01 A, independent Python source resolution is not a second conformance
target. Tests cover the thin client, injected/exported snapshots and the installed
Rust offline helper; host refusal never activates a native legacy resolver.
Target-selection fixtures must cover both Workbench spellings (JSON key versus
module name), missing module_id, subcomponent inheritance, explicit domain
targets, reserved-name collision and refusal without fallback. An ordering fixture
must use the ordinary loader with 80-operator.json and detailed 90-operator-*
files, plus an explicit legacy 90-operator.json migration case. Workflow fixtures
cover JSON-e Flow and Arca without translating one into the other's plan schema.
Security is a prerequisite to exposing new read/control routes, not a late UI polish step. P091-005a owns these named executable fixtures:
| Fixture ID | Required refusal/disclosure proof |
|---|---|
config-security-path-escape |
Traversal, symlink escape and unsafe target bindings refuse without reading/writing an escaped source |
config-security-cross-module |
A scoped client cannot read or edit another module's settings |
config-security-losing-secret |
Secret-bearing losing/equal/rejected declarations remain protected in values, derivation and errors |
config-security-diff-secret |
Plans, predicted values, receipts and failed changes do not leak secrets |
config-security-digest-oracle |
Low-entropy hidden values cannot be tested through exposed digests, sizes or equality fingerprints |
config-security-scope-enumeration |
Describe/list cannot enumerate unauthorized owners, addresses or source existence |
config-security-deny-before-acquire |
Instrumented loaders prove zero acquisition, source fetch or secret-store calls on denied requests |
config-security-control-dispatch |
Generic module dispatch refuses plan/commit/apply; registry visibility and host attachment do not confer operator authority |
These are fixture requirements, not an invented existing security-review
command. Verify each supported transport and offline/export boundary as applicable;
a later adapter cannot inherit untested security coverage.
When implementation changes coverage, reconcile
node:docs/implementation-ledger.toml, regenerate
node:docs/IMPLEMENTATION-LEDGER.md through
node:tools/generate-implementation-ledger.py, and synchronize
node:docs/MVP.md for any affected hard-MVP scope. P091-002 (contract surface),
P091-014 (retained acceptance) and P091-015 (wide rollout) each include this
checkpoint. Docs-only planning does not promote runtime status.
Trade-offs¶
- Benefit: one discoverable control model reduces memorized locations and makes GUI changes reproducible without making GUI mandatory.
- Benefit: per-key derivations explain layered maps, constraints and shadowing without forcing operators to manually reconstruct merge order.
- Cost: retaining source identity and application history adds bounded metadata and requires explicit reload/recovery contracts.
- Cost: manual editing and concurrent UI writes require cooperation and conflict handling; an ordinary filesystem is not a distributed transactional database.
- Deliberate limit: uniform operations do not make every limit overridable or eliminate signed approvals, secrets stores and domain state machines.
- Migration risk: blindly flattening defaults or deleting legacy toggles can change behavior. Preserve current intent before improving inheritance ergonomics.
Alternatives considered¶
- One giant file: easy to locate, but obscures ownership and increases write conflicts; keep a common address space across bounded sources instead.
- GUI database plus periodic export: leaves files as a secondary mirror and violates the promise of first-class text editing for ordinary settings.
- Universal deep merge: simple mechanically, but unsafe for permissions, append-only approvals and P085 limit algebra.
- More manuals only: useful immediately, but cannot explain exact active sources, detect stale resolution bindings or enforce shared write semantics.
- New configuration DSL first: defers operator value and adds another model to learn; keep JSON and typed data contracts for the initial implementation.
Failure Modes and Mitigations¶
| Failure | Required behavior/evidence |
|---|---|
| GUI stores a preference outside the declared file sources | Inventory gate rejects coverage; UI names the source it actually changes |
| A read seeds or rewrites files | Snapshot-before/after test proves zero filesystem mutations, producer starts and implicit source-fetch network calls |
| Removing an override writes today's default instead | Inheritance test changes the lower source and observes the inherited update |
| A file edit is silently shadowed | An 80-operator.json / 90-operator-arca.json fixture predicts unchanged effective values plus shadowed; explicit commit reports no resolved-value change, not activation |
| A map is attributed to one file despite mixed leaves | Per-leaf derivation fixture with at least three source classes |
| Workflow specificity widens host authority | Domain refusal test; scope order does not replace admission |
| Stale GUI or a host seed write invalidates a plan | Relevant source membership/order/content and descriptor/constraint preconditions conflict; all cooperating host writers serialize; new shadowing file and ancestor replacement fixtures |
| An unrelated shared-file edit invalidates another owner's plan | Host-proven unrelated-source-content-changed permits bounded replan, never stale commit or carried-over approval; affected/ancestor/unclassified changes and redaction have separate fixtures |
| Save succeeds but activation fails or only partly completes | Separate durable save outcome and per-instance application state; preserve prior acknowledgement, do not claim whole-node activation |
| A legacy on/off endpoint bypasses P091 or the wrapper writes/applies twice | Compatibility-path fixtures prove the same source resolution and commit path, bounded retry without duplicate dispatch, and retained domain/lifecycle guards |
| Restart fabricates history from current files | Replay pinned source, descriptor, constraint and normalization inputs for the retained resolution/id, or report unavailable history |
| Secret leaks through a losing value, diff or digest | Redaction fixtures cover successful and refused queries and exports |
| A key query reads an arbitrary file or another participant's scope | Read authorization and source-confinement fixtures refuse before source acquisition |
| Migration changes default inheritance or drops an old source | Dry-run equivalence report and explicit operator resolution before commit |
| Unknown module/schema/merge rule becomes active | Typed unsupported/unvalidated result; no implicit execution or authority |
| Python recomputes settings after host refusal or reads mutable files after launch | No managed ambient fallback; startup and later reads bind exact scoped resolution/id and consumption evidence |
| Two loaders disagree on selected files or invalid JSON | Differential fixtures and explicit legacy profiles; migration reports changed admission instead of claiming parity |
| A config client becomes a route to host control or all node settings | Read/write grants remain separate; cross-module and forged-identity requests refuse before disclosure or effects |
| A module cannot start until the channel it initializes is ready | Supervisor supplies a bounded launch snapshot; startup test requires no configuration RPC |
| A display label, component path or duplicate stem selects a file | Resolve admitted owner bindings; reject ambiguous/reserved/unsafe targets, never auto-sanitize or redirect failed writes |
| Common defaults overwrite detailed settings due to naming | Use existing sort order with 80-operator.json before 90-operator-<component-name>.json; legacy 90-operator.json requires explicit migration |
| Workflow scope only works for JSON-e Flow | Arca non-Flow fixture uses the same binding contract with its own schema/normalization; unknown kinds and wrong definition/run bindings refuse |
Descriptor withdrawn but its 50-*.json remains |
source-set/revision stays equal, descriptor-set changes; descriptor-invalidated blocks new admission, while the old resolution remains historically explainable |
| Snapshot delivered but never consumed | provisioned remains distinct from acknowledgement, component outage and unknown |
| Authority expires between plan, commit and apply, including queued/recovered apply | config-time-expiry-between-phases preserves the historical verdict and save but refuses current apply as constraint-conflict / constraint-expired, reports application-rejected, and records no lifecycle effect or new consumption acknowledgement |
| Crash leaves intent without outcome | Old/new/third/absent/unreadable/no-op fixtures reconcile or refuse; no automatic overwrite, successful receipt or application before a durable outcome |
| Offline package files are mistaken for approval | No owner-issued pinned descriptor/admission export means unresolved validation; historical exports grant no current authority |
| Lazy explanation uses today's descriptor set | Values-only and detailed modes replay identical retained inputs, even after withdrawal/file edits; evicted evidence is explicitly unavailable |
| Reset leaves an empty shared file | Retain {} with unchanged unrelated declarations/permissions; no value contributor, no implicit deletion |
| Backup restore passes on byte equality but behavior differs | Compare pre-migration effective values under pinned baseline inputs; independently validate current authority and report incompatible descriptors |
| Excessive derivation silently omits a denying constraint | Bound exceeded requires admitted aggregate or refusal; summary cannot become more permissive |
| Registered operator route becomes module-dispatchable | All five operations are discoverable within scope; only describe/explain permit module dispatch, with independent read admission |
Implementation Tracker¶
All rows are todo for new P091 scope. The tracker vocabulary is
todo | partial | done; an abandoned task must retain an explicit dated
supersession, not silently disappear. Inventory vocabulary is
planned | pending | partial | done: planned/identified unfinished items map
to tracker todo, not a literal inventory state named todo.
The dated Node audit is input, not complete machine-readable coverage.
Dependencies are completion prerequisites; fixtures can be authored earlier.
Code/DTO registration can exist behind disabled routes while security evidence is
pending. No new route is enabled before P091-005a covers its boundary.
A task cannot be done while the ledger marks the same claimed surface
partial. A completed contract-only slice may coexist with a partial component
only when both explicitly delimit that narrower claim.
| ID | Work item | Depends on | Status | Completion gate |
|---|---|---|---|---|
P091-001 |
Close the configuration/reuse inventory audit | P091-001a, P091-001b |
todo |
Aggregate audit completion, not runtime implementation: source/writer and retirement ownership mapped, discrepancies documented, every remaining gap represented in the machine-readable inventory. |
P091-001a |
Inventory foundation sources/writers and establish the executable inventory | — | todo |
Deliver node:docs/configuration-inventory.v1.json, node:tools/check-configuration-inventory.py, structural tests and CI --verify-current from day one. Map daemon's raw/effective/layered entries, retirement check, bootstrap seeds, toggles, Agora, Node UI, identities/filenames and all four first-slice seams, including minimum Python launch and Arca/Flow bindings needed to freeze cross-language DTOs. Audit claims remain planned/pending until verified. |
P091-001b |
Inventory remaining module-local and record-backed controls | — | todo |
Extend P091-001a's common format with Python/services, domain loaders, workflow record fields, env/CLI, projections, non-settings and retirement owners. May run alongside contracts; no implication that a database resource is a setting. Named gaps remain visible. |
P091-002 |
Freeze shared contracts under resolved operator choices | P091-001a |
todo |
Satisfy the final P091-002 freeze gate below. Adopted semantics, registered contracts and runtime adoption remain distinct. |
P091-003 |
Implement pure source-aware resolution over reused JSON primitives | P091-002 |
todo |
configuration-core has no host/UI/provider dependencies; layering checks cover all current upper crates. Preserve json-utils behavior, extract retirement-policy validation and bootstrap writes. Both detail modes yield identical values/refusals; retained inputs support lazy per-pointer explanation after source/descriptor change. No read-time mutation/fetch. |
P091-004 |
Bind descriptors, admission and domain derivation ports | P091-002 |
todo |
Reuse registry sealing/CAS and explicit unresolved constraints with separate identity axes. First-slice owners retain validation/merge policy, P085 and sidecar semantics. Withdrawal with unchanged source bytes invalidates new resolution/admission but preserves historical explanation; offline admission never inferred from file presence. |
P091-005 |
Build scoped read/explain adapters and deliver the offline helper | P091-003, P091-004 |
todo |
Rust, P080 and retained HTTP share resolution-bound DTOs; thin non-UI Python facade validates responses, has no fallback. Deliver/install/pin the one-shot Rust helper with bounded structured I/O and missing-binary/version refusals; support injected/exported snapshots. Bounded carrier/projection/aggregate fixtures pass. New routes remain disabled pending P091-005a. |
P091-005a |
Pass the named security boundary fixtures before exposure | P091-005, P091-006 |
todo |
All eight config-security-* fixtures cover relevant transports/offline/export paths; instrumented denial precedes source acquisition. Route flags do not grant scope or operator authority. No UI/compatibility adoption or enabled new API without this proof. Later adapters require equivalent coverage. |
P091-006 |
Implement shared targets, predicted-value plans and durable commit | P091-003, P091-004 |
todo |
Reuse owner/stem and explicit domain targets; preserve 80/90 lexical order and legacy 90 common-file case. Sparse patch/reset retains unrelated values and empty {}. Plans predict every affected effective value and derive shadowing. Affected/unrelated/unclassified conflict diagnostics and bounded replan without stale authorization are tested. Source membership, bootstrap writers and separate descriptor/constraint changes conflict. Intent/rename/outcome ordering, old/new/third/no-op recovery, durable failure and no pre-outcome apply are tested behind the exposure gate. |
P091-007 |
Bind application identity, consumption and lifecycle recovery | P091-006 |
todo |
Reuse supervisor/daemon control and S028 facts/projections. Per-instance activation/generation binds a durably admitted resolution (commit or observed-source admission); provisioned, acknowledged, pending, rejected and invalidated remain distinct. config-time-expiry-between-phases and controlled-clock edge cases prove fresh current-use checks before apply/retry/recovery; expiry yields constraint-conflict / constraint-expired and application-rejected without effects, preserving replay and save facts. Pre-channel scoped provisioning, restart fencing and idempotent retry pass without repeating effects or reviving authority. |
P091-007a |
Wrap retained middleware on/off and repoint existing callers | P091-005a, P091-007 |
todo |
Preserve the existing toggle/supervisor implementation and domain guards. Repoint handlers/UI/CLI to the shared contract with minimal helper extraction, no bypass writer, dual-file mirror or duplicate apply. Test eligibility, dependencies, operator-stopped, shadowing, saved-not-applied and restart; reads never materialize. Physical source migration remains P091-010. |
P091-008 |
Project shared contracts into Node UI | P091-005a, P091-007, P091-007a |
todo |
Expose keys, files, projected derivation, source and predicted-value diffs, apply modes and per-instance receipts. No private setting store or implicit pinning; file/GUI changes agree after reload/restart. |
P091-009a |
Retain the early Arca non-Flow contract fixture | P091-002, P091-004 |
todo |
Scope/owner/source/definition/run binding, normalization and refusal goldens work without a live UI/control stack or universal plan schema. Do not claim runtime consumption. |
P091-009 |
Bind workflow configuration to live domain owners | P091-009a, P091-005a, P091-007 |
todo |
Live JSON-e Flow case plus retained Arca contract proof use existing definitions/validators and exact revisions. Wrong or ambiguous handlers and schemas refuse. No duplicate workflow store or closed thematic repertoire. |
P091-010 |
Migrate first-slice sources and readers explicitly | P091-001a, P091-003, P091-005a, P091-007a |
todo |
Preview/protected backup/effective-value equivalence cover seeding, toggles, first-slice Python loading, env/CLI and optional legacy 90-to-80 migration. Test restoration against pinned pre-migration effective values and independently current authority; changed descriptors report incompatibility. Preserve factory/domain targets unless separately migrated. No read-time migration/workflow trigger. |
P091-010a |
Consolidate remaining configuration loaders and register legacy profiles | P091-001b, P091-003, P091-005 |
todo |
Each configuration deep-merge/acquisition copy migrates to shared primitives/contracts or has a named legacy profile with owner/retirement evidence. Include Agora selection/skip-malformed semantics and listed Python copies. No unregistered local configuration merge outside approved seams; unrelated domain JSON patching is not banned. Remaining ordinary-setting gaps block P091-015 promotion. |
P091-011 |
Prove cross-process and standalone consumption | P091-004, P091-005a, P091-007, P091-009, P091-010 |
todo |
Actual supervised Python and separate-process Rust consumers, plus daemon/Node UI, consume explained resolutions. Cover pre-channel inline/ref snapshots, acknowledgement, host denial/outage without fallback, pinned Rust helper and Python exports/injection. Histories retain descriptor/context evidence and no current-use grant is inferred. |
P091-012 |
Publish shared manual, component references and HOWTOs | P091-002, P091-005, P091-008, P091-009, P091-010, P091-011 |
todo |
Exact keys/sources, four identity names, views, shadowed commits, restore/invalidation and apply modes agree across manual/navigation/CLI/UI. Separate proposed, legacy and supported behavior. |
P091-013 |
Complete refusal, concurrency and recovery evidence | P091-005a, P091-007, P091-007a, P091-008, P091-009, P091-010, P091-011 |
todo |
Every failure row has retained executable evidence, including journal/file crash points, host-seed races, descriptor withdrawal/expiry, clock failures, shared-file conflict classification and replan, no-op/third-version recovery, secret redaction and evicted replay inputs. |
P091-014 |
Retain the four-case operator slice and synchronize evidence | P091-011, P091-012, P091-013 |
todo |
All cases complete locate/explain/edit/apply/restart/reset through files and interfaces. Update node:docs/implementation-ledger.toml, regenerate via node:tools/generate-implementation-ledger.py, reconcile node:docs/MVP.md for affected scope. Record usability obstacles, not universal cognitive-load reduction. |
P091-015 |
Close broad coverage and enforce drift/promotion gates | P091-001, P091-010a, P091-014 |
todo |
check-configuration-inventory.py --verify-current and check-config-source-boundaries.py run in Node .github/workflows/docs.yml; --promote rejects unfinished required settings. Every ordinary durable setting has file-backed shared control; bootstrap/non-setting exceptions stay narrow. Reconcile ledger, generated view, affected MVP/manual coverage and schema indexes; hidden ordinary stores or partial same-surface ledger rows prevent done. |
Dependency-ordered slices¶
Start P091-001a and P091-001b concurrently as audit work; only the bounded foundation audit blocks P091-002. Contract freeze opens P091-003/004. Read and change adapters follow; their named security gate opens exposure and the retained on/off wrapper. P091-009a's Arca contract fixture can run before the live control/UI stack. P091-007/007a, P091-008 and P091-009 then converge with first-slice migration on P091-011's actual process evidence. Design Python/launch DTO fixtures during the foundation, not for the first time at cross-process acceptance.
P091-010a consolidates remaining loaders independently of first-slice acceptance. P091-014 closes that coherent operator stage; P091-015 additionally requires the complete inventory and broader adoption. No task requires finishing all of P085, P090, Sensorium, Room or Corpus; reuse the exact existing contracts needed by the selected cases and preserve their separate implementation claims.
Resolved 2026-09-06¶
The operator selected A/A/B, refined the common filename to preserve ordinary lexical ordering, and chose to wrap the existing middleware on/off implementation. These are adopted decisions; alternatives are retained below as history, not active implementation branches.
OQ-01: One reference resolver and a thin Python client — A¶
Managed Python uses Rust-host operations/snapshots. Standalone resolution uses the same Rust core through an installed one-shot helper; Python without Rust can consume an exported or injected snapshot. Fresh composition requires that helper or a new export. Local getters and unit tests need neither daemon nor RPC.
Alternative B, a native Python resolver, was not selected: it would support independent Python-only composition at the cost of another semantic implementation and permanent parity tests. No implicit fallback to it is allowed. Affected tasks: P091-002/003/005/010/011.
OQ-02: Reuse existing owner-specific workflow declarations — A¶
Use existing declaration mechanisms, identities, revisions and validators. JSON-e Flow remains the initial concrete example, not the meaning of workflow: the additional audit established the broader definition/handler boundary and Arca's declarative orchestration path (Decision 13).
Alternative B, a new config/workflows/ values wrapper in V1, was not selected.
The choice does not exempt record-backed behavioral controls from file-backed
configuration; their migration is explicit rather than a duplicate workflow store.
Affected tasks: P091-002/003/006/009/010/012.
OQ-03: Per-component operator fragments, with common fallback — B¶
Use 90-operator-<component-name>.json, for example 90-operator-arca.json and
90-operator-sensorium-workbench.json. Reuse existing module identity/owner
bindings and domain-specific write targets as detailed in Decision 3.
The common fallback is 80-operator.json, as subsequently requested by the
operator: existing lexical ordering then places it before detailed files.
The original common-only option A and initial 90-operator.json fallback are
superseded. The intermediate special-ordering idea is also superseded; no custom
comparator is needed. Existing legacy files migrate explicitly with source/value
comparison. Fallback is a target-selection result, not a way around invalid
ownership or failed writes. Affected tasks: P091-002/006/008/010/012.
Middleware on/off: retain implementation behind the P091 wrapper¶
The operator chose implementation reuse with minimal helper extraction and
call-site refactoring: the shared contract controls durable enabled, while the
existing middleware mechanism and supervisor retain execution ownership
(Decision 8). Neither a permanently independent on/off writer nor a replacement
lifecycle engine was selected. The existing file may remain a declared legacy
source until explicit migration; this choice does not reopen OQ-03 or require
an immediate file move. Affected tasks: P091-002/007/007a/008/010/013.
Review integration: explicit identities and reuse boundaries¶
On 2026-09-06 the operator accepted the follow-up review integration: four distinct identity axes; retained replay inputs with lazy derivation; all five operations in the scoped registry with writes non-dispatchable; shared temporal persistence and explicit intent/replace/outcome recovery; predicted effective values for shadowed commits; descriptor invalidation and owner-issued offline admission context. Retain empty shared files on reset and test effective-value restoration, not just byte restoration. Named security evidence precedes new interface exposure.
The follow-up review on 2026-09-06 made time-dependent current-use admission
explicit at commit/apply/retry/recovery without changing historical replay.
Expiry uses the existing constraint-conflict refusal family and
application-rejected state. Shared-file conflicts receive host-proven reason
codes; automatic replan does not transfer write approval. The two read operations
join the curated human registry, while operator writes remain machine-registered
and separately documented. These refinements do not promote implementation status.
The dated source/workflow audit moved to Node. This refines, rather than reopens, A/A/B and the retained on/off wrapper. Earlier suggestions of one counter, a blanket environment ban, unregistered write operations or new control-directory stores were not selected. Reuse precedents do not make these P091 mechanisms implemented. Affected tasks are defined in the updated tracker above.
Open Questions¶
No unanswered operator choices remain from OQ-01/02/03. Engineering closure and evidence-driven extensions below remain separate from those resolved decisions.
Engineering closure items, owned by P091-001a/P091-002¶
The reuse audit resolves the earlier route-family uncertainty: reuse P080 host capability dispatch for admitted reads and existing operator control for changes/application. The five-operation flag matrix and candidate DTO family are specified; exact schemas still need registration and executable fixtures. Workflow-kind/definition and Flow instance bindings are available; Decision 13 defines their distinct configuration ports under resolved OQ-02 A.
Freeze finite source/file/byte/depth/derivation/output/history budgets, the common JSON admission profile and descriptor application modes using the inventory and boundary tests. These require measured engineering defaults, not an unsupported promise that every setting reloads live. Configuration inspection cannot disable the host's structural safety guards; do not inherit unbounded helper reads.
Post-first-slice, evidence-driven¶
- Is single-file commit sufficient for common operator workflows, or is an activation-group transaction justified by observed multi-component changes?
- Which explanation details belong in the default CLI view versus an expanded derivation view? Retain the full bounded data contract in either case.
- Do operator trials justify additional editable formats? JSON is sufficient for V1; preserving comments or adding a DSL is not a prerequisite for uniformity.
Next Actions¶
- Deliver P091-001a's initial inventory/gate; extend the audit with P091-001b alongside contract work under the recorded A/A/B decisions.
- Freeze the shared Rust/Python contract and fixtures; extend existing primitives, supervisor provisioning and host bridges before implementing UI.
- Complete the four-case slice with real cross-process consumption, then expand by inventory rather than declaring that every subsystem became configurable through one demonstration.
P091-002 freeze gate¶
P091-002 is done only when the P091-001a evidence freezes the preceding contract
as executable data: address/owner/stem rules and 80/90 ordering; separate source,
descriptor, resolution and activation identities with exact CAS/digest preimages
and safe counter ranges; closed views/source classes/application/refusal enums and
transition tables; descriptor withdrawal, time-dependent current-use gates and
explicit offline admission; retained
replay inputs, both detail modes, per-pointer/aggregate derivation and disclosure;
predicted-value plans, classified source conflicts, bounded replan without reused
approval, source-membership/host-writer conflicts and crash-safe
single-file outcomes; typed Rust/Python/launch/offline DTOs; all five registry
operations with read dispatch versus operator-only host routing and explicit
docs.human-registry choices; finite
source/file/byte/depth/history/output budgets, canonical JSON admission and the
16 KiB inline ceiling. Positive/negative Schema Gate and pure-core fixtures cover
these relations, including conditional-required fields; local source validation
remains explicitly local. Register the exact request/response/plan/receipt schemas
with P091 basis metadata, synchronize mirrors/index/COVERAGE and the two curated
human registry tables, pass make check-capability-registry, and record exact
contract-only coverage in node:docs/implementation-ledger.toml, regenerate via
node:tools/generate-implementation-ledger.py and reconcile affected
node:docs/MVP.md scope. P091-005a remains mandatory before route exposure:
contract freeze does not claim runtime adoption or replace security evidence.