Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ hollow alias (minimality ≠ grounding) · state-space conflation (an `Option`/`
- **witness realization — the v2 execution path.** Emit witnesses to native code (Rust now; C a row-policy option, §7) and use realization/materialization to schedule the runs; the dependency-ordered work that discharges the v2 memory-control findings, none of it gated on self-hosting. → [witness realization plan](docs/plans/witness-realization-plan.md) (findings it discharges: [v2 memory-control audit](docs/plans/v2-memory-control-audit.md))
- **machine-shape orthogonal scheduling — one realization kernel over faithfully modeled machine facts, with locality (§2).** DRAFT for review; model-before-implement, each phase names its trigger. Its authority transfers to the std/extdeps carriers it names when Phase 1 lands, at which point this md dissolves into a registered `gunbc.plan.Plan` row (§6). → [machine-shape scheduling design](docs/plans/machine-shape-orthogonal-scheduling.md)
- **namespace-only name resolution — the containment tree is the single naming authority (operator-signed 2026-07-06).** Supersedes resolver-graph-major §1c's *import-name-universe* ("own declarations ∪ direct import lists" visibility), carrying forward its mechanics unchanged (binding-edge owner, unbound/double-bound = error, patterns-via-scrutinee, no expected-type picker). One structure — **syntactic containment** — induces everything at once: qualified name = nesting position (`T{a}` → `a` *is* `T.a`; `T{a{a}}` → inner *is* `T.a.a`, so Rule-2 no-ambiguity holds by construction), reference = lexical lookup up the ancestor chain (a sibling module is visible, its members projected `node.Symbol`), `.` = projection one level down (field / module-member / variant — one op), and `.` *is* the `std.induction` sub-value relation (descent evidence). So one content-addressed tree grounds THREE consumers — resolution *walks* it, content-addressing *hashes* it, termination reads its *sub-value* edges (§2 Realization: one structure, N consumers). Frontend BUILDS the tree from nesting (pure structure, zero resolution logic); backend WALKS it (lexical up, project down) — no heuristics to tune (the tuning risk = resolving against a flat index instead of the tree). Two governing rules (operator, 2026-07-06): **no dual representation / minimal at every layer; no ambiguity at any layer.** Rides on `SymbolIndex` = "the containment tree materialized" (`type-env-single-authority`, lively-raven lane) — the loyal-heron scaling gate that formerly blocked resolver surgery was **discharged at #6809** (per §7.5 of the design doc: "the lane is no longer blocked on it"); `import-scoped` (§1c, today) and `namespace-only-Y` (position-info uniqueness: expected type filters a variant to one, never picks) are two walk-rules over one *fill-agnostic* index (fill = whole tree; policy gates lookup, never fill). Census: 98% of names globally unique → always bare; the residue is v1-seed forks (resolve by subtree, free) + github-style variants ((Y) context-resolves); no consolidate-now forks (the census's 2 same-name flags proved to be a homonym + whole-file v1-seed duplication, not genuine cross-tree §3 forks). Terminal step: delete the `import` grammar + supporting code → `import` becomes a *parse error*, deps derived from `container.member` references (Rule-1 end-state). → [namespace-only resolution design](docs/plans/namespace-resolution-design.md)
- **fleet-reconcile spine — one grain-agnostic membership diff (deploy · fleet · session).** ONE `membership_reconcile<M,K>` reuses `std.change.keyed_two_way_diff` verbatim (no fork): desired set vs observed set keyed by member IDENTITY — Added/Modified → upsert, Removed → teardown-or-refuse, Unchanged → noop (absence of a hunk). A member may be a service/unit/host OR a Session (operator: 'it should all be the same'), so a new member type is a new instantiation of the same fn with its own `(key_of, key_eq, value_eq, ownership_of)` bundle — zero spine change (a Realization, §2; a forked reconcile means the genericity bought nothing). R5 teardown-owned-only is a CONSTRUCTION WALL: a Removed non-owned member yields `MemberTeardownRefused`, which has no effect arm in any apply dispatch, so the bad state is unwritable — never a post-check, never 'assume owned' (the absorbing fallback §5 forbids); ownership-unknown REFUSES too, typed/located/counted. Ownership is extracted as its own single authority (`gunbc.ownership.Ownership`) and `live_deploy`'s `DeploymentStep` re-grounds onto it (pass 2, a declared dissolution trigger — never a second `Owned|Ensured`). Pass 1 (spine + R5 + a synthetic discriminating witness the degenerate apply/retract poles cannot exercise) landed. → [membership-diff reconcile spine design](docs/plans/membership-diff-reconcile-spine-design.md)
- **fleet-reconcile spine — one grain-agnostic membership diff (deploy · fleet · session).** ONE `membership_reconcile<M,K>` reuses `std.change.keyed_two_way_diff` verbatim (no fork): desired set vs observed set keyed by member IDENTITY — Added → `MemberAdded{to}`, Modified → `MemberChanged{from,to}`, Removed → `MemberRemoved`-or-`MemberRemovalRefused`, Unchanged → noop (absence of a hunk). **Add and change are separate arms, not one `upsert`** (operator ruling): the collapse discarded `from`, which is precisely the fact a safe realization of a change needs — an SCM ref update is a compare-and-swap naming the expected prior (`git update-ref <ref> <new> <old>`), and a host resource whose address moved needs the prior endpoint to retire, so with only `to` in hand the sole reachable realization is the unconditional write. `gunbc.change_realization` `classify_change` is the table that consumes both sides: SCM ref → atomic CAS; source declaration move → atomic SCM tree transformation; same runtime address → in-place update; changed host address → staged replacement with **declared** intermediates. That last row is the srv4 lesson made unwritable rather than warned about — `StagedIntermediates` is head+tail so an empty intermediate plan has no constructor, and an undeclared one REFUSES (typed, located) instead of degrading to an overwrite. It also kills a live conflation: a `.dag` module re-home is an SCM tree transformation, **not** a host migration. **Only removal consults ownership**, and that asymmetry is deliberate: `gunbc.ownership` defines `Ensured` as 'requires present but does NOT own (never torn down)', a constraint on removal, so converging a drifted member the model does not own is the intended behavior of an `Ensured` member rather than a violation of it (`tool_readiness` exists to re-install a drifted pin). A change-refusal arm was built during this work and WITHDRAWN for over-firing on exactly that; the regression control `w_ensured_member_converges_but_refuses_removal` keeps it withdrawn by execution rather than by prose. A member may be a service/unit/host OR a Session (operator: 'it should all be the same'), so a new member type is a new instantiation of the same fn with its own `(key_of, key_eq, value_eq, ownership_of)` bundle — zero spine change (a Realization, §2; a forked reconcile means the genericity bought nothing). R5 removal-owned-only is a CONSTRUCTION WALL: a Removed non-owned member yields `MemberRemovalRefused`, which has no effect arm in any apply dispatch, so the bad state is unwritable — never a post-check, never 'assume owned' (the absorbing fallback §5 forbids); ownership-unknown REFUSES too, typed/located/counted. Ownership is extracted as its own single authority (`gunbc.ownership.Ownership`) and `live_deploy`'s `DeploymentStep` re-grounds onto it (pass 2, a declared dissolution trigger — never a second `Owned|Ensured`). Pass 1 (spine + R5 + a synthetic discriminating witness the degenerate apply/retract poles cannot exercise) landed. Identity is **declaration-owned and allocator-minted**, never derived from unit/path/content (operator ruling): `gunbc.host_resource` carries `HostResourceIdentity{host, occurrence}` and `HostResource<R, V>{identity, role, value}`, where the occurrence is minted by an allocator, `role` is a **type parameter** (so a new role is an instantiation, never a widened central enum), and unit name / FIFO / storage path / configuration are mutable *value* fields. That is what makes a rename ONE `MemberChanged` rather than a delete-plus-create some key function must be clever enough to re-pair — git does not track renames, it INFERS them from content similarity, and §4 rules a heuristic never necessary in a closed system; a content-or-path-derived `key_of` is that same inference wearing a bundle. It is a carrier distinct from `std.occurrence_identity` deliberately: that module is specifically about SOURCE occurrences (its `OccurrenceCategory` enumerates callable/type/field/method, its allocator is coupled to `AuthoredTokenOrdinalSpace`), and sharing one `Int` space across two subjects would let `occurrence_id_eq` answer `true` on a numeric coincidence — the cross-family comparison the `ContentHash` family grounding closed. Extracting one subject-parameterized minted-identity carrier (the `Vendor<Domain>` shape) is registered as a trigger, not built under a load-bearing module with stage0 work in flight. → [membership-diff reconcile spine design](docs/plans/membership-diff-reconcile-spine-design.md)
- **one authorization kernel, several typed request profiles (operator directive 2026-08-01).** `std.access.AccessRequest` carries subject, action, object, typed context, and typed evidence; `AccessPolicy` is the sole policy-to-decision seam, `decision_meet` the sole conjunction, and every denial carries a typed cause at structured request coordinates. Effect, reference, publication admission, disclosure, and sealed execution are exact request products projected into that kernel, never independent allow/deny folds. Principals project external evidence — OIDC issuer/subject and observed POSIX effective principal — rather than being invented internal identities. The provisional gunbc#7541 `Verb::Reference` / `Verb::Publish` / `AudienceScopeTree` carriers dissolved: audiences are sets with join and partial-order subset, because real audiences can overlap without either containing the other. `PublicationAdmissionRequest` is only the generic request shape here: the operator-deleted Stage-0 placement wall, publisher policy, and pathname census are deliberately absent.
- **publication and reference-admission capabilities — carrier retirement, replacement ontology unresolved.** **Removal record (operator ruling 2026-08-01):** the Stage-0 `Publish`/`Reference` effect-grant experiment retired after its sole live publication consumer, the placement gate, was deleted. `Reference` and `Publish` were removed from `std.effect_grant.Verb`, `AudienceScopeTree` was removed, and `std.visibility_grant` plus its carrier-only witnesses were deleted; generic authorization admission re-homed onto the `std.access` kernel. This retires those carriers, not the underlying capabilities. Publication remains required, but no replacement ontology has been selected and any redesign requires operator review before implementation. Reference-edge admission — whether an edge may form within one compiled program — is a separate deferred capability awaiting a concrete consumer; the publication rollback decides nothing about it. **Stage 0 per-file placement gate DELETED:** its constant `Publish World` rows duplicated Git's post-cutover path population, four same-day repair rounds raced identical reconstructions and repeatedly redded fleet CI, ordinary modifications were invisible, and a CI check after a public push could protect merge integrity but not confidentiality because the objects had already reached public storage. New public files require no stamp. Any future confidentiality boundary must act before unauthorized bytes enter public storage — for example by withholding public-write capability from ordinary sessions — but that constraint selects no replacement architecture. The locked extension remains a **publication capability product, not a ladder**: source/interface disclosure, artifact delivery, local/remote execution, integrity, reproducibility, existence/churn disclosure, revocation/erase, and placement/trust are independent policy axes. An emitted artifact is not source disclosure but remains inspectable; client-held key means plaintext access, distinct from remote/attested key custody. Locked blobs use AEAD with subject/interface/version/audience/policy context bound as associated data plus confidentiality-and-integrity-preserving per-audience key wraps; wrong key/context/authentication is typed refusal. Per-statement holes remain refused; interface/realization is the cut; size padding and `Secret`/`Pii`→`SecretRef` construction constraints remain. Commitments never promise zero leakage: length, timing, interface shape, wrap count, access pattern, low-entropy guessing, and churn stay in a named threat model. Remote execution and SCM diagnostics obey information-flow/noninterference + explicit declassification; metering prices an oracle but does not secure it, and unauthorized audiences receive only a non-leaking diagnostic projection. Cryptographic erase is conditional on every key/wrap/backup/cache/derivative/recovery path being sanitized and the result validated, never “delete one key.” → [retired node/subtree carrier experiment](docs/plans/node-subtree-visibility-grants.md)
- **effect grants over namespaces — dissolve `Hermetic | Wet` into (frame × verb × subtree).** The 2-valued `Hermetic | Wet` enum conflates four axes (input closure / replayability, output reach / interference, handler binding, selection eligibility) — a §3 state-space conflation with proto-envelopes already forked (the hand-rolled `workspace_root` containment gate, `std.resources.ResourceHandle`, `AuthScope`, `LiveTreeDisposition`). End shape (§3, cited not minted): an effect target is a position in a containment tree that already exists — filesystem paths, URIs, `/proc`, service-operation paths, and the unifying case **code names themselves** (the containment tree the namespace-resolution lane makes the single naming authority); permission is a grant of (verb × subtree) attached to a `Frame`; admissibility is the namespace prefix relation — the same `⊑` the resolver walks, content-addressing hashes, and termination reads, so effects become the **fourth consumer** of the one containment structure. Dispatch projects an `EffectRequest` through `std.access.AccessPolicy` fail-closed (`Deny` carries the typed/located/counted refusal, never a silent widen — §5); *replayable* / *isolated* / `LiveTreeDisposition` become **derived** projections, and `Hermetic | Wet | Record` survive only as named envelope presets, deleted at the end. Frame-containment is graded on the §5 construction/validation axis (`LifecycleByConstruction` vs `LifecycleByConvention`). No code lands from the design doc; the FLAG-A interim is the only near-term consumer. → [effect-namespace-grants design](docs/plans/effect-namespace-grants.md)
Expand Down
Loading
Loading