Skip to content

v1: register service ops node-locally (fix Filesystem dropped by name-collision poison at closure scale) - #5670

Merged
briansrls merged 2 commits into
mainfrom
session/vivid-pike-122
Jun 23, 2026
Merged

briansrls merged 2 commits into
mainfrom
session/vivid-pike-122

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Problem

In a large import closure the v1 interpreter dropped the Filesystem service's operations, so Filesystem.Write raised unknown service operation: Filesystem.Write at runtime (other services — gcp/redfish/shell/Urandom — registered fine). Same class as the a1 type-name-collision poison.

Root cause (reproduced)

InterpContext::with_runtime_options built the runtime service_ops table by gating on a name-keyed lookup: graph.item_registry.get(&authored_name).kind == ServiceItem. item_registry is keyed by authored name and merged across the whole closure (overlay wins, rc_map_merge). Two top-level items share the name Filesystem:

  • std.resources resource Filesystem → item_kind = OtherItem
  • extdeps.filesystem.filesystem_io service Filesystem → ServiceItem

Once both are reachable, the resource wins the merge and poisons the lookup, so the genuine service node's ops are never registered. The "~35 sources" framing is incidental — reproduced at an 18-module closure (import filesystem_io + import std.resources { ResourceHandle }). Small fs witnesses (filesystem_io only, 7 modules) never pull std.resources, which masked the bug.

Fix

Detect service-ness node-locally: if item_kind(item.clone()) == ItemKind::ServiceItem. The service node carries the transport that defines it as a service — it is the single authority. This removes the second, poisonable representation (fail-closed by construction, DESIGN §5). v1_interpreter.rs is a hand-maintained stage0 seed, so the direct edit is correct.

Verification (by execution)

  • 18-module repro: red pre-fix (unknown service operation) → green post-fix (wet write/read round-trip).
  • 7-module control witness: green both ways (no regression).
  • Full interp_recorded_fixture narrow-gate suite: 23 passed, 0 failed.
  • New gated regression: dsl/test/claim/filesystem_write_closure_scale_witness.dag + filesystem_write_closure_scale_record_then_hermetic_replay_holds in interp_recorded_fixture_test.rs. Deliberately NOT enrolled in claim_witness_corpus_ci_runner.dag (that module has no consumer — would be coverage-by-illusion).

🤖 Generated with Claude Code

@gunbai-bot
gunbai-bot Bot marked this pull request as ready for review June 23, 2026 20:30
@gunbai-bot gunbai-bot Bot changed the title v1 resolver closure-scale bug: service_ops registration DROPS the Filesystem service when the import closure grows to ~35 sources (gcp/redfish/shell/Urandom register fine in the same closure); Filesystem.Write then raises unknown-service-operation at runtime. Same class as a1-closure-poison-typename v1: register service ops node-locally (fix Filesystem dropped by name-collision poison at closure scale) Jun 23, 2026
@gunbai-bot
gunbai-bot Bot marked this pull request as draft June 23, 2026 20:31
@briansrls
briansrls marked this pull request as ready for review June 23, 2026 20:38
@briansrls
briansrls merged commit 724998b into main Jun 23, 2026
0 of 4 checks passed
@briansrls
briansrls deleted the session/vivid-pike-122 branch June 23, 2026 20:38
gunbai-bot Bot pushed a commit that referenced this pull request Jun 23, 2026
…required data name

