Skip to content
Merged
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
1 change: 1 addition & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ The domain roster is a modeled fact, not this prose: `gunbc.design_argument` `co
- **leasing / locking / grants** — exclusive or bounded use: leases with a declared deadline, compare-and-set expectations, scoped authorizations, grants naming who may perform an effect and how it is released, resource handles. Home authorities: `std.temporal_effect::HeldLease`, `std.durable_compare_and_set::CasExpectation`, `std.scoped_authorization::ScopedAuthorization`, `std.effect_grant::Grant`, `std.resources::ResourceHandle`.
- **fabric / compute** — allocation, placement and negotiation of resources: work contracts, compute layouts, fabric cell reservations, capacity class admission. Home authorities: `gunbc.compute.work_request::WorkOperation`, `gunbc.compute.work_provider_local::ComputeLayout`, `gunbc.fabric_control_plane::CellReservation`, `gunbc.fabric_executor_class::CapacityClassAdmission`.
- **decision / selection** — a choice must remain attributable to its subject, candidate field, constraints, evidence and policy, and conformance exposes accidental forks of that authority; the hard selection laws -- no answer outrunning its evidence, no front standing as a winner -- stay with the §3d reviewer, and goal assessment (std.goal_assessment) is a boundary of this row, not a member of it. Home authorities: `std.decision::DecisionSubject`, `std.decision::RealizationSelectionResult`, `std.decision::SelectionReceipt`, `std.decision::select_realization`, `std.pareto::SelectionAxis`, `std.pareto::ParetoEntry`, `std.pareto::DominanceVerdict`.
- **process observability / reporting** — operators and automated readers need reports traceable to the facts and occurrences they describe; the owning domain establishes the fact, the observation model represents its occurrence, the renderer or codec encodes it, and a bound effect delivers it -- four responsibilities, and producing bytes is not delivery. The row is scoped to process reporting: a repository population measured at a revision stays with gunbc.repository_census_observation, and durable delivery with the effect authorities. Home authorities: `std.observation::ObservationEvent`, `std.observation::RecordedObservation`, `std.observation::ObservationPresentation`, `gunbc.observation_ci_render::ci_event_line`, `gunbc.observation_ci_render::ci_render_line`.

A domain is added by adding a row with its home; a domain whose home does not yet exist is not a conformance row but a modeling obligation (§6), and a row naming a home that does not resolve is a §3 citation defect. Two boundaries keep this criterion exclusive of its neighbors: **single-authority** (§3) keeps naming and citation — a second name, a literal where a modeled value exists, a positional citation — while conformance owns *inhabitance* of an existing model; and **minimize-redundancy** (§2) keeps copied work regardless of domain. The partition is a boundary between propositions, not a proof: `gunbc.roadmap.roadmap_review_criteria` `review_criterion_identities_are_unique` establishes only that no two criteria share a key and explicitly disclaims semantic exclusivity, and the worker feedback's overlap count groups findings by file for presentation, so two criteria may legitimately report one defect from two perspectives. Semantic exclusivity is held at the level of representative boundary cases in review, at its actual evidence level and no higher. One more distinction the row vocabulary keeps: a home that resolves but owns only part of the scope a row claims is a scope/ownership mismatch on the row, not the citation defect of a reference that fails to resolve.

Expand Down
22 changes: 22 additions & 0 deletions dag/gunbc/design_argument.dag
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,28 @@ data conformance_domains: List<ConformanceDomain> = [
],
narrative: "a choice must remain attributable to its subject, candidate field, constraints, evidence and policy, and conformance exposes accidental forks of that authority; the hard selection laws -- no answer outrunning its evidence, no front standing as a winner -- stay with the §3d reviewer, and goal assessment (std.goal_assessment) is a boundary of this row, not a member of it",
},
ConformanceDomain {
key: "conformance-observability",
label: "process observability / reporting",
question: "When this change reports process progress, an occurrence, a measurement or an outcome -- a progress line, a heartbeat, a receipt line, a refusal restated for a reader -- does it use the existing observation and presentation authorities: an ObservationEvent or RecordedObservation carrying the typed subject, transition and Measured readings, projected through the CI renderer's event line and rendered line; and where it departs (a seed mirror, a wire format, a transport), does it state the departure and its reason?",
homes: ConformanceHomes {
first: decl_ref(module_path: "std.observation", decl_name: "ObservationEvent"),
rest: [
decl_ref(module_path: "std.observation", decl_name: "RecordedObservation"),
decl_ref(module_path: "std.observation", decl_name: "ObservationPresentation"),
decl_ref(module_path: "gunbc.observation_ci_render", decl_name: "ci_event_line"),
decl_ref(module_path: "gunbc.observation_ci_render", decl_name: "ci_render_line"),
],
},
tells: [
"a status, count, duration or refusal cause built into an ad hoc formatted message instead of a typed event field the renderer projects",
"presentation assignment (glyph, attention, grouping, human units) rebuilt at the emit site rather than read from ObservationPresentation and the renderer",
"a reader that parses human prose as an undeclared wire contract, or a producer cut over while its reader still expects the old shape",
"an unavailable reading suppressed into a numeric default (a zero, a na that prints as a number) instead of a MeasuredUnavailable arm naming its cause",
"a site recorded as migrated in gunbc.observation_emit_census while the active producer bypasses the projection the row names"
],
narrative: "operators and automated readers need reports traceable to the facts and occurrences they describe; the owning domain establishes the fact, the observation model represents its occurrence, the renderer or codec encodes it, and a bound effect delivers it -- four responsibilities, and producing bytes is not delivery. The row is scoped to process reporting: a repository population measured at a revision stays with gunbc.repository_census_observation, and durable delivery with the effect authorities",
},
]

fn conformance_review_part(d: ConformanceDomain) -> PropositionReview {
Expand Down
83 changes: 72 additions & 11 deletions dag/gunbc/observation_ci_render.dag
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import std.measure {
byte_size_count,
basis_point,
basis_point_count,
events_per_minute_count,
gibibyte_scale_factor_bytes,
milliseconds_per_second,
seconds_per_minute,
Expand All @@ -31,6 +32,11 @@ import gunbc.witness_row_cost {
shared_fill_payer_tag,
}
import extdeps.render.glyphs { resolve_symbol }
import gunbc.memory_stall_refusal {
MemoryStallObservation,
memory_stall_major_fault_rate,
memory_stall_self_cpu_share,
}
import extdeps.github.log_annotations { annotation_group_prefix, annotation_endgroup, neutralize_workflow_commands }
import std.render_repeat_string_bootstrap { repeat_string }
import std.observation {
Expand Down Expand Up @@ -373,30 +379,85 @@ fn ci_event_line(event: ObservationEvent, basis: AttentionBasis) -> RenderedLine
)
}

// THE FLOOR HEARTBEAT SAMPLE IS WHAT THE FLOOR ACTUALLY READS ON EACH BEAT, TYPED, EVERY FIELD
// A MEASURED ARM (restored 2026-09-11). The earlier shape of this record carried a batch/entry
// position and cgroup swap and PSI readings because it was projected from the batched plan/walk
// floor; #9228 deleted that floor and its projection together, and the one-attempt floor that
// replaced it beat on with a raw [floor-heartbeat] eprintln whose fields were each added under a
// measured justification the old record could not hold: this floor's own cpu delta since it
// started, its major-fault counter, the cgroup leaf's charge and its high/max/local-high throttle
// events, host-wide swap-in and major faults from /proc/vmstat (the pair that separates host swap
// from file-backed churn), and the memory-stall window the refusal in
// gunbc.host.memory_stall_refusal decides on. So the record is REPLACED, not extended: the batch
// position has no producer any more and a field nothing measures is a fabrication waiting to be
// filled. The stall window is carried as the authority's own MemoryStallObservation so the rate and
// share printed here are computed by the same two functions the refusal consumes -- a second
// arithmetic here would agree the day it was written and drift after. Unreadability is kept at the
// grain the seed reads it: one /proc/self/stat read feeds cpu and major_faults together, statm feeds
// rss, memory.current, memory.events and memory.events.local are three files, /proc/vmstat is one,
// and the stall window needs both its counters -- each arm names its cause and never prints 0.
type HeartbeatSample {
elapsed: Millisecond
active: ObservationSubject
entry_index: Nat
entry_total: Nat
cpu: Measured<Millisecond>
major_faults: Measured<Nat>
rss: Measured<ByteSize>
swap: Measured<ByteSize>
pressure: Measured<BasisPoint>
cgroup_charge: Measured<ByteSize>
cgroup_high_events: Measured<Nat>
cgroup_max_events: Measured<Nat>
cgroup_local_high_events: Measured<Nat>
host_swap_in: Measured<Nat>
host_major_faults: Measured<Nat>
stall: Measured<MemoryStallObservation>
}

fn ci_measured_count_text(m: Measured<Nat>) -> String {
match m {
MeasuredValue { value: n } => to_string(n)
MeasuredUnavailable { cause: c } => concat("unreadable (", concat(c, ")"))
}
}

fn ci_measured_duration_text(m: Measured<Millisecond>) -> String {
match m {
MeasuredValue { value: d } => ci_human_duration(d: d)
MeasuredUnavailable { cause: c } => concat("unreadable (", concat(c, ")"))
}
}

// The stall clause prints the two figures the refusal is decided on, computed by the refusal's own
// functions: faults per minute and the process's user-cpu share of the window's wall.
fn ci_measured_stall_text(m: Measured<MemoryStallObservation>) -> String {
match m {
MeasuredValue { value: o } =>
concat(
to_string(events_per_minute_count(r: memory_stall_major_fault_rate(o: o))),
concat(" faults/min at ", concat(ci_human_percent(bp: memory_stall_self_cpu_share(o: o)), " user cpu"))
)
MeasuredUnavailable { cause: c } => concat("unreadable (", concat(c, ")"))
}
}

// Law 2: the heartbeat carries IDENTITY, not just vitals — and identity comes first in the sentence, vitals last. The ordering is the whole point. A reader scanning a wall of heartbeats needs to know WHAT is running; the memory numbers only matter once they know that. The captured crawl window is the counter-example in the record: sixty heartbeats that named no subject at all while one module typechecked for ten minutes, so the operator could see the process was alive and could not see what it was doing.
fn ci_heartbeat_line(sample: HeartbeatSample) -> RenderedLine {
let detail = ci_subject_detail_text(subject: sample.active)
let position = concat(": entry ", concat(to_string(sample.entry_index), concat(" of ", to_string(sample.entry_total))))
let identity = concat(
"still in ",
concat(ci_subject_activity_text(subject: sample.active),
concat(position, if detail == "" { "" } else { concat(", now ", detail) }))
)
let vitals = concat(
"memory ", concat(ci_measured_bytes_text(m: sample.rss),
concat(", swap ", concat(ci_measured_bytes_text(m: sample.swap),
concat(", pressure ", ci_measured_percent_text(m: sample.pressure)))))
if detail == "" { "" } else { concat(", now ", detail) })
)
let vitals = join([
concat("cpu ", ci_measured_duration_text(m: sample.cpu)),
concat("memory ", ci_measured_bytes_text(m: sample.rss)),
concat("cgroup charge ", ci_measured_bytes_text(m: sample.cgroup_charge)),
concat("cgroup events high ", concat(ci_measured_count_text(m: sample.cgroup_high_events),
concat(" / max ", concat(ci_measured_count_text(m: sample.cgroup_max_events),
concat(" / local high ", ci_measured_count_text(m: sample.cgroup_local_high_events)))))),
concat("major faults ", ci_measured_count_text(m: sample.major_faults)),
concat("host swap-in ", ci_measured_count_text(m: sample.host_swap_in)),
concat("host major faults ", ci_measured_count_text(m: sample.host_major_faults)),
concat("stall ", ci_measured_stall_text(m: sample.stall))
], ", ")
RenderedLine {
glyph: StatusPulse,
text: concat(ci_human_duration(d: sample.elapsed), concat(" in — ", concat(identity, concat(". ", vitals)))),
Expand Down
Loading
Loading