Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
81ed09d
WIP: kind-annotated type parameters, and refuse a type parameter in v…
Sep 20, 2026
abcf71c
Ask the declared type-parameter roster, not the inferred type; carry …
Sep 20, 2026
8dd8b7d
Resolve both sides of the kind check; the alias declaration does not …
Sep 20, 2026
4f44a27
Make the kind a two-arm coproduct so its roster is readable; restore …
Sep 20, 2026
93b636f
Carry the children arm into the seed, and file two language-layer cla…
Sep 20, 2026
97cd138
Two plan docs said no SHA-256 computation exists in .dag; extdeps.cry…
Sep 21, 2026
5eea716
Merge remote-tracking branch 'origin/session/gentle-seal-490' into se…
Sep 21, 2026
cc575c0
std.content_hash gains a COMPUTING cryptographic mint over extdeps.cr…
Sep 21, 2026
3dde230
fabric object refs move onto the cryptographic mint, and the refusal …
Sep 21, 2026
8a0f5ec
fabric_storage_witness threads the Optional, and gains the claim that…
Sep 21, 2026
b4a00b8
File the mint/parser stranding class, and drop the scm-design hunk to…
Sep 21, 2026
957928b
The two remaining fabric witnesses thread the Optional mint, once per…
Sep 21, 2026
189cab5
B3: v2.std.node Hash cannot retype today, and the row guarding that w…
Sep 21, 2026
e20e8c5
The kind wall gets its discriminating pair, enrolled and CI-runnable
Sep 21, 2026
1d74c3c
The census found a second row carrying the same artifact-shaped trigger
Sep 21, 2026
5a0204f
Piece A delivers ONE working wall, not three: the third is inert in t…
Sep 21, 2026
ddfb367
Merge remote-tracking branch 'origin/main' into session/nimble-hawk-154
Sep 21, 2026
1cbc682
The fabric-storage Known-limits bullet described the code I had not w…
Sep 21, 2026
17a4cca
Declare the kind-reflection seed growth, and wire it into the roster …
Sep 21, 2026
9e1f47b
Address the external review: the peer-parameter digest, the empty sto…
Sep 22, 2026
6882717
Merge remote-tracking branch 'origin/main' into session/nimble-hawk-154
Sep 22, 2026
2ac15a9
B2 withdrawn on measurement: the structural family stays, the computi…
Sep 22, 2026
19ba088
B1 withdrawn on a second measurement, and three citation defects the …
Sep 22, 2026
1570252
Two prose honesty defects review 69986 found, both in text this branc…
Sep 22, 2026
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 dag/gunbc/fabric/fabric_event_log.dag
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import std.measure { Count, One, Measure }
import product.placement_supply { HostIdentity }
import std.process { ProcessExit, ExitSuccess, exit_failure }
import std.fabric_storage {
FabricObject, FabricObjectRef, fabric_object_ref_of, fabric_object_ref_wire, fabric_object_ref_of_wire,
FabricObject, FabricObjectRef, fabric_object_ref_wire, fabric_object_ref_of_wire,
FabricHeadAbsent, FabricHeadAt, ExpectHeadAbsent, ExpectHeadAt, FabricHeadExpectation,
FabricStorageFault, fabric_storage_fault_wire,
FabricObjectStored, FabricPutRefused, FabricHeadObserved, FabricHeadReadRefused,
Expand Down
37 changes: 25 additions & 12 deletions dag/gunbc/fabric/fabric_storage_file_store.dag
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import std.durable_compare_and_set {
CasSlotObservationRefused, CasGenerationPublicationRefused, CasUnreadableMalformed, CasUnreadableReadRefused,
}
import std.fabric_storage {
FabricObjectRef, FabricObject, FabricStoredObject, fabric_object_ref_of, fabric_object_ref_wire, fabric_object_ref_of_wire, fabric_object_ref_eq,
FabricObjectRef, FabricObject, FabricStoredObject, FabricAddressedObject, fabric_object_addressed, fabric_object_ref_wire, fabric_object_ref_of_wire, fabric_object_ref_eq,
fabric_object_preimage, fabric_object_decode, fabric_object_verified, FabricObjectVerified, FabricObjectVerificationFailed,
FabricHeadReading, FabricHeadAbsent, FabricHeadAt, FabricHeadExpectation, fabric_head_admits,
FabricStorageFault, FabricStoreRefused, FabricObjectMissing, FabricObjectCorrupt, FabricHeadNameRefused,
Expand Down Expand Up @@ -104,11 +104,19 @@ fn fabric_storage_file_get(root: FabricStorageFileRoot, object: FabricObjectRef)

// PUTTING AN OBJECT. Its ref is derived from its bytes, so a put names nothing the caller chose. A
// put is idempotent -- the name already holding THESE bytes is the object already stored -- and an
// occupied name holding OTHER bytes is a digest collision, refused as corruption rather than read
// through: the digest family is non-cryptographic (std.content_hash content_hash_of_value), and this
// arm is what keeps a collision loud.
fn fabric_storage_put_from_create(object: FabricObject, created: FilesystemCreateNew, existing: FabricObjectGet) -> FabricPut {
let r = fabric_object_ref_of(object: object)
// occupied name holding OTHER bytes is refused as corruption rather than read through.
//
// THE COLLISION ARM STAYS. The digest family is non-cryptographic (gunbc.recurring_failure_mode
// fabric_object_identity_is_a_structural_locator), so a collision is a thing an ordinary workload
// can actually meet and the arm is a wall against an accident. It must not be removed when the
// family becomes cryptographic either: under that family a collision stops being noise and becomes
// an ATTACK, which is exactly when the check earns its keep.
//
// THE REF IS RECEIVED, NOT RE-DERIVED: the object and its ref arrive as ONE carrier, minted once at
// the entry below, so this function cannot be handed a ref for some other object's bytes.
fn fabric_storage_put_from_create(addressed: FabricAddressedObject, created: FilesystemCreateNew, existing: FabricObjectGet) -> FabricPut {
let r = addressed.ref
let object = addressed.object
match created {
FilesystemCreated { path: _ } => FabricObjectStored { object: r }
FilesystemCreateTargetOccupied { path: _ } =>
Expand All @@ -125,16 +133,21 @@ fn fabric_storage_put_from_create(object: FabricObject, created: FilesystemCreat
}
}

// BOTH HALVES OF THE ADDRESSED OBJECT COME OFF THE CARRIER, and the write below reads a.object
// rather than the outer `object` deliberately. They are the same VALUE; the difference is SHAPE.
// Reading the outer binding is the spelling that lets a later edit change one side without the
// other, which is how the peer-parameter defect FabricAddressedObject exists to close gets back in.
fn fabric_storage_file_put(root: FabricStorageFileRoot, object: FabricObject) -> FabricPut {
let r = fabric_object_ref_of(object: object)
let a = fabric_object_addressed(object: object)
let r = a.ref
let path = fabric_storage_object_path(root: root, object: r)
let write = Filesystem.WriteCreateNew(path: path, content: fabric_object_preimage(object: object) as String)
let write = Filesystem.WriteCreateNew(path: path, content: fabric_object_preimage(object: a.object) as String)
let created = filesystem_create_new(path: write.path, success: write.success, error: write.error, error_kind: write.error_kind)
match created {
FilesystemCreateTargetOccupied { path: _ } => fabric_storage_put_from_create(object: object, created: created, existing: fabric_storage_file_get(root: root, object: r))
FilesystemCreated { path: _ } => fabric_storage_put_from_create(object: object, created: created, existing: FabricObjectGetRefused { fault: FabricObjectMissing { object: r } })
FilesystemCreateRefused { path: _, kind: _, error: _ } => fabric_storage_put_from_create(object: object, created: created, existing: FabricObjectGetRefused { fault: FabricObjectMissing { object: r } })
FilesystemCreateKindUnrecognized { path: _, observed: _, error: _ } => fabric_storage_put_from_create(object: object, created: created, existing: FabricObjectGetRefused { fault: FabricObjectMissing { object: r } })
FilesystemCreateTargetOccupied { path: _ } => fabric_storage_put_from_create(addressed: a, created: created, existing: fabric_storage_file_get(root: root, object: r))
FilesystemCreated { path: _ } => fabric_storage_put_from_create(addressed: a, created: created, existing: FabricObjectGetRefused { fault: FabricObjectMissing { object: r } })
FilesystemCreateRefused { path: _, kind: _, error: _ } => fabric_storage_put_from_create(addressed: a, created: created, existing: FabricObjectGetRefused { fault: FabricObjectMissing { object: r } })
FilesystemCreateKindUnrecognized { path: _, observed: _, error: _ } => fabric_storage_put_from_create(addressed: a, created: created, existing: FabricObjectGetRefused { fault: FabricObjectMissing { object: r } })
}
}

Expand Down
68 changes: 68 additions & 0 deletions dag/gunbc/kind_reflection_seed_growth.dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
module gunbc.kind_reflection_seed_growth

import gunbc.roadmap_model { RoadmapNodeId }
import gunbc.seed_growth { SeedGrowthJustification }
import std.decl_ref { DeclarationRef, WholeDeclaration }

// FORWARD-FREEZE RECEIPT for the hand Rust carried into the seed by the kind-reflection work
// merged from gunbc#11819. Authored because review 69733 found the growth undeclared, and it was
// right: gunbc.seed_growth_admission makes unenumerated hand growth in src/v1 a stop-line, and
// prose in a // annotation cannot discharge it. DESIGN section 4c is the reason the annotations
// those declarations already carry are not enough -- a dissolution condition is a typed carrier's
// job, and "replaced by generated bytes on the next regen" living only in a comment is
// uncountable by the roster that exists to count it.
//
// THE POPULATION SPLITS IN TWO, AND ONLY ONE HALF IS REAL GROWTH. Sixteen declarations were added
// or modified across v1_compiler_infer_resolve, v1_compiler_parse, v1_std_core,
// std_machine_constraints and v1_compiler_infer. FIFTEEN of them resolve in the .dag authority --
// type_arg_kind_inhabitance, KindInhabitance, kind_admissible_inhabitant_name,
// kind_names_admissible_inhabitant, kind_lookup_trace, kind_decl_for_message,
// node_authored_or_own_name, type_arg_display_spelling, type_param_kind_diagnostics,
// type_arg_name_is_bound_generic_parameter, parse_optional_type_param_kind,
// with_type_param_kind_property, TypeParamKindResult, type_param_kind_property_name, and the
// WidthResolution arms -- so they are generated bytes carried BY HAND AHEAD OF A REGEN, which is
// ExistingSeedItemModified rather than addition. ONE does not: binding_resolves_to_type_parameter
// occurs ZERO times in every .dag under src/v1. That one is the growth, and it is the only
// declaration this row cites.
//
// WHAT IT IS: A STUB WHOSE BODY IS THE LITERAL false, AND SAYING SO IS THE POINT. It stands where
// v1.compiler.infer declares name_is_enclosing_declared_type_parameter, which asks the DECLARED
// type-parameter roster carried on InferScope as enclosing_declared_type_param_names. The seed's
// InferScope HAS NO SUCH FIELD. Implementing the predicate by hand therefore means adding a struct
// field and plumbing it through every InferScope construction site in generated Rust -- which is
// cementing compiler logic into the seed to make a check go green, the direction DESIGN section 7
// and this repository's standing instruction both refuse. The stub is the smaller, more honest
// debt, and it is declared here rather than left silent.
//
// THE CURRENT BOUNDARY IS A DEAD BRANCH AND AN INERT WALL, MEASURED RATHER THAN ASSERTED. Because
// the predicate is false, both call sites in v1_compiler_infer take their else arm always, so the
// net behavioural delta of all three added items is ZERO -- and the consequence is that
// TypeParameterInValuePosition, which v1.std.core declares with SeverityError and
// GateBlocking and v1.compiler.infer constructs at two sites, CANNOT FIRE. A module whose entire
// content is a generic function returning its own type parameter compiles with exit 0, zero
// blocking errors, and emits. That is not a side note about this row; it is what this row is
// declaring, and DESIGN section 4b names it: the tier where the machinery exists but nothing gates
// on it, and an inert lens is itself a lie.
//
// THE SILENCE IS WITNESSED RATHER THAN DESCRIBED.
// test.claim.type_argument_kind_inhabitance_witness_test pins it as a SILENCE assertion with an
// adequacy control on the same harness and the same run, so the zero is the compiler's and not a
// harness that never reached the judgment. It goes red the day the wall works, and that red means
// flip the assertion, not relax it. The class is
// gunbc.recurring_failure_mode.a_wall_declared_in_dag_is_inert_in_the_seed_that_compiles.
//
// WHY THE STUB IS NOT SIMPLY DELETED, which is the first repair anyone should reach for. Deleting
// it means deleting the two call sites with it, and those two branches DO resolve in the .dag --
// v1.compiler.infer constructs the diagnostic at exactly those points. Removing them would put the
// seed further from the authority rather than closer, and the next regen would re-add them. The
// divergence worth removing is the predicate, and the way to remove it is the regen, not a second
// hand edit.
data kind_reflection_seed_growth_justification: SeedGrowthJustification = SeedGrowthJustification {
hand_authored_declarations: [
DeclarationRef { module_path: "v1_compiler.v1_compiler_infer", decl_name: "binding_resolves_to_type_parameter", field: WholeDeclaration }
],
reason: "A stub standing where v1.compiler.infer declares name_is_enclosing_declared_type_parameter. The declared predicate reads InferScope's enclosing_declared_type_param_names roster; the seed's InferScope carries no such field, so honouring it by hand would mean adding a struct field and threading it through every construction site in generated Rust -- hand-authoring compiler logic into the seed to make a wall go green, which DESIGN section 7 refuses. Its body is the literal false, so both call sites take their else arm always and the behavioural delta of the whole kind-reflection seed carry is zero.\n\nWHAT IT COSTS, STATED AND NOT NETTED AWAY: TypeParameterInValuePosition is declared SeverityError/GateBlocking and constructed at two sites, and it cannot fire. A generic function returning its own type parameter compiles clean and emits. The silence is pinned by test.claim.type_argument_kind_inhabitance_witness_test as an explicit hole assertion with an adequacy control on the same run, and filed as gunbc.recurring_failure_mode.a_wall_declared_in_dag_is_inert_in_the_seed_that_compiles.\n\nSCOPE, ENUMERATED RATHER THAN COUNTED. Fifteen further declarations in this change resolve in the .dag authority and are generated bytes carried ahead of a regen (ExistingSeedItemModified per gunbc.seed_growth_admission SeedGrowthChangeDisposition), so they are not counted as additions; they are named in this file's header rather than omitted. TWO of them diverge in SHAPE and both are disclosed for the same reason; an earlier revision of this row said ONE and was off by one on its own stated axis (review 69986, BLOCKING). FIRST: the seed's type_arg_kind_inhabitance takes three parameters where v1.compiler.infer_resolve declares four, because the seed lacks the kind_inhabitant_matches_resolved arm. SECOND: src/v1/stage0/src/std_machine_constraints.rs carries `pub type WidthResolution = PointerWidth;`, an ALIAS, where std.machine_constraints now declares the two-arm coproduct StaticWidthIndex | PointerWidth -- the emission convention for such a coproduct is one enum plus a marker struct per arm, visible in this same change at v1_compiler_infer_resolve's KindInhabitance, and StaticWidthIndex occurs nowhere under src/. So the mirror is not the bytes a regen would produce, and the seed cannot express the literal arm at all. Neither divergence is repaired by hand: hand-carrying either is the cementing DESIGN section 7 refuses, and both close on the same regen this row's trigger names. The behaviour they leave is measured rather than assumed -- the kind wall's reds and its positive control are witnessed on the BUILT compiler by test.claim.type_argument_kind_inhabitance_witness_test, and the literal axis is admitted unconditionally by the language-level rule in either tree, so the alias costs no refusal the coproduct would have made today.",
owning_dissolution_lane: "v1-hand-queue-drain" as RoadmapNodeId,
trigger: "Regenerate the stage0 mirror from v1.compiler.infer and v1.compiler.infer_resolve -- SUFFICIENT FOR the built compiler to consult the declared type-parameter roster and to carry the four-parameter kind-inhabitance fold, so that a type name in value position refuses at its own location rather than at rustc's, and the seed's checking predicates are the authority's rather than hand-carried approximations of them. The regen is what deletes this row; a further hand patch to either declaration would not, because it would leave the seed and the authority disagreeing about which predicate decides.",
current_boundary: "Both call sites are dead branches: the predicate is false, so the net behavioural change is zero and no program is judged differently than before this change. The cost is not a wrong answer but an absent one -- the value-position wall is unreachable by construction, and gunbc#11819's claim that it fires is retracted in gunbc#11996 rather than carried. The kind-inhabitance wall it sits beside DOES fire and is witnessed in both directions on one run, so the two must not be read together: one wall discriminates, the other is inert, and the seed is why."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module gunbc.recurring_failure_mode.a_carrier_swap_moves_the_mint_and_strands_the_parser

import std.types { NonEmptyStr }
import gunbc.recurring_failure_mode { RecurringFailureMode }
import std.decl_ref { DeclarationRef, WholeDeclaration }

data a_carrier_swap_moves_the_mint_and_strands_the_parser: RecurringFailureMode = RecurringFailureMode {
identity: "a_carrier_swap_moves_the_mint_and_strands_the_parser" as NonEmptyStr,

receipts: [
"A carrier swap moves the MINT and strands the PARSER. INVALID STATE: one value has a producer and an inverse -- a digest and the reader of its wire form, an id and its parser, an encoder and its decoder -- and a migration retypes the producer while the inverse keeps admitting only the OLD form. The two halves are separate declarations, so nothing relates them, and the type system is satisfied on both sides: each is internally consistent with the type it names, and only their COMPOSITION is broken.",

"WHY IT IS WORSE THAN AN ORDINARY BREAK, AND THIS IS THE WHOLE ROW: the failure is READ-PATH ONLY, and the two paths exercise different halves. A write exercises the mint, which is correct. A read exercises the parser, which is not. So the system accepts work, the corpus fills with values written under the new form, and every attempt to read one refuses -- the damage accumulates during exactly the period in which everything looks healthy. A put-only smoke test is GREEN BY CONSTRUCTION and cannot detect it, which is why the usual first instrument is the one that will not fire.",

"RECEIPT, 2026-09-21, gunbc#11996, found by reading the module rather than by a red. std.fabric_storage fabric_object_ref_of was moved from the fnv1a64 structural digest onto a computed SHA-256, so fabric_object_ref_wire began serializing the sha256-prefixed wire form. Its inverse, fabric_object_ref_of_wire, read content_hash_from_structural_digest, which admits sixteen hex digits and refuses everything else. Left alone it would have refused every ref the module mints. The brief that directed the migration named the mint and did not name the inverse; neither did the module's own note, which describes ref-minting and wire-parsing as the two halves of one closed loop and still did not couple them at the point of change.",

"THE TELL, STATED SO IT CAN BE LOOKED FOR BEFORE THE READ FAILS: a migration diff that touches a producer and not its inverse, where the inverse is discoverable by name -- of_wire beside to_wire, parse beside serialize, decode beside encode. The corpus already carries the coupling as prose in the same modules; what it does not carry is a mechanism that makes changing one without the other refuse. The second tell is a round-trip claim that passes because it round-trips through the NEW form on both sides while the STORED population is in the old one -- agreement between two halves that moved together is not evidence that either matches what is already written down.",

"RUNG FOUND AT: below the ladder -- silent wrongness on the read path, with no diagnostic at the change site and a discriminating red available only from a read of a value written before the swap. ATTAINABLE CEILING: structurally guaranteed. A serializer and its parser over one carrier are an inverse PAIR, and DESIGN section 4 already names this shape -- one grammar read in both directions -- so a substrate that derived the parser from the same rows as the serializer would make the two unable to disagree, exactly as it does for emission and ingestion. NEXT-RUNG TRIGGER: the wire grammar of a carrier declared once and read in both directions -- SUFFICIENT FOR no module to be able to author a serializer and a parser that admit different languages, so a family change propagates to both halves or refuses at the declaration. MECHANICALLY PREVENTABLE is reachable sooner and is not the ceiling: a round-trip claim pinned against a SUPPLIED wire literal from the old population, rather than against a value this run just serialized, goes red when the parser is stranded.",
],

evidence: [
DeclarationRef { module_path: "std.fabric_storage", decl_name: "fabric_object_ref_of_wire", field: WholeDeclaration },
DeclarationRef { module_path: "std.fabric_storage", decl_name: "fabric_object_ref_wire", field: WholeDeclaration },
DeclarationRef { module_path: "std.content_hash", decl_name: "parse_content_hash", field: WholeDeclaration },
],
}
Loading