extdeps/ctrl/jobserver.dag declared its anchor as `ctrl_jobserver_authority`,
but the extdeps_external_authority_gate projector (read_external_authority_anchor_from_items)
matches the data def by the EXACT name `extdeps_external_authority_anchor`. So jobserver
projected as Absent -> a "missing:extdeps.ctrl.jobserver" violation, turning
extdeps_external_authority_live_clean_tree_holds() false on main (landed via #5663/#5670).
That is the batch-2 failure my PR inherited through the merge (corpus_live_clean_tree_holds /
corpus_live_anchored_modules_clean_holds + extdeps_external_authority_gate_passes all RED).

Rename the data def to the required name (no other ref to ctrl_jobserver_authority exists
in dsl/ or src/v2/). Verified: both authority witnesses flip GREEN, my oomd witness stays green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
briansrls added a commit that referenced this pull request Jun 24, 2026
…tdeps/systemd unit-management interface plus oomd config surface, apt install-as-effect, and a gunbc install Realization that grounds OomdEnforced from a verify/show-effective read (#5677)

* WIP: Model systemd-oomd as a dependency (pure .dag, no live execution): extde

* WIP: Model systemd-oomd as a dependency (pure .dag, no live execution): extde

* Model systemd-oomd as a dependency (pure .dag): reconcile carrier + systemd/oomd interface + apt install-effect + grounded OomdEnforced

Shared std carrier (std/realization_reconcile.dag), first instance = oomd:
- reconcile(apply_effect, show_read, grounding) -> Converged{evidence} | NotConverged{reason}.
  Substrate deviation from the pre-signed fn(R)->Grounding<E> param (v1 seed cannot
  type a fn-typed param returning a generic-applied enum); closest faithful form takes
  the grounding value and enforces the read-presence gate. Section-5 invariants kept:
  ReadAbsent => NotConverged (evidence impossible without an observed read); the verdict
  never depends on apply_effect (threaded as provenance only).

extdeps systemd interface (consolidated under the existing extdeps.os.systemd home,
reusing SystemdUnitActiveState from #5635 rather than minting a parallel enum):
- extdeps/os/systemctl.dag: enable / start / set-property / daemon-reload / is-active /
  show-effective-property operation shapes + UnitFileState; unit is always a parameter.
- extdeps/os/oomd.dag: oomd config surface (ManagedOOMMemoryPressure/Swap, pressure
  limit/duration, oomd.conf globals) + oomctl dump read; real names from oomd.conf(5)
  and systemd.resource-control(5).

apt install-as-effect:
- extdeps/apt.dag: Install operation (package is a parameter, no business literal) +
  systemd-oomd-defaults package, reusing the existing package-name concept.

gunbc install Realization (sibling to os_install.dag), instance #1 of the carrier:
- gunbc/oomd_install.dag: compose apt-install + runner-slice ManagedOOM drop-in +
  enable/start + a show-effective VERIFY that GROUNDS OomdEnforced. Pure .dag, no live
  execution: committed read is ReadAbsent, so the carrier derives OomdUnverified
  (fail-closed). OomdEnforced is derivable ONLY from a ReadObserved carrying an active
  unit AND effective ManagedOOMMemoryPressure=kill.

Witness (claim_batch-discoverable), discriminating by execution:
- managed read (active + kill) grounds OomdEnforced; unmanaged (auto), inactive, and
  absent reads stay OomdUnverified. Fail-open perturbation (auto also grounds) flips the
  witness RED, proving the grounding is real, not a constant.

Does not touch committed gunbc_fleet_oomd_evidence (stays OomdUnverified until a real
live verify runs through the carrier).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* WIP: Model systemd-oomd as a dependency (pure .dag, no live execution): extde

* Fix main-red: name jobserver's external-authority anchor to the gate-required data name

extdeps/ctrl/jobserver.dag declared its anchor as `ctrl_jobserver_authority`,
but the extdeps_external_authority_gate projector (read_external_authority_anchor_from_items)
matches the data def by the EXACT name `extdeps_external_authority_anchor`. So jobserver
projected as Absent -> a "missing:extdeps.ctrl.jobserver" violation, turning
extdeps_external_authority_live_clean_tree_holds() false on main (landed via #5663/#5670).
That is the batch-2 failure my PR inherited through the merge (corpus_live_clean_tree_holds /
corpus_live_anchored_modules_clean_holds + extdeps_external_authority_gate_passes all RED).

Rename the data def to the required name (no other ref to ctrl_jobserver_authority exists
in dsl/ or src/v2/). Verified: both authority witnesses flip GREEN, my oomd witness stays green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Fix tree-wide main-red: regenerate ROADMAP.md from authority (dedup merge-artifact CI-humming line)

ROADMAP.md is a generated projection of gunbc.roadmap_authority (the
GeneratedArtifactDriftGate enforces committed == expected_roadmap_md()).
A textual git-merge of origin/main duplicated the '→ [CI humming]' line;
the authority emits it once, so the committed file drifted and the floor
drift gate returned Bool(false) (main has carried this red since the bad
merge). Regenerated via main_wet on dsl/tools/generated_artifact_gate.dag;
no hand-edit. Drift gate now green by execution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* WIP: Model systemd-oomd as a dependency (pure .dag, no live execution): extde

* Strengthen OomdEnforced grounding to a full conjunction; retarget to the SESSIONS slice (close the oomd-active-alone fail-open)

Per the CI-humming critical path (bright-stag-194 / sharp-heron-321 /
fierce-carp-462): OomdEnforced must NOT be satisfiable by systemd-oomd
being active alone — an active oomd with no managed session cgroup has
nothing to evict-within under aggregate pressure (zero protection), a §5
fail-open. ground_oomd_from_read now requires the full conjunction over
the effective read:
  (a) the managed sessions slice EXISTS
  (b) effective ManagedOOMMemoryPressure == kill on the slice
  (c) per-session memory.max == C (the eviction ceiling) AND parent
      sessions.slice MemoryMax == the measured aggregate reservation
      — both BYTE ceilings, reusing the §3 single authority
      RunnerSliceCapEffectiveness (CapUnbounded => fail-closed)
  (d) ManagedOOMMemoryPressureLimit == intended PSI percent — a SEPARATE
      Percent fact, never conflated with the byte caps (systemd's
      MemoryPressureLimit takes a PSI percentage, not bytes)
  (e) systemd-oomd active AND enabled (UnitFileState == UnitEnabled)
  (f) oomctl confirms the slice is in oomd's managed set
Thresholds (C, aggregate, PSI%) are PARAMETERS resolved from their single
authorities (REGIME-2 oomd_eviction_ceiling / aggregate_reservation;
policy PSI default 60), not minted here.

Retarget: the ManagedOOM policy + drop-in now target the (held,
not-final) sessions.slice — the oomd subject — not gunbc-runner.slice,
which was both the wrong subject (the runner slice is hard-capped, never
evicted) and a §3 nickname for the live system-actions-runner.slice. The
slice literal is parameterized pending the operator-blessed docker
cgroup-parent change.

Committed model stays ReadAbsent => OomdUnverified (pure .dag, fail-closed).
Witness green by execution: the all-facts-hold read grounds Enforced, and
each single missing/wrong fact (slice absent, inactive, disabled, auto,
wrong/unbounded per-session cap, wrong parent cap, wrong PSI %, oomctl
unmanaged, unmeasured aggregate) fails closed — a discriminating red
against the old fail-open.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* WIP: Model systemd-oomd as a dependency (pure .dag, no live execution): extde

* Address review: ground oomd durations on std.measure Second; enumerate UnitFileState arms

Two findings from claude-opus-4-7 review of #5677:

1. unit-modeling hard-block (extdeps/os/oomd.dag): the duration fields
   default_memory_pressure_duration / managed_oom_memory_pressure_duration
   were typed Seconds (a bare branded Int in std/types.dag), a flat scalar
   for a time quantity. std.measure already has the Time dimension + One
   scale + the Nanosecond/Microsecond precedent, and Seconds was used ONLY
   in oomd.dag — so this lands on the carrier (M9 DFS-attach, not a 🟡
   defer): add Second = Measure<Time, One, Nat> (one row beside Nanosecond/
   Microsecond) and type both fields Second / Second?, values second(30) /
   second(20). No flat scalar propagates into the policy authority.

2. §5 fail-open (gunbc/oomd_install.dag): unit_is_enabled matched
   UnitEnabled => true; _ => false over the closed 10-variant UnitFileState,
   hiding every future arm behind one false. Now enumerates all ten arms
   explicitly (like the sibling unit_is_active). UnitEnabledRuntime => false
   is deliberate: runtime-only enablement does not survive a reboot, which
   the grounding requires.

Witness still green by execution; measure carrier tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Address review nits: String? grounding sentinel; promote Percent to std.types

Non-blocking findings from claude-opus-4-7's approving review of #5677:

2. first_grounding_failure overloaded "" as the no-failure sentinel (a
   second representation of absence). Now returns String? — Absent = all
   checks pass, Present{reason} = first failure — and ground_oomd_from_read
   matches on it. No empty-as-absent straddle.

3. Percent was declared in the extdeps.os.oomd leaf, but a percentage is a
   universal (not oomd-specific) concept; the next caller would re-mint or
   import-up awkwardly. Promoted to std.types beside the other branded-Int
   scalars (Duration/Milliseconds/Seconds) that are collectively pending the
   value-Measure migration; oomd.dag and oomd_install.dag now import it from
   std. No fork (no std.Percent existed before).

Finding 1 (the coproduct->Bool enforcement predicates) is left as-is — the
reviewer called it defensible: they encode a domain semantic (only
Active/UnitEnabled constitute enforcement; Activating/Reloading deliberately
fail closed), not mechanical variant-equality, and the per-check
failure_reason carries the located cause.

Witness green by execution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Add gunbc_oomd_fleet_evidence seam for the fleet capacity gate (CI-humming PR2)

sharp-heron-321's CI-humming PR2 needs to bind fleet_host_budget's
gunbc_fleet_oomd_evidence (today a literal OomdUnverified, consumed by
ci_runner_placement REGIME-2) to this PR's grounded result rather than
hardcoding = OomdEnforced. Expose a single binding seam:

  gunbc_oomd_fleet_evidence(req: OomdGroundingThresholds) -> OomdEnforcementEvidence

It grounds over the committed gunbc_oomd_show_effective_read, so it stays
OomdUnverified (fail-closed) until a real live ReadObserved on the managed
sessions slice grounds the full conjunction — §5 verify-the-realization,
no test-greening literal flip. It returns OomdEnforcementEvidence directly
so std.realization_reconcile.Reconciliation never leaks into
fleet_host_budget (which carries product.budget_tree.Reconciliation) —
sidestepping the documented flat-namespace collision. The read shape is
internal, so the seam is stable across future grounding-conjunct additions
(e.g. a session-membership check coordinated with fierce-carp's
cgroup-parent PR).

Witness exercises it green (fail-closed on the committed ReadAbsent).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* WIP: Model systemd-oomd as a dependency (pure .dag, no live execution): extde

* Address RC: ground Percent as a Measure in std.measure (not a std.types brand)

The prior std.types Percent (branded Int) forked the unit-modeling
authority — exactly what the in-tree dissolve_on (container_stats.dag:115:
'Ratio or PercentagePoint typed alias lands in std/measure') was authored
against. It satisfied the trigger in name without grounding it in the
Measure carrier, so container_stats.cpu_percent could not reuse it and the
oomd percent fields consumed a forked unit (DESIGN §3 single authority +
unit-modeling hard-block).

Fix: add a Dimensionless quantity to std.measure and land
  type Percent = Measure<Dimensionless, One, Nat>   (+ percent / percent_count)
beside the other Measure aliases (Second/ByteSize/Hertz). Quantity is a
phantom type param (never matched), so the new variant is additive.
Removed Percent from std.types. extdeps/os/oomd.dag and gunbc/oomd_install.dag
import it from std.measure; percent literals are percent(N); the PSI%
grounding check compares via percent_count (no record == straddle). This
gives percent ONE home in the Measure framework — container_stats's
cpu_percent can now dissolve onto the same Dimensionless quantity (a future
Ratio sibling) instead of a parallel brand.

Predicate-dissolution finding is non-blocking (workflow-local, domain
semantic) — left as-is, addressed in prior replies.

Witness green by execution; measure carrier tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* WIP: Model systemd-oomd as a dependency (pure .dag, no live execution): extde

---------

Co-authored-by: Brian Searls <briansearls1@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant