diff --git a/dag/gunbc/doc_graph_roots.dag b/dag/gunbc/doc_graph_roots.dag index b618eda42ec..ac98316e334 100644 --- a/dag/gunbc/doc_graph_roots.dag +++ b/dag/gunbc/doc_graph_roots.dag @@ -239,17 +239,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the required floor's in-flight shared-fill OWNERSHIP is changed so this characterization no longer describes the shipped mechanism; the row deletes with the superseded findings document. THE OTHER CONJUNCT OF THIS TRIGGER HAS ALREADY FIRED AND THE ROW IS DELIBERATELY RETAINED, which is stated here because a half-fired trigger read as unfired is how a dead assumption keeps answering questions: the deadline-accounting half died on 2026-09-12 when the claim ceiling moved onto eval_steps and CPU became observed-only for every claim, and changed_witness_cpu_deadline -- the subject this row was registered at -- was deleted with it. The registration moves to claim_cost_basis_standing, the successor decision about which quantity may refuse a claim, and the document carries a superseded-half banner at its head. The shared-fill half is untouched and is the live subject: eval steps are netted of fill exactly as CPU is, so the ownership argument transfers to the new ceiling without restatement, and it is still what required-floor-in-flight-shared-fill-design.md cites.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "fabric-ci-replacement", - primary_work: DeclarationRef { module_path: "gunbc.witness_floor_workflow", decl_name: "witness_floor_job", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "extdeps.github.checks", decl_name: "CheckRun", field: WholeDeclaration }, - DeclarationRef { module_path: "product.fabric.execution", decl_name: "ExecutionGrant", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.ci_runner_target", decl_name: "CiRunnerTarget", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the cutover lands: the fabric issues the grant that runs the required floor, the check verdict is reported through the modeled Checks surface, and .github/workflows/witnesses.yml is deleted along with the emitter that produced it -- at which point this document describes a shipped system rather than a planned one and the row deletes with it. Registered at subject grain: the four systems bound here are the ones whose movement would make this document's purpose false. witness_floor_job is the thing being replaced and is bound deliberately, so that if it is renamed or restructured before the cutover this document reds rather than silently describing a job that no longer exists.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "fabric-recut-program", @@ -360,53 +349,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "each parked item is either promoted to its own design document or retired with a reason, leaving this file with no subjects; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "affected-set-differential-falsifier", - primary_work: DeclarationRef { module_path: "v2.lens.module_graph", decl_name: "entry_affected_by_touched_paths", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject entry_affected_by_touched_paths describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "algebra-grounding-unification-design", - primary_work: DeclarationRef { module_path: "std.algebra", decl_name: "FreeMonoid", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject FreeMonoid describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "body-lowering-design", - primary_work: DeclarationRef { module_path: "std.termination", decl_name: "DescentEvidence", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.cardinality", decl_name: "node_multiplicity", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.std.cardinality", decl_name: "loop_bound_witness_for_node", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject DescentEvidence describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "build-cache-placement-receipt", - primary_work: DeclarationRef { module_path: "extdeps.cache.sccache", decl_name: "sccache_stats_query_does_not_spawn_note", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject sccache_stats_query_does_not_spawn_note describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "c-linkage-unit-realization-design", - primary_work: DeclarationRef { module_path: "v2.std.compilers.compilation_unit", decl_name: "CompilationUnit", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.compilers.compilation_unit", decl_name: "partition_fold_outcome", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.std.compilers.compilation_unit", decl_name: "CompilationUnitId", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject CompilationUnit describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "cli-run-reconcile-defork", @@ -416,55 +358,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject import_closure_live describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "compile-wall-endgame", - primary_work: DeclarationRef { module_path: "std.computation_identity", decl_name: "ComputationIdentity", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.termination", decl_name: "DescentEvidence", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject ComputationIdentity describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "compiler-algorithm-survey-2026-07-04", - primary_work: DeclarationRef { module_path: "v2.std.node", decl_name: "fold_node", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.algebra", decl_name: "list_snoc_item", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.std.compilers.lexing", decl_name: "token_stream_first", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject fold_node describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "containment-binding-cross-module-type-references-design", - primary_work: DeclarationRef { module_path: "std.occurrence_binding", decl_name: "OccurrenceBindingResult", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.symbol_index", decl_name: "symbol_index_lookup", field: WholeDeclaration }, - DeclarationRef { module_path: "std.occurrence_binding", decl_name: "occurrence_binding_from_candidates", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject OccurrenceBindingResult describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "cost-risk-benefit-floor-model", - primary_work: DeclarationRef { module_path: "std.realization_schedule", decl_name: "WitnessKind", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.verification", decl_name: "TestClassification", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.std.live_tree", decl_name: "LiveTreeDisposition", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject WitnessKind describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "cross-entry-typed-module-memo-sketch", - primary_work: DeclarationRef { module_path: "std.computation_identity", decl_name: "ComputationIdentity", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.materialization_ladder", decl_name: "CacheProvider", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.host_effect", decl_name: "HostEffect", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject ComputationIdentity describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "dag-scm-design", @@ -474,44 +367,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject GroupMembership describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "decl-emission-defork-design", - primary_work: DeclarationRef { module_path: "v2.std.compilers.target_model", decl_name: "TargetModel", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.compilers.semantic_decl_emission", decl_name: "TargetSemanticDeclEmission", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject TargetModel describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "declared-source-ref-selection-design", - primary_work: DeclarationRef { module_path: "v2.compiler.source_authority", decl_name: "SourceRef", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.compiler.source_authority", decl_name: "ModuleStorageIndex", field: WholeDeclaration }, - DeclarationRef { module_path: "std.content_hash", decl_name: "ContentHash", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject SourceRef describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "dependency-fidelity-design", - primary_work: DeclarationRef { module_path: "v2.std.fn_index", decl_name: "FnArrowDecl", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.lens.dependency_fidelity", decl_name: "FidelityVerdict", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject FnArrowDecl describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "design-register-library", - primary_work: DeclarationRef { module_path: "gunbc.design.material", decl_name: "Material", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.design.theme", decl_name: "Theme", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.design.material", decl_name: "AreaClass", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject Material describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "dispatch-maintain-cc", @@ -522,44 +377,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject dispatch_brief_template describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "duplicate-work-graph-lens-design", - primary_work: DeclarationRef { module_path: "v2.std.materialize", decl_name: "materialize", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.realization", decl_name: "Materialization", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.std.staging", decl_name: "cached_stage", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject materialize describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "emit-host-batch-isolation", - primary_work: DeclarationRef { module_path: "v2.workflow.executor", decl_name: "parallel_executor_plan_from_dependencies", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.realization_schedule", decl_name: "Runnable", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject parallel_executor_plan_from_dependencies describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "emitted-crate-partition-design", - primary_work: DeclarationRef { module_path: "v2.std.compilers.compilation_unit", decl_name: "CompilationUnit", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.compilers.compilation_unit", decl_name: "partition_fold_outcome", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.workflow.rust_crate_partition", decl_name: "PartitionPolicy", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject CompilationUnit describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "enforcement-intent-design", - primary_work: DeclarationRef { module_path: "v2.lens.enforcement.contract", decl_name: "LensContract", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.lens.enforcement.standing_intent", decl_name: "StandingIntent", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject LensContract describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "engram-materialization-placement", @@ -570,43 +387,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "gunbc.fabric.engram_materialization lands and the placement of this component is decided by a consumed select_realization fold whose result the serving subject reads, so its subject deepseek_v4_1_flash_engram is placed by shipped behavior rather than by a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "entry-graph-union-slice2-typecheck-attribution", - primary_work: DeclarationRef { module_path: "std.interface_summary", decl_name: "module_key", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject module_key describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "eval-bind-node-eval-propose", - primary_work: DeclarationRef { module_path: "v2.compiler.eval", decl_name: "eval_bind_node_eval", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.runtime", decl_name: "RuntimeBehaviorInterpreter", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject eval_bind_node_eval describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "execution-spine-design", - primary_work: DeclarationRef { module_path: "v2.std.materialize", decl_name: "materialize", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.dependency", decl_name: "DependencyView", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.std.spine", decl_name: "spine_receipt", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject materialize describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "extdeps-std-grounding-inventory", - primary_work: DeclarationRef { module_path: "std.measure", decl_name: "Measure", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject Measure describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "emit-type-surface-traversal-cost", @@ -634,23 +414,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the integration/floor-cut cutover merges to main and the old generated-ci / ci_floor_plan authorities named in this plan are deleted per its terminal receipt; the row deletes with the document once the cut is no longer the active migration vehicle. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "floor-memory-pool-parse-regression-diagnosis", - primary_work: DeclarationRef { module_path: "v2.lens.live_read_classification", decl_name: "live_read_classification", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject live_read_classification describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "floor-shared-compute-memoization", - primary_work: DeclarationRef { module_path: "gunbc.ci_layer_roots", decl_name: "witness_layer_roots", field: WholeDeclaration }, - additional_works: [ - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject witness_layer_roots describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "floor-time-namespace-walk-regression-diagnosis", @@ -658,52 +421,6 @@ data hand_authored_doc_bind_incomings: List = [ additional_works: [], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the assembly cost class this document attributes stops being where the floor wall is, measured on a main floor run rather than asserted; the row deletes with the document. REBOUND 2026-10-03: this row named v1.compiler.infer rewire_type_env_import_str_binding_identity, the 58 percent row this document located. That pass is DELETED (calm-pike-525: at a fresh compile 100 percent of its rewrites were the same Rc already present, whole tree and floor subject), so the row now names reconcile_with_census_extra, the reconcile assembly the diagnosis is about; the floor-run measurement the condition asks for decides retirement. REBOUND 2026-08-21: this row named v2.workflow.ci_floor_plan gate_runnable_profile and floor_plan_at_most_one_heavy_gate_resolve_per_batch — the batch-scheduling knobs the diagnosis turned, deleted with their module by the floor cut. The diagnosis is not about scheduling: its own section 7 locates the measured root in reconcile assembly, one row of which is 58 percent of the resolve wall, and that row is the subject bound here. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "for-sugar-over-fold", - primary_work: DeclarationRef { module_path: "v2.compiler.emit_orchestration", decl_name: "orch_emit_step", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.compilers.sugar", decl_name: "SugarKey", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject orch_emit_step describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "formal-concepts-extdeps-grounding", - primary_work: DeclarationRef { module_path: "std.termination", decl_name: "DescentEvidence", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject DescentEvidence describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "func-env-sigs-single-authority", - primary_work: DeclarationRef { module_path: "v2.compiler.resolve", decl_name: "ResolvedTree", field: WholeDeclaration }, - additional_works: [ - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject ResolvedTree describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "groupcompletion-pair-construction-design", - primary_work: DeclarationRef { module_path: "std.algebra", decl_name: "GroupCompletion", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.approximate_field", decl_name: "ApproximateField", field: WholeDeclaration }, - DeclarationRef { module_path: "std.rational", decl_name: "Rational", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject GroupCompletion describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "gunb-ai-site-subsumption-design", - primary_work: DeclarationRef { module_path: "extdeps.languages.typescript.program", decl_name: "TsProgram", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.design.material", decl_name: "Material", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.design.material", decl_name: "BuildRule", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject TsProgram describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "gunbc-served-dashboard-design", @@ -714,16 +431,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject belt_dispatch_node describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "hollow-alias-construction-wall", - primary_work: DeclarationRef { module_path: "std.algebra", decl_name: "FieldOfFractions", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.algebra", decl_name: "GroupCompletion", field: WholeDeclaration }, - DeclarationRef { module_path: "std.rational", decl_name: "Rational", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject FieldOfFractions describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "import-strip-witness-discovery-cascade-diagnosis", @@ -733,34 +440,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject DeclarationRef describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "ingest-manifest-source-ref-carrier-design", - primary_work: DeclarationRef { module_path: "v2.compiler.source_authority", decl_name: "SourceRef", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.compiler.source_authority", decl_name: "SourceRootIngest", field: WholeDeclaration }, - DeclarationRef { module_path: "std.content_hash", decl_name: "ContentHash", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject SourceRef describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "interface-summary-declared-use-arity", - primary_work: DeclarationRef { module_path: "std.interface_summary", decl_name: "InterfaceSummary", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.types", decl_name: "NonEmptyStr", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject InterfaceSummary describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "interpreter-memory-chronicle", - primary_work: DeclarationRef { module_path: "gunbc.fleet_intent", decl_name: "PerformanceReceipt", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject PerformanceReceipt describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "keying-relation-design", @@ -774,15 +453,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject KeyRelation describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "language-support-completion-design", - primary_work: DeclarationRef { module_path: "gunbc.ci_layer_roots", decl_name: "falsifier_substrate_long_lane_rows", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject falsifier_substrate_long_lane_rows describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "layering-imports-reference-repoint-design", @@ -793,34 +463,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject LayerImportFact describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "lens-consolidation-design", - primary_work: DeclarationRef { module_path: "std.materialization_ladder", decl_name: "LadderVerdict", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.lens.enforcement.standing_intent", decl_name: "StandingIntent", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.lens.vacuity", decl_name: "VacuityEvidence", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject LadderVerdict describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "lens-input-authority-design", - primary_work: DeclarationRef { module_path: "extdeps.cache.materialization", decl_name: "provider_from_catalog", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.compiler.inferred_tree", decl_name: "InferredTree", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject provider_from_catalog describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "mechanism-inventory-red-controls", - primary_work: DeclarationRef { module_path: "gunbc.ci_gate", decl_name: "Gate", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.process", decl_name: "ProcessExit", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject Gate describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "membership-diff-reconcile-spine-design", @@ -878,53 +520,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject occurrence_binding_from_candidates describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "native-selected-witness-bundle-design", - primary_work: DeclarationRef { module_path: "std.selected_witness_bundle", decl_name: "NativeWitnessBundleIdentity", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject NativeWitnessBundleIdentity describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "node-minimal-representation-sketch", - primary_work: DeclarationRef { module_path: "v2.std.node_minimal", decl_name: "MinimalResolvedNode", field: WholeDeclaration }, - additional_works: [ - - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject MinimalResolvedNode describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "node-subtree-visibility-grants", - primary_work: DeclarationRef { module_path: "std.effect_grant", decl_name: "position_under", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.effect_grant", decl_name: "NamespacePosition", field: WholeDeclaration }, - DeclarationRef { module_path: "std.effect_grant", decl_name: "admit_effect", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject position_under describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "non-fold-irreducible-residue-catalogue", - primary_work: DeclarationRef { module_path: "v2.lens.complexity_linearity_audit", decl_name: "triage_wildcard", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.induction", decl_name: "SubValueRelation", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.lens.cost", decl_name: "SymbolicCost", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject triage_wildcard describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "orchestration-as-intent-design", - primary_work: DeclarationRef { module_path: "v2.std.orchestration", decl_name: "Run", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.orchestration", decl_name: "Pipeline", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject Run describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "post-engine-pr-roadmap", @@ -934,42 +529,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject is_container_type describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "provisioning-window-executor-capability-design", - primary_work: DeclarationRef { module_path: "v2.std.execution_surface", decl_name: "ShellEmissionJustification", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.execution_surface", decl_name: "ExecutorCapability", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.std.execution_surface", decl_name: "ProvisioningWindow", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject ShellEmissionJustification describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "real-debt-syntactic-sample-audit", - primary_work: DeclarationRef { module_path: "v2.lens.complexity_linearity_audit", decl_name: "triage_wildcard", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.verification", decl_name: "ManualAnchorKey", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.std.compilers.target_model", decl_name: "ConcreteSyntaxToken", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject triage_wildcard describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "resolve-regression-journey", - primary_work: DeclarationRef { module_path: "gunbc.ci_layer_roots", decl_name: "bin_witness_wet_per_row_wall_budget_seconds", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the resolve-regression class this document diagnoses stops recurring and the wet per-row wall budget it now binds to describes settled behavior rather than a live pressure; the row deletes with the document. REBOUND 2026-08-21: the original subject was gunbc.ci_workflow gunbc_ci_floor_step_timeout_discovery_flip_note, which the floor cut deleted along with its module — the citation outlived its subject and the cited-symbol lens refused it. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "resolver-graph-major-design", - primary_work: DeclarationRef { module_path: "v2.std.dependency", decl_name: "DependencyView", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.realization_schedule", decl_name: "RealizationPlan", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject DependencyView describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "resource-aware-scheduler", @@ -1020,14 +579,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject SeedHonestyDischarge describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "seed-shrink-census", - primary_work: DeclarationRef { module_path: "v2.std.node", decl_name: "content_hash", field: WholeDeclaration }, - additional_works: [ - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject content_hash describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "shell-intent-emit-realization-design", @@ -1048,16 +599,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject host_effect_apply describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "space-complexity-design", - primary_work: DeclarationRef { module_path: "v2.lens.cost.expr", decl_name: "CostExpr", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.termination", decl_name: "DescentEvidence", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.lens.cost.summary", decl_name: "ComplexitySummary", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject CostExpr describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "space-lens-minimal-project", @@ -1068,33 +609,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject ResourceEnvelope describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "srv3-webui-kvm-virtual-media", - primary_work: DeclarationRef { module_path: "gunbc.boot_artifact_delivery", decl_name: "solve_boot_artifact_delivery", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "extdeps.external_authority", decl_name: "ExternalAuthority", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject solve_boot_artifact_delivery describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "srv4-bmc-onboarding-gap", - primary_work: DeclarationRef { module_path: "gunbc.host_standup", decl_name: "host_standup_spine", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "product.host_identity", decl_name: "HostIdentity", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject host_standup_spine describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "srvn-buildcache-provisioning-design", - primary_work: DeclarationRef { module_path: "gunbc.host_converge", decl_name: "ConvergeTarget", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "extdeps.cache.sccache", decl_name: "sccache_local_facts", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject ConvergeTarget describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "type-env-single-authority-design", @@ -1114,72 +628,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject whole_tree_strict_resolve_exclusion_substrings describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "v2-memory-control-audit", - primary_work: DeclarationRef { module_path: "std.materialization_ladder", decl_name: "CacheProvider", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.std.dependency", decl_name: "DependencyView", field: WholeDeclaration }, - DeclarationRef { module_path: "std.realization_schedule", decl_name: "CostAccount", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject CacheProvider describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "vacuity-lens-design", - primary_work: DeclarationRef { module_path: "v2.lens.vacuity", decl_name: "VacuityEvidence", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.lens.coverage", decl_name: "coverage_defect_vacuous_arm", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.std.verification", decl_name: "TestClaim", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject VacuityEvidence describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "witness-subject-execution-audit", - primary_work: DeclarationRef { module_path: "v2.std.diagnostic", decl_name: "Outcome", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.lens.module_graph", decl_name: "import_closure_live", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the work this document plans lands and its subject Outcome describes shipped behavior rather than a planned change; the row deletes with the document. Registered at subject grain: the systems bound here are the ones whose movement would make this document's purpose false, not every system its prose mentions.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "general-body-producer-design", - primary_work: body_lowering_symbol_index_ref, - additional_works: [ - body_lowering_lexical_lookup_ref, - body_lowering_production_consumer_ref, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the general body producer lands and the document describes a shipped fold rather than a planned one. THIS BIND EXISTS BECAUSE OF A MEASURED FAILURE IN THIS EXACT DOCUMENT: it named v2.std.symbol_index SymbolIndex as an open prerequisite gating Stage A, the prerequisite was discharged, and the sentence stayed -- four copies of that one claim were live across DESIGN.md and two plan documents. Nothing was wrong with the declaration, so no declaration-level check could fire; the sentence was not a reference to anything, so no reference-level check could either. Binding the three systems it discusses puts them inside the cited-symbol population, so a rename or deletion reds without anyone editing the prose. WHAT THIS DOES NOT ESTABLISH, stated so the bind is not over-read: a resolved reference proves the system EXISTS and is uniquely identified, not that the named consumer invokes the named implementation on a production path -- that is a relationship claim, it needs an executing witness, and this row makes none.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "ci0-witness-execution-census", - primary_work: DeclarationRef { module_path: "gunbc.witness_execution_class", decl_name: "witness_execution_class_note", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.witness_execution_class", decl_name: "native_blocked_cause_note", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the general witness→TargetModel producer lands (body-lowering keystone) and the enrolled-roster census is re-derived from executed emission attempts rather than the fixture-bound dispatch surface; the census numbers are superseded by that re-derivation and this receipt deletes with the NativeBlocked\{GeneralWitnessTargetModelUnavailable\} population it counts.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "ci2-native-cutover-reobservation", - primary_work: DeclarationRef { module_path: "gunbc.ci_materialization", decl_name: "ci_native_cache_root_exempt_note", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.native_witness_transition_receipt", decl_name: "native_at_small_scale_transition_receipt", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the CI2 correctness flip lands — the native-selected population is native-required with the counted interpreter fallback deleted — and a main floor run's [native-selected-bundle] receipt shows verdict accepted with fallback = interpreted = unavailable = 0. The measured-standing content is superseded at that point by the run receipts and the transition-receipt authority; this doc records the pre-flip standing and its two located defects, and deletes with the fallback machinery it diagnosed.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "witness-bridge-reobservation", - primary_work: DeclarationRef { module_path: "v2.compiler.body_producer_forward", decl_name: "body_producer_forward_note", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.compiler.body_lowering_fold", decl_name: "body_lowering_fold_note", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the first witness-bridge stage lands (lowered body -> derived TargetModel -> bundle member -> native execution with planted-red control) and the bridge carrier's own notes record the standing this receipt captured; then this doc folds into the bridge design/receipt and deletes.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "review-surface-subject-map", @@ -1191,32 +639,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "every row in the map has been deleted, consumed by a named live edge, or transferred to a typed roadmap obligation with an owner and acceptance. The four bound works are the map's load-bearing subjects and each names its own end state: the completeness roster note's exemption for v2.lens.grounding_ledger cites consumption by v2.lens.grounding that does not exist, and dissolves when the ledger is deleted so the row disappears rather than being reworded; LedgerEntry is that inert module's carrier; first_coincident_target folds to the FIRST non-self match while GroundingWorklistEntry stores a single coincides_with string, which is the silent-pick defect blocking any exclusive-candidate review and dissolves when the candidate population becomes complete, zero/one/many explicit, and identified by DeclarationRef rather than presentation strings; MergeReadinessTally has no producer outside fixtures and a dormant consumer whose kernel has no in-tree driver, and its unread approval_count field dissolves when the two-approval rule becomes a typed obligation with a zero-approval RED. The map is a worksheet, not an authority: it registers here so it cannot sit unreachable, and it deletes outright rather than migrating to DESIGN.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "witness-evidence-lifecycle-design", - primary_work: DeclarationRef { module_path: "v2.workflow.floor_discovery_producer", decl_name: "floor_discovery_process_dag_file", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.ci_layer_roots", decl_name: "long_lane_exclusion_note", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.explicit_witness_admission", decl_name: "known_red_class_note", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "steps 3-5 land or are honestly retired (steps 1-2 are done: the modeling decision was signed by the operator 2026-08-04 and the typed direct-rust-door observation executes green). Remaining: census-before-routing in floor_discovery_process_dag_file, the attempt/evidence algebra retiring QuarantineProbeExpectRed and batch-wide expect_red inversion, and route derived from execution requirement — with test/claim/long DELETED as the terminal state per the operator step-5 ruling (it may persist as migration scaffolding meanwhile, but no new semantic dependence may be added to it, and operator visibility comes from a generated long-route projection rather than the directory name). Then the surviving policy rows migrate to DESIGN or a registered gunbc.plan.Plan row and this bind deletes with the doc.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "production-stage-origin-carrier-design", - primary_work: DeclarationRef { module_path: "v2.compiler.self_host.candidate_generation", decl_name: "mint_provenanced_rust_artifact", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.compiler.self_host.fixture_synthetic_emission", decl_name: "fixture_synthetic_emission_note", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "B1-B4 land or are honestly retired: covered-form construction admission, call-occurrence population closure consuming the #7786 successor, production API migration removing raw stage outputs from qualification boundaries, and bypass closure for casts / module identity / fail-open lookup. The ProductionOriginClosureCertificate becomes constructible - constructor and consumer populations closed, fixture reachability zero, unknown-origin zero - at which point the carrier law lives in the substrate and this note deletes. If the census successor proves the class cannot close under the current sole_constructor mechanism, this note is superseded by the recut rather than deleted silently.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "candidate-scoped-gates-atomic-landing", - primary_work: DeclarationRef { module_path: "gunbc.extdeps_scope_frontier", decl_name: "legacy_manifest_freeze_sha", field: WholeDeclaration }, - additional_works: [DeclarationRef { module_path: "gunbc.ci_failure_class", decl_name: "merge_freshness_gating_status", field: WholeDeclaration }, DeclarationRef { module_path: "tools.floor_effect_gate_witness", decl_name: "emit_host_gate_passes", field: WholeDeclaration }, DeclarationRef { module_path: "gunbc.merge_admission", decl_name: "merge_admission_required_check_binding_dissolution_trigger", field: WholeDeclaration }], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the four program walls are live: the merge-queue landing boundary enforced (MergeQueueOnly + ALLGREEN, dashboard enqueue-only), the candidate/freeze/manifest observations split with the false law text corrected and the DefaultBranchAudit backstop scheduled, the RunnableProcessExitClaim migration complete with the Boolean gate adapters deleted, and the repository ruleset modeled as desired state with read-back. Then the surviving policy rows migrate to DESIGN or a registered gunbc.plan.Plan row and this bind deletes with the doc.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "generated-file-conflict-policy", @@ -1224,20 +646,6 @@ data hand_authored_doc_bind_incomings: List = [ additional_works: [DeclarationRef { module_path: "tools.generated_artifact_gate", decl_name: "run_generated_artifact_drift_gate", field: WholeDeclaration }, DeclarationRef { module_path: "gunbc.commit_workflow", decl_name: "commit_gate_roster", field: WholeDeclaration }, DeclarationRef { module_path: "gunbc.stage0_rust_source_lifecycle_scaffold", decl_name: "derived_generated_stage0_repo_paths", field: WholeDeclaration }, DeclarationRef { module_path: "std.keyed_roster", decl_name: "keyed_roster_build", field: WholeDeclaration }], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "all four charter lanes are landed AND bound: commit-writer admission consumed at every writer transport (the heal arm and the dashboard autocommit daemon), the .gitattributes + repo-local git-config projections live with read-back, stage0 membership derived with its host-supply scaffold dissolved or re-triggered, and the keyed-roster construction wall enrolled per-PR. Then the surviving policy rows migrate to DESIGN or a registered gunbc.plan.Plan row and this bind deletes with the doc.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "seamless-deploys-design", - primary_work: DeclarationRef { module_path: "gunbc.live_deploy.service_ready", decl_name: "live_deploy_service_ready_poll_bound_reason", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "all four items the note models are landed or retired: the observation-outcome de-conflation, the systemd F1 readiness assertion, the deploy reconcile (comparable artifact value, service de-fused from the unit file, observed provider), and the handover. Registers as a gunbc.plan.Plan row if the design freezes before they land; deletes outright if they land first. The reasoning and receipts live in the note, not here.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "inner-cost-lanes-scoping", - primary_work: DeclarationRef { module_path: "gunbc.roadmap_authority", decl_name: "roadmap_authority", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "both lanes have landed or been retired: Lane A when process_escapes_loop is migrated onto source_chars with its discriminating non-ASCII receipt, Lane B when the shared-overlap measurement it asks for exists and either opens the program or prices it down. The note is scoping for two roadmap nodes, so it dissolves when those nodes close, not into a gunbc.plan.Plan row.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "five-minute-ci-gate-design", @@ -1251,47 +659,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "every sub-lane row is accepted or honestly retired and the program parent five-minute-ci-gate is accepted on fleet receipts showing a representative leaf .dag PR at ≤5 minutes wall with byte-identical verdicts vs a cold recompute control — or the operator recuts the program and this note is superseded by a registered plan row.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "spark-standup-program-accounting", - primary_work: DeclarationRef { module_path: "gunbc.plans.fleet_subsumption_manual_gaps", decl_name: "fleet_subsumption_manual_gaps_plan", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.spark.dgx_procurement", decl_name: "dgx_spark_arrival_standing", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.spark.dgx_procurement", decl_name: "dgx_spark_router_bindings", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the Spark standup lanes are carried as registered rows rather than by this note — the manual-gap items reach zero and srv5/srv6 are enrolled with their arrival obligations modeled where that arrival lives, at which point the accounting has no facts of its own and this bind deletes with the doc.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "floor-prep-tax-program", - primary_work: DeclarationRef { module_path: "gunbc.executor_schedule_retention", decl_name: "schedule_retention_policy_note", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.roadmap_authority", decl_name: "five_minute_ci_gate_program_note", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.roadmap_authority", decl_name: "roadmap_authority", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.p1_retention_cohort_receipt", decl_name: "p1_retention_cohort_receipt_verdict", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.ci_cost_arc_closeout_receipt", decl_name: "end_to_end_residual", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.ci_cost_arc_closeout_receipt", decl_name: "retention_settle", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "floor receipts show amortized prep (entries 2..N ≪ ~2s setup), selection receipts expose state/ratio/fallback on every affected run, and width>1 / broad native decisions cite those receipts (and the index-share conjunction) — or the operator folds this bridge into the five-minute / materialization authorities and this bind deletes with the doc.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "worker-private-memory-decomposition", - primary_work: DeclarationRef { module_path: "gunbc.executor_schedule_retention", decl_name: "schedule_retention_heads_stay_resident", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.executor_schedule_retention", decl_name: "schedule_retention_policy_note", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.p1_retention_cohort_receipt", decl_name: "p1_retention_cohort_receipt_verdict", field: WholeDeclaration }, - DeclarationRef { module_path: "gunbc.roadmap_authority", decl_name: "five_minute_ci_gate_program_note", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the width-2 crossover decision is taken and cites these terms or a superseding measurement, or the ResolvedGraph-anchored bundle this receipt names becomes shared — at which point the decomposition no longer describes a worker and this bind deletes with the doc.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "accelerator-demo-roundtrip", - primary_work: DeclarationRef { module_path: "extdeps.languages.simd.kernel", decl_name: "simd_kernel_medium", field: WholeDeclaration }, - additional_works: [DeclarationRef { module_path: "gunbc.accelerator_demo_plan", decl_name: "AcceleratorRecognitionOutcome", field: WholeDeclaration }], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "doc migrates into a registered gunbc.plan.Plan row (the registration becomes the binding); §6 item 2 execution lane lands on the simd handler (merged from a two-row form 2026-07-31: home+slug is this list's symbolic identity, so two rows gave one doc two independently removable authority rows)") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "machine-shape-orthogonal-scheduling", @@ -1313,27 +680,6 @@ data hand_authored_doc_bind_incomings: List = [ additional_works: [DeclarationRef { module_path: "v2.compiler.self_host", decl_name: "canonical_emitted_bytes_digest", field: WholeDeclaration }, DeclarationRef { module_path: "gunbc.bootstrap", decl_name: "CompilerStage", field: WholeDeclaration }], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "Stages 1–9 in the design doc land or are honestly retired: candidate/promoted lifecycle with RecoveryAnchor retention, SuccessorCapabilityReceipt fix-forward gate, declared-change vs regression on the merge path, generation lineage binds ConsumerRead on regen and warm-merge (Promoted bindings only), bridge-generation protocol for incompatible requirement revisions, phased-single-process CI prelude-duplication witness is green on a representative PR, and the doc migrates into a registered gunbc.plan.Plan row (the registration becomes the binding)") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "fleet-self-converge-enforcement-design", - primary_work: DeclarationRef { module_path: "gunbc.plans.host_effect_orchestration", decl_name: "host_effect_orchestration_plan", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "companion detail for the host-effect plan's Phase E (pull-mode self-converge); dissolves when Phase E's enforcement topology lands in carriers or the doc registers as a gunbc.plan.Plan row") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "holds-base-runtime-error-investigation-receipts", - primary_work: DeclarationRef { module_path: "v2.std.witness", decl_name: "Witness", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the deferred verify-7916-holds-base receipt (CI floor with selection-applied on the reproducing tree, plus cargo test -p v1-compiler --lib) runs against a healthy fleet and its outcome is folded into #7916's history, or the doc is otherwise superseded; either way this bind and the doc delete together") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "e0599-implementation-proposal", - primary_work: DeclarationRef { module_path: "tools.e0599_probe_census", decl_name: "e0599_probe_census_note", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "Phase 0 consumer graph accepted and each named predicate (P-fn, P-derive, P-optional) lands with measured receipt or the doc migrates into a registered gunbc.plan.Plan row") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "rc-ownership-wrap-decision-design", @@ -1347,45 +693,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "doc migrates into a registered gunbc.plan.Plan row (the registration becomes the binding)") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "arc-store-path-migration-decision", - primary_work: DeclarationRef { module_path: "v1.compiler.languages", decl_name: "sharing_wrap_ctor_for_target", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.compiler.wrap_decision", decl_name: "WrapDecisionBundleChildLookup", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the v1-run-stability width>1 resumption trigger in this note fires and the parked alias-flip plus C1 in-memory store land in one motion — or the decision record migrates into a registered gunbc.plan.Plan row") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "module-identity-storage-binding-design", - primary_work: DeclarationRef { module_path: "v2.lens.effect_reach", decl_name: "effect_reach_law", field: WholeDeclaration }, - additional_works: [DeclarationRef { module_path: "v2.workflow.witness_admission", decl_name: "consumer_for_explicit_rosters", field: WholeDeclaration }], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "BOTH conditions gate dissolution (merged from a two-row form 2026-07-31, same identity rule as the guarantee-recovery row): typed SourceRef at host boundaries is ENFORCED and the bare-string file-dependency class is migrated (effect_reach_law's own dissolve condition), and the doc migrates into a registered gunbc.plan.Plan row (the registration becomes the binding)") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "live-read-witness-classification-design", - primary_work: DeclarationRef { module_path: "v2.lens.live_read_classification", decl_name: "live_read_classification_law", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "doc migrates into a registered gunbc.plan.Plan row (the registration becomes the binding)") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "witness-realization-plan", - primary_work: DeclarationRef { module_path: "std.emit_on_demand", decl_name: "witness_family_build_grain_ruling", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "std.realize_pack", decl_name: "realize_pack_width", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "dissolves into a registered gunbc.plan.Plan row when its P1 lands. REBOUND 2026-08-21: this row named v2.workflow.ci_floor_plan gunbc_ci_floor_plan — the floor plan that would have SCHEDULED the realized witnesses, deleted with its module by the floor cut. The plan itself is live (six documents cite it as their parent authority), and its landed subjects are the two carriers named here: the family build-grain ruling that answers its FLAG B, and the packing law its P4 derives. Scheduling re-enters as a consumer when a floor plan exists again; it was never the subject of this document.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "ci-floor-time-45-72-band-attribution", - primary_work: DeclarationRef { module_path: "gunbc.ci_materialization", decl_name: "ci_floor_declared_resolve_count", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "dissolves when realization_measurement_loop Phase-0 Gantt carrier supersedes prose receipts (same trigger as ci-floor-fractal-gantt)") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "ci-floor-child-spawn-attribution", @@ -1393,13 +700,6 @@ data hand_authored_doc_bind_incomings: List = [ additional_works: [], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "dissolves when the child-spawn class is dissolved — run_gunbc_claims_ready pools ClaimRuns into one process or runs them against the executor's warm process_shared_index (the ci_floor_resolve_receipt_note 'resolve once, share by reference' terminal); this receipt's counterfactuals become that PR's before/after") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "progress-observation-design", - primary_work: DeclarationRef { module_path: "std.observation", decl_name: "ObservationEvent", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "P0 lands the observation laws + glyph authority as .dag data rows (roadmap 2d ts-obs-model) — the carriers become the authority and this doc stays as the design record. REBOUND 2026-08-21: this row named v2.workflow.ci_floor_plan gunbc_ci_floor_plan, the floor plan the doc measured its displaced cost against, which the floor cut deleted with its module; the doc is a live roadmap deliverable authority, so the repair is to bind it to the subject it actually designs — std.observation's event model — rather than to a consumer that no longer exists") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "roadmap-workspace-ux-plan", @@ -1424,52 +724,6 @@ data hand_authored_doc_bind_incomings: List = [ ], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the RLM-6 terminal receipt is recorded -- one receipt joining every frozen identity of the serial gate chain (the canary parent Done by explicit acceptance, its child Ready, the parent's relaunch refused AlreadyAccepted); at that point the plan describes shipped behavior and the row deletes with the document") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "recursive-workflow-advancement-design", - primary_work: DeclarationRef { module_path: "gunbc.workflow_reconcile", decl_name: "workflow_recursive_reconcile_note", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the advancement plan, exact-candidate receipt producers through goal audit, and recursive child-workflow realization land as typed carriers and register as a gunbc.plan.Plan row; then this bind deletes while the md remains the design record") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "ci-two-tier-placement-redesign", - primary_work: DeclarationRef { module_path: "gunbc.ci_spec", decl_name: "gunbc_ci_spec", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "D3 lands the placement axis (PrTier | Gauntlet) on CiSpec rows with the measured 5s-rule roster — the spec rows become the authority; the D4 audit-step deletion and D0/D1 preconditions are tracked on their carriers") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "test-decomposition-wcf-cut", - primary_work: DeclarationRef { module_path: "gunbc.witness_row_cost", decl_name: "gunbc_ci_fast_lane_rule_note", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "every Class C row on the measured head lands fixture/cadence split (or reclassifies F with a named cost-shape owner); dark long/ files classify only at Gauntlet enrollment — then authority collapses into CiSpec placement rows + enrolled cadence rosters and this doc deletes") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "fallback-arm-census", - primary_work: DeclarationRef { module_path: "v2.lens.fallback_arm_census", decl_name: "fallback_arm_census_law", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "W3 promotes the lens to a compile refusal (zero-false-positive corpus-wide) and/or the doc migrates into a registered gunbc.plan.Plan row — then this bind deletes with the md") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "dag-note-prose-census", - primary_work: DeclarationRef { module_path: "v2.lens.enforcement.standing_intent", decl_name: "StandingIntent", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the typed annotation carriers land (ruling rows on StandingIntent, an event log keyed to the declaration, citation edges) and the annotation-budget lens counts ROWS — at which point a lexical census over prose is superseded by a fold over typed rows, and the doc plus its instrument delete together") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "dag-prose-policy-audit", - primary_work: DeclarationRef { module_path: "v2.std.compilers.lexing", decl_name: "LexRule", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.compiler.parse", decl_name: "ParseArtifact", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.extdeps.languages.dag", decl_name: "dag_line_comment_fidelity", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.test.round_trip.dag_comment_wall_test", decl_name: "dag_comment_wall_line_comment_refused", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the source-annotation carrier lands with its executed controls — line comments captured on a THIRD lexical channel (AnnotationRule, never TriviaRule and never an ordinary TokenRule) returning an authored wrapper around an unchanged semantic artifact, consuming no semantic occurrence identity, attached at module-item grain with trailing/body/unattached/block forms refusing, and erasure established by the seven D-C proofs rather than by emitted-byte equality alone (which cannot detect a moved occurrence-identity graph) — and the representation partition over the measured prose population completes; at which point a lexical audit over prose is superseded by a fold over the annotation graph and the typed populations beside it, and this doc plus its uncommitted instrument delete together (same trigger family as dag-note-prose-census)") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "dissolution-census-a-ci-layer-roots", @@ -1477,41 +731,6 @@ data hand_authored_doc_bind_incomings: List = [ additional_works: [DeclarationRef { module_path: "v2.lens.enforcement.standing_intent", decl_name: "StandingIntent", field: WholeDeclaration }], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the typed annotation carriers land (StandingIntent, event log, citation edges) and an annotation-budget lens counts rows — at which point a lexical census over ci_layer_roots prose is superseded by a fold over typed rows, and this doc plus its instrument delete together (same trigger as dag-note-prose-census)") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "extdeps-positioning-restructure", - primary_work: DeclarationRef { module_path: "v2.lens.extdeps_shape_transport_policy", decl_name: "extdeps_shape_transport_policy_clean_holds", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the extdeps positioning restructure PR (pure git mv + import repoint: os/ de-grab-bag + findings 1-4 hub-loose, all under the §3 extdeps discipline this lens enforces) lands and the extdeps directories are §3-congruent — the brief's scope is discharged; delete this row and the doc together") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "v1rt-witness-diagnostic-carrier-decouple-design", - primary_work: DeclarationRef { module_path: "v2.std.witness", decl_name: "witness_from_optional", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "Stage-4 Gate-1 E0308 burn-down probe receipt lands (design note §3 — measurement only); doc archives or registers as a gunbc.plan.Plan row") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "hermetic-tool-provisioning-design", - primary_work: DeclarationRef { module_path: "extdeps.tools", decl_name: "CliTool", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "P1/P2 land the digest-bearing pin and the tool instantiation of gunbc.membership_reconcile — the host-program ensure over the CliTool catalog row lands (program-install lane, successor to the deleted extdeps.tools.resolve) and the four forked resolvers delete; the pin carrier becomes the authority and the plan registers as a gunbc.plan.Plan row") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "ci-invocation-fixed-tax-attribution", - primary_work: DeclarationRef { module_path: "v1.compiler.tokenize", decl_name: "process_escapes_loop", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "REBOUND 2026-08-21: this row named gunbc.ci_workflow gunbc_ci_selection_control_step_note, the demotion rationale this receipt supplied the basis for; the floor cut deleted gunbc.ci_workflow whole and the citation outlived it. The measurement itself is about the invocation fixed tax, whose located root this note names as its lever 1 — the unmigrated frontend construction in v1.compiler.tokenize process_escapes_loop — so the doc is rebound to the system whose movement would make its purpose false. Dissolves when the pre-evaluation whole-corpus passes are persisted across processes or derived from the containment tree (namespace-only resolution lane) — its levers then re-price against the new baseline, and the demotion's outcome-based return trigger (selection control back per-PR once a named receipt shows the whole control step consistently below a seconds-scale ceiling on the CI fleet across a full falsifier window) consumes its measurements") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "srv1-deploy-serialization-and-false-greens", - primary_work: DeclarationRef { module_path: "gunbc.ci_spec", decl_name: "gunbc_ci_deploy_srv1_stage", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "all FOUR findings this diagnosis names are facts on their carriers: (1) srv1 deployment holds a typed HOST-SIDE lease spanning mutation, readiness and identity readback — a runner label or concurrency group is mitigation only, since neither excludes the operator apply path — plus a counted refusal when the candidate is not the authoritative revision under whichever revision authority is ruled, (2) the healthz fingerprint identifies what is DEPLOYED (source closure and binary artifact identity, not build provenance alone) rather than five rendered bodies, (3) the refusal path preserves the decl's located diagnosis instead of substituting a render complaint, with control-byte admission decided per CursorAction and DynamicLineState rather than one global roster, and (4) job_yaml emits Job.concurrency with a discriminating RED. Then the doc deletes with this row. Twice corrected in review: review 44735 caught that the trigger omitted the misattributed-refusal defect entirely, and review 44766 caught that it would have accepted scheduler-only exclusion, source-only identity and a global byte-roster fix") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "compiler-guarantee-recovery-gap-analysis", @@ -1526,51 +745,6 @@ data hand_authored_doc_bind_incomings: List = [ additional_works: [DeclarationRef { module_path: "gunbc.roadmap_component", decl_name: "roadmap_row_archetype_note", field: WholeDeclaration }], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the A2 presentation seam lands on its carriers — the view projection module (WorkRowView/ActivityView/ActionView/DailyWorkspaceView/ObservationImpactView), the row archetype's ActivityRegion placed UnderHead with ActuatorRegion shrunk to actions only, and the attention-rule witnesses including the falsifier specimen (active attempt + seven obligations + session-present/process-exited + long title, coherent at three widths) — and the plan registers as a gunbc.plan.Plan row (extends roadmap-workspace-ux-plan and roadmap-workspace-remodel-plan, whose rows dissolve on their own triggers)") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "p1-retention-vs-drain-cohort-receipt", - primary_work: DeclarationRef { module_path: "gunbc.p1_retention_cohort_receipt", decl_name: "p1_retention_cohort_receipt_reproduction_steps", field: WholeDeclaration }, - additional_works: [DeclarationRef { module_path: "gunbc.p1_retention_cohort_receipt", decl_name: "p1_retention_cohort_receipt_verdict", field: WholeDeclaration }], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the redirected assembly/materialization lane this receipt's REJECT verdict points to is picked up and either supersedes this pinned measurement with a canonical carrier or the parent floor-prep-tax-program plan closes P1 outright — then this receipt and its anchor module delete together, per the same scaffold trigger named beside p1_cohort_receipt_enabled in cli_run.rs.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "p3-classification-cost-ab-receipt", - primary_work: DeclarationRef { module_path: "v2.lens.live_read_classification", decl_name: "live_read_classification_law", field: WholeDeclaration }, - additional_works: [], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "paired fleet receipts for ClassificationCostStanding land on the pinned merge SHA with headSha-verified control/candidate arms and the five-valued verdict is recorded — then surviving measurement facts migrate to the canonical carrier and this bind deletes with the doc.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "push-event-affected-set-baseline-design", - primary_work: DeclarationRef { module_path: "gunbc.diff_baseline", decl_name: "resolve_diff_baseline", field: WholeDeclaration }, - additional_works: [DeclarationRef { module_path: "v2.workflow.floor_diff_observe", decl_name: "floor_observe_ci_diff_event", field: WholeDeclaration }, DeclarationRef { module_path: "gunbc.diff_baseline", decl_name: "diff_baseline_fail_closed_law", field: WholeDeclaration }], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the before..head push baseline lands AND the merge-queue candidate gate is enforcing (gunbc.ci_failure_class merge_freshness_gating_status = GatingEnforced) — at which point the exact tree becoming main is verified before it becomes main and this note describes the past. CORRECTED 2026-08-03 (operator ruling): this trigger previously read \"the last-green baseline lands\". Last-green was REJECTED for ordinary selection, so that condition could never be met and this row would have been permanently undissolvable — an unfireable trigger is indistinguishable from an unfired one, so no gate would ever have flagged it.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "provider-control-interface-audit", - primary_work: DeclarationRef { module_path: "gunbc.dispatch_selection", decl_name: "DispatchResolvedExecution", field: WholeDeclaration }, - additional_works: [DeclarationRef { module_path: "extdeps.llm.cli_lifecycle", decl_name: "ProviderReadiness", field: WholeDeclaration }, DeclarationRef { module_path: "gunbc.roadmap_provider_events", decl_name: "provider_execution_state", field: WholeDeclaration }], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the audit's locked decisions are facts on their carriers, not prose. PROVIDER INTERFACE HALF: DispatchResolvedExecution carries ONE nested ProviderInterfaceBinding whose runtime is a CLOSURE identity (package closure + runtime toolchain + adapter artifact + protocol identity — not a single artifact digest), provider identity is split from control interface and both from transport, ProviderReadiness is decomposed into independent installation/auth/entitlement/limit/turn standings with an open native limit token, and provider_execution_state reads typed app-server terminals instead of the exec --json category-discarding arm. PACKAGE DELIVERY HALF (added 2026-08-03 per operator ruling — the audit's §2 gap is a package-management gap, not a provider problem, so closing only the provider carriers would leave the note's load-bearing half undelivered): the generic package-delivery authority exists and is CONSUMED IN PRODUCTION — requirement vs exact selection, lock graph with artifact integrity admitted before unpack, materialization receipt with install scripts forbidden unless explicitly admitted, executable-vs-module consumer binding, runtime-bundle identity, ReleaseBundled posture with no acquisition on the deployed host, and an explicit reviewed lock-refresh path — with the Codex realization delivered through it and srv1's exec/tmux offer removed. The §4 paired Claude entitlement probe must also have RUN, deciding whether ClaudeDirectStreamJson survives as an interface or dissolves; an undecided probe leaves this row live because the audit's retraction of the entitlement claim is what makes the row honest. Then the note becomes the design record and this bind deletes with it.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "sole-modeled-publisher-design", - primary_work: DeclarationRef { module_path: "tools.publication_publisher", decl_name: "PublicationAuthority", field: WholeDeclaration }, - additional_works: [DeclarationRef { module_path: "tools.publication_push_shadow", decl_name: "shadow_replay_public_pushes", field: WholeDeclaration }], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "live slice lands: ordinary sessions lose public-write credentials, git.PublicationTransport dispatch binds to PublicationAuthority, and shadow witness becomes regression control — then register as gunbc.plan.Plan row or delete with carriers") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "c1-cost-expr-authority-design", - primary_work: DeclarationRef { module_path: "v2.lens.cost", decl_name: "cost_lens", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.plans.v2_complexity_capability_parity", decl_name: "v2_complexity_capability_parity_plan", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.lens.enforcement.complexity_contract_subject", decl_name: "complexity_derived_kernel_boundary", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "v2.lens.cost carries grounded SizeExpr/CostExpr with normalize_cost_expr_to_symbolic live, the C1 blocking-wall baseline roster is green post-carrier, bounded-summation and CostRefused witnesses execute green, and parity note C1 acceptance is met — then the carriers state the capability and this bind deletes with the doc.") }, - }, HandAuthoredDocBind { home: ProbeDoc, slug: "leading_minus_continuation_silently_truncates_2026-08-23", @@ -1578,25 +752,6 @@ data hand_authored_doc_bind_incomings: List = [ additional_works: [], dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the reduced parser cases this document carries are held by an EXECUTING REGRESSION CARRIER -- a witness that authors `100 \\n - 1` and its `+` and `*` controls and refuses when the leading-minus continuation is truncated again -- the production citation in extdeps.cpu_attachment.lotes_azifa072 is repointed to that carrier, and no other evidence consumer names this document. Then the receipt is the carrier and this bind deletes with the md. IT IS DELIBERATELY NOT PlanHasNoRetirement: the evidence being load-bearing TODAY is not grounds for making a Markdown file the permanent terminal form of a parser guarantee, which is exactly the shape the three g1 controls were wrongly given before they moved to v2.lens.cited_symbol_resolution.") }, }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "v2-frontend-std-ingestion-frontier", - primary_work: DeclarationRef { module_path: "v2.compiler.normalize", decl_name: "normalize", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "v2.compiler.parse", decl_name: "parse_module", field: WholeDeclaration }, - DeclarationRef { module_path: "v2.compiler.tokenize", decl_name: "lex_walk_artifact", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the measured frontier closes: the v2 frontend ingests the std substrate modules this receipt records as rejecting, at each of the three stages it attributes them to — normalize's retention-then-rejection contract violation first, then the lex and parse gaps — and the per-module stage-and-reason table describes shipped behaviour rather than a standing frontier, at which point the receipt and this bind delete together. Registered at subject grain: the bound declarations are the three frontend stages whose movement would make the table false, not every module its rows name.") }, - }, - HandAuthoredDocBind { - home: PlanDoc, - slug: "four-lane-closeout-and-root", - primary_work: DeclarationRef { module_path: "gunbc.roadmap_authority", decl_name: "five_minute_ci_gate_program_note", field: WholeDeclaration }, - additional_works: [ - DeclarationRef { module_path: "gunbc.witness_floor_workflow", decl_name: "witness_floor_concurrency_group", field: WholeDeclaration }, - ], - dissolution: PlanRetiresWhen { condition: unbound_dissolution(description: "the six findings this note records outlive their archived lanes only until each has a carrier of its own: the rejected_with_pending diagnostic-order defect, the entry-vs-member position divergence, the three-entrypoint admission divergence, the standing section 4c population that makes a full-corpus compile exit 1, the floor-gated deploy, and the concurrency-group key -- that last one now carried by gunbc.witness_floor_workflow witness_floor_concurrency_group, which keys merge_group by sha, heal dispatch by expected_healed_sha, pull_request by number, push by github.ref, and run_id only as the bare-dispatch fallback, with the 2026-08-17 every-main-must-finish unique-run_id reading retired as gunbc.rung_drop main_per_commit_floor_verdict, and by witness_floor_concurrency_policy, which derives the cancel axis from the observed fleet standing; the note is repointed there because the ci.yml-era second authority it used to cite was deleted with its workflow. When each is either fixed or carried as a typed row, the note is a historical account and deletes with this bind. It is registered rather than deleted today because four sessions were archived holding these measurements and nothing else records them — but a cross-lane status narrative is the parallel-ledger shape DESIGN section 6 warns about, so its home is a question for the operator, not a settled one.") }, - }, HandAuthoredDocBind { home: PlanDoc, slug: "self-host-cargo-refusal-root-partition", diff --git a/docs/plans/accumulator-copy-roster-gate-census-ledger.md b/docs/plans/accumulator-copy-roster-gate-census-ledger.md deleted file mode 100644 index 3cdcba2b658..00000000000 --- a/docs/plans/accumulator-copy-roster-gate-census-ledger.md +++ /dev/null @@ -1,17 +0,0 @@ -# Ledger: accumulator_copy roster gate (300s census) - -Parent: sunny-bat-82 / snappy-stag-806 silent reds. Subject: `complexity/accumulator_copy_roster_gate_*` as one roster-scan cost. Cap not raised. Not enrolled in the required gate. - -Instrument: out-of-gate 300s census of those entries. Classification: **(d)** — real standing and scan-cost defects, not stale greens. - -Typed standing for priced-out lens walks: `gunbc.rung_drop.accumulator_copy_priced_out_file_gate_ratchets`. Executing consumer for those walks: `gunbc.offline_local_recipe` `RecipeFunctions` rows (not the drop's population strings). Exclusion rationale stays `gunbc.ci.ci_layer_roots` `accumulator_copy_roster_gate_exclusion_note` (unchanged from main). - -| Row | Class | Cause | Repair | -| --- | --- | --- | --- | -| TIMEOUT lean/bash/swift entries | (d) | Interpreted `file_gate` of `src/v2/extdeps/languages/{lean,bash,swift}.dag`. | Freeze identities deleted (meaning fork: ratchet name on a weaker contract). Walks: `roster_{lean,bash,swift}_file_gate_walk` in `*_walk.dag`, named by `gunbc.offline_local_recipe`. | -| TIMEOUT `accumulator_copy_roster_gate_test.dag` | (d) | Same scan packed `00_compile.dag` and `analyze.dag`. | Ordinary `fn`s `roster_compile_stage_file_gate_walk`, `roster_lens_traversal_file_gate_walk`. Executing `file_gate` `test fn`s: snippets plus `roster_std_algebra_zero_suspects_within_ratchet`, `roster_machine_code_zero_suspects_within_ratchet`, `roster_lens_cost_model_zero_suspects_within_ratchet`. | -| RED `red_control_planted_copy_still_alarms` | (d) | Control used `list_append(left: acc, right: x)` — linear non-copy polarity (`noncarrier_name_snippet`). Fixture `planted_copy.dag` and `quadratic_snippet` use `left: x, right: acc`. | Snippet polarity aligned with the planted fixture. | -| RED `roster_glob_discovery_zero_suspects_within_ratchet` | (d) | Path moved to `src/v2/test/workflow/glob_discovery_law.dag` (#9637). Interpreted `file_gate` also interrupted the 8s changed-witness wall. | Freeze identity deleted. Walk: `roster_glob_discovery_file_gate_walk` named by `gunbc.offline_local_recipe`. | -| RED `roster_compile_stage_zero_suspects_within_ratchet` | (d) | `00_compile.dag` is not a 300s interpreted subject. | Renamed `roster_compile_stage_file_gate_walk`; named by the recipe. | - -Freeze rows for demoted walks were deleted so they cannot rot as `StaleFrozenPathDeferral`. CI exclusion substring `claim/complexity/accumulator_copy_roster_gate` is unchanged. diff --git a/docs/plans/anonymous-record-literal-design.md b/docs/plans/anonymous-record-literal-design.md deleted file mode 100644 index 7cb43d6b562..00000000000 --- a/docs/plans/anonymous-record-literal-design.md +++ /dev/null @@ -1,305 +0,0 @@ -# Anonymous record literals: the tag-absent construct, elaborated from the expected type - -Status: PR1 of two (the model and its consumer census). PR2 is the cut. Owner lane: sleek-fox-423, -parent gentle-koi-724, reviewer neat-boar-16. This builds on the construct carrier of -`docs/plans/construct-tag-reference-design.md` (gunbc#12701, lively-eagle-657) and does not restate -it. - -## Symptom and the chain (DESIGN 6b) - -A headless brace literal, `data x: T = { f: v }` or a nested `f: { g: w }`, file-refuses at native -ingest with `body_lowering_reason_unsupported_form`. A refused file drops its declarations from the -native index, so every importer then fails as unbound. The survey (lively-eagle-657, quiet-hawk-702 -probe on the #12420 tree) names seven files. - -The slice, read from the grammar forward: - -- **Grammar.** `v2.extdeps.languages.dag` `dag_grammar_primary_expr_core` has ONE headless-brace - alternative, `{ field_init_list }`. `dag_grammar_field_init_expr` keys each item on EITHER a - binding name OR a string literal. The record literal `{ f: v }` and the map literal - `{ "k": v }` are the same production. Only the lexical kind of each key tells them apart. -- **Lowering.** `v2.compiler.body_lowering_fold` `body_lower_try_record_literal` needs a head tag - from `body_lower_record_literal_tag_optional`. A headless brace has none, so the primary is - unreadable and refuses at the primary (the XL-2 refusal arm, - `unrecognized_primary_expression_lowers_to_its_first_atom_at_v2_body_lowering`). This refusal is - CORRECT at this link. Lowering runs before resolve and before infer, so it cannot know the - constructor. The earliest unjustified boundary is therefore not lowering. It is the construct - carrier, which has no tag-absent case, together with the absence of any stage that carries a - declared type DOWN into the expression it types. -- **No stage pushes a declared type down.** v2 infer is a bottom-up fold. Its declared-type - judgments run AFTER a body is synthesised: `infer_arrow_body_inhabits_declared_return` compares a - derived body type with a declared return (cause `arrow_body_does_not_inhabit_declared_return`), - and only for a return the language join denotes (`dag_binding_denotation`). Call arguments are - judged the same way (`position_direct_call_argument`). A tag-elided construct cannot be - synthesised at all, because its tag is what types it. So its tag must be supplied BEFORE infer's - fold reaches it, by a stage that walks top-down with the declared type as context. -- **The seed is not an oracle for the choice.** v1 chooses the struct in its Rust emitter by - matching field names: `v1.compiler.emit_rust` `anonymous_record_struct_candidates`, and it - refuses `AmbiguousAnonymousRecordLiteral` ("shape matches N structs -- add a nominal type"). - That is the selection this lane is forbidden to make. So seed-vs-native agreement on the chosen - tag is not evidence. The oracle is content equality with the authored headed form (control 1). - -## The model - -**The carrier.** Form B is the tag-ABSENT case of the one construct carrier of #12701. It is not a -new node kind. After #12701 a construct is `Conj { : , fields.. }`. -The tag-absent construct is `Conj { : , fields.. }`. - -- The tag edge is always present, so every construct reader keys on the same first edge and asks - only whether its target is a reference or the elision marker. The alternative, a construct with - no tag edge, is a bare `Conj` of Named edges. That is also the anonymous record TYPE - (`body_lower_anonymous_record_type_optional`, `-> { success: Bool }`) and the shape every - "unmarked Conj" reader already means something else by. Presence-by-absence would be a second, - implicit representation. It is refused for the same reason #12701 keeps `construct_tag_marker` - distinct from `declaration_reference_marker`. -- `construct_tag_elided_marker` is a structural core marker declared beside `construct_tag_marker` - in `v2.std.node`, in the same form, and counted once in the #12473 `EdgeLabel` census. It - carries no spelling and no guess. -- **Key kind is recorded, not interpreted.** Each field edge keeps its authored key kind. A name key - is `Named { name }`, as today. A string key has no field-edge form yet (see Scope). Lowering does - not decide "record" versus "map". It records what was written. - -**Where elaboration happens: in resolve, at the construct-tag writer, with an expected-type -context threaded down from declared types.** (Revised after the first review. The first draft -placed it in infer "before synthesis". Infer is a bottom-up fold, so that would have been a second, -top-down pass beside it.) - -Choosing the tag reads exactly two facts, and both are NAME facts that resolve already has: - -- The expected type's HEAD resolves to a declaration. Resolve resolves every type expression. -- Whether that declaration is a single record, and, for nesting, its fields' declared types. Resolve's - `ResolveContext.namespace.symbol_index` returns the declaration node through `symbol_index_lookup`, - and `v2.std.type_binder` `type_decl_view` reads its shape. - -Resolve is also, after #12714, the ONE writer of construct tags: `resolve_construct_walk` rewrites -the authored tag spine into `declaration_reference_node(path)`. Writing the elided tag anywhere else -would give one fact, "the declaration this construct constructs", two writers (§3). So the elided -case is a second arm of that same writer, with the reference derived from the expected type rather -than from an authored spine. Infer then sees only ordinary resolved constructs, and checks them -with its existing construct typing and declared-return judgment. - -**The expectation is a parameter, not ambient context.** An expectation (`ResolveExpectation`: the -authored type expression, and the position its names are read at) is handed, as an explicit -argument, by exactly the four position rules below to the one child each rule owns -(`resolve_expected_edge`). It is never a `ResolveContext` field. Every other walk goes through -`resolve_node_walk`, which has no expectation to give, and a headless literal there refuses. That -makes review condition 3 structural rather than a clearing discipline: a field on the context would -have been inherited by every child walk that copies the context (a call's arguments, for one), and -each would have had to remember to clear it. - -**The expectation is set ONLY from an AUTHORED type annotation** (ruling, gentle-koi-724): a data -declaration's declared type, a fn's declared return, a record field's declared type, or the element -type of an annotated list. Resolve never infers, unifies or propagates a type it computed. Where no -annotation reaches an elided construct, it refuses located (`resolve_anonymous_record_no_expected_type`) -and never guesses. A record field's declared type is an annotation authored on the record -declaration, so nesting stays within this rule. **Syntactic peel only** (review condition 1, neat-boar-16, binding both arms). The expectation is peeled -syntactically from the authored annotation. Where reaching a closed record head would need -inference, type-variable instantiation, alias unfolding beyond resolve's existing lookup, or any -head that is not a closed record (or `Map`, for the map arm), the construct REFUSES located. There -is no unification and no substitution in resolve. A record field whose declared type is one of the -record's own binders (`top: T` of `BoundedLattice`) therefore gives no record head, and a headless -literal there refuses. The four positions: - -- **data initializer**: `data x: T = e` lowers to `Arrow(, T, T, body: e)`, so the - body edge of an Arrow is walked with its declared return as the expectation; -- **declared return**: the same Arrow rule covers `fn f(..) -> T { e }`, reaching `e`'s tail - expression through blocks and `let .. in` bodies (the value position, not the statements); -- **record field**: under a construct resolved to record `R` (authored OR elaborated), field `f`'s - init is walked with the declared type of `R.f` as the expectation, as authored (no substitution). This is how nesting works: `compile_stage_memo`'s - `key_derivation: { .. }`, and `host_transport`'s `RuntimePrimitive { value: { .. } }`; -- **list element**: under a list literal whose expected head is `List` (through its alias - authority), each element is walked with the element type argument as the expectation. - `dag/std/algebra`'s 88 `AlgebraFieldTemplate` rows need this. - -At a tag-elided construct, the elided arm of the writer: - -1. Takes the expectation. If there is none, refuse `resolve_anonymous_record_no_expected_type`. -2. Takes its HEAD's resolved declaration, through resolve's existing lookup only. Type arguments - (`BoundedLattice`) never choose the constructor, and they are not substituted - anywhere. If the head does not resolve, the head's own resolve refusal stands, and no second - cause is minted for it. A head that is a type binder, or that reaches a record only through - alias unfolding the lookup does not already do, refuses - `resolve_anonymous_record_expected_type_not_record`. -3. Requires the declaration to be a single record (`type_decl_view` `PlainTypeDecl` whose member is a - product), or `Map` (the MAP arm, which this lane refuses located; see Scope). Anything else, - whether a multi-variant coproduct, a primitive, or `List`, refuses - `resolve_anonymous_record_expected_type_not_record`. -4. Writes `declaration_reference_node(path)` of that declaration through `resolved_reference_node`, - the same carrier the authored arm writes. -5. Walks the fields under the record-field rule above. - -The choice reads the expectation only. The field set is never an input to steps 1-3. - -**Resolve's choice is not proof** (review condition 2). Infer CHECKS the elaborated construct -against the declared type exactly as it checks an authored tag: field names and field value types -through its existing construct typing, and the declared return through -`infer_arrow_body_inhabits_declared_return`. A wrong field or a wrong value type refuses in infer, at -the field. - -**Refusal causes** (typed, located at the literal's `{`, each with an ownership row in -`v2.workflow.compile_door_cause_ownership`, per -`refusal_producer_lands_without_its_ownership_row`): - -- `resolve_anonymous_record_no_expected_type`: the construct sits in a position with no declared - type. Examples: a `let` without an annotation, a call argument, a match-arm body, a list literal - that itself has no expected type. These are not guessed. Call arguments stay here on purpose: - their formal's declared type is a fact about the CALLEE, whose resolution this position does not - own. Widening to them is a named follow-up, not an implicit one. -- `resolve_anonymous_record_expected_type_not_record`: the expected head resolves to something other - than a single record or `Map`. -- `map_literal_construction_not_modeled`: the expected head is `Map`. This is the MAP arm, and it - plugs into the same writer. - -A tag-elided construct that survives resolve is unwritable downstream. The emit, eval and target_model -readers gate on a reference target, and the elision marker is not one. So a missed elaboration -refuses at their existing gates, and cannot emit an anonymous struct. - -## As built in PR2 (gunbc#12740) - -The model above was refined while the cut was reviewed. Each refinement below is the authority that -the PR2 code cites by section name. - -### One reader of the construct tag - -`v2.std.node_query` `construct_tag_reading` answers -`ConstructTagAuthored { reference } | ConstructTagElided | NotAConstruct`. Every reader (body -lowering's core-substrate test, resolve's construct-tag writer, the claims) matches on that one -answer. `construct_tag_optional` is its projection. The elision is carried as an EDGE LABEL -(`construct_tag_elided_marker`) on the tag's target, the mechanism the tag marker itself uses. A -marker carried as an atom's identity did not survive to resolve. And body lowering must count an -elided construct as core substrate, or its fold re-lowers the literal to its first atom. - -### The string key is a grammar production - -`dag_grammar_field_init_expr` is a choice between a name alternative and a production of its own, -`^dag_production_field_init_string_key` (emitted `^dag_surface_field_init_string_key`), whose key is -the class-stamped string terminal that gunbc#12759 decodes. So the key's kind is a fact of the parse -tree, read by its production identity (`body_lower_field_init_is_string_keyed`), and never recovered -from a spelling. A record literal refuses a string key at the item (`field_init_unlowered`). The -occurrence-role row of the new production reads its names as references, and the field-init reader -answers `NoNameHere` for it. The map arm (gunbc#12758) dispatches on the same identity. Admitting a -quoted key whose DECODED text names a declared field is a declared frontier. It is a follow-up in the -record arm, and it also carries the quoted-key round trip; the first lane that admits quoted keys -(gunbc#12758) carries that round trip for maps. - -### The head resolves the way the annotation resolves - -An annotation written in the literal's own module resolves its head through `resolve_atom` in the -literal's context, the same route the annotation itself takes, so the two cannot bind different -declarations. A field type is written in the record's own module, so it is read at the record's path -through the index's lexical lookup. - -### The list-element rule - -A list literal passes its element type to its elements only when the annotation's head reaches the -declaration infer types list literals with (`v2.std.list_introduction` -`list_introduction_head_path`, `std.algebra.FreeMonoid`). - -### Pure-renaming aliases - -`List` is an alias (`type List = FreeMonoid`), so the list rule needs one kind of -alias to be followed. Exactly one kind is followed: a PURE RENAMING, `type A = H` -(the aliased expression is a head applied to exactly the alias's own binders, in declared order) or -`type A = H`. Following one substitutes nothing, because the annotation's arguments stay in the -positions they were written. - -- Purity is decided ONCE, on the declaration (`v2.std.type_binder` `type_alias_renaming`). It is - recorded when the index records the declaration (`v2.std.symbol_index` `renaming_aliases`, - `symbol_index_renaming_alias_at`), and resolve reads the recorded fact, never re-deriving it at a - use. -- Chains are followed transitively, each renamed head read at the alias's own position. A revisited - path refuses located (`resolve_anonymous_record_alias_cycle`). -- A reordering, constant or partially applied alias is not a renaming. It is the declaration the head - names, so the literal refuses. - -## Consumers (producers, readers, rewriters) - -Stated as a delta on #12701's census. Every row there still applies; the rows here are the ones -the elided case adds. - -| Module | Symbol | Role | Change | -|---|---|---|---| -| v2.std.node | `construct_tag_elided_marker` | vocabulary | New structural core marker beside `construct_tag_marker`. | -| v2.std.node_query | `construct_node` | producer | Takes the tag target as the reference OR the elision marker (one parameter; a typed `ConstructTag = Authored { reference } \| Elided` at the call boundary so a caller cannot pass an arbitrary node). | -| v2.std.node_query | `construct_tag_optional` | reader | Answers the reference only. For an elided tag it answers Absent. `construct_tag_is_elided` is the one reader of the elided case. | -| v2.std.node | `arrow_body_record_construct_conforms`, `classify_arrow_body_form` | well_formed gate | Admits the elided marker ONLY before resolve. After resolve the well_formed gate refuses it. | -| v2.compiler.body_lowering_fold | `body_lower_try_record_literal` | producer | A headless `{ field_init_list }` whose every key is a name lowers to `construct_node(Elided, ..)`. The field inits go through the existing `body_lower_field_init_edge`. | -| v2.compiler.resolve | `ResolveExpectation`, `resolve_expected_edge`, `resolve_arrow_body_or_named_edge`, `resolve_record_field_edge`, `resolve_list_literal_expected` | position rules | The expectation parameter and the four rules that hand it to one child each. `ResolveContext` is unchanged. | -| v2.compiler.resolve | `resolve_construct_walk`, new `resolve_construct_walk_with_tag`, `resolve_elided_construct_walk` | writer | The tag is walked once before the fields, and its resolved path is handed to the field rule, so a headless literal nested under an authored head elaborates. The elided arm writes the tag from the expectation (steps 1-5). In `resolve_node_walk`, an elided construct refuses `resolve_anonymous_record_no_expected_type`. | -| v2.compiler.resolve | `resolve_pattern_node_walk` | reader | A tag-elided PATTERN does not arise (the pattern grammar requires a head). No change. | -| v2.compiler.infer | Conj gather arms | reader | No change. A tag-elided construct cannot reach infer, because resolve either writes its tag or refuses. | -| v2.workflow.compile_door_cause_ownership | three rows | ownership | One row per refusal cause. | -| v2.test.claim.namespace_xl0.reference_conservation_accepted_drops | `a_map_literal_value_refuses_at_the_literal_holds` | control | Today it asserts `unsupported_form` for `Map = { "k": .. }`. UNCHANGED by this lane: a string-keyed item still refuses at lowering here. It moves with the map arm (gunbc#12734, jolly-boar-246), which gives string keys an edge form and must then read the RESOLVE outcome. | -| v2.compiler.eval, v2.std.compilers.target_model, emit | construct gates | reader | No change. They already refuse a non-reference tag after #12701. PR2 adds a control proving an elided construct cannot reach them. | - -## Scope: which of the seven Form B admits - -THE READING THAT SET THIS LANE'S SCOPE, not a standing fact. It comes from a one-off probe on main -7f144278c57: each file's source was run through the parse and normalize route of -`v2.test.claim.namespace_xl0.reference_conservation` `conservation_subject`, and the first fatal was -rendered through the parse's `SpanIndex`. Only the FIRST fatal per file is shown, so a file can have -later blockers. The standing instrument is the #12550 base-vs-head native census, run on PR2's head. -It re-derives which of these files are admitted, and PR2 cites that census rather than this table -(DESIGN §6: name the instrument, never transcribe its output). - -| File | First fatal refusal (line) | Form | Form B admits it? | -|---|---|---|---| -| dag/std/algebra | 359, `{ name: "filter", .. }` | the body of `fn collection_filter_shape() -> AlgebraFieldTemplate`; 88 more rows of the same record inside list literals | yes: declared return, and list element | -| dag/std/primitives | 17, `data char_at_contract: PrimitiveContract = {` | data initializer | yes | -| dag/std/termination | 45, `data .. : BoundedLattice = {` | data initializer, generic, newline-separated fields | yes, by HEAD lookup: `BoundedLattice` resolves to a single record. Its type argument is not substituted and chooses nothing, and none of its field inits is headless. The newline separator is to be confirmed in PR2. | -| dag/extdeps/realization/compile_stage_memo | 39, `key_derivation: {` | record field under a headless data initializer (lowering refuses the inner brace first) | yes: data initializer plus record field | -| dag/extdeps/realization/parse_table_memo | 50, `key_derivation: {` | same | yes | -| dag/std/types | 5, `data kernel_type_set: Map = {` | MAP literal | **no: MAP arm.** Reaches the elided-tag writer in resolve and refuses located `map_literal_construction_not_modeled`, as a declared population (below). | -| src/v2/std/host_transport | about 119, `reason: ^emit_host_runtime_row_..` | **caret symbol literal** (`body_lowering_reason_caret_symbol_not_lowered`), not Form B | **not by Form B alone.** Its first blocker is the caret form. Its Form B site (`RuntimePrimitive { value: { .. } }`, record field) is admitted by this lane, but the file stays refused until caret symbols lower. That is #12420 (vivid-ant-536), so PR2 lists it as depending on #12420. | - -**The map arm (ruling, gentle-koi-724).** This lane builds the ONE elided-tag writer (in resolve) and its -RECORD arm. At that same site, a headless brace whose expected head is `Map` takes the MAP arm, which -refuses located `map_literal_construction_not_modeled`, with its ownership row. It is not left as -`unsupported_form`, and there is no second site later: map construction plugs into this arm. - -For lowering to reach the site, a string-keyed item needs a field-edge form. It lowers to the same -tag-elided construct, with each item carried as a `map_literal_entry_marker` edge to a -`(key, value)` pair. Lowering still does not pick map or record; a construct that mixes name keys -and string keys refuses at lowering, located at the first key of the other kind. - -The refusing population is DECLARED, as a `gunbc.rung_drop` row. Population: `dag/std/types` plus -every other map-literal file the base census locates, named. Restoration trigger: map construction -is modeled, so a `Map` expected type elaborates the literal to a value that inhabits it, linked -to the open question on the XL-2 row -(`unrecognized_primary_expression_lowers_to_its_first_atom_at_v2_body_lowering`). - -If `dag/std/types` staying refused still blocks the native seven transitively, the map arm is on the -critical path; the parent dispatches it. - -**The seed.** The seed choosing by field names is not an oracle. If it chooses WRONGLY for a real -file, meaning the chosen struct differs from the declared type, that is a silent seed defect, and PR2 -files a `gunbc.recurring_failure_mode` receipt for it. - -## PR2 (the cut) and its controls - -1. `data x: T = { f: v }` lowers, resolves and elaborates to a node whose `content_hash` equals that - of `data x: T = T { f: v }`. Content equality with the headed form. The authored-tag token occurrence - is the one expected difference. `v2.std.node` `content_hash` folds only kind, edge labels and - children, so it should fall outside the hash. PR2 confirms this by execution. If it does not, the - control compares the occurrence-erased projection, and says so. -2. A tag-elided literal with no expected type (a `let` without an annotation) refuses - `resolve_anonymous_record_no_expected_type`, located at its `{`. -3. The expected type is a two-constructor coproduct: refuses `resolve_anonymous_record_expected_type_not_record`. -4. Fields that do not match the chosen record refuse at the field, through the existing construct - typing, and not as "no constructor". -5. **Mutation: choose by field names.** Two records share the literal's field set. The expected - type names the one declared SECOND. The control asserts the declared type's path is chosen. A - field-name chooser picks the first or refuses as ambiguous, so it goes red either way. -6. Nesting: `host_transport`'s form, and a two-level `compile_stage_memo` form, each equal to their - hand-headed form. -7. **Infer still checks** (review condition 2): elaboration succeeds, and infer then refuses a field - value of the wrong type, located at the field. -8. **The expectation does not leak** (review condition 3): an anonymous literal passed as a call argument - under an annotated data declaration refuses `resolve_anonymous_record_no_expected_type`. A second - case puts it in a match-arm body under an annotated fn return. -9. **Syntactic peel** (review condition 1): a headless literal at a record field declared as the - record's own binder (`top: T`) refuses `resolve_anonymous_record_expected_type_not_record`. -10. An elided construct cannot pass the post-resolve well_formed gate or the target_model gate - (a discriminating pair with the headed form). -11. Evidence: the #12550 base-vs-head native census shows each Form B file of the table admitted, - with every other outcome change explained. Seed claims show no true->false. diff --git a/docs/plans/approval-enrolment-handshake.md b/docs/plans/approval-enrolment-handshake.md deleted file mode 100644 index 8b9087f647a..00000000000 --- a/docs/plans/approval-enrolment-handshake.md +++ /dev/null @@ -1,129 +0,0 @@ -# Approval device enrolment as a handshake - -Status: design (node://adhoc-19a8196b-f67, operator scope 2026-09-30). Authority once landed: -`gunbc.auth.approval_enrolment_handshake`. This page states the design; the model is the fact. - -## The defect this replaces - -Today one POST both consumes the enrolment code and mints the enrolment -(`gunbc.auth.approval_device_redemption` `commit_enrolment`: slot generation 1 `code` → 2 -`enrolled`). A phone whose POST gets no decodable answer holds `SubmissionUnknown`, and the only way -out is a readback authenticated by the *enrolled* App Attest key. For an enrolment that never -existed, that read can only refuse, and a bare refusal is not proof of absence. So the phone can't -safely discard its keys, can't reuse its code, and has no typed exit. On 2026-09-30 an operator's -phone sat in exactly that state after a front-door 404 (the device routes were unrouted), with an -expired code the server had never spent. - -The operator's requirement: **enrolment must never be a death sentence.** Every phone state has a -typed exit, and the phone may always start over safely. - -## The one invariant - -> **An enrolment can decide only if it is ACTIVE, and it becomes ACTIVE only on a signature by its -> decision key over a server challenge issued for that enrolment.** - -Everything else follows. A phone that discards its prepared keys can't produce that signature, so -nothing it discarded before sending the ACK can ever become ACTIVE. The worst case is a PENDING -record nobody holds, and PENDING lapses at its deadline on its own. **So a handshake ends by expiry -or by either party walking away, and the phone never has to tell the server it is leaving** -(operator refinement, 2026-09-30). Cancel is a local discard of the prepared keys. The one state -where a discard can leave something behind is after the ACK may have landed (see -*ConfirmUnknown*). What it leaves is an ACTIVE enrolment whose decision key no longer exists -anywhere, so it can never sign a decision. - -## The server record: one CAS slot per code - -The code stays the identifier (one thing typed, one slot). The slot's generations form a line, and -each write is a compare-and-set expecting the generation before it -(`std.durable_compare_and_set`), so two writers can never both win: - -| gen | record | written by | reached from | -|---|---|---|---| -| 1 | `IssuedCode { code, login, issued_at, expires_at }` | code issue verb (operator, via fleet-converge) | absent | -| 2 | `Pending { enrollment_id, login, platform, decision_key, attestation, submitted_at, pending_deadline, confirm_nonce }` | SUBMIT (SYN) | 1, code live | -| 3 | `Active { …pending, confirmed_at }` | CONFIRM (ACK) | 2, before `pending_deadline` | -| 4 | `Revoked { …active, revoked_at, reason }` | operator revocation | 3 Active | - -**The PENDING deadline is read, not swept.** A `Pending` observed at or after `pending_deadline` -*reads* as `Lapsed`, and CONFIRM refuses it. Nothing is ever written to end a handshake. This is the same pattern -the code already uses for its own window (`expires_at` compared at observation by -`utc_instant_before`). No background writer is needed and none can be forgotten. - -*Stated divergence (DESIGN §3b, leasing):* the brief said "std.temporal_effect lease". Its -`HeldLease` models an observed running process, `std.durable_exclusive_hold` has no deadline, and -`std.scoped_authorization` `OperatorGrant.expires_at` is an operator's grant, not a party's -provisional hold. So the deadline follows the enrolment code's existing `expires_at` pattern in -this module. If a deadline-bearing hold is ever added to std, this record should inhabit it. - -## The legs - -| leg | route | authenticated by | effect | -|---|---|---|---| -| SYN | `POST /approve/device/enrol` `{code, platform, decision_key, evidence, push}` | the code (bearer secret) + App Attest attestation over the transcript | gen 1→2 `Pending`; answers SYN-ACK | -| SYN-ACK | (the SYN's answer) `{enrollment_id, confirm_challenge {nonce, pending_deadline}}` | — | — | -| ACK | `POST /approve/device/confirm` `{enrollment_id, signature}` | **decision key** signature over `confirm_signing_input(enrollment_id, nonce)` (one Face ID at enrolment; operator-confirmed 2026-09-30) | gen 2→3 `Active`; answers `{standing: active}` | -| STATUS | `POST /approve/device/enrolment-status` `{code, requested_at}` | possession of the code | none; answers the closed status | - -**SYN is idempotent.** A SYN against a `Pending` slot whose stored decision key and attest key id -equal the request's answers the *same* SYN-ACK (same `enrollment_id`, same nonce, same deadline) -and writes nothing. A SYN against a slot already at gen 2+ with a *different* key refuses with -`code_already_used`. That's the only way a spent code refuses, and it can't be reached by the phone -that spent it. - -**STATUS is authenticated by the code.** Every state has a code, and the phone holds it from the -moment it's typed. So one authentication covers every state, including states where the server -holds no key for the phone (gen 1, absent). The answer is a closed set: - -``` -NeverSubmitted gen 1, code live → phone may SYN (again) -CodeExpired gen 1, code past expires_at → phone discards, needs a new code -CodeNotIssued slot absent → phone discards, needs a new code -Pending { enrollment_id, confirm_challenge } → phone confirms (ACK), or walks away -Lapsed → gen 2 past its deadline; phone discards -Active { enrollment_id, decision_key_matches: Bool } → phone adopts iff its key matches -Revoked → terminal, phone discards -``` - -`Active` answers whether the stored decision key equals the one in the request's proof. That's -why STATUS also carries the phone's decision key: a code holder learns only whether *its own* key -is the active one. - -## Phone states and their exits (every one typed) - -| phone state | how it's left | -|---|---| -| `Unenrolled` | code typed → `Prepared` | -| `Prepared` (keys made, nothing sent) | SYN → `SubmissionUnknown`; Cancel → discard → `Unenrolled` (nothing left the phone) | -| `SubmissionUnknown` (SYN may have landed) | SYN-ACK → `Pending`; STATUS: `NeverSubmitted` → resend SYN; `Pending` → `Pending`; `CodeExpired`/`CodeNotIssued`/`Lapsed` → discard → `Unenrolled`; `Active`+match → `Enrolled` (impossible without an ACK, answered for completeness) | -| `Pending` (SYN-ACK held) | ACK → `ConfirmUnknown`; Cancel → local discard → `Unenrolled` (the PENDING lapses unaided) | -| `ConfirmUnknown` (ACK may have landed) | `{standing: active}` → `Enrolled`; STATUS: `Active`+match → `Enrolled`; `Pending` → re-ACK; `Lapsed` → discard → `Unenrolled`. Cancel → local discard. If the ACK had landed, the ACTIVE record is left with a decision key that no longer exists: inert for deciding, and removed by the operator's existing revocation. The phone shows that consequence before discarding here. | -| `Enrolled` | revocation, key invalidation (existing) | - -Cancel is the operator's existing local Cancel button, and it stays local: no route, no signature, no server write. The phone never has to revoke or abandon anything (operator refinement, 2026-09-30); a server-confirmed withdraw route is explicitly not the design. - -## RED per transition (a lost answer at each leg) - -Each row is a claim in the PR. It names the state the phone holds, what was lost, what the server -holds, and the typed exit the phone must take. The RED is the mutation that must make it fail. - -| # | lost | server holds | phone exit | RED | -|---|---|---|---|---| -| R1 | SYN never arrived | gen 1 live | STATUS→`NeverSubmitted`→resend | STATUS on gen 1 answers anything but `NeverSubmitted` | -| R2 | SYN-ACK lost | gen 2 `Pending` | resend SYN → same SYN-ACK, one record | a repeated identical SYN writes or answers a different nonce | -| R3 | SYN with another key | gen 2 | `code_already_used` | a second key's SYN is admitted or overwrites | -| R4 | ACK never arrived | gen 2 | STATUS→`Pending`→re-ACK | a PENDING record decides a redemption | -| R5 | ACK answer lost | gen 3 `Active` | STATUS→`Active`+match→`Enrolled` | STATUS answers `Active` without the key match, or matches a foreign key | -| R6 | ACK after deadline | gen 2 past deadline | refuse → STATUS `Lapsed` → discard | a lapsed PENDING confirms | -| R7 | phone walked away (Cancel) | gen 2 | deadline lapses → reads `Lapsed`; a new code enrols afresh | a lapsed PENDING reads `Pending`, or blocks a new code | -| R8 | two ACKs (retry after a lost answer) | gen 2 or 3 | the second answers `active` idempotently | a repeated ACK writes a second record or refuses a confirmed enrolment | -| R9 | code expired unspent | gen 1 past expiry | STATUS→`CodeExpired`→discard | an expired code reads `NeverSubmitted` | -| R10 | ACK with a key other than the pending one | gen 2 | refuse | a signature by any key but the pending decision key activates | - -## Migration - -This replaces generations 2 and 3 of the enrolment slot (`enrolled`, `revoked` → `pending`, -`active`, `revoked`). It's a replacement migration cut at the root (DESIGN §3): the -store on srv1 holds **no** enrolled record today (only two expired gen-1 codes), so there's no -persisted population to carry. The redemption path (`device_redemption_admission`) reads `Active` -where it read `EnrolmentCodeConsumed`, and every enrolment-only route that took the enrolled -standing takes `Active` and nothing else. diff --git a/docs/plans/arrow-contract-edges.md b/docs/plans/arrow-contract-edges.md deleted file mode 100644 index 3763fc13eff..00000000000 --- a/docs/plans/arrow-contract-edges.md +++ /dev/null @@ -1,89 +0,0 @@ -# Arrow contract edges (XL-2 PR2a) - -Parent design: [service-interface-and-uses-carriers.md](service-interface-and-uses-carriers.md) (#12851). Ruling: the side chat, 2026-10-01, option A, admitted with no operator sign-off needed. The ruling forbids four things, none of which this change does: a new connective or behaviour, an annotation bag, unknown labels, and a second carrier for one fact. - -## What lands - -An Arrow may carry three optional **contract edges**. The first two (effect claims and execution mode) landed in PR2a. The third (resource requirements) landed with D13 step (a); see the section on it below. - -| Edge | Target shape | Vocabulary home | Admitted labels | -| --- | --- | --- | --- | -| `^arrow_effect_claims_edge` | NONEMPTY Conj of self-named childless Atoms, no label twice (no claims has one form: the absent edge) | `std.effects` `EffectClaim` (`ReadonlyClaim`, `IdempotentClaim`), new here | `readonly`, `idempotent` | -| `^arrow_execution_mode_claim_edge` | one childless Atom | `std.execution_mode` `ExecutionMode` (existing) | `hermetic` (only `Hermetic` is claimable) | -| `^arrow_resource_requirements_edge` | one of THREE declared forms: a NONEMPTY Conj, one Named edge per member (label = spelling, target = type reference); the childless Atom `^requirements_declared_none` (`requires none`); the childless Atom `^requirements_opaque` (`requires opaque`). An ABSENT edge is an undeclared operation, read as Undecided (see the reversal below) | the open set of `resource` declarations, checked at resolution (`v2.std.symbol_index` `symbol_index_declares_resource_at`) | any declared resource, each at most once by resolved declaration | - -`hermetic` is kept off the effect claims: it says where the Arrow may run, not what its effect does. The surface `OperationModifier` stays one syntax vocabulary (`v2.extdeps.languages.dag` `dag_grammar_op_modifier_expr`), and lowering (PR2c) projects each arm to its home. - -**Three layers, one fact each:** -- **Structure: `v2.std.node`.** - - `arrow_named_edge_is_contract`. - - `arrow_named_edge_is_non_binder`, the one predicate every count of an Arrow's binders reads, here and in `v2.std.type_binder` `arrow_named_labels_conform`, which no longer restates an allowlist. - - `arrow_signature_edges_conform`: at most one of each contract edge, and never a binder. This module names no claim or mode. -- **Vocabulary: the homes.** `std.effects` `EffectClaim` and `std.execution_mode` `ExecutionMode`. -- **Spelling and check: `v2.std.arrow_contract`.** - - `effect_claim_label` and `execution_mode_claims_label` are exhaustive matches over the home types. They are the only place the surface spellings appear, so a variant added to a home must be decided there. - - `arrow_contract_conforms` checks each contract edge's target against the homes. `v2.std.type_binder` `type_binder_node_conforms` runs it on every Arrow. - -**Declared residue (🟡).** A label is admitted by comparing it against each claimable variant's projection (`effect_claim_label_is_admitted`, `execution_mode_claim_label_is_admitted`). That is a hand list of the variants, because the language cannot fold over a closed coproduct's constructors. No decoded variant is produced, because nothing consumes one: lowering (PR2c) projects variants to labels, never the reverse. A variant missing from the list is refused, never admitted, so the residue fails closed. **Bound:** two claims and one claimable mode. **Owner:** XL-2. **Trigger:** a language capability to enumerate a closed coproduct's constructors; the list is then derived from the projections and deleted. For the same reason, the edge target is a label checked against the home rather than a value of the home type: a Node edge carries Symbols, and making a wrong claim unwritable would need typed node targets. That is a substrate capability beyond this ruling, which forbids a new connective without operator sign-off. - -Properties required by the ruling, and where each is established: -- **Contract edges are not binders.** `arrow_named_edge_is_non_binder`. Test: `admitted_contract_edges_conform_and_are_not_counted_as_a_binder`. -- **Duplicates and unknown labels refuse.** Tests: `a_malformed_effect_claim_refuses` and `a_malformed_execution_mode_claim_refuses`. The reds include an empty claims edge (a second identity for "no claims"), `hermetic` among the effect claims, and `wet`, a real `ExecutionMode` that is not claimable. Every test checks both walls, the structural one and `v2.std.type_binder` `type_binder_node_conforms`, so a positive control goes red if `arrow_named_labels_conform` omits either label. -- **Canonical identity is order-independent, and a claim is part of identity.** Test: `contract_edge_identity_is_order_independent`, over `v2.std.node` `content_hash`. -- **A claim-free Arrow keeps its identity.** It carries neither edge, so nothing about it changes. - -All tests are in `v2.test.claim.arrow_contract_edge_conformance`. - -## Readers that must not consume the edges - -Audit of every Arrow reader outside `src/v2/test`. "Metadata" means: handled exactly as the declared-order edge is. - -| Reader | Today with an unknown named edge | Owed | -| --- | --- | --- | -| `v2.std.node` `arrow_signature_edges_conform` | counted as the binder | **this change** | -| `v2.std.type_binder` `arrow_named_labels_conform` | refuses | **this change**: reads `arrow_named_edge_is_non_binder` | -| `v2.std.type_binder` `type_binder_node_conforms` | — | **this change**: runs `v2.std.arrow_contract` `arrow_contract_conforms` | -| `v2.std.type_binder` `node_inferred_subtree_nodes` | walked as inferable | skip every `arrow_named_edge_is_contract` edge. **Landed with D13 step (a)** (the first producer) | -| `v2.compiler.resolve` `resolve_arrow_node_in` | resolved under the body scope | effect claims and execution mode: carried unwalked. Resource requirements: walked in the Arrow's type scope (no value params), each member must name a declared resource, at most once (`resolve_arrow_resource_requirements`). **Landed with D13 step (a)** | -| `v2.compiler.infer` `infer_gather_fold_step` / `infer_arrow_signature_order_edge` | inferred as a value child | skip every contract edge. **Landed with D13 step (a)** | -| `v2.compiler.infer` `infer_product_child_evidence_edges` (the Arrow's derived TYPE) | its type enters the derived type | every contract edge rides as metadata. **Landed with D13 step (a)** | -| `v2.compiler.translate` `translate_grounding_derived_gate_subtree` | requires grounding for every subtree node | exempt (metadata). **Landed with PR2c-i**: the walk skips a contract edge under an Arrow (`translate_edge_is_arrow_contract`, reading `arrow_named_edge_is_contract`), so a claim payload is never required to carry grounding. Discriminator: `v2.test.claim.translate_underived_refusal` `translate_gates_a_positional_payload_but_not_a_contract_payload_holds` | -| `v2.std.node` content hash (`canonicalize_arrow_labeled`) | hashes every edge, named edges sorted by label | in the hash, order-independent (tested here) | - -Readers that ignore a named edge safely: the domain, codomain and body readers in resolve, infer, eval, translate and `v2.std.compilers.target_model`; `v2.std.node_query`; `v2.std.decl_index`; `v2.compiler.symbol_index_fill`; `v2.compiler.emit_produced`; `v2.compiler.compile`; `v2.std.node` `cost_edge_role`; `v2.lens.cost.copied_port_derivation`. - -## Standing: DESIGN §3c, per edge family - -The two families have different producers and different consumers, so each has its own standing. - -### Effect claims and execution mode (PR2a, produced by PR2c-i): consumer a declared frontier - -- **Consumed by execution:** the conformance wall (`v2.std.node` `arrow_signature_edges_conform`, reached by every `well_formed` check of an Arrow) and `v2.test.claim.arrow_contract_edge_conformance`. The pipeline reader arms (resolve carries both unwalked; infer and `type_binder` skip them) landed with D13 step (a), the first producer of any contract edge. -- **Produced by execution (PR2c-i):** the operation-modifier lowering (`v2.compiler.body_lowering_fold` `body_lower_operation_modifier`) emits both edges from `readonly`, `idempotent` and `hermetic`, and refuses a repeated modifier at the second (`v2.test.claim.normalize.operation_modifier`). `translate_grounding_derived_gate_subtree` carries them as metadata in the same change. -- **Consumer of the claims themselves, still a declared frontier:** no stage yet reads an effect claim or the execution mode to decide anything (retry admission, caching, hermetic placement). Trigger: the first such reader lands and consumes the edge off the operation Arrow; until then the conformance wall and the reader arms are the consumers. -- **Why ahead of the consumer:** the 2026-10-01 ruling asked for the substrate change to be reviewable on its own diff. - -### Resource requirements (D13 step a): producer landed, demand consumer a declared frontier - -- **Produced and consumed by execution in step (a):** `body_lower_operation_requires` emits the edge from an operation's `requires` clause. `v2.compiler.resolve` `resolve_arrow_resource_requirements` reads it on every resolved operation Arrow and refuses a member that is not a declared resource or that repeats one. Controls: `v2.test.claim.normalize.operation_requires_edge`. That is a conformance consumer: it establishes that the requirement names real resources. It does not use the requirement. -- **Production consumer of the requirement itself, a named later change:** D13 step (b), the DependencyDemand derivation. A function's demand is the union of three parts: its direct resource operations; for each service operation it calls, that callee Arrow's `^arrow_resource_requirements_edge`; and its resolved callees' demand. That is how a `net: Network` requirement becomes derived instead of authored, which is the restoration trigger of `gunbc.rung_drop` `network_requirement_unrepresented_after_uses_cut`. -- **Transition trigger, at executable grain:** step (b) lands, and its derivation reads this edge off the callee operation's Arrow into the calling function's derived demand. Executed evidence is a control in which a function calling an operation that `requires Network` derives demand naming `std.resources.Network`, and that demand disappears when the clause is removed. Reading the edge only for conformance, or adding Network to demand by spelling, does not satisfy the trigger. -- **If step (b) is abandoned:** the `requires` grammar member, `body_lower_operation_requires`, the `^arrow_resource_requirements_edge` label and its at-most-one count, its target check in `v2.std.arrow_contract`, and `resolve_arrow_resource_requirements` with its two refusal reasons are deleted. The resource declaration's index mark (`symbol_index_declares_resource_at`, from #12898) then has no reader left, so it is deleted with them, along with its `resource_declarations` carrier. - -## Not in this change: input defaults - -Per the ruling, a default belongs to the binder and not to its type. One binder representation (a required type, an optional single default, closed labels, one reader and builder) serves fn parameters, service io fields and record fields. That changes the existing name-to-type binder shape, so it is a replacement migration (DESIGN §3) and is PR2b: its producers and readers are measured first, then all move together. - -## Third edge: resource requirements (D13 step a) - -`^arrow_resource_requirements_edge` (`v2.std.node` `arrow_resource_requirements_label`) is the operation's declared `requires R, ..` (`v2.extdeps.languages.dag` `dag_grammar_op_requires_expr`), lowered by `v2.compiler.body_lowering_fold` `body_lower_operation_requires`. Its target is a nonempty Conj with one Named edge per member: the label is the member's spelling, and the target is its type reference. Unlike the two claim edges, it names DECLARATIONS from an open set, so `v2.compiler.resolve` walks it and checks each member against `v2.std.symbol_index` `symbol_index_declares_resource_at`. It is the first producer of any contract edge, so the pipeline reader arms above landed with it, for all three edges. Controls: `v2.test.claim.normalize.operation_requires_edge`. - -### Reversal: an absent requirements edge is UNDECLARED, not "none" (D13 ruling B) - -**This reverses #12911**, where "no requirements has one form, the absent edge". D13 step (b) derives a function's demand from the operations it calls. Under the old reading, every operation nobody audited, and every one added later, would silently read as requiring nothing. That is the fail-open default DESIGN section 5 forbids, so the lane owner ruled it out (option B). Every operation now declares one of three forms, all on this one edge, with no second carrier: - -- `requires R, ..`: the named resources, checked at resolution as above. -- `requires none`: **no requirement in the audited vocabulary.** Today that vocabulary is `Network` only (the D13 Network audit, `docs/plans/d13-network-audit.md`). It is not a claim about Filesystem, Clock or Entropy: their demand derives from direct capability calls and is never authored. When the audited vocabulary grows, the meaning of `none` grows with it, and the rows are re-audited. -- `requires opaque`: the operation's demand is decided by runtime values (a generic exec, `shell.exec Run`, `systemd-run`, a runtime-program runner), so a caller's derived demand is **Undecided**, a located refusal at the uses-wall, never empty. - -An operation with no clause carries no edge. Step (b) reads that as **Undecided**, exactly like `opaque`, so silence can never mint "none". The grammar (`dag_grammar_op_requires_expr`) makes `none` and `opaque` whole clauses: a member after either refuses. The v1 seed carries the same three forms (`v1.compiler.parse` `operation_requires_declaration`: `RequiresUndeclared | RequiresNone | RequiresOpaque | RequiresResources`). Controls: `v2.test.claim.normalize.operation_requires_edge` and `test.claim.v1_operation_requires_parse_witness_test`. diff --git a/docs/plans/arrow-elimination-model.md b/docs/plans/arrow-elimination-model.md deleted file mode 100644 index 44614d3bd65..00000000000 --- a/docs/plans/arrow-elimination-model.md +++ /dev/null @@ -1,125 +0,0 @@ -# Arrow elimination in v2 infer — model for ruling - -Status: RULED by v2 foundation (neat-boar-16), 2026-09-26, and implemented in the PR that -carries this file. Rulings: Int's value type is owned by `v2.std.integer` -(`integer_int_type_node`) as Bool's is by `v2.std.logic` (`bool_node`); `dag_binding_denotation` -stays the one binding→type join and its rows point at those authorities. Elimination reads the -DECLARED return atom, never the composed evidence. The composed-evidence defect is rostered as -`gunbc.recurring_failure_mode` `function_type_evidence_carries_its_body`. - -Found while building: the evaluator carried the same second representation for Bool -(`v2.extdeps.runtimes.v2_evaluator` `v2_eval_bool_literal_pin` as every Bool runtime value's -type), so eval's resolved-type acceptance refused the derived Bool application with -`eval_rejected_resolved_type_mismatch`. Bool runtime values now carry `bool_node()`; the pin -remains only as the evaluator's literal-shaped node for `true`. The evaluator's Int copy -(`v2_eval_int_type_node`) is deleted in favour of `integer_int_type_node`. -Trigger: work item "v2: infer derives a Bool-returning application", parent `deep-bee-18`, -consumer `v2.compiler.refinement_discharge`. - -## What was measured (main `21f4d0c390`, a locally built `gunbc run` over a probe entry) - -| application `Transform[Arrow(Conj{x: Int}, R, body), 1]` | infer | root facts | eval | -|---|---|---|---| -| R = Int, body = int literal | Accepted | GroundingNotDerived | `eval_rejected_grounding_not_derived` | -| R = Bool, body = `true` | Accepted | GroundingNotDerived | `eval_rejected_grounding_not_derived` | -| R = Int, body = `true` | Accepted | GroundingNotDerived | — | -| R = Bool, body = int literal | Accepted | GroundingNotDerived | — | - -`infer_compatible_argument_admits` asserts only `Accepted`, so it never witnessed derivation. - -## The slice, re-derived (DESIGN §6b) - -1. **Return type atom → its grounding.** `v2.compiler.infer` `infer_node_facts` grounds a - binding atom through `v2.extdeps.languages.dag` `dag_binding_denotation`, which declares - only `^dag_binding_type_int` and maps it to `dag_int_inhabitant_node` — the *roster record - describing* Int (inhabitant atom + surface spelling), not the Int type. Bool has no row, so - a Bool return atom stays on the frontier, and so does its Arrow (the product row needs every - child). -2. **Arrow introduction.** Nothing compares an Arrow's body type to its declared return. The - Arrow product row composes children's evidence — including the body's — into the Arrow's - "type". -3. **Arrow elimination — the earliest unjustified boundary.** - `infer_transform_derived_optional` derives a type only for binary Int add and casts. No rule - types `f(a)` as `f`'s codomain, so NO application derives, Int included. Argument - inhabitance (`infer_application_argument_inhabitance`) is judged, then the node falls to - the frontier. - -## One Int, not two (the unification) - -Census of what each form means today: - -- `Atom(^dag_binding_type_int)` is the Int **value type** at every consumer: the Int literal - rule (`infer_branch_int_binding_type_node`), binary add's unified type, match/branch - unification (`infer_branch_type_is_int`), parameter operand types (the declared domain atom - via `infer_find_arrow_domain_type_in_tree`), argument inhabitance (declared formal compared - structurally to the argument's type), and the evaluator's runtime primitive type - (`v2.extdeps.runtimes.v2_evaluator` `v2_eval_int_type_node`). -- `dag_int_inhabitant_node` (a Conj) is the dag language roster's record about Int. Its only - use as a *type* is `dag_binding_denotation` → the binding atom's derived type. - -So the fork is `dag_binding_denotation` returning the record. The model: - -- **`dag_binding_denotation(sym)` is the single binding → value-type authority** and returns - the value type the binding names: Int → `Atom(^dag_binding_type_int)`, Bool → - `v2.std.logic` `bool_node()` (the node the Bool literal rule already uses). The Bool row is - one more row of the same join, not a special case. -- **The literal rules consume it** instead of minting their own nodes: an Int literal's type is - `dag_binding_denotation(^dag_binding_type_int)`, a Bool literal's is - `dag_binding_denotation(^bool_node_symbol)` (bool_node's own identity since gunbc#12785, which - derives every kernel row from `dag_kernel_value_type_roster` and deleted the second Bool name). `infer_branch_int_binding_type_node` - deletes. (The evaluator's `v2_eval_int_type_node` is the runtime's own copy of that node, and - is left to a later cut; it is equal by structure today.) -- **A type-expression atom's OWN grounding is its kind**, not its denotation: - `kind_node(TypeDenotationKind)` (`std.kind`), exactly as a roster type member derives today. - This is forced, not chosen: the Int denotation is structurally the atom itself, so taking it as - the atom's derived type hits `std.constraints` `canonical_grounding_from_derived_type`'s - self-evidence wall — and it was always a category error (the type of the expression `Int` is - a type-kind, not `Int`). - -## The two rules - -**Arrow introduction (body/return check), at the Arrow product row.** When an Arrow carries a -body edge (`^arrow_body_edge`) and both the body's facts and its declared return atom's -denotation are derived: the body's value type must equal the denotation, structurally with -provenance stripped (`infer_type_equal_ignoring_provenance`, the list rule's authority). A -mismatch refuses with the new reason `^arrow_body_does_not_inhabit_declared_return`, located at -the body. A body or return that is not derived leaves the Arrow on the counted frontier, never -admitted. - -**Arrow elimination, in `infer_transform_derived_optional`.** For `Transform[callee, args…]` -whose callee is an Arrow and whose argument inhabitance was admitted: the application's derived -type is `dag_binding_denotation` of the callee's declared return atom. The derivation requires -the callee Arrow itself derived (so the introduction check has run). A non-Arrow callee (an Atom -operator — `FormalUnresolved`), an underived callee, or a return with no denotation stays on the -counted frontier. - -Red and controls owed: -- Accepting: `R = Bool, body = true` derives Bool at the application; eval of it equals eval of - the literal `true` and differs from eval of `false`. -- Accepting: `R = Int, body = 1` derives Int at the application (same rule, not a Bool case). -- Red: `R = Bool, body = 1` and `R = Int, body = true` refuse - `^arrow_body_does_not_inhabit_declared_return`. -- The existing argument-inhabitance reds stay red. - -## Consumers that see the newly derived facts - -- `v2.compiler.eval` `inferred_facts_for_eval` — applications now pass its grounding gate. -- `v2.compiler.translate` — the `translate_rejected_grounding_not_derived` gate, same. -- `v2.std.coercion` via `infer_transform_cast_optional` — a cast whose operand is an - application is now judged by `coercion_cast_crossing` instead of staying on the frontier. -- `v2.compiler.refinement_discharge` (deep-bee-18) — the intended consumer. -- infer's own branch/match/loop unification — an arm that is an application now has a type, so - a previously frontier arm pair can now unify or refuse `arm type mismatch`. This is the - population the floor has to census. -- Readers of an Arrow's evidence or a binding atom's resolved type: the return atom's evidence - changes from the inhabitant record to the type kind. `v2.test.execution.dag_binding_denotation` - counts derivation only and holds either way. - -## Questions for the ruling - -1. Is `dag_binding_denotation` the right single authority for binding → value type, or should - Int's value type live in `std.integer` (as Bool's lives in `std.logic`), with the language row - pointing at it? -2. The Arrow's composed evidence includes the body's type (and `dag_arrow_with_body_node` lists - the return atom twice). A function type is domain → codomain. Out of scope here unless you - rule otherwise; noted so it is not mistaken for part of this model. diff --git a/docs/plans/binder-node-representation.md b/docs/plans/binder-node-representation.md deleted file mode 100644 index b69d1070400..00000000000 --- a/docs/plans/binder-node-representation.md +++ /dev/null @@ -1,81 +0,0 @@ -# The binder node (XL-2 PR2b-3) - -Parent design: [service-interface-and-uses-carriers.md](service-interface-and-uses-carriers.md). - -Rulings (side chat, 2026-10-01): -- A default belongs to the BINDER, never to its type. -- One binder representation (a required type, an optional default, closed labels, one reader and one builder) is shared by fn parameters, service io fields and record fields. -- For the representation, option A: a `Conj` told apart in the way `v2.std.type_binder` tells type-parameter binders apart. That needs no new connective, so no operator sign-off. - -## The representation - -A binder is still the Named edge `name -> target` inside an Arrow domain, io payload or record payload. Its **target** is now a **binder node**: - -``` -Conj { - -> T (exactly one) - -> v (at most one) -} -``` - -- **Self-identifying labels.** Both labels are interned from lexemes no source can spell, as `type_params_marker` is. A binder node therefore identifies itself from its own labels, with no parent context and no guessing. No authored record field can collide with it: a field spelled `binder_type` interns a different symbol. -- **One reader.** `v2.std.node_query`: `binder_node_parts`, `declared_field_from_edge` (which now carries `default`), `member_edge_type_node` (now `Optional`), `find_binder_type`. -- **One builder.** `binder_edge`, `binder_edge_with_default`, `binder_relabelled`. 2b-1 (#12899) and 2b-2 (#12904) routed every reader and every producer through this pair, so the representation changed in these functions alone. -- **Fail-closed.** - - A Named member whose target is not a binder node is **not** a binder. `declared_field_from_edge` answers Absent, `member_edge_type_node` answers Absent, and `find_binder_type` refuses with `^named_child_not_a_binder`. - - The **binder wall**, `v2.std.type_binder` `arrow_domain_members_are_binders`, run by `type_binder_node_conforms` on every Arrow, refuses a hand-built domain member at the Arrow. - - A record payload the wall does not reach still fails closed at its first reader. `type_decl_view` cannot tell a record type from a module body structurally, so the wall cannot be stated there without guessing. - -## Walkers that would have misread a binder node, and what each does now - -The inventory covered every Arrow, domain and payload reader outside `src/v2/test`. **None needed parent context** (the ruling's stop condition): - -| Walker | Now | -| --- | --- | -| `v2.compiler.infer` formation (`infer_formation_facts_from_entries`) | A binder node has its own arm, `infer_binder_node_facts_from_entries`. Its derived type is a binder node over the type's derived type with **no default**, so defaults are not type identity, and derived types and source types share one binder shape. A binder's default is judged at the binder's declared type (`infer_binder_default_check`, position `PositionBinderDefault`; XL-2 PR2c-ii). | -| `v2.compiler.symbol_index_fill` `symbol_index_fill_containment_edge` | Indexes a binder at its **type** (`symbol_index_binder_type_or_target`), so a parameter or field path resolves to its declared type. ``/`` never become path segments. | -| `v2.compiler.translate` `translate_algebra` | A binder node translates **as its type** (`TypeExprTranslateBinder`, `translate_binder_node_step`). The default's fold result is discarded. A non-binder record member refuses (`translate_binder_member_type`). Arrow type expressions read parameter types through `find_binder_type`. | -| `v2.compiler.resolve` declaring path | Does not extend across `binder_node_label`s. | -| `v2.std.bounded_lattice_completeness` | Reads a binder field's shape from its type (`bounded_lattice_field_subject`). | -| Lenses (unit modeling, lifecycle, residency) | Through `member_edge_type_satisfies`. A non-binder answers no. | -| Type-name indexes (`node_query`, `concept_index`) | Through `member_edge_type_name`. A non-binder renders ``, never a fabricated name. | -| `target_model` parameter rendering | Through the reader. A non-binder refuses. | -| `lens/application` paths | A path that addressed a field's type now addresses its binder node. An edit replacing it with a bare type produces a hand-built binder, which the readers and the wall refuse. Loud, not silent. | -| Subtree-fold lenses (effect reach, determinism, cost, fn index, mandatory tag, decl-facts skeleton) | Will see a default as code once defaults exist. No default exists before 2c, which decides count-or-skip per lens. | -| Structural equality / anti-unification | Compare binder nodes structurally. Derived types carry no default, so two Arrows differing only in a default compare equal once defaults are judged in 2c. Nothing to change before then. | - -## Expected content-hash churn (one-time, intended) - -Every binder's target changes from `T` to `Conj { -> T }`. So the content hash (`v2.std.node` `content_hash`) of **every Arrow with a parameter, every record type with a field, every variant arm with a payload field, and every node containing one** changes once in this PR. The ruling anticipated this ("a claim-free Arrow keeps its identity except for an intentional one-time binder migration"). - -What moves with it: -- `v2.lens.interface_summary` `signature_fingerprint_of_node`. Every declaration with a parameter or field gets a new fingerprint. No API change is implied. -- `v2.lens.affected_set` `canonical_boundary_hash_receipt`. -- Any committed fixture or receipt that pins such a hash. - -A nullary Arrow, an Atom type, and a payload-free variant keep their hashes. - -## Defaults (XL-2 PR2c-ii) - -- **Lowering.** Every default lowers onto its binder through `v2.compiler.body_lowering_fold` `body_lower_binder_edge_read` (`binder_edge_with_default`): - - a fn or pattern parameter's from its own tail (`body_lower_typed_param_optional`); - - a record field's and a service io field's from the field tail (`body_lower_io_field_default_optional`). - - The value lowers through the one value reader (`body_lower_value_read`) and refuses at the value under that reader's cause. `body_lowering_reason_default_value_unmodeled` has no producer and is deleted. A field's **wire key** is a realization fact and still refuses (records) or is set aside (io blocks) until the realization binding (XL-2 PR3). -- **Judgment.** `v2.compiler.infer` `infer_binder_default_check` judges the default at the binder's declared type through `infer_judge_declared_position`, at a fifth position, `v2.std.inhabitance` `PositionBinderDefault`. That is the same relation an argument meets at its formal. A non-inhabiting default refuses as `^infer_reason_default_does_not_inhabit_binder_type`, located at the default. `^infer_binder_default_unjudged` is retired. -- **Scope.** A default sits under the Arrow's domain, which resolve walks in the type-parameter frame over the OUTER scope, never the Arrow's own value parameters. So a default cannot read a sibling parameter. -- **Emission.** A target with no parameter or field defaults refuses a defaulted binder, located at the default (`^produced_decl_render_param_default_unrealized`, `^target_semantic_decl_field_default_unrealized`), rather than dropping it. -- **Subtree lenses.** A default is code that runs when its argument is omitted, so the subtree-fold lenses (effect reach, determinism, cost, fn index, mandatory tag, decl-facts skeleton) COUNT it as code of the declaration it sits in. No lens skips it. - -## Declared frontier: omission at a call or a construction - -A caller may not yet OMIT a defaulted argument, and a construction may not yet omit a defaulted field. Both bind through one binding plan, which still requires every formal, so an omitting call or construction refuses at the application or construct, loudly: -- calls: `v2.std.arrow_signature` `application_binding_plan`, used by infer and eval; -- record construction: `application_binding_plan_over`, used by `v2.compiler.infer` `infer_record_field_binding`. - -**Trigger:** one shared binding plan that reads each formal's binder metadata (via `declared_field_from_edge`) and admits a defaulted formal as optional, together with default materialization, so that eval, and emission where the target admits it, supply the default for the missing slot or field. Until then a default is lowered, judged and carried, and an omitting site refuses. - -## Scope and generics (side-chat review on #12948) - -- A default resolves in the ENCLOSING value scope, with the declaration's type parameters only as its type scope (`v2.compiler.resolve` `resolve_arrow_domain_walk`). An outer value is visible; a sibling parameter is not; a type parameter's spelling does not resolve as a default value. -- A default under a generic declaration (an Arrow or type declaration with type parameters) refuses at the default as `^infer_binder_default_generic_unmodeled` (`v2.compiler.infer` `infer_generic_default_refusal`), until a default is judged with its declaring type parameters in scope. diff --git a/docs/plans/buganizer-visual-contract.md b/docs/plans/buganizer-visual-contract.md deleted file mode 100644 index 2e21590095c..00000000000 --- a/docs/plans/buganizer-visual-contract.md +++ /dev/null @@ -1,50 +0,0 @@ -# Buganizer visual contract (captured 2026-09-22) - -Authority for the issue-tracker frontend's look and feel. Owner's directive: anchor -on Google's Issue Tracker — no homegrown UX. Provenance per section; anything marked -CONVENTION is public product convention pending a saved results page. - -## Captures on disk -- `~/Assigned to me - Issue Tracker.html` (259KB — client-rendered DOM, empty state + advanced search open) -- `~/Assigned to me - Issue Tracker_files/` (assets incl. main CSS bundle `m=c` 6.3MB) - -## Status vocabulary (VERIFIED — the product's own query builder) -open · new · assigned · accepted · closed · fixed · verified · duplicate · inactive · -infeasible · intended behavior · not reproducible · obsolete - -## Sidebar roster (VERIFIED — owner's session) -Assigned to me · Starred by me · Upvoted by me · CC'd to me · Collaborating · -Reported by me · To be verified · Bookmark groups · Saved searches · Hotlists -(Create hotlist) · Create bookmark group · Browse components - -## App shell (VERIFIED) -- Header: product wordmark "Issue Tracker"; search input with "Search help" + "Save"; - "Create Issue" primary button; right side: theme switcher, help, Google Account block. -- Content region: search builder (Match all/any, + OR rows), "Issue search results" heading. -- Empty state: "No issues match your search." + "Learn more about searching" link. - -## Design tokens (VERIFIED — extracted from the CSS bundle) -- Text: `#202124` primary · `#5f6368` secondary · `#3c4043` tertiary -- Borders/dividers: `#dadce0` · `#bdc1c6` · `#e8eaed` -- Action blue: `#1a73e8` · hover `#1967d2` · pressed `#185abc` -- Error/red: `#c5221f` · `#ea4335` -- Backgrounds: `#f1f3f4` · `#f8f9fa` · highlight blue `#e8f0fe` · `#d2e3fc` -- Type: Roboto, Arial, sans-serif · Google Sans, Roboto, Arial, sans-serif (headings) - -## Issue-row anatomy (CONVENTION — pending a saved results page) -Results table columns: ID (linked) · Component · Title · Status · Priority (P0–P4) · -Severity (S0–S4) · Assignee · Stars (+1 count) · Modified. Row is one line, dense, -single-click to detail; star toggles inline. - -## Issue detail anatomy (CONVENTION) -Header: issue id + title + action overflow. Field panel: Status, Priority, Severity, -Assignee, CC, Reporter, Components, Blocking/Blocked-by, Stars/Votes, Created/Modified. -Tabs: Comments (default, the discussion) · History (field-change log) · plus -duplicate/mark actions. Our Work tab maps onto the History half + execution receipts. - -## Mapping notes for the frontend (workflow policy, not extdeps) -- Issue = roadmap node; Component = domain placement (owner/lane); Blocking edges = - dependency edges; Stars/CC/Upvotes = per-user subscription facts (new modeled state). -- Status = our derived present-tense explanation mapped to the product's standings - (open/new/assigned/accepted/fixed/verified), never a flattened label. -- "To be verified" (stock view) = our verification-owed candidates. diff --git a/docs/plans/canonical-content-order-draft.md b/docs/plans/canonical-content-order-draft.md deleted file mode 100644 index 6d83f0a77ec..00000000000 --- a/docs/plans/canonical-content-order-draft.md +++ /dev/null @@ -1,77 +0,0 @@ -# Canonical content order — draft declaration for fit check (node adhoc-77383faf-d07) - -Decision (deep-ferret-305): declare in std now; #12895 portable_value_cmp becomes its interpreter realization. - -## 1. Inhabit std.algebra, no parallel vocabulary -- `type TotalOrder { compare: fn(T, T) -> Ordering }` — result is the existing `std.algebra Ordering`. -- `AlgebraProfile` gains `TotalOrderProfile`; `total_order_templates()` = [compare]. -- `OrderedRing` / `Field` COMPOSE it: `ordered_ring_templates()` / `approximate_field_templates()` - take their `compare` row from `total_order_templates()` instead of restating it (one compare concept). - -## 2. The canonical order is a DERIVED inhabitance, not a function over a dynamic value -No `.dag` carrier for "any value" exists (PortableValue is seed Rust), and typed `.dag` map keys are -one type K. So the authority is a structural derivation rule — the same shape as -`algebra_profile_equality_extensional` — saying how every admissible key type inhabits TotalOrder: -- Bool: false < true. Int: numeric. String: Unicode-scalar lexicographic (= UTF-8 byte order). -- Float: IEEE 754-2019 §5.10 totalOrder, cited through the binary64 authority of #12547 - (lively-newt-480). Realized by `f64::total_cmp`. NOT raw u64 bit compare (that inverts negatives). -- List: lexicographic by element, then length. Map: lexicographic over entries in canonical key order, - comparing (key, value). Set: lexicographic over members in canonical order. -- Record: fields compared in canonical field order. Variant: by arm key, then fields as a record. -- Cross-kind rank (Null < Unit < Bool < Int < Float < Str < List < Map < Set < Record < Variant) is - only reachable in the interpreter's untyped value space; it is the interpreter realization's - extension and is never observable on a well-typed key. - -## 3. Record/variant key: spelling now, declared identity is the climb -Declared identity (owner module + declaration, arm ordinal) is the right key, but it is not carried by -`Value::Variant` / `PortableValue::Variant` today — `gunbc.guarantee_stall.variant_owner_identity_stall` -(NS-0B). Spelling is the only key BOTH realizations carry, and it is total on what they carry. -The residual (two same-spelled owners compare Equal) is exactly that stall's population; the order -switches to declared identity when NS-0B's trigger fires. Stated, not silent. - -## 4. Realizations (the one authority, two realizations) -- Interpreter: portable_value_cmp (#12895) realizes §2; to_string/interpolation of Value::Map render - entries in that order. -- Emitted Rust: map rendering sorts entries by the emitted compare for K (generated from the same - derivation rows), never by im::HashMap iteration. -- Control: a fixture spanning every kind rank (incl. -0.0/+0.0/negative/NaN floats, same-length lists, - nested maps) — interpreter and emitted compare agree; a multi-entry map renders byte-identically - across 2 processes, interpreted and emitted. - -## 5. Gate -v2.lens.determinism classifies every remaining host-order path (iteration, Debug formatting) as -HostUnspecifiedOrder; red on any unclassified one. - -## Fit check (neat-boar-16) — conditions accepted -1. Interpreter and emitted comparisons are both DERIVED from the rule rows (kind rank, float rule, field/arm - key), not two hand comparators; control = same multi-entry map, identical bytes, 2 processes, both. -2. OrderedRing/Field DELETE their restated compare in the same change. -3. Ordering reused; no new carrier. -4. Spelling key is a named stand-in on the carrier, trigger = variant_owner_identity_stall (NS-0B); - spelling is an ordering key only, never identity. OPEN — see below. -5. Float controls: -0/+0, NaN payloads, negatives (totalOrder via #12547). -6. #12895 consumes this authority; sign-off from jolly-boar-500 / sleek-ibex-207 before either lands. - -### Condition 4 is not realizable in the interpreter before NS-0B -Value::Variant / PortableValue::Variant carry no owner identity, so the interpreter cannot detect "two -distinct declarations, same spelling": it can neither refuse nor tiebreak by declaring module. In the -emitted Rust the case cannot arise for a typed key (distinct declarations are distinct Rust types; an -enum key distinguishes them by arm). So the residual is interpreter-only and is exactly the stall's -population. - -### Ruling on condition 4 (neat-boar-16): option A -- Variant/record order = spelling, THEN full payload by the same canonical order. Keys tie only when they - render to identical bytes, so tie order is unobservable in output. -- Control: two same-spelled variants with DIFFERENT payloads render in a fixed order across 2 processes. -- §4b drop row, interpreter path only. Population: variant_owner_identity_stall's two members, plus - same-spelled variant keys from distinct declarations. The interpreter conflates those for ordering AND, - pre-existing (not introduced here), for equality. Trigger: owner identity carried on Value::Variant and - PortableValue::Variant (NS-0B), the capability, not a PR. Emitted Rust: no row (distinct types). - -### jolly-boar-500 sign-off (conditions) -(a) Kind rank derived from the declaration order of the portable value model's variants. PortableValue's - order (Null, Unit, Bool, Int, Float, Str, List, Map, Set, Record, Variant) matches the list exactly; no change. - Correction: Set is OrdSet, so "canonical member order" equals byte-lexicographic member strings. - #12895's Set rule was already conformant; only the float rule diverges. -(b) Differential control: interpreter vs emitted compare agree on a generated corpus covering every kind - pair, NaN, -0.0/+0.0, empty vs non-empty collections, nested maps and sets. diff --git a/docs/plans/carrier-realization-arbiter-repair-design.md b/docs/plans/carrier-realization-arbiter-repair-design.md deleted file mode 100644 index e085e4336b1..00000000000 --- a/docs/plans/carrier-realization-arbiter-repair-design.md +++ /dev/null @@ -1,646 +0,0 @@ -# Repair design: shadow census, then an A/B/C/D counterfactual over the one fork (2026-08-21) - -**Session:** `royal-dove-436`. **Work item:** `node://adhoc-c735d227-60b`. -**Authorized by:** `smart-ram-730`, after the arm census in -[`t2_t3_realization_route_2026-08-21.md`](../probes/t2_t3_realization_route_2026-08-21.md). -**Status: DESIGN. Nothing here is built.** -**This revision supersedes an earlier one in the same PR** that proposed deleting the short-circuit -as the first experiment. That is still the right eventual repair and it is the wrong first move; §2 -says why, and the reason is not caution — it is that a deletion cannot measure the quantity the -decision turns on. - -## What the measurement forces, before any design choice - -| arm | T2 | T3 | -|---|---:|---:| -| **A** — generic carrier signature (`list_append`, `length`, `is_empty`) | **25** | 0 | -| **B** — declared-structure constructor | 4 | 17 | -| **C** — direct carrier-to-carrier assignment | 3 | 0 | -| UNALIGNED — not attributed | 2 | 1 | - -Arm A makes this a forced conclusion rather than a menu: - -> `list_append(left: FreeMonoid, right: FreeMonoid)` is emitted **once**, generically, so it -> has exactly **one** host parameter type. Its `.dag` callers pass values declared `String` — the same -> modeled type, by `std.string_type` `String = FreeMonoid`. With two host realizations the -> generic function can only be one of them, and every call from the other is an E0308 **no call-site -> edit can remove**. - -So the landed shape is **one exact identity query → one base target realization → position-specific -reference-layer rendering**, and two candidate repairs are already refuted: - -- **Constructor-side only** closes arm B — 21 of 52 — and leaves arm A's 25, the largest arm. -- **Anything relying on monomorphization** cannot help: the type renderer's test is - `rust_host_text_carrier_elem_name(n) == "Char"`, a syntactic read of the authored element spelling - decided *before* instantiation. A generic `T` is never spelled `Char`. - -## The authority exists, is unreachable, and is MIS-KEYED - -*An earlier revision of this heading said "is correct". That was wrong, and wrong in the -direction that matters: the authority's **logic** was checked and its **key** was not. A total -function over a wrong key does not fail — it returns a confidently wrong answer. The key defect -is `type_reference_decl_file`'s fallback arm, traced in the section beginning "the identity key -is a LOCATION" below; the retraction is restated here because a reader who stops at this heading -must not leave with the superseded claim.* - -`type_realization_decision` **does have consumers** — `v1.compiler.coercion` `lookup_checkpoint` is a -thin derivation of it for every `decl_file != ""` caller. (An earlier revision of this lane's PR body -claimed zero consumers; that was an error, from grepping the type name rather than the function.) - -`structural_declaration_modules_for("String")` = `["src/v2/std/text.dag", "dag/std/string_type.dag"]`, -so the strict query **would** refuse the native spelling. But six type renderers — `render_rust_type`, `render_rust_type_without_applied_binding`, -`render_rust_applied_type`, `render_rust_type_with_applied_binding`, `render_rust_decl_type`, -`render_rust_fn_sig_type` — each **return on their first line** via -`is_host_text_carrier_type` → `"String"`, unconditional on `decl_file`. **No `String`-spelled -reference in type position can reach the authority.** An unreachable wall, not a missing one (DESIGN -§6 coverage-by-illusion). Established from those six renderers' control flow — each call verified to be the statement -immediately following its own `fn` declaration, not merely early in the body; **not** established by -a discriminating execution. (This count read *five* until 2026-08-21: -`render_rust_type_with_applied_binding` was missed when the first sweep's grep output was truncated -before reaching it. The full derivation is in gunbc#8805; the correction is recorded rather than -silently applied because the earlier count was relayed upward and a review restated it as fact.) - -## §2 — Why the experiment is not "delete the short-circuit" - -Two quantities are being confused, and only one is answerable without emitting: - -- **Decision divergence** — how many occurrences get a different answer from the short-circuit than - from the authority. Statically computable. -- **Diagnostic conversion** — which rustc failures appear once the authority controls emitted Rust. - Only observable by emitting and compiling. - -A deletion produces the second with no record of the first, so every downstream question ("was this -error caused by the change, or exposed by it?") becomes narration. The census below produces the -first *before* any byte moves, which is what makes the second adjudicable. - -## Step 1 — Shadow census, no output change - -At every affected type and value renderer, compute **both** answers and keep emitting the legacy one. -Write a **sidecar receipt** — explicitly **not** comments into the emitted Rust (an earlier -suggestion, withdrawn: DESIGN §4c makes an annotation unreadable by any `Accepted` program, so a -receipt emitted as commentary is a receipt no consumer can join on). - -One row per occurrence, keyed by: source `DeclarationRef`; source occurrence / use site; emitted file -+ enclosing declaration; **position kind** (declaration / fn signature / value constructor / applied -type argument); legacy base realization; authority decision; reference-layer decision; identity -available? - -Two constraints that are load-bearing, not stylistic: - -1. **Query the strict `type_realization_decision`, never `lookup_checkpoint`.** `lookup_checkpoint` - deliberately preserves the bare-name fallback when `decl_file == ""`, so censusing through it - measures the bypass against itself. -2. **Outcomes are `Agrees` / `DivergesWithExactIdentity` / `IdentityUnavailable`, and - `IdentityUnavailable` is never folded into structural rendering.** `Unrealized` (a known - structural declaration) and `Refused` (identity not supplied) are different answers. Merging them - reproduces the bypass under a newer name — the exact defect this work exists to delete. - -### Step 1 shape: RULED — (b), a separate substrate walk, plus a mandatory calibration control - -Reconnaissance surfaced a fork the brief did not settle, and `smart-ram-730` ruled it. Both the fork -and the ruling are recorded, because the reasoning constrains how step 1 may report. - -**What was never the obstacle.** Both inputs the census needs are pure and exist today: -`v1.compiler.coercion` `type_realization_decision` is importable (three modules already import from -that module), and `v1.compiler.05_emit_rust` `is_host_text_carrier_type` is pure — but **private to -that module**, imported by nothing. - -**The fork was where the census runs.** - -- **(a) Inside the emitter** — the brief's literal shape: both answers at each of the six renderers, - legacy one emitted, receipt routed out. The v1 Rust emit path's file writing is host-driven, so - routing a receipt out means a **second output channel through a load-bearing file**. -- **(b) A separate substrate walk** — a `v2.workflow` census module over the same assembled closure, - a probe entry, and a thin host driver: the shape `v2.workflow.realization_sweep` and - `realization_sweep_survey.rs` already establish, substrate analysing and host transporting. - -**Ruled: (b).** On the merits: (a) obliges step 1 to prove its own inertness before it can report -anything about its subject, and *an instrument that must first prove it did not disturb the thing it -measures is a worse instrument than one that cannot disturb it*. Under (b), acceptance condition 8 — -bytes outside the divergence population unchanged — is true **by construction** for step 1 rather -than something step 1 establishes. - -**The objection against (b), and what neutralises it.** A parallel walk can drift from the control -flow it claims to describe, and *this lane exists because a parallel answer path was authoritative in -the wrong place*. That suspicion is correct, but the drift risk is not uniform across the two things -(b) borrows, and separating them is what decides it: - -- **The predicate cannot drift**, because `is_host_text_carrier_type` is **imported, not - re-derived**. A re-derived copy would be the same §3 fork again and is an immediate refusal; - importing it is the whole reason (b) is admissible. -- **The traversal can drift.** The walk may visit occurrences the emitter never renders, or miss ones - it does. That is the real exposure, and it is not hypothetical: a subtly wrong occurrence set makes - every count wrong while looking perfectly well-formed. - -**So the calibration control is a precondition of the census being reportable, not a nice-to-have:** - -> The walk must **reproduce the known 25 as its diagnostic-producing divergence subset.** Partition -> the walk's `DivergesWithExactIdentity` rows by whether the occurrence currently produces a rustc -> diagnostic; that subset must equal the 25 sites of arm A — joined by source declaration + enclosing -> emitted declaration + operation, **never by line**. **If it does not, the census is WRONG and its -> divergence count must not be published. Report the mismatch instead.** - -This converts the drift objection from an argument into a measurement, which is the only way to -settle it. Note what it does **not** require: the walk need not *explain* the 25, only **find** them. -It composes with the cross-tab already committed to — the calibration is one cell of it, and the -interesting cell (`DivergesWithExactIdentity` at zero diagnostics) is trustworthy only once the -calibrated cell checks out. - -### Correction to (b) as ruled: the v2 assembly is the WRONG TREE, so (b) is v1-side - -Found while building it, and recorded because it falsifies a premise both the recommendation and the -ruling rested on — not the ruling's *reasoning*, which survives intact, but the concrete artifact it -named. - -**(b) was described as "a `v2.workflow` census module over the same assembled closure", reusing the -`v2.workflow.realization_sweep` pattern. That is not implementable as stated.** That pattern's -`assemble_program_from_ingest` returns an `Outcome` over **`v2.std.node.Node`**, while -`v1.compiler.05_emit_rust` — the module whose decisions the census exists to measure — operates on -**`v1.std.core.Node`**, imported by name at the top of that file. They are different types. A walk -over the v2 assembly would be measuring a different tree from the one the emitter renders, which is -the traversal-drift failure the calibration control exists to catch, built in at the foundation. - -**(b′), the corrected shape, is v1-side and is implementable today.** `v1.compiler.compile` -`front_end_sources(sources: List) -> FrontendResult` is a public entry returning -`FrontendResult { graph: ModuleGraph?, newline_indices, intern_table, … }` — the v1 tree the emitter -consumes, plus the `newline_indices` that `authored_name_at` needs. So: - -1. the host supplies the closure's `SourceFile` rows — **transport only**, the same role - `realization_sweep_survey.rs` plays for its probe; -2. the census module calls `front_end_sources` and walks each module's items for type-reference - occurrences; -3. per occurrence: `authored_name_at`, `v1.compiler.coercion` `type_reference_decl_file`, position - kind; -4. legacy answer from the **imported** `is_host_text_carrier_type`; authority answer from the strict - `type_realization_decision`; -5. rows out as TSV. - -**Everything the ruling actually reasoned about survives this correction**, which is why it is a -correction and not a re-open: the predicate is still imported rather than re-derived, no output -channel is added to the emit path, acceptance condition 8 is still true by construction for step 1, -and the calibration control is unchanged and now matters more — a v1-side walk *can* still drift from -the emitter's control flow, and reproducing the 25 is still what settles it. - -**REFUTED BY CI, 2026-08-21 — the census module DOES have to live in `src/v1/`.** This paragraph -claimed the module need not, on the grounds that import direction is not a layer rule (DESIGN §3 -makes acyclicity the only structural law and folders a browsing convention), and concluded that this -"removes the v1-growth question from step 1 entirely". - -**The premise is true and the conclusion is false.** Import direction is indeed not a layer rule, but -that is not the constraint that binds. The required floor discovers subjects by **path**, over the -roots `dag` and `src/v2` (`witness_layer_roots`), and resolves that closure as one program — -`modules_resolved=3820`. A module authored at `src/v2/workflow/` is therefore swept into discovery -because of **where it sits**, not because of what imports it, and its `v1.*` imports are unresolvable -inside that envelope. Measured, on PR #8816: - -``` -src/v2/workflow/carrier_realization_census.dag:3:1: error: unresolved import: - module 'v1.compiler.compile' not found -required-ci: floor refused: subject=7b5536161546187e modules_resolved=3820 modules_excluded=4 -``` - -This is the same structural unresolvability `dag/test/claim/checkpoint_identity_keying_witness_test.dag` -already documents about itself; I read that precedent and still authored outside `src/v1/`. - -The module now lives at `src/v1/tests/claim/carrier_realization_census.dag` and produces -**byte-identical** results there, so nothing measured is invalidated — only this placement claim is. - -**So the v1-growth question is REOPENED, and it is an admission to state rather than one to assume.** -Step 1 adds one module to the seed tree. Under DESIGN §3's v1 maintenance standing the admission test -is PURPOSE — *"anything in support of v2 self host is safe"* — and the E0308 board is that program, -so it reads as admissible. It is recorded here as an open admission, not as a settled one: this -document does not get to grant itself the exception, and the paragraph it replaces is exactly what -happens when a design talks a question out of existence instead of answering it. - -### There is no visibility change: (b′) is zero-touch on `05_emit_rust` after all - -This heading previously read *"(b) is NOT zero-touch on `05_emit_rust`, stated plainly"* and -disclosed a visibility change on `is_host_text_carrier_type` as the one seed edit step 1 needs. -**That disclosure was wrong, and it is corrected in the direction of touching less, which is exactly -the direction a disclosure must not be left wrong in.** - -The `.dag` language has **no visibility syntax at all** — corpus-wide there is no `export`, `pub` or -`private` form; every top-level `fn` in a module is importable by name. `is_host_text_carrier_type` -is an ordinary top-level `fn`, exactly like `rust_scalar_checkpoint_render_base` — which -`src/v1/tests/claim/checkpoint_identity_keying_witness_test.dag` **already imports from this same -module** today. `v1.compiler.05_emit_rust` is likewise already imported by `v1.compiler.compile` and -`v1.compiler.stage0_crates`. - -So **step 1 requires no edit to `v1.compiler.05_emit_rust`, and no edit to the v1 seed at all.** The -predicate is imported, not re-derived — the property the ruling actually depended on — and nothing is -widened to achieve it. - -**The lifetime question dissolves with the change that raised it.** It asked whether a widened -surface survives the repair or is scoped to the census. There is no widened surface: the predicate's -importability is a property of the language, not a grant this lane made. If the terminal repair -deletes the spelling-keyed short-circuits and the predicate with them, the census's import fails -loudly at that point — which is the correct coupling, and is a live dependent rather than residue. -Recorded rather than dropped, because an unasked question and a dissolved one look identical in six -weeks. - -### The census's executing consumer is its own driver, because the batch it would have used is gone - -`src/v1/tests/claim/checkpoint_identity_keying_witness_test.dag` documents its own execution as -`gunbc.ci_layer_roots` `v1_claim_scoped_witness_entries` → `v1_claim_scoped_witness_batch`, "whose -source-root envelope is dag plus src/v1". **That batch is deleted** — `ci_layer_roots` -`v1_claim_scoped_witness_batch_deleted_note` records the deletion (2026-08-15), and -`witness_fold_src_v1_coverage_gap_note` carries the resulting declared coverage gap. - -So a census module cannot inherit that enrollment, and step 1 does not try to. Its executing consumer -is **its own host driver**, the "local recipe" standing `realization_sweep_survey.rs` already -occupies for its probe — the driver supplies the `dag` + `src/v1` source-root envelope for the census -module itself, and separately supplies the subject closure's sources as `SourceFile` **data**. Those -are two different roles for two different populations and the design keeps them apart deliberately: -confusing them is how a census ends up measuring its own pool instead of its subject. - -## Step 2 — Four counterfactual crates over one pinned tree - -Same toolchain, same assembly path, same manifest, same source population, separate fresh output -directories: - -| | type position (decl + fn sig) | value position | -|---|---|---| -| **A** baseline | legacy | legacy | -| **B** | strict | legacy | -| **C** | legacy | strict | -| **D** | strict | strict | - -A factorial over exactly the fork the census measured. It answers what no total and no single -treatment can: whether arm A's 25 are controlled by the **type** short-circuit, whether arm B's 21 -are controlled by the **value** short-circuit, and whether converging both produces agreement or -merely relocates the disagreement. - -**Publish a site-conversion ledger, not four totals:** baseline site, treatment site, source -declaration, old expected/found, new expected/found, old category, new category. **Join by source -declaration + enclosing emitted declaration + operation — never by line.** One error block can split -into several sites, and lines move. - -**Do not land the dual renderer.** It is experimental quarry: leaving both answers in production -creates the second authority this work exists to delete (DESIGN §3). - -### Ledger requirement: diff the full warning histogram — and the probe's column CANNOT carry it - -**The requirement, and the near-miss that produced it.** `smart-ram-730`, from another lane: a repair -keyed on the emitted pattern string moved its board 31 → 32 — noise — while underneath it -`unreachable_pattern:6` appeared. **Those are errors here, not lints.** Two arms of the same variant -emitted different patterns, the first lost its guard and shadowed the second into dead code at six -sites. The headline moved by one; six arms died. A realization change is exactly the edit that can -make one arm subsume another: if a carrier's base realization changes, arms distinguishable by its -host type may cease to be. - -**An earlier revision of this section said to satisfy that by diffing the probe's histogram column. -That check cannot work, and the defect is in the instrument, not the diff.** Verified first-hand in -`docs/probes/curated_cargo_probe_one.sh`: `ERROR_HISTOGRAM` greps `'^error\[E[0-9]+\]'` and -`HISTOGRAM_SUM` greps `'^error(\[E[0-9]+\])?:'`, over a plain `cargo build --release --lib` with no -`--message-format=json`. `warning: unreachable pattern` matches **neither**. The class can never -appear in that column. - -**So absence of that row is blindness, not zero.** Reporting the class clean from that column would -not be a measurement — it would be an instrument that cannot express the class, which is the -absorbing fallback (DESIGN §5) relocated into the measuring apparatus. (Older readings that *did* -carry the class legitimately came from a `rustc --message-format=json` instrument, which includes -lints. Same question, different instrument, different answer.) - -**What the A/B/C/D ledger does instead:** read the kept cargo log directly and diff the **full sorted -`warning:` histogram** across all four crates, in one dispatch covering every arm. - -**And it carries the honest bound, because at a refusing baseline nobody can do better:** the emitted -crate fails to build, so rustc stops before typechecking most items and never runs reachability on -them. A zero from that log means *"none among what rustc reached, and unchanged between arms"* — it -does **not** mean the crate has none. That bound is published with the number, not left to a reader. - -**NARROWING, verified first-hand after `bright-dove-741` refuted the general form.** The emitted -crate carries `#![deny(unreachable_patterns)]` — confirmed in `v1.compiler.stage0_crates`'s emitted -crate attributes and in the seed's own `lib.rs`. A **denied** lint renders as `error: unreachable -pattern`, which **does** match the probe's uncoded-suffix grep. So the blindness above is real for -**warning-form** lints and **not** for denied ones, and `bright-dove-741` holds the positive control -in band: 6-then-0 across their own defective and shipped runs, on this instrument and entry. - -**So the standing rule is narrower than first stated, and this is the corrected form: whether an -instrument can emit a class is a property of the EMITTED ARTIFACT's lint attributes, answered per -artifact — not a fixed property of the probe.** The cheap way to answer it is to find a run where -that class was nonzero; a class no run has ever produced is a class you cannot report clean. The -log-level warning reading above stays the right mechanism for warning-form classes; the rule around -it is what changes. - -## Step 3 — Pre-registered acceptance - -A rising rustc total is neither proof of progress nor proof of failure. The repair is semantically -correct only if **all** hold: - -1. Every changed occurrence was already in the shadow divergence population. -2. The selected base equals `type_realization_decision` for the exact declaration. -3. Type and value positions consume the **same** base answer for one declaration. -4. The reference layer stays a **separate axis**; `Rc` is never inferred from base spelling. -5. Same spelling + different declaration identity can still give different answers. -6. Unavailable identity **refuses** rather than taking the legacy bare-name answer. -7. A generic `FreeMonoid` is not classified as text because some instantiation later uses `Char`. -8. **Emitted bytes outside the divergence population remain byte-identical.** - -Condition 8 is the discriminator this lane did not previously have, and it is why it is stated as a -test rather than a hope: **a new error in an emitted file whose bytes did not change is not "debt -exposed by the repair" — it is contamination or an unrelated tree delta.** It converts a vague worry -into a mechanical check. - -Then classify every treatment-only error as `ExpectedSuccessor` / `UnrelatedRegression` / -`Unclassified`. `ExpectedSuccessor` requires a **carried** causal relation, not a narrated one: it -occurs at the changed occurrence or a direct use of the changed declaration; its expected/found -contains the authority-selected realization or its independently derived reference layer; the -baseline emitted a different type at that exact semantic position; and the obligation could not have -arisen until that representation was selected. - -**Acceptance: wrong-authority decisions = 0 over the scoped population, unrelated regressions = 0, -unclassified = 0.** Only then does the raw total speak to convergence at all. - -## The Root B precedent, used for what it actually establishes - -Root B's forced `HostNative` changed **two independent axes at once** — primitive realization *and* -import/use-line synthesis. So its 693 → 807 established neither "the rise is hidden debt" nor the -opposite; it established that the repr caused its target family, and that the switch was not a valid -repair because it carried a fused unrelated decision. **Use Root B as precedent for isolating axes — -which is exactly what A/B/C/D does — never as precedent for admitting an increase.** This experiment -can be cleaner than its precedent: the identity authority already exists, and the short-circuit -varies without touching import policy, closure selection, or Cargo config. - -## Instrument lesson this design adopts for its own ledgers - -`smart-ram-730`, on the RT-builtin misattribution: **a classifier that folds its discriminating input -into its derived label destroys the ability to re-adjudicate later**, and the cost lands on whoever -needs a different partition than the one that was authored. The 2026-08-21 partition consumed each -block's `note: function defined here` into its `root` column, so 5 sites coded `RT-builtin` cannot be -re-split from the TSV — the callee note is a good **field** and was a bad **label**. - -This design's ledgers therefore carry the raw discriminating inputs beside every derived label, as -`routes.tsv` and `arbiter_arms.tsv` already do (raw `expected`/`found` beside `expected_route` / -`found_route` / `carrier` / `verdict`). Concretely, the step-2 ledger carries position kind, identity -availability, and both decisions as **fields**, never only the conversion verdict computed from them. - -## Population, pinned so it cannot drift - -The scoped population is the **52 shared-root sites** in `arbiter_arms.tsv`, and the first deliverable -is the A/B/C/D conversion receipt over **arm A's 25**: largest measured arm, constructor-only already -falsified against it, mechanism located, authority present, and its failure is that the authority is -*unreachable* rather than unspecified. - -The 5 `RT-builtin` sites in `v2_std_compilers_target_model.rs` (lines 6183 ×2, 6198, 6242, 6267) are -**outside** this population and stay outside it. Two of them plausibly belong to this root and three -to the cross-realization signature-drift root, but this lane's spelling-keyed classifier cannot -discriminate `HashMap`-from-inhabitant-row from `HashMap`-from-`v1_rt::lookup`-signature, so the -adjudication waits for the callee-note field rather than being made by an instrument that cannot make -it. Two sites either way do not touch the 25. - -## What this design does not establish - -- **It is not costed.** No estimate is offered, because the A/B/C/D receipt is what produces the - blast-radius number any estimate would need. -- **It does not claim the arms share one fix.** B and C are treatments precisely so that question is - measured rather than assumed. -- **It does not pick structural vs native `String`.** That is a policy about the seed's own - representation; the experiment produces the data it should be decided on, and - `checkpoint_table_bypasses_identity_note` records what picking a direction without that data cost - last time (the over-broad `Bool` row: 5 fixture refusals plus `required-regen` drift). -- **It does not touch the v1 freeze question.** Every step edits the v1 seed emitter, admissible - under the DESIGN §3 purpose test only insofar as it serves the v2 self-host program — which the - E0308 board is, but the admission is the operator's, not this document's. - -### The census is permanent, and it is NOT enrolled — declared, not solved - -`smart-ram-730` asked whether the census is a permanent lens or a one-measurement instrument, -because the second is a scaffold and DESIGN §6 lets no scaffold land on author declaration alone. - -**It is permanent, by design and not by relabelling.** The question it answers — *does the legacy -short-circuit still diverge from the identity authority at this occurrence* — does not retire when -T2/T3 is repaired; it **inverts**. Today the divergence set is the defect population. After the -repair, the same walk over the same subject must produce an **empty** divergence set, and any row -reappearing is the regression. That is DESIGN §4b(4) exactly: a climb deletes the redundant -production machinery (the short-circuit in the six renderers) and **keeps the discriminating -evidence enrolled**, because deleting the evidence alongside the machinery recreates -specification-without-execution one rung up. The census carries both halves — the diverging rows are -the discriminating RED, the `Agrees` rows the accepted positive control. - -So no scaffold admission is owed and no dissolution trigger is owed. - -**But "permanent" must not be allowed to do the work of "enrolled", and today it cannot.** CI's only -invocation is `claim_executor --required-ci --source-root dag --source-root src/v2`. `src/v1` is not -a source root; it is reached only by the `.dag` **parse sweep** (`v1_src_dag_parse`), which parses -and does not evaluate. A module at `src/v1/tests/claim/` is therefore **parsed and never executed** -by the required run. Claiming it as an enrolled regression control would be precisely the tier -DESIGN §6 names — the machinery exists and nothing gates on it — and an inert lens is itself a lie. - -Stated at the honest grain instead: - -| | | -|---|---| -| **rung now** | *mitigatable* — a hand-invoked probe; its evidence is real when run, and runs only when a human runs it | -| **ceiling** | *mechanically preventable* — a witness the required floor executes on every PR | -| **next-rung trigger** | an execution route from the required run into `src/v1`, **or** relocation to a home the floor already executes | - -Which of those two is right is not decided here, and the second one is now known to be **closed**: -relocating out of `src/v1` is exactly what CI refused, because the `v1.*` imports are unresolvable -inside the `dag` + `src/v2` envelope. So the trigger is the first: **`src/v1` becomes an executed -root, or an execution route reaches it.** That is a CI-scope change affecting every lane, it is not -this lane's to land, and it is owned by `smart-ram-730`. - -**One connection is withdrawn as wrong, and it is recorded here even though it never reached a -shipped revision of this document** — it was the reasoning behind the trigger, so a reader who -re-derives the trigger would re-derive the error. Replying to the permanence ruling I named the -seven-witnesses-in-a-declined-home lane (`royal-tern-339`) as the lane whose answer this trigger -would consume. It is not, and the difference is mechanism rather than degree: those witnesses live -inside `src/v2`, which **is** a source root, so they are **discovered and then declined by home -policy**, and their fix is a move between homes inside an already-discovered root. This census is -never discovered at all. Same symptom, different mechanism — the surface-versus-mechanism error this -whole lane exists to measure, committed by me at the level of coordination, and caught by -`smart-ram-730` only because the connection was stated out loud rather than assumed silently. - -For the record, and because it re-sizes the trigger rather than merely relocating it: the objection -to adding `src/v1` as a root is **not** roster blast radius. The whole `src/v1` tree currently holds -14 test fns in one file — the checkpoint-identity assertions that an emitter authority note cites -**by name** and that have never executed — and this census would make it two files. The real open -question is **resolution**, not population: the floor resolves its roots as one program -(`modules_resolved=3820`), so whether `v1` and `v2` module namespaces collide when unified is the -thing to measure. That measurement is a one-command experiment and it belongs to the owner above. - -## THE REPAIR IN THIS DOCUMENT DOES NOT FIX ARM A — the identity key is a location (2026-08-21) - -Measured, then confirmed from the code. Both halves are below, and the second one invalidates the -design above rather than qualifying it. - -### The measurement: `decl_file` is where the reference SITS, not where the type is DECLARED - -The calibration control ran over the 22-module import closure of `src/v2/compiler/01_tokenize.dag` — -the module that emits `src/v2_compiler_tokenize.rs`, where all 25 arm-A sites live. It **failed**, and -the failure is the finding: - -``` -1142 rows: 1134 Agrees, 8 DivergesWithExactIdentity, 0 IdentityUnavailable -arm x outcome: 1036 fallback|Agrees 98 resolved|Agrees 8 fallback|Diverges -``` - -Every one of the **111 `String` references took the fallback arm**; the resolved arm fired zero times -for this carrier. `decl_file` came back as the referencing module's own file in every case — -`std.types` → `dag/std/types.dag`, `v2.compiler.tokenize` → `src/v2/compiler/01_tokenize.dag`, -`v2.std.text` → `src/v2/std/text.dag`. The value `src/v2/std/text.dag` is produced **only** from -inside `v2.std.text` itself; no cross-module reference to `String` ever yields it. - -So the 8 divergences are **true by accident**: they are exactly the references that happen to sit -inside their own declaring module, where "the file I am in" and "the file it is declared in" are the -same string. - -This also dissolves the `decl_file` partition recorded earlier in this lane (41 `types.dag` / -6 `algebra.dag` / 4 `string_type.dag`, "zero counterexamples"). That was never a partition by -declaring module — it was a histogram of which file each occurrence sits in. Its zero counterexamples -were **structural**: a column that is a function of the row's own location cannot produce one. - -### The mechanism, named as the class DESIGN already names - -`v1.compiler.coercion` `type_reference_decl_file` projects the resolved node bound to the reference -and otherwise **falls back to the reference node's own `ident_span`**. Its authority note describes -that arm as firing *"when the reference IS the declaration"* — but the arm also fires whenever -inference is simply unavailable, with no way to tell the two apart from outside. That is: - -- **state-space conflation** (§5) — *the reference is its own declaration* and *inference was - unavailable* are different states with different remedies, collapsed into one arm; -- **the absorbing fallback** (§5) — a failure arm that **widens instead of refusing**. The note itself - warns that an empty identity yields no realization and silently renders structurally; the fallback - avoids the empty string by substituting a **wrong non-empty one**, which is strictly worse — it is - undetectable rather than merely absent, and its deficit frequency is zeroed by construction. - -Splitting that arm into two named outcomes is a **prerequisite** for the census, not a cleanup after -it: until they are split, 1036 of 1142 rows are `fallback` and cannot be scored correct or wrong. - -### Why this invalidates the repair above - -`type_realization_decision` **takes `decl_file` as a parameter** — - -``` -fn type_realization_decision(target: RenderTarget, dag_name: String, decl_file: String) -``` - -— and derives nothing declaration-shaped internally. Every production caller supplies it identically: -`v1.compiler.emit` and `v1.compiler.emit_rust` both pass `type_reference_decl_file(n: n)`, and -`lookup_checkpoint` is a thin derivation of the same call, so it inherits the key too. - -**Therefore routing the six renderers through the authority changes which function computes the answer -and leaves the basis of the answer identical.** The authority is not a second opinion about identity; -it is the same location-valued key wearing a better name. Arm A survives the repair with its 25 sites -intact, and an A/B counterfactual would return `converted=0` for a reason unrelated to whether routing -was the right idea. - -**One retraction this forces, on the finding that opened this lane.** It was reported that -`type_realization_decision` is *"present, correct, and unreachable"* for the text carrier in type -position. *Present* and *unreachable* hold. **Correct does not** — its logic was checked and its key -never was. A total function over a wrong key is not a correct authority that happens to be bypassed; -reaching it would have produced a confidently wrong answer instead of no answer. That word is what -made routing look sufficient, so it is retracted here rather than left standing as the premise. - -### Sequencing, replacing the step order above - -1. Split the fallback arm into two named outcomes — prerequisite. -2. Establish **why** `n.inferred` is not `Resolved` at these reference sites. This is likely the defect - itself rather than something to route around, and the fallback is what has been hiding it. -3. Only then ask whether routing to the authority is the right repair, on a key that means something. - -No production change is made on any of this yet. The A/B/C/D counterfactual crates are **not** built -on the current design, and no divergence count is published. - -**One thing deliberately not claimed:** `v2.compiler.tokenize` has exactly 25 `String` rows and there -are exactly 25 arm-A sites. The relation should be many-to-one, so equal counts are as consistent with -coincidence as with a join. Upgrading it requires a join on the **same occurrences**, not on both sets -having 25 members. - -## THE MEASUREMENT ABOVE IS PRE-RECONCILE — the subject claim is withdrawn (2026-08-21) - -Everything above about `0 of 111` is a real measurement of the **wrong phase**, and the sentence that -made it look like a production finding is withdrawn here rather than amended in place. - -**Verified first-hand in `v1.compiler.compile` `compile_to_resolved_with_options`:** - -``` -let frontend = front_end_sources(sources: sources) <- what the census walks -let norm = normalize_graph(graph: graph, ...) -let typed = reconcile_with_census_extra(graph: norm.graph, ...) -ResolvedPipelineResult { graph: typed, ... } -``` - -and `emit_artifact(typed: ResolvedGraph, artifact: Artifact)` consumes the **typed** graph. So -`front_end_sources` returns the **input** to normalization and inference — not the output. - -| | | -|---|---| -| **what `0 of 111` establishes** | type references in the **pre-reconcile** tree do not carry the inference answer that reconcile is responsible for producing | -| **what it does NOT establish** | that production emission takes the fallback at those sites | - -The second sentence was published in this document and in this lane's PR body. **Withdrawn.** - -This is the same shape as the other errors recorded in this lane: the instrument ran, the number is -correct, and it answers a *neighbouring* question. The earlier (b′) correction moved the census onto -`front_end_sources` because the v2 assembly was the wrong **tree** — it fixed the tree and missed the -**phase**. - -**What survives unconditionally:** the helper's shape defect. `type_reference_decl_file` cannot -distinguish *this node IS the declaration* from *this is a reference whose declaration was not -recovered here*, and in the second case returns the file containing the reference. That is -state-space conflation on any tree. What is **unmeasured** is whether the production typed graph -reaches the bad arm for the T2 population. The three-case cross-module trace recorded above is built -on the withdrawn premise and is likewise unconfirmed for production. - -### The corrected grain - -`compile_to_resolved` → `ResolvedGraph.modules` → `TypedModule.items` + `TypedModule.type_env`. - -Every `TypedModule` carries both, which is exactly the pair the census needs, and `05_emit_rust.dag` -**already imports `lookup_type_for`** — so this is an existing dependency, not a new abstraction. The -renderers visibly receive `env` while the current identity helper ignores it. - -At every occurrence, record all three answers **independently**, never letting one substitute for -another before comparison: - -| column | source | -|---|---| -| `inferred_declaration` | `n.inferred` → `Resolved` node, if present | -| `environment_declaration` | `lookup_type_for(m.type_env, n)`, if present | -| `legacy_file_key` | `type_reference_decl_file(n)` | - -Two constraints on the carrier. `ReferenceIsDeclaration` may be produced **only** when the resolved -declaration is demonstrably the same declaration node — never merely because inference was absent, -since that is the conflation under measurement and the instrument must not reproduce it. And the raw -reference-file fallback appears only as `LegacyFallbackUsed { reference_file }`, an observation of -current behaviour; it is **never** labelled declaration identity. - -### The four outcomes, fixed before the data - -- **A — inference resolves and agrees with the TypeEnv.** The identity key is *not* the active T2 - root; the first-line text shortcut is the primary defect, and routing proceeds after recalibration - on the typed tree. -- **B — inference absent but `lookup_type_for` resolves correctly.** The defect is narrower than - "inference is broken": `type_reference_decl_file` reads the **wrong carrier** for declaration - identity. Repair the helper to consume the TypeEnv or an already-resolved declaration; do **not** - modify global inference to populate a redundant field. Most plausible, given the renderers already - carry `env`. -- **C — both fail.** The binding/inference defect is genuinely upstream and becomes the primary - front; T2 is downstream. Refusal stays explicit — no falling back to reference file or bare - spelling. -- **D — they resolve different declarations.** Strongest possible finding: two resolution authorities - disagreeing inside the production typed tree. Realization work stops and that fork closes first. - -### T3 is not wholly downstream of an identity repair - -Recorded as a correction to this document's own framing. The collection paths are decided by separate -mechanisms — `rust_seed_host_container_base`, `node_is_keyed_collection`, `node_is_set_collection`, -`emit_map_type`, element-collection rendering, declared-structure record construction — which select -`Vec` / `HashMap` / `BTreeSet` / `PartialFunction` / `PointwisePower` independently of the checkpoint -decision. A correct declaration key is **necessary infrastructure** for a unified realization -authority, but will not by itself make `HashMap`↔`PartialFunction` or `BTreeSet`↔`PointwisePower` -converge. - -So **"52 sites are one root" reads from here on as one _non-confluence class_** — target -representation selected by position — **and not as one implementation repair.** Likely decomposition: -(I) exact reference→declaration identity, (II) T2 text-carrier base realization, (III) T3 parametric -collection realization, (IV) shared reference-layer projection. - -### What is blocked and what is not - -The **production routing edit** is blocked; the lane is not. Proceeding: refresh the 52-site -population against the current 399-error board, map each site to its type/value rendering consumer, -separate T2 from T3, and prepare the counterfactual comparison. **The six shortcuts are not deleted -until the identity census has a trustworthy key.** diff --git a/docs/plans/census-part-2-inference-design.md b/docs/plans/census-part-2-inference-design.md deleted file mode 100644 index 2371f9aec71..00000000000 --- a/docs/plans/census-part-2-inference-design.md +++ /dev/null @@ -1,213 +0,0 @@ -# Census part 2 — inference (and emit) census below resolve - -**Status: design only.** The build waits until census part 1 lane A lands: #13026, -deep-badger-684, reconciled here against its final head `9b7d2adc5c`. This doc does not mint a row -format. A's member already carries a stage, so part 2 **adds two arms to A's stage coproduct** and -adds no new field. Items marked **[lane C]** are a declared frontier, because lane C has not been -dispatched (§8). - -Governing sections: DESIGN §3 (one row format, not two), §3c (every declaration added here names its -consumer), §4b (rung honesty at a declared subject grain), §5 (a refusal is a row, never a widened -pass), §6 (name the instrument; never transcribe its output). - -## 1. What part 1 already establishes (reused, not restated) - -- **The population authority.** `v2.compiler.compile` `native_census_modules` maps an ingest to - `NativeCensusModule { module, path }`, one row per file that declares a module line. -- **The resolve decision.** `native_census_module_resolution` returns one of - `NativeCensusModuleFileRefused | NativeCensusModuleResolveRefused { first, rest, observation } | - NativeCensusModuleResolved`. The lane and the census both read this one decision. -- **The residual grain.** `NativeCensusResidualRow { module, path, chain }` has one row per failure - chain, so one module can produce several rows. The fatal is *derived* with - `v2.std.diagnostic diagnostics_fatal(chain)` and is never stored. -- **The file-refusal row.** `v2.compiler.native_test_vocabulary` `NativeTestFileRefusal { path, - module, head_reason, fatal_reason }`. The rendering in swift-lynx-592's #13005 groups by **fatal** - and treats **head** as an advisory field, because `parse_grammar_choice_overlap_residue` heads - every file. -- **Locus.** swift-lynx-592's path renders a chain's located fatal through - `lens_verdict_diagnostics_located_chain_text_in_spans`. Part 2 uses that renderer and adds no - second one. -- **Lane A (#13026).** The carrier is `v2.compiler.compile` `NativeCensusCauseMember - { stage: NativeCensusCauseStage, path, module, head_reason, … }`, grouped into - `NativeCensusCauseGroup { fatal_reason, members }` by `native_census_cause_groups_add`. The - partition check is `native_census_cause_partition_holds`. The stage coproduct is - `NativeCensusCauseFrontEnd | NativeCensusCauseResolve`. The instrument row is - `//gunbc/instruments:v2-native-census` (`V2NativeCensusProducer`), and it runs the driver verb - `census-resolve`. **Line:col is not part of A.** It waits on swift-lynx-592's path, which is held - on #12506, so it is a dependency of this doc (§8) and not an A field. - -## 2. Subject population - -The population is **every module that `native_census_module_resolution` answers -`NativeCensusModuleResolved`** over the same ingest part 1 censused. The ingest is the same source -roots at the same revision. Below that boundary, a module is a subject of: - -1. **infer**: `native_test_infer_resolved`, that is `infer_and_discharge`, over the module's - `ResolvedTree`. -2. **emit**: `compile_inferred_translate`, that is `v2.compiler.emit` `emit`, over the - `InferredTree` for the target the instrument row declares. Only modules that infer are subjects - of emit. - -The population is **derived, never re-enumerated**. Part 2 reads part 1's per-module decision and -filters to the resolved arm. A module that part 1 left in the file-refused or resolve-refused arms -is **not a part 2 subject**. It still appears in the receipt as an `UpstreamRefused` disposition -(§5), so the identity join over `native_census_modules` stays complete (DESIGN §5: completeness is -an identity join, not a count). - - -### 2a. The infer demand is shared (sharp-raven-357 ruling) - -The per-module infer step is **one demand subject in the native drain**, not a walk owned by this -census. The working name is `NativeInferCensusSubject { entry }`. It resolves via -`native_demand_resolved_tree`, then runs `native_test_infer_resolved`, and yields -`Inferred { InferredTree } | InferRefused { chains }`. Two folds read that one result: - -- This census reads the `InferRefused` arm: one row per chain, with cascade attribution (§4). -- M0 of the nominal-type plan (gunbc#13024) reads the `Inferred` arm: type declarations and - `as` sites. On `InferRefused`, M0 records each of the module's `as` sites as - `OperandTypeUndecided` with the infer cause, counted and never dropped. - -Each fold measures a different fact, so each keeps its own line kind (refusal rows vs -classification rows) in one stdout. They share the subject. They do not share a row format. - -The driver verb is **`census-infer`**. Widening `census-resolve` would make that word mean two -things, and §3 forbids a meaning fork. `census-resolve` stays part 1's verb. Its resolve rows and -`census-infer`'s rows are the one carrier of §5, distinguished by `stage`. - -## 3. What a refusal at each stage means - -| stage | refusal means | row stage | -|---|---|---| -| file (front end) | The file never parsed into a module. This is part 1's `NativeTestFileRefusal` and is not re-reported. | — | -| resolve | Some reference in the module did not bind. Part 1 reports this. | — | -| **infer** | The module resolved, but `infer_and_discharge` refused. Causes include no grounding derived, a fact-lookup miss, incoherent canonical grounding, a branch-shape mismatch, or an undischarged obligation. Each is a located `Diagnostic` from `v2.compiler.infer`. | `Infer` | -| **emit** | The module inferred, but the target realization refused. The refusal is the target's inability to realize a semantically legal program (DESIGN §4: the target never decides legality). | `Emit` | - -**Part 2 needs a discriminator that does not exist yet.** The native lane folds resolve and infer -into one `NativeTestStagePrepare` (`native_test_prepare_module` binds them). A census whose stage -column read `Prepare` would attribute an infer refusal to resolve, which is the fabricated -attribution that the `NativeLaneModuleResolution` comment already refuses at the context/resolve -split. So the census does **not** read the lane's `Prepare` verdict. It calls the two halves -separately, using part 1's resolved tree, and stamps the stage from which call refused. Whether -`NativeTestStagePrepare` should itself be split into `Resolve | Infer` is a lane-vocabulary change -that is out of scope for this doc. It is flagged here because the census makes that conflation -visible. - -An infer refusal is also **per chain**, as it is for resolve. If `infer_and_discharge` returns -several `NonEmptyDiagnostics`, the module yields one row per chain. If infer stops at the first -fatal, the row set for that module is a **lower bound**. The receipt says so with the same -`ObservationCompleteness` arm resolve uses (`ObservationIncomplete { reason }`), and the census does -not report it as complete. - -## 4. Cascade attribution - -Part 1 lane C collapses import cascades to their roots. If module M fails to resolve only because an -import target T was refused, the row belongs to T and M is a cascade member. - -Below resolve, the same structure applies along a different edge. **Infer runs per module over a -resolved tree whose imported declarations come from other modules.** When M's infer refuses at an -occurrence whose fact is owned by an imported declaration in module T, and T itself refused infer, -M's row is a **cascade** of T's root row. - -- **The attribution edge.** The refused fatal's locus resolves to a declaration. If that - declaration's owning module is not M and that owning module has its own infer row, M's row is - attributed `CascadeOf { root: }`. Otherwise it is `Root`. -- **The rule is reused, not reinvented.** Lane C's collapse rule (root = earliest refused module on - the import path) is applied with the infer-stage row table substituted for the resolve-stage one - **[lane C]**. Part 2 adds no second cascade authority. If lane C's rule cannot take a stage - parameter, that rule is the thing to generalize. -- **Cross-stage cascades are not collapsed.** A module whose import target refused at *resolve* is - not an infer subject at all (§2), so it never produces an infer row to attribute. -- **Emit cascades.** Emit runs over one module's inferred tree. A cascade at emit is only possible - if emit reads another module's emitted artifact. Until a measurement shows such an edge, every - emit row is `Root`. A cascade reported at emit is then a **red signal** that the edge exists. It - is never silently attributed. - -## 5. Receipt shape — A's carrier, two more stage arms - -``` -NativeCensusCauseStage - = NativeCensusCauseFrontEnd -- A - | NativeCensusCauseResolve -- A - | NativeCensusCauseInfer -- part 2 - | NativeCensusCauseEmit -- part 2 -NativeCensusCauseMember -- A's member, unchanged apart from the stage arms -NativeCensusCauseGroup { fatal_reason, members } -- A's grouping key, unchanged -attribution: Root | CascadeOf{root} -- [lane C] declared frontier -``` - -- **Grouping stays on `fatal_reason` alone,** as A has it. Stage is a member field and never a key. - This is the same rule A applies to head: a fatal shared across stages is one cause group with - members at several stages, and a reader filters on the field. Heads are carried and never tallied. -- **The partition check is A's.** `native_census_cause_partition_holds` covers infer and emit - members without change. A member that lands in no group, or in two, breaks the partition at every - stage. -- **Per-module disposition** is one coproduct per module, so the join over `native_census_modules` - is total: `UpstreamRefused { stage: FrontEnd | Resolve }` | `InferRefused` | `EmitRefused` | - `Emitted`. Rows hang off the refused arms. -- **Completeness** is a coproduct, as in part 1 (`Xl2CensusCompleteness` pattern). An incomplete - infer observation or an unjoinable cascade root each refuses completeness. Neither is a zero. -- **No parallel carrier.** An `InferCensusRow` beside A's member would be the §3 fork. Adding - arms makes every exhaustive match over `NativeCensusCauseStage` fail to compile until it handles - them, which is the enrollment we want. - -## 6. Instrument label — extend `v2-native-census`, no second label - -Part 2 is **the same label**: `//gunbc/instruments:v2-native-census`. The label measures one fact, -the native census's cause groups over the self-host roots. Part 2 extends the stages the census -reaches; it does not measure a different fact. A second label would give one measurement two -routes, which DESIGN's "Building & checks" forbids (the `--self-host` precedent A also cites). - -- **Realization.** `V2NativeCensusProducer` switches from running `census-resolve` to running - `census-infer` (§2a). That verb reaches infer and emit through the shared demand subject and - prints A's `cause_group` lines, whose members now include infer and emit members. `census-resolve` - stays as a verb for part 1's grain. It is a realization, not a peer route, so this is not a - second label. -- **Exit codes are A's, unchanged.** 0: the partition held. 1: the partition broke. 2: the run did - not complete. Refusals are the census's data and do not change the exit code. Completeness below - complete is reported on the status line, as A reports `cause_groups` and `partition_holds`. -- **Emit target.** The target is the instrument's own fact (the row names it), never a CLI option, - so that one label cannot quietly measure a different target. -- **Cost.** This is a whole-tree run: CI or `ctrl-build --remote`, never in a session. The infer - pass reuses the single `ResolutionContext` per ingest (#11401). Inferring per module must not - rebuild that context. - -## 7. Discriminating controls - -Each control must go red when the behavior it guards is wrong. These are fixtures handed to the -compiler (DESIGN §4b: a compiler's probes are invalid programs). - -1. **Stage discrimination.** In a two-module fixture, A resolves and fails infer (a branch-shape - mismatch), and B fails resolve. The expected rows are A with `Infer` and B with `Resolve`. - Rendering both as `Prepare`, or giving A `Resolve`, must red. -2. **Per-chain grain.** One module with two independent infer refusals yields two rows. Folding - them to one must red. If infer cannot produce two, the control instead asserts - `ObservationIncomplete`. -3. **Cascade.** T fails infer at a declaration that M uses, and M's only infer failure is at that - use. Expected: M is `CascadeOf T` and T is `Root`. Removing T's defect must make **both** rows - disappear. That is the route assertion, not only the answer (DESIGN §3 pairing). -4. **Non-cascade.** M has its own infer defect and also imports a refused T. M's own row must be - `Root`. This catches over-collapse. -5. **Emit.** A module that infers but uses a construct the target refuses yields one `Emit` row. - The positive control is a module that emits, with disposition `Emitted` and no rows. -6. **Join completeness.** Delete one module from the disposition table. The identity join must - refuse, and a count comparison must not pass it. -7. **Inhabitance.** One control runs the real whole-tree route (the instrument row itself, on CI) - and asserts that at least one `Infer`-stage row was produced **by the infer call** and not by a - supplied fixture. Removing the infer call from the route must red it. - -## 8. Open dependencies - -- **Line:col on members.** swift-lynx-592 owns this path (#13005 follow-up), and it is held on - #12506. A's member shape takes the field once that lands. Part 2's infer and emit members inherit - it with no second locus path. Until then, a member's position is whatever its chain's diagnostics - carry, rendered by nothing new. -- **[lane C], declared frontier.** The cascade carrier (`Root | CascadeOf`) and its collapse rule - belong to lane C, which has not been dispatched. Its trigger is lane C landing a carrier that takes - the stage as a parameter. If C's rule is resolve-only, it is generalized in C's module, not copied - here. Until then, part 2 members carry no attribution, and the census reports `attribution - unobserved` as a completeness refusal, never as `Root`. -- **The shared infer subject** (§2a), with tidy-koi-264's M0. Whichever lane builds first owns the - subject and the `census-infer` verb arm. -- **`NativeTestStagePrepare` split.** This is a lane-vocabulary question (§3) that this doc raises - and does not decide. diff --git a/docs/plans/ci-throughput-subject-census.md b/docs/plans/ci-throughput-subject-census.md deleted file mode 100644 index 7ebf77fc75c..00000000000 --- a/docs/plans/ci-throughput-subject-census.md +++ /dev/null @@ -1,190 +0,0 @@ -# CI throughput comparability — a whole-path census of the current tree - -Stage 3 of the throughput-qualified convergence design, which named the CI side a frontier rather than -a bound subject and required its census to read the CURRENT tree. **That parent does not exist in this -tree: it is open as gunbc#11540 and this census must land with or after it** — the link below resolves -only once that PR lands, and naming it here is the declared frontier's trigger (§3c) rather than a -citation that silently dangles: [throughput-qualified-convergence-design.md](throughput-qualified-convergence-design.md). -That instruction was specific and is honoured literally here: `docs/plans/ci-humming.md` describes the -June slot/cgroup program and an older control/apply architecture, and deriving axes from it would -model a fleet that has since been replaced. - -This is a census, not a design. It answers one question — **which properties, if they change, make a -prior CI rate inapplicable** — and it answers it by naming carriers that exist today. - -## The finding that motivates the rest - -**No fold anywhere derives an aggregate CI rate** — completed homogeneous work per unit time, bound to -an admitted subject, a declared protocol and an observed realization. Durations DO exist and are -derived (`gunbc.superseded_run_starvation_census` `timestamp_diff_seconds`); a rate does not. - -The runner modules under `dag/gunbc/runner/` model slot identity, width admission, microVM sizing, -placement, connectivity repair, and receipts for nearly every step of an attempt's life. The query -behind the claims below is named rather than its output copied (§6): a case-insensitive search of -those modules for a rate or duration vocabulary — `throughput`, `per_hour`, `jobs_per`, `duration`, -`elapsed`, `wall_clock` — re-derives what this census read, and a reader who runs it today sees -whatever is true today rather than what was true when this was written. **Naming the producer instead -of its count is not a formality here**: an earlier revision of this census copied a hit count out of a -narrower search and used it as the evidence for a claim this document has since retracted (see the -corrections below), so the copied number outlived the search that produced it by exactly the margin -that makes transcription dangerous. - -What that search surfaces in the runner modules is not a rate: `runner_unit_file` `InvocationLocalCargo { jobs_per_slot }` is a **cargo invocation -parameter** — how many compile jobs one slot may spawn — and `runner_slot_allocation` carries a prose -aside about oversubscription degrading throughput. Both are inputs to a configuration; neither is an -observation of delivered work per unit time. - -So the CI side is in exactly the state the parent design describes for serving: a configuration -derived with care, converged, verified, and never put to the question it was chosen to answer. - -## Candidate axes, each with its carrier in the current tree - -An axis is a property whose change makes a prior number inapplicable. Each row names where the -property lives today, so the eventual subject type is assembled from existing authorities rather than -re-coined. - -| candidate axis | carrier in the tree today | -|---|---| -| host class | `gunbc.runner.runner_host_tpm_observation` and the Mt Collins extdeps briefs distinguish host populations; no single host-class carrier joins them yet | -| requested and observed slot width | `gunbc.runner_capacity_plan` `RunnerCapacityHostPlan { requested_width, observed_width }` | -| microVM shape | `gunbc.runner.runner_microvm` `RunnerMicroVmSize { vcpu_count, memory }`, with `GuestMemoryRequest` and `GuestResources` beside it | -| guest image | `gunbc.runner.runner_microvm` `RunnerGuestImage { distribution, release_series, arch, runner }` | -| actions-runner revision | `RunnerGuestImage.runner` (`ActionsRunnerRelease`) | -| cores per slot / jobs per slot | `gunbc.runner_slot_allocation` `gunbc_runner_cores_per_slot`, consumed by `runner_unit_file` `InvocationLocalCargo` | -| memory envelope per slot | `gunbc.memory_per_vcpu_convention`, with the microVM sizing relation | -| kernel and rootfs | `gunbc.runner.runner_microvm` config resolution (`kernel_path`, `rootfs_path`) | -| toolchain revision | not carried by the runner subsystem; the closest authority is the repo's own toolchain coherence module | -| cache topology and cache state | **no carrier** — see below | -| exact Work and source-tree identity | `gunbc.compute.work_request` `WorkOperation` is the nearest home; the join from a CI attempt to the exact tree it built is not modelled | -| dispatch policy | `gunbc.runner_attempt_launch` and the broker modules carry attempt admission; no policy-identity carrier | - -## What the census found missing, which is the useful half - -**1. Cache state has no carrier, and it is the axis most able to produce a meaningless comparison.** -`sccache` appears in the build environment and in `extdeps.cache` as a general notion, but nothing -models whether a given attempt ran against a warm or cold cache, or against which cache population. A -cold-cache run and a warm-cache run over the same tree on the same hardware differ by a large factor, -so without this axis two honest readings can disagree wildly and neither is wrong. Any CI floor set -before this exists is a floor over an unstated variable. - -**2. The actions-runner revision already diverges across the fleet, and it is declared.** -`runner_microvm` states that the guest image pins 2.337.0 while the fleet's host slots pin 2.336.0 — -found in review rather than by anything in the repository, and resolved forward deliberately. This is -a live specimen of exactly what an axis is for: two attempts on "the same fleet" can differ on runner -revision depending on which population served them, so a rate compared across that boundary is -comparing two subjects. - -**3. The rate INPUTS largely exist and nothing folds them — which an earlier revision of this census -missed, and the correction makes the CI probe cheaper rather than more expensive.** -`extdeps.github.workflow_runs` `WorkflowJobRun` carries `created_at`, `started_at`, `completed_at`, -`run_attempt`, `labels`, `status` and `conclusion`, and `gunbc.public_workload_census` already holds -observed execution rows with those timestamps populated. So the claim to make is narrower and sharper -than "nothing measures a rate": **every current consumer uses those timestamps as ORDERING -PREDICATES, never as an interval.** `gunbc.run_disposition` asks whether `run_started_at` is strictly -later than `created_at` to prove execution began; `gunbc.pr_base_freshness` asks whether a base commit -landed after a run started — both use the timestamps as ordering predicates rather than intervals. -That is a fact about THOSE TWO consumers and not about the tree, which is where an earlier revision of -this census overreached. - -**And the sentence above was itself wrong when first written, which is the correction worth reading.** -A revision of this census asserted that "neither subtracts; no fold in the tree derives a duration, a -queue delay or a rate". That is false. `gunbc.superseded_run_starvation_census` declares -`timestamp_diff_seconds(end, start) -> DurationComputed { duration_seconds: Second }` and uses it for -dead time under hold and for group hold after jobs — real timestamp arithmetic into a typed -`std.measure` quantity, over live workflow-run and job API calls with paging and terminal -classification. It is an executing precedent for acquisition and interval derivation, not a declared -shape. The claim was over-stated from an incomplete grep and asserted as established, which is the -§4d failure of asserting as deduced what was only inferred. - -The surviving claim is narrower and is the one that matters: **no fold derives an aggregate RATE** — -completed homogeneous work per unit time, bound to an admitted subject, a declared protocol and an -observed realization. Durations exist; a rate does not. - -Two consequences. A CI rate needs a fold over a substrate that exists, with an interval precedent -already executing — a materially smaller job than this census first implied. And because that -substrate is a census of REAL production jobs, some CI rate questions can be answered without a -synthetic probe at all, as a reading over observed work carrying `WindowSharedWithDeclaredTraffic` -rather than an isolated window: a weaker standing, much cheaper, and for a trend possibly the right -instrument. - -What remains genuinely absent is the **join from an attempt to the exact Work identity and source tree -it built**. A rate needs an operation ledger over homogeneous work (a one-minute no-op and a -fifteen-minute clean build are not interchangeable units), and job labels plus a run attempt do not -establish what was compiled. That, not the timestamps, is the precondition still missing. -(Correction raised by the side-chat review, 2026-09-17.) - -**4. Host class is implied by module paths rather than carried.** Mt Collins facts live in extdeps -briefs and the runner modules observe individual hosts; nothing names "this host belongs to class C" -in a way a measurement could cite. - -## Corrections after review (2026-09-17) - -**The census searched the wrong closure.** It read the modules under `dag/gunbc/runner/` and drew a -conclusion about CI, while the measurement substrate lives largely outside that subtree: -`extdeps.github.workflow_runs::WorkflowJobRun` (exact run attempt, status, conclusion, observed runner -labels, three timestamps, with exact-attempt and latest-attempt listings), -`gunbc.public_workload_census::ObservedExecution` (which joins a job to repository, workflow path, -workflow-blob SHA at the observed head, head SHA and an observed correctness standing — much closer to -a homogeneous operation ledger than this census allowed), and -`gunbc.superseded_run_starvation_census` (live API acquisition plus the interval derivation above). A -subtree is not a closure, and "I searched `runner/`" does not ground a claim about CI. - -**A second run-record authority already exists.** `extdeps.github.actions_runs` is another -workflow-runs projection with run timestamps and a `gh run list` transport, and `workflow_runs` -already names the pair as a meaning fork awaiting consolidation. A CI probe must CHOOSE or consolidate -that authority; introducing a third run record would make the fork three-way (§3). - -**The axis table conflates three layers.** Its test — does changing this make a prior rate -inapplicable — identifies a comparability dimension but does not decide where the dimension lives, and -the parent design already partitions subject / protocol / realization / window. Putting exact Work in -the subject makes the runner a new subject every commit; putting cache POPULATION in the subject makes -it change on every fill or eviction. Both are comparability facts and neither is stable configuration. - -| dimension | layer | -|---|---| -| host hardware class; configured slot width and resource envelope | subject | -| runner agent, guest environment, toolchain identities | subject | -| cache implementation, namespace/topology, configured policy | subject | -| exact Work, source tree, workflow definition | protocol (operation identity) | -| offered concurrency, stopping rule, required warm/cold condition | protocol | -| exact cache population encountered; exact host, VM, runner and attempt population | realization | -| ambient or foreign work during the window | window/isolation standing | - -So the cache finding splits three ways rather than being one missing axis: topology and policy are -subject, the required condition is protocol, the observed hit state is realization. - -**The Work-identity gap is narrower than stated, and the repair is different.** -`gunbc.runner_attempt_launch::plan_attempt_launch` already RECEIVES exact Work, Demand and Offer -identities and calls `grant_authorizes_reservation` before planning the VM — so Work identity reaches -the launch planner. The loss is downstream: `LaunchAuthorized` retains the coarse `GrantAuthorization` -verdict and not the admitted identities, so later receipts cannot rejoin the attempt to the exact Work. -The missing piece is a BRIDGE — admitted fabric Work identity → launched microVM attempt → observed -`WorkflowJobRun` and terminal receipt — and the repair carries the existing admitted identity forward -rather than coining a second CI-work identity. - -**Two "existing axes" are locators, not execution identities.** `RunnerGuestImage` carries -distribution, release series, architecture and actions-runner release; none of that content-identifies -the guest rootfs bytes, whatever its own comment calls itself. The launch planner likewise takes -`firecracker_binary`, `kernel_source` and `rootfs_source` as paths, and one path can name different -bytes across hosts or across time. So exact rootfs content identity, exact kernel content identity, -Firecracker/jailer release identity, and a host execution class covering host kernel, CPU class/policy -and storage realization are all REMAINING GAPS, not settled axes. - -## What this census does NOT do - -It does not propose the CI subject type. The gaps above are modelling obligations -(DESIGN §6) rather than conformance rows (§3b), because a row whose home does not exist is an -obligation and not a domain — and coining a subject over carriers that do not exist would be the -re-invention DESIGN §2's test names. The order is: close the cache-state and work-identity gaps, then -assemble the subject from carriers that exist, then probe. - -It also does not rank hosts, shapes or widths. The moment anything chooses among configurations, -`std.decision` is the home and §3d binds. - -## Standing - -A census, not enrolled in DESIGN, governing nothing. Its consumer is stage 3 of the parent design — -which is gunbc#11540 and is NOT YET IN THE TREE, so this document's only consumer is a declared -frontier and its merge dependency is that PR, -and its finding — that CI models its configuration thoroughly and its rate not at all — is the same -finding the parent made for serving, arrived at independently on the other side of the fleet. diff --git a/docs/plans/cli-invocation-emission-design.md b/docs/plans/cli-invocation-emission-design.md deleted file mode 100644 index 5f71cedd681..00000000000 --- a/docs/plans/cli-invocation-emission-design.md +++ /dev/null @@ -1,1418 +0,0 @@ -# CLI invocation emission — the tree is the authority, argv is emitted - -**Status: DESIGN NOTE PLUS ITS FIRST CUT.** An earlier revision read "No code lands from this -note yet" while the branch already carried a carrier, a seed realization, witnesses and one -migrated production site — the stale-citation class in the lane's own authority. What has landed -is stated at each wave below; what has not is stated as not. Design-note-first, per the -hollow-alias precedent. - -**Lane scope, narrowed (raised by the `shell → dag` session, accepted):** this note governs -**CLI-backed host effects only** — effects realized as a process invoked with an argument vector. -It is *not* a universal effect model: a Redfish/REST path reaches a modeled request and must reach -**no** `JqInvocation`, `CliSurface`, `ProcessArgvExpansion` or shell target. Treating every host -effect as a CLI invocation would repeat at the effect layer the mistake this lane removes at the -argv layer — one realization mistaken for the interface. A negative falsifier witness proving a -REST path reaches none of these carriers is **owed by this lane and not yet built.** - -**Lane:** supersedes the Lane B destination of `gunbc.plans.transport_argv_anemia_dissolution`. -Its *diagnosis* stands — "the model is the request; the curl argv is one derived realization of -it" — and its Lane A (HTTP → `transport rest`) is substantially right. Its Lane B destination -terminates at typed `extdeps/tools/*` operations and `ArgvCommand` builders, both still containing -literal argv — one layer short. - ---- - -## 1. The defect - -An invocation is authored today as a list of opaque strings: - -``` -argv: ["jq", "-er", "--argjson", "index", "{index}", ".data[0] | keys | .[$index]"] -``` - -Three independent facts are welded into that one list: - -1. **what jq is** — a program operand, a raw-output mode, typed variable bindings, an exit policy; -2. **how we reached it** — locally, or over `sshpass`+`ssh` to a BMC; -3. **what we asked it** — an OpenBMC-specific projection over fan PID zones. - -Welded, every new question is a new hand-typed list re-deciding all three. A flag nobody -modeled is trivially droppable — which is how `--fail` went missing from eleven curl sites and -made a 401 read as success. - -### The census (measured in-tree, not inherited from the plan) - -| fact | count | -|---|---| -| `argv:` lines | 597 across 129 files | -| `transport shell` blocks | 250 | -| `transport rest` blocks | 95 | -| `operation` declarations | 322 | -| operations declaring `success: Bool from "exit_success"` | 173 | -| operations declaring `exit_code: Int from "exit_code"` | 69 | -| `shell_quote` call sites | 36 | -| argv lines with a literal program head | 282 | -| …of those, heads that are **wrappers**, not tools | 78 | -| `sh -c` / `bash -c` embedded-language leaves | 14 | - -**Every count in this section is a measurement of one commit, `5c106b8a30` (2026-09-04), and is -not re-derived as the tree moves.** It is recorded that way deliberately rather than hand-refreshed: -a number edited to match today's tree is unreachable from whatever produced it and rots silently at -both ends (§6). Re-derive against a named ref before relying on any figure here; what the section -argues does not depend on the exact values. - -Two precision bounds: 315 of 597 argv lines have computed heads and are unclassifiable by -this method, so 78 is a **lower bound** on wrapper-hidden tools; the metacharacter count is -a text match, not a parse. - -**The plan's own census is stale by ~2.3×** — it records 262 argv lines across 37 files, and -148/51 for the success/exit_code split. Both measured above. - -### The census cannot currently be taken - -Counting by argv head yields `git` 43, `sshpass` 40, `sh` 13, `sudo` 9, `env` 9. The middle -four are wrappers, not tools; the real program sits at some positional offset. All seventeen -jq-over-ssh sites count as `sshpass`, not jq. - -**Moved since that measurement: #11061 binds the 40 `sshpass` heads to the computed -`extdeps.exec.command` `sshpass_binary_name`**, in `extdeps.bmc.openbmc_password_ssh_transport` and -`extdeps.ssh.password_session`. The invocations are unchanged — those operations still exec sshpass, -so the tool count of 40 still holds — but they have crossed from the literal-head population into -the computed-head one, so a re-derivation by matching the literal now answers zero for sshpass. That -is the shape this section is arguing about: the count was never wrong, the METHOD is what the corpus -moved out from under. - -This is itself the argument: tool identity is a positional accident in a flat string array, so -the corpus cannot answer "what do we invoke" without knowing `argv[11]` is the real program -because `argv[0..10]` happened to be an ssh prefix. - ---- - -## 2. Facts, separated by kind - -### 2a. Substrate facts (read from source) - -- `v2.std.compilers.target_model` `TargetModel` is target-agnostic in its bundle/lex/spellings - shape, but **text-specialized** in three fields: `binding_spellings: Map`, - `token_class_emit_transforms: Map String>`, `authority_source_text: String`. -- The emit carrier is `v2.std.compilers.target_model` `TargetText { source: String }` — one - String→atom introduction, concatenation as the only join, whole-value render as the only exit. -- `v2.extdeps.formats.sql_target` proves a **non-programming-language** target registers through - `target_model_make_with_emit_transforms`. It does **not** prove carrier independence: its - serialization node is still source-text shaped. -- The public emit seam commits to `Medium`. `Medium` itself is generic; the text lock - is in `TargetModel`, `serialize_target` and `emit`, not in `Medium`. -- `dag/extdeps/languages/` holds 32 language authorities. **None is a CLI-invocation authority.** -- `extdeps.exec.command` renders argv as `join(map(argv, shell_quote), " ")`, and is anchored to - the **SSH** manual while defining `curl`, `mkdir`, `tar`, `make`, `sed`, `cp` builders — plainly - a cross-tool realization utility, not the authority for those tools' semantics. -- `v2.std.host_transport` `ProcessInvocation` / `InvocationArg` exist with 72 consumer references. -- `v2.extdeps.posix` `Command`, `PosixArgument`, `SpawnProcessRequest` exist with **zero consumers - outside the declaring module**. -- `extdeps.posix.shell_command_language` exists as its own subject. -- `gunbc.command_runner` carries `command_runner_scaffold: Disposition = Scaffold`. - -### 2b. Upstream authority facts (cited) - -- **IEEE Std 1003.1-2024, Base Specifications Issue 8, §12** — 14 numbered Utility Syntax - Guidelines; `--` ends options; options precede operands (G9); `-` denotes stdin (G13); - option grouping behind one `-`; option-arguments as separate tokens. -- **POSIX `sed`** declares an explicit deviation: the relative order of `-e` and `-f` is - significant. So POSIX is the *base* grammar, never the whole tool grammar. -- **RFC 4254 §6.5** — the SSH `exec` channel request carries a single `command` field of type - **string**. No argv vector exists in the request, so argv structure cannot cross that boundary - as structure. The protocol does *not* define that string as shell source; shell re-parse is an - OpenSSH implementation fact, as `extdeps.ssh.session` already records. -- **IEEE Std 1003.1 Shell Command Language** — grounds the shell leg. Already cited by - `extdeps.posix.shell_command_language`, whose own note reserves it for exactly this use: - a POSIX-shell target "registers against THIS declaration and needs no new grounding." -- **jq manual** (`jqlang.org/manual/`) — `--raw-output`/`-r`, `--exit-status`/`-e`, - `--argjson name JSON-text`, `--arg name value`, `--` terminator, file operands vs stdin. -- **RFC 7950 (YANG 1.1)** — abstract data model held separate from concrete encoding; one model - renders as NETCONF XML or RESTCONF JSON. Cisco IOS XR ships "Model-Driven CLI" on that basis. - Prior art: the networking industry ran this exact migration from CLI-scraping to modeled trees. -- **PowerShell 7.6** — parameters bind to typed .NET objects; parameter metadata (`Position`, - `Required`, `Accepts multiple values`, `Accepts pipeline input`) is machine-readable. Evidence - that a per-tool option table can be data rather than prose. - -### 2c. Domain facts discovered during design (these constrain the model) - -- `extdeps.bmc.openbmc_fan_control` declares `OpenBmcSensorValueAbsent` — a **third state** - beside Refused and Observed. `openbmc_sensor_integer_projection_result` decodes empty stdout - to it, and `dag/test/claim/bmc_typed_operations_witness_test.dag` witnesses it. - **jq producing no output is a modeled value, not an error.** Any exit partition folding - "no result" into failure breaks working, witnessed code. -- `gunbc.host_effect_realize` `bmcweb_token_extraction_verdict` refuses blank stdout on its own. - So at that site `-e` was never the mechanism; it is transport *loudness*. -- Of the four jq operations in `openbmc_fan_control`, **all are live**: - `ProjectFanConfig`, `ObjectMapperServiceCount`, `ObjectMapperServiceAt`, `SensorIntegerValue`. - Only `extdeps.tools.jq` `jq.Json.ExtractRaw` is zero-consumer tree-wide. - ---- - -## 3. The layering - -A layer is only real if something varies at it. Checked against three CLI families: - -| layer | owns | POSIX | PowerShell | Cisco | -|---|---|---|---|---| -| **intent** | the tree: subject, named params, operands | *identical across all three* | -| **vocabulary** | which params exist, types, cardinality, positional, exclusivity | tool's manual | cmdlet parameter metadata | YANG module | -| **surface grammar** | how a bound param renders | `-a`, `--long=v`, bundling, `--` | `-Name value` / `-Name:value` | keyword paths in a mode hierarchy | -| **value encoding** | how a typed value becomes a param value | bytes; nested languages embedded | .NET objects — no serialization | typed per YANG leaf → XML/JSON | -| **carrier** | what the emitted thing physically is | argv vector *or* shell text | in-process object pipeline | NETCONF RPC document | -| **transport** | where it runs | local exec, ssh, enable-mode session, REST | - -The families agree at *intent* and disagree at *surface grammar*, *value encoding* and -*carrier* — so intent is the authority and everything below is a rendering. - -### Two consequences - -**Carrier ≠ surface grammar.** Emitting to shell text when the destination is `execve` invents -a quoting problem that does not exist. `shell_quote` at 36 sites is the current mitigation for -that conflation, not a requirement. - -**Value encoding nests.** A jq program is a language embedded as a leaf in a POSIX argument; so -is `sh -c` — 14 sites. Such a leaf should be a typed sub-tree, not an opaque String. Modeling -jq's *expression* language is a separate vertical from its *invocation* structure and is not a -prerequisite here. - ---- - -## 4. The model - -``` -JqInvocation semantic axes only; no flag spellings reachable - | - | jq cited option rows + shared CLI grammar rows - v -CliInvocationTree options grouped with their values; operands; terminator - | - | emission — roles erased here, correctly - v -List argument boundaries structural; no quoting - | - +-- local: tool resolution -> extdeps.posix Command -> spawn - | - +-- ssh: -> shell simple-command tree -> shell text -> RFC 4254 command string -``` - -### 4a. Semantic layer - -``` -type JqProgram { source: NonEmptyStr } // opaque, upstream-grounded, for now - -type JqBinding - = JqJsonBinding { name: JqBindingName, value: Json } - | JqTextBinding { name: JqBindingName, value: String } - -// JqBindingName is jq-owned, not a bare NonEmptyStr. Duplicate-name policy is declared, -// not left to jq's last-wins behaviour: the admission fold refuses a repeated name. - -type JqInput - = JqInputFile { path: FilePath } - | JqInputStdin { content: String } - -type JqOutputEncoding = JqJsonOutput | JqRawOutput // -r writes STRING results raw; - // non-string results stay JSON-formatted - -type JqExitPolicy = JqProgramExit | JqLastResultTruthiness - -type JqInvocation { - program: JqProgram - bindings: List - input: JqInput - output_encoding: JqOutputEncoding - exit_policy: JqExitPolicy -} -``` - -`JqInput` is where stdin stops pretending to be an option: `JqInputFile` produces one file -operand, `JqInputStdin` produces **no operand** and sets process stdin. Load-bearing: the four -local sites are stdin-fed, the seventeen remote ones file-operand. - -### 4b. Observation: two orthogonal axes, sealed - -Two earlier revisions of this section put on one axis a fact that lives on two. - -**Termination and output presence are orthogonal.** The live `SensorIntegerValue` specimen holds -both at once: exit status 0 **and** zero results. It runs `-r` without `-e`, its filter takes the -`empty` branch for a non-number, and exits zero with empty stdout. So -`JqExitZero | JqProducedNoResult` is a product, not a coproduct; written as a sum, the live case -is unrepresentable. - -Verified against `jqlang.org/manual/`: - -``` -default exit 0 when the program runs successfully, REGARDLESS of output ---exit-status / -e exit 0 last output neither false nor null - exit 1 last output false or null - exit 4 no valid result was ever produced -halt_error program-chosen, default 5 usage 2 compile error 3 -``` - -``` -type JqExitDisposition - = JqExitZero - | JqExitFalseOrNullUnderExitStatus // JqLastResultExit only - | JqExitNoResultUnderExitStatus // JqLastResultExit only - | JqExitRefused { code: Int } - -type JqOutputPresence - = JqOutputAbsent - | JqOutputPresent { stdout: String } - -type JqObservation sole_constructor { - process: ProcessObservation // raw evidence, retained - exit: JqExitDisposition // decoded projection, bound to it - output: JqOutputPresence - stderr: String -} -``` - -| case | exit disposition | output presence | -|---|---|---| -| `SensorIntegerValue`, numeric | `JqExitZero` | `JqOutputPresent` | -| `SensorIntegerValue`, non-numeric | `JqExitZero` | **`JqOutputAbsent`** | -| `ObjectMapperServiceAt`, found | `JqExitZero` | `JqOutputPresent` | -| truthiness policy, no result | `JqExitNoResultUnderExitStatus` | `JqOutputAbsent` | -| jq execution error | `JqExitRefused` | independently observed | - -**The decoder is policy-indexed and is the only mint.** -`decode_jq_observation(invocation, process) -> JqObservation` — not a global `exit_code -> JqExit` -lookup, because codes 1 and 4 mean nothing under `JqProgramExit`, and zero results under -`JqProgramExit` still produce code zero. - -**Sealing is required, not decorative.** Rejecting `success: Bool` beside `exit_code` removes one -writable contradiction; a freely constructible `JqObservation` recreates it under richer names — -`{ exit: JqExitZero, exit_code: 4 }` would be authorable. `sole_constructor`, minted only by the -policy-aware decoder, closes it: the raw process observation is retained as evidence and the -decoded facts are a projection **bound to it**, never two caller-authored fields. This is the first -concrete place where a construction wall is available today. - -**A stdout `String` cannot generically prove "exactly one nonempty raw string".** jq emits a -*stream*; under `--raw-output` a raw string may contain newlines, which is why `--raw-output0` -exists ("Like `-r` but jq will print NUL instead of newline after each output"). `foo\nbar\n` is -ambiguous between one string with a newline and two results. This does not block the first -vertical — an integer has an unambiguous textual grammar — but **this note promises no generic -`decode_exactly_one_nonempty_string`.** Lifting that requires one of: a single JSON envelope -parsed as JSON; `--raw-output0` once jq version/capability is modeled; or a program collecting its -result into a one-result container the domain decoder checks. - -**No `success: Bool`.** The seed derives it as exactly `exit_code == 0` — one fact twice. -`extdeps.git.git` `ls_remote_reports_termination_not_success_note` establishes this; 173 -operations still carry the Bool. - -### 4c. Two-level row derivation — the keystone - -Both the **selection** of an option and its **spelling** must be row-derived: - -``` -JqLastResultTruthiness -> JqCliOptionExitStatus (prevents omission) -JqCliOptionExitStatus -> canonical "--exit-status", alias "-e" (prevents bespoke spelling) - -JqJsonBinding { name, value } - -> [ argument "--argjson", argument emit(name), argument emit(value, Json) ] -``` - -If only the second level is data-driven while a function still says "when policy is X, append -option Y," the droppable-option class survives one layer down — the difference between this design -and centralizing 597 literals into N helper functions. - -The precise statement replacing "`-e` is part of the contract, not a flag": - -> `JqLastResultTruthiness` is part of the contract. `-e` and `--exit-status` are concrete -> spellings of that property in the jq CLI realization. - -### 4d. Grammar decomposition - -``` -shared CLI grammar option, option-argument, operand, repeated group, - terminator, short-option cluster, long spelling, - joined vs separate value - -+ per-tool cited rows jq options and operand order - sed's -e/-f ordering exception - git subcommand grammar - tar mode-word grammar - ssh -o forms -``` - -Named `CliSyntax`, not `PosixCli`: real tools deviate (POSIX `sed` provably), and the name must -not assert conformance the corpus cannot claim. - -### 4e. The structured CLI tree — why roles live here and not in the arg vector - -``` -type CliElement - = CliOption { option: CliOptionRef, values: List } - | CliOperand { value: CliValue } - | CliOptionTerminator -``` - -An option's values cannot detach from their option: `--argjson`, its binding name and its JSON -value stay one subtree until the grammar lowers them. - -**Role and provenance are orthogonal axes and must not share a coproduct.** A workspace path may -be an option value *or* an operand; a produced artifact either; a literal an option, subcommand, -option value, mode word or operand. Fusing them makes `OptionValue ∧ WorkspacePath` -unrepresentable, and expanding to `WorkspacePathOptionValue`, `WorkspacePathOperand`, … is the -combinatorial growth the grammar exists to remove. - -**`CliOption { values: List }` is too permissive as a *public* carrier.** Freely -constructible, it admits `--argjson` with zero, one or three values; a flag with a value; a -non-repeatable option twice; illegal order; another tool's option; operands before options where -forbidden. So the cited row must carry option identity, canonical spelling, aliases, argument -arity, repeatability, placement and joining discipline — and the emitter accepts only a -**normalized, admitted** tree: - -``` -type CliOptionSchema { - option: CliOptionRef - arguments: CliArgumentSchema - repeatability: CliOptionRepeatability - spelling: CliCanonicalSpelling - placement: CliOptionPlacement -} - -type AdmittedCliInvocation { tool: CliToolRef, options: List, operands: List } -``` - -Domain callers receive **no constructor** for `AdmittedCliOptionUse`. The jq semantic-to-syntax -projection selects the row and supplies values; the admission fold checks the schema — the -`sole_constructor` + `admit_callers` shape that sealed `TransportScript`, and what raises the rung -for the migrated path (§9). - -Roles are then **deliberately erased** at emission, because the OS receives an ordered vector of -strings. That erasure is emission, not anemia; the present defect is only that no preceding tree -exists — authors write the vector directly. - -### 4e-bis. The process plan is not the CLI grammar - -`CliSyntax` owns how options and operands become argument boundaries and **nothing else**: -stdin, environment, working directory and descriptor routing belong to the *process plan*. - -``` -type JqProcessPlan { command: CliCommandSurface, stdin: ProcessInput } - -JqInputFile(path) -> one CLI operand, no jq-data stdin -JqInputStdin(content) -> no file operand, stdin = content -``` - -This is why `SensorIntegerValue` is a good first proof: input content stays **outside argv** while -the jq program stays **one argv argument** — two properties an argv-only model cannot state, and -the gap `extdeps.llm.cursor_cli` hit when an argv-shaped carrier could not hold an -environment-passed credential. - -### 4f. Carrier: argv is native, text is the derived special case - -POSIX utility syntax operates over *arguments*; shell syntax is the preceding language that turns -words and expansions into them, with quote removal before the utility sees them. So argv is the -higher-structure carrier and shell text a serialization of it through an additional language. - -Making text canonical and re-deriving argv by parsing is backwards: it forces the strongest local -transport through an unnecessary shell grammar, quoting discipline and injection surface because -one weaker remote transport loses structure. - -**Local and SSH are not two jq targets.** They are one utility target, two realization carriers, -and one *additional nested target* on the SSH path — the POSIX shell grammar, modeled -independently and already grounded by `extdeps.posix.shell_command_language`. - -Counterfactual proving command text is not intrinsic: a remote receiver accepting a structured -request and calling `execve` itself would eliminate the shell leg while keeping SSH as transport. - -**Precedent for the composition shape:** `effect_plan_bash_materialize` obtains an argv vector, -converts it to Bash command nodes, then serializes the Bash grammar. Its argv source is still the -old declaration-owned materializer, but the shape is right. - -### 4g. Physical layer - -Lower into `v2.extdeps.posix` `Command` / `PosixArgument`, **not** `v2.std.host_transport` -`ProcessInvocation`. The latter is an emit-harness carrier whose `InvocationArg` variants -(`LiteralArg | WorkspacePath | ProducedArtifact`) classify build-workspace provenance, not CLI -roles; `build_transport_admission` only asks whether a step reads a declared workspace resource, -and `emit_host` flattens all three variants back to text. - -Its provenance vocabulary is hand-applied, not derived: `python.dag` models its script as -`WorkspacePath`, `go.dag` models `main.go` as `WorkspacePath`, and `c.dag` models `fixture.c`, -`-o` and `fixture` as three undifferentiated `LiteralArg`s — so C's compiler read of `fixture.c` -is invisible to the admission fold. A real harness defect, and an independent reason not to -promote this carrier to the universal process boundary. - -`ProcessInvocation` also lacks process-wide axes needed elsewhere: **no environment, no stdin, no -working directory, no file-descriptor mapping.** `extdeps.llm.cursor_cli` already forked around -this, minting `CursorProcessInvocation { program, argv, environment }` rather than reuse it; its -note states the principle with a security consequence — - -> an argv is not a process. A process is a program, an argv AND an environment, and a credential -> passed by environment is invisible in a model whose output stops at the argv — there was no -> field for it to appear in. - -A fork in `dag/extdeps/llm` against a carrier in `src/v2/std` would be unnecessary if -`host_transport` `ProcessInvocation` were the general authority its name suggests — independent -corroboration of the name collision from a different lane. - -**What `ProcessInvocation` remains good for.** Not deleted by this lane. It stays as a downstream -adapter for the emit-host harness — fixed option spelling to `LiteralArg`, workspace file operand -to `WorkspacePath`, generated binary operand to `ProducedArtifact` — keeping effect provenance -*after* CLI emission. Two constraints: no jq or domain caller constructs those variants directly, -and jq does not route through the emit-host harness merely because the type exists. The longer-term -cleanup is renaming it and `InvocationArg` to their real scope (`EmitHostProcessInvocation`, -`EmitHostArgumentMaterialization`) — a separate replacement migration, not bundled here. - -**Generalizing that subsystem is NOT a prerequisite for the first vertical.** Landing stdin, cwd -and fd mapping on `Command`/`SpawnProcessRequest` before any jq migration turns one proof vertical -into a process-runtime project. The stable boundary is `CliCommandSurface + ProcessInput`; it -binds first through one narrow local jq realization whose entire content is the jq executable -identity, a splice of already-emitted arguments, and the process channel wiring — **no option -spelling and no domain filter**. An honest realization boundary, not another argv authority. Its -dissolution trigger: the same emitted surface binds to `extdeps.posix` `Command`. Emit a -`CliToolRef` and let each realization resolve `"jq"` or an absolute path. - -`v2.extdeps.posix` `Command` is the right conceptual layer, with four bounded deficiencies: - -1. `program: AbsolutePath` — a semantic invocation names a *tool*; resolution must produce the - path at realization time. -2. no stdin or file-descriptor actions on `Command`/`SpawnProcessRequest`. -3. `PosixByteString = FreeMonoid` does not visibly exclude embedded NUL, which POSIX - process arguments require. -4. no executed proof that the active `dag/` lane can consume this `src/v2` carrier. - ---- - -## 5. Ownership - -| owner | owns | -|---|---| -| `extdeps.tools.jq` | what raw output means, what exit-status mode means, that `--argjson` takes a name and a JSON value, which options repeat — and **which spellings exist and which is canonical** (`long = exit-status`, `short alias = e`, canonical preference, value cardinality) | -| shared `CliSyntax` | **how a chosen form is constructed** — a long name renders `--` + name, a short renders `-` + character, clustering where the tool row permits, joined vs separate values, option/operand/terminator order | -| `gunbc.bmc_fan_projection` | the `TEMP_SOC` policy, the fan curve, controller cardinality, the desired topology | -| `extdeps.bmc.*` | programs projecting a documented OpenBMC response or config field | -| realization | local vs SSH vs another handler | -| **no caller** | `-e`, `-r`, `--argjson`, `--`, their positions, or the choice between `-e` and `--exit-status` | - -The tool row owns *which forms exist and which is canonical*; the grammar owns *how a chosen form -is constructed*. An earlier revision gave both "canonical spelling" — two authorities for one -decision, the §3 violation this note is about, committed inside it. - -The split is by **what fact the program encodes**, not that jq executes it. `ProjectFanConfig` -embeds `TEMP_SOC`, the aggregate fan PID entry, desired inputs, minimum and failsafe duties, -hysteresis and exact-one-controller policy — gunbc's chosen topology, so it moves. -`SensorIntegerValue`, `ObjectMapperServiceCount` and `ObjectMapperServiceAt` interpret **upstream -OpenBMC response shapes** and stay under `extdeps.bmc` even though jq realizes them. The several -*remote* fan-query programs carrying a literal `TEMP_SOC` are the ambiguous middle: they belong -beside the projection authority, or should derive that value from it, not keep the literal in -`extdeps`. - -Filters leave both `extdeps.tools.jq` (business policy in extdeps is a layer inversion) and -`openbmc.PasswordSshTransport` (SSH does not own what the remote program means). They do not all -land in one generic "workflow layer": policy-bearing ones go to `gunbc.bmc_fan_projection`, which -already owns the board, firmware, curve, config path, controller entry and tach inputs. - ---- - -## 6. Rules - -1. `-e`, `-r`, `--argjson`, `--` and their aliases may appear **only** in cited concrete-syntax rows. -2. Semantic modules construct no option node and no argv string. -3. Per-tool lowering contains no bespoke option-selection fold; semantic variants select grammar - rows exhaustively. -4. Transport consumes an inner invocation. SSH may **not** be modeled as `append(ssh_prefix, inner.argv)`. -5. The CLI inverse consumes `List`, not shell text — and it is **partial and - ambiguity-aware**, not bijective: clustered short options, optional option-arguments, operands - beginning with `-` and tool deviations admit multiple parses, so it answers - `CliParseUnique | CliParseAmbiguous | CliParseRefused`. It is **not** a Wave 1 acceptance - criterion; current argv remains an offline behavioral oracle. Shell text is parsed by the shell - grammar first; only a static literal simple command projects back to a CLI surface. -6. Every vertical deletes its prior argv authoring site **in the same cut**. "New tree plus old - helper for compatibility" creates two authorities. -7. No new production function returns `List` or `ArgvCommand` containing tool flag literals. -8. `ArgvCommand` builders and `transport shell { argv: [...] }` are migration substrates, not the - final authority. - ---- - -## 7. The cut - -Per the §3 replacement-migration rule, stated at exact identity grain. - -**Authority being replaced:** argv-as-authored-authority for jq invocations. - -**Root consumer × subject population:** the four live `openbmc.JsonProjection` operations -(`ProjectFanConfig`, `ObjectMapperServiceCount`, `ObjectMapperServiceAt`, `SensorIntegerValue`) -plus the seventeen `openbmc_password_ssh_transport` remote jq operations. - -**Recut: the first vertical is LOCAL only.** An earlier revision put local execution and SSH in -one "smallest complete" vertical. SSH adds a second language target, RFC 4254 string loss, -portable-word refusal, quoting, and the latent `sshpass -d 0` collision. The unit is **one local -jq vertical, demonstrated by one feature-rich migration and one discriminating counterexample.** - -**Wave 0 — carrier probe.** Stacked with its first production consumer, never mergeable alone. -A probe-only non-text serializer beside the text one; sealed `CliSurface`; existing text emission -byte-identical; argument boundaries observable *by execution*; direct/cast/unadmitted-mint REDs; -explicit refusal when no CLI serializer is wired. See §10 — the probe must execute, because it -will not refuse at compile time. - -**WAVE ORDER CORRECTED BY EXECUTION: 1B CUT FIRST, and the note was wrong, not the branch.** -Wave 1A was named first because it exercises the most axes at once, including a typed JSON binding -through `--argjson` — exactly why it cannot be first: that lowering is **not built**, and -`jq_invocation_lower` refuses it as `JqBindingLoweringUnwired`. `SensorIntegerValue` (Wave 1B) -migrated first because every axis is wired: program operand, stdin input, raw output, and -`JqProgramExit` selecting no exit-status option. Its falsifier role is undiminished by going first -— a counterexample does not require the thing it falsifies to exist yet. 1A follows once -`--argjson` lowers. - -**Wave 1A — `ObjectMapperServiceAt`.** The strongest exemplar: its argv exercises nearly every -load-bearing axis — `-e` truthiness exit, `-r` raw output, a typed JSON binding through -`--argjson` (an option with **two** values), stdin input, a jq program operand, exact -nonempty-string domain decoding, and a live recursive consumer. After migration the OpenBMC caller -stops reading `success`/`stdout`/`stderr` and matches -`OpenBmcServiceObserved | OpenBmcServiceProjectionRefused`. No layer above jq's cited rows can -name `-e`, `-r`, `-er` or `--argjson`. - -**Wave 1B — `SensorIntegerValue`, the required falsifier.** Immediately stacked, *before* the -abstraction is advertised as ready for repetition. The counterexample to a design overfit to `-e`, -proving four things Wave 1A cannot: `JqProgramExit` selects **no** `JqCliOptionExitStatus`; -`JqOutputAbsent` is not automatically a refusal; termination and output presence are orthogonal in -the observation model; one lowering supports two opposite domain policies unchanged. Its -acceptance population: - -``` -{"data": 41.6} -> Observed 42 {"data": "41"} -> Absent -{"data": 41} -> Observed 41 {"data": null} -> Absent -{} -> Absent invalid JSON -> Refused -two numeric inputs -> Refused, never first-pick jq nonzero -> Refused -``` - -**Wave 2 — first SSH migration.** Only after the local vertical is green: `CliSurface` → shell -simple-command tree → shell grammar emission → RFC 4254 command string, reusing the grammar-owned -quoting the effect-plan Bash path demonstrates rather than retaining `shell_quote`. Owns the -portable-word limitation, file-input-only admission, and a typed refusal for stdin-fed remote jq -while `sshpass -d 0` holds fd 0. Then the remaining remote population is mechanical. - -Emission-side witnesses, proven separately from semantics at every wave: - -``` -the semantic property selects the option identity (row level 1 perturbation) -that identity selects its canonical spelling (row level 2 perturbation) -JqProgramExit emits NO exit-status option -the jq program is exactly one argument -stdin content appears NOWHERE in argv -argument order is stable -direct / cast / unadmitted-mint construction refuses -the old transport argv site is absent in the same cut -no success: Bool survives on the new path -``` - -Both row levels need independent perturbation controls: a spelling perturbation proves spelling -is authoritative but says nothing about whether a required semantic property can be omitted. - -**Do not make the first vertical solve SSH.** Local exemplar and transport composition are -separate proofs. - -**First vertical (smallest complete):** - -1. `JqInvocation` and its semantic axes. -2. `CliInvocationTree`. -3. Shared CLI grammar rows + jq's cited option rows. -4. Emission to `List`. -5. Local resolution into `extdeps.posix` `Command`. -6. A process-input realization for jq stdin, separate from argv. -7. One live jq population cut over, with its prior argv authoring site deleted. - -The SSH handler is **Wave 2 and deliberately absent from this list.** An earlier revision carried -it as item 7, contradicting "Do not make the first vertical solve SSH" above and the recut in §7. -SSH adds a second language target, RFC 4254 string loss and the `sshpass -d 0` collision; none is -needed to prove a local vertical. - -The carrier lands **with** its first live consumer, or in an immediately preceding stacked PR -containing an executing consumer. A standalone carrier PR would reproduce the unconsumed -`v2.extdeps.posix` situation exactly. - -**Deleted at cutover:** `extdeps.tools.jq` `jq.Json.ExtractRaw` (zero consumers — deleted first, -not kept beside the new authority); the migrated sites' `transport shell` argv literals; the -seventeen re-inlined `sshpass`/`ssh` prefixes. - -**Explicitly NOT in this cut:** adding variants to `InvocationArg`; updating its 72-reference -population; renaming the harness carrier to `EmitHostProcessInvocation`; repairing the C/Go/Python -descriptor inconsistency; making `HostTransportDescriptor` a general process authority; modeling -jq's expression language; the other 575 argv lines. Those are independent lanes. - -**Deliberately not built:** a coverage lens over argv arrays. Under the replacement-migration -rule that is a census over a structure being deleted — the deletion is the census. The plan -proposes one; this note declines it. - ---- - -## 8. Witnesses - -Semantics and serialization are witnessed separately: - -- **semantic** — the caller requested the correct jq behavior. -- **emission** — that semantic tree lowers to the expected argument vector. -- **perturbation** — changing the jq option row changes or refuses the emitted vector; the - control proving the rows are load-bearing, not decorative. -- **domain** — absent, null, false, blank, wrong-type and multiple outputs cannot reach the write. -- **positive control** — one valid token still does. -- **absence control** — a non-numeric sensor reading still decodes to `OpenBmcSensorValueAbsent` - and does not become a refusal. - ---- - -## 9. Rung, by exact subject - -An earlier revision said "mechanically preventable, because a caller can still construct a raw -`List` and nothing yet refuses it." **If nothing refuses it, it is not mechanically -prevented** — it is measured or mitigated. It also *understated* what the migrated path can reach. -One number for several populations was the error. - -| exact subject | honest rung after the first vertical | mechanism | -|---|---|---| -| forging a `CliSurface` **by record literal or cast** | structurally impossible | `sole_constructor`, whose cross-module refusal is witnessed by the corpus `sole-constructor-cross-module` probes — not re-proved here | -| minting a `CliSurface` **whose content did not come from rows** | **mitigatable only** | *no wall.* `serialize_cli_arguments` is public and accepts caller-supplied `lex` and fragments, so any module can obtain a well-typed `CliSurface` carrying arbitrary text | -| omitting `--exit-status` from an accepted truthiness invocation | **structurally impossible within the new path** | exhaustive semantic→option selection; zero-or-many rows refuse; no mint on miss | -| hand-authoring flags inside a migrated route | **structurally impossible after cutover** | handler takes a semantic invocation, emits internally, old argv site deleted | -| reintroducing the exact old operation transport | mechanically preventable | structural deletion witness | -| a computed argument vector losing its argument boundaries at the host edge | **mitigatable only** | the `ProcessArgvExpansion` arm makes boundaries survive *on the carrier path* and refuses every malformed shape it is handed, but the guessing path it sits in front of is untouched for every value that is not this carrier — 21 operations still splice a declared `List` param. This is a repair at ONE seam, not a wall over the class | -| a domain module authoring jq program source | **mitigatable only** | §12 — `JqProgram` is public; nothing prevents it | -| decoding a jq exit code against the wrong contract | **structurally guaranteed on the new path** | §13 — the policy travels on the plan and `jq_classify_observation` is the only reader; a domain module has no exit code to misread | -| writing another raw jq argv elsewhere | **still writable** | unrelated raw shell/process routes remain | -| arbitrary raw argv corpus-wide | outside this vertical | later process/transport confinement | - -So: **the first migrated jq path can be structural now; the repository-wide raw-jq class stays -open; the repository-wide raw-argv class is a later migration.** - -**The seal is on the shape, not on the provenance — corrected 2026-08-19.** The row above -previously read *structurally impossible now — only the row emitter, serializer and decoder may -mint*: false, and the §4b inflation this document warns about, asserted about its own subject. -`sole_constructor` closes the record literal and the cast; it says nothing about a **public fold -that takes caller-supplied inputs**, which `serialize_cli_arguments` is. An earlier claim that -`CliSurface` was therefore *stronger* than `TransportScript` had it backwards: `TransportScript`'s -mint is `admit_callers`-sealed to two named production declarations; `CliSurface`'s is not sealed. - -**Ceiling and trigger.** The obvious repair — `admit_callers` on the serializer — is refused on §3 -grounds: `v2.std.compilers.cli_surface` would name every tool module that may emit a CLI, dispatch -fused into the interface. So the wall belongs on the **input**: a sealed `AdmittedCliInvocation` -that only a tool's own row-derived lowering can produce, with the serializer total over it. That -is **not built**, and its mint design is genuinely open — whatever seals it faces the same -recursion one level up. Until it lands, provenance sits at *mitigatable*; the practical -containment is that the only public jq entry point is `jq_invocation_process_plan` — a -convention, not a wall. - -**Application-argument typechecking is not the terminal trigger.** The `04_infer` gap is real — -`explicit_return_conformance_note` records that conformance is judged only by the -ground-kernel-scalar and ground-element-collection discipline — but it is neither *necessary* for -sealing this path nor *sufficient* to eliminate alternate raw routes. It matters when a sealed -carrier crosses an open function boundary and correctness rests on argument conformance; the first -vertical avoids that: no caller receives a public `execute(surface: CliSurface)` to smuggle into. -The domain caller passes a `JqInvocation`; the handler emits and consumes the sealed surface -internally. - -The real terminal trigger for the broad jq class is a **dominator condition**: - -> every production path capable of executing jq is dominated by the semantic jq handler, and raw -> process/shell routes cannot name jq except through an explicitly retained escape population. - -General argument typechecking strengthens the boundary; it does not prove that property. - -## 10. Open questions - -1. **Carrier parameterization — and the probe will NOT refuse.** The current answer is not - "unknown diagnostic" but: **there is no carrier-mismatch refusal on this path, so a compile-only - probe false-greens.** `emit`, `serialize_target` and `serialize_source_for_emitted` are fixed - to `Medium`, and underneath them `target_serialize_source_from_model_bounded` returns - `TargetText` rendered through `target_source_medium`. Passing a `cli_target` still takes the - text serializer. And `v1.04_infer` `explicit_return_conformance_note` records that declared - return conformance is judged only by the ground-kernel-scalar and ground-element-collection - discipline, so a structured mismatch such as `Outcome>` vs - `Outcome>` yields no diagnostic. **Do not treat a green compile as a - positive result.** - - The smallest discriminating probe is a **sibling concrete seam**, executed rather than compiled - — `emit_cli_probe` binding `translate`'s target tree to a non-text serializer — asking: can - `translate`'s output be consumed by a non-text serializer while the text serializer stays - byte-identical? It does not ask the current `emit` to produce what its signature cannot - express, and does **not** start by genericizing `TargetModel`. - - Controls: existing `emit` returns the exact prior bytes; the probe returns a sealed `CliSurface` - whose argument boundaries are inspectable **by execution**; swapping the CLI serializer for the - text one must *fail the test*; direct, cast and unadmitted-mint construction of `CliSurface` - refuse; a missing serializer raises a typed `TargetSurfaceSerializerUnwired` rather than - falling back to source text. - - Only after that receipt is the seam chosen. Prior: `TargetSurfaceSerializer` is the smaller - terminal seam, with the existing `emit` kept as a compatibility wrapper selecting the text - serializer — parameterizing every `TargetModel` consumer before a non-text target has proven it - necessary is the larger, less reversible move. - -2. **`sshpass -d 0` and stdin.** The password occupies fd 0; a remote stdin-fed jq would collide. - Latent, not live — all seventeen remote sites use file operands. Must be a typed refusal, not - an accidental limitation. Options: remote file-input only; password delivery on another - descriptor; a framed remote receiver. -3. **Portable-word allowlist.** `gunbc.typed_argv_exec` bounds what may cross SSH. jq programs - contain spaces, pipes, brackets, parentheses and quotes — outside that alphabet. The shell leg - must own canonical shell-word serialization with injection controls, or refuse. -4. Which of the 315 computed-head argv lines hide further wrappers. -5. **The remote jq rows do not preserve the jq program as one argument. Source-proven.** - Traced end to end, with the local half verified in this tree: - - - `v1_interpreter.rs` `push_shell_argv_tokens` pushes each evaluated `String` **verbatim** — no - splitting, no quoting — and both exec branches call - `Command::new(&argv[0]).args(&argv[1..])`. Despite its name, `transport shell` here is - **direct process execution**: no local shell, no `shell_quote`, no command-string renderer. - - `sshpass` parses its own options, copies the remaining argument pointers, and `execvp`s them - unchanged. - - OpenSSH's client consumes the `--` after the destination as its **own option terminator** - (not a remote quoting construct) and builds the remote command by appending each remaining - argv member separated by one literal space, with no escaping. - - OpenBMC's default SSH server is **Dropbear**, not OpenSSH — its `run_shell_command` - invokes the user's login shell with `-c`. - - So `["jq", "-er", "[.zones[].pids[] | select(...)] | length", path]` arrives remotely as - `jq -er [.zones[].pids[] | select(...)] | length /path`, and the remote shell reads the - unquoted `|` as pipeline operators. The law the code needs is - `shell_parse(join(map(shell_quote, argv), " ")) == argv`; it assumes - `shell_parse(join(argv, " ")) == argv`, which holds only inside the portable-word alphabet - these filters are outside — the alphabet `gunbc.typed_argv_exec` exists to bound. Dynamic values - such as `config_path` are unquoted too; the current path happens to be shell-safe, but - `NonEmptyStr` does not establish that. - - **Split the judgments rather than calling the whole thing broken:** - - ``` - RemoteJqArgumentBoundaryPreservation Violates — source-proven - DeployedSshClientIsStandardOpenSsh Unverified - DeployedBmcServerUsesDropbearShellExec StronglySupported, not observed on this firmware - AffectedOperationReachability Unverified - ObservedProductionManifestation Unverified - ``` - - The saving conditions are enumerable and none is visible in the repository: a custom binary - named `ssh` that shell-quotes (most plausible, since `sshpass` resolves through `PATH`); filters - already carrying canonical outer quoting; a forced-command or structured receiver decoding an - encoded argv; a row whose program is entirely portable words. - - **The executed control** uses the exact current prefix and direct argv execution — not a local - shell — with `jq -n`, so it needs no stdin or file and cannot mutate BMC state: - - ``` - portable positive ["jq","-n","-r","7"] -> exit 0, stdout "7" - current form ["jq","-n","-r","[1,2] | length"] -> intended "2" - serialized form ["jq","-n","-r","'[1,2] | length'"] -> expected "2" - ``` - - Discriminating result: portable and serialized controls succeed while the current form does not - produce the same exit/stdout observation. Capture local client version, remote SSH banner and - remote login shell. **If the current form unexpectedly yields `2`, that is positive evidence for - an unmodeled saving layer**, and the next probe captures the command string seen remotely. - -6. The SSH slice needs a discriminating program containing spaces, a pipe, quotes, brackets and a - value containing a single quote, and must reuse the grammar-owned quoting the effect-plan Bash - path already demonstrates rather than retaining `shell_quote`. -7. Reverse ingestion — reading argv back through the same rows — is the terminal second reading - and is **not a prerequisite for the first emitter cut**. -8. **Measured substrate observation — `Optional` at a primitive `T` in record-field - construction position.** `stdin: Optional` with an inline `Present { value: … }` - refuses with `expected 'Coproduct(Optional)', got 'Primitive(String)'`, independent of the - payload expression: a pattern-bound variable, a renamed binding, a shorthand pattern and a bare - `"x"` literal all reproduce it, while `stdin: Absent` resolves. Inline `Present` into a declared - `Optional` field is ordinary elsewhere (`gunbc.witness_row_cost` `basis`, - `gunbc.declared_import_closure_binding` `binding_source`, - `v2.workflow.effect_plan_bash_materialize` `operation`), but every such `T` is a record or - coproduct — so the discriminator is the **primitive type argument**, not the field form or the - explicit-vs-sugar spelling. This lane uses the corpus's own `String?` sugar, the existing - modeled form for an optional field (625 sites), not a workaround. **The observation is - recorded, not diagnosed:** whether sugar and explicit generic genuinely differ at a primitive - argument, or the refusal has another cause this bisect did not separate, is unestablished — a - §5 line-stop is owed before anything in this lane relies on the distinction. - -9. **A live Class B specimen, in this lane's own new code (found by review, 2026-08-19).** - `extdeps.tools.jq` `jq_option_canonical_spelling` calls `cli_long_option_spelling` while the - module's import list named only `CliArgumentSyntax`, `CliSurface` and `serialize_cli_arguments`. - The call resolved — and every Wave 1A assertion passed — because `cli_surface` was already in - the assembled closure via those imports: the accidental coverage DESIGN's import-strip thread - records as **blocking all further `dag/**` import stripping**, binding by pool membership, not - the bare-reference closure. The bystander probe above does *not* catch this — it refuses a - *qualified* import of an unexported name, whereas this is an *unimported bare reference* into a - module present for other reasons; only the second is silent. Fixed by naming the symbol in the - import list. **The general defect is untouched and not this lane's:** nothing refuses the next - such reference, and a green witness is not evidence that a module's imports are complete. - -9-ter. **BLOCKER, PROVEN BY EXECUTION: the argv transport silently destroys argument boundaries - for computed lists (2026-08-19).** A `List` built by folding is CONCATENATED into one - argv word; the identical literal list is SPLICED. Same declared type, elements and handler, two - different processes. - - **Receipt, through the production handler `jq.Process.RunWithStdin`:** - - ``` - arguments = ["--raw-output", "."] literal -> exit 0 (jq ran; two argv words) - arguments = fold_list(... same two ...) folded -> exit 2 - jq: Unknown option --raw-output. - ``` - - And from the first wet run of the real vertical, three lowered arguments arriving as one: - - ``` - $ jq --raw-output--exit-status.missing (exit=2) - ``` - - **Mechanism** — `v1_interpreter.rs` `push_shell_argv_tokens`. `Value::List` splices each item. - `Value::Variant` tries `value_as_host_string` FIRST, which walks a free monoid and concatenates - it into one String, pushing a single argv word; only on `None` does it fall through to - `free_monoid_to_vec` and splice. A folded list is monoid-encoded with all-`Str` elements, so - the concatenating reader wins. `gunbc.WitnessBin.Run` escapes only because its `args` is a - literal at each call site. - - **Independently attacked:** an outside reviewer failed to refute this — the source matches the - reading, and the collapse occurs BEFORE `Command::args`, ruling out a downstream shell or SSH - join. - - **Why neither obvious repair is correct.** Reordering the arms would splice this case but SHRED - a modeled `String`, because a String is itself a monoid here (`value_as_host_string` - reconstructs one from code points): both readings succeed on a monoid of `Str`s, and the runtime - cannot separate *list of arguments* from *string assembled from pieces*. The disambiguator is - the DECLARED TYPE, erased at that seam — the missing application-argument typechecking that - `target_model` `target_text_carrier_scaffold_note` names as the trigger for every type-based - construction guarantee at a call boundary. Hand-authoring a literal argv in the handler defeats - row-derived lowering and leaves the next caller silently joined — the workaround shape §5 treats - as a line-stop. - - **Rung:** a §5 fail-open — it fabricates one plausible argument instead of refusing — and the - LANE'S OWN DEFECT CLASS one layer below the lane: Wave 0's witness asserts fragments concatenate - within an argument and never across, and the transport violates exactly that. Also a live - specimen of the model↔realization fork DESIGN tracks as an open thread. - - **CORRECTION (same day, adversarial review): "blocked on application-argument typechecking" - was WRONG, and too strong.** The diagnosis survived independent verification — the reviewer - traced `build_service_param_env` (clones call arguments with no conformance check), - `dispatch_shell`, and `Command::new(&argv[0]).args(&argv[1..])`, confirming the vector is - malformed before `Command` sees it, so no later join is an alternative explanation. The refusal - to reorder the branches was upheld. The conclusion was not: **a correct construction is - available now.** - - In-tree counter-evidence: `OperationInputValue = InputText | InputTextList` already carries - tagged list intent, and its realization converts that tag into a native `Value::List` - (`free_monoid_to_vec` then `list_value`), so the later flattening splices. Tagged intent → - native list is proven possible *without* global argument typechecking. - - The deeper correction: even a fully typechecked system would not answer the transport's - question. `List` does not say whether a collection becomes several argv words, one JSON - argument, a comma-joined option value, or repeated option/value groups. **That is a transport - ROLE, not a data type**, to be modeled rather than inferred from runtime encoding. - Application-argument typechecking remains independently valuable and is neither necessary nor - sufficient here. - - **The construction:** one explicit nominal carrier — `ProcessArgvExpansion { surface: CliSurface }`, - sealed — with an interpreter branch BEFORE the generic string/list heuristic that requires the - nominal `CliSurface`, iterates its arguments, and pushes exactly one host word per - `CliArgument`, concatenating that argument's fragments into that word. It carries `CliSurface` - rather than `List` so the payload cannot be dynamically ambiguous: a modeled string - cannot impersonate a list of nominal `CliArgument` records, and `value_as_host_string` is used - only on one admitted argument's text, where concatenation IS correct. Wave 0's theorem enforced - at the host edge. - - **Explicitly not to be landed** (each rejected for a stated reason): reversing the two `Variant` - branches (damages modeled strings); `map(identity)` to coerce a native list, rebuilding the - emitted argv as a literal, or join-then-shell-split (runtime-representation workarounds, and the - last reintroduces shell parsing); routing jq through `OperationInputValue` permanently (that - carrier belongs to declaration-owned operation materialization); accepting BOTH a raw - `List` and the explicit carrier (two authorities, future callers free to pick the unsafe - one). The raw-list handler signature is REPLACED, not retained for compatibility. - - **Consequence for this lane:** the cutover is blocked at the execution seam *until that carrier - lands* — a build, not a decision. The semantic spine is built and resolving; no consumer can - move until then, because every migrated call site computes its argv rather than authoring it - literally — precisely what the migration asks callers to do. - -9-bis. **The Class B specimen RECURRED, in code written after it was documented (review 53669, - 2026-08-19).** `jq_invocation_process_plan` used `bind_outcome` and `outcome_accepted` with - neither in the module's `v2.std.diagnostic` import list. Same mechanism as the - `cli_long_option_spelling` case one commit earlier, same lane, same author who had just written - that entry. - - **Two things this settles.** First, the mitigation must run *after every addition*: the - unimported-reference sweep was clean, then a function was added and the sweep not re-run. A - one-time audit does not close a class whose defect is invisible. Second — correcting the review - that found it — the stated consequence, "as written this file will not resolve", is **false**, - and its falsity is the point. The file resolves, and - `stdin_survives_lowering_into_the_process_plan` executes green *through the affected function*, - because `v2.std.diagnostic` is already in the closure via other imports. A reviewer predicting a - resolution failure would be refuted by running it and might dismiss a real finding. The correct - statement: the reference is unimported, it binds by pool membership, and **nothing in the tree - will tell you** — not the resolver, not CI, not a green witness. - -10. **The spelling-seam extraction is right, but its obvious shape is forbidden — corrected - 2026-08-19.** The static-dependency finding stands: at execution grain the jq fold reaches no - compiler machinery, but at dependency grain `cli_surface` imports `v2.std.compilers.target_model`, - whose closure carries target-representation, host-runtime and node-query machinery. The property - worth having is not *nobody calls translate* but *this fold cannot acquire that dependency - accidentally*. - - The proposed repair — extract a shared `spell_concrete_syntax_fragments -> Outcome` - below `target_model`, with the TargetText serializer wrapping its result — **violates an - operator ruling in the file it would edit.** `target_model` `target_text_carrier_scaffold_note` - (operator, 2026-07-15) places `TargetText` beside `bound_tokens_source_text` so that fold is - *carrier-native*: the single `String -> TargetText` introduction is `text_atom` applied to one - grammar-classified token **inside** the fold, the only join is `target_text_seq`, the only exit - is `render_target_text`, and there is deliberately **no** `String -> TargetText` lift of an - already-composed unit — "that direction is the smuggle the wall closes." Wrapping a composed - `String` is that lift. - - **The corrected decomposition, better anyway.** The shared authority is *what is this token's - spelling* — `lex_rules_literal_for_class` and `bound_spelling_from_map`, both token-grain — - **not** *how spellings are concatenated*. Concatenation is carrier-specific: emitted source - composes through `TargetText`, an argv argument into a `CliArgument` — different media. So the - extraction moves the token type and the two token-grain lookups into - `v2.std.compilers.concrete_syntax`, `target_model` imports them back (re-export proven — see - below), and each carrier keeps its own fold. No composed `String` is lifted, `cli_surface` stops - importing `target_model`, and one lookup authority serves both. - - **Re-export is measured, not assumed.** A three-module probe showed a consumer importing a - symbol from a module that merely imported it resolves and executes. Alone that proves nothing, - since this resolver can bind by *pool-membership coincidence* (DESIGN's Class B finding). The - discriminating control: importing the same symbol from a bystander module that never saw it - **refuses**, located — `name 'ProbeToken' not found in module '...bystander'` — even with the - defining module in the pool. Qualified-import resolution is import-list-driven, so - `target_model` importing the seam back keeps its 167 other importers untouched. - - **Not performed.** `target_model` is load-bearing and carries the ruling above; the move is its - own increment under its own review. - -11. **A downstream typed verdict may be the redundant lower rung, not a peer** (raised by - `eager-wren-138`, 2026-08-18). `gunbc.host_effect_realize` `bmcweb_token_extraction_verdict` - refuses blank stdout as its own decode — the wall that closed that fail-open, with `jq -e` only - loudness beside it. If the semantic layer makes *absence-is-a-value* unwritable at the decode, - that verdict is a second representation of one requirement (§2/§3) and must **dissolve into** - the decode. A §4b climb: its discriminating RED stays enrolled against the new decode while the - production check is deleted. Open because it is not established whether the requirement is - exactly one nonempty string at *every* consumer or only at the credential path; the OpenBMC - sensor path models absence as a legitimate third state (`OpenBmcSensorValueAbsent`), so a - blanket absence-is-refusal decode would be the state-space collapse this lane exists to remove. - ---- - -## 11. Corrections receipt - -Claims asserted during this design and refuted by measurement, kept so they are not re-derived: - -- *"The declarative `transport shell` form is fixed-arity and cannot express variadic options."* - **False** — `ssh.Session.ExecArgv` and `ExecPortableWords` splice `List`. -- *"SSH becomes a bound handler over the same tree."* **Incomplete** — RFC 4254 forces a string - at that boundary; structure cannot cross it as structure. -- *"`ProcessInvocation` is the general process carrier."* **False** — it is an emit-harness carrier; - its argument variants are provenance, not CLI roles. -- *"`SensorIntegerValue` has no consumers."* **False** — two live consumers, and its empty-output - branch is a modeled `OpenBmcSensorValueAbsent`, so an `-e` sweep would have collapsed a correct - three-state decode into a refusal. -- *"`ExtractRaw` and `SensorIntegerValue` are both latent traps."* **False** for the second; only - `ExtractRaw` is zero-consumer. -- The plan's census (262 lines / 37 files; 148/51 success/exit_code) is **stale**; measured - 597/129 and 173/69. -- *"`JqProducedNoResult` is a general arm on every jq execution."* **False** — verified against - `jqlang.org/manual/`: default jq exits 0 regardless of output, and only `--exit-status` yields - exit 4 for "no valid result was ever produced". Claiming the arm unconditionally would have - reintroduced the `Absent → Refused` collapse #8454 had just withdrawn. -- *"'Exactly one nonempty string' is a decode the domain can perform."* **Over-claimed** — jq emits - a stream and `-r` permits newlines inside a raw string, so a collapsed stdout `String` is - ambiguous. `--raw-output0` exists precisely because of this. -- *"The class sits at mechanically preventable."* **Too coarse** — that is the corpus-wide claim; - the migrated path can reach structurally guaranteed now via a sealed constructor. -- *"Filters move to `gunbc.bmc_fan_projection`."* **Needed splitting** — only policy-bearing - programs move; OpenBMC wire decoders stay in `extdeps.bmc`. -- *"`JqProducedNoResult` is an arm beside `JqExitZero`."* **False** — the live `SensorIntegerValue` - case holds both at once (exit 0, zero results), so termination and output presence are a - product, not a sum. Written as a sum, the live case is unrepresentable. -- *"Removing `success: Bool` closes the contradiction."* **Insufficient** — an unsealed - `JqObservation` lets `{ exit: JqExitZero, exit_code: 4 }` be authored, the same contradiction - under richer names. The carrier must be `sole_constructor`, minted only by the decoder. -- *"The class sits at mechanically preventable because nothing refuses it."* **Internally - inconsistent** — if nothing refuses it, it is not prevented. Replaced by the subject-grained - matrix. -- *"jq owns how it spells options AND `CliSyntax` owns canonical spelling."* **Two authorities for - one decision**, committed inside a note about exactly that. -- *"The carrier probe will hit a named refusal."* **False** — `emit`/`serialize_target` are fixed - to `Medium` and structured return mismatches fall outside the judged conformance - population, so a compile-only probe **false-greens**. -- *"The smallest complete vertical includes SSH."* **False** — SSH adds a second language target - and three independent failure modes; the unit is local-only. - ---- - -## 12. `JqProgram` is public, and the domain still authors jq source - -Raised by the `shell → dag` session against the `SensorIntegerValue` cut: after the migration, -`extdeps.bmc.openbmc_fan_control` no longer authors argv — but it *does* author - -``` -data openbmc_sensor_integer_program: String = - "if (.data | type) == \"number\" then (.data | round) else empty end" -``` - -so the workflow moved from authoring shell to authoring jq. `JqProgram { source: NonEmptyStr }` -is a public constructor, so every domain module may do the same. - -**The objection is right about the residue.** The cut established something narrower than "the -domain stopped authoring foreign syntax": it stopped authoring *argv*. OpenBMC no longer names -`-r`, `--raw-output`, an option spelling, an argument position, argv[0], or the stdin routing — all -derived from jq's cited rows, the property the two-level derivation buys. The jq *program* is -untouched. - -**The proposed remedy — keep `OpenBmcSensorIntegerProjection { content }` public and have the -handler privately select a sealed program identity — is refused as stated, on §2.** It does not -decompose the program; it relocates the same string behind an indirection and adds a registry. -If the handler is OpenBMC's, the jq source is still in the OpenBMC layer with a wrapper added. If -the handler is jq's, `extdeps.tools.jq` knows what a BMC sensor reading is — a layer inversion. -Neither beats the existing row, and the second is worse. A sealed identity buys something only if -*derived*, which is the real answer: - -**The terminal shape is that the program is not authored at all.** `.data`, "is it a number", -"round it", "otherwise nothing" are a modeled JSON projection — a path, a type guard, a rounding -policy, an absence arm — of which a jq program is ONE realization, as an argv vector is one -serialization of a `CliSurface`. The domain declares the projection, jq's module derives the -program text as it already derives option spellings, and a second realization (a native JSON fold -with no process) comes free. The same move the lane made one layer out, applied one layer in — -and what stops this work generalizing into a thousand bespoke jq programs where it found a -thousand bespoke argv lines. - -**Rung, honestly.** Domain-authored jq source is **mitigatable**: nothing prevents it; the only -things between it and an arbitrary computed program are that the row is `data`, not a function — -so it cannot vary per call — and review. Not a wall. - -**Dissolution trigger:** a modeled JSON projection carrier with jq as a derived realization. Until -it lands, `JqProgram` stays public and this section is the reason. Deliberately NOT in the current -cut: a JSON projection algebra is its own vertical, and bundling it would repeat the Wave 1A -mistake of selecting the richest thing first. - - ---- - -## 13. The observation contract travels with the plan - -Raised as "`JqAdmittedProcessPlan` cannot be decoded" by the `shell → dag` session. Verified -against the code and **worse than raised** — a live latent defect in the cut itself, not a missing -convenience. - -The sensor decoder read `exit_code`, `stdout` and `stderr` directly and mapped every nonzero exit to -a refusal — correct under `JqProgramExit`, silently wrong under `JqLastResultExit`, where jq -documents exit **1** as "the last output was false or null" (a legitimate *value*) and exit **4** -as "no valid result was ever produced" (*absence*). Nothing connected the decoder to the policy -that produced the argv, so changing `exit_policy` would leave the decoder answering the previous -contract. §5's own tell: the declaration is edited while the realization goes on lying, and the -witnesses stay green because they never varied the policy. - -**Repair.** `JqAdmittedProcessPlan` carries `exit_policy`, so the plan is self-describing. One -authority, `jq_classify_observation`, reads an exit code *against* the policy that asked for it and -returns `JqOutputPresent | JqOutputAbsent | JqExecutionRefused`. The domain matches those arms and -decides only what OpenBMC alone can — whether a present projection parses as an integer. No domain -module reads a jq exit code any more. - -**Discriminating evidence.** Five assertions hold one observation fixed and vary only the policy: - -``` -exit 1, stdout "false" under JqProgramExit -> Refused -exit 1, stdout "false" under JqLastResultExit -> Present <- the policy-blind decoder fails here -exit 4, stdout "" under JqLastResultExit -> Absent -exit 2, stderr "usage" under BOTH -> Refused -exit 0, stdout " " under JqProgramExit -> Absent -``` - -The second row discriminates: a policy-blind decoder must answer `Refused` for exit 1 under both -policies, so it cannot make rows one and two true at once. The fourth row keeps the law from -overfitting — the policy changes how *some* codes read, not whether jq can fail. - -**This also closes the point `eager-wren-138` reached from the other direction.** That session -proposed sweeping `-e` across jq sites as hardening; the counter was that `-e` reports on the *last -output*, converting legitimate `false`/`null` into failure. Both facts are now modeled: selecting -`JqLastResultExit` changes the *decode*, not just the flag, and absence stays distinct from refusal -on both arms. - ---- - -## 14. Verification scoped to the consumers I already knew about - -Recorded as the third instance of one failure mode in this lane's own work; the first two were -caught by other people. - -Changing `openbmc_sensor_integer_projection_result`'s signature broke two enrolled witnesses -(`failed_sensor_projection_cannot_become_absent`, `successful_empty_sensor_projection_is_absent`) -that call the decoder directly. I did not find them: I selected witnesses by asking which files -import `openbmc_fan_control.dag` **and then hand-picking three** — the verification denominator was -a list I wrote, not one the tree produced. The break surfaced only when a resolve failed on an -unrelated run. - -The same shape produced the two earlier defects: an unimported-reference sweep run *before* the -last addition, and a grep whose zero result was accepted without a control that must hit. Each -time the mechanism was sound and the **denominator** was authored. - -**A third instance, one level up, caught by CI after the above was written.** The rule below fixed -the witness denominator and was still not enough: I minted `type ExpectedOutcome` in the jq witness, -colliding with `gunbc.output_policy`'s load-bearing carrier of the same name, and the failure landed -in `output_policy_witness_test.dag` — **a file referencing none of my symbols, never touched by this -branch.** Bare-reference resolution is corpus-global, so a new top-level name can red a file no -per-file closure of mine would load. Every local run passed; the whole-corpus strict resolve caught -it. - -Two rules follow, both mechanical because the authored version keeps failing: - -``` -# every top-level name the branch ADDS, from the diff -- not from memory -git diff main...HEAD -- '*.dag' | grep "^+" | grep -oE "^\+\s*(type|fn|data)\s+[A-Za-z_][A-Za-z0-9_]*" | awk '{print $NF}' | sort -u -# each one must have exactly ONE declaration corpus-wide - -# and run what CI runs, before pushing, not after it fails -claim_executor --required-floor --source-root dag --source-root src/v2 -``` - -The second matters most: **a per-file run and the floor are different universes**, and only the -floor is the acceptance path. Reporting a per-file green as the floor is rung inflation against a -declared boundary (§4b) — citing the strongest path while another stays silent, committed against -my own change. - -**Rule adopted for the rest of the lane:** verification enumerates test functions from the FILE, -never from memory — - -``` -grep "^test fn" | sed 's/test fn \([a-z_0-9]*\).*/\1/' -``` - -— and every function in every witness file touching a changed signature runs, not a chosen subset. -§5's oracle principle applied to coverage: a population I author is not an observation of the -population that exists. - -**What the break demonstrates — the argument for the cut.** Both witnesses were re-enrolled -unchanged in *requirement* — a refusal must not decay into absence, an empty successful projection -must not decay into a refusal — while their *carrier* moved from a `success: Bool` triple to the -typed jq outcome. The migration also made a third requirement stateable that the old triple could -not: a present numeric projection is an observation. A replacement migration behaving correctly -(§3): deletion surfaced the load, the load was dispositioned as re-enrolled evidence rather than -restored by reflex, and the new representation is strictly more expressive. - ---- - -## 15. Wave 1B cutover receipt - -Executed on BuildBuddy against the branch tree. Counts are from enumerating `^test fn` in each -file, not from a chosen subset (§14). - -``` -extdeps.bmc.openbmc_fan_control SensorIntegerValue operation DELETED - consumers rewired 2 (openbmc_sensor_value, openbmc_sensor_threshold_result) - both now route through openbmc_sensor_integer_projection (one shared fn, not two call sites) - argv authored by the domain after cutover 0 -- no -r, no --raw-output, no argv position, no argv[0] - residual foreign syntax authored 1 jq program row (S12, mitigatable, trigger recorded) - -test/claim/bmc_typed_operations_witness 29 / 29 pass - of which migrated onto the typed jq outcome 2 (requirement unchanged, carrier replaced) - of which newly stateable and added 1 (present numeric projection is an observation) -test/claim/jq_invocation_lowering_witness 9 / 9 pass (lowering) - 5 / 5 pass (S13 exit-policy classification) -test/claim/bmc_fan_converge_witness duty_curve pass -test/manual/process_argv_expansion_receipt case 4 pass -- real jq, exit 0 reachable only - with two argv words, through the production handler -``` - -**What this receipt does NOT establish** — the table reads stronger than the lane's position: - -- The argv splice defect is repaired at **one seam**, not closed as a class. 21 operations still - splice a declared `List` parameter through the guessing path. -- The other three `openbmc.JsonProjection` operations were unmigrated, and two of them - (`ObjectMapperServiceCount`, `ObjectMapperServiceAt`) needed `--argjson` lowering, which was - unbuilt. Both are now landed — see §17 (Wave 1A cutover receipt). The remaining unmigrated - operation is `ProjectFanConfig`, whose sole consumer is dead code; file-input lowering has since - landed with the remote population (Wave 2, §18), so its migration is unblocked but not yet cut. -- No negative falsifier exists yet proving a REST path reaches none of these carriers. -- **The wet receipt does not execute in CI — a declared gap, not an enrollment.** The hermetic - floor refuses `jq.Process.RunWithStdin` (no `mock_response`) and mocking would defeat the - assertion, so the file is named in `cli_run.rs` `floor_prepared_subject_exclusions`. No wet lane - exists to host it: the falsifier and wet batches died with the floor cut, and - `v2.workflow.required_floor` deliberately defers wet lanes until the question can be "asked - against a live consumer". The claim sits at **UNEXECUTED-IN-CI**, evidenced only by the recorded - run above and reproducible locally with: - - ``` - claim_batch --wet --source-root dag --source-root src/v2 --entry dag/test/manual/process_argv_expansion_receipt_test.dag --functions case4_expansion_carrier_splices - ``` - - **Re-enrollment trigger:** a wet lane with a live consumer exists again. - - The first attempt at this admission added rows to `gunbc.ci_layer_roots` - (`witness_exclusion_frontier` + `bin_witness_wet_entries`) with a commit message asserting they - would take effect. They did not — `run_required_floor` consults the Rust list and nothing else; - the CI receipt was an unchanged `modules_excluded=2`. Both rows were reverted: a roster row with - no live consumer is specification-without-execution, and the enrollment half would have *claimed* - an executing consumer for a witness nothing runs. Instructive mistake — I read a neighbouring - row's shape as the mechanism instead of tracing the mechanism's caller. -- The wet receipt run reports `[expectation-frontier] 1 site(s), 1 dispatch(es) undeclared: - jq.Process.RunWithStdin=1` — the new handler's dispatch is not declared to the expectation - registry. Left standing, not silenced, because it is the shape §5 asks for — typed, located, - counted, visible — but it IS an open item; a `PASS` line does not mean nothing else was reported. -- The 5 classification assertions and the wet receipt ran separately from the 29+9; a single run - of all 43 exceeded the 45-minute remote timeout, because every `gunbc run` pays a whole-corpus - typecheck. A cost-shape observation about the harness, not evidence about the code. - -## 16. The negative falsifier: the REST/CLI boundary becomes a row that reds - -Every prior section asserts, in prose, that the REST path constructs no process invocation. -Prose cannot be contradicted by the tree; this section records the mechanism that can. - -**The question already had an authority, so no second mechanism was minted.** -`v2.lens.realization_vocabulary_containment` answers "does module X reach construction vocabulary -from set V" and has since the TypeScript-only ratchet. The obstacle was that `V` was a literal: -`scan_facts_for_leaks_under` threads the *importer-path* axis as a parameter while -`module_is_target_ast_vocab` was welded into the predicate. A second lens for a second `V` would -be the §3 duplication this lens exists to detect, so the vocabulary axis was opened instead — the -§2 horizontal move, one axis rather than N copies. - -What that cost: - -- `RealizationVocabularySet` (name, modules, module_prefixes) and `module_is_in_vocab`. -- `target_ast_vocabulary()` derives from the existing `target_ast_vocab_modules` / - `target_ast_vocab_module_prefixes` rows rather than replacing them — those rows are consumed - directly by `gunbc.realization_vocab_confinement_census`, which has live claims against them. -- `is_vocab_leak_in` / `scan_facts_for_leaks_in` / `vocab_leak_count_in` / `vocab_leak_count_live_in`, - taking vocabulary and exempt-edge population as arguments. Every pre-existing entry point - delegates to these with the target-AST set, so no behavior moved. -- The exempt population is a **parameter**, not a global roster read, so "this vocabulary has - zero admitted exceptions" is a stated fact, not an accident of the target-AST roster naming no - CLI module. - -**Two sites were left target-AST-only, deliberately.** -`realization_vocab_leak_candidate_paths_from_facts` and -`realization_vocab_live_leak_edges_from_facts` feed the grandfathered-roster staleness check, and -every row in that roster is target-AST debt by construction — `RealizationVocabDebtClass` has no -other inhabitant. A second vocabulary reaches the lens with an empty exempt population, so it has -no roster to be stale against; parameterizing them now would answer staleness about a population -that does not exist. Trigger recorded in-file: the first non-empty exempt roster for a second -vocabulary. - -**The subject is not an empty universe** — the usual way a negative claim turns vacuous. The -scanned population (`dag/extdeps/bmc`, `dag/extdeps/transports`) *contains* a module that -legitimately reaches CLI vocabulary: `extdeps.bmc.openbmc_fan_control`, which this lane routes -through jq, in the same directory as `extdeps.bmc.redfish`, which does not. So the scan -discriminates *within* the population, and the RED control is live corpus data, not a planted -fixture: withdraw the one admitted edge and the count must become 1. Without that assertion the -positive result is indistinguishable from a scan that read nothing — the empty-observation narrow -DESIGN names, ⊥-as-answer conflated with ⊥-as-ignorance. - -The admitted edge is named at exact `(importer_path, vocab_module)` grain, not by category or path -prefix, so a *second* jq-reaching module anywhere in the scanned roots reds instead of being -absorbed by a broad pattern. - -**What executes, and what does not.** The witness is floor-discovered — neither `long/`-homed nor -in `floor_prepared_subject_exclusions` — and its scan is a live read of the two named directories: -corpus data, not a fixture. It is **not** whole-corpus coverage: a module outside those roots -reaching CLI vocabulary is not seen, and no green here says otherwise. The whole-corpus half of -this lens is enrolled on a cadence that does not currently run — a fact about that cadence, not -this witness. Both halves are stated because a scoped green presented as general is the rung -inflation §4b calls worse than sitting low. - -**Where this goes, not scoped here.** The boundary in #8535 is a paragraph today. If this -generalization holds, the successor is that the paragraph becomes a row that reds — changing what -the lens is *for*: not one witness for one lane, but the mechanism by which a named architectural -boundary is enforceable at all. A separate change, deliberately not attempted here. - -## 17. Wave 1A cutover receipt - -Executed on the session tree against `origin/main` @ #10104. Counts are from enumerating `^test fn` -in each file, not from a chosen subset (§14). - -``` -extdeps.tools.jq jq_invocation_lower now lowers a JqJsonBinding instead of refusing it - JqCliOptionArgJson --argjson, cited long form, canonical spelling, lex rule - refusal causes JqBindingNameDuplicate | JqTextBindingUnwired | JqFileInputLoweringUnwired - binding argv --argjson as THREE words, before the program operand - binding spelling keys injective over (name, role): role prefix + name, duplicate names refuse - JqBindingLoweringUnwired DELETED (dissolution on climb) - -extdeps.bmc.openbmc_fan_control ObjectMapperServiceAt / ObjectMapperServiceCount operations DELETED - consumers rewired 2 (openbmc_object_mapper_services_rec, openbmc_sensor_service) - both now route through openbmc_object_mapper_service_at / _count (semantic JqInvocation) - argv authored by the domain after cutover 0 -- no --argjson, no -e, no -r, no argv position - residual foreign syntax authored 2 jq program rows (S12, mitigatable, trigger recorded) - callers match OpenBmcServiceObserved | OpenBmcServiceProjectionRefused (+ count sibling) -``` - -**Witness standing at cutover:** - -``` -test/claim/jq_invocation_lowering_witness 16 / 16 pass (lowering + S13 classification) - of which newly stateable and added 3 (json_binding_lowers_to_exact_argv, duplicate_binding_names_refuse, text_binding_refuses) - of which flipped from refusal to positive 1 (the old unlowered_bindings_refuse became json_binding_lowers_to_exact_argv) -test/claim/bmc_typed_operations_witness object_mapper_zero/one_cardinality + duplicate_matches pass -whole-corpus regen first_generation_equal=true -``` - -**What this receipt does NOT establish** — the table reads stronger than the lane's position: - -- Wave 1A lands the **local** `--argjson` vertical. File-input lowering and the first SSH - migration have since landed — see §18 (Wave 2 cutover receipt). `ProjectFanConfig` still carries - a raw argv (its sole consumer is dead code), and `JqFileInputLoweringUnwired` dissolved with the - file-input lowering. -- `JqTextBinding` deliberately refuses: no live consumer this wave, so it must not emit a plausible - guess (DESIGN §5). It lowers when a caller needs `--arg name value`. -- The ObjectMapper callers were verified against the bmc witness mocks; no live BMC was exercised. - -## 18. Wave 2 cutover receipt — the first SSH migration - -Executed on the session tree against `origin/main` (PR #10148). Counts are from enumerating -`^test fn` in each file, not from a chosen subset (§14). - -``` -extdeps.tools.jq jq_invocation_lower now lowers JqInputFile instead of refusing it - file-input argv the path is ONE bound operand AFTER the program operand - process input JqProcessNoStdin -- the file's bytes never enter a process channel - JqFileInputLoweringUnwired DELETED (dissolution on climb) - -extdeps.bmc.openbmc_password_ssh_transport - FanStepwiseCount operation DELETED at the root (raw sshpass/ssh argv: jq -er {config_path}) - RunCommand operation ADDED -- the RFC 4254 command-string carrier, fixed sshpass/ssh argv - note one command-string row declared; the caller never authors argv - -extdeps.bmc.openbmc_fan_control openbmc_fan_config_stepwise_count (semantic JqInvocation -> remote realization) - remote realization openbmc_remote_jq_command / openbmc_remote_jq_execute - stdin-fed remote jq TYPED refusal (OpenBmcRemoteJqStdinUnwired): sshpass -d 0 holds fd 0 - command string grammar-owned single-quote encoding (remote_exec_command_string), - never append(ssh_prefix, inner.argv) - interpretation PRODUCTION PATH PASSES Unknown AND REFUSES until a live target - confirms a POSIX shell (review 38602; openbmc_dropbear_interpretation - stays the pure emitter's witness-time understanding, not a - fabricated production assumption) - decode openbmc_fan_config_stepwise_count_result reads JqOutcome, - never success/stdout/stderr - -test/claim/jq_invocation_lowering_witness 16 / 16 pass (file-input lowering + refusal-cause split) - of which flipped from refusal to positive 2 (unlowered_file_input_refuses -> - file_input_lowers_to_exact_argv; and the file-cause - test became file_input_reaches_the_plan_as_no_stdin) -test/claim/remote_jq_ssh_migration_witness 12 / 12 pass (decode + words + command string + - stdin refusal + interpretation + review pin + - production-gate refusals) -test/claim/bmc_typed_operations_witness object_mapper/threshold/sensor tests still pass -whole-corpus regen first_generation_equal=true -``` - -**What this receipt does NOT establish** — the table reads stronger than the lane's position: - -- Wave 2 migrates the FIRST remote jq population (`OpenBmcStepwiseCount`). The other remote jq - operations (`FanMinimumDuty` … `FanInputAt`, `FanConfigValidate`, `FanConfigVerify`) still carry - raw sshpass/ssh argv; each is the same mechanical shape now proven — a semantic JqInvocation, - the remote realization, a JqOutcome decode — but the cutover is deliberately one site. -- The REMOTE half of the command-string path is UNEXECUTED against a live target. The hermetic - witnesses prove `emit_remote_shell` matches the cited IEEE 1003.1-2017 §2.2.2 single-quote - encoding exactly; they never prove the far side agrees. That is the declared, unexecuted - `remote_exec_command_live_confirmation` receipt in `gunbc.remote_shell_command` — run it against - a live OpenBMC/Dropbear target before the remaining remote population is cut over. -- **The first migration's production cutover is GATED on that live confirmation** (review 38602, - REQUEST_CHANGES): `openbmc_remote_jq_execute` passes `Unknown` and REFUSES until a live target - confirms a POSIX-shell interpretation — DESIGN §4b/§5, the unobserved assumption is never - fabricated into the accepted path. The migration is structurally complete and hermetic-witnessed, - but a live OpenBMC/Dropbear target must confirm the interpretation (and record it in - `remote_exec_command_live_confirmation`) before `openbmc_fan_config_stepwise_count` can produce - an observation instead of the gate refusal. -- `ProjectFanConfig` still carries a raw argv (its sole consumer `openbmc_fan_config_project_local` - is dead code). It needs file-input lowering (now landed) plus its own consumer migration; it is - not part of this wave's first SSH migration. -- `JqTextBinding` still deliberately refuses: no live consumer, so it must not emit a plausible - guess (DESIGN §5). It lowers when a caller needs `--arg name value`. -- The migrated decode was verified against witness fixtures; no live BMC was exercised. diff --git a/docs/plans/concept-index-enumeration-census-ledger.md b/docs/plans/concept-index-enumeration-census-ledger.md deleted file mode 100644 index c3660fac121..00000000000 --- a/docs/plans/concept-index-enumeration-census-ledger.md +++ /dev/null @@ -1,17 +0,0 @@ -# Census ledger: `concept_index_enumeration` (7 reds, one root) - -Parent lane: sunny-bat-82 (out-of-gate v2 silent reds). Pin at dispatch: `9bb74408bd`. Re-derived on current main. - -Classification: **(a) producer encoding vs type-name reader**, not (b) drifted claims, not (c) gone subject. - -| Row | Cause | Disposition | PR | -| --- | --- | --- | --- | -| `witness_fieldref_self_projection` | `member_edge_type_name` required an XL-2 binder; `concept_decl_facts` / `_live` marshal `name → Atom` | Read Atom identity when the binder reader is Absent; claims keep `String` | this PR | -| `witness_qualified_concept_self_projection` | same root (`qualified_name: String`) | same | this PR | -| `witness_local_record_enumerated` | same root (`alpha: String`) | same | this PR | -| `witness_parse_only_extract_reflects_canonical_field_types` | live and parse-only share `marshal_variant_arm_target` | same | this PR | -| `witness_parse_only_extract_reflects_perturbed_field_type` | same root; `beta: Int` was never the failing conjunct | same | this PR | -| `witness_coproduct_arms_live` | payload `{ alpha: String, beta: Bool }` is `conj_authored_payload_type_name` over the same function | same | this PR | -| `witness_coproduct_arms_parse_only` | same as live | same | this PR | - -Not Instantiation/`FreeMonoid`: marshal always stamps an Atom from the authored type spelling (`marshal_type_expr_ref`). Discrimination (`String` vs `Bool` vs `Int` vs `ConceptStruct`, coproduct payload vs nullary) is unchanged. Marshal wrapping through `binder_edge` waits on stage0 HOLD (#13388). `member_edge_type_node` is not relaxed. diff --git a/docs/plans/construct-tag-reference-design.md b/docs/plans/construct-tag-reference-design.md deleted file mode 100644 index 46b436823a1..00000000000 --- a/docs/plans/construct-tag-reference-design.md +++ /dev/null @@ -1,156 +0,0 @@ -# Construct tag as a declaration reference (qualified variant construction and patterns) - -Status: PR1 of two (model and migration census). PR2 is the cut. Owner lane: lively-eagle-657, parent -gentle-koi-724, reviewer neat-boar-16. - -## Symptom and the chain (DESIGN 6b) - -`src/v2/compiler/01_tokenize.dag` (`v2.compiler.tokenize`) is file-refused at native ingest with -`body_lowering_reason_unsupported_form` at `v2.std.diagnostic.Rejected { diagnostics: .. }` in -`lex_walk_artifact`, so none of its declarations enter the index and every importer refuses -`tokenize` as unbound. That is the single root of the native seven. quiet-hawk-702 established this -by execution on D1: the native resolve walk names only `tokenize` for all seven, while explicit -imports of admitted files bind. - -The chain behind it: - -- **Unqualified `R { f: e }`** lowers through `v2.compiler.body_lowering_fold` - `body_lower_try_record_literal` to `v2.std.node_query` `construct_node(tag: Symbol, ..)`, which - builds `Conj { R: Atom(R), f: e }`. A fielded pattern lowers to the same shape. -- **Dotted head `a.b.R { .. }`** never reaches that route. `body_lower_record_literal_tag_optional` - reads only one branded atom. The postfix read classifies the brace suffix as - `PostfixChainSuffixCarried` and declines it. Its own comments name the reason: a construct tag - is a `Symbol`, so a module-qualified tag has no representation. -- **The earliest unjustified boundary is therefore the construct carrier**, not body lowering. - -The same boundary is already wrong on the unqualified path, one stage later. `v2.compiler.resolve` -`resolve_pattern_node_walk` and `resolve_node_walk` resolve the tag atom through `resolve_atom`, -then `resolved_reference_node`, into `declaration_reference_node(path)`. The edge label stays the -leaf `Symbol`. After resolve, the readers that key on "the label equals the atom's identity" answer -Absent for every user constructor: `construct_tag_optional`, `v2.std.node` -`arrow_body_record_construct_conforms`, and `v2.std.compilers.target_model` -`target_value_expr_record_construct_gate`. The construct shape holds only before resolve and in -hand-built fixtures. - -## The model - -The tag edge stops being a nickname and becomes a reference. - -- **Construct** = `Conj { : , field edges.. }`. - - `construct_tag_marker` is a STRUCTURAL CORE MARKER in the typed `EdgeLabel` vocabulary of - #12473 (quiet-koi-814, ruling A; review condition from neat-boar-16). It names the edge's role - (constructor tag); the target is an ordinary authored reference. It is counted once in the - #12473 census. Landing order, agreed with quiet-koi-814: this lane's PR2 lands first, because #12473 is the - model document only and its cut has not started. PR2 therefore declares the marker beside - `declaration_reference_marker` in `v2.std.node`, in the same form. It also records the marker once, in - the Core-marker list of `docs/plans/edge-label-coproduct/README.md` (item 1 under "Who owns the - closed set") if #12473 has landed by then, or in #12701 otherwise. The #12473 cut converts it to - the Core arm `ConstructTag` together with the other markers. It is distinct from - `declaration_reference_marker`. If they were the same, a nullary `Rec {}` - would read as a declaration reference under `declaration_reference_body_marked`. - - The label is never the constructor's spelling. That removes the label-as-second-name (§3). -- **Reference form: one form, a qualified_name spine.** Before resolve, `` is the - authored `qualified_name` spine built by `v2.std.qualified_name` `qualified_name_spine_of_atoms` - from the authored segment atoms, so each segment keeps its token occurrence. An unqualified `R` is - the one-segment case of that spine: `fold_list_node` over one atom is the two-edge head/tail Conj - that `qualified_name_spine_shape_present` accepts. Readers do not branch on atom-or-spine. - - The ambiguity of a bare spine (an integer literal is also a cons list) is discharged by the - marker label, not by the spine's shape. -- **After resolve,** `` is `declaration_reference_node(path)` produced by - `resolved_reference_node`, the same carrier every other resolved reference uses. The path is the - VARIANT's own declaration path, never its owning coproduct (quiet-hawk-702's v1 trap). No new tag - type; the shared identity stays the declaration path (`std.decl_ref DeclarationRef` in v1). -- **Lowering never resolves or trims.** The construct producer receives the whole authored path. - Nothing takes the last segment anywhere on the route. -- **Resolution is one route.** A tag spine resolves in both walks through a single function that - reads the path with `qualified_name_from_node` and dispatches by length: - - two or more segments go to the existing `try_resolve_qualified_name_node`; - - one segment goes to the existing bare door, `resolve_atom` on its only atom. - - Both end in `resolved_reference_node`. The one-segment case is needed because - `try_resolve_qualified_name_node` answers Absent for length 1. -- **Nullary qualified values** (`v2.std.diagnostic.None`) are not constructs. They are dotted - references, which already lower to the spine and resolve through `try_resolve_qualified_name_node`. - PR2 adds a control proving they bind to the variant declaration. It changes that route only if - the control goes red. -- **Patterns** use the same producer and the same reference form for the constructor head. - `resolve_pattern_binders` keeps reading only the field edges. - -## Reader and producer census, with each migration - -Established by greps over `src/` and `dag/` (`*.dag` and `*.rs`) for `construct_node`, -`construct_tag`, `construct_field_edges`, `RecordConstructBody`, `record_construct`, -`TargetRecordConstructShape`, and the label-equals-identity comparison. No Rust code reads the v2 -construct shape. `src/v1/stage0` `coproduct_reflection` emits the unrelated v1 -`record_construction_spelling` edge. - -| Module | Symbol | Role | Migration | -|---|---|---|---| -| v2.std.node_query | `construct_tag_edge`, `construct_node` | producer | Take the reference node (a spine) in place of `tag: Symbol`, and emit `Named{construct_tag_marker}` targeting it. | -| v2.std.node_query | `construct_tag_optional` | reader | Key on first-edge label == marker, and answer the reference node (a pre-resolve spine or a post-resolve marked reference). It no longer answers a Symbol. | -| v2.std.node_query | `construct_field_edges` | reader | Unchanged apart from the gate. | -| v2.std.node | `arrow_body_record_construct_conforms` | well_formed gate | First edge is the marker, targeting a spine or a marked reference, and every other edge is Named. Fixes resolved bodies being rejected today. | -| v2.std.node | `classify_arrow_body_form`, `declaration_reference_body_marked` | classifier | Classify a construct by the marker explicitly, not as "any unmarked Conj". | -| v2.compiler.body_lowering_fold | `body_lower_try_record_literal`, `body_lower_record_literal_tag_optional` | producer | Read the head as a qualified-name spine (bare or dotted) and pass it to `construct_node`. The brace suffix leaves `PostfixChainSuffixCarried`; the declined arm and its advisory for this suffix are deleted. | -| v2.compiler.body_lowering_fold | constructor-pattern lowering (the `construct_node` call in the pattern arm) | producer | Same: the spine from the pattern head. | -| v2.compiler.body_lowering_fold | `body_lower_is_core_substrate` | reader | Through the migrated `construct_tag_optional`. Check the order against the `qualified_name_spine_shape_present` test. | -| v2.compiler.resolve | `resolve_pattern_node_walk`, `resolve_node_walk` Conj arm | rewriter | Resolve the marker edge's spine through the one length-dispatching function above. Field edges walk as today. Check the `resolve_ctx_snoc_position` side effect now that the label is the marker. | -| v2.compiler.resolve | `resolve_pattern_binders` | reader | Unchanged apart from the gate. | -| v2.compiler.infer | Conj gather arms (`infer_gather_*`, `infer_product_facts_from_entries`) | reader | The marker edge must not be read as a product field. Skip it by its TYPED edge role, never by the label's spelling, and type the construct by the referenced declaration where a row exists. Today the tag is `infer_gather_fold_not_derived`. | -| v2.compiler.eval | `eval_callee_body_refusal_reason` | reader | Refusal only; no change. | -| v2.std.compilers.target_model | `target_value_expr_record_construct_gate`, `target_project_record_construct` | reader | Gate on the marker. `type_name` comes from the resolved declaration's path, spelled through the target's binding rows, not from an atom identity. This admits resolved constructs the gate refuses today; that is a behaviour change, and it is named. | -| v2.std.compilers.target_model | `target_value_expr_arrow_has_record_construct_body`, the arm body projection and match-arm slot readers | reader | Follow the gate. | -| v2.std.compilers.target_model | `TargetRecordConstructShape` decode; the `record_construct_form` rows in extdeps.languages rust, typescript, c and dag | token rows | No change. | -| v2.extdeps.languages.dag | `dag_rec_construct_body_node`, `dag_variant_holds_construct_node` and their users | fixture | Rebuild through `construct_node` with a spine. | -| v2.lens.machine_shape | `constructed_tag` | reader | Compare the resolved declaration path of `MachineShape`, not a leaf Symbol. | -| tests: `variant_field_lowering_test`, `wildcard_pattern_form_test`, `arrow_body_form_witness_test` and `arrow_body_form_semantic_helpers`, `record_construct_emit_test`, `rust_record_construct_emit_test`, `rust_record_construct_emit_host_equals_eval_test`, `reference_conservation_test` (occurrence counts), `value_position_whole_read_test`, `declaration_graft_assemble_test` | | test | Rebuild each fixture with the marker, and recount occurrences (segments keep their token occurrences). The unmarked reference-shaped negative must still go red. | -| recurring_failure_mode and rung_drop prose (`postfix_suffix_step_declined_to_the_pre_existing_arms`, `call_expression_erased_at_v2_body_lowering`), `docs/plans/lowering-occurrence-projection-design.md` | | prose | Update the prose. Retire the brace-suffix population of the rung drop by its trigger. | - -Not readers of this shape: `v2.std.data_initializer_identity`, `v2.std.decl_facts_skeleton` and the -v1 `record_construction_census` fixtures all read the v1 reflection edge. The orchestration -`construct_tag:` fields are an unrelated name clash. - -## One decision for a dotted path (ruled by gentle-koi-724, 2026-09-29) - -Whether a dotted path is a qualified name or a field projection on a local is decided in ONE place: -`v2.compiler.resolve` `try_resolve_qualified_name_node`, by `qualified_head_bound_on_chain`, which is -the scope of the first segment. eager-newt-412's lane owns the projection arm there. This lane only -consumes that door. A construct tag whose resolved answer is not a constructor declaration (or a -kernel canonical atom) refuses `resolve_reason_construct_tag_not_a_constructor`. The door's projection arm is gunbc#12506's -`resolve_bound_head_projection`. The LOCAL wins: a bound first segment shadows a whole-path -declaration, so the bound-head check precedes any declaration lookup, and that change is made inside -#12506. Whichever of #12506 and this lane's PR2 lands second adds the control. It is one module with -`artifact.tree` (a match binder), `v2.std.diagnostic.Rejected { .. }` (qualified), and a local -shadowing a module segment. - -## The live defect on main - -Filed as `gunbc.recurring_failure_mode` -`construct_tag_read_by_label_equality_after_resolve_rewrote_the_target`. Confirmed by execution: a -resolved `PcrA { n: true }` is `Conj { PcrA -> v2.test.pc_provider.PcrA, n -> true }`, -and `construct_tag_optional` answers Absent. The well_formed and target_model gate arms are inferred -from their definitions, and the row labels them that way. - -## PR2 (the cut) and its evidence - -All readers migrate in one motion, and no `Symbol`-tag path remains. Controls, fed by production: - -1. `a.b.R { f: e }`, `a.b.C` (nullary) and `a.b.R { f: x } =>` each lower and resolve to the same - node as their unqualified form under an import of the same declaration. This is a content - equality check. -2. Two variants with the same leaf in different modules, constructed qualified, bind to different - declaration paths. -3. Mutations go red: dropping the qualifier binds the other module's variant or refuses as - ambiguous, and dropping the record body changes the node. -4. The variant is bound, not its coproduct. -5. A qualified tag whose last segment names a TYPE rather than a variant, or a variant of a - different type, refuses at a located site and never resolves by spelling (neat-boar-16). -6. The target_model gate is shown by a discriminating pair: a resolved construct is admitted, and - an unresolved or wrong-variant construct still refuses at that gate. -7. The seven's actual form, `v2.std.diagnostic.Rejected { diagnostics: .. }`, is the positive - control on the production route. -8. The live defect below has a control that is RED on main (a resolved user construct read by - `construct_tag_optional`), turns green in PR2, and stays enrolled as the regression control. -9. `01_tokenize.dag` is admitted on the native route. A base-vs-head census (the #12550 recipe) - shows the newly admitted files and no unexplained new refusal. quiet-hawk-702 runs the native - seven against the head. diff --git a/docs/plans/convergence-one-program.md b/docs/plans/convergence-one-program.md deleted file mode 100644 index 10b9f73bada..00000000000 --- a/docs/plans/convergence-one-program.md +++ /dev/null @@ -1,300 +0,0 @@ -# CONVERGENCE-ONE — program record at wind-down - -One convergence protocol and one fleet authority for persistent host effects. A subject is -**selected**, **observed goal-blind**, **assessed** against the goal, **planned**, **admitted -against the observed basis**, **applied**, **independently read back**, and given a **terminal -verdict**. Anything that calls itself convergence without running that lifecycle is a second -convergence algebra — a §3 authority fork — and the program's job was to end it. - -This page is the record at the point the program was wound down (2026-09-20) under system -pressure, so that v1 performance and v2 migration take priority. It exists because the state -was held in a session's pinned plan and in seven lanes, none of which survive. Its roadmap home -is `fleet-closed-loop-converge`; the two remaining cuts that have no home of their own are -`fleet-convergence-census-closure` and `fleet-convergence-legacy-deletion`. - -## What landed - -- **C0** — the census of persistent host effects; dual-home made unwritable. -- **C1** — the canonical protocol bound; `ensure` is a projection of it. -- **C2** — one selected LiveDeploy roster. `deployment_spec_srv1` has exactly one home. -- **C3** — LiveDeploy scope, member basis, plan-side frontier discharge; and, in its remainder, - the request arm binding `DesiredDeployment.selected_identity`. -- **C4** — the apply frontier row, honestly **awaiting** rather than falsely discharged - (see *An undischarged frontier* below), plus a production gate on the protocol being bound. -- **C5** — readiness defork and the canonical readiness algebra. -- **C6** — owned roots folded into the member model. - -## Ready but unmerged when the program stopped - -Six pull requests were CLEAN with every check green, waiting on an operator merge: - -| PR | Cut | What it carries | -|----|-----|-----------------| -| #11678 | C9a.1 | GCP Secret IAM: read the policy back **independently** after the write | -| #11676 | C7 | provider-state root as a `live_deploy` ensured member, read back — **now DIRTY** | -| #11689 | C11 | GitHub reads rebound onto the canonical join — **parked, see below** | -| #11732 | C9b | App-key rotation: exact-version verifier, contract, `DisableOnly` — **MERGED** | -| #11795 | — | `RungDropAmendment` carrier, so a later ruling on a standing drop has a home | -| #11828 | — | `mtcollins1_boot` narrowed to `FleetSshKeyNotConsumed` | - -**#11689 is parked for the same reason, one rung further on.** It is complete and correct at -`2e63f9d53`: the rebind is done, and a scratch regeneration driver that an earlier `git add -A` had -swept in — no consumer, re-minting a generated workflow path as a literal — was found by review and -deleted, so the PR no longer carries a defect that would land. It then went DIRTY again within the -hour on the contended pair. It is left open, not closed. - -**#11732 is parked green, and parking it leaves a real gap open.** At `e0c453f961` it had one -approval, no change requests, and all four checks green by name; its eighth review cleared the -thing most likely to be wrong in such a cut — that the frontier row names the add-version and -disable-prior *capability* rather than letting the verifier's own existence retire it. It was -stopped by queue position on the contended pair, twice. The consequence, stated so it can be -weighed rather than forgotten: **`ci-github-app-private-key` still has no rotation deadline, no -restore test and no rehearsed compromise answer on main, and nothing in the corpus can answer -"does the key in version N actually work."** That key is the root of trust for every org-admin -action CI takes. - -**#11676 (C7) MERGED.** The provider-state root is an ensured member derived from the spec, so the emitted ensure and the read-back member come from one function and cannot diverge; absent, duplicated, wrong-owner and not-a-directory have no constructor, so the first cut's refusal arms were deleted rather than left permanently green. - -**The launcher deletion is the next cut, and its census is already on main** as an annotation on `deployment_provider_state_step`. For `srv1_live` the provision plan's `DashboardEnsureProviderStateRoot` is now REDUNDANT — that is the op the deletion may remove. For `srv1_lab`, `srv2_lab`, `srv2_deploy` and `macbook_local` it remains the ONLY creator: their specs carry the member, but no production caller applies those specs, so the member creates nothing for them yet. The op may be deleted for an instance only when a production caller applies its spec; deleting it sooner is the serve-binary deadlock again. - -**#11828 must not merge on checks alone.** Its acceptance is a wet dispatch of -`fleet-converge mode=mtcollins1_boot`: if that mode does need the fleet key, it fails at the -point of use, and discovering that on main is strictly worse than discovering it before. - -**C9b's verifier has never run against the live surface.** All fifteen witnesses supply an -observation record; none executes the real mint, which needs the fleet-converge runner, the -federated principal and the live App. Three shapes are therefore *readings* of upstream rather than -observations: that the Secret Manager response carries the resolved version in its name field, that -`curl -D` writes a status line whose second word is the code, and that the mint step's refusal arms -leave their record lines before exiting. **The first dispatch of `app_key_version_verify` is the -inhabitance claim for that whole cut**, and it is cheap — read-only, one installation-token mint, no -mutation. If it refuses with an absent mint observation on a version that plainly exists, suspect -the record wire before the mint. Its actuation half (add-version, disable-prior) is blocked on -`gunbc.github_actions_wif` being absent from main: on a runner there is no federated identity for a -Secret Manager write, so the only honest route today is an operator workstation. - -## Two operator acts C9b left open, one of them dated - -**`rotate_by` on main is `2026-12-18`, and it is a placeholder a lane chose rather than policy.** -The verifier reads it on every run and refuses once it passes. So if nobody sets it, the first -thing that happens on that date is that a green verify run turns red with a deadline-passed -refusal. That is the honest behaviour, and it is also a trap for whoever meets it first: **the fix -is a policy decision and a rotation, not a code change.** - -**Nothing in that cut has run against the live surface.** Every witness supplies its own -observation record. The first dispatch of `app_key_version_verify` against the currently enabled -version *is* the inhabitance claim for the whole thing, and it is cheap and safe — read-only, one -installation-token mint. Three shapes are readings of upstream rather than observations, so that is -where a first-dispatch failure will be: that the Secret Manager response carries the resolved -version in its name field, that `curl -D` writes a status line whose second word is the code, and -that the mint's refusal arms leave their record line before exiting. A refusal reporting an absent -mint observation on a version that plainly exists means the record wire, not the mint. - -**The actuation half is blocked on identity, not on design.** Add-version and disable-prior with -independent readback need a federated identity for a Secret Manager *write*, and -`gunbc.github_actions_wif` is still absent from main — so a runner has none, and the only honest -route today is an operator workstation. Reaching for a print-token on a runner fails at runtime -rather than degrading, which is why the lane did not. - -## Still held, and on what - -- **C8 — wet apply.** Held until three proofs close: temporal order (control 6), Baseline vs - PostApply (control 7), and apply-script path disjointness. C8 must **execute** a nonempty - `ReleaseEffectPlan`; until it does, that terminal answers `AwaitingCapability`. C8 also owes - the discharge of C4's frontier: naming its projection-routed apply carrier moves that row off - a prose condition. Note the cost trap recorded by C4's lane — a bound discharge needs an - observed population, and computing it costs ~60s over the live tree, so it must not go on the - plan route blindly. -- **C9 — census closure by effect family.** The denominator is every non-terminal row in the C0 - census, not a keyword search. One effect family per change, each answering the same seven - questions: who selected the desired state, what independently observed reality, what assessed - the difference, what exact plan was admitted, what authority executed it, what independently - read it back, and what old path was deleted or renamed. C9a (GCP IAM) was pulled forward - because it touches production; C9a.2 (the bare-grant authorization closure) is unstarted. -- **C10 — deletion and the recurrence wall.** Deliberately last, and performs no new semantic - migration: it deletes superseded routes and makes recurrence mechanically visible. Its gate - must join against an independently discovered producer population, never a search for - suspicious names. - -## Unowned residue - -- **C5's poll Ready-exit** is recorded unproven. It closes when a fixture can supply `Ready` - into the poll outcome without materializing the live site. -- **The org-admin credential admission surface** was censused and the answer is DELETE, but the - deletion was never written: that lane produced analysis only and has **no PR**. Its findings - exist nowhere else, so they are here. - - Obligations 1–8 are met or dissolved by the live App-token path, and two carry the argument. - **Genealogy is enforced by construction**: the mint is serial and each link exits on failure, - so a token cannot exist with an incomplete genealogy — deleting a rung-2 validation that a - rung-4 construction replaced. **Capability sufficiency is enforced better**: an unconditional - probe that refuses on the real response proves more than a declared capability list, which is - exactly why the dead model carried `CredentialOperationProbeRequired`. - - **The sharpest fact in the census**: `ci_spec`'s own annotation says the interim PAT step *"was - never taken, and it was never necessary."* So the whole `SecretMaterial` surface — custody, - genealogy, rotation contract, continuity due-dates — modelled the lifecycle of a credential - **that was never minted at all**. Not superseded; never inhabited. That turns the deletion from - a cleanup into evidence about how the model got there. - - **The delete set is grep-verified, not refusal-verified.** Under §3 the deletion *is* the - census, so treat the set as a hypothesis and let the compiler refuse. Six declarations are - already dangling on main today with no consumer at all, including the witness. - - **Keep** `org_admin_app_key_unreadable_message`, `org_admin_installation_token_refused_message` - and the two arms they name: `ci_spec`'s prelude renders them and a witness checks it. They are - a declared frontier with a stated trigger, not dead code, and a dead-code sweep would wrongly - take them. Do not touch `extdeps.github.org_admin_auth` — faithful upstream modeling with four - live importers. - - **Open, for the operator**: `docs/plans/org-admin-credential-acquisition.md` is the prose home - of this model. Deleting it orphans two live annotations that cite it; keeping it leaves a plan - document for a credential that was never minted. -- **#11760 would re-fork the authority #11795 just consolidated.** It still carries the postscript - text inline on the drop's `restoration_trigger`. If it lands after #11795, that string must be - deleted and replaced with a row under `dag/gunbc/rung_drop_amendment/`. Its author agreed. This - is the only known change that would undo that consolidation. -- **The amendment witness is enrolled on no lane at all.** Its four claims were executed on the - committed tree, but not through a claim harness: `claim_executor` has no per-module selection, - and a whole-floor run is a CI job rather than a session one. They were run via `gunbc run - --function`, which refuses to map a `Bool` to an exit code and *prints the value in the refusal* - — a real execution of the real function, but not a harness run. Since required CI runs no claims, - nothing today would notice if that file went red. If claims return to the merge path, it belongs - in the first batch. -- **A read-write-re-read fold is unfalsifiable under the current replay harness**, and this is a - frame-level gap rather than a defect in any subject. REST replay keys a fixture by exact - invocation, and the first read and the re-read of such a fold are the *same* invocation — same - operation, target and input digest — so no fixture set can answer 200 to one and 403 to the - other. Sequencing is not expressible at all. Worse, an invocation's `input_digest` is minted - host-side over the bound parameter environment and has no `.dag` constructor, so an authored - fixture cannot reproduce it for any operation that takes inputs; the existing probe matches only - because its operations take none. This is why C9a.1's unreadable-re-read stop is declared - unexecuted for its route. It belongs to `v2.std.witness_evaluation`, and closing it is what - would let that stop be executed rather than argued. -- **The projected `restoration_trigger`** on the namespace-wave-admission drop does not state - that restoration half (1) was built and then deleted; #11795's amendment supplies that context - in the projection, so check the rendered page before re-opening it. - -## Findings worth keeping - -These cost real time to establish and are not derivable from the diffs. - -**An undischarged frontier was unwritable.** `converge_apply_for_host` calls `host_effect_apply` -directly and never the projection, so no honest consumer exists before C8 — but `FrontierStanding` -carried only *honest* and three defect arms. The corpus forced a choice between a citation that -lies and a protocol that can never bind, whose gate would then refuse every healthy run. C4 -resolved it by inhabiting `std.dissolution`: an awaiting arm that can never be read as discharged, -refusing when the capability has arrived and the row was never updated. The closing sentence is -**derived** from the row's standing; authoring it would have been a second authority for -retirement. - -**A successful actuation return cannot mint convergence.** The GCP IAM writer classified the -policy returned *by its own write* and greened. The repair reads the policy back independently. -The load-bearing control is the one that fails when the write response carries the grant and the -re-read does not. - -**Observation must be goal-blind.** `decode_registration_json` took the expected App identity -*into the decoder*, so a valid response describing another App was filed as *unobserved* rather -than observed-then-diverged — a known mismatch laundered through the refusal arm. - -**Check that a check can fail.** Two witnesses were deleted rather than kept: one asserted that a -prose sentence contained certain words while its name claimed a structural impossibility, and one -tested a state that, once the member was derived from the spec, had no constructor at all. A -permanently green check is worse than none, because it gets cited as coverage. - -**A stale-plan fingerprint must carry identity.** The LiveDeploy baseline hashed member *labels*, -so a changed executable digest, tree revision, dirty tree, unit document, running release or root -owner all left it unchanged. Caught by an external review after two approvals, including mine. - -## A first-apply risk C8 must clear before it touches srv1 - -C7 models the provider-state root as an ensured directory, and the emitted command is -`install -d -m 0755 -o -g `. **`install -d` chmods and -chowns a directory that already exists.** The mode is a constant of the ensured-directory arm, -not a fact read from the host, and nothing in the model refuses a widening. - -So if srv1's provider-state root is currently `0700` — which is what a provider CLI creating its -own state directory under a umask would plausibly leave — **the first real apply silently widens a -credential directory to world-readable**, and the same command re-owns it if the live owner -differs. This was never verified against a host, because no wet effects were permitted; it is an -inference from reading both code paths, and its author flagged it rather than letting it pass as -settled. - -**Before C8's first apply: stat the live directory** and compare its mode and owner against what -the ensured step emits. If the observed mode is tighter, the ensured-directory arm needs a mode -carried per subject — the `ManagedDirectory` derivation — **before** anything applies, not after. - -**A typed outcome on the policy read is a fail-open, and the next reader will re-derive it.** The -obvious way to make C9a.1's inaccessible arm a typed value is to declare `RestOutcome` on -`GetSecretIamPolicy`. Do not. The other caller reads the policy in the node-pattern form, which -cannot branch, so a failed read there returns an empty policy with a blank etag that flows -straight into `SetSecretIamPolicy` — an unconditional whole-policy overwrite of a live IAM policy, -driven by a read that never happened. That is strictly worse than the defect C9a.1 repairs. The -ordering is therefore fixed: make that caller an `EffectPlan` step that can refuse **first**, and -only then declare the outcome. - -## Process facts a later program will need - -- **The required CI check builds the compiler and runs no witnesses** (#11742), and the - namespace-wave-admission gate was removed (#11774). A green check means *it compiles*. Lanes - must run what they claim and say what they ran. Three composition breaks reached main in one - day — each PR green against its own base, red only in composition — and the repair commits say - so in their titles. -- **A PR failing the generated-artifact gate self-heals.** Since #11791, `heal.yml` is - dispatch-only and the repair moved into the required run's fleet lane: the branch picks up a - `gunbai-bot` commit within ~10 minutes. Authors must **wait for the publisher**, never - hand-produce the bytes and never push over a sealed candidate — doing so invalidates it and - restarts the cycle. -- **Heal regenerates `.github/workflows` but cannot publish it, and nothing detects that.** It does - push — a heal commit landed `.gitattributes` and `docs/design-rung-drops.md` on one branch — so - the constraint is *workflow paths specifically*, consistent with an App token that cannot write - them. On one head heal reported success, logged writing the 89,904-byte workflow in its own - workspace, and emitted a candidate manifest holding only the two non-workflow paths. Since #11742 - removed the regeneration gate from PR CI, a stale workflow projection is now **invisible**: that - drift survived three pushes and four green heal runs and was caught only by reviewers. Any PR - changing a `ci_spec` entry target inherits this, and must carry its own derived bytes. -- **`fleet_converge_workflow.dag` and its generated `.yml` are the contended pair**: every lane that - adds a dispatch mode touches the same two rows, so PRs there go DIRTY on queue position rather - than on any defect. A conflict region there can split a step, and a keep-both resolution is not - safe by reading — the class and its fourth specimen are rostered at - `gunbc.recurring_failure_mode.merge_region_excludes_shared_tail`. -- **The verification that discriminates, for anyone hand-committing a projection:** push, then read - heal's repair-candidate *manifest* on that head. An empty entry set against two entries on the - prior head is a real signal. A byte count matching heal's own is corroboration only — a different - delta could preserve length. -- **No session can execute a `gunbc run` remotely.** BuildBuddy executors expose no cgroup memory - limit, so `gunbc.host_budget_source` refuses with `HostBudgetUnreadable` before planning; a lane - that bound a limit by hand got SIGKILLed instead. Briefs that offer "CI or one remote dispatch" - as the escape from local memory pressure are offering something that does not exist today. -- **The local regeneration hazard is LOAD, not a ceiling.** The same command made no progress in - 40 minutes at host load ~270 and finished in about 20 at load ~66, writing correct bytes. Each - `gunbc run` loads the whole corpus, so runs must be serial — running claims alongside a regen - makes both thrash. Do not conclude the local arm is dead; check the load first. -- **A workflow run's branch column is not the ref it built.** `heal.yml` checks out an input SHA, - so the branch column names where the dispatch fired. Reading it as the built ref produced a - false "main is red" alarm and a dispatched repair lane against innocent substrate files. -- **`git merge origin/main` in a session worktree is dangerous and looks fine.** The worktree is a - shallow clone, so the merge base is wrong. On one lane it reported two trivially additive - conflicts and produced a commit that reverted 99 files and 6,886 lines of other lanes' work — - including `witnesses.yml` back to a pre-#11742 revision, so CI then ran an old job roster and - went **green on a revision that had reverted main**. The safe recipe: `reset --hard origin/main`, - check out your own files, then assert that every deleted line in `git diff origin/main` is one - you wrote. -- **A session worktree is a shallow clone, and git lies about it confidently.** Two lanes hit this - with different symptoms: one was told it was 231 commits *ahead* of main and not an ancestor - (truth after `git fetch --deepen=200`: 0 ahead, 26 behind, ancestor yes), the other had a merge - refuse with *unrelated histories* while GitHub's compare API reported eleven ahead and four - behind. It also re-shallows, so the false readings come back. Run - `git rev-parse --is-shallow-repository` before believing any ancestor or count answer — a missing - merge base here is a fetch-depth artifact, never a force-pushed main. -- **Do not revert main's generated-artifact drift out of your own PR.** The auto-heal bot pushes - exactly that regeneration back onto your branch, and its commit **resets the approval tally**. The - only real choice is whose commit carries the bytes, and the bot's is better because it is - attributable. -- **`extdeps.time.rfc3339` models no arithmetic and no parse**, only a conditional lexical - comparator. So a period-shaped deadline (`create_time + N days`) is not expressible, which is why - C9b's rotation contract carries an absolute `rotate_by` that a completed rotation must move - forward by hand. A period first owes rfc3339 parse and arithmetic. -- **A one-arm coproduct is not a coproduct.** `type X = OnlyArm` — a name, equals, a single bare - arm, no pipe — parses as a type *alias* to an unknown name and fails with *name not found in - module*, attributed to the importing module rather than to the declaration. It cost a lane a - cycle. C9b's restore cadence became a record naming the consumer that discharges it, which is - better anyway: the intent builds its roster from that record, so the field is read rather than - declared beside the thing it describes. -- **Merge order is not a preference.** Out-of-order merges broke sibling cuts four times, and none - of it was a defect in the changes. diff --git a/docs/plans/corpus-acquisition-design.md b/docs/plans/corpus-acquisition-design.md deleted file mode 100644 index 6fc197a066f..00000000000 --- a/docs/plans/corpus-acquisition-design.md +++ /dev/null @@ -1,533 +0,0 @@ -# Corpus acquisition: the listing root cut, the descent measure, and the symlink rule - -The corpus manifest landed in gunbc#10445 as a carrier with no producer: `store_corpus_manifest` -builds one from entries a caller already holds, and nothing builds one from a working tree. This -document is the design for that producer, settled **before** any of it is written. - -It exists because the survey turned up two modeling questions the SCM brief does not answer, and -answering them unilaterally is how the previous PR acquired five rounds of findings. Both were put -to external review and ruled on; this records the ruling and the facts that forced it. - -## 0. The two questions, and what the survey actually found - -**Q1 — where does enumeration live?** `extdeps.filesystem.filesystem_io` returns a directory listing -whose `entries` member is a raw newline-joined `String`, with membership answered by a substring test -over that blob. It carries no file-versus-directory discriminator, so a corpus walk cannot ask the -one question it must ask at every entry: descend, or read. - -Three facts qualify that, and the first two cut against widening: - -- **No existing consumer enumerates.** Every one of the eight modules that consume the listing feeds - it straight into `filesystem_entry_presence`, which answers *membership* — is this one name in this - directory. Not one iterates the entries; not one needs `EntryKind`. ~~The blob is adequate for every - consumer that exists.~~ **SUPERSEDED — see §10.** The blob is not adequate for its existing - consumers: its producer silently narrows the population it returns, so the membership answers built - on it are unsound on two paths. That existing consumers ask only membership does not save a - population the producer already narrowed. -- ~~**So enumeration is a new capability, not a failing carrier.**~~ **SUPERSEDED — see §10.** It is - both. Enumeration and `EntryKind` are new capability; the listing is *also* a failing carrier, and - the failure is live on main. The repair therefore comes first and does not wait on the capability - that discovered it. -- **But the blob is a workaround whose justification has lapsed.** `filesystem_io` justifies the - substring test by saying newline-wrapping makes the test line-exact "without needing a split the - interpreter does not offer". That is false today: `split` is a builtin string method — declared in - the seed at `std_algebra` and dispatched by the v1 interpreter as `MethodCallSplit`, requiring no - import — and `extdeps.languages.markdown` `md_split_blocks` calls `split(s:, delimiter:)` on a plain - `String` now, with further splits in `extdeps.git.object_store` and `extdeps.systemd`. - -The deciding argument is `filesystem_io`'s own. The comment above `filesystem_listing_names_entry` -pulls the membership predicate down beside the operation because two consumers had spelled it -independently over one wire format, "which is the second representation section 3 forbids: the -encoding is `List`'s fact, so the membership question over it is too." That argument does not weaken -when the question is enumeration instead of membership. Either the structure belongs at the -authority, or that comment is wrong about membership. - -**Q2 — what does the walk descend on?** DESIGN §4 makes execution bounded and forward, with -termination *checked* rather than discovered. A directory tree offers no static bound and, with -symlinks, not even a guaranteed finite one. - -## 1. RULING — root-widen the listing at its authority - -Replace the lossy listing representation where it is produced and migrate its consumers together. -Names-only consumers project names from the richer result; they acquire no interest in ingestion, -MIME types, or semantic source validity. The unrelated read consumers do not migrate merely because -their operations share a module. - -The migration population, corrected after the first census undercounted it: - -| module | how it reaches the listing | -|---|---| -| `gunbc.generated_artifact_observation` | directly | -| `gunbc.deploy_transition` | directly | -| `gunbc.scm.init` | directly | -| `gunbc.roadmap.roadmap_verification_receipt` | directly | -| `gunbc.roadmap.roadmap_publication_helper` | directly | -| `gunbc.fleet.fleet_converge_plan_cli` | directly | -| `gunbc.devboot.build` | directly | -| `gunbc.roadmap.roadmap_belt_actuate` | directly | -| `gunbc.fabric.fabric_allocation_store_substrate` | only `FilesystemEstablishedAbsence` | -| `gunbc.scm.repository_save` | only `FilesystemEstablishedAbsence` | - -The first census reported eight. It keyed on the listing's *type and constructor spellings*, so it -found every module that names the listing and missed the two that reach it through the derived -absence carrier. Those two move only if the widening changes the shape of `FilesystemEstablishedAbsence` -rather than the blob behind it, so they may be zero-diff — but they are in the subject population, and -a filter whose vocabulary is narrower than its subject reporting its own reach as the population is -the partial-observer class this lane keeps finding. It is recorded here rather than quietly fixed. - -The cut is not eight call expressions: the listing folds, the witnesses, the transport realization -and the emitted representation belong to it too. - -### Two properties must survive the replacement - -**Enumeration failure stays distinct from successful exhaustion.** Opening a directory successfully -does not establish that enumerating it succeeded — a host iterator can fail while advancing. A result -accumulated before such an error must not become a complete listing, and so must not establish -absence for an entry it never reached. This is the absorbing-fallback rule (§5) at the listing -boundary: the failure arm refuses, it does not widen into "not there". - -**Entry names and kind observations stay unconflated.** An entry's name can be known while observing -its kind fails. That entry is neither dropped nor called `Other`; the two facts are separately -carried, because the host interface itself separates a name from a fallible type observation. - -And no newline-delimited membership may survive underneath the structured result as an independent -authority. `filesystem_listing_names_entry` is deleted by this cut, not kept beside it. - -### A consequence the cut inherits: two of `FilesystemEntryName`'s refusals lose their reason - -`admit_filesystem_entry_name` refuses a name containing `\n` and separately one containing `\r`, and -both refusals are justified **by the wire format** — the newline is `Filesystem.List`'s delimiter, so -such a name could make the membership test answer about a different spelling. Under a structured -listing that delimiter does not exist, and those two justifications lapse with it. - -That does not automatically mean the refusals go. It means each must be re-decided on its own -grounds: either the structured representation preserves such a name losslessly and the refusal is -deleted, or the refusal stays as an explicit *unsupported spelling* with a new stated reason. What it -may not do is survive carrying a justification the cut has falsified — which would be this same class -a second time, created by the repair rather than found by it. - -The `/`, `\` and NUL refusals are untouched: path traversal and C-string truncation are facts about -the host, not about the listing's encoding. - -## 2. RULING — layering: acquisition, captured inputs, pure construction - -``` -bounded filesystem acquisition - ↓ -explicitly scoped captured inputs - ↓ -pure corpus construction / semantic ingestion -``` - -Build **both** layers. The already-enumerated interface is not eliminated by the root cut — it is -placed correctly. An explicit input list is the right subject for the pure operation, and it is *not* -evidence that anybody enumerated a directory successfully: a caller may intentionally select ten -files, which is a complete selection of those ten and not a complete capture of their parent. - -The failure mode to refuse explicitly: using the lower layer to claim the upper producer has been -delivered. - -## 3. RULING — finite work fuel is the descent measure - -Admit a finite, nonnegative budget supplied by the acquisition policy. The walk carries `R(s)`, -remaining work fuel. Every continuing discovery transition establishes `R(s) > 0` and -`R(s') = R(s) - 1`. - -That is a well-founded arithmetic descent even though discovering one directory adds several pending -children: **the worklist may grow; the rank does not.** - -``` -all acquisition obligations discharged → completed capture -obligations remain, no work fuel remains → located budget-exhaustion refusal -obligations remain, fuel positive → one bounded transition, continue with less fuel -``` - -Prohibited: resetting the budget per sibling, replenishing it when a new directory appears, or -charging only newly discovered inodes while another continuation path runs unbounded. Each loop needs -its own demonstrable descent or must share the globally decreasing measure. - -The refusal says *the admitted budget was exhausted before completion was established*. It does not -claim the corpus contains more than some number of files — that number was never observed. Reaching -the numeric limit after every obligation is discharged is not a reason to refuse. - -**The budget is operational, not part of corpus selection.** Raising it must not change the identity -of a successfully captured unchanged corpus, and exhausting it must not mean "this now denotes the -prefix we happened to read" — that is the absorbing fallback wearing a limit's clothes. - -### Why the three candidates I proposed are not this - -| candidate | honest role | what it does not establish | -|---|---|---| -| remaining depth | can prove recursive descent when each child enumeration is a finite admitted input | any bound on total discovery work, directory width, or I/O | -| visited-object set | detects repeated expansion; supports traversal of a *fixed finite* graph | that the live universe of discoverable objects is fixed and finite | -| no symlink following | removes symlink-induced traversal into aliases and cycles | that remaining discovery terminates or fits a resource bound | - -A visited set becomes a termination proof through a measure like `|U \ V|` only when `U` is an -established finite universe and each continuation removes an element from it. Maintaining `V` — what -has been seen — does not establish `U`. Directory contents can change during enumeration, and POSIX -leaves aspects of observing concurrent additions and removals unspecified. - -A depth budget is legitimate as an *additional* policy limit with its own located refusal, and an -explicit worklist with work fuel also stops the host call stack from being the mechanism that -enforces it. - -One qualification I had wrong: an unknown compile-time size is not the same as having no termination -argument. A materialized finite tree traverses structurally without a compile-time size, and a finite -list folds without a literal length. The problem here is specifically that a filesystem walk -**discovers more input through effects while it runs** — a child pathname is not a compiler-visible -subvalue of an already-held finite tree. `std.termination` already separates `TreeSize`, -`ListLength`, `ArithmeticValue` and `SetCardinality`; these are different arguments, not -interchangeable labels for "making progress". - -### The fuel must reach the actual discovery mechanism - -The boundary most likely to recreate the problem: - -``` -unbounded host enumeration → fully collected list → check the budget -``` - -A budget cannot bound work that already happened. The listing realization must either expose bounded -progression or perform a bounded collection internally, with explicit completion-versus-limit -outcomes. A new streaming framework is not required; a host-side bounded collector that **refuses -rather than truncates** satisfies the contract. - -The same distinction applies to content: a bound on directory transitions does not bound a whole-file -read. Content acquisition is bounded separately, and I/O deadlines or cancellation stay in the effect -contract. An arithmetic rank bounds modeled continuations; it does not prove an arbitrary kernel or -remote-filesystem call returns within a wall clock. - -And the decrement must be established by the compiler's actual enforcing path. `Strict` existing in -`std.termination` is not proof that the compiler derives it for this walk. - -## 4. RULING — never follow a symlink implicitly - -Do not follow file or directory symlinks during authored-corpus acquisition. Following a link -substitutes the referent's contents for the authored link, which changes the subject being captured. -A link to an external file, a link to a directory, and a dangling link must not acquire different -preservation semantics because one happens to resolve today. - -Three facts stay separate: - -``` -the directory entry is a symlink -the symlink stores a particular target spelling -resolving that spelling currently produces some outcome -``` - -`std.filesystem.types.SymlinkTarget` is `TargetFile | TargetDir | Broken`. That records the *third* -fact only. It cannot serve as a persisted link payload, because it does not carry the stored target -spelling needed to reconstruct the link. A link-preserving representation needs the link's actual -stored target, read without substituting the referent's contents, and without accepting a truncated -target. - -### What this producer does, given the manifest it actually has - -`CorpusManifestEntry` is a path and an `AuthoredSourceTarget`, with no link discriminator. It cannot -express "this path denotes a symlink with this target". So for an in-scope symlink this producer -returns a **located unsupported-symlink acquisition refusal**. It must not: - -- omit the link silently and report complete capture; -- store its target text as though it were an ordinary file; -- put `Broken` or a traversal error into the manifest as though it were authored content. - -The refusal belongs to the acquisition outcome. The manifest's existing ability to represent a -*deliberate* partial corpus is not permission to label an *accidentally* incomplete acquisition -complete — that distinction is the whole of §5 at this boundary. - -A regular-source-only producer with an explicit refusal is a valid narrower contract. It is not -full-directory support, and an unrestricted directory-freezing operation over a scope containing -links needs real link-preservation semantics — an explicit representation with its identity, -persistence and reconstruction behaviour — before it can claim that input. - -### One existing helper is the wrong policy to reuse - -`std.filesystem.is_text_readable` treats a symlink resolving to a text file as readable, and -`partition_entries` divides inputs into `readable` and `skipped`. That is a *readability* policy, not -an exhaustive authored-corpus capture policy. Consuming its `readable` population would both admit -link dereferences and discard other entries. Reuse the `EntryKind` vocabulary; do not inherit that -partition as the capture decision. - -## 5. RULING — enforce no-follow at acquisition, not only at observation - -This sequence is insufficient: - -``` -observe "regular file" → another actor replaces the entry with a symlink → read the pathname -``` - -The observation and the read then concern different objects; a prior `EntryKind` value does not -constrain subsequent pathname resolution. - -The acquisition realization must enforce no-follow and root-resolution when it **opens** the object, -and inspect and read the object actually opened. On Linux `openat2` supplies resolution constraints -(`RESOLVE_NO_SYMLINKS`, `RESOLVE_BENEATH`); plain `O_NOFOLLOW` constrains only the final component, -not every component of a pathname. Those are realization facts modeled under their platform -authority, not assumptions attached to a generic string read. - -This changes nothing for the other read consumers. It means the new corpus-acquisition path obtains -the capability it needs instead of assuming the generic `Read` already provides it — and where a -realization cannot supply it, the result is an explicit unsupported-capability refusal, never a -fallback to following the path. - -## 6. RULING — completion must say what was completed - -**"The walk exhausted its selected observations" is not "these files all existed together at one -instant."** A traversal over a mutable directory is not a snapshot; successful enumeration and -content reads do not supply that temporal guarantee, and POSIX's concurrent-enumeration semantics -rule out deriving it from ordinary listing. - -This producer may freeze the exact admitted contents it captured, under an explicit selection and -observation contract. A point-in-time whole-tree claim additionally needs a snapshot or quiescence -mechanism. Snapshot infrastructure is not silently made a prerequisite for a smaller capture — and a -snapshot is not silently claimed either. - -The completed-capture arm requires that every selected enumeration continuation reached successful -exhaustion, every selected entry was accounted for, and every required content acquisition completed. -Budget exhaustion, an inaccessible selected subtree, an unsupported in-scope entry, or an unresolved -acquisition failure must not reach that arm. Partial observations may remain for diagnostics; they do -not authorize publication as the completed capture. - -## 7. The discriminating evidence - -Witnesses target the distinctions above rather than demonstrating one successful walk. - -| property | discriminating specimen | -|---|---| -| listing completeness | a directory yields one entry then fails: no complete listing, and no false absence for an unreached name | -| work descent | a synthetic provider keeps revealing new work: finite fuel refuses, with no replenishment and no successful prefix | -| budget boundary | a small capture completes; the same attempt with pending work at exhaustion refuses | -| symlink policy | file, directory, ancestor-cycle, external and dangling links cause **no referent acquisition** and receive the declared refusal | -| observation-to-open safety | an entry changes from regular file to symlink between observation and open: no implicit dereference | -| occurrence preservation | two selected paths naming the same underlying file: both paths stay represented | -| completion authority | an acquisition fails after earlier files succeeded: no completed-capture result and no publication under that claim | - -Compiler-side evidence is separate: the intended loop is accepted with its real decreasing measure, -and the non-decreasing variant is **rejected by the path claimed to enforce termination** — because a -wall nothing red has ever hit is a decoration (§4b). - -Inode deduplication must not erase path *occurrences*: object identity on Unix is device plus inode, -and several directory entries may name one file. The manifest needs both selected paths even when -captured content shares storage. - -## 8. Sequencing - -1. **The listing root cut.** Widen the listing at its authority, migrate the ten-module population, - delete `filesystem_listing_names_entry`, re-decide the two `FilesystemEntryName` refusals whose - justification the cut falsifies, and file the failure-mode row below. Self-contained, with real - consumers today. -2. **Bounded acquisition.** Work fuel, the bounded collector that refuses rather than truncates, - no-follow at open. -3. **The producer.** Captured inputs → `CorpusManifestObject`, with the completion contract of §6. -4. **`RepositoryCommit.corpus`**, then the verbs. `gunbc.scm.status`'s own header currently declares - that it has no path and no working tree and calls those facts unproducible; the manifest is what - makes a path-bearing status expressible, so that header is revised *with* the change, not around it. - -## 9. The failure-mode row this survey owes - -`a_workaround_outlives_the_constraint_that_justified_it` — a construction justified by a named absent -capability, where the capability now resolves and nothing re-reads the justification. - -The three nearest rostered rows are each a different class. `unmarked_workaround` is a workaround with -*no* trigger; this one has a stated justification acting as an implicit one. -`trigger_satisfied_before_the_row_was_written` is a trigger already true *at filing time*; this is its -temporal sibling — true at filing, lifted later. -`a_live_authority_name_carries_a_superseded_claim` is a name refreshed while its claim died; here -nobody touched the sentence at all and its precondition moved underneath it. - -Recognition rule, decidable: a comment justifying a construction by a named absent capability, where -the capability now resolves. Receipt: `filesystem_io`'s "a split the interpreter does not offer", -against `split` as a seed builtin dispatched as `MethodCallSplit`. - -It lands with the cut in step 1, not as a bare roster append. - -## 10. A live defect found while sizing the cut, and a correction to how I first reported it - -The completeness property §1 requires is not merely unmodeled. It is **violated on main today**, in -the seed, where no `.dag` wall can see it. The file transport's `list` verb: - -```rust -return match std::fs::read_dir(&path) { - Ok(entries) => { - let mut names: Vec = entries - .filter_map(|e| e.ok()) - .filter_map(|e| e.file_name().into_string().ok()) - .collect(); - ... - Ok(FileResult { success: true, content: names.join("\n"), .. }) -``` - -Two silent drops, both landing in a result whose `success` channel says `true`. **They are not -equally harmful, and my first report of them said they were.** - -**The iterator drop is live.** `filter_map(|e| e.ok())` discards a `read_dir` iterator error -mid-enumeration and returns what was collected as a *complete* listing. ~~the prefix collected so -far~~ — **corrected: not a prefix.** The API permits iteration to continue past an error, so what a -discarding filter collects is an arbitrary *subset*, and "prefix" understates it. It omits ordinary, -askable names, so `filesystem_entry_presence` finds no membership for an entry that exists and -constructs a `FilesystemEstablishedAbsence` for it. - -~~A wrong answer under every absence established in the tree.~~ **Corrected:** that was a -shared-dependency claim dressed as an observed population. What is established is a *reachable route* -from a discarded enumeration error to an unsupported absence assertion — not that any particular -absence is wrong, nor that anything destructive followed. The absorbing fallback ("could not -enumerate" widened into "enumerated, nothing more") sits underneath the module built to make absence -honest; how often it has fired is unmeasured. - -**The non-UTF-8 drop is latent.** `into_string().ok()` discards any entry whose name is not valid -UTF-8, again into a `success: true` listing. But `filesystem_entry_presence` takes `name: String`, so -the asked name is always valid UTF-8 by construction and a dropped entry could never have matched it. -~~`admit_filesystem_entry_name` would refuse such a spelling independently.~~ **Corrected:** it would -not. It checks component spellings — separators, NUL, LF, CR — and is not the native-name conversion -boundary; a non-UTF-8 native spelling cannot reach its `String` parameter faithfully in the first -place. It must not be credited with a second UTF-8 admission check. Since **no consumer -enumerates** — the fact §0 establishes — the dropped entry is currently unobservable. - -It is still a success channel claiming a completeness it does not have, and it becomes load-bearing -the instant enumeration exists, which is this cut. But it is not a live wrong answer, and reporting it -as the second half of a live fail-open was the same defect this lane keeps finding, applied to my own -finding: asserting a population without checking whether the distinction reaches a consumer. The -correction is recorded rather than quietly edited, because a design document that silently agrees -with whatever was true last is not evidence of anything. - -### A third defect, which review found and I had missed: fabricated presence - -The two drops narrow the population. There is a third path that *widens* the answer, and it is the -exact dual. - -While the newline wire stands, a returned name containing a line feed makes one entry -indistinguishable from two. These produce identical bytes: - -``` -one entry: "prefix\nrepo.json" -two entries: "prefix", "repo.json" -``` - -`filesystem_listing_names_entry` searches for a newline-delimited occurrence, so it answers -`repo.json` **present** in both cases. That is a fabricated presence, and admitting the *queried* -name does not protect against it — `admit_filesystem_entry_name` constrains what a caller may ask, -and the collision is in the name the directory *returned*. - -So the repair has two directions to close, not one: a listing may not silently shrink, and it may not -silently claim an entry it does not hold. Both belong to 1A, because both are live under the current -encoding. - -Carriage return is deliberately **not** given a matching refusal. It does not collide under the -exact-`\n` join and split, and minting a refusal for it here would be a restriction with no -demonstrated defect behind it — the inverse error of leaving one standing on a lapsed justification. -Its disposition belongs with the LF case to the cut that retires this encoding. - -### What that implies for sequencing - -Step 1 of §8 splits, and the split settled differently from the first proposal here. - -~~The latent defect has no honest home except the cut that activates it.~~ **Superseded.** Refusing -an unrepresentable name in the *current* producer is also a valid repair, and it is the one taken: -the existing `String` output cannot carry such a name faithfully, and silent omission is not faithful -enumeration. That is the operation's own semantics, not a convenience. - -- **1A — the listing refuses rather than narrowing or fabricating.** All three routes, not the - iterator alone: the advancement error stops being discarded, an unrepresentable native name refuses - instead of being dropped, and a returned name colliding with the delimiter refuses instead of - fabricating an entry. Precisely: *a listing cannot report success after discarding an iterator - error, dropping an unrepresentable native name, or serializing a returned name that collides with - the listing delimiter. Supported successful listings retain their existing representation.* The - wire representation is unchanged; **the conditions for returning success change**, which is - observable behaviour and is not concealed under "the contract is untouched". -- **1B — the structured listing.** The JSON representation, exact-Unicode support with explicit - native-name refusal, one completion authority, deletion of `filesystem_listing_names_entry`, and - re-decision of the restrictions the replacement makes unnecessary — **including the two 1A makes - correct for the old encoding.** They must not survive 1B merely because 1A justified them. - -### The substrate constraint that shapes 1B - -The effect boundary carries only flat scalars and lists of **string-alias** elements: every `List` -output in `extdeps` is `List`, `List` or `List`, all from -`stdout_lines`, and no operation anywhere returns a list of records. So `entries` cannot come back as -`List<{name, kind}>` directly. Three candidates: - -| candidate | verdict | -|---|---| -| per-line encoding decoded in `filesystem_io` | viable — a *rigorously specified* record-per-line format could escape names and carry a terminal record | -| two index-correlated parallel lists | **rejected** — an unenforced index correlation is the conflation this module exists to remove | -| a JSON document on the `entries` channel, decoded once beside the operation | **chosen** | - -~~JSON is the only encoding that preserves a newline-bearing name and makes completion -representable.~~ **Corrected:** it is not the only *possible* lossless framed encoding, and a -per-line format does not *necessarily* restrict names. The reason to choose one document is simpler: -it gives this operation a single place to bind entries, their observations and the enumeration -outcome, and there is no reason to invent another line protocol. Decoding it beside the operation is -the move `filesystem_io` already makes for membership — the encoding is `List`'s fact, so the -structure over it is too. - -**And JSON framing is not a name contract.** JSON strings are Unicode; escaping removes the delimiter -collision but does not make arbitrary native filename bytes into Unicode. The two honest contracts -are *exact Unicode names*, refusing the listing on a native name that cannot be represented, and -*exact native names*, which needs an explicit reversible representation carried through every -operation that consumes the name. **1B takes the first.** Supporting arbitrary native names is a -larger, separately declared capability — not something JSON supplies automatically — and the generic -`OsString` internal encoding is explicitly not a portable wire format. - -Completion belongs to the decoded **outcome's shape**, not to an independent flag beside `entries`: -`EnumerationCompleted { entries } | EnumerationRefused { cause }`, with only the completed arm -supplying a population from which non-membership can establish absence. A document parsing -successfully does not establish that enumeration completed, and missing or malformed completion -information must not default to completion. - -## 11. What this cut does not claim - -`filesystem_absence_establishment_adoption_standing` carries a next-rung trigger reading that the raw -`List` and `Read` result projections cease to be reachable outside this module's folds, so a consumer -cannot spell the conflation at all. **This cut does not satisfy that trigger** — the raw operations -stay callable — and must not be reported as retiring it. A trigger is retired by its trigger and by -nothing else. - -## 12. The seed-growth receipt this cut owes - -1A adds hand Rust to `src/v1`, which `gunbc.seed_growth_admission` -`seed_growth_forward_freeze_policy_note` makes a stop-line unless the addition is enumerated. The row -is `gunbc.listing_completeness_seed_growth`, enrolled in the roster's own declaration list. - -**The population is derived, not authored.** `tools.seed_growth_change_population` runs -`observe_rust_item_change_against` over this branch and projects each added item through -`item_declaration_ref`; the row's list is that output. No cardinality appears in the row or in this -section — the same policy note retired authored hand-item and hand-LOC deltas because they are -functions of the diff, and a number here would re-create exactly the second representation it -deleted. Run the instrument to re-derive the list. - -**Inline modules are scopes, not leaves.** `item_declaration_ref_in` folds `enclosing_module` into -each member's `module_path`, and the admission join compares exact references, so naming a test -module does not cover the functions inside it. A first cut of the row named four items on the -opposite reading; the instrument named the nested functions the row was missing. Test items belong -in the roster on the same footing as production ones — there is no production-only exemption. - -**Its trigger binds the declarations, not a format milestone.** Three conditions, all required: -implementation displacement (the production consumers no longer need this hand code — a renamed hand -successor moves the obligation rather than retiring it), evidence continuity (each still-live -behaviour stays discriminated at the boundary where it can fail; a `.dag` witness starting from -`EnumerationRefused` begins *after* the point where the host defect discarded the error), and the -changed representation contract (under a representation supporting LF-bearing names, "LF must -refuse" is re-decided into exact preservation with no fabricated membership, not carried forward). -The narrow check is that **no operational name observation depends on the ambiguous unescaped -delimiter representation** — not a ban on `List` or on joining, since an internal list of -names, or a join over already-escaped fragments, is not the lossy transport. - -**What the instrument is not.** It is the callable **roster-coverage diagnostic**. A required -admission phase may reuse its observation and coverage components, but must retain the full admission -authority's obligations: `seed_growth_admit_change` takes `change`, `live`, `bridges` and `evidence`, -performs `gunbc.capability_origin`'s origin checks, and preserves diagnostics this entry point -deliberately omits — and the policy's next-rung trigger names stopping the line on -`SeedGrowthAdmissionRefused` specifically. **Scheduling this function would not establish that -trigger**, and running it green retires nothing. - -**Its own partial-observer repair.** `item_declaration_ref` has three outcomes and the first cut of -the verdict decided success from two: `projected_declaration_refs` excludes uncitable *and* refused -projections, `projected_refused_keys` collects only refused ones, so an added `RustImplMethod` — -which `std.decl_ref` cannot name at all — fell out of both lists and the run exited success reporting -neither the item nor the limitation. The same defect this branch exists to repair, committed by the -instrument that measures it. The verdict now carries all three causes together rather than ranking -them, is complete only when every one is empty, and keeps them distinct: unaddressable is an -unclaimed source tree, uncitable is an item no `DeclarationRef` can spell (not a row an author can -add), unjustified is a missing roster row. `test.claim.seed_growth_change_population_witness_test` -hands the decision populations no diff on this branch produces, which is the only way the uncitable -arm is reachable. diff --git a/docs/plans/d13-network-audit.md b/docs/plans/d13-network-audit.md deleted file mode 100644 index 56645f9e0f3..00000000000 --- a/docs/plans/d13-network-audit.md +++ /dev/null @@ -1,199 +0,0 @@ -# D13 Network audit — the selection rule and its citations - -Input to D13 step (b) (`gunbc.plans.demand_engine_program`; rung drop `gunbc.rung_drop` `network_requirement_unrepresented_after_uses_cut`). This page holds the reasoning behind the `requires` clauses on `dag/extdeps` operations: the rule, the rulings, and a citation for each program or API class. **It holds no per-operation verdicts.** Each verdict is the operation's own `requires` clause, which is the single authority (DESIGN §3, §6). Read the population from the clauses themselves, e.g. `grep -rn 'requires ' dag/extdeps`. The per-row table used for review was posted on gunbc#12965 rather than committed. - -## Selection rule (ruled by quiet-seal-543, 2026-10-01) - -A `requires` clause states what a sandbox must GRANT, so the fail-closed direction is to over-declare. An operation is **Network** if executing it Wet **may** open a connection to a non-local endpoint. This replaces the first draft's "necessarily". The judgement is made from the operation's own declaration (transport kind + argv/endpoint) against the cited upstream behaviour of the program or API it invokes. - -- **rest**: Network unless the declared endpoint is a unix socket or a fixed loopback address. Docker Engine binds `extdeps.docker.endpoint` `docker_default_endpoint`, a unix socket, so it is NotNetwork. The GCP metadata server (169.254.169.254) is link-local, not loopback, so it is Network. -- **file**: NotNetwork. -- **shell**: decided by program **and** authored argv: - - A fixed network protocol to a host is Network: ssh/scp/sshpass, `ipmitool -I lanplus`, `curl https://…`, `gh`, `gcloud auth`, hosted-model CLIs. - - A url/remote parameter is Network: `curl {url}`, `git fetch|push|ls-remote {remote}`, Playwright `goto {url}`, and `github.OIDC` `GetToken` with its runtime `request_url`. An operation name is not a type, so `http.Client` `GetLocalhostBounded` is Network until its `{url}` is typed loopback. That typing is the trigger that would move it. - - A fetch that depends on cache state is Network: cargo without `--offline`, `npm ci`, `npm cache add`, `npx -y`, `apt-get install`, `gcloud auth print-access-token`. - - `arping` is Network: it uses the network interface. - - A runtime-program parameter is **OpaqueDemand**: `shell.exec` Run*, `systemd-run` with `command_argv`, the sudo probe `Check`, `gunbc.WitnessBin` `Run`, and the node/python/go `RunFile` runners. The argv is runtime data, so step (b) yields DemandUndecided for their callers instead of an empty demand. - - **Runtime program in an argument** (ruling 2026-10-02): an argument that carries a program the invoked tool executes is **OpaqueDemand** -- scripts, expressions, inner argv, executing templates, and a caller-supplied program path in argv[0] (`codex_app_server` `GenerateJsonSchema`). This includes an unrefined string in option position that the tool accepts as a program-executing option (`git grep -O`). A runtime program whose language has no network or exec primitive (a jq filter; Rust source that rustc only compiles) does not raise the demand above its host program's. - - **Remote-selecting option in an unrefined string** (ruling 2026-10-02): if an unrefined string sits where the tool parses options, and the tool has an option that selects a remote host or URL (`systemctl -H/--host=`, `hostnamectl -H`, curl `-K`/`-x`), the operation is **Network**. That holds unless the argv structurally prevents it, e.g. the string follows `--` or is the value of a preceding option. - - Everything else is a local program on local paths: NotNetwork. - - **Live browser page** (ruling 2026-10-02): an interaction with a running browser is **Network**. A click can navigate, page scripts run during any call, and browser startup can fetch. A program whose option surface cannot be verified (`playwright-runner` is not in this repository) is **OpaqueDemand** for every runtime positional it receives; it is never assumed none. - - **Partial clone** (ruling 2026-10-02): a git read that needs blob or tree objects is **Network**, because in a partial clone git lazily fetches missing objects from the promisor remote, and whether a repository is a partial clone belongs to the repository the operation is pointed at, i.e. its input. Reads of commits, refs, the index or config only are not affected. -- **Repository-selected executable** (ruling 2026-10-02): if the invoked command executes a program selected by the input repository or its configuration, the operation is **OpaqueDemand**, unless the operation structurally disables that surface. This covers hooks, clean/smudge/process filters, diff/merge drivers, textconv, the credential helper, the fsmonitor hook, `core.sshCommand`, `remote.*.uploadpack`, a cargo build script or proc macro, `.cargo/config` aliases and runners, the `.npmrc` `git` executable, and agent hooks in a settings argument. Upstream surfaces, all git per git-scm.com/docs: - - githooks(5): `pre-commit`, `prepare-commit-msg` (not suppressed by `--no-verify`), `commit-msg`, `post-commit`, `post-checkout` (checkout, worktree add), `pre-merge-commit`/`post-merge`, `pre-push`, `reference-transaction` (every ref update, including `update-ref`), `post-index-change` (every index write: add, read-tree, update-index, write-tree). - - gitattributes(5): clean on add/status/diff-against-worktree/`hash-object` with a path; smudge on checkout/restore/reset/checkout-index/merge; diff drivers and textconv on patch output; merge drivers on merge and merge-tree. - - git-config(1): `core.fsmonitor` on index refresh (status, add, commit, untracked scans); `credential.helper`, `core.sshCommand`, `remote..uploadpack`/`receivepack` on every transport (fetch, push, ls-remote). - - Cargo: build scripts and proc macros (doc.rust-lang.org/cargo/reference/build-scripts.html), and user aliases that shadow external subcommands such as `fmt` (doc.rust-lang.org/cargo/reference/config.html#alias). - - npm: the `git` config key (docs.npmjs.com/cli/using-npm/config#git) for git dependencies. - - Claude Code: hooks in `--settings` (docs.anthropic.com/claude-code/hooks). - - Agent CLIs (`codex exec`, and `claude -p` with a runtime permission mode) execute model-chosen commands, which makes them runtime programs. - - - Index reads (ruling 2026-10-02, no special case): any git command that reads the index can run the `core.fsmonitor` hook on the first index load (read-cache `tweak_fsmonitor`). That covers plain `ls-files`, and also every unrefined revision argument, because a revision may be spelled `:` or `::`, which reads the index (gitrevisions(7)). So rev-parse/show/cat-file/ls-tree/merge-base/reflog/rev-list/commit-tree/diff with a runtime revision are opaque. - - Toolchain proxies in an input directory (ruling 2026-10-02): rustup selects the toolchain from `rust-toolchain.toml` in the working directory (rust-lang.github.io/rustup/overrides.html). npx reads the project `.npmrc` (e.g. `node-options`). So an operation that runs these in a cwd or repository it takes as input is opaque. The same programs run with an ambient cwd (`cargo --version`, `rustc --version`) fall under the host-environment boundary. - - Git operations that reach none of these surfaces keep their transport verdict. They have fixed revisions and no index read: rev-parse of `HEAD`/`--show-toplevel`, `rev-list --before=… HEAD`, `for-each-ref`, `worktree list`, `branch -r`, `config`, `init`, `hash-object --stdin`, and `log -- {path}` (Network: partial clone). Paging is not reached because stdout is captured, not a TTY. -- **Boundary — host environment is not demand** (ruling 2026-10-02): host-wide configuration (global/system git config, `~/.ssh/config`, rustup toolchain selection from an ambient cwd) and host-wide resolver configuration (NSS, which can route `id`/`whoami`/`stat %U` to LDAP; DNS) is the sandbox's environment, not an operation's demand, and does not raise a clause. - -## Citations by class - -| verdict → clause | citation | modules | -|---|---|---| -| Network → `requires Network` | Cloudflare API v4 (developers.cloudflare.com/api) | `extdeps.cloudflare.account_api_tokens`, `extdeps.cloudflare.r2_buckets` | -| Network → `requires Network` | GCP metadata server (cloud.google.com/compute/docs/metadata/overview): metadata.google.internal = 169.254.169.254, link-local, not loopback | `extdeps.cloud.gcp.sts` | -| Network → `requires Network` | GitHub REST API docs (docs.github.com/rest), base https://api.github.com | `extdeps.github.actions_jit_runner`, `extdeps.github.app`, `extdeps.github.checks`, `extdeps.github.code_search`, `extdeps.github.commits`, `extdeps.github.gists`, `extdeps.github.git_database`, `extdeps.github.issues`, `extdeps.github.org_actions`, `extdeps.github.pulls`, `extdeps.github.repository_contents`, `extdeps.github.rulesets`, `extdeps.github.users`, `extdeps.github.workflow_runs`, `extdeps.github.workflows` | -| Network → `requires Network` | Google Drive API v3 reference (developers.google.com/drive/api/reference/rest/v3) | `extdeps.google.drive` | -| Network → `requires Network` | Google Sheets API v4 reference (developers.google.com/sheets/api/reference/rest) | `extdeps.google.sheets` | -| Network → `requires Network` | PARAM remote: git-fetch(1)/git-push(1)/git-ls-remote(1) use the remote's transport (gitprotocol-v2(5)); a {remote} may be a local path (file transport) or a URL; operations run in a repository are now opaque (repository-selected credential helper/sshCommand/uploadpack/pre-push/reference-transaction); the Network clause remains only where none of those surfaces is reached | `extdeps.git`, `extdeps.git.publication_transport` | -| Network → `requires Network` | PARAM request_url: endpoint is the runtime ACTIONS_ID_TOKEN_REQUEST_URL (docs.github.com/actions/reference/security/oidc); declaration fixes no host | `extdeps.cloud.gcp.sts` | -| Network → `requires Network` | PARAM tarball: npm cache add (docs.npmjs.com/cli/commands/npm-cache) accepts a path or a URL | `extdeps.tools.npm` | -| Network → `requires Network` | PARAM url: Playwright page.goto (playwright.dev/docs/api/class-page#page-goto) navigates to a runtime URL | `extdeps.browser` | -| Network → `requires Network` | PARAM url: curl(1) URL scheme+host both runtime (could be file:// or loopback) | `extdeps.http.client` | -| Network → `requires Network` | RULING link-layer: arping(8) broadcasts ARP on {device} to {target}; L2 traffic, no connection to an endpoint | `extdeps.iputils.arping` | -| Network → `requires Network` | SEC EDGAR APIs (sec.gov/search-filings/edgar-application-programming-interfaces) | `extdeps.sec.edgar_rest` | -| Network → `requires Network` | STATE credential cache: gcloud auth print-access-token (cloud.google.com/sdk/gcloud/reference/auth/print-access-token) refreshes over oauth2.googleapis.com only when the cached token is expired | `extdeps.shell` | -| Network → `requires Network` | STATE package cache: apt-get(8) install downloads .debs from sources.list unless already installed/cached; PARAM package | `extdeps.apt` | -| Network → `requires Network` | STATE package cache: npm ci (docs.npmjs.com/cli/commands/npm-ci) fetches from the registry unless every tarball is cached; superseded for `npm.Ci` by the repository-selected executable rule (`.npmrc` `git`): opaque | `extdeps.tools.npm` | -| Network → `requires Network` | STATE package cache: npx -y -p (docs.npmjs.com/cli/commands/npx) installs the package from the registry unless already cached | `extdeps.typescript` | -| Network → `requires Network` | STATE registry cache: cargo build/check/test (doc.rust-lang.org/cargo/commands/cargo-build.html, --offline/--frozen) fetch the index/crates only when Cargo.lock deps are not already downloaded; argv does not pass --offline; superseded for `cargo.Build` by the repository-selected executable rule (build scripts, proc macros): opaque | `extdeps.cargo_build` | -| Network → `requires Network` | TCGplayer API (docs.tcgplayer.com), base https://api.tcgplayer.com | `extdeps.tcgplayer.catalog`, `extdeps.tcgplayer.pricing`, `extdeps.tcgplayer.store`, `extdeps.tcgplayer.tcgplayer` | -| Network → `requires Network` | curl(1) to fixed https://api.github.com (GitHub Apps REST docs.github.com/rest/apps) | `extdeps.github.app` | -| Network → `requires Network` | curl(1) to https://{bmc_host}: DMTF Redfish DSP0266 / MegaRAC web API over HTTPS — argv fixes the https scheme to a BMC host | `extdeps.bmc.http`, `extdeps.bmc.megarac` | -| Network → `requires Network` | eBay REST APIs (developer.ebay.com/api-docs), base https://api.ebay.com | `extdeps.ebay.browse`, `extdeps.ebay.inventory`, `extdeps.ebay.oauth` | -| Network → `requires Network` | fixed HTTPS endpoint https://api.anthropic.com (upstream API reference for that host) | `extdeps.llm.anthropic_rest` | -| Network → `requires Network` | fixed HTTPS endpoint https://api.openai.com (upstream API reference for that host) | `extdeps.llm.openai_rest` | -| Network → `requires Network` | fixed HTTPS endpoint https://api.tailscale.com (upstream API reference for that host) | `extdeps.tailscale.acl_api` | -| Network → `requires Network` | fixed HTTPS endpoint https://cloudresourcemanager.googleapis.com (upstream API reference for that host) | `extdeps.cloud.gcp.iam` | -| Network → `requires Network` | fixed HTTPS endpoint https://iam.googleapis.com (upstream API reference for that host) | `extdeps.cloud.gcp.iam`, `extdeps.cloud.gcp.iam_admin` | -| Network → `requires Network` | fixed HTTPS endpoint https://iamcredentials.googleapis.com (upstream API reference for that host) | `extdeps.cloud.gcp.iam` | -| Network → `requires Network` | fixed HTTPS endpoint https://jsonplaceholder.typicode.com (upstream API reference for that host) | `extdeps.test.http_pilot` | -| Network → `requires Network` | fixed HTTPS endpoint https://oauth2.googleapis.com (upstream API reference for that host) | `extdeps.cloud.gcp.gcp` | -| Network → `requires Network` | fixed HTTPS endpoint https://secretmanager.googleapis.com (upstream API reference for that host) | `extdeps.cloud.gcp.secret_manager` | -| Network → `requires Network` | fixed HTTPS endpoint https://serviceusage.googleapis.com (upstream API reference for that host) | `extdeps.cloud.gcp.serviceusage` | -| Network → `requires Network` | fixed HTTPS endpoint https://sts.googleapis.com (upstream API reference for that host) | `extdeps.cloud.gcp.sts` | -| Network → `requires Network` | gcloud auth login (cloud.google.com/sdk/gcloud/reference/auth/login): OAuth flow against accounts.google.com | `extdeps.cloud.gcp.gcp` | -| Network → `requires Network` | gh(1) manual (cli.github.com/manual): `gh api`/`gh pr`/`gh run` call api.github.com | `extdeps.github.actions_runs`, `extdeps.github.ci_runner`, `extdeps.github.org_actions`, `extdeps.github.organizations`, `extdeps.github.pulls` | -| Network → `requires Network` | ipmitool(1) INTERFACES: -I lanplus = IPMI v2.0 RMCP+ over UDP/623 to -H {bmc_host} | `extdeps.bmc.ipmi` | -| Network → `requires Network` | ssh(1)/scp(1) (+sshpass(1)): opens TCP/22 session to the host named in argv | `extdeps.bmc.openbmc_password_ssh_transport`, `extdeps.ssh.password_session`, `extdeps.ssh.session` | -| Network → `requires Network` | vendor CLI prompt mode calls the hosted model API (Claude Code docs.anthropic.com/claude-code/cli-reference; Codex CLI `exec` github.com/openai/codex; Gemini CLI github.com/google-gemini/gemini-cli); `claude.Invoke.Run` (hooks in `--settings`), `llm.Anthropic.CliPrompt` (runtime permission mode) and `llm.Codex.Review` (agent exec in `-C {cwd}`) are opaque | `extdeps.llm.anthropic_rest`, `extdeps.llm.cli` | -| NotNetwork → `requires none` | /usr/bin/stat: local coreutils/POSIX utility (man stat(1)); argv names only local paths/values | `extdeps.tools.stat` | -| NotNetwork → `requires none` | Docker Engine API (docs.docker.com/reference/api/engine): default endpoint unix:///var/run/docker.sock (extdeps.docker.endpoint docker_default_endpoint), a unix socket | `extdeps.docker.container_inspect`, `extdeps.docker.container_stats` | -| Network → `requires Network` | RULING live page: Playwright Page/BrowserContext API (playwright.dev/docs/api/class-page); a running page and browser startup may fetch (`Launch`, `CurrentUrl`, `Title`) | `extdeps.browser` | -| OpaqueDemand → `requires opaque` | RULING unverifiable surface: `playwright-runner` is not in this repository, so a runtime positional (selector, text, path, ms, context) may reach an unknown option | `extdeps.browser` | -| OpaqueDemand → `requires opaque` | RULING runtime program: Playwright page.evaluate / locator.evaluate (playwright.dev/docs/evaluating) run runtime JavaScript in the page, which can call fetch | `extdeps.browser` | -| NotNetwork → `requires none` | cargo(1) --version / cargo-fmt: no registry access (doc.rust-lang.org/cargo/commands); `cargo fmt` is now opaque (a workspace alias can shadow the external subcommand); `--version` stays none | `extdeps.cargo_build` | -| NotNetwork → `requires none` | cat: local coreutils/POSIX utility (man cat(1)); argv names only local paths/values | `extdeps.linux.cgroup_v2`, `extdeps.linux.procfs` | -| NotNetwork → `requires none` | chmod: local coreutils/POSIX utility (man chmod(1)); argv names only local paths/values | `extdeps.shell` | -| OpaqueDemand → `requires opaque` | RULING runtime program: argv[0] is the caller-supplied `CodexAppServerExecutable.path`, so the executed program is runtime-selected (codex app-server generate-json-schema itself writes schema files locally, per the github.com/openai/codex app-server README) | `extdeps.llm.codex_app_server` | -| NotNetwork → `requires none` | cp: local coreutils/POSIX utility (man cp(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | crontab(1): local spool | `extdeps.cron` | -| Network → `requires Network` | RULING remote option: curl(1) --unix-socket, but the `{url}` positional is unrefined and not after `--`, so it can carry curl options (`-K`, `-x`) that retarget the connection | `extdeps.http.client` | -| NotNetwork → `requires none` | date: local coreutils/POSIX utility (man date(1)); argv names only local paths/values | `extdeps.clock` | -| NotNetwork → `requires none` | diff: local coreutils/POSIX utility (man diff(1)); argv names only local paths/values | `extdeps.tools.diffutils` | -| NotNetwork → `requires none` | dpkg(1): local status database | `extdeps.dpkg` | -| NotNetwork → `requires none` | find: local coreutils/POSIX utility (man find(1)); argv names only local paths/values | `extdeps.linux.cgroup_v2`, `extdeps.shell` | -| NotNetwork → `requires none` | getconf: local coreutils/POSIX utility (man getconf(1)); argv names only local paths/values | `extdeps.posix.getconf` | -| NotNetwork → `requires none` | git(1); local subcommand per git-scm.com/docs reading only commits, refs, the index or config (no transport; only fetch/push/ls-remote/clone/pull use git transfer protocols, gitprotocol-v2(5)), and running no hook, filter, driver or fsmonitor surface | `extdeps.git`, `extdeps.git.inspect`, `extdeps.git.plumbing`, `extdeps.git.publication_transport` | -| Network → `requires Network` | RULING partial clone: git-partial-clone (git-scm.com/docs/partial-clone) lazily fetches missing blobs/trees from the promisor remote; diff, show, cat-file, ls-tree, log -- path, read-tree, checkout-index, unpack-file, merge, merge-tree, checkout, restore, reset --hard, worktree add; any of these that also runs a repository-selected executable is opaque instead | `extdeps.git`, `extdeps.git.inspect`, `extdeps.git.plumbing` | -| OpaqueDemand → `requires opaque` | RULING runtime program: git-grep(1) `-O` opens matches with a runtime program; `{pattern}` and `{ref}` (GitRef is only non-empty) sit in option position | `extdeps.git.inspect` | -| NotNetwork → `requires none` | grep: local coreutils/POSIX utility (man grep(1)); argv names only local paths/values | `extdeps.tools.grep` | -| NotNetwork → `requires none` | gunbc file transport: local filesystem read/write (POSIX open(2), read(2)) | `extdeps.filesystem.filesystem_io`, `extdeps.linux.procfs` | -| NotNetwork → `requires none` | gzip: local coreutils/POSIX utility (man gzip(1)); argv names only local paths/values | `extdeps.tools.gzip` | -| NotNetwork → `requires none` | hostname: local coreutils/POSIX utility (man hostname(1)); argv names only local paths/values | `extdeps.tools.hostname` | -| Network → `requires Network` | RULING remote option: hostnamectl(1) `-H/--host=` runs over SSH; `Process.Run` forwards an unrestricted ProcessArgvExpansion | `extdeps.tools.hostname` | -| NotNetwork → `requires none` | id: local coreutils/POSIX utility (man id(1)); argv names only local paths/values | `extdeps.shell`, `extdeps.tools.id` | -| NotNetwork → `requires none` | ip-address(8): rtnetlink to the local kernel | `extdeps.iproute2.ip_address` | -| NotNetwork → `requires none` | journalctl(1): reads the local journal | `extdeps.systemd.journalctl` | -| NotNetwork → `requires none` | jq(1) manual: filter over stdin/args | `extdeps.bmc.openbmc_fan_control`, `extdeps.tools.jq` | -| NotNetwork → `requires none` | kill: local coreutils/POSIX utility (man kill(1)); argv names only local paths/values | `extdeps.posix.signal` | -| NotNetwork → `requires none` | ln: local coreutils/POSIX utility (man ln(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | mkdir: local coreutils/POSIX utility (man mkdir(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | mktemp: local coreutils/POSIX utility (man mktemp(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | mv: local coreutils/POSIX utility (man mv(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | node: local coreutils/POSIX utility (man node(1)); argv names only local paths/values | `extdeps.tools.node` | -| NotNetwork → `requires none` | npm --version (docs.npmjs.com/cli/commands/npm) | `extdeps.tools.npm` | -| NotNetwork → `requires none` | npm ci --offline (docs.npmjs.com/cli/using-npm/config#offline): forces cache-only, no network; superseded by the repository-selected executable rule: opaque | `extdeps.tools.npm` | -| NotNetwork → `requires none` | nvidia-smi(1): local NVML | `extdeps.nvidia.system_management_interface` | -| NotNetwork → `requires none` | oomctl(1): local systemd-oomd | `extdeps.systemd.oomd` | -| NotNetwork → `requires none` | openssl-dgst(1): local signing | `extdeps.tools.openssl` | -| NotNetwork → `requires none` | printenv: local coreutils/POSIX utility (man printenv(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | ras-mc-ctl(8): local EDAC sysfs/rasdaemon DB | `extdeps.linux.edac` | -| NotNetwork → `requires none` | realpath: local coreutils/POSIX utility (man realpath(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | rm: local coreutils/POSIX utility (man rm(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | rmdir: local coreutils/POSIX utility (man rmdir(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | rustc --version / local compilation (doc.rust-lang.org/rustc) | `extdeps.rustc` | -| NotNetwork → `requires none` | rustfmt: local coreutils/POSIX utility (man rustfmt(1)); argv names only local paths/values | `extdeps.tools.rustfmt` | -| OpaqueDemand → `requires opaque` | RULING runtime program: GNU sed's `e` command and `s///e` execute shell commands; the operations take a runtime sed program without `--sandbox` | `extdeps.tools.sed` | -| NotNetwork → `requires none` | sh -c script authored in the declaration uses only local programs (mktemp(1), find(1), sort(1), head(1)/tr(1) over /dev/urandom, rustc --emit=metadata, command -v) | `extdeps.entropy`, `extdeps.rustc`, `extdeps.shell` | -| NotNetwork → `requires none` | sha256sum: local coreutils/POSIX utility (man sha256sum(1)); argv names only local paths/values | `extdeps.crypto.hash`, `extdeps.tools.sha256sum` | -| NotNetwork → `requires none` | sha512sum: local coreutils/POSIX utility (man sha512sum(1)); argv names only local paths/values | `extdeps.tools.sha512sum` | -| NotNetwork → `requires none` | sleep: local coreutils/POSIX utility (man sleep(1)); argv names only local paths/values | `extdeps.tools.sleep` | -| NotNetwork → `requires none` | stat: local coreutils/POSIX utility (man stat(1)); argv names only local paths/values | `extdeps.shell`, `extdeps.tools.coreutils_stat` | -| NotNetwork → `requires none` | sudo(8) -l: local policy | `extdeps.sudo.nopasswd_execute_probe_check_op` | -| NotNetwork → `requires none` | systemctl(1): talks to the local systemd manager over D-Bus/private socket; only operations with no unrefined positional string (`DaemonReload`) | `extdeps.systemd.systemctl` | -| Network → `requires Network` | RULING remote option: systemctl(1) `-H/--host=` runs over SSH; the unit/pattern positional is an unrefined NonEmptyStr with no `--` before it | `extdeps.systemd.systemctl` | -| NotNetwork → `requires none` | tailscale CLI `serve status` reads the local tailscaled LocalAPI socket (tailscale.com/kb/1242/tailscale-serve) | `extdeps.tailscale.serve` | -| NotNetwork → `requires none` | test: local coreutils/POSIX utility (man test(1)); argv names only local paths/values | `extdeps.linux.cgroup_v2`, `extdeps.shell` | -| NotNetwork → `requires none` | tmux(1): local server socket (`List`, `Kill`) | `extdeps.tmux` | -| OpaqueDemand → `requires opaque` | RULING runtime program: tmux new-session runs `inner_argv`, a runtime command | `extdeps.tmux` | -| NotNetwork → `requires none` | uname: local coreutils/POSIX utility (man uname(1)); argv names only local paths/values | `extdeps.shell` | -| NotNetwork → `requires none` | wc: local coreutils/POSIX utility (man wc(1)); argv names only local paths/values | `extdeps.tools.wc` | -| NotNetwork → `requires none` | whoami: local coreutils/POSIX utility (man whoami(1)); argv names only local paths/values | `extdeps.access.posix_effective_principal_read_op` | -| NotNetwork → `requires none` | xorriso: local coreutils/POSIX utility (man xorriso(1)); argv names only local paths/values | `extdeps.tools.xorriso` | -| OpaqueDemand → `requires opaque` | PARAM bin_path: program is runtime | `extdeps.gunbc` | -| OpaqueDemand → `requires opaque` | PARAM command_argv: systemd-run(1) itself is local; the transient unit runs a runtime command | `extdeps.systemd.systemd_run` | -| OpaqueDemand → `requires opaque` | PARAM probe.command_path: sudo(8) runs a runtime program | `extdeps.sudo.nopasswd_execute_probe_check_op` | -| OpaqueDemand → `requires opaque` | PARAM program/command body: argv supplied at runtime | `extdeps.shell.exec` | -| OpaqueDemand → `requires opaque` | PARAM script_path: behaviour is the runtime script | `extdeps.go`, `extdeps.node`, `extdeps.python` | - -## Follow-ups (better modeling than the clauses above; not done here) - -- Repository-selected executables in git, by structural disabling. Each item has its citation, and each would move the affected operations back to their transport verdict: - - `-c core.hooksPath=/dev/null` disables every hook, including `prepare-commit-msg` and `reference-transaction` (git-config `core.hooksPath`; githooks(5)). `--no-verify` alone is insufficient. - - `-c core.fsmonitor=false` (git-config `core.fsmonitor`) disables the fsmonitor hook on every index read. `--no-optional-locks` does not: it only skips the opportunistic index write (git(1)). - - Revision arguments: a refined revision type that excludes the `:` index forms, or `--end-of-options` plus a full object id, removes the index read from rev-parse/show/cat-file/ls-tree/merge-base/rev-list. - - Toolchain proxies: pin the toolchain explicitly (`cargo +`, `RUSTUP_TOOLCHAIN`) and run npx with `--userconfig`/an isolated cwd, or the operation stays opaque. - - `--no-textconv --no-ext-diff` on diff (git-diff(1)). - - `hash-object --no-filters` (git-hash-object(1)). - - Overriding the filter and merge-driver surfaces needs per-driver `-c filter..*`/`merge..driver` overrides, which are unknown in advance. A typed attribute-free checkout realization is the structural route; otherwise those operations stay opaque. - - On transports, `-c credential.helper=` (an empty value resets the helper list; gitcredentials(7)), `-c core.sshCommand=ssh`, and dropping `--upload-pack`. -- Cargo: no flag disables build scripts or proc macros, so those operations stay opaque until the workspace's build-time programs are modeled. -- npm ci: `--git=false`-style suppression is not documented. Pin the lockfile to registry-only sources and refuse git dependencies. - -- `systemd.Systemctl` and `hostnamectl.Process`: terminate options before the unit/pattern positional (`--`), or refine the input to a unit-name type. Either move those operations back to `requires none`. -- `sed.Sed`: a typed non-executing sed subset, or `--sandbox` in the argv, would move both operations to `requires none`. -- `tmux.Session.New`: an inner-command carrier whose program demand is declared. -- `browser.Page.Evaluate` / `browser.Element.EvaluateOn`: a structurally non-network expression vocabulary. -- `git.Inspect` grep operations: `-e {pattern}` plus `--` before the revision, or a refined GitRef, would remove the `-O` route. -- git object reads: add `git --no-lazy-fetch` (git 2.44+) to the read operations, then move them to `requires none`. -- `extdeps.browser`: bring the runner's CLI into the repository (or cite its option surface) and terminate options before positionals; the selector operations then fall to the live-page Network rule. -- `http.Client.PostStdinWithinUnixSocket`: put `--` before `{url}` and type the url to the socket's authority. - -## Product-layer operations outside `dag/extdeps` (same rule) - -These 14 services in `dag/gunbc` had no clause; an absent clause is Undecided, so every caller would refuse. Verdicts: - -| verdict → clause | reason | operations | -|---|---|---| -| Network → `requires Network` | rest to `approval_ntfy_endpoint` (`https://ntfy.sh` or the tailnet HTTPS base), not a unix socket or loopback | `gunbc.auth.approval_ntfy_deployment` `ntfy.Publish.PublishMessage` | -| OpaqueDemand → `requires opaque` | runtime program: argv[0] is the caller-supplied `bin_path` | `gunbc.cli_services` `gunbc.Cli.Run`, `claim_executor.Executor.VerifyBuildArtifacts` | -| OpaqueDemand → `requires opaque` | runtime program: the script runs the caller-supplied `{ipmitool}` path (lanplus to `{bmc_host}`) | `gunbc.machine_intake.sol_hold` `ActivateHeld` | -| OpaqueDemand → `requires opaque` | runtime program: launches the caller-supplied `command` argv | `gunbc.owned_process` `launch.LaunchOwned` | -| NotNetwork → `requires none` | local only: reads the pid record, checks `/proc`, `kill`s the recorded pid | `gunbc.machine_intake.sol_hold` `ReleaseHeld` | -| NotNetwork → `requires none` | no transport: a pure fold over its inputs | `gunbc.code_change_workflow` `ClassifyGithubPrTerminalAnchor`, `DecideTransition`; `gunbc.pr_digests` `ExtractAttachedUrls`, `RenderPrSummaryLine`, `JudgeMergeReadiness`, `ClassifyRestFallback`; `gunbc.review_verdict` `Parse`, `Tally` | - -## Deliberately undeclared test fixtures - -These test and fixture operations carry no `requires` clause on purpose. Each exists to exercise a transport, argv or governance mechanism, and no test of theirs reads a demand, so none needs a clause today. A fixture gains one when a test first needs it. The list is the population at this writing; read the current one by scanning for `operation` blocks without `requires` outside `dag/extdeps` and `dag/gunbc`. - -- `dag/test/claim/m4_governed_service_witness.dag`: `GovernedProbe` `Allowed`, `Forbidden` -- `dag/test/claim/reconstruction_door_fixture_probe.dag`: `DoorProbe.Fetch` -- `dag/test/claim/reconstruction_door_rest_probe.dag`: `DoorProbeRest.Fetch` -- `dag/test/claim/shell_spawn_refused_real_execution_witness_test.dag`: `test.ShellSpawnProbe.Observed` -- `dag/test/fixture/argv_executable_position_probe.dag`: `ArgvExecutablePositionProbe` `SmuggleExecutable`, `LiteralExecutable` -- `dag/test/fixture/m4_universal_governed_probe.dag`: `GovernedUniversalProbe` `Allowed`, `Forbidden` -- `dag/test/fixture/rest_exchange_replay_probe.dag`: `test.RestReplay` `Observe`, `RootArray`, `RootFile`, `RootPage`, `WorkflowRunsPage`, `WifProvidersPage`, `WifPool`, `WorkflowRunsPageLegacy`, `RootUncontracted`, `Sibling`, `OptionalObserve`, `Legacy` -- `fixtures/atomic_materialization/subject.dag`: `AtomicFixture.Read` -- `fixtures/bare_service_provider/provider.dag`: `fixture.BareServiceEcho.Say` -- `fixtures/fixture_closure_rustc/argv_word_list_splice_probe.dag`: `fixture.WordListTransport` `InteriorList`, `TrailingList`, `TwoListsOneArgv`, `SingleWordOnly` -- `fixtures/fixture_closure_rustc/shell_multi_field_projection_probe.dag`: `fixture.MultiFieldProjection.TwoFieldExitSuccess` -- `fixtures/fixture_closure_rustc/shell_single_field_projection_probe.dag`: `fixture.SingleFieldProjection.OneFieldExitSuccess` diff --git a/docs/plans/dag-emit-one-grammar-backward.md b/docs/plans/dag-emit-one-grammar-backward.md deleted file mode 100644 index 5bf453025e3..00000000000 --- a/docs/plans/dag-emit-one-grammar-backward.md +++ /dev/null @@ -1,248 +0,0 @@ -# The dag target emits by reading `dag_grammar_root` backward - -Status: **implemented** in gunbc#12878 (`v2.std.grammar` `grammar_emit_parse_tree`, `v2.extdeps.languages.dag` `dag_emit_parse_tree`) (work item adhoc-da997c00-fa0). This page states how emit selects a -production for a Node shape and how each terminal's token class is recovered. It was written as the -model before code and accepted, and the implementation follows it except where a section below says -otherwise. - -## The defect being replaced - -DESIGN §4 says there is one grammar, read in both directions. For the v2 dag target that is -currently false (measured by tidy-lark-233 on main `cc2fefacac`): - -- `v2.extdeps.languages.dag` `dag_translation_rules_node` carries one row, the `fn add` fixture. -- `token_class_emit_transforms` is `target_model_emit_transforms_empty`. -- The `emit_row_*` functions have no consumer outside the module. -- `data m: Int = 1` refuses with `v2.compiler.ingest` `parse_tree_atom_token_class_not_recoverable`. -- The only declaration round trip, - `v2.test.execution.emit_ingest_type_decl_round_trip`, runs on - `dag_type_decl_structural_formal_productions` and its own lexer - (`dag_type_decl_structural_lex_rules`). That is a second, hand-authored grammar for one - language, which is a parallel authority under DESIGN §3. - -`gunbc.dag_grammar_fork_census` rosters the root of the problem: two production tables. One is the -reachable `dag_grammar_root`, which is `GrammarExpr` with choice, repeat, optional and -nonterminals. The other is the unreachable flat `FormalProduction` token spines, and every -relation row is grounded on them. The backward machinery in `v2.std.grammar` -(`grammar_relation_row_backward_selection`, `resolve_emitted_productions`) can only read the -second table. - -## The subject: which Node emit starts from - -Emit's input is the **parse-tree Node that ingest's parse produces**. That is the surface Node whose -production wraps are stamped by `v2.compiler.parse` `parse_wrap_production_captured`. It is not the -core Node that body lowering produces from it. - -The grammar's own `emitted` values are surface atoms (`^dag_surface_fn_decl`, and so on). So -"one grammar read backward" relates source text to *this* Node. Inverting body lowering is a -different relation with a different authority, and it is out of scope here (see the open -questions). The round-trip control is therefore: - - parse(text) = N - emit(N) = text' - parse(text') = N' - -and it asserts `v2.std.node` `content_hash(N) == content_hash(N')`. `content_hash` does not cover -occurrence identity, so source spans can legitimately differ. - -## How emit selects a production: read the stamp, then invert each `GrammarExpr` arm - -**Production selection is a lookup, not a search.** Every production capture in the parse tree is -the Conj `{grammar_production_identity_node_projection: production.emitted, -grammar_production_captured_node_projection: captured}`. Emit reads the identity edge and looks up -the one production in `dag_grammar_root` whose `emitted` is that identity. That lookup is total over -the closed production list. A missing production is the typed refusal below. - -There is no shape-guessing at production grain, and no second index. `emitted` atoms are unique -per production. The implementation checks that once, when it builds the index from the -`GrammarRoot`, and refuses a duplicate there. - -**Within a production, emit walks the `GrammarExpr` and the captured Node together.** Each arm is -the exact inverse of what its parse arm in `v2.compiler.parse` builds: - -| `GrammarExpr` arm | What parse captured | What emit does | -|---|---|---| -| `Sequence { left, right }` | Conj `{sequence_left: cap1, sequence_right: cap2}` (`parse_stamp_derived_conj`) | emit `left` over `cap1`, then `right` over `cap2`, and concatenate the token spines | -| `Repeat { element }` | right-folded Conj spine of `sequence_left`/`sequence_right`, ending in empty Conj (`parse_repeat_captured_node`) | unfold the spine and emit `element` over each item | -| `Optional { element }` | the element's capture, or `grammar_empty_node()` | empty Conj gives no tokens; otherwise emit `element` | -| `Choice { left, right }` | the winning arm's capture, untagged | **ordered:** try `left` backward; if it refuses, try `right`; if both refuse, refuse | -| `Expect { element, reason }` | the element's capture (an Expect is a label, never a cut) | emit `element` | -| `Nonterminal { production }` | a production wrap | select by stamp as described above, and check the stamp names `production` | -| `Terminal` / `LiteralTerminal` | an atom, or a literal payload node | emit one token (next section) | - -Ordered backward choice mirrors parse's ordered forward choice. If the left arm accepts a shape, -its spine re-parses through the left arm first. - -There is one place where a shape alone could mislead: a `StampClass` atom and a `StampLexeme` -atom are both childless atoms. An identifier spelled exactly like a token-class symbol could -therefore satisfy the wrong arm. That is not a case to guess about. The round-trip control is what -falsifies it. The walk resolves the collision one way: a `StampLexeme` position reads a childless -atom as its lexeme first. - -None of this needs a per-production emit row. The rows ARE the `dag_grammar_root` productions. A -new production gets emit for free, and a new target language is still rows, not an edit to the -walk. The walk is generic over `GrammarExpr`, so it belongs in `v2.std.grammar` beside the forward -reader, not in `extdeps/languages/dag.dag`. - -## How each terminal's token class is recovered: from the grammar position, never from the atom - -`parse_tree_atom_token_class_not_recoverable` exists because today's reverse path asks the *atom* -for its class. A `StampLexeme` atom carries the interned lexeme, so its class cannot be read back -from it. Walking the grammar alongside the tree removes the question. At every terminal, emit -already holds `Terminal { token_class, stamp }` or `LiteralTerminal { token_class, lexeme }`, so -the class is a fact of the grammar position. - -The **spelling** of the token then comes from one of two places. - -**Fixed-text classes**, such as keywords and punctuation, are spelled by the class's -`TokenRule { pattern: LiteralPattern }` in `dag_lex_rules`. That is the lexer's own row, so there -is no second spelling table. A `LiteralTerminal` is spelled by its `lexeme`. - -**Value-carrying classes** are spelled by `v2.extdeps.languages.dag` -`dag_emit_class_terminal_spelling`. Each arm is the inverse of the parse-side decoder that #12759 and -its predecessors stamped. These arms do **not** go into `TargetModel` `token_class_emit_transforms`: -that map transforms one spelling into another, whereas these arms start from a decoded value. The -map stays empty for the dag target. - -| class | captured Node (parse side) | emit transform | -|---|---|---| -| `dag_token_ident` (`StampLexeme`, `StampBinding`) | atom whose identity is the interned lexeme | the interned spelling | -| `dag_token_int_literal` | `dag_int_literal_node_from_magnitude` payload | the magnitude in decimal, the inverse of `dag_int_literal_node_from_lexeme` | -| `dag_token_float_literal` | `float_literal_lexeme_field` payload | the stored lexeme, verbatim | -| `dag_token_string_literal` | `string_literal_value_field`, the decoded value (#12759) | quote-frame the value, and **escape it from the same `dag_string_escapes` rows** the decoder reads; a scalar with no printable row is written `\u{H..H}` | -| caret symbol | `dag_token_caret` then an ident (`dag_grammar_caret_symbol_expr`) | not a transform: a fixed token, then the ident transform | -| quoted field key | `dag_grammar_field_init_string_key_expr`'s class-stamped string terminal | not a transform: the string-literal row (the key and the literal are one terminal since #12759) | - -For strings, the encoder and the decoder read one table. Decode composed with encode is the -identity on values. Encode composed with decode is not the identity on spellings, and does not need -to be: the round trip is on the Node. - -**Literal payloads are read through the model's own readers:** -`dag_int_literal_magnitude_int_from_node`, `dag_string_literal_value_optional`, and -`dag_float_literal_lexeme_optional`. Lowering uses the same readers. A literal is an `Atom` that -carries a payload edge, and `v2.std.node_query` `named_child_lookup` searches only `Conj` roots, so -it cannot be used to read one. - -**Token separation.** Emit writes one space between adjacent tokens. This is **not** source-faithful -layout: `module rt.x` comes back as `module rt . x`. The one exception is layout the grammar -itself requires. An `AfterLineBreak` row (gunbc#12773) matches only after a line break, so the walk -yields `GrammarEmitLineBreak` before its element, and the dag spelling writes a newline there. -A `RefuseOnMatch` row derives no tree, so backward it is an arm that never fits. Whitespace is a -`TriviaRule`, so this cannot change the Node. Annotations (DESIGN §4c) are erased from the parse -tree and are not emitted. A target that needs layout is a separate projection. - -## Typed refusals - -Each refusal is located with `node_locus` at the node being emitted. The generic ones come from -`v2.std.grammar`; the spelling one comes from the dag model. - -**Hard refusals.** These are never retried by an enclosing choice: - -- `grammar_emit_production_unknown`: the stamp names no production in the root. This is the - "missing emit row" red. -- `grammar_emit_projection_ambiguous`: a capture carries two projection edges of one name. Parse - mints each projection edge once, so this is a malformed tree, not an arm that does not fit. - -**Arm mismatches.** An enclosing ordered choice tries its next arm; if no arm fits, the last -mismatch is the refusal: - -- `grammar_emit_production_stamp_absent`: a nonterminal position holds a capture with no - production stamp. This is neat-boar-16's condition (3): a production is never guessed. -- `grammar_emit_nonterminal_stamp_mismatch`: the stamp names a different production than the - position expects. -- `grammar_emit_sequence_shape_mismatch`, `grammar_emit_terminal_not_atom`, - `grammar_emit_terminal_class_mismatch`: the capture is not what the arm's parse builds. - -**Index refusals.** These refuse the grammar itself: - -- `grammar_emit_production_emitted_not_atom` -- `grammar_emit_production_emitted_duplicate` - -**Spelling refusal.** `dag_emit_token_class_untransformable`: a class with neither a fixed lexeme nor -a value inverse. It is located at the terminal. - -The atom-collision case named above (a class atom versus a lexeme atom) is not a separate refusal. A -`StampLexeme` position reads a childless atom as its lexeme first, and the round-trip control is the -falsifier. - -There is no fallback arm anywhere. A refusal never widens to "emit the atom's spelling" (DESIGN §5). - -## The cut - -This is one change, per DESIGN §3 on replacement migrations: delete first, then fix forward. - -1. **Add** the generic backward walk to `v2.std.grammar` (`grammar_emit_parse_tree`), and the dag - spelling to `v2.extdeps.languages.dag` (`dag_emit_parse_tree`). The string encoder shares - `dag_string_escapes`. -2. **Not done here; this is the declared frontier.** `dag_translation_rules_node` still enrolls only - the `fn add` row. It serves the core route, which needs the inverse of body lowering before it - can read this walk (see "Rulings and declared frontier"). -3. **Delete, in the same change:** the hand-authored declaration grammar. That means - `dag_type_decl_structural_formal_productions`, - `dag_type_decl_productions_only_translation_rules`, `dag_type_decl_structural_lex_rules`, - `dag_type_decl_structural_lex`, `dag_type_decl_structural_target_model`, and their only consumer, - `v2.test.execution.emit_ingest_type_decl_round_trip`. Its roster entries in - `v2.workflow.floor_grandfathered_roster` and `gunbc.witness.witness_deferral_freeze` go with it. - - **Correction, measured while cutting.** The flat `dag_formal_production_fn_*` table and - `v2.compiler.ingest` `parse_atom_frontier_class` are **not** part of this route, so this change - does not delete them: - - They serve the cross-language forward bridge `parse_tree_to_emitted_node`, which Python and - TypeScript also use. - - They serve `v2.compiler.body_producer_forward`. - - They serve the core-route emit, `compile_dag_source_to_target_text`. - - That is the core -> surface frontier above. They are retired when that item lands, against the - roster in `gunbc.dag_grammar_fork_census`, which keeps rostering them until then. -4. **Replace** `emit_ingest_type_decl_round_trip` with the coverage round trip over the real - grammar. - -**Controls.** Round trip on the parse-tree Node for each of these fixtures: - -- `data m: Int = 1` -- a fn declaration -- a record type -- a coproduct -- `match` -- `let` -- a list -- a map literal -- a quoted key -- string escapes (`\"`, `\\`, `\n`, `\u{e9}`, `\x41`) -- a caret symbol - -Quoted keys add more controls (from gentle-koi-724, for #12740 and #12758): - -- **The key's kind follows production identity, not spelling.** `{"Node": true}` must emit quoted, - through `^dag_surface_field_init_string_key`, and `{Node: true}` must emit bare. Both are - fixtures. -- **An escaped key round-trips.** `"a\"b"` and a key containing a `\u{..}` scalar re-escape to - an equivalent spelling and re-ingest to the identical node. -- **Two mutants must go red:** emitting the raw lexeme, and emitting a quoted key in bare form. -- **The tree covered is the surface tree.** This is the pre-elaboration map literal, not resolve's - elaborated `map_from_entries(..)`. Quoted keys in record literals still refuse at lowering - (#12740's frontier), so they are parse-tree fixtures only. - -There is also one red: a root with one production removed must refuse with -`grammar_emit_production_unknown` at the right locus. Both the reds and the positives run through the -real `parse_module` route, not a supplied tree. This route is the inhabitance claim. - -## Rulings and declared frontier - -**Subject (neat-boar-16's ruling).** Emit's subject is the **parse-tree Node**. The control is -text -> parse tree -> text -> parse tree, and the two trees must be identical. The core Node is not -the subject. - -**A missing stamp refuses.** A tree that lacks the production stamps emit needs is refused, typed -and located, as `grammar_emit_production_stamp_absent`. Emit never guesses a production from the -shape. - -**Declared frontier: core -> surface.** This emitter is a parse-tree emitter. It is **not** full -dag emission. To emit a program that was constructed or lowered rather than parsed, emit needs the -inverse of body lowering: from a core Node back to a production-stamped surface tree, which this -walk then reads. That inverse belongs to the lowering's own authority and is a separate work item. -Its named consumer is gen-2 self-host emission: the built CLI emitting a closure (DESIGN §7). Until -that item lands, nothing on that route may cite this walk as dag emission. - -**Open: 02_parse's prepared grammar.** I still need its owner to confirm that the projection-edge -shapes in the table above are a stable contract. diff --git a/docs/plans/dag-native-scm-design.md b/docs/plans/dag-native-scm-design.md deleted file mode 100644 index 807b59f5a9e..00000000000 --- a/docs/plans/dag-native-scm-design.md +++ /dev/null @@ -1,548 +0,0 @@ -# A `.dag`-native SCM — model, vocabulary, and first executable slice - -DRAFT, design-note-first. **No code lands from this note.** Claims are marked **verified** (read -out of the live tree), **derived** (a consequence of a verified claim plus an authority doc), or -**proposed** (a design position awaiting a discriminating witness or an operator ruling). - -This note supersedes an earlier draft on the same branch that modelled merge as a three-way diff -over parsed trees. §9 records what was rejected and why; the scenario corpus from that draft -survives unchanged in §8. - ---- - -## 1. Vocabulary — git's words, git's constraints deliberately not inherited - -Operator ruling: **anchor on git's terms** — for anti-forking, not familiarity. Coining a new word -for a concept that already has an industry-standard one is the nicknaming DESIGN §3 forbids, and -git's vocabulary is the shared one. New vocabulary must earn its place by naming something git -genuinely fuses or lacks. - -The separating rule: **keep the word for what it *means*; drop what git *does* to implement it.** -"Commit" means *record a state I can point at later* — that survives. Git's mechanism — a whole-tree -snapshot fused with one parent, an author and a timestamp — is one realization, not the meaning: -DESIGN §3's interface/realization split applied to naming. - -| term | meaning here | -|---|---| -| **object** | immutable language content, content-addressed | -| **proposal** | requested obligations — what an author asks the program to contain | -| **branch** | a line of proposals and commits; a grouping label, contributing nothing to identity | -| **role-requirement integration** | combine proposals into one target commit. Named `merge` until 2026-09-04; renamed because two-commit merge is a materially different contract and one spelling cannot carry both | -| **commit** | the exact frozen program | -| **acceptance** | agreement to use a commit — the one new term | -| **deploy** | make an accepted commit active | - -`acceptance` earns its place because git's `commit` fuses three separate questions: *is this program -valid?*, *have we agreed to use it?*, and *is it what is running?* Merging them back into one word -would re-create the fusion this model exists to remove. - -**Git terms and their disposition.** Almost every row is *refused*: it names a *mechanism* rather -than something a person wants, and exists to repair a model that loses information. One row is a -*withdrawal* — a term this note refused on reasoning that turned out to be wrong — carried here and -marked rather than deleted, so the correction stays attached to the claim it corrects. Each term's -standing is stated by its `disposition` cell; membership in this table no longer asserts refusal. - -| term | disposition | why | -|---|---|---| -| `rebase` | refused | "rewrite my work as though it started elsewhere" is a procedure, not a goal; the goal — make my work apply to the current state — happens without it | -| merge base / common ancestor | **withdrawn 2026-09-04** | It read "requires a total order that need not exist", which is false: a commit DAG supplies a PARTIAL ancestry order, common ancestors are the intersection of two ancestor closures, and best common ancestors are the maximal elements of that intersection. No total order is involved. The concept is a prerequisite of two-commit merge, which this note does not yet model | -| `HEAD~3`, `HEAD^` | refused | parent arithmetic assumes a single line | -| force push | refused | acceptances only append; there is nothing to force | -| index / staging area | refused | an artifact of the working-copy model | -| `reset --hard` | refused | mutating a pointer | -| `cherry-pick` | refused | patch transport, which this model does not do | -| conflict markers | refused | a textual representation of an unresolved choice | -| `squash` | refused | role-requirement integration already produces one exact commit; squash is a git-export description | - -## 2. Comments, formatting, and everything that is not the program - -Stated first because it decides what a commit *is*, and because getting it wrong is how a system -silently loses what people wrote. - -**Verified.** `std.source_annotation` already models this: `AnnotationPlacement`, -`UnboundAnnotationCapture`, `SourceAnnotationDebt`, `SourceAnnotationGraph`, -`AnnotationAttachmentRefusal`. Comments are captured *data attached to structural subjects*, not -discarded trivia and not semantic program content. - -DESIGN §4c rules the two facts this design needs. Adding, deleting or moving an annotation **cannot -alter any semantic occurrence identity, semantic graph, resolution result, semantic hash, or -target-program bytes**; and semantic passes receive the annotation-erased projection while *"source -authoring, formatting, **SCM**, and annotation lenses consume and preserve the authored wrapper."* - -**Derived — two identities, deliberately:** - -- **semantic identity** — over the annotation-erased graph. Annotation-invariant *by rule*. -- **authored identity** — the semantic graph together with its annotation graph. What was written. - -Four consequences, all of them features: - -1. **A comment-only change is provably a semantic no-op** — new authored identity, identical - semantic identity, stated as a fact rather than guessed from bytes. -2. **Comments never cause semantic conflicts.** Annotations are keyed by their subject, so two - people annotating different declarations never interact. -3. **An annotation collision is its own lower-stakes conflict**, reportable separately from a - semantic one. -4. **Reformatting is free**, and the authored form still round-trips because captures carry the raw - `lexeme` — delimiter included — normalized on read, never on capture. That distinction is - load-bearing: its own carrier records that a realization stripping the delimiter where another - does not makes parse→render→parse either double it or silently change content. - -**Comments are eternal carried debt** (operator framing; the type is literally -`SourceAnnotationDebt`). DESIGN calls plain annotations modeling debt: prose not yet migrated into -typed carriers, carried forward until someone models it properly. The SCM's obligation is to **carry -them faithfully without enshrining them** as permanently first-class, so the debt stays payable -rather than load-bearing. - -## 3. The model - -**The store is append-only and immutable.** Editing creates another object, never modifies one. -Objects are content-addressed, so identical content is one object. Nothing in the store ever -conflicts. - -**A program is an act of exclusion, so the store alone is not enough.** The store only grows — -adding an object never invalidates anything — but choosing one `foo` means not the other. A -monotonic store cannot produce that non-monotonic fact, so something must *close* a selection and be -nameable. This is A3 (agreement holds only on what stays stable across time) reaching the design -directly. - -**A commit is that closed thing: one root object identity whose every reachable edge names exact -content.** No parent list, author, timestamp, message, or branch — those are facts about proposals -and acceptances, not the program. - -**Selection must not stay live after a commit.** A program re-querying the store at use time would -let later additions retroactively change its meaning. Integration outputs an immutable exact graph, which -is why the unit of agreement is a commit root rather than a choice function. - -Five facts, kept apart: - -``` -store possibility — every object that exists -commit a program — one exact closed graph -certificate validity — does this program hold together -acceptance agreement — we have agreed to use it -deploy now — it is what is running -``` - -**Identity does not need to survive across revisions, and cannot.** *Verified:* -`std.occurrence_identity` `occurrence_identity_scope_law` makes `OccurrenceId` unique only inside -one graph-scoped allocator and forbids filename, span, authored name, structural equality and -content hash as identity inputs. *Derived:* cross-revision identity could therefore only be minted -at authoring time and carried in the source — and DESIGN §3's fleet ruling says inferring it from -content similarity is the heuristic §4 forbids. **It is also unnecessary:** identity's only job in a -line-based merge is transporting a patch to the right place, and combining proposals transports -nothing. - -**A name identifies a binding role in a context, not a thing.** Renaming changes a binding; the -value is untouched. So content equality does not imply one program occurrence — one object may sit -at several positions, sharing storage while independently addressable by path. - -**"Latest" is a query, not an identity.** Already ruled here: `extdeps.pin` `pin_selection_note` — -*execution always resolves an exact identity, because resolving "latest" at execution is -nondeterminism at the substrate boundary; re-resolution is an explicit action producing a reviewable -diff.* Pinning a moving reference is no contradiction: resolve it to an exact identity and record -that it came from a channel. `Pin` is already that shape, subject-generic by operator -ruling, and `Pin` is an instantiation, not a new concept. - -## 4. What role-requirement integration is - -This section is about combining PROPOSALS into one target commit. It is not about two-commit merge, -which this note does not model; see the vocabulary table in section 1. - -Two proposals combine by taking both. A conflict exists only where one binding is required to hold -two different objects — a genuine disagreement about what the code should be, never a collision of -positions. - -Where the substrate says an edge is `Named`, recursion is keyed by that name; where `Positional`, -**order carries meaning** — list elements, match-arm order — and the region is compared whole. -Refusing there is the safety half of the design: combining positional edits by identity would -fabricate a sequence nobody wrote, which is precisely what line-based merge does and calls success. -*Verified:* `v2.std.node` carries `EdgeLabel = Named { name } | Positional` per-edge at every depth, -and named edges reach deep — `match_arm_pattern`, `binding`, `field`, and a function attaching to -its parent as `Named { name: fn_name }`. - -**One grain — the node.** Depth is not a parameter; it falls out of where the substrate says -identity is by name versus by order. - -## 5. What this model does not claim - -**Integrations are not "always safe."** Deep semantic understanding makes conflicts rarer and better -explained, not absent. Two changes can each be valid alone and interact — a new call to `foo` plus a -narrowing of `foo`'s contract. Structural combination and **validity** are separate, and the second -genuinely refuses: a structurally clean rename plus a stale reference is a broken program. The -honest claim is *far fewer conflicts, each a real question in the user's own vocabulary.* - -**The hard part is not removed, only the accidental part.** Two proposals may admit no common -program, or several materially distinct ones — the same compatibility problem that combining -independently authored work has always addressed. Gone is everything around it: mutable files, patch transport, branch topology, global -history, working-tree alignment, rebasing, and cloning. - -**Identity is currently weaker than the model needs.** *Verified:* `v2.std.node` `Hash` is -`Fnv1a64Structural` — 64-bit, non-cryptographic. A 64-bit non-cryptographic digest is a **locator, -not a durable intersubjective identity**, and adding a host builtin is closed because DESIGN freezes -the v1 seed's growth surfaces. **Declared rung:** the first slice uses the available digest and -states this limitation rather than implying cross-party agreement it cannot support. - -**Corrected 2026-09-21 — the dissolve-on condition has been half met, and the half that remains is a -different one.** This paragraph said "no SHA-256 *computation* exists in `.dag` -(`std.content_hash` `sha256_hex_digest` and `extdeps.crypto.hash` `sha256_digest` validate hex; they -do not hash bytes)". That was true when written. `extdeps.crypto.sha2` now computes SHA-256 in the -substrate (`sha256`, `sha256_hex`, FIPS 180-4), witnessed by `test.claim.sha256_fips_witness` — and -the witness discriminates rather than restating the type, because `sha2` routes every 32-bit -operation through `std.bitwise`, whose `word32_add` wraps by comparison against `word32_modulus` so no value transits above 2^32-1, and the -interpreter's unbounded-`Int` evaluation yields the same residues and a wrong wrap changes the -digest. - -What has **not** happened is the migration: `extdeps.crypto.sha2`'s only importers are -`extdeps.crypto.nist_p256` and its own witness. `std.content_hash` does not import it, so -`v2.std.node` `Hash` and `std.fabric_storage`'s object refs still mint through -`content_hash_of_value`. **Revised dissolve-on:** `v2.std.node` `Hash` minted from a cryptographic -family. `std.fabric_storage`'s object refs are being moved onto the pure kernel in gunbc#11996 — -the shell-out alternative was refused there for a structural reason worth recording, since it is the -one this note would otherwise invite: `fabric_object_preimage` produces bytes **in memory**, so a -shell transport over a file path means a temp-file write per object, which manufactures the custody -gap (hash one file, store another) that verification exists to close. `v2.std.node` is the half that -remains, and it is gated on native emission of the `sha2` closure — blocked by -`gunbc.recurring_failure_mode` `bounded_natural_arithmetic_evaluated_as_unbounded_int`, whose -prerequisite is `MachineWidth` reified as a value. Both halves are owned by the lane holding -`#11819`; open question 2 below is narrowed accordingly rather than closed. - -## 6. Confidentiality - -Secrets are the forcing function for redaction, and redaction is where a content-addressed history -either has an answer or admits it does not. - -**Git's answer is a non-answer**, and that is the design input. A secret in git history requires -rewriting every subsequent commit; every hash changes; every clone, fork and open PR diverges. On a -public repository this is *ineffective*, not merely expensive — the objects are already distributed. -Practitioners actually "rotate the credential and assume the bytes are permanently public." We must -not build a better version of that theater. - -**Five independent facts git fuses into one.** Prevention, current-state retraction, -audience-history retraction, credential invalidation, and bounded erasure are separate, and the -distinction between the two retractions is load-bearing: - -- **Prevention** — bytes never reach the public store. *The only operation that preserves - confidentiality.* -- **Current-state retraction** — the head no longer contains the material. **Not enough**: a clone - replaying from the original root still recovers it. -- **Audience-history retraction** — new readers begin from a sanitized anchor and never receive the - material. This is what people mean by redaction, and a different fact from the above. -- **Invalidation** — rotate the credential so leaked bytes are worthless. **Already modeled**: - `gunbc.auth.secret_rotation`, with exactly-once walls, receipt-backed retirement, and no stored - payload digest. -- **Erasure** — bytes unrecoverable. Claimable only inside a closed sanitization scope covering - every key, wrap, backup, cache, derivative, index and recovery path. Never "delete one key." - -**Derived rule:** on a committed secret, SCM performs retraction, the rotation kernel performs -invalidation, and erasure is claimable only in the private realm. **The SCM must never report -retraction as erasure.** Once disclosure escapes a controlled scope, `UncontrolledCopiesMayRemain` is -a **terminal standing** and **no global-erasure constructor exists** — the impossible claim is made -unwritable rather than merely discouraged. - -Two consequences easy to get wrong in opposite directions. Invalidation is **not** established by -the secret manager disabling a version: the service may still accept the leaked token, so -invalidation needs a subject-specific negative authentication probe. And a rewrite lacking a -complete sanitization receipt is `PrivateHistoryReanchored`, **not** erased. - -**Where the wall sits.** Integration-time or CI-time checking is structurally incapable of confidentiality -— the deleted Stage-0 placement gate established exactly this: a required check could refuse `main`, -but pushed objects had already reached public storage. The chronology must be private capture → -private integration and admission → derive an audience-authorized projection → serialize → write public -storage. The public writer accepts only a projection minted by the authoritative private context, -never an arbitrary `Node`, blob or patch. **Corollary:** a public PR branch cannot safely carry -secret bytes on the theory that squash-landing removes them. The surface is wider than file content: -a secret in a commit message, path, diagnostic, workflow log, or projection metadata is the same -disclosure. - -**Retraction without rewriting.** Where a commit is *accepted parent plus sparse transformation*, -retraction can be an ordinary **appended transformation** — no existing identity changes, no -downstream clone is invalidated. **This holds only if public history is an audience projection -rather than a replayable copy of private transitions**; otherwise replay from an early checkpoint -reconstructs the secret on the way to the head, achieving current-state retraction only. The -resolution is an **audience-specific projection epoch with a sanitized anchor**: re-anchor the -*public projection*, never the authoritative history. - -Three constraints on that epoch, each of which an earlier draft got wrong by vagueness. The -sanitized epoch must have **no public predecessor relation** to the contaminated one — a tombstone -naming the old root, path, or incident reason is itself the leak, so the mapping is retained -privately or not at all. The old epoch must be **retired from authoritative service**: a clean head -with the old refs still advertised is not retraction. And **hiding a ref is not retraction** if -direct object retrieval still serves the bytes. *(Proposed — the least settled part of this note.)* - -**No confidentiality receipt carries secret bytes or a secret-derived digest**, following the -rotation kernel's existing rule. Receipts retain opaque incident identity, subject references, -non-secret-derived transition identities, verification verdicts, audience standings, and bounded -sanitization evidence. - -**Diagnostics are an information-flow surface.** If an old-root lookup answers "retracted" where a -nonexistent root answers "unknown", that difference is an existence oracle; where existence is -confidential the unauthorized responses must be indistinguishable. Over-redaction is the paired -failure — an authorized responder must still receive located detail — so both directions need -controls. - -**At the user boundary:** "keep the revoked secret" is never offered as a `ChoiceRequired`. It is a -hard `CouldNotLand`. - -**`.env` does not belong in the graph.** Secret *values* live in a secret store; the graph holds -`SecretRef` nodes, schema, and required binding identities, and `.env` becomes a local -materialization, not source-control authority. A marker inside the file cannot self-authorize: -removing `secret=true` must be an authorized policy change, not an ordinary content edit. - -**Node-grain withholding survives only at a real interface/realization boundary**, consistent with -the existing refusal of per-statement holes. Public signature with encrypted body: allowed. Public -body with one expression silently missing: refused. - -**Erasure is not strictly harder than git's** — git is already a Merkle DAG, and finer grain can -help: a secret subtree can carry its own key instead of requiring a whole file's destruction. But -*accounting* is harder, and two hazards are specific to this design: never publish the private -canonical root hash as the public root, and **never deduplicate secret plaintext across audience -realms — cross-audience content-address equality is an oracle.** - -## 7. `NATIVE-COMMIT-0` — the first executable slice - -**Honest feasibility ruling first.** An executable slice exists without the parked authoring-capture -surface, but it **cannot honestly integrate arbitrary concurrent edits to existing `.dag` files**. -Without capture, nothing can tell from two file endpoints which proposal, deletion, rename or frame -the author intended; claiming otherwise reconstructs patch inference under new vocabulary. So the -first slice integrates **explicitly authored proposals**, and says so. - -Three operations. No base, ancestor, parent, branch pointer, working copy, or patch. **This is the -shipped signature, corrected 2026-08-21 against the operation then at `dag/gunbc/scm/merge.dag` as -merged in #8719, now `gunbc.scm.role_requirement_integration`** — the -draft below it specified a two-proposal call and a `ChoiceRequired` arm, neither of which exists, -and a stale authority describing an operation nobody can invoke is the premise contamination DESIGN -documents against its own CI paragraph: - -``` -integrate_role_requirements(store, target, target_dependencies, proposals) - -> RoleRequirementsIntegrated { store, commit, roles } - | RoleRequirementsContested { role, alternatives } - | RoleRequirementIntegrationRefused { cause } -accept(store, commit, authority, contract) -> appended; never changes the commit -checkout(store, commit) -> CheckedOut { program } | CheckoutRefused { cause } -``` - -**Why the signature grew a target and lost a proposal count** (operator direction, 2026-08-20). The -two-proposal form had nothing to preserve *from*, so all it could do was union bindings — neither -the literal closed-scope reading nor a dependency model — and it ships a program referencing a -deleted node when one proposal adds `k` depending on `f` while the other deletes `f`. The target -supplies every fact outside the implication frontier, and **silence means preserve it**. Proposals -became a list because a contest is a property of the whole population, not a pair. - -**Why the outcome arms are named for less than they conclude.** `RoleRequirementIntegrationRefused` rather than -`CouldNotLand`: the engine cannot establish terminality — an unsupported dependency kind may be -unmodelled implementation, a missing target root an incomplete fetch. `RoleRequirementsContested` rather -than `ChoiceRequired`: this layer establishes that distinct alternatives exist; that a *user* must -choose requires proving they survive the admitted equivalence quotient, that the candidate -population is closed, and that no further machine work resolves them — none of which lives here. -**`RoleRequirementsContested` is complete over the proposal population supplied to that call and proves -nothing about global candidate-space closure.** That boundary is held by this paragraph and the type -name, not structurally; the landing seam is where it becomes structural. - -A proposal is a set of authored requirements — `RequireBinding { role, value }` or -`RequireBindingAbsent { role }` — meaning *the resulting program must contain this exact value at -this named binding*, or *must not bind this role at all*. Absence is authored, not implied, because -silence already means preserve: a delete by omission would read as "leave it alone", the inversion -of the request. Both sides normalize to a requested state (`DesiredRoleValue`) before any contest is -decided, so identical authorings are **agreement**, not a question with identical alternatives. -Distinct bindings combine by construction; two different requested states at one binding produce -`RoleRequirementsContested` carrying **every** distinct alternative — accumulated over the whole -population so arrival order cannot decide which evidence survives, and never latest-wins, -first-wins, or branch-priority-wins. - -`checkout` follows exact object links from the commit root and consults **nothing else** — not -names, branches, proposals, acceptances, or the live store beyond the identities it was given. - -**Storage:** objects keyed by identity, plus append-only acceptances. Canonical encoding excludes -occurrence provenance, sorts named edges, retains positional order, and tags constructors distinctly. -In-memory first; persistence, packing, networking and collection are separate realizations -deliberately kept out of the semantic slice. - -**Witnesses**, each with a deliberately-wrong control so that a passing suite means something: - -| claim | witness | wrong control that must fail | -|---|---|---| -| a later object cannot change an agreed program | commit, accept, add a second object at the same name, re-checkout | a checkout that queries live names | -| a commit rebuilds from its own closure | discard proposals, branches, acceptances and indexes; checkout from the root alone | — | -| a missing object refuses | remove one reachable object, re-checkout | any fallback substitution | -| proposals combine without node identity | remint every occurrence id, re-integrate | an integration requiring matching occurrence ids | -| conflicting bindings ask rather than guess | same name, different objects | any winner rule | -| arrival order is not authority | integrate (a,b) and (b,a) | — | -| integration does not imply agreement | integrate without accept | — | - -**Deliberate non-goals:** no proposal inference from file edits, no capture, no diffing, no -deletion/rename/move, no repository-wide candidate search, no general constraint solving, no -positional-sequence integration, no git import or export, no `GIT-PLUMBING-0` dependency, no refs, no -compare-and-swap, no cross-process persistence, no deploy, no confidentiality, and **no merging of -this repository's live concurrent edits.** - -**Known scope limit, stated because it affects what the slice demonstrates.** Proposals are keyed by -top-level binding, so two proposals touching one binding differently ask a question even when they -changed unrelated sub-nodes. **The headline win — two people editing different functions in one -module combining cleanly — is the *next* slice**, which recurses §4's named-edge rule. This slice -proves the foundation: content-addressed objects, stability under growth, exact reconstruction, and -combination without identity. - -**Dogfood disposition:** a small controlled subject first. Making this repository's concurrent edits -the first consumer would fuse a model proof with unrelated frontend and performance work. - -**Direction after `NATIVE-COMMIT-0`, operator ruling 2026-08-21: a CLI vertical before the depth -recursion**, reversing the "headline win is the next slice" ordering above. The reason is DESIGN -§5's specification-without-execution trap, not a changed view of which slice is more valuable. -Nothing has ever *consumed* this kernel: the claims declared in `test.claim.scm_role_requirement_integration_witness` are -assertions **about** it, authored alongside it, and a witness suite is not a consumer. A CLI is the -first artifact that uses the store, checkout, identity and role-requirement integration together under conditions nobody -authored to make them pass. - -The ordering is safe because **depth changes how `integrate_role_requirements` combines, not how -`add` authors**. A user -adds a module either way; whether integration recurses into it is orthogonal, so the command surface built -now survives the recursion landing later. - -The known cost, designed for rather than discovered: a CLI makes the top-level-binding limitation -*user-visible*, since the first thing anyone tries is editing two functions in one module — which -today asks a question instead of combining. That refusal must say exactly that, in those terms, -rather than reading as a defect. - -## 8. Scenario corpus (retained) - -No role-requirement integration kernel should be built without these. Each is a RED unless marked -otherwise. - -**Structural integration — with measured coverage as of 2026-08-21 (#8719 merged).** The corpus -opens "no integration kernel should be built without these", and a kernel was built, so this states -which rows it -actually answers. **4 of 13 were covered when that join was run on 2026-08-21**; see the -measurement note below the table for why that figure has not been re-derived since. The `witness` -column names the claim, which now resolves in -`test.claim.scm_role_requirement_integration_witness`; the `blocked on` column says what would close each gap, because -"uncovered" collapses three situations — a gap the current grain could close today, a gap needing -the sub-node recursion, and a gap needing vocabulary the model does not yet have. - -| scenario | required result | covered | witness / blocked on | -|---|---|---|---| -| target changes an untouched named node | preserved, no conflict | **yes** | `an_independent_sibling_is_preserved_exactly`, `an_empty_proposal_set_preserves_the_target` — preserved at *identity* grain, so it is object reuse rather than a recomputed equal | -| proposal edits one named child, target edits another | both preserved | no | **sub-node recursion.** This is the headline win; the kernel is top-level-binding keyed and cannot express it | -| proposal deletes a subtree, target edits a descendant | conflict | no | **sub-node recursion.** `dependent_add_and_delete_refuses` is the role-grain analogue — delete `f` while `k` requires it — and is *not* this scenario | -| concurrent same-name add, same kind, different children | conflict | **yes** | `two_distinct_requests_for_one_role_are_contested` — different children give a different content hash, hence a distinct `DesiredRoleValue` | -| one side changes node kind, other edits a child | conflict | no | **sub-node recursion** | -| both sides produce an identical kind-changed subtree | clean | **yes**, degenerately | `the_same_request_authored_twice_is_not_a_contest` — identical results agree at top-level grain; the sub-node path is untested | -| unchanged-body rename | unique relocation candidate | no | **no rename vocabulary.** Neither `Requirement` nor the outcome can express relocation | -| rename versus edit under the old name | conflict; the edit is not lost | no | **no rename vocabulary** | -| two sides move one subtree to different parents | conflict, never duplicated | no | **no move vocabulary** | -| duplicate named siblings in any input | input refusal | no | **closable at the current grain.** Measured 2026-08-21: nothing in `gunbc.scm.*` or `v2.std.node` refuses a node carrying two children with one name. The nearest live behaviour is content-hash canonicalization *sorting* named edges, which orders duplicates rather than refusing them. This is the one structural gap that needs no new grain and no new vocabulary | -| two different positional appends | conflict or explicit order choice | no | **positional-sequence integration**, an explicit §7 non-goal for this slice | -| distinct positional ordinals edited without shape change | both preserved | no | **positional-sequence integration** | -| only one side changed a node | **preserved — the §6 asymmetry regression control** | **yes** | `an_independent_sibling_is_preserved_exactly` | - -**What the partition says about the next slice.** Four gaps are one job — the sub-node recursion of -§4's named-edge rule. Three are a second job needing rename/move vocabulary the model lacks. Two are -the positional-sequence-integration non-goal. **One — duplicate named siblings — is closable now**: the only -structural scenario waiting on neither depth nor new vocabulary, hence the cheapest real coverage -available. - -**Coverage claims about this table must be measured, not recalled.** The `4 of 13` above was -measured on 2026-08-21 by joining, one row at a time, the claims that the witness module declared -ON THAT DATE — when it was `test.claim.scm_merge_witness` and held a different population. An -earlier estimate from reading was "roughly five" — close enough to feel safe, wrong enough to -mis-scope the next slice. - -**That number is a historical result and is NOT a current-completeness claim.** Naming today's -module does not re-run an August join, so this paragraph deliberately does not cite the renamed -module as the measurement's source: a citation that silently re-points at a changed population -would make a dated receipt look re-derived when nothing re-derived it. The 13-row table below is -the current authority on WHICH scenarios exist; what is missing is an executable instrument that -re-joins today's claims against those rows, and until one exists no row here may be described as -covered on the strength of the August figure. - -**Admission and capture** - -| scenario | required result | -|---|---| -| structurally clean rename plus stale reference | semantic admission refuses | -| occurrence ids collide numerically across allocator scopes | result reminted or remapped | -| comment or annotation absent from `Node` capture | opaque residue or capture refusal, never silent loss | - -**Confidentiality.** Every refusal needs a nearby positive control, so that "always refuse" cannot -satisfy the suite. - -| scenario | required result | -|---|---| -| raw payload reaches the public writer before policy evaluation | writer invocation count stays zero; `PublicationPrevented` | -| publication authority unavailable | refuse before write; never classify as clean | -| secret inside opaque/unparsed file residue | capture-fidelity refusal before serialization | -| secret in commit message, path, diagnostic, log, or projection metadata | refuse or redact; no public object carries it | -| bytes already public but lifecycle reports `PublicationPrevented` | impossible state | -| append a retraction, then clone by replaying from the original root | secret recovered — only `CurrentStateRetracted`, never audience retraction | -| sanitized epoch names the contaminated predecessor | commitment/existence leak | -| old ref hidden but direct object retrieval still serves the bytes | retraction incomplete | -| clean head while the old epoch is still advertised | retraction incomplete | -| any receipt stores a payload digest | type uninhabitable | -| one backup, cache, wrap, derivative or recovery path unobserved | `ErasureUnestablished` | -| rewrite succeeds without a sanitization receipt | `PrivateHistoryReanchored`, not erased | -| private realm erased after public exposure | private erasure **plus** `UncontrolledCopiesMayRemain` | -| secret-manager version disabled but the service still accepts the token | not invalidated | -| unauthorized diagnostic names retracted path, secret kind, or incident time | audience-projection refusal | -| authorized responder denied located detail | over-redaction control | -| retracted-vs-nonexistent lookups distinguishable to an unauthorized reader | existence oracle | -| public graph retains a dangling reference to retracted material | public projection refuses | -| proposal from a revoked epoch persisted publicly before inspection | ingress wall | -| proposal independent of revoked material blanket-refused | recovery-expectation failure | -| proposal relying on revoked material lands via ordinary stale-parent retry | admission failure | -| "keep the revoked secret" offered as a choice | user-boundary failure — must be `CouldNotLand` | -| retraction reported to a user as erasure | never | - -## 9. What was rejected, and why - -Kept because the reasoning is the most reusable part of this note. - -**Three-way merge over parsed trees.** The first draft modelled merge as `capture(base, proposal)` -producing a patch keyed by declaration name. It works and beats line-based merge, but it is -diff-and-patch with a semantic key — reconstructing intent from endpoints because the information -was destroyed before it was seen. It also inherits a base, and through the base a timeline. - -**Cross-revision node identity.** Pursued for most of a day as the way to make renames carry. Closed -by `occurrence_identity_scope_law` and, more importantly, unnecessary: identity exists to transport -patches. - -**`std.change` `keyed_three_way_fold` as the integration kernel.** *Verified:* its leaf verdict returns -`KeyedConflict` when only one side changed a value — correct for reconciliation, where `desired` is -authority, and wrong for integration, where the sides are peers. The traversal is shared with the -fleet-reconcile spine; the verdict is not. - -**The seven inherited assumptions.** Text as storage, commit-as-whole-tree-snapshot, one global -timeline, branch-as-mutable-pointer, merge-as-invoked-event, the working copy, and the repository as -a unit. Three are simply wrong here. Four contain a real requirement in a git costume: a state -commitment is load-bearing though the snapshot is not; arbitration is real at a unique effectful -target though compare-and-swap is not fundamental to content; a sparse working *context* is real -though a materialized tree is not; and authority domain, confidentiality realm, retention scope and -trust policy are real boundaries that must not be re-fused into a "repository" type. - -**The existing P1 kernel.** `gunbc.source_integration_proof_kernel` is built around an -`accepted_parent` and a before/after delta — the mutation-and-parent assumptions this model rejects. -*Proposed:* freeze it as quarry rather than extend it. This touches an active roadmap node and is an -operator decision, not this note's. - -## 10. Open questions - -1. **Authoring capture.** The largest fork, and the reason §7 is scoped as it is. Until an authoring - surface records what an author *did*, proposals must be stated explicitly rather than inferred. -2. **Durable identity** (§5) — the declared rung. **Narrowed 2026-09-21:** a computing - cryptographic digest now exists (`extdeps.crypto.sha2`); what is missing is a consumed path from - it to `v2.std.node` `Hash`, which is gated on native emission of that closure. - `std.fabric_storage` is being migrated in gunbc#11996; `v2.std.node` is the remaining half. - See §5. -3. **Recursion into named edges** — the next slice, and the one that demonstrates the differentiator. -4. **Retraction epochs** (§6) — the weakest claim in the note. -5. **Positional append** — whether two appends commute. Special-casing risks re-importing the - line-merge heuristic through the back door. -6. **Alpha-renaming neutrality** — may hold only for non-exported, non-shadowing names, since an - exported name is an external interface. - -## Dissolution trigger (DESIGN §6) - -This note dissolves into the carriers it names when an integration kernel and a confidentiality -lifecycle -land. Until then it is evidence about a model, never authority over one. The §8 corpus outlives it: -those rows re-enroll against whatever kernel lands, per DESIGN §4b's rule that discriminating -evidence survives the machinery that prompted it. diff --git a/docs/plans/declaration-body-resolution-provider.md b/docs/plans/declaration-body-resolution-provider.md deleted file mode 100644 index 53f1038a641..00000000000 --- a/docs/plans/declaration-body-resolution-provider.md +++ /dev/null @@ -1,201 +0,0 @@ -# Declaration-body types: one resolution per ingest, produced on demand - -Work item adhoc-603dcdcb-f4b (royal-stag-371). Plan only; no code lands under this document until -quiet-gull-780 and neat-boar-16 clear it. Rulings it builds to: quiet-gull-780 (demand-driven, one -resolution, not eager) and neat-boar-16 (conditions a, b and c, stated in §5). - -## 1. The defect, re-derived - -The brief assumed field and payload type positions reach resolve as unlowered shells. On main that -is no longer true. `v2.compiler.body_lowering_fold` already lowers record fields -(`body_lower_field_decl_edge_optional`), positional payloads (`body_lower_positional_payload`) and -alias right-hand sides (`body_lower_alias_rhs_optional`) through `body_lower_type_expr_lowered_optional`, -and #12629 adds the where-head. So `gunbc.recurring_failure_mode` -`declaration_body_type_shell_preserved_unresolved` overstates its frontier. - -The earliest unjustified boundary is one link later. `v2.compiler.infer` `infer_projection_receiver` -(on the field-projection lane, #12506) reads the receiver's declaration from -`ResolvedTree.symbol_index`. That index holds declarations AS AUTHORED: it is built by -`v2.compiler.name_resolve` `resolution_context` from normalized roots, before any resolve. So -`artifact.tree` is typed as the bare atom `ParseTree`, while a parameter annotated `ParseTree` -carries the resolved reference. The match-arm mismatch that calm-pike-507 measured on #12641 -follows directly. - -#12629's `ResolvedTree.resolved_declarations` cannot repair it. `resolved_declarations_of(root)` -folds the SUBJECT module's declarations only, and the real seven reads `v2.compiler.parse` -`ParseArtifact.tree` from another module. On the native lane that declaring module often has no -resolve result at all. The seed main (`v1.compiler.emit_rust`, the native driver loop) resolves, -infers and evaluates one universe member at a time, in universe order rather than import order, and -the universe is test-bearing modules only. - -Why a type position needs its DECLARING module: the reference resolves in that module's namespace -(its import bindings and origins). `v2.compiler.name_resolve` `namespace_for_subject_root` builds -that namespace per subject, lazily, every time the subject is resolved. It is carried nowhere. - -## 2. The construction - -Two produced values, both scoped to ONE INGEST and keyed by declaration identity, both on the -existing per-ingest carrier `v2.compiler.name_resolve` `ResolutionContext` (#11401 R1: one context -per native ingest, built in `v2.compiler.compile`). - -**P1. The module namespace provider.** -- Identity: the module's `QualifiedName`, the key `ValidatedModuleRoots.by_name` already uses. -- Value: the admitted `Namespace` that `namespace_for_subject_root` computes today. -- Scope and retention: one ingest, meaning every module the native ingest read into the closure - (not only universe members). Released with the context. -- Production: on first demand, by either consumer (the module's own resolve, or a declaration-body - resolution in P2). Both read the same stored value. -- Refusal: a module outside the ingested closure refuses at the provider with a typed, located - diagnostic. There is no per-subject rebuild and no fallback. -- `namespace_for_subject_root` stops being a builder that callers invoke and becomes the provider's - producer. It has exactly one call site, the provider's miss arm. - -**P2. The resolved declaration-body provider.** -- Identity: `std.decl_ref` `DeclarationRef`, the identity `v2.compiler.resolve` - `resolved_reference_node` already writes into resolved trees. -- Subjects: every declaration whose TYPE POSITIONS a later stage reads -- type declarations - (record fields, variant payloads, alias right-hand sides, where-heads) AND function signatures - (parameter and return types), because infer reads a callee's Arrow across modules too. -- Value: the declaration's body with every type position resolved in its declaring module's P1 - namespace, by the same `resolve_node_walk` (no second resolver). -- Scope and retention: one ingest, beside P1. -- Production: on first demand. A module's own resolve DEMANDS its own type declarations and grafts - the provided bodies instead of walking them, so no type position is ever resolved twice. After - the walk, the module's resolve demands the transitive closure of declarations its resolved tree - references in type positions, following resolved bodies (`ParseArtifact` then `ParseTree`, and so - on). -- Refusal: a reference whose declaring module is outside the ingest refuses at P1, and so does a - declaration P1's namespace does not hold. - -**P2 mechanism: how "one resolution" and "the module grafts" are realized.** -- Subject grain: one top-level declaration, either a type declaration or a function SIGNATURE (the - Arrow's parameter and return types; never a function body, which is not a declaration type). - Identity: `DeclarationRef` (declaring module, declaration name). -- Production: ONE WALK OF THE DECLARING MODULE IN A DECLARATION-TYPES-ONLY SELECTION. The walker is - the existing `resolve_node_walk` over the module's validated normalized root and its P1 namespace, - and `ResolveContext` gains a selection: `WholeModule` or `DeclarationTypesOnly`. Under the second, - value bodies are carried unwalked: an Arrow's named body edges and a `data` declaration's value. - Every type position is walked exactly as the module's own walk would walk it, so each declaration - gets the scope, position and `under_module_root` the full walk would give it BY CONSTRUCTION. The - spine descent is not replicated by hand. Production grain is the module, because one selective walk - covers all its declarations. Storage and snapshot grain is the declaration: entries are cut from - that walk's resolved root, keyed by `DeclarationRef`. A refusal in a declaration type is stored as - that declaration's outcome, and the grafting module surfaces it, located. -- Graft: before walking, a module's own resolve demands every type declaration and signature it - declares from P2. The walk then carries a READ-ONLY map from occurrence id to provided node in - `ResolveContext`, and at such a node it returns the stored node instead of descending. The walk - itself stays pure; only the pre-walk demand threads the context. -- Snapshot: after the walk, the module's resolve demands the transitive closure of declarations its - resolved tree references in type positions, and `ResolvedTree` carries that set as a projection of - the provider's values (C10). - -**Every loop over modules threads the context in PR2.** In PR1 a module's namespace is demanded only -by its own resolve, so two loops drop the returned context at no cost: `v2.compiler.compile` -`native_demand_execute`, the demand-engine drain that replaced the seed main's lane loop in #12401, -and `gunbc.namespace_xl2_rehearsal_census` `xl2_observe_module`. Once PR2 adds cross-module -declaration demands, a dropped context re-produces what another module already produced. So PR2 -carries the context on `NativeDemandRun` and through that census fold. - -**Sequencing (quiet-gull-780 decision A, 2026-09-30).** #12629, #12407, #12506 and smart-newt-725's PR -land first. PR2 then cuts over on main in one commit: every reader switch plus the deletion. P2 is -built meanwhile on a branch off PR1, not opened for merge, and never merges without its production -reader switch in the same change. Before cutting, the reader census is re-taken on main at identity -grain, because each of the four adds readers of per-module `resolved_declarations`. If one stalls -more than a day, quiet-gull-780 decides whether its reader is pulled into PR2. - -**Purity: how "at most once" is realized in a pure substrate.** A provider cannot mutate. The -context is threaded as a value: every resolve returns `(ResolvedTree, ResolutionContext)`, and the -driver loop carries the returned context into the next module. The native driver loop is the seed -main rendered by `v1.compiler.emit_rust`; that template changes so the module loop binds the -context it gets back. That is a v1 change admitted under `gunbc.v1_maintenance_standing` purpose -admission, because it serves the v2 native route. Inserting under a key that is already present -refuses (`resolve_provider_entry_built_twice`), so a double build is a typed error, never a silent -overwrite. - -**Infer reads a snapshot, not the provider.** `ResolvedTree` carries the P2 bodies demanded for this -module (the closure computed above). Infer stays pure and single-argument. A lookup outside that -snapshot refuses as `infer_declaration_outside_resolved_closure`; it never widens to `symbol_index`. - -## 3. The PRs (stacked) - -**PR1: namespace provider (P1), replacing the lazy per-subject build.** CLEARED (quiet-gull-780, 2026-09-30) to land alone as one motion: the provider replaces the lazy build, the lazy build is deleted in the same PR, and no reader can reach both. Condition (a) then governs PR2. The v1 `emit_rust` main-loop change is admitted under v1 purpose admission because it serves the v2 self-host. A root cut in one motion: after -it, "what is module X's namespace" has one producer and every caller reads it. Context threading -lands here, through `native_lane_module_resolution`, `native_test_resolve_module` and its -`_walk`, the seed main template and the stage0 mirror, `v2.compiler.compile`'s single-subject -route, and the test harnesses. Condition (a): whether PR1 may land alone, as a self-contained -replacement with no two structures, or must fold into PR2, is neat-boar-16's call. Folding it in is -mechanical. - -**PR2: declaration-body provider (P2) and the reader cut, together.** P2 lands; resolve grafts P2 -bodies; four infer readers move to the snapshot in the same commit: `infer_projection_receiver` -(field projection), `refinement_declaration` (#12407, per wise-bat-862), and -`infer_declaration_reference_facts`'s callee-Arrow read (#12506, found by calm-pike-507: it reads -`symbol_index_lookup` and gets unresolved atoms for the seven's cross-module -`parse_module_prepared(...)` call; its Arrow feeds `infer_application_callee_arrow_with_facts` and -`infer_arrow_declared_return_type`), and `infer_match_coproduct_of_type` with -`infer_binding_type_in_scope` (#12641, calm-pike-507: variant payload and field types for match -binders; type parameters are still substituted from use-site arguments, over the resolved body). -A fifth consumer moves in the same cut (quiet-gull-780, 2026-09-30): smart-newt-725's -record-construct field typing. It reads #12629's per-module `ResolvedTree.resolved_declarations` for -same-module records, so it must move in the commit that deletes that field; its cross-module -population is what P2 adds. smart-newt-725 names this PR as its trigger. A sixth consumer (quiet-gull-780, 2026-09-30): quick-hawk-799's gunbc#12809, a v2 `String` -literal lowered through the unfold. Its judge sees `declared = Ref(q.String)`, an imported -declaration, so it needs `q.String`'s resolved body, which #12629's root-only index lacks. It -stays draft until this PR lands. What dissolves in this -commit is #12629's `ResolvedTree.resolved_declarations` with `resolved_declarations_of`; no reader -is left on it. -All of these took declaration types off an authored or subject-local node; after PR2 none of them can -reach it. Branch: `session/royal-stag-371-pr2-declaration-bodies`. calm-pike-507 stacks the return-type derivation on this branch. `ResolvedTree.resolved_declarations` and `resolved_declarations_of` (#12629) are deleted -in the same commit. The RFM row is retired, and its red stays enrolled. - -**PR3 (stacked on PR2): call-return derivation** (scope added by quiet-gull-780 when calm-pike-507's -node closed with #12641 merged into #12506's branch). `infer_arrow_declared_return_type` derives a -declared corpus return type beyond kernel and `Bool`, from the resolved callee Arrow PR2 hands -`infer_declaration_reference_facts`. As a result the seven's `parse_module_prepared(...)` is typed as -`Outcome`. Proof: calm-pike-507's seven's-frontier claim in -`v2.test.claim.match_binder.match_binder_typing` -(`infer_match_scrutinee_type_underived` on that call shape) flips to a typed binder (C12). Concretely, -`mbt_the_sevens_call_scrutinee_is_a_counted_frontier_holds` goes red and is replaced by a positive -claim that the binder `artifact` in `mbt_seven_src` is `ParseArtifact` (`mbp_fact_of` with -`mbp_find_atom(^artifact)`, the shape of `mbt_binder_is_typed_parse_artifact_from_accepted_value_holds`). -The coproduct readers (`infer_match_coproduct_of_type`, `infer_binding_type_in_scope`, -`infer_match_field_type_instantiated`) currently leave non-parameter field types that are scoped to -their declaration underived on purpose. In PR2 they read the resolved types from the provider -instead. - -## 4. Controls - -| # | Control | Kind | -|---|---|---| -| C1 | calm-pike-507's one-module projection-vs-param arm (`v2.test.claim.match_binder.match_binder_typing` `mbt_match_is_typed_parse_tree_holds`) | red before PR2, green after | -| C2 | cross-module `ParseArtifact.tree`, where the declaring module is not a universe member | red before PR2, green after | -| C3 | a field whose type names an undeclared type refuses at resolve, located | red | -| C4 | a declaration demanded from a module outside the ingest refuses at P1, typed | red | -| C5 | AT MOST ONCE: two subjects that both demand module X's namespace build it once. The double-insert refusal goes red if it is built twice. | red if violated | -| C6 | IDENTITY: the `DeclarationRef` for a type in the P2 body equals the one the module's own resolved root holds at the same use | positive | -| C7 | wise-bat-862's `body_cast_node` rows 15, 15b, 15c, 15c2 and 15d stay green, and 15d, the miss, still refuses | regression | -| C8 | #12629's where-head controls (`declaration_graft_where_alias_*`), re-pointed at the P2 read | regression | -| C10 | SNAPSHOT IDENTITY: an entry in infer's per-module snapshot and the P2 provider entry it projects are the identical `DeclarationRef` and body; the snapshot is a derived projection, never re-resolved | positive | -| C11 | a cross-module callee's Arrow read by `infer_declaration_reference_facts` carries resolved parameter and return types (calm-pike-507's measurement on the stacked branch) | red before PR2, green after | -| C12 | the seven's `parse_module_prepared(...)` scrutinee types as `Outcome`, so calm-pike-507's `infer_match_scrutinee_type_underived` frontier claim flips to a typed binder | red before PR3, green after | -| C9 | the retired RFM row's discriminating red stays enrolled as a regression control (condition c) | regression | - -## 5. Conditions this plan builds to (neat-boar-16, via quiet-gull-780) - -- (a) There is no moment where two structures answer the same question. The reader switch, the - deletion of per-module `resolved_declarations`, and the deletion of the lazy per-subject namespace - build all land in the transition that makes them redundant. -- (b) The provider has no fallback. Out-of-closure refuses at the provider. "In the ingest" means - every module the native ingest read into the closure. The provider's identity and per-ingest scope - are named the way #11401's `ResolutionContext` is. At-most-once is C5. -- (c) The RFM row retires in PR2, and its red stays enrolled (C9). - -## 6. Cost - -Baseline, taken before PR1 on `origin/main` 077af249458: `gunbc test //v2/test/claim/body_lowering/...` -on the native route, reading the per-module `[native-prepare-split]` `resolve_nanos` lines and the -`[native-cost-partition]` rows. The same pattern is re-run on each PR head. Cite the run by tag; do not -transcribe its numbers. Expected shape, not a claim: P1 moves namespace work from once per resolve to -once per module per ingest. For a declaring module that is not a universe member it is new work, -bounded by demand. Demand-scoping follows bold-bat-516's cost lane (no eager whole-closure -admission). The baseline measures the change; it does not define it. diff --git a/docs/plans/dedicated-coding-harness-plan.md b/docs/plans/dedicated-coding-harness-plan.md deleted file mode 100644 index e1dd062ed57..00000000000 --- a/docs/plans/dedicated-coding-harness-plan.md +++ /dev/null @@ -1,795 +0,0 @@ -# Dedicated Coding Harness — scope - -Project ID: DCH - -Repository anchor: gunb-ai/gunbc - -Baseline: main@de2f5f86346, read 2026-09-01. - -Operator direction (2026-09-01): serve a large open model on the DGX Sparks and drive a minimal -coding harness against it, retiring the Claude, Codex and Cursor provider runtimes. Written from -the ground up in `.dag` and Rust. **DCH and the dedicated harness must not import, vendor, depend -on, or cite `ctrl` as authority.** Stated at that grain deliberately: the wider claim would be false -of the repository today, because `extdeps.ctrl.gunbc_pin` already declares an `ExternalAuthority` -pointing into `gunb-ai/ctrl` and compares the host pin against the ctrl pin. That row is out of this -lane's scope and is not to be touched here. Spark parity is to be handled by fleet convergence -rather than by hand. **Operator direction 2026-09-02 withdrew the RLM-first start barrier**: DCH-0 -through DCH-2 proceed CONCURRENTLY with RLM, and the two lanes meet at DCH-3 rather than queueing. -DCH-3 is a join, not a start barrier. - -## 0. What is measured, and what is assumed - -Every claim below is either a read of this tree at the stated baseline or a live probe taken -2026-09-01. Where a fact came from another session it is attributed, and where it is unverified it -says so. Three earlier readings in this lane's own correspondence were stale or truncated, so the -provenance is part of the scope rather than beside it. - -## 1. Terminal - -Success is the RLM terminal procedure, unchanged, executed with our harness selected as the -provider realization: - -> From a clean revision R on main, clicking Launch on the roadmap canary creates exactly one -> attempt, one worktree, **one harness process of ours — the default and only harness**, and one -> durable attempt record; the -> worker performs only the canary's exact change; verification evaluates the pinned oracle against -> the exact attempt head; publication records a receipt; the child unblocks on acceptance. - -This lane authors **no new acceptance procedure**. RLM already owns a 14-step terminal that is -provider-agnostic in every step but one, and reusing it is what keeps this from being graded on its -own homework. `docs/plans/roadmap-launch-mvp-plan.md` is that procedure. - -No substitute terminal counts. A harness that completes a turn against a Spark in isolation is a -component probe, not this terminal. - -**And this terminal is necessary without being sufficient** — accepted without objection by the -controlling reviewer on 2026-09-02, at this grain: - -> The RLM terminal is the sole DCH integration terminal and remains necessary; it is **not a -> coverage closure** for DCH's operational failure classes. Its bounded canary proves that the -> accepted components compose on one exact task. It does not prove behavior under long context, -> long tools, unreadable observation, process failure, control-plane deployment, serving -> maintenance, cache reuse, or transport truncation. Each such class is discharged only by its own -> named instrument, exact subject, positive control, and discriminating refused case. **A green RLM -> terminal carries no evidence for a class whose distinguishing subject it did not execute.** - -This strengthens the rule already in §4 — treat one completed turn as evidence for nothing but that -— rather than weakening the terminal. - -## 2. Current-state ruling - -### Already usable - -- **The seam exists and is a single-variant coproduct.** `gunbc.provider_interface_binding` - declares `ProviderControlInterface = CodexAppServer {}`. Adding a harness is a variant addition - at a modeled seam. That module's own annotation already separates the two senses of "provider" — - the runtime we install and control, versus the inference backend it sends requests to — and names - `gunbc.spark.serving_desired` `spark_serving_local_endpoint_url` as the seam where the two would - silently fuse. -- **The serving runtime speaks the shape the harness needs, natively.** Probed against - 192.168.1.225 on 2026-09-01: Ollama 0.32.9 answers `POST /v1/messages` with an Anthropic-shaped - response — content blocks including `thinking` and `tool_use`, `stop_reason: "tool_use"`, and a - `usage` object — for a request carrying a tool with a JSON input schema. `/v1/chat/completions` - and `/api/chat` also route. So no translating proxy is required for gunbc to drive it. -- **Some of the wire shape is already modeled.** `extdeps.llm.anthropic` (378 lines) carries tool - result blocks, image sources and model specs; `extdeps.llm.anthropic_rest` names the `/v1/messages` - path and body. -- **The convergence spine reaches spark serving.** `gunbc.fleet.fleet_converge_plan` imports - `gunbc.spark.serving_membership`, `serving_realization` and `serving_execution_schedule`, and - declares `fleet_converge_spark_serving_row_executions` / `_row_effects`. -- **Both Sparks are reachable and serving.** `/api/version` answers 0.32.9 on 192.168.1.225 and - 192.168.1.226; `/v1/models` returns the same seven ids on each. - -### Prevents it today - -- **The Sparks are not enrolled in the fleet.** `fleet_intent_network.endpoints` lists srv1–srv4 - only, and `srv5`/`srv6` appear in neither it nor `gunbc.fleet_intent`'s `ComputeHost` list. In - that module **membership IS enrollment**, so this is not an omission to be patched around: it is - the reason parity is managed by hand. The module's own note records that the standing reason has - already changed from "there is nothing to enroll" to "enrolling is a decision nobody has taken - yet." -- **"The models resident" is not representable.** `spark_serving_desired_manifest` governs exactly - one member; the other six resident models are outside that authority and nothing in gunbc owns - them. The structural tell, from eager-pike-541: the materializer carries a single manifest, digest - and blob closure, so no carrier could hold seven. Nobody can state, check, or drift-detect the - difference between the model we converge and the models the hosts hold. -- **Desired and observed disagree on both hosts.** The deployed user unit still describes - `gpt-oss:20b`, predating the #9859 amendment to 120b, and carried no context-length line at all. - Two independently declared desired values, un-rendered on both hosts. **This survives #9960** - (see §2.1): that PR completed the DESIRED side, and a complete desired value is not a converged - host. -- **There is no generic inference interface.** `extdeps/llm/llm.dag` is 11 lines and - `llm_contracts.dag` is 14 — anchors only. Every wire fact lives in a per-vendor module, and - tool-calling is modeled only inside `openai.dag` and `cursor_stream.dag`. What exists in - `anthropic.dag` fuses **shape** with **vendor**, which DESIGN's external-upstream decomposition - separates: Ollama implementing the Anthropic wire shape is precisely the shared-standard case, so - the shape belongs in its own module with the two implementations citing it. -- **Whether the spine can APPLY a serving row is unproven.** The `converge` CLI verb is unwired - deliberately — rebuilding it would make the interpreter load-bearing for a new capability while - two lanes delete it — and its refusal names the live successor route. But this lane's own wet plan - run came back `PartiallyApplied` with apply refused. That is what RLM-2b is currently measuring. - -### 2.1 Changes since the baseline — the concurrency axis is RESOLVED ON MAIN - -The baseline above is fixed at `main@de2f5f86346` and is not rewritten as main moves; completed work -is dispositioned here instead. Recorded 2026-09-01, after the controlling reviewer identified that -this plan's active claims had been falsified by a merge that landed while it was in review. - -**#9960 (`7810e68b3ea`, ancestor of current main) resolves the concurrency-axis item outright**, and -verified here rather than relayed: - -- `extdeps.ollama.server_env` declares `OllamaNumParallel` → `OLLAMA_NUM_PARALLEL`, and - `spark.serving_unit_render` now emits **four** environment axes, binding - `ollama_num_parallel_env_assignment` to the spec's slot count. The axis is no longer absent. -- `spark_serving_desired_serving_slots_value` carries a desired count of 4, so the axis has a - desired value and is drift-reportable — the specific consequence this plan said was missing. -- The false cost model is corrected at the desired value, in the direction this plan predicted: - the count does **not** divide the window, each slot receives its own full window, and the - per-slot price is recorded as a property of the **realization** rather than a fleet-wide constant - — the per-realization objection this plan raised is answered by a declared §4b drop naming that - exact subject. - -**One defect found while verifying it, and it is not #9960's to fix by this lane.** -`gunbc.spark.serving_desired` still carries an *earlier* annotation block stating in the present -tense that concurrency "is not modeled anywhere in desired state", that the two facts trade "because -the slot count divides the context window", and that the concurrency row "is P1b and is deliberately -not smuggled in here". All three are now false, and they contradict that same module's own value and -annotation roughly 140 lines below. That is a §3 meaning fork inside one authority — a stale -annotation is data the substrate cannot check, so nothing reds. **Reported, not repaired here**, and -out of this lane's scope. - -**What this does NOT resolve.** #9960 made the state representable, declared and renderable. It did -not read either host back. The observed-versus-desired receipt below stays open, and so do the two -outstanding operator hand-edits it must account for. - -### Explicitly off the critical path - -`ctrl`'s harness and router are **prior art, not a dependency and not a citation**. Two of their -findings are worth re-deriving deliberately rather than inheriting, because gunbc must establish -them on its own evidence: that a cold large model can exceed a default HTTP client's header/body -timeout, and that an assistant turn must be echoed back verbatim — thinking blocks included — with -all tool results for one turn batched into a single user message. Treat both as **hypotheses with a -predicted failure**, not as facts on loan. - -Two more joined that list on 2026-09-01 from the transcript §5 reads, on the same terms: that a -streaming request may terminate a response early where an otherwise identical non-streaming request -returns it complete, and that an assistant turn may stop at `end_turn` having announced work it did -not perform. - -**The second is superseded on 2026-09-02, and the correction is worth more than the hypothesis.** -A mechanical cause was found: a serving package carrying `PARAMETER stop ###` terminates generation -on ordinary Markdown H3 output while returning a well-formed stream and `stop_reason: end_turn`. -That is a *package configuration* colliding with content, reproduced by a tiny prompt and removed by -rebuilding the package — not a model deciding to stop, which is what the single surviving -observation appeared to show. The heading-then-stop symptom therefore had a determinate external -cause the whole time, and the hypothesis about model behaviour was an artifact of reading a -transport symptom as a semantic one. It leaves the hypothesis list and becomes DCH-0p's subject. -The streaming hypothesis is untouched and still has **no** observation: its isolating step was never -run. - -## 3. Serial gate chain - -Only one gate mutates at a time. A later gate may author inert types and fixtures but may not -activate production rows before its predecessor's receipt is accepted. - -**A gate does not complete while a §5.2 question that owns one of its dispositions is open.** This -is a precondition of the gate, not a note beside it: §5 exists to answer these classes *in advance*, -and a plan that lets a gate finish while its own identified fail-open is unadjudicated has recorded -the class instead of handling it. The bindings, and each is repeated in the gate itself: - -**All five returned on 2026-09-02** (§5.2). The rule stands for any question opened later; what the -returned rulings changed in the chain is recorded here: - -| Question | Effect on the chain | -|---|---| -| Q3 | **A new gate, DCH-0r, is inserted before DCH-0c.** A §4b drop was REJECTED as the normal shape — reordering removes the need for debt. | -| Q1 | DCH-2's failure-arm partition is specified, not left open. | -| Q2 | DCH-2 owes a typed-obligation adjudication; length-based dispositions are forbidden. | -| Q5 | DCH-2 owes journal-before-effect ordering and a two-axis model. | -| Q4 | Governs how §2's hypothesis list is read; each hypothesis owes its own instrument before DCH-3. | - -The resulting order is: - -``` -DCH-0 endpoint rows -DCH-0r quiescent-maintenance construction -DCH-0c ComputeHost enrolment / apply reachability -DCH-2 turn activation -``` - -DCH-0c may precede DCH-0r **only** if the destructive restart path stays structurally unreachable, -or refuses because the maintenance fence is absent. - -### DCH-0 — Enrol the Sparks, and reconcile the rendered unit against the live hosts - -**Question owned:** what does the fleet know about these two machines, who says so, and does either -host actually carry what desired state now declares? - -The parallel axis and the cost model **left this gate on 2026-09-01**; #9960 performed both, and -§2.1 records the verification. What remains is enrolment and the live reconciliation. - -- Enrol srv5/srv6 in `fleet_intent_network.endpoints` and `fleet_intent`'s `ComputeHost` list — the - act the address correction deliberately did not perform. (The `ComputeHost` half is DCH-0c; see - below.) -- Reconcile the rendered unit on both hosts against desired state, which is now complete on four - axes where it was complete on three. -- Q3 **returned**: the drain is not deferred and not declared as debt. It becomes its own gate, - **DCH-0r**, sequenced immediately after this one. This gate's own receipt is unchanged. - -**Receipt:** an observed-vs-desired comparison of the rendered unit on both hosts, not a return -code. The rendered unit is the only member of the must-move-together set that produces **no failure -signal** when stale — ref, manifest, digest and closure each raise a checksum event, while a stale -unit is silent, which is how both hosts drifted on two axes unreported. Two hand-edits are -outstanding against that receipt: the context length raised today, and `OLLAMA_NUM_PARALLEL=4` set -on both hosts, each under operator instruction. **#9960 does not discharge this and it sharpens -it**: a hand-set value on a host and a declared value in the corpus agreeing by coincidence is -exactly what a silent carrier cannot distinguish from convergence, so the readback is the whole -receipt. - -**Enrolment is inert today, and that is the risk rather than the reassurance.** -eager-pike-541 looked for the executor rather than arguing from principle: no -`ctrl-fleet-converge` timer or service exists on either Spark in either scope, and -`gunbc.fleet_converge_timer` says of itself that it "remains the naming and cadence authority; it is -no longer a renderer" — its installer chain died with `ci.yml` in #8283. So enrolment cannot cause -an apply, because nothing on those hosts is scheduled to run one. The failure mode to guard is -therefore not prematurity but **a row that reads as an outcome**. - -**The consumer census, which was the open item on that reasoning, is done and it sharpens the gate.** -Of 57 non-test modules importing `fleet_intent_network`, the number that read the `endpoints` list -or `fleet_intent_network_topology()` is **zero** — production modules consume individual endpoint -rows *by name* (`bmc_virtual_media`, `host_identity_access` take `srv1_host_lan_endpoint` and -friends). The topology function has exactly one consumer in the tree: -`test.claim.fleet.fleet_intent_network_witness_test`, asserting -`list_length(items: fleet_intent_network.endpoints) == 11`. - -Two consequences. First, **list membership is not the load-bearing act** — authoring the srv5/srv6 -endpoint rows that named consumers can reach is. Second, enrolment will turn that witness red at -11 → 13, and the right response is not to bump the literal: a count copied from the current tree is -the change-detector DESIGN §5 names, and completeness here is an identity join, not a count -equality. Enrolment is the occasion to fix the oracle, and that repair belongs in this gate. - -**ENROLMENT IS TWO ACTS, NOT ONE, AND ONLY ONE OF THEM IS INERT.** The census above covers -`fleet_intent_network.endpoints`. `fleet_intent`'s `fleet_intent_known_hosts` is its opposite — -censused by eager-pike-541 and verified here — with three production consumers: - -- `gunbc.generated_artifact` maps the list to `RunnerHostSudoersArtifact`, so enrolment **mints two - new generated artifacts**; the rows cannot land without a regeneration in the same commit or the - drift gate reds. Mechanical consequence, mechanical receipt. -- `gunbc.runner.runner_host_deploy` `admit_runner_host` returns `RunnerHostUnenrolled` for any host - absent from the list. Its annotation calls itself **the enrollment wall** and is explicit that it - is construction rather than a check: no function in the module takes a bare `RunnerHostDeploy` and - yields a command, so "run the installer on an unenrolled host" is unrepresentable rather than - discouraged. It prices the srv4 host-convergence OOM — converge against an unenrolled host derives - nothing for it, or stamps drop-ins carrying another host's widths — and names the enrolled row as - exactly where `runner_deployment_plan`'s conservation wall gets the host RAM to check slot caps - against. **So enrolling srv5/srv6 removes a fail-closed refusal that currently protects them.** -- `gunbc.fleet_converge_apply` finds hosts by identity in that list, so enrolment is precisely what - makes a Spark reachable by the apply path this lane measured as `PartiallyApplied` / refused. - -The inertness argument had two independent legs — no executor, and no consumer. `ComputeHost` -knocks out the second. **Only the executor leg survives**, and it is the leg that changes the moment -anyone installs the timer. - -**So DCH-0 lands the endpoints half only.** Author `srv5_host_lan_endpoint` / `srv6_host_lan_endpoint` -as rows the named-import consumers can reach, add them to the list as the authority's completeness -statement, repair the count oracle. The `ComputeHost` half is **DCH-0c**, and it is a decision -rather than a row: it needs the host facts the conservation wall consumes (RAM at minimum), the two -sudoers artifacts regenerated in the same commit, and someone stating out loud that the enrollment -wall is coming down for these two machines on purpose. - -Note the row/list split holds on both sides and inverts between them: `srv1_host` and `srv2_host` are -imported by name in at least `os_install_actuator_selection`, `deployed_intent_v1`, -`nbd_proxy_virtual_media_install`, `ci_deploy_access` and `srv3_install_media_fetch`, so authoring -`srv5_host`/`srv6_host` rows is separable from list membership here too — except that here the LIST -is the load-bearing half and the row is the quiet one. - -**Owner: eager-pike-541**, agreed 2026-09-01. - -### DCH-0r — Quiescent serving maintenance - -**Question owned:** can a converge restart the serving process without destroying work in flight, -and can it prove it? - -Created by the Q3 ruling. A §4b drop was **rejected** as the normal shape: a drop reports a lower -guarantee, it does not turn an unsafe arm green, and DESIGN is explicit that a dissolution trigger -does not authorize creating the debt. Reordering the chain removes the need for debt entirely. - -**The mechanical blocker, verified in this tree rather than relayed.** `spark.serving_realization` -realizes `EnableSystemUnit` as three commands in one effect — `systemctl enable`, `systemctl start` -and `systemctl restart` — so **restart has no independently matchable effect identity** and no fence -can structurally guard it. Two refinements found while verifying, both sharper than the finding as -received: - -- The restart is **unconditional**: it is emitted every time the effect is realized, not only when - the unit definition changed. So converging this unit is destructive to in-flight work even when it - changes nothing. -- **The user-unit path is the opposite shape**, and the Sparks are on it. Its closure is - `[… EnableUserUnit, StartUserUnit …]` — enable and start already separated — and it carries **no - restart effect at all**. So on the very hosts this lane targets, a changed unit definition has no - modeled route to take effect. That is a candidate cause for the observed-versus-desired drift - DCH-0 must reconcile, and it is stated as a candidate because nothing here has read a host back. - -So the first required change is that restart becomes its own typed effect, distinct from -enablement and from initial start, on both paths. - -**The admitted restart sequence.** A bare `active == 0` reading is insufficient, because a turn may -enter immediately after the read: **the admission fence and the drain are one protocol.** - -**THREE IDENTITIES, NOT ONE.** The 2026-09-02 ruling corrected an earlier conflation in this -protocol, and the correction is the load-bearing part of the gate: - -| Identity | Stability | Answers | -|---|---|---| -| **Service locus** | stable across restarts | which service is fenced — host plus unit slot | -| **Service invocation identity** | changes on every activation | is this still the exact process generation observed before? | -| **Running-definition identity** | stable for equal restart-relevant specs | which definition produced this invocation? | - -The `/proc//environ` stamp answers the **third**. It is invocation-BOUND evidence but it is -not an invocation IDENTITY: two successive restarts from one definition carry the same stamp, so it -cannot prove the old process was replaced. DCH-0r still needs the second, read from observation -rather than desired state, and it must change whenever the process is replaced, stay stable within -one observation transaction, never repeat merely because a PID was reused, and bind to the process -whose environment and health were read. `MainPID` alone satisfies none of the last two. - -**The fence is keyed by the LOCUS, not by the incarnation.** An earlier wording said draining refuses -leases "for that incarnation"; that is too narrow, and it fails exactly when it matters — if the -backend restarts unexpectedly mid-drain, an incarnation-scoped fence stops blocking admissions at the -moment an unvalidated new incarnation appears. The pre-restart invocation is a compare-and-swap -condition and an evidence anchor, never the scope of the refusal. Each running lease separately binds -the invocation it uses, which is what lets an unexpected replacement produce a typed interruption -against the right turns. - -1. Observe one transactional pre-state: locus, invocation `J0`, running definition `D0`, desired `D*`. -2. Atomically move `Open` to `Draining`, keyed by locus and maintenance, with `J0` as the expected - pre-invocation. -3. Refuse **every** new turn admission at the locus, whatever invocation is present. -4. Drain the complete active lease population at the locus to zero; each lease identifies its - invocation. -5. While the same fence is held, re-observe zero leases **and** that the invocation is still `J0`. - If it changed, the drain did not succeed: settle the affected turns as interrupted, reconcile the - pre-state, and restart the protocol. -6. Execute the independently typed restart. -7. Read one transactionally joined post-state and require ALL of: `J1 != J0`; `D1 == D*`; the - registered definition equals the render of `D*`; readiness and health belong to `J1`; and an - empty replan. -8. Reopen admission bound to the pair `(J1, D1)`, and have every later admission recheck that the - observed pair still equals the admitted one — so an out-of-band restart refuses immediately - rather than silently granting a turn against an unvalidated backend. - -`std.temporal_effect` already carries the general direction — held leases, refusal on foreign -ownership, `DrainThenStart` as a distinct plan — but the service-level population and admission -fence that make the drain exact do not exist yet. - -**Guarantee grain, stated rather than assumed.** A census of DCH turn leases proves quiescence for -DCH-managed clients only. To claim the *service* is quiescent, one of these must hold: every -admitted client routes through the same lease authority; DCH holds exclusive access for the window; -or the service exposes a complete active-request observation including external clients. Otherwise -external occupancy is **outside the modeled guarantee** — and then an automated destructive restart -must refuse, or the guarantee is stated narrowly as DCH-managed-turn continuity. It may not be -stated broadly and held narrowly. - -**THE UNSTAMPED READING, NARROWED.** A complete successful environment read that finds no stamp is -positive evidence and is not "unobservable" — but its honest conclusion is that **this invocation -cannot be established as having been started from the current desired definition**, whose render -stamps unconditionally. It is NOT that the process temporally predates that definition: a foreign or -manually started process could have been launched later and would carry no stamp either. It still -requires reactivation; chronology is simply not what was established. The arm holds only while four -conditions do: the environment read completed, absence is distinguished from permission, parse, -truncation and observation failure, the desired unit specification stamps unconditionally, and the -stamp identity covers every process-start input whose change requires reactivation. - -**THE SUBORDER.** DCH-0r-a deletes the fused unconditional restart, separates enable from start, and -defines the reactivation vocabulary with its refusing selector. DCH-0r-b observes the -running-definition identity AND the service-invocation identity, and proves the process/environment -read is transactionally joined. DCH-0r-c builds the locus admission fence, the exact lease drain, the -guarded production reactivation, the two-identity readback and the validated reopening. Joining the -reactivation arm into the production planned-effect sum belongs with DCH-0r-c's guarded producer, not -with DCH-0r-a — no production consumer can use the arm before then. - -**DCH-0r-c's CONSTRUCTION HALF HAS LANDED, AND WHAT IT DOES NOT ESTABLISH IS THE LOAD-BEARING HALF OF -THIS SENTENCE.** `gunbc.spark.serving_admission_fence` carries the locus, the three identities kept -apart, the locus-keyed admission fence, the compare-and-swap onto `Draining`, the exact drain -adjudicated as one reading with its revalidation, the occupancy grain, `BoundReactivationPlan`, the -guarded producer and the two-identity readback with its validated reopening; the fence and the -drained evidence are `sole_constructor`, so a plan cannot be assembled from a count taken outside a -held fence. The production consumer is `gunbc.spark.serving_converge_slice_wet` -`spark_serving_reactivation_standing_for`, which computes the remedy verdict from the host's own -observation transaction and REFUSES on every host, because no admission-state authority and no -active-turn population exist. The executable sum was NOT widened: the exclusion in -`SparkServingExecutablePlannedEffect` and its witness still stand, and the plan carries its steps as -a derivation rather than the `{ plan, steps }` field pair sketched above — storing them beside the -plan would rebuild the authored-label-beside-unrelated-commands defect that exclusion holds shut. - -**What remains, and it is the receipt below rather than wiring:** a lease authority that admits every -turn at a serving locus and can enumerate the population held there, a persisted admission state the -fence can swap, and SSH to srv5/srv6 under an operator credential decision. Until those exist the -Planned arm has never been reached from a production call site, and neither DCH-0r-a's nor DCH-0r-b's -standing is retired. - -**Receipt:** a restart executed against a held fence, with the drain observed to zero under that -same fence, and a post-restart incarnation readback. An exit status of zero from the invoking -program establishes only that the program reported completion — service convergence still requires -independent observation and an empty replan. - -### DCH-0b — Make residency representable - -**Question owned:** what is a resident-model fact, and who owns the six nobody converged? - -Split from DCH-0 at eager-pike-541's request, and the reason is the right one: enrolment and the -parallel axis both write a value into a carrier that already exists and has a shape, whereas the -resident set has **no carrier at all**. Someone must first decide whether a resident-model fact is -desired state or observation, whether it is per host or per fleet, what its identity is when the -same weights appear under two refs, and what a drift between the converged member and the resident -set *means* when nothing put six of those models there through gunbc. That is design, and folding -it into a gate of row-writes would make the row-writes wait on it. - -The structural tell that no carrier exists: the materializer carries a single manifest, digest and -blob closure, so nothing in it could hold seven. - -**AND THE SUBJECT IS A BEHAVIOUR-BEARING PACKAGE, NOT RESIDENT WEIGHTS** (ruling, 2026-09-02). The -discriminating pair is: same weight blob, same template, same runtime, same host, **different -effective stop set** — materially different observable behaviour. So a weights digest, a GGUF -manifest or a model tag is not the model identity this lane needs. The carrier is a -`ServingModelPackageIdentity` over the target model artifact, quantization, prompt-template identity, -parser/renderer identity, effective parameter identity, **effective stop-sequence set**, runtime -release and decoding realization. A tag is an allocated handle to that package and must never be -treated as the semantic identity — a package rebuilt from the same weights without a stop string is -a **different serving package**, and treating it as identical makes the repair invisible to -convergence. The gate's question becomes *which exact behaviour-bearing package is resident and -active on each router-eligible host*, not which weights occupy disk. - -### DCH-0p — Effective package qualification - -**Question owned:** what is this endpoint actually serving, and can a stop string in its package -silently truncate our output? - -Created by the 2026-09-02 ruling. It runs after DCH-0b makes package identity representable and -after DCH-0r makes a required restart safe, and it must complete before DCH-2's production -qualification. - -**The defect it exists to prevent, which is not hypothetical.** A serving package carrying -`PARAMETER stop ###` terminates generation on ordinary Markdown H3 output, while the serving surface -returns a well-formed stream and `stop_reason: end_turn`. A tiny prompt reproduces it; removing the -package stop restores the answer. The client can neither identify the cause from the response nor -remove a server-side stop through an additive request option. **This is a mechanical cause for the -heading-then-stop symptom §5 records as a hypothesis** — see the reclassification there. - -**Read the effective configuration, never the intended one.** Before admitting a backend, read what -the running endpoint actually serves (Ollama exposes template, serialized parameters and model -information on `/api/show`). A source Modelfile, a tag name, or the command used to build the package -are all statements of intent, not observations. The receipt binds host, service invocation, model -handle, target artifact, runtime release, template, the complete effective parameter set, the -canonicalized stop-sequence population, parser and decode realizations, and the observation -transaction. Where a behaviour-bearing field is not fully exposed, the answer is -`PackageConfigurationIncomplete` — never an inferred value. - -**Stop-sequence admission is the wall.** Every effective stop carries a typed role: either a -protocol-boundary sentinel binding uniquely to a declared boundary in that exact package's -template/parser contract, or an unbound content stop. A copied allowlist of strings is not a role. -`###` has no protocol-boundary role and is ordinary output content, so a package carrying it refuses -**before** DCH starts a turn. Sentinel-looking values inherited from a prior lane are admitted only -once DCH independently establishes their relationship to the exact conversation protocol — "the -other lane called them legitimate" is not our evidence. - -**Controls this gate does not exit without:** stop sets differing only by `###` produce different -package identities; a package carrying unbound `###` refuses; a fixed package completes a request -requiring H3 output with the H3 bytes present; the original package discriminates by terminating -before those bytes; an unreadable, incomplete, or handle-inconsistent `/api/show` refuses; one tag -resolving to different package identities on two router-eligible hosts refuses; a router endpoint -missing the selected package becomes ineligible rather than silently retained; and a package -configuration changing under an active session changes the session generation, so old evidence -cannot carry. - -The runtime H3 pair is evidence that the endpoint realizes the modeled rule. It is not a substitute -for reading the effective configuration. - -### DCH-1 — Hoist the wire shape off the vendor - -**Question owned:** what is the interface, and which module owns it? - -- One module for the messages/tool-use shape, cited to its specification; `extdeps.llm.anthropic` - and the Ollama serving surface become two implementations that reference it. -- Tool-calling gets a shape here rather than a third copy inside a vendor module. -- This is a replacement migration at the root, not a new fork: the vendor module's shape rows move, - they are not duplicated. - -The authority is `extdeps.llm.anthropic_messages_api`, its own module rather than a row -in `extdeps.llm.llm` or `extdeps.llm.llm_contracts`: those two are agnostic anchors, and the -specification is a named, versioned upstream subject with a citation and a version axis of its own, -which is the `extdeps.whatwg.html_navigation` position rather than the generic-hub one. It keeps the -publisher's names because coining a neutral second name for a concept that already has one is the -nicknaming violation, and it names no implementation: `MessagesApiImplementation` is the carrier -each implementation declares its own row in. - -The shape rows MOVE, they are not copied. `extdeps.llm.anthropic` retains only what is true of the -API product — its model roster — plus its own conformance row; the wire contracts move with the -shape into the `_contracts` sidecar the emitter merges into the specification module, so no contract -row is left pointing at the vendor module. `extdeps.ollama.api` gains the `/v1/messages` endpoint -row and `ollama_messages_api_conformance`, and re-models none of the shape. This section records the -design the gate implements; git records what merged, and nothing here claims a merge state. - -The version binding is the axis worth having, and it lives on the carrier rather than in this -document. Upstream documents that Ollama ACCEPTS the `anthropic-version` header and does not use it, -so an accepted request is not evidence of the revision served — an accepted-and-ignored parameter is -worse than a refused one, because it looks like a control and is not. The two implementations -therefore declare different arms of `MessagesApiVersionBinding` against the same spec version. -`test.claim.messages_api_conformance_witness_test` carries the discriminating controls: it goes red -if the axis collapses, if either arm goes uninhabited, if a conformance row stops pointing at a -declaration in its own module, or if a wire contract is left behind on the vendor module. Ollama's -`/v1/messages` behaviour is grounded in upstream's own `docs/api/anthropic-compatibility.mdx`, not in -a probe transcribed into prose. - -### DCH-2 — The harness, in `.dag` and Rust - -**Question owned:** can we drive a turn to a terminal disposition and report it? - -- The loop: post a request, and while the response stops on tool use, execute the tools and post the - results back. Four tools is the starting surface: run a command, read, write, edit. -- **The tool surface is where §5.1 row 7 lands, so it is specified here rather than left to the - implementer.** Every quirk in the relayed transcript was a real semantic of this surface that - nothing announced, so the rule for all of them is one rule: **a tool's contract is carried in the - tool's declared shape, and any limit it enforces is reported in its result rather than inferred - from a truncated or missing one.** Concretely, six dispositions this gate owes: - - **Working directory.** Either a command's working directory is part of the call's declared - input, or the surface states that each call is independent — the failure to avoid is a caller - that happens to comply, which is what theirs did, by luck and unprompted. - - **Duration.** A limit that kills a build is a real refusal and must arrive as one: a typed - timeout disposition naming the limit, distinguishable from the command's own failure. A default - chosen for a chat turn is the wrong default for a compile. - - **Input.** No stdin means anything that prompts hangs until the limit kills it, and the operator - sees a timeout rather than a prompt. Either the surface supplies input, or a prompt-shaped hang - is typed as its own disposition. - - **Truncation.** Keep their shape: a cap that **announces** it, so a large result is - distinguishable from a complete one. This is already right and is adopted deliberately. - - **Edit matching.** Keep their shape: refuse on zero or multiple matches rather than patching the - first. Construction over validation, and already right. - - **Context exhaustion.** A full window must be a typed refusal, not a turn that begins to fail. - Compaction is a later capability; the disposition is owed now, because without it exhaustion - presents as the model getting worse. -- Preserve what the codex realization learned expensively, because these are provider-independent - and were paid for once: the three separated layers (durable goal, execution lease, controller), - the exactly-once command identity, and the trap that an idle thread never authorizes a start. -- A harness that **reports** its own status retires the observation layer that dominates every - existing provider module — guessing which transcript belongs to a session, inferring state from a - foreign schema. That is where the simplification is, and it should be measured, not asserted. - -**The exit bar, set by the Q1/Q2/Q5 rulings.** This gate does not exit on "it performs the four -tools and reaches one terminal turn". Four qualification groups, each owing a named instrument with -a positive control and a discriminating refused case: - -- **Supervision and observation.** A harness throw yields a durable failed-or-interrupted attempt - **while the controller stays observable**. A transcript-resolution failure stays distinct from - idle and from provider silence — the specific conflation that made a working session read as a - frozen one. -- **Execution provenance and lifecycle.** Tests execute the production transport rather than a - test-owned twin; a control-plane restart does not terminate the turn worker; a backend restart - produces a typed interruption. -- **Turn and transport semantics.** The stream/non-stream differential; `EndTurn` with outstanding - typed obligations; performance separated by cold, warm and cache-reused state. - - **The harness may never state that `end_turn` means the model naturally finished.** On this - transport at least two upstream causes produce that same observed label — natural completion, - and a configured server-side stop colliding with content. So the raw provider observation and - the semantic disposition stay different axes: a provider terminal observation carries the wire - stop reason and, honestly, that the native cause is *not exposed*; the DCH disposition is - decided by obligations, and external pinned verification — never the model's stop reason — - decides success. This is the second of two independent walls: DCH-0p prevents known - content-colliding package stops, and DCH-2 still refuses to equate a provider label with task - completion, because the client cannot prove the native cause from a surface that erased it. - - **Throughput is a construction, not a threshold.** "No four-digit rate is reachable" is a - threshold over a measured value — a view, which #9946's ruling forbids as a correctness wall, - and which faster hardware would falsify while a smaller fabricated value passed. The durable - shape is a cache lineage (backend host, service invocation, package identity, conversation - prefix identity, cache generation) joined to per-turn accounting that separates total input - tokens from **newly evaluated** and **reused** ones. A prefill rate may be constructed only over - newly evaluated tokens against the prefill duration; anything else divides mismatched subjects - and produces a valid arithmetic operation that is not a hardware measurement. -- **Tool-contract honesty.** The six dispositions above, at this grain: explicit working directory - on every invocation or a separately leased persistent shell; typed stdin or a typed refusal; a - per-operation budget rather than one universal kill; termination of the **owned process - population** rather than one PID; and compaction or an explicit typed context-exhaustion refusal. - -### DCH-3 — Bind it at the seam, make it the default, and run RLM's terminal - -**Operator direction, 2026-09-02: the harness is to be the only and default harness; Claude and -Codex are to be ignored.** That is adopted, and it changes this gate's shape in a way worth stating -rather than absorbing. - -**Default is a selection; retirement is a deletion. They are separated deliberately.** "Ignore the -other providers" is discharged by making ours the default selection at the seam and stopping -investment in the others — it does **not** require deleting them, and DCH-4's deletion still waits -on this gate's receipt. Deleting them to satisfy "only" before that receipt would leave the -repository with zero working harnesses, which is the failure mode the replacement-migration doctrine -calls erasing a correctness distinction rather than completing the replacement. The other variants -stay **frozen** in the doctrine's sense — no new investment, no new rows on their growth surfaces — -until DCH-4. - -**"Only" raises this gate's bar; it does not lower it.** With three providers, a weak DCH-3 is -tolerable because a fallback exists. With one, there is none — every DCH-2 qualification class -becomes load-bearing on the day it becomes default, and the relayed transcript in §5 is precisely a -record of what a sole harness with no fallback feels like when it breaks: three sessions dead, an -exit status of zero, and an operator who cannot tell working from dead. So the combined admission -below is not ceremony to be traded away for focus — it is the thing that makes "only" survivable. - -- The `ProviderControlInterface` variant, its receipt types, and the belt binding. -- Make it the **default selection** at that seam. -- Re-run the RLM terminal with the variant selected. -- **Admission requires BOTH**, and neither substitutes for the other: every DCH-2 qualification - receipt accepted, **and** the unchanged RLM 14-step terminal with the DCH variant selected. The - §2 hypotheses each owe their own DCH-run instrument before this gate, per Q4. - -### DCH-4 — Retire the three provider runtimes - -Delete-first at the root, per the replacement-migration doctrine, once DCH-3's receipt is accepted -— which now means the **combined** admission above, not the canary alone. Retiring a working runtime -on the strength of an easy-path green is how a replacement erases a correctness distinction instead -of completing. -The population, to be counted rather than estimated at the time: in `extdeps/llm` the anthropic, -openai, cursor and codex families; in `gunbc` the codex supervised-turn and runtime modules, the -cursor SDK modules, provider account and standing, and the Claude setup-token enrolment. The auth -surface goes with them — it exists to solve a problem that a self-hosted endpoint does not have. - -## 4. What this lane must not do - -- Import, vendor, or cite `ctrl`. -- Become the default production harness before DCH-2's qualification receipts and the RLM terminal - are BOTH ready for the DCH-3 join. Concurrent authoring and qualification are authorized; an early - cutover is not, and "only" raises that bar rather than licensing it. -- Land a change to an authority already inside another lane's frozen approved delta without - serializing it. Concurrency is authorized for authoring, testing and review; it is not authorized - for landing on a shared file, because a clean textual composition still yields a blob nobody - approved. `dag/gunbc/fleet/fleet_converge_plan.dag` is the live instance, shared with #9832. -- Enrol the Sparks as a side effect of harness work — DCH-0 is a separate gate with a separate owner. -- Bump `fleet_intent_network_witness_test`'s endpoint-count literal to absorb enrolment. Repair the - oracle or leave it red; a count copied from the tree it measures is not one. -- Treat "it completed one turn" as evidence for anything but that. -- Delete or break the existing provider runtimes to satisfy "ours is the only harness" before - DCH-3's receipt. Default first, retire after — otherwise the count of working harnesses passes - through zero. - -## 5. Adversarial review — the failure classes this lane must answer in advance - -**Provenance, stated once.** On 2026-09-01 the operator relayed a transcript of the `ctrl` -mini-agent lane debugging its own harness against these same Sparks, and directed that DCH be made -to answer it before it starts. Everything in this section is **second-hand and unreproduced here**. -It is admitted on exactly the terms §2 sets for the rest of that lane's output: each row is a -**hypothesis about our design with a predicted failure**, never a fact on loan, and none of it is -citable as evidence. What their session buys us is not findings — it is a cheap enumeration of -where a harness of this shape breaks, produced by someone who paid for it. - -The value is concentrated in one property: **every class below was operator-visible as something -other than itself.** A harness throw read as a clean exit. A dead session read as a working one. A -control-plane restart read as a Spark fault. An inflated rate read as fast hardware. A model that -stopped early read as a truncated message. That is one class, not five, and it is the class DESIGN -§5 exists for — so the obligation this section places on DCH is not "handle these bugs" but -**produce a disposition that cannot be mistaken for a different one.** - -### 5.1 Classes, and the gate that owns each - -| # | Class | Observed as | Owner | -|---|---|---|---| -| 1 | A harness fault reaches the operator as success | process exited 0 for an hour after a first-write throw | DCH-2, and Q5 below | -| 2 | One throw destroys unrelated work | any error in the loop took down the whole container | DCH-2, and Q1 below | -| 3 | Self-reported status that no observer can resolve | frozen at `idle` while the harness emitted `working` throughout | DCH-2 | -| 4 | A backend restart kills every in-flight turn | control plane and inference router shared a process | **DCH-0**, and Q3 below | -| 5 | A turn ends without doing the work and nothing types it | `end_turn`, one heading, after 50k input | DCH-2, and Q2 below | -| 6 | An instrument that fails toward a flattering number | 687 tok/s the hardware never reached, from KV reuse | DCH-2 | -| 7 | Tool-surface semantics that are real and unannounced | `cd` not persisting; a 120s SIGKILL; no stdin; no compaction | DCH-2 | -| 8 | A repair whose blast radius exceeds the bug's | a failed `chown` under `set -e` blocked every spawn on the node | this lane's own discipline | -| 9 | Evidence filtered by survivorship | one completed final turn existed; the rest died of other bugs | Q4 below | - -Row 3 is the one that most directly touches a claim this plan already makes. DCH-2 says a harness -that reports its own status retires the observation layer. Their harness **did** report its own -status, correctly, the entire time — and the operator still saw a frozen session, because the -report's *transport* failed independently of the report. So self-reporting is necessary and is not -the simplification on its own; the plan's existing instruction to **measure** that claim rather -than assert it is upheld, and this is what it will be measured against. - -Row 6 is the same defect this plan already corrects in DCH-0 for a different subject: a plausible -number that nobody could re-derive. DESIGN §6 governs both — name the instrument, never transcribe -its output. A harness of ours may not report a rate it cannot ground, and where a quantity is not -measurable the honest render is a refusal to render, not a zero. - -Row 7 was a single line in DCH-2 ("four tools: run a command, read, write, edit"), and that line is -where every operator-visible quirk in their lane lived. **It is now specified in DCH-2 as six owed -dispositions** — working directory, duration, input, truncation, edit matching, context exhaustion — -rather than deferred by a forward reference, since a promise to expand a brief elsewhere is the same -unreachable route this plan refuses in others. Two of their choices are adopted deliberately because -they are already the right shape: an output cap that **announces** its truncation, so a large result -is distinguishable from a complete one; and an edit that **refuses on zero or multiple matches** -rather than patching the first, which is construction over validation. - -Row 8 is not about the harness. It is a note to this lane's own reviewers: their repair for a -mini-agent-only failure aborted bootstrap for every session on the node. A fix that widens the -failure population is a regression regardless of the class it closes. - -### 5.2 The five dispositions, returned 2026-09-02 - -Sent 2026-09-01 and **all five returned**. Recorded as rulings rather than positions; where my -proposal was accepted with refinement, the refinement is what binds. - -**Q1 — the stop-the-line boundary. Accepted with refinement.** "Whose failure?" is evidence; it does -not decide. The controlling discriminator is **which continuation preconditions remain established, -and what is the smallest closed causal scope containing the uncertainty**. Stop-the-line means no -ordinary next effect is admissible — it does **not** mean the supervisor must die: a supervisor -alive in a typed stopped state substitutes no answer and admits no ordinary work, so it is not an -absorbing fallback, and destroying the mechanism that reports the failure was never §5's bar. -Consequences DCH-2 owes: - -- A tool timeout is first an observation that completion was **not seen by the deadline** — not - proof the command failed or changed nothing. It splits: timeout **plus** a settled owned process - population **plus** known effect state may be returned to the model as a typed result and the turn - continues; timeout with a surviving process or unknown effect state enters reconciliation, where - mutating tools and automatic retry are refused. So the honest surface is two dispositions, - `TimedOutQuiesced` and `TimedOutEffectStateUnknown`, plus termination of the owned process - population rather than one PID, and captured output through the last observed byte. -- An unexpected model stop reason is **turn-stopping**, not tool-level. A recognized-but-state- - invalid reason is a typed turn-protocol refusal; an undecodable wire value is a typed - unreadable-stop-reason observation carrying its raw evidence. None may throw through the - supervisor boundary, and none may collapse into one generic failure — terminal disposition and - native cause are separate axes, and an unreadable stream is neither "nothing happened" nor "the - provider failed". - -**Q2 — a degenerate turn. Accepted at typed-obligation grain, rejected at prose or character grain.** -No length, token count or heading shape may produce a disposition; those may drive an operator -attention view and nothing more. The adjudication is over **outstanding typed obligations**, whose -authority is the task and execution contract — the requested change, required validation, tool calls -awaiting results, mutations requiring readback — not the assistant's prose. The model may add -structured obligations; it may not erase task-owned ones or self-declare an empty set to obtain -completion. Three arms: `EndTurn` with obligations outstanding is **ended-incomplete**; with -obligations closed it is **awaiting verification**, which is deliberately *not* acceptance, because -the model's own `EndTurn` cannot grade the model's work; with obligation standing unobserved it is -**unadjudicated**. A heading-only reply becomes incomplete without any length rule, whenever the -task obligation is still unsatisfied. This also fixes the UI defect at its root: the surface renders -a typed state and never infers idle from a small text buffer. - -**Q3 — convergence restarting the serving process. (a) required, a constrained (b) required, (c) -rejected.** The drain is the construction and it becomes its own gate, **DCH-0r**; a §4b drop is not -the planned answer, because a drop reports a lower guarantee rather than making an unsafe arm green. -The prior-art incident is also **two** defects, answered differently: a control-plane restart killing -the worker is a lifecycle-separation problem (the harness worker must be supervised independently of -the dashboard), while a serving restart killing a request is a quiescence problem. The constrained -(b): the harness must survive backend death as a typed `BackendStreamInterrupted` carrying the old -incarnation, the observed termination and the retained stream prefix — and it must **not** resend. -A partially streamed response may already have executed tool calls and shown the operator prose; -exactly-once command identity covers commands that reached that boundary, not the replayability of a -model turn. Restart survival means **survive to report interruption**, never transparent -continuation. - -**Q4 — n=1 under survivorship.** It licenses exactly one existential proposition — that at least one -surviving execution showed the symptom — and not frequency, typicality, threshold, cause, or any -claim about long-context turns in general. Because it is prior art rather than DCH-owned evidence, -it does not establish even that existential proposition **for our harness**. Both hypotheses join -§2's list now and stay **separate**, because they are different subjects: streaming terminal -integrity is transport and decoder; incomplete-stop is turn semantics. Sharing a fixture does not -merge their verdicts. The streaming instrument is a paired differential holding model artifact, -host, service incarnation, request bytes, sampling parameters, context and cache condition fixed and -**changing only the stream mode**, judged on content-block sequence, tool-call sequence, usage and -terminal completeness rather than final rendered text. The incomplete-stop instrument constructs a -typed task with a known outstanding obligation and must not mention character count. And any -throughput receipt binds its cache condition as an **input identity**, not a footnote. - -**Q5 — the process exit status. Neither of my options exactly; closest to the first, corrected.** -An exit status carries **how that exact observed process terminated** — never whether the attempt -completed. An exhaustive integer partition does not fix the incident, because no mapping can say -whether the integer belongs to the watchdog, the wrapper, the container or the harness, whether the -harness ever started, whether a child outlived its wrapper, or whether the attempt had already -failed before something else exited zero. Two axes: a process-termination observation -(exited / signaled / unobserved, which this tree already models in `std.process_termination`, -including its refusal to fabricate a code for signal death) is **evidence carried into** an attempt -disposition, and never substitutes for it. The ordering, which is a real constraint on DCH-2: -**journal before effect** — attempt, task, base and worktree identity durably written before the -harness spawns or mutates anything; the process bound to that record; the semantic receipt written -before voluntary exit; and if the harness disappears without one, an independent supervisor records -interrupted, carrying the exact termination observation. A supervisor may move a journaled record -from running to interrupted and **may never move it to accepted**. So a watchdog exiting zero over a -thrown harness resolves to interrupted-with-receipt-missing, and no integer is read as "done". - -### 5.3 Standing rules this section adds to §4 - -- Do not report a measurement the harness cannot ground; render a refusal where a quantity is not - measurable, never a zero and never a back-computed figure. -- Do not treat a green canary as evidence for any class in §5.1. -- Do not land a repair whose failure population is larger than the one it closes. -- Do not produce a turn or attempt disposition from a length, token count or output shape. -- Do not resend a request whose response was already partially streamed. -- Do not read any process exit status as evidence that an attempt completed. diff --git a/docs/plans/dependency-acquisition-gap-analysis.md b/docs/plans/dependency-acquisition-gap-analysis.md deleted file mode 100644 index 67346ef029a..00000000000 --- a/docs/plans/dependency-acquisition-gap-analysis.md +++ /dev/null @@ -1,172 +0,0 @@ -# Dependency acquisition: why every new dependency is a modeling project - -**Status:** gap analysis, 2026-09-07. Reached from a concrete blocker — the approval loop needs an -ntfy server on srv1, and "install ntfy" turned out to be a slice rather than a data row. The -question this answers is the operator's: *the convergence is supposed to handle this; what is the -gap from here to there?* - -**Framing correction, operator, 2026-09-07:** fleet convergence "was never supposed to be host keys -— I think we started making a world convergence but got halfway through like all things." That is -the right frame and this document adopts it. What follows is not "convergence lacks a feature"; it -is an inventory of which half got built. - ---- - -## 1. The finding - -**There is no `Dependency` concept.** There is no type whose inhabitants are "a piece of software -this fleet depends on", and consequently no single place that answers *where does it come from, how -is it verified, and how do we know it is present*. - -What exists instead is `gunbc.host_effect` `HostEffect`, a closed coproduct of **34 arms**. It is -genuinely the world-convergence spine the operator describes — it has arms for host OS, BMC, -deploys, runners, compile pools, and OS install media. But its vocabulary is **one arm per -instance, not one arm per concept**, and at least eight of the 34 arms mean the same thing: - -`OsInstallActuatorToolchainEnsure` · `EnsureBuildCacheInstance` · `ProvisionCodexRuntime` · -`EnsureCompilePoolSlice` · `Srv3SeededInstallMediaToolchainEnsure` · `LiveDeployEnsureDependency` · -`EnsureActionsRunnerRelease` · `EnsureRunnerSlotDirectory` - -Every one of those is *ensure some software is present and configured on a host*. A world -convergence with one arm per thing in the world is an **enumeration wearing a convergence's name**, -and its cost per new dependency is constant — it never amortizes. That is DESIGN §2 exactly: the -same work, duplicated once per dependency, priced as if each were new. - -## 2. The census — six dependencies, five mechanisms, five homes - -| dependency | acquisition mechanism | authority home | actuated through | -|---|---|---|---| -| tailscale, tmux | apt package, `dpkg -s` guard then `apt-get install` | `live_deploy.spec` `EnsuredDependencyKind` | `LiveDeployEnsureDependency` | -| docker-ce | third-party apt repo + **pinned GPG fingerprint** | `extdeps.container.docker_ce` | `runner_host_docker_provision_script` | -| sccache | pinned GitHub release tarball + **per-arch sha256** | `extdeps.cache.sccache` | `EnsureBuildCacheInstance` | -| actions-runner | GitHub release artifact | `extdeps.github.actions_runner` | `EnsureActionsRunnerRelease` | -| codex | npm registry + lockfile integrity | `gunbc.package_delivery` (3,391 lines) | `ProvisionCodexRuntime` | -| **npm itself** | **a prose string** | `gunbc.host_cli_dependency` | **nothing** | - -The verification posture is good and was clearly reasoned each time — docker's keyring fingerprint -is pinned against a published fact, sccache's tarball is sha256-verified per architecture. **The -defect is not rigor; it is that the rigor was re-derived from scratch six times**, and lives in -five modules that share no vocabulary. - -### The last row is the class the operator named - -`gunbc.host_cli_dependency` carries the fallback for a dependency with no bespoke arm. - -The roster is `gunbc.host_cli_dependency` `codex_wet_materialization_host_cli_requirements`, walked -by `first_absent_host_cli_dependency` and observed through `observe_host_cli_dependency`. Read those -declarations rather than a copy of them here — §6, name the instrument rather than transcribing its -output, and §3, cite the symbol rather than a positional or duplicated copy. - -**Updated by #11061, because that change falsified how this section used to make its point.** It -transcribed the row as it then stood and observed that its `provision` field was a `NonEmptyStr` — -"an instruction to a human, typed as a string". That field is gone: the requirement wrapper carried -a tool NAME resolved back to a `CliTool` by a hand-rolled if-chain, and carrying the `CliTool` -itself made the unresolvable row unconstructible, which deleted the resolver, its unresolved arm, -and the `provision` prose that only fed that arm. The roster is now a plain `List`, and -the install sentence a refusal carries is derived from `CliTool.installable_via` through -`install_hint` rather than authored beside it. - -**That narrows the defect below without dissolving it.** The hint is no longer a free-text sentence -that can disagree with the tool it describes; it is derived from the tool's own declared install -sources. But `installable_via` is still a description of how a human would install the thing, not a -route anything executes — `SourceApt { package }` names a package, and no modeled effect consumes it -to converge a host. So the paragraph that follows was written about `provision` and remains true of -`installable_via`: what changed is that the sentence is now derived rather than authored, not that -anything acquires the dependency. - -It is worth being precise about what is and is not wrong here, because the obvious reading is the -wrong one. The module's *refusal* behaviour is correct and deliberately so: an absent tool stops -with a typed `HostDependencyAbsent` carrying tool and hint, and the module's own annotation says -"absent npm stops as HostDependencyAbsent, **never auto-install inside the witness**". That is -§5 fail-closed, and auto-installing inside a witness would be the absorbing fallback. - -**The defect is that the refusal has no route to a fix.** It hands a person a sentence — derived -from the tool's declared install sources since #11061, but still only a sentence — and there is -no modeled thing that sentence refers to — so the convergence cannot act on it, no census can count -what is unprovisioned, and nothing goes red when the hint rots. An unmodeled dependency is invisible -to every instrument that would otherwise rank it for work. - -## 3. The gap is already named by the corpus - -This is not an unrecognized problem. `extdeps.cache.sccache` states it in its own dissolution -trigger: - -> It dissolves when `host_effect_apply` binds a **verified-download-plus-install effect -> (FetchAndVerify then InstallBinary)** without the shell leaf, at which point -> `sccache_install_script` is deleted rather than edited. - -Neither `FetchAndVerify` nor `InstallBinary` exists anywhere in the corpus — grep returns exactly -that one comment. The missing concept was identified, its dissolution trigger was written, and the -capability was never built. `extdeps.container.docker_ce` carries a parallel trigger for the same -reason. - -So `sccache_install_script` and `docker_ce_repo_install_script` remain **shell leaves**: install -sequences assembled as strings, typed as `String`, with the modeled row supplying only version and -digest. The shell-leaf residue and the missing `Dependency` concept are **one gap seen twice**. - -## 4. What "install ntfy" costs today - -Concretely, why the blocker is a slice and not a row. Adding ntfy through the existing shape -requires: - -1. a new `extdeps` module for the release, its version and per-arch digests (sccache's shape); -2. a **35th `HostEffect` arm**, plus its realize handler and its plan handler; -3. an actuation site — `live_deploy` (scheduled for deletion) or `runner_host_deploy` (runner-scoped, and ntfy is not a runner concern); -4. a shell-leaf install script that lands already carrying a dissolution trigger it cannot discharge; -5. a systemd unit, which has no general home either — units are emitted per service family (`RunnerUnitStanding`, `LaunchUnitStanding`) rather than by one authority. - -Five pieces of bespoke modeling to install one binary — and every one of them is work the *next* -dependency will do again from scratch. **That is the cost the operator is feeling, and it is a real -cost rather than an impression.** - -## 5. The route - -The shape of the repair follows from §2: model the concept once, derive every use. - -**Phase 1 — `Dependency` in `extdeps`.** Identity, version, and an acquisition source as a -coproduct over the mechanisms already in evidence: - -``` -AptPackage { package } -AptRepoPackage { package, repo_url, keyring_url, keyring_path, keyring_fingerprint } -PinnedRelease { url_template, digest_by_arch } -NpmPackage { name, lock_integrity } -``` - -Every arm is a shape the corpus already implements correctly somewhere — this is decompression and -reduction of existing rows, not invention. Verification stops being per-dependency diligence and -becomes a property of the arm: a `PinnedRelease` **cannot be constructed without a digest**, which -moves supply-chain verification from §4b rung 1 (each author remembered) to rung 4 (unwritable -otherwise). That is the safety payoff, and it is larger than the DRY payoff. - -**Phase 2 — one arm.** `EnsureDependency { dep: Dependency }` replaces the bespoke arms; the six -existing dependencies become **data rows**. This is a §3 replacement migration and it must cut at -the root: build the arm, move all six at once, delete the old arms and their shell leaves in the -same motion. Six is a tractable atomic cut, and a surviving `EnsuredDependencyKind` would be an -attractor — every later dependency would be answered in its vocabulary. - -**Phase 3 — the prose route closes.** Stated against `provision: NonEmptyStr` when written; #11061 -deleted that field, so the subject is now the install sentence `host_cli_dependency` derives from -`CliTool.installable_via` through `install_hint`. The phase is unchanged in substance: that -derivation becomes a reference to a `Dependency`, so the refusal *names the acquisition that would -satisfy it* and an absent tool is a routable obligation rather than a sentence — derived prose is -still prose. Unprovisioned dependencies become countable only at that point. - -**Phase 4 — ntfy is a data row**, and so is the next one. - -**Ordering note.** Phase 1 is worth landing on its own even if Phase 2 waits: it is additive, it -gives the digest-by-construction win immediately, and the six existing rows can migrate one at a -time *into* it. Phase 2's cut is what must be atomic — not the whole program. - -## 6. What this does not settle - -- **Units.** `EnsureDependency` gets the binary onto the host; it does not run it. Unit emission has - no general authority either (`live_deploy` for apps, per-family standings elsewhere), and that is - a **separate half-built convergence** deserving its own analysis. The ntfy work needs both. -- **`live_deploy`'s disposition.** It holds app deployment members and the only working apt-ensure - path, and is scheduled for deletion with no declared cut. Phase 2 would take the apt path out of - it, which makes the cut smaller — but does not make it. -- **ntfy's actual packaging.** Whether ntfy ships an apt repo, a `.deb`, or only a release tarball - was **not verified while writing this** (no apt available in the session container). It changes - which arm the row uses and nothing structural, but it should be checked rather than assumed - before the row is written. diff --git a/docs/plans/deployment-risk-conformance.md b/docs/plans/deployment-risk-conformance.md deleted file mode 100644 index 0aaf1789486..00000000000 --- a/docs/plans/deployment-risk-conformance.md +++ /dev/null @@ -1,41 +0,0 @@ -# Deployment-risk conformance - -Operator direction 2026-10-03; vocabulary fixed the same day by a side-chat ruling relayed through silent-lark-156. Home: `gunbc.deployment_risk`. - -## Terms (approved; use these names) - -| Term | Meaning | Is NOT | -|---|---|---| -| `DeploymentId` | the stable identity of one Deployment whose desired state, mutable state, and external attachments move together; an instance realizes that Deployment and carries this identity | a host, machine, process, release stage, dashboard `instance_id` | -| `DeploymentRiskClass = TestRisk \| ProdRisk` | coarse harm class, DERIVED from `ProdRoleSelection` only | a number, machine qualification, stage, branch, host | -| `DeploymentHarmMagnitudeBet` | Fermi order of magnitude of harm conditional on doing the wrong thing, typed as a §4d bet | a probability, a fact, the class, an SLA | -| `ProdRoleSelection = NoProdRole \| ProdRoleHeldBy { deployment }` | the sole switch (`prod_role_selection`) | inferred from srv1, "live", main, a host | -| `DeploymentExternalBindingSet` | the external attachments of exactly one class; absent set refuses | a fallback, a shared default, a borrow | -| `DeploymentRiskInterface` | what each deployment exposes (identity, harm bet) and consumes (class, bindings) | a `DeploymentSpec`, an actuator, a stage model | -| `DeploymentReleaseStage` | independent rollout axis | out of scope; next candidate | - -`DesiredDeployment` (selection row) and `DeploymentSpec` (desired realization) stay distinct from `DeploymentId`. `MachineOperatingEnvironment` is replaced by `MachineQualificationPolicy { admits: DeploymentRiskClass }`: a machine admits a class, never holds one. A `ProdRoleHeldBy` assignment refuses unless its host admits `ProdRisk`. - -## Deliverables - -1. **Model** (this PR): `gunbc.deployment_risk`; `HostDashboardInstance.deployment` (required, all six constructors); `dashboard_instance_risk_class`; the failure-mode row `a_test_deployment_acts_on_the_prod_deployments_state`; RED `test.claim.deployment_risk_witness_test`. - - Declared frontier (§3c): `resolve_deployment_bindings` and `dashboard_instance_risk_class` gain production consumers in deliverables 2 and 3.1. - - Deferred to deliverable 2 (review 74937): `DeploymentRiskInterface` and `DeploymentHarmMagnitudeBet` land with their first executing consumer, the operator-disposition gate (which actions on a deployment need an operator), and the bet's magnitude is grounded as an order of magnitude over `std.measure` `Duration`, not a bare `Int`. -1b. **Machine qualification migration**: one motion over machine_intake (re-census on main first; coordinate warm-crane-577's mtcollins1 untangle lane). eager-gull-22 handed it to this lane. -2. **Repoint the prod pins** through `prod_role_selection` (broker owner, fabric storage placement, approval client/store/trust, serve/dispatch entry points, apply's prod peer → `DashboardProtectedSibling`, `fleet_reach_endpoint`). RED: a fixture selection moves the broker owner and storage placement. -3. **Split shared bindings**, one PR each: ntfy → Codex account/home → admitted ref → publication identity/GitHub App → R2/GCP → OIDC. Each adds a field to `DeploymentExternalBindingSet` and either a TestRisk resource (operator approval first) or a standing refusal. -4. **One spec per deployment**; retire the `live_deploy.desired` rung drop by its own trigger. Renames: `srv1_production_desired_deployment → srv1_primary_desired_deployment`, `roadmap_belt_production_spawn_mode → roadmap_belt_operational_spawn_mode`. -5. **Conformance row** `deployment-risk conformance` in `gunbc.design_argument conformance_domains`, once the home is consumed. - -Widened scope (side chat): classify every convergence subject as deployment-bound (Spark serving/experiments/seat grants, fabric storage, approval broker, R2 keyed by class + `BucketPurpose`, IAM targets, DNS/routes/certs, runner workloads, mutable caches), host-shared (Spark Wi-Fi/SSH/admin grants, KVM, base networking, toolchains), or fleet-control (root IAM writer, account admin). Pure computation identity and content-addressed outputs gain no deployment key. Manual convergence requests take `DeploymentConvergence { deployment } | HostResourceConvergence { host, resource } | FleetControlConvergence { operation }`. - -## Shared-binding table (as of deliverable 1) - -| Binding | State | Reason | -|---|---|---| -| ntfy topic | modeled in the binding set; ProdRisk only; TestRisk **refuses** at the resolver | broker path still reads `approval_ntfy_topic` directly until 3.1 | -| GitHub App (publication) | modeled; ProdRisk only; TestRisk **refuses** at the resolver | publishers still name `gunbai_*_declared` until 3.4 | -| Codex account and home | still shared | 3.2 | -| admitted ref | still shared (labs on `refs/fleet/desired`) | 3.3 | -| R2 / GCP / WIF | still shared | 3.5; coordinate royal-moth-86 (#13035) | -| OIDC client | still shared, unmodeled | 3.6 | diff --git a/docs/plans/derived-node-identity-design.md b/docs/plans/derived-node-identity-design.md deleted file mode 100644 index 49fcf128cc2..00000000000 --- a/docs/plans/derived-node-identity-design.md +++ /dev/null @@ -1,184 +0,0 @@ -# Derived-node identity in v1 infer - -Status: step 1 landed (#12913, #12914); step 2 scheduled 2026-10-04, starting with §7's instrument. -Owner: jolly-pike-330 (staffed by gentle-dove-36); scheduled by gentle-dove-36 on neat-boar-16's -handoff, 2026-10-04. Step 1 owner: clever-lynx-801. Ruling: quiet-gull-780, 2026-10-01. -Class row: `gunbc.recurring_failure_mode` `generic_identity_decided_by_spelling`. - -## 1. The question - -When `v1.compiler.infer` **builds** a type node after resolution, what identity does that node -carry, and who says so? - -"Builds" covers: -- substituting a generic's binding into a signature, -- instantiating a generic record's fields or an alias chain, -- forming a call plan's formals, -- minting a kernel container type for a value. - -Today the answer is: whatever spelling the node happens to have. Every consumer that needs to know -whether a node *is* a generic parameter, rather than merely spelled like one, has nothing else to -read. This note models the identity those nodes must carry. - -## 2. What was measured (not inferred) - -These facts locate the problem. Their magnitudes are deliberately not transcribed (DESIGN §6); -§7 names the instrument that re-derives them. - -- **The spelling collides by construction.** The kernel container type names its element child - after its own template parameter. Every `List` therefore carries a child spelled `T` and - resolved to `M`, and it collides with any generic parameter also spelled `T`. -- **Three sites decide generic identity by spelling:** - - `unify_generics` binds a bare formal by its label; - - `substitute_generics_apply` replaces a bare node by its label; - - that function's self-binding guard compares labels. -- **Two identity facts already exist after resolution.** Neither reaches the nodes infer builds: - - `TypeVariable` (inferred) is bound to each declared parameter by - `v1.compiler.infer_env` `env_with_type_variable_bindings`. Its id is the bare parameter name. - - `Node.declaration = (owner, TypeParameter { name })` is written by `v1.compiler.resolve` - `binder_marked_type` (#12690) on authored signature positions: parameter types, returns, type - arguments, declaration fields and arrow positions. -- **Coverage, measured over the whole-corpus compile.** A large share of generic bind and - substitute events act on nodes that carry neither fact in a usable form. They come from about a - dozen constructors, listed on the class row, led by `infer_call_arguments_generic_pass` and - `build_call_application_plan`. No magnitudes are given here: per DESIGN §6 they belong to the - instrument (§7), not to prose. -- **What happened when each fact was read alone.** Reading the `TypeVariable` mark made the - compiler's next generation refuse its own sources: generic record fields arrive unmarked. Reading - the declaration mark would stop substituting at every event the constructors above produce. - -## 3. Hypotheses step 2 must settle first (labelled as bets, DESIGN §4d) - -1. **Read before marking, not lost in copying.** The unmarked call-plan formals are read from a - signature representation built before `binder_marked_type` runs, not lost while copying. - `v1.compiler.infer` `bind_local_func_conformance` builds `ResolvedFormal.declared_type` from - `param_node_type_expr` of the func-env signature. If that signature predates marking, the repair - at those two sites is to read the marked signature, not to add a carrier. The test is the same - instrument, tagged by whether the node is reachable from a marked signature. -2. **`substitute_generics_apply` keeps its input's declaration.** It already copies - `n.declaration` into the node it rebuilds, so it is not a loss site. Its *output* node, the - binding, carries the binding's identity, which is correct. -3. **The kernel element child needs no parameter spelling.** Its identity is "element position - of this container". The open question is whether any reader depends on the child being spelled - `T`. The instrument answers it by deleting the spelling and listing what breaks; quiet-gull-780 - asked for that reader to be named. - -## 4. The model - -A derived type node carries exactly one **origin**, named by the constructor that builds it, never -reconstructed from the node's shape (bold-fox-455's constraint): - -```dag -type DerivedTypeOrigin - = CarriedDeclaration { declaration: DeclarationRef } // a copy keeps the identity it was given - | ParameterBinding { parameter: DeclarationRef } // the node a generic parameter was bound to - | KernelElementPosition { container: KernelContainer, position: Int } - | DerivationRefused { constructor: NonEmptyStr, cause: NonEmptyStr } -``` - -- **`CarriedDeclaration`** is the default for every copy. A constructor that rebuilds a node from - another node carries the input's declaration unchanged. This is the copy law, and most of the - inventory falls under it. -- **`ParameterBinding`** names the generic parameter a substituted node stands in for. It is - needed only where a consumer asks "which parameter did this come from" (cross-owner keying, §6). -- **`KernelElementPosition`** replaces the spelled `T`. A container's element child is identified - by its position in a kernel container and is never mistaken for a `TypeParameter`. -- **`DerivationRefused`** is the fail-closed arm. A constructor that cannot name an origin refuses, - located at the constructor. It never falls back to the spelling. - -Generic identity at the three sites becomes: a node is generic parameter `p` exactly when its -origin is `CarriedDeclaration` with `field: TypeParameter`, or a `ParameterBinding` to `p` whose -binding is `p` itself. A self-binding is identity equality. Spelling never enters the decision. - -**Laws** (the step-1 carrier states these and its control executes them): -- **L1, copy:** a copied node's origin equals its input's origin. -- **L2, no shape inference:** a constructor that builds a node names its origin, and two nodes - that are structurally equal but have different origins stay distinct. -- **L3, kernel distinctness:** a `KernelElementPosition` origin is never a `TypeParameter` identity, - whatever its spelling. -- **L4, refusal:** `DerivationRefused` is never read as a generic parameter or as a concrete type; - a consumer reaching it refuses. - -## 5. Its own relation, or an extension of an existing one (DESIGN §3b) - -This is **not a new keying relation**, and **not an extension of `std.occurrence_identity`**. - -- **Not #12790.** Occurrence identity answers "which authored occurrence". The nodes in question - are built by infer and have no authored occurrence, so allocating one would infer their cause - from their shape. That is option A, which 3e already rejected for the same reason. -- **Not the facts site key (3e).** That key answers "which site of one inferred tree a fact is - about". It is positional inside one tree and says nothing about what a node denotes. -- **The same relation as `std.decl_ref`, carried further.** Its relation is "this node denotes this - declaration-level entity". The model extends where that identity is *carried*, through infer's - constructors, and adds the two arms `DeclarationRef` cannot express: a kernel element position - and a refusal. - -**Reconciliation with A′** (`keying-relation-design.md` §3e, declared model item): A′ is "identity -for every node at infer's input, with each builder naming its cause". This note is A′'s -type-level interior: the same constraint, that builders name their cause, applied to the type -nodes infer builds *inside* the stage. They are one model item, not two. If A′ lands, its -input-side origins and these interior origins are arms of one carrier. This note's carrier is -named and placed so A′ extends it rather than forking it. - -**Scope:** type nodes built by `v1.compiler.infer` after resolution, covering the dozen -constructors inventoried in §2. The following are outside this note: -- value-expression nodes; -- other stages; -- v2 infer, which has its own carrier work (#12763 and its line). - -## 6. Sequencing - -1. **Step 1, in two PRs, with no infer edits:** - - **1a (#12913):** this note and the class row. - - **1b (#12914):** the std carrier `std.derived_type_origin`, which declares `DerivedTypeOrigin` - and `GenericIdentityVerdict`, and its control `test.claim.derived_type_origin_witness_test`, - which executes L1 to L4 over supplied origins. Until step 2 lands, that control is the - carrier's only consumer, a declared frontier whose trigger is step 2 (DESIGN §3c). -2. **Step 2:** first settle §3's three bets with the instrument. Then carry origins through the - inventoried constructors, switch the three sites to identity, and delete their name branches. - The evidence is: - - the compiler's next generation builds and compiles its own sources (the control that caught - the first attempt); - - a whole-corpus census in both directions at identity grain; - - the srv3 witness, the `00_compile` native-lane site, and `head_of(xs: [1], d: 2)` into a - String parameter. - - **Step 2 does not wait on the site-keyed facts store** (struck 2026-10-04, gentle-dove-36). This - note used to sequence step 2 after that work "because both rewrite infer's copying paths". They - are two modules both named infer, and they share no symbol: - - step 2 edits `v1.compiler.infer` (`unify_generics`, `substitute_generics_apply`, - `infer_call_arguments_generic_pass`, `build_call_application_plan`, - `bind_local_func_conformance`, `infer_record_lit_structural`) and `v1.compiler.infer_env` - `env_with_type_variable_bindings`, which copy type nodes; - - the facts store edits `v2.compiler.infer` (`inferred_facts_map_enter`, - `facts_map_from_entries`, `inferred_facts_witness_for_node`) and the readers of - `InferredTree.facts` in v2 eval, emit, translate and the lenses, which key a facts table. - - `v1.compiler.infer` imports nothing from v2 and holds no facts table, and §5 already scopes v2 - infer out of this note. The facts store was never built past its model - (`keying-relation-design.md` §3e) and has no owner. The one indirect link: step 2's - next-generation control compiles `src/v2`, so a facts conversion landing during step 2 changes - that control's corpus, not step 2's code. - **Residue admitted to step 2** (quiet-gull-780, 2026-10-01, via clever-newt-773): a generic - variant literal with no expected type, such as `Cons { head, tail }` at - `gunbc.spark.host_commitment`, is typed as its parent's *unapplied* declaration in - `v1.compiler.infer` `infer_record_lit_structural` (`raw_resolved`). Instantiating it from its - field values needs an applied-type constructor that carries identity, which is this model's - `ParameterBinding` origin. It is left to step 2 rather than added as another spelling-keyed - instantiation site. -3. **Then:** re-key the substitution map by the `TypeParameter` declaration - `(owner, declaration, name)`, so two owners' parameters spelled alike cannot share a key. This - is a separate PR, and the first production reader of #12690's field for this population. - -## 7. Instrument - -- **What it measures.** For each event in which `unify_generics` or `substitute_generics_apply` - binds or substitutes a generic, whether the node carries a `TypeParameter` declaration, and its - two nearest infer callers. It runs over the whole-corpus compile, `gunbc compile --source-root dag - --source-root src/v2 --target dag --repository gunbc --measured-root-demands - tools/whole_corpus_compile_measured_root_demands.json`. -- **Not yet an entry point.** The one measurement taken so far was a local, uncommitted probe on - main `b793732902`. It is a one-off, so its numbers are not quoted in this note or on the row. - Step 2's first task is to make it an instrument: a `gunbc test` label over the same compile, - reporting by constructor. The step-2 decisions rest on its output, not on any figure written - here. diff --git a/docs/plans/dogfood-route-manual-interventions.md b/docs/plans/dogfood-route-manual-interventions.md deleted file mode 100644 index 490651cee3e..00000000000 --- a/docs/plans/dogfood-route-manual-interventions.md +++ /dev/null @@ -1,175 +0,0 @@ -# Dogfood route: the manual interventions, dispositioned - -Status: plan, operator-agreed 2026-10-03. Each functional row below lands under the home it names. `factory-dogfood-route` (gunbc#13077) consumes their receipts, or carries dependency edges where they gate the qualifying route. Its lane coordinates route integration; it does not own every underlying capability. - -## Why this exists - -The first end-to-end attempt on srv2 (attempt `01feb0b65dee1377`, node `shell-dag-live-deploy-restart-tailscale`) did its work: 40 steps on a group-b seat, ending at its planned alignment checkpoint (exit 3). It never reached a PR, because every belt tick after about 06:50 UTC hit its start timeout and was killed. Getting it that far took six kinds of hand intervention. The operator's go/no-go on external customers is their own confidence from dogfooding, and a route that needs a person to keep it moving is not yet that evidence. - -This document records each intervention, what it exposed, what must become automatic, what stays with the operator, and the test that must go red first. - -## Three properties, not one - -Each intervention was first read as "make it idempotent / retryable". That conflates three properties: - -1. **Safe to retry**: repeating an operation cannot duplicate or corrupt its effect. Required everywhere. -2. **Convergent**: desired state and observed state decide Noop, Apply or Refuse. Required everywhere. -3. **Automatically repaired**: the controller chooses and applies the repair with no operator. A policy choice, and today the answer is **no**. - -`gunbc.ensure` `EnsurePolicy` already carries the distinction. `EnsureObserveOnly` refuses a mismatch; `EnsureMayApply` applies an available plan. Convergence therefore does not imply self-healing. It can converge to a safe, visible state and stop there. - -### Operator ruling (2026-10-03): manual resume, for now - -A crash or failure is observed by a person, root-caused, then resumed by hand, until there is confidence in the automated remedy. This is DESIGN §5's factory model: the line stops, and the stop is analysed before restart. What the system owes is that the stop is loud and located, and that the operator's resume is a single retry-safe action. - -### The incident shape every intervention below uses - -```text -normal desired state - -> fault observed -safe incident state - evidence captured - route or work withdrawn where necessary - AwaitingOperatorDisposition - -> operator records a disposition, keyed by incident identity -ResumeRequested | AbandonRequested | ClearRequested | QuarantineRequested - -> ensure -Noop | Apply | Refuse -``` - -Every disposition effect is keyed by its incident identity, so running it twice is a no-op. A crash after the effect but before its receipt re-observes the subject and finds the effect already done. - -The same rule applies to every outward effect on the route: "open the PR" means "ensure logical work item X has exactly one PR". An attempt or candidate revision updates that PR; it never acquires a second PR identity. After uncertain remote success, observe before create. The attempt identity may key the publication operation and its receipt, but not the PR itself. Pushes, comments and R2 writes follow the same rule. - -## The six interventions - -### 1. Stale units cleared by hand, three times - -**Exposed:** exited worker and supervisor units blocked redispatch until a teardown pass ran. With the timer off, they had to be cleared by hand, and the clearing left no record. #13066 records the same follow-up. - -**Automation owed:** -- observe the terminal or lease-expired unit; -- bind it to one incident identity; -- preserve its status, journal, attempt, lease and process evidence; -- stop counting it as live; -- withhold any replacement attempt until a disposition is recorded. - -A lease deadline (`std.temporal_effect` `HeldLease`) establishes `TerminalOrLeaseExpired`. It never by itself kills, deletes or restarts. - -**Stays manual:** the root cause, and the choice of Resume, Abandon, Clear or Quarantine. - -**Home:** the factory attempt lifecycle, consumed by `factory-dogfood-route`. - -**RED first:** kill a worker mid-turn. The unit is reported stale under one incident identity, with evidence preserved, and no replacement spawns. Recording `ClearIncident` twice changes nothing the second time. - -### 2. srv10 Wi-Fi restored by hand - -**Operator ruling:** the Sparks will serve on Wi-Fi for a while, and that is a valid medium. Why the link dropped still needs debugging. - -**Exposed:** a host lost its network and stayed routable. - -**Automation owed:** -- a missing or stale health receipt withdraws the host from routing; -- link evidence is captured before reconnection obscures it: association state, addressing and routing state, relevant service and driver evidence, and timestamps. - -A fresh health reading may say the host is technically available again, while a separate unresolved incident keeps it operator-held. - -**Stays manual:** the diagnosis, and returning the host to service. - -**Home:** fabric host health (`gunbc.fleet_health`) and network incident handling, with an edge into the route. The existing roadmap row "A host serves only on a fresh satisfied health receipt" is the admission half. - -**RED first:** drop Wi-Fi while the serving process stays alive. The host leaves the routable population, the incident evidence survives reconnection, and the host does not return until the operator's disposition permits it. - -### 3. Untracked senders on group-b, paused or killed by hand - -**Exposed:** a second door. Seat admission (`gunbc.serving.turn_admission` `serving_group_admission`) is not the only path onto group-b. - -**Operator ruling:** move the other senders onto seat admission, and add authentication to the Spark deployments through a real auth process, so every caller is forced onto the right path. - -**Authentication alone does not close the door.** A caller with a valid general-purpose credential could still bypass the seat ledger. The forwarding boundary needs both: -- an authenticated caller identity; **and** -- a current serving grant naming the admitted serving launch, the granted route or group, the seat entitlement, the caller or work identity, and an expiry or revocation boundary. - -A practical realization is a short-lived signed request permit minted by successful seat admission. A Spark-side front door verifies it before forwarding to vLLM, and refuses before the engine sees the request. - -**Stays manual:** migrating the existing senders, and deciding which identities may request grants. - -**Home:** managed identity owns the credentials (`credential-lifecycle-revocation`, gunbc#13068); serving and capacity own request admission and grant validation. - -**RED first:** a request with a valid caller credential and no seat grant is refused at the front door, and the refusal is counted. - -### 4. Belt timer started by hand - -**Operator ruling:** event-driven wherever possible, not timer-driven. - -**Automation owed:** -- events invoke the smallest affected obligation directly; -- deadline-bearing obligations schedule a one-shot wake-up; -- a bounded anti-entropy sweep discovers missed events and **queues** them. It never performs the work itself. - -**Stays manual:** only typed refusals and incident dispositions. - -**Home:** factory controller and event delivery. The timer is not a product requirement and does not become a desired-state row. - -**RED first:** with no timer installed, a worker exit causes its capture to run. - -### 5. #13066 deployed to srv2 by hand - -**Exposed:** srv2 runs `cc5bbdcc` plus the #13066 commit, placed by hand. The model exists: `gunbc.live_deploy.desired` declares srv1-live, srv1-lab and srv2-deploy (`srv2_deploy_desired_deployment`) beside `repository_convergence`. What was missing is converging an admitted revision onto the host. - -**Correction to the first reading:** a branch revision is valid when it has been explicitly admitted to the deployment that runs it. "Not main" is not inherently drift. - -**Operator context:** srv1 and srv2 are both used for development today. That use is separate from deployment risk: the srv1 daily-workspace deployment holds ProdRisk because `ProdRoleSelection` selects it, and the srv2 deployment derives TestRisk. - -**Automation owed:** each deployment's explicitly admitted revision is observed and converged into the running deployment, with readiness and member-identity readback. - -**Stays manual:** selecting and admitting the revision for that deployment. - -**Home:** generic live-deploy convergence (`gunbc.live_deploy`). - -**Operator ruling (2026-10-03): converges are triggered manually today, and automated later.** Until they are automated, the manual trigger is one retry-safe action, the converge entry point, never a hand-run sequence of steps. Running it twice against a converged host is a Noop. The deployment hand-placed on srv2 for #13066 is the case this rules out: the admitted revision goes onto the host through the converge, not around it. - -**Which deployment is prod: decided 2026-10-03.** The operator approved the deployment-risk conformance vocabulary. `DeploymentRiskClass = TestRisk | ProdRisk` is derived from one switchable row, `ProdRoleSelection = NoProdRole | ProdRoleHeldBy { deployment }`, and is never inferred from srv1, "live", main or a host name. The row initially holds the srv1 daily-workspace dashboard. Turning prod off is setting that row to `NoProdRole`. A TestRisk deployment that lacks its own `DeploymentExternalBindingSet` refuses rather than borrowing prod's. The migration that names it is a separate lane; this item only consumes it. The belt-tick near miss (a tick ran `git worktree add` into srv1's production tree) is one of the wrong-instance incidents that migration files as a recurring failure mode. - -**RED first:** admit a revision for srv2. Convergence brings the running deployment to it and reads it back. A running revision that differs from the admitted one is reported as drift. - -### 6. Every belt tick times out (unsolved) - -**Exposed:** `gunbc.roadmap_belt_actuate` `belt_run_once` is one monolithic pass that runs these stages in sequence: -1. spawn; -2. teardown; -3. launch; -4. commit; -5. verify; -6. review; -7. publish; -8. write the served observation. - -Any slow stage blocks every stage behind it. The module's own note on `belt_observe_once_cli` says that when the monolithic pass is too slow to reach its observation write, the page freezes at the last completed tick's bytes. After about 06:50 UTC, no tick completed, so capture, verify and publish never ran for the attempt. - -**Not the fix:** stage checkpoints that make the monolithic tick resumable. That preserves the structure that should go. - -**Automation owed:** expensive work leaves the tick. Each durable obligation gets its own event-driven ensure and its own effect identity: -- a worker-terminal event triggers capture and submission classification; -- a captured `SubmissionIsCandidate` triggers authoritative verification; -- a passing verification receipt triggers review and goal audit, independently; -- an approved review plus an approved goal audit admits publication; -- a yielded, blocked or needs-context capture stays visible but does not enter verification. - -The anti-entropy pass only discovers and queues a bounded number of outstanding obligations. - -**Stays manual:** intervention only on a typed refusal. - -**Home:** belt demotion and the factory lifecycle. - -**RED first:** a verify that runs long does not delay publish of an unrelated attempt, or the served observation. - -## Order - -The two that matter most for the dogfood clock: -- **6 (belt):** it is why the first attempt has no PR. -- **3 (second door):** untracked traffic blocks admission. - -Next, 1 (incident record for stale units) and 5 (admitted-revision convergence) turn the remaining hand steps into single recorded actions. Items 2 and 4 follow. - -None of this counts toward the dogfood window by itself. The window starts with the first qualifying run under `factory-dogfood-route` that also carries the joined evidence receipt from `service-evidence-capture` (gunbc#13068). diff --git a/docs/plans/dsv41-cut-d-redesign.md b/docs/plans/dsv41-cut-d-redesign.md deleted file mode 100644 index 76f468529d9..00000000000 --- a/docs/plans/dsv41-cut-d-redesign.md +++ /dev/null @@ -1,721 +0,0 @@ -# DSV41 Cut D — redesign against the observed Group A, not the assumed one - -Cut D of DSV41-ENGRAM-RUNTIME-0 was specified as a bounded replacement transaction against a -live V4 Flash incumbent: drain its offer, stop it, launch V4.1 TP4, probe, then **promote or -restore V4 Flash**, with promotion gated on *rollback demonstrated* — an incumbent request -served again after the experiment. - -**No incumbent was observed**, twice, 14 hours apart. Every terminal in the original cut that -names V4 Flash therefore has no subject *on those readings*, so the cut cannot be executed as -written, and executing a lightly-edited version would silently drop safety properties the -original bought. - -**That absence is a dated observation, not a property of Group A** — D0 re-establishes it under -the transaction's own claim, because something can become live between a reading and a stop. -This document records what was observed, what follows, and the cut that replaces the original. - -> ## ⛔ THIS CUT CANNOT BE EXECUTED TODAY -> -> It describes a transaction that depends on **suspending Group A's pair-serving authority** — -> the realization disabled while all four hosts stay held for the named successor, build and -> probe admission still fenced, under a bounded lease with cleanup. -> -> **No carrier provides that.** The model can express *withdrawal* only, as -> `spark_pair_serving_groups` shrinking — and for Group A, which is that roster's sole member, -> shrinking means **emptying**, which reclassifies the hosts the cut needs and hands two of them -> to any lane that asks. Emptying the roster is not a suspension substitute. -> -> So this is a **design with a named prerequisite**, not an instruction sheet. Modelling -> `PairServingGroupAuthority` (§2) is the work that has to land before D0 can take its first -> step. Everything below assumes that operation exists. - -**HOW TO READ THIS DOCUMENT.** It went through nine review rounds and several of its own -premises were rejected along the way. Rejected premises are marked **⚠ REJECTED PREMISE** and -kept only so they are not re-derived; everything else is operative. Nothing here should require -inferring that a later section supersedes an earlier one. - -## What was observed - -Read directly from the four Group A ranks (`spark-a3ee`, `spark-3bd5`, `spark-c2b1`, -`spark-ac79`) over SSH, twice, ~14 hours apart: - -| fact | reading | -|---|---| -| serving process | none — no `vllm`/serving systemd unit running | -| OpenAI endpoint on the serving port | no answer | -| occupant | one `gunbc-spark-pair` container, image `gunbc-spark-pair-runtime:ray-2.58.0`, created `2026-09-14T00:15:35Z`, up >2 days | -| memory held | 107–109 GiB of 121 GiB per rank; `nvidia-smi` attributes ~100.7 GiB to `ray::RayWorkerProc.run` | -| top process RSS | 2.6 GiB | -| free disk | 2.6–3.1 TB of 3.7 TB per rank | - -Two details make that table read correctly rather than look contradictory. `nvidia-smi ---query-gpu=memory.total` returns `[N/A]` on a GB10 because there is no separate accelerator -pool — the figure it attributes to the Ray worker is a claim on the same unified RAM `free` -reports. And the top process RSS being 2.6 GiB while 108 GiB is used is not an inconsistency: -CUDA device-side allocations on a unified pool do not appear in RSS. A process listing -therefore makes these nodes look idle while they are 89% full. - -The operator's statement that Group A "is not being used by anyone" is correct about -**traffic** and not about **occupancy**. - -### These readings do not contradict the fold's input, and the document must say so - -`gunbc.spark.pair_serving_observed` `group_rank_free_memory_observed` — the reading the fit -fold consumes — is qualified **"with the engine idle"**. The table above is a reading of an -**occupied** rank. They are answers to different questions and neither supersedes the other: - -| question | condition | who answers it | -|---|---|---| -| would V4.1 at TP4 fit if these hosts were cleared? | engine **idle** | `group_rank_free_memory_observed`, consumed by `candidate_component_budget` | -| are these hosts clear right now? | **as found** | the table above, and D0 | - -So the occupancy reading is not evidence that the fold's input is stale, and the fit verdict -stands on its own reading. **Presenting both without reconciling them would have been the -defect** — a reader comparing 116.9 GiB free against 107–109 GiB used would conclude one of -them is wrong, when what actually differs is whether anything was running at the time. - -### Why these readings are prose here, stated as a divergence rather than left silent - -§6 says name the instrument, never transcribe its output — and `pair_serving_observed` is the -modeled home, carrying `PairServingObservationProvenance` with `observed_on` / `observed_by` / -`sources`, and free memory as a `std.measure` interval with an `instrument` and a -`precision_caveat` rather than a hand-typed figure. A `FabricGroupA` occupancy row there is the -conforming move, and these figures are not in it. - -**This is a §3b divergence and the reason is that D0 is the step that produces that row.** The -cut below exists precisely to take the claim, reconcile the occupancy against the declared -realization, and emit an entry-state receipt. Hand-authoring the row now would author by hand -the artifact the cut produces by execution — and it would do so from a one-off SSH procedure -rather than through the instrument the model would name, which is the weaker evidence of the -two. - -So these figures are carried here as **what motivated the redesign**, not as the corpus's -record of Group A's occupancy. The corpus's record is `EntryStateReceipt`, D0 produces it, and -if this program stalls before D0 runs then the corpus correctly continues to have no modeled -observation of Group A being occupied — which is honest, because nothing executing has made -one. - -## What follows, stated as consequences rather than edits - -### 1. On the incumbent-absent branch there is no drain, and calling a reclaim a drain would claim a property we do not have - -**Everything in this section is scoped to `IncumbentAbsent`.** If D0 finds a live incumbent, the -original drain-and-preserve transaction applies instead and none of what follows does. - -The original sequence was *withdraw the incumbent offer → stop the incumbent*. A drain exists -to preserve continuity for live traffic. There is no live traffic and no offer being served, -so what the transaction actually performs is a **reclaim** of an occupying container. - -The distinction is not cosmetic. A drain's safety property is "no in-flight request was lost"; -a reclaim has no such property and needs none. Keeping the word would assert a guarantee the -step does not provide — §4b rung honesty applied to a transaction step. - -**But dropping the drain obligation does not drop the QUIESCENCE obligation, and an earlier -draft read as though it did.** No answering front door means no demonstrated serving traffic. -It does not mean the occupant is safe to stop: those Ray workers hold ~100 GiB each, and what -they are holding it for is unread. Before the reclaim, D0 must establish or revoke — never -assume — Ray jobs, actors and placement groups; serving offers and new-admission eligibility; -acquired or stranded seats; host claims, leases and ownership; and any actuator that could -recreate the occupant after it is stopped. So the operation is: - -``` -fence new acquisition -→ establish or revoke active work, claims and seats -→ stop the occupying realization -→ verify resource release -``` - -A reclaim with no in-flight-request obligation still has a fencing one. And note that -enrollment says only that Group A is a route that *may be asked* — it does not say the route -is live, and availability is separately three-valued (Available / Unavailable / **Unread**). -Unread is where Group A sits, and unread is not empty. - -### 2. The occupant is DECLARED, not residue — and that changes the rollback question entirely - -An earlier draft of this document rejected "restore the occupant" on the grounds that *we hold -no declaration that produces that Ray container, so restoring it would mean reconstructing from -an observation*. **That sentence is false**, and the correction matters more than the error. - -The corpus declares exactly what was observed: - -- `extdeps.docker.images.sparkrun_vllm_ds4_gb10` `gunbc_spark_pair_runtime_image_tag` is - `gunbc-spark-pair-runtime:ray-2.58.0` — the observed image, with its pinned base and its - exact `ray[default]==2.58.0` requirement; -- `gunbc.spark.pair_serving_realization` `spark_pair_container_name` is `gunbc-spark-pair` — - the observed container, with head and worker units and a one-container-per-host realization; -- `gunbc.spark.pair_serving_desired` `spark_pair_serving_groups` still contains - **`FabricGroupA`**, and `spark_pair_realization` maps that desired set into realizations. - -So the ranks are not carrying residue. They are carrying an instance with **declared lineage** — -image tag, container name, desired roster, realization and apply path all join. - -**But "converged" is stronger than the evidence, and an earlier draft claimed it.** This -document's own observation table says no vLLM process, no serving unit running, no answer on -the enrolled endpoint — while the declared realization includes a head unit that starts Ray and -then vLLM, worker units under supervision, a head engine with `Restart=always`, and an apply -that treats an inactive unit as grounds for restart and finally requires `systemctl is-active`. -An instance matching that declaration would be answering. What the evidence supports is: - -``` -declared producer and authority ESTABLISHED -full realization presently converged NOT ESTABLISHED -observed instance apparently DRIFTED -``` - -So on the facts written here, the expected D0 answer is **`DeclaredOccupantDrifted`**, not -`DeclaredOccupantObserved`, unless D0 reads the rendered units, container specs, rank membership -and serving state and proves otherwise. That does not weaken this section's conclusion — the old -authority can still recreate its desired realization, which is the whole hazard — it only stops -container lineage being used as proof that the realization currently converges. - -**The consequence is a design break, not a wording fix.** If Group A remains in the desired set, -an existing apply path can recreate exactly what D1 would call residue. "Return to a released -empty state" is then *not a converged state*: a converger reasserting the desired realization -would undo D1's terminal, and nothing in the transaction would notice. A terminal that another -authority can silently reverse is not a terminal. - -So the question is not *may we restore the occupant* — it is **under whose authority do the -hosts sit while V4.1 is on them**, and the plan must answer it explicitly: - -- **suspend Group A's pair-serving desired authority**, and record that suspension as part of - the authorization, so the hosts are never in a position where two authorities both believe - they own them. *Suspend*, not *withdraw*: different operations, and only withdrawal is - expressible today. - -**THE SUSPENSION'S LIFETIME IS ONE RULE, because an earlier draft gave three and they were not -interchangeable** — "for the experiment's duration", "record that withdrawal", and an -undifferentiated "claims released" each permitted a different implementation: - -``` -atomic transfer into suspension -→ the suspension AND the exact four-host reservation persist through - QuiescentReservedBaseline — they do NOT end when D1 ends -→ they end only when one of: - D2 promotion assumes the authority - the prior authority is explicitly restored - FleetReleasedBaseline is explicitly selected - -D1 cleanup RELEASES rank and process claims, offers, seats, live allocations -D1 cleanup RETAINS the four-host reservation, under QuiescentReservedBaseline -``` - -"Claims released" is not one category: the execution claims must end and the host reservation -must not, and a cut that says only "claims released" leaves an implementer to guess which. - -That is a real decision with its own consequences — it means Group A is deliberately not -pair-serving while this runs — and it belongs in the authorization, not in a footnote. It also -**cannot be performed today**: the operation it names does not exist, which is the gap this -section ends on. - -**THE CORPUS HAS FACED THIS HAZARD BEFORE, THOUGH IT ANSWERED A DIFFERENT QUESTION.** -`gunbc.serving.serving_enrollment` records that one list used to answer two questions that move -for different reasons — *whose hosts this repository claims and converges*, and *which endpoints -a turn may be offered* — and that they stopped coinciding on 2026-09-08. The reason given is the -hazard this section is about: Group B's head is a host another authority also names, so leaving -B in the convergence roster left **two authorities claiming one machine, and the first -pair-serving convergence to run would have acted on a host it believed idle.** The operator -authorised removing the claim, and the module was split so the two questions could move apart. - -That split is real and is observable in the current rosters — it is why a group can be enrolled -for serving without being claimed for convergence: - -``` -serving_enrolled_groups = [FabricGroupA, FabricGroupB] ← who may be ASKED -spark_pair_serving_groups = [FabricGroupA] ← whose hosts we CLAIM and converge -``` - -**⚠ REJECTED PREMISE, kept only so it is not re-derived.** An earlier draft concluded from this -that Group B is "precisely the state this cut needs for Group A", that withdrawal therefore -needs "no new mechanism", and that the precedent could simply be applied a second time. **That -is wrong and the next subsection says why.** Group B's withdrawal was a removal from a roster -that still had another member; Group A's would empty it, and Group A is the row that speaks for -srv7 and srv8 whereas Group B's hosts were claimed by another authority that wanted them. The -precedent establishes that the enrolled/claimed split exists and that a claim can be withdrawn — -it does **not** establish that this withdrawal is safe. - -**Does the withdrawal also remove the host admissibility or launch authority D1 and D2 need?** -**Yes — and an earlier version of this section answered that wrongly.** It traced admissibility -to `gunbc.spark.host_commitment` `spark_serving_admissible_hosts`, found that removing a claim -*loosens* commitment, and concluded the withdrawal "revokes a competing claim, which is the -objective." That conclusion is false, because it asked who is let in and never asked who is let -out. - -`host_commitment` derives every `MemberOfClaimedServingGroup` cause directly from -`spark_pair_serving_groups`, and uses those causes to decide **which serving subject owns each -host**. The cell-role roster assigns `SparkServingCell` to **srv5 and srv6 only** — the Group A -claim is what speaks for srv7 and srv8. So a literal withdrawal produces: - -| hosts | after withdrawal | -|---|---| -| srv5, srv6 | cell role remains, Group A ownership cause gone → serving role held by **no serving subject** | -| srv7, srv8 | no cell-role row, no ownership cause → **uncommitted**, admissible to any serving subject, and eligible as "unplaced" effect hosts | - -And V4.1's own candidate field derives its host population through `spark_serving_admissible_hosts` -for the **Group A pair-unit subject** — `PairServingUnitOn { FabricGroupA }`, deliberately -unchanged across the V4→V4.1 transition. **So the TP4 subject this program exists to run loses -two of its four hosts**, while srv7 and srv8 become available to unrelated lanes: the vLLM -source-build and runtime-image-probe roots both authorize effects through `admit_unplaced_host`, -and the latter pulls an image and starts a container. - -The remedy would have destroyed the authority relation it exists to protect, and the plan's -prose-level "exclusive claim" would have disagreed with the production authority that build and -probe lanes actually consult. Loosening is not neutral when your own exclusivity is what is -being loosened. - -**WITHDRAW AND SUSPEND ARE NOT INTERCHANGEABLE, and this document may not use them as though -they were.** Suspension is the right operation here. **No such modeled operation exists yet** — -that is the gap this cut must close before it can run, and it is a modeling obligation rather -than a sequencing detail. It needs a typed authority state whose projections differ per -consumer: - -``` -PairServingGroupAuthority - = PairServingActive { group, exact_realization } - | SuspensionPendingReconciliation { group, transaction, lease } // both actuators disabled - | SuspendedForAuthorizedSuccessor { group, authorization, - exact_candidate_realization, - lease, cleanup } - | FencedRefusal { group, cause, disposition_authority } // foreign / unread - | ReleasedToFleet { group, release_receipt } -``` - -**Every state the rest of this document requires is in that list.** -`SuspensionPendingReconciliation` and `FencedRefusal` are authority states, not diagnostic -labels — D0 holds the fleet in the first while both actuators are disabled, and the second is -the long-lived terminal for foreign or unread occupancy. A coproduct without them cannot express -the state machine D0 declares total. - -And the realization must survive in the genealogy: `PairServingActive { group }` alone cannot -distinguish V4 from V4.1, because both deliberately share `PairServingUnitOn { FabricGroupA }`. -A successor is discriminated by its exact authorization and candidate realization, never by -subject identity. - -**THE LEASE NEEDS AN EXPIRY TRANSITION, and the lifetime rule above does not give it one.** The -three ways it ends — D2 assumes the authority, the prior authority is restored, -`FleetReleasedBaseline` is selected — are all deliberate acts. A bounded lease also ends by -*running out*, and each silent answer is unsafe: expiry releasing the reservation lets another -lane take a host with no cleanup or readback; expiry restoring the old authority may restart -convergence over drifted, foreign or unread state; expiry changing nothing makes "bounded" a -word rather than a property. So: - -``` -authorization expires -→ launch, offer and seat authority are GONE -→ cleanup and fencing authority REMAIN -→ release or restoration only after the required receipt -``` - -Expiry removes the right to *start* things and keeps the right to *finish* them, which is the -only arrangement where a lease can expire mid-transaction without stranding the fleet. - -with each consumer told explicitly what it sees: - -| consumer | Active | Suspended | -|---|---|---| -| pair realization / apply | acts | **does not act** | -| host commitment | all four hosts owned | all four hosts **still owned**, by the named successor | -| build / probe host admission | placed | **still placed**, never unclaimed | -| serving enrollment | unchanged | unchanged | -| capacity standing | ordinary result | a **typed suspended** result, never an empty population | - -**One positive result from the trace:** serving enrollment is genuinely independent of pair -desired state. Group A stays enrolled as a route and the harness still probes it before -offering work, so suspending the old realization does not delete the front door. - -### The withdrawal EMPTIES the roster, and that is not a local toggle - -`spark_pair_serving_groups` is `[FabricGroupA]`. **Group A is its only member**, so withdrawing -it does not remove one entry — it leaves the list empty. Every fold over that roster then runs -over an empty domain, and the repository already carries this class by name: -`gunbc.recurring_failure_mode.predicate_vacuously_true_on_an_empty_domain`. - -An `all(...)` over an empty list is **true**. So a check of the form *every claimed serving -group satisfies X* stops being evidence the moment the roster empties — it passes, loudly and -greenly, because there is nothing left to fail it. That is the opposite of what a safety check -should do when its subject disappears, and it would arrive exactly when the fleet is least -ordinary. - -**The obligation lives on the roster, and this document deliberately does not restate it.** §6 -says the mark on the carrier is the authority; an earlier draft then restated the obligation -here in its own words, which drifted from the carrier's as the carrier was corrected — the two -disagreed on whether it covers *folds* or *every direct and transitive consumer*, and on whether -the row is *ownership* or a *commitment input*. Restating an authority is how you end up -maintaining two of them. - -So the obligation is the annotation above `spark_pair_serving_groups` in -`gunbc.spark.pair_serving_desired`, and it is authoritative over anything this document says -about it. Read it there. - -The roster also reaches **build and probe admission** and **capacity**, not only convergence — -which is precisely why the accepted design is suspension and **not** a roster edit: a suspension -that emptied this row would reclassify the hosts it is trying to hold. The authorization names -the authority it suspends and the reservation it retains; it empties nothing. - -**THREE SUBJECTS, THREE NAMES.** An earlier draft used one word — "baseline" — for the state -D0 records and the state D1 returns to. Those are mutually exclusive, and the ambiguity sat in -the one sentence that tells a rollout worker where to leave the hosts. - -| subject | what it is | who produces it | is it a return target | -|---|---|---|---| -| **`EntryStateReceipt`** | the ranks as found — occupant present, ~108 GiB held | D0 | **no** | -| **`ReleasedBaselineSpec`** | the *desired* post-experiment state: no ranks, no engine, no serving offer, no seat, no occupying container, no live device allocation. **Whether a host reservation remains is the choice made below** — for the ordinary D1→D2 path it does | declared before D1 runs | it is the target | -| **`ReleasedBaselineReceipt`** | a readback establishing that the spec holds | D1's terminal | — | - -A spec and its readback are two facts, and collapsing them is how "we returned to baseline" -becomes an assertion instead of a reading. - -**RESERVED OR FREE — the spec must choose, and an earlier draft claimed the guarantees of -both.** It defined the released state as having *no host claim* while also suspending the old -authority only "for the experiment's duration". That leaves three readings, and two of them are -broken: if suspension ends when D1 ends, the old realization can be reapplied immediately and -the released terminal is invalidated; if suspension persists with no reservation, srv7 and srv8 -become effect targets and srv5 and srv6 are held by nobody. Only the third is coherent: - -``` -QuiescentReservedBaseline ← the target when D2 is expected to follow - no rank processes, no engine, no offer, no seat, no live device allocation - immutable verified artifacts may remain - the exact four-host reservation REMAINS -``` - -That holds no runtime live across the authorization boundary — only the right to use the four -hosts, so an unrelated build or probe cannot occupy them before D2. If the intent is instead to -give the hosts back to the fleet, that is a **different** terminal and must be named as one: -`FleetReleasedBaseline`, where D2 must reacquire the hosts and may not assume the same four -remain available. - -**What may remain.** The released state releases *processes, seats and live memory* — not bytes -at rest, and under `QuiescentReservedBaseline` not the reservation either. The exact runtime image and the content-addressed row stores may stay on -local storage. Rematerialising hundreds of gigabytes to satisfy a definition would be redundant -work, and immutable content-addressed artifacts carry no live runtime across the authorization -boundary. The spec says so explicitly rather than leaving a later reader to decide whether a -staged row store violates "released". - -The **released state** remains the return target rather than the entry state, but now for a -stated reason: the entry state is an instance of a desired authority we are suspending, so returning to it means *restoring that authority*, which is a separate decision -from ending the experiment. D1 ends the experiment; restoring pair-serving authority is its own -step with its own evidence. - -### 3. On the same branch the risk profile inverts, and that makes P1 Cut 3 more important rather than less - -**Also scoped to `IncumbentAbsent`.** The original cut's danger was **breaking a live service**. -On this branch that danger is absent — not abolished, and it returns the moment D0 answers -`IncumbentLive`. The remaining -danger is **not being able to give the hosts back** — a partial or stalled launch leaving -ranks up, seats held, and 121 GiB per node unavailable, with no incumbent whose restoration -would incidentally clear it. - -The original brief said P1 Cut 3 "should block" ordinary production promotion. Against an -incumbent, that hedge was defensible because restoring the incumbent was itself a cleanup path. -Without one it is not: **promotion must refuse.** But the condition is CONJUNCTIVE, and an -earlier draft joined two different problems with `or`: - -``` -(P1 Cut 3, or an exact seat-lifecycle equivalent) -AND -(a demonstrated rank / process / offer / claim cleanup path to ReleasedBaselineSpec) -``` - -P1 Cut 2 detects an exporter still live over an engine that has stopped advancing and -explicitly leaves held-seat invalidation to Cut 3. That is Cut 3's subject: **seat and -incarnation invalidation.** It is not the same fact as killing partial ranks, withdrawing -offers, releasing host claims, stopping containers and returning unified memory — those are -host and runtime cleanup. A single realization may discharge both, but only if its evidence -actually proves both, and writing `or` invites treating either one as sufficient. - -### 4. The serving route is declared and unfulfilled, and the experiment must not paper over it - -`gunbc.serving.serving_enrollment` enrolls `FabricGroupA` — `serving_enrolled_groups` carries -it, with an endpoint, a port and a turn ceiling. The model says Group A is a serving route. -Nothing answers there. - -So *one bounded request through the normal route* is, today, unachievable for **any** model -rather than specifically for V4.1. A cut that lists it as a terminal without saying so would be -specifying an outcome nobody can currently produce, and the first lane to attempt it would -discover the gap under a live transaction. Establishing that the enrolled route actually -carries a request is its own precondition, and it is stated as one below. - -## The replacement cut - -### D0 — take the claim, reconcile the occupancy, then record the ENTRY state - -**D0 performs an ATOMIC AUTHORITY TRANSFER, not an acquire-then-withdraw sequence.** An earlier -draft said D0 takes the exclusive claim first *and* that the hosts must never be owned by two -authorities. **Those cannot both hold without a transfer operation**, and writing both was a -contradiction rather than a plan: - -``` -acquire experiment claim, then withdraw old → OVERLAP: two authorities own the hosts -withdraw old, then acquire experiment claim → GAP: build/probe lanes may take them -atomic transfer → one active owner throughout -``` - -The gap arm is not theoretical: srv7 and srv8 become `admit_unplaced_host` targets the moment -their ownership cause disappears. So the transfer is the operation, and it is part of the -suspension gap named in §2 — there is no modeled transfer today. - -**D0 still brackets its readings with the claim it holds.** "No incumbent was -observed" is a dated observation, not a property of Group A — the two SSH readings above are -14 hours apart and say nothing about the interval between the last one and the stop. Without a -claim held across that interval, something can become live between observing and reclaiming, -and the transaction would proceed on a stale premise. That is the same defect this whole -document exists to correct, one level down. - -**THE QUESTIONS ARE ORDERED, because an earlier draft's branches overlapped on the load-bearing -state.** It asked occupancy reconciliation and incumbent presence as if independent — but this -document establishes that a *converged* declared realization would be answering, so -`DeclaredOccupantObserved { converged }` and "a live serving incumbent" are the **same state**, -sent by one branch into the reclaim redesign and by the other into the original transaction. The -incumbent question is asked **first**: - -``` -under SuspensionPendingReconciliation, both actuators disabled: - - incumbent live - → hand off to the original drain-and-preserve transaction, - or a typed fenced refusal - incumbent absent - → reconcile the remaining occupancy (below) -``` - -**Then** the reconciliation question. Not *what is here*, but: - -``` -does the observed container / image / unit population reconcile -with the declared pair-serving realization? -``` - -with four answers, each leading somewhere different: - -| answer | meaning | disposition | -|---|---|---| -| `DeclaredOccupantObserved` | declared, and shown converged **without answering** — a narrow case, since a converged realization would ordinarily answer | `SuspensionPendingReconciliation → SuspendedForAuthorizedSuccessor` | -| `DeclaredOccupantDrifted` | declared lineage, convergence not established — **the expected answer on the readings above** | `SuspensionPendingReconciliation → SuspendedForAuthorizedSuccessor`, drift recorded; the drifted state is not a target | -| `ForeignOccupantObserved` | something we do not declare | `→ FencedRefusal` — not ours to reclaim; needs operator disposition | -| `OccupancyUnread` | the question could not be answered | `→ FencedRefusal` — unread is not empty | - -**EVERY ARM NEEDS AN AUTHORITY-STATE TERMINAL, including the refusing ones.** The transfer -happens before the readings — that is what closes the overlap and gap arms — so a refusal fires -*after* the authority has already moved, and an earlier draft said nothing about what becomes of -it. Neither silent answer is acceptable: restoring `PairServingActive` may reactivate -convergence over a foreign or unread occupant, and leaving `SuspendedForAuthorizedSuccessor` -standing means a **refused** D0 has permanently changed a long-lived authority. So the transfer -lands in an intermediate state that inspection happens inside: - -``` -PairServingActive -→ SuspensionPendingReconciliation { transaction, lease, - old actuator disabled, - successor actuator disabled } -→ read and reconcile, then: - SuspendedForAuthorizedSuccessor declared eligible branch - | PairServingActive only when restoration is shown safe - | FencedRefusal foreign or unread — needs operator disposition -``` - -A versioned compare-and-swap protocol with the same dispositions would serve equally. The -requirement is that **no D0 answer leaves the authority state undefined**, and that both -actuators are disabled while the answer is being determined. - -And it branches on the incumbent rather than assuming its absence: - -``` -live serving incumbent observed → the original drain/preserve path, or refuse this one -no incumbent + quiescent declared occupant → the reclaim path below -foreign, active, or unread occupancy → refuse -``` - -Only then does it record the entry state per rank: occupant identity and creation time, memory -used and free, absence of a serving process, absence of an answer on the enrolled endpoint, and -free disk. That receipt is evidence of **what changed** and forensic record. It is not the -rollback target. - -**D0'S TERMINAL IS TWO SHAPES, NOT ONE, because an earlier draft required a complete receipt on -every arm — including the arm where the instrument answered "unread".** If occupancy could not -be read, D0 cannot produce the entry-state facts an `EntryStateReceipt` is defined to contain, -and demanding one anyway would force either a fabricated receipt or an unreachable terminal: - -``` -D0Eligible { - entry_state_receipt : EntryStateReceipt, // complete, all four ranks - authority : SuspendedForAuthorizedSuccessor, -} - -D0Refused { - partial_readings : whatever was read, - cause : foreign | unread | incumbent-live, - authority : FencedRefusal, -} -``` - -Equivalently, `EntryStateReceipt` could itself be total, with per-rank unread arms carrying their -causes. What must not happen is a complete positive receipt being required after the instrument -has already said it could not read. - -Terminal: the authority is in exactly one of `SuspendedForAuthorizedSuccessor` or -`FencedRefusal`, the corresponding receipt exists, and no host state was altered. - -### D1 — reclaim, bounded experiment, mandatory return to the RELEASED state - -Authorization is exact and time-bounded over: candidate key, the four host identities, the -**produced** runtime image digest, model source identity, the four row-store identities, the -TP4 profile, the selected Engram route, the D0 entry-state receipt, and the cleanup path of §3. -Candidate identity is not authorization. - -``` -claim the four ranks -→ reclaim the occupant, read back that its memory is released -→ re-verify the staged artifacts against their declared identities -→ launch V4.1 TP4 -→ establish all-rank agreement -→ complete model load -→ complete graph/runtime initialisation -→ answer semantic and differential probes -→ ONE NORMAL ROUTED REQUEST, while the candidate is still live -→ RETURN TO THE BASELINE THE AUTHORIZATION NAMED, unconditionally - (QuiescentReservedBaseline on the ordinary D1→D2 path) -→ read back that baseline's receipt -``` - -D1 **always** ends at the released state — not at the entry state, which it deliberately does -not restore. It does not promote. - -**AND THE IDEMPOTENCE CLAIM AN EARLIER DRAFT MADE HERE WAS FALSE.** It said a rerun of D1 -mutates nothing. A rerun of D1 cannot mutate nothing: it reclaims hosts, launches V4.1, -initialises it, routes a request and tears it down. **The experiment is REPEATABLE; the cleanup -converger is IDEMPOTENT**, and those are different properties that one sentence was conflating. -What is actually checkable: - -``` -run the D1 experiment -→ converge the baseline (QuiescentReservedBaseline or FleetReleasedBaseline) -→ read the baseline receipt -→ run THAT CONVERGER again -→ require no mutation -``` - -The no-mutation property belongs to the converger and is asserted against it. Attaching it to -the experiment made the receipt unfalsifiable, because no honest rerun could have satisfied it. - -Weight loading is not success. Acceptance begins after runtime initialisation and one valid -semantic completion. An HTTP 200 discharges nothing. - -**The normal routed request happens INSIDE this window, and an earlier draft had it outside.** -The end-to-end join — router request, selected offer, authorization, four agreeing ranks, -produced completion — binds the exact realization D1 created. Deferring it past the teardown -would mean relaunching V4.1 to perform it, which is a second fleet-mutating experiment, or -performing it against a different model, which establishes nothing about this candidate's -offer and rank wiring. The original Cut D placed it correctly and this redesign keeps that -ordering. - -**The cleanup closure D2 requires is needed HERE too**, not only at promotion: once D1 -acquires a seat to serve that request, any failure after acquisition can strand exactly the -seat P1 Cut 3 exists to invalidate. - -Every stage capable of preventing a later observation carries a terminal disposition: ranks -stopped, rank and process claims released, offers and seats released, live allocations released, -**the host reservation retained or released exactly as the named baseline requires**, that -baseline read back — or a typed refusal naming the missing cleanup evidence. A failure arm that cannot reach it is the one outcome that must -stop the line loudly, because it is the risk this cut actually carries. - -### D1a — the route reaches a process, which is the only model-independent half - -An earlier draft claimed the routed request was separable from V4.1 and then wrote a terminal -requiring *four agreeing ranks and a produced completion* — which no model-independent step can -produce. **That terminal was not separable and the claim of separability was wrong.** The two -halves come apart like this: - -**Model-independent, but NOT free, and an earlier draft called it cheap.** Whether the enrolled -route reaches a listening process is a fact about routing rather than about V4.1, and it can be -established against any responder. But **binding a responder to the enrolled endpoint means -starting a process on Group A's declared address and port** — that is fleet mutation, on the -hosts this entire cut exists to fence. It cannot happen before the authority transfer, and it -owes a teardown like any other launch: - -``` -after D0 reaches SuspendedForAuthorizedSuccessor -→ launch a bounded endpoint responder -→ issue the enrolled-route reachability request -→ stop the responder -→ re-establish QuiescentReservedBaseline -``` - -Its output is narrow and should be named so nobody reads it as more: -**`EnrolledEndpointReachabilityReceipt`** — it establishes router-to-endpoint transport and -listener reachability, and **nothing else**. Not offer selection, not seat acquisition, not rank -agreement, not model advertisement, not that any candidate serves. D1 and D2 still owe every one -of those joins. - -**Model-dependent, and therefore NOT separable.** The end-to-end join — router request, to -selected offer, to authorization, to four agreeing ranks, to a produced completion — cannot -exist without a TP4 model actually loaded. It belongs inside D1 (as the experiment's semantic -terminal) and inside D2 (as the promotion terminal), and it is stated in both. Extracting it -into a precondition would be specifying a step nobody can perform alone. - -### D2 — production promotion, separately authorized - -A new authorization over the already-proven realization. Promotion **refuses** unless: - -- the candidate is realized, not merely keyed — produced image digest, applied patch - population, four row-store identities; -- row-store faithfulness is established over the **published** population, not a fixture; -- D1 completed and its `ReleasedBaselineReceipt` was read back; -- D1a established that the enrolled route reaches a process, and D1 produced the end-to-end join while the candidate was live; -- **P1 Cut 3 (or an exact seat-lifecycle equivalent) AND a demonstrated cleanup path to `ReleasedBaselineSpec`** — two facts, not one; -- **and the no-mutation rerun is a TERMINAL, not a precondition.** An earlier draft listed - "a second convergence run mutates nothing" among the conditions promotion refuses without — - but a no-mutation *promotion* rerun cannot precede the first promotion. The order is: - -``` -D2 preconditions satisfied -→ apply the production promotion -→ one exact normal routed request succeeds -→ apply the production desired state AGAIN -→ require no mutation -``` - -Capacity calibration is a follow-up. Record steady resident bytes, cache retention, local read -bytes, lookup hit/miss population, prefill and decode rates, TTFT and ITL — and do not turn any -of them into policy before correctness converges. - -## What this redesign does not change - -The identity discipline is untouched: checkpoint identity is not runtime identity, route B -versus D is not a different binary, TP4 is an execution-profile fact, patch order is semantic, -and build evidence does not transfer across a patch, tokenizer, checkpoint, row-store, topology -or route change. The published runtime's scoped `NoFeasibleRealization` stays true about the -published runtime; a file-backed runtime is a new candidate subject and does not weaken it. - -## Why the program is still worth running - -**Named, not transcribed.** An earlier draft re-derived the fit in prose and inverted it. The -correction is not a better number — it is that this document should not carry the number at -all. - -The authority is `gunbc.spark.serving_deployment_selection` `candidate_component_budget`. The -reading it consumes is `gunbc.spark.pair_serving_observed` `group_rank_free_memory_observed`. -Its per-rank verdict at 8 and at 4 ranks, the component breakdown behind the budget, and the -corroborating figure from the rollout lane's independent instrument are all carried in the -annotation beside `test.claim.spark.serving_deployment_selection_witness_test`'s selecting -witness. Read them there; they move when the fold's inputs move, and a copy here would not. - -What this document needs to state is the **shape** of that result, because the cut depends on -it: **route A at TP4 is excluded by a measured shortfall that exceeds the reading's own -precision** — an established exclusion, not a near miss. - -Three things invert that answer if taken from outside the fold, and all three are mistakes this -document made before it was corrected: - -- **the denominator is the observed idle-free reading, not the device total.** - `candidate_component_budget` says so in as many words, and refuses rather than substituting - the total when a reading is missing; -- **the budget is what remains after the artifact's other three components, not one.** The - vision tower with its aligner and the DSpark draft model are the difference between a - two-component sum showing headroom and the fold showing a shortfall; -- **the shortfall must clear the reading's precision.** The runtime prints one decimal of GiB, - so the free figure is an interval, and a verdict drawn against it is established only if it - exceeds that width. The fold's does. - -**That is why Cut B exists.** Not because the published runtime nearly fits — it does not fit, -by a margin the measurement can carry — but because moving Engram off the resident budget is -the only change that alters the term the fold is short on. diff --git a/docs/plans/edge-label-coproduct/README.md b/docs/plans/edge-label-coproduct/README.md deleted file mode 100644 index b05f9f5fd4f..00000000000 --- a/docs/plans/edge-label-coproduct/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# Typed EdgeLabel coproduct — model proposal (step 1 of 2) - -Status: model approved and landed (gunbc#12473). Step 2, the cut, is the follow-up PR from quiet-koi-814; its scope was ruled by gentle-koi-724 (see "Step 2 as landed" below). -Climb on record: `gunbc.recurring_failure_mode` `behavior_named_edge_label_validated_not_constructed` (the next-rung trigger names "edge labels ... a closed coproduct owned by `v2.std.node`"). No parallel RFM row is filed. The text-matching readers below are a second instance of that class, and the step-2 PR adds their evidence to that row. - -## Defect - -`v2.std.node` `EdgeLabel = Named { name: Symbol } | Positional`. Grammar-structural labels (for example `^dag_surface_module_header`, `^arrow_body_edge`, ``) and AUTHORED names (a field, a named actual, a declaration name) share one `Symbol` identity space. A reader that asks "is this the module-header edge?" can only compare text, and an authored name spelled the same way inhabits the answer. The invalid state is constructible, and today only `v2.std.type_binder` `type_binder_labels_conform` refuses it, for behavior nodes (rung 3), while four readers are not guarded at all: - -| reader | module | reads | -|---|---|---| -| `dag_surface_module_header_metadata_edge` | `v2.extdeps.languages.dag` | `Symbol ==` two header symbols; its callers bind `Named { name }` first | -| `dag_node_is_module_root_conj` (fallback arm) | `v2.extdeps.languages.dag` | `sym == ^dag_surface_module_header` | -| `d1_edge_names_where_clause` | `v2.test.claim.parse.d1_declaration_grammar_parse_test` | `n == ^dag_surface_where_refinement_clause` | -| `site_is_import_syntax_mention` | `v2.lens.module_graph` | `contains(site.position, ^dag_surface_import_*)`; the path is a Symbol list that already mixes production names with authored names (`v2.compiler.reference_site_collector` `reference_sites_in_edge`) | - -## Proposed shape - -``` -// v2.std.node -type EdgeLabel - = Structural { label: StructuralEdgeLabel } // constructed only by a grammar / lowering authority - | Authored { name: Symbol } // carries the source name - | Positional -``` - -`Named { name: Symbol }` does not survive beside these arms (DESIGN §3 replacement migration). Step 2 deletes it first and fixes forward. - -### Who owns the closed set (ruled: A) - -Candidate structural labels come from two layers: - -1. **Core substrate markers**, owned by `v2.std.node` and `v2.std.type_binder`: arrow body and signature order, loop bound and carrier, loop domain (`^loop_domain_edge`, #12548) and the MQ-5 Loop head-reference edge `^loop_realized_declaration_edge` (Core arm `LoopRealizedDeclaration`; at most one, never on for/while; deep-raven-602 S3), match arm pattern and body, cast target, type annotation, type params, type body, type alias, the `v2.std.node_query` projection markers, and `declaration_reference_marker`. This set is language-independent and closed today, so it can be a closed coproduct in `v2.std.node`: `CoreEdgeLabel = ArrowBody | ArrowSignatureOrder | LoopBound | ...`. -2. **Grammar production edges**, per language: `dag_surface_*`, `rust_*`, `ts_*`, `target_model_edge_*`, and the per-language `*_named_edge` helpers that build them. These belong to the language rows in `extdeps/languages/`. Enumerating them in `v2.std.node` would be a layer inversion (DESIGN §3: each upstream has its own authority, and a generic hub may not enumerate products). Adding a language would also widen a core enum. - -Recommended (A): -``` -type StructuralEdgeLabel - = Core { marker: CoreEdgeLabel } // closed, v2.std.node - | Production { grammar: GrammarRef, edge: Symbol } // minted only by the grammar fold from a declared row -``` -Here `Production` is closed per grammar by construction. Its only constructor is the grammar's own projection (`v2.std.grammar` / `v2.compiler.02_parse` `parse_tree_projection_edge`, already a closed roster, is the precedent). Readers compare `Production` values minted from the same row, not `^text`. -Alternative (B): one flat closed enum of every candidate in `v2.std.node`. Rejected for the layer inversion above. -**Ruled (neat-boar-16, 2026-09-28): A.** Core markers live in `v2.std.node`; `Production { grammar, edge }` can be minted only by the grammar fold. Rung 4 is claimed only with a discriminating RED enrolled on the real acceptance path. Otherwise the claim is rung 3. -Open for the cut: whether `Production.edge` can be a row reference rather than a Symbol. That depends on `v2.std.grammar` exposing a row identity type. If it cannot, `Production` is rung 3 per grammar (it is checked when the grammar is admitted), not rung 4. - -### Other judgment calls flagged for review - -- **Hand-built type-node literals** (`^magma_field_op` in `v2.std.algebra`, `effects`, `testgen`, `target_model`, and others) model the AUTHORED field names of modeled records but are spelled like markers. **Ruled: `Authored`.** If those nodes are meant to equal what ingest produces, their names are already wrong (`magma_field_op` ≠ `op`). That is a separate finding. -- **`ReferenceSite.position`** must carry `EdgeLabel` segments, or only Authored segments plus a separate structural context. This is a model change to `v2.compiler.reference_site_collector`, not a rename. **Ruled: it lands in the cut**, because `site_is_import_syntax_mention` is a consumer that needs it. -- **Pending question (adhoc-db43a2ff-e50):** whether the namespace-spine mark belongs in this coproduct as a `Core` marker. The rule is the same as for the other markers: it joins `CoreEdgeLabel` only if it is language-independent and minted by the substrate, not by a grammar row. -- **Consistency with the Loop edge roster (#12548 and MQ-5 S3, deep-raven-602):** the loop domain edge and the Loop head-reference edge are `Core` markers, added ONCE, so the Loop edge roster and this census agree. Whichever PR lands second adds the label to the other roster. If this model lands first, S3 constructs the label through the `Core` arm and never as an authored Symbol. `v2.std.node` `loop_edge_role` (#12548) is the one reader of Loop labels, so it is the single site the cut converts. -- **Symbol-keyed query APIs** (`v2.std.node_query` `find_named_child`, `named_child_lookup`, `named_edge_target_lookup`, and `v2.std.node` `name_occurrences`) split into a structural lookup that takes a `StructuralEdgeLabel` and an authored lookup that takes a `Symbol`. A single Symbol-keyed lookup must not survive. -- **named-args PR-B #12382** (vivid-ram-65): the Named edges for named actuals under Transform are `Authored`. `PositionalPlusOneNamedEdges` for Transform then counts `Structural { Core { CastTarget } }` separately from the authored actuals. - -## Consumers of each new declaration (DESIGN §3c) - -Every declaration below lands in the step-2 cut together with its consumers. None of them lands in this model PR. - -| declaration | consumer, executing route | -|---|---| -| `EdgeLabel.Structural` / `Authored` (replacing `Named`) | every current `Edge.label` reader and constructor. The fail-closed deletion refuses each one until it is dispositioned. `v2.std.node` `content_hash` hashes both arms. | -| `StructuralEdgeLabel.Core` / `CoreEdgeLabel` | `v2.std.node` edge discipline (`kind_edge_discipline`, `arrow_signature_order_label`, the loop bound/carrier checks) and `v2.std.type_binder` `type_binder_labels_conform`, whose behavior-label arm dissolves | -| `StructuralEdgeLabel.Production` | the grammar fold (`v2.compiler.02_parse` `parse_tree_projection_edge`, `v2.extdeps.languages.dag` productions) mints it. `dag_surface_module_header_metadata_edge`, `dag_node_is_module_root_conj` and `d1_edge_names_where_clause` read it. | -| `ReferenceSite.position` segments carrying `EdgeLabel` | `v2.lens.module_graph` `site_is_import_syntax_mention` | -| split structural/authored lookups | callers of `v2.std.node_query` `find_named_child` and its siblings | - -## Census - -[census.md](census.md) keeps only the findings the ruling was made over: the four readers and the non-EdgeLabel `Named` types that are excluded (`std.algebra` `ContainerSource`, `extdeps.formats.spice`). No counts are transcribed (DESIGN §6). The authoritative consumer population is the step-2 deletion itself (DESIGN §3, "the deletion is the census"): removing `Named` makes every dependent refuse under self-host and neat-boar-16's srv1 per-file native census, and each refusal gets one disposition: Structural/Core, Structural/Production, Authored, or arm-only. The largest dependent populations are the per-language `*_named_edge` helpers and the Symbol-keyed lookups in `v2.std.node_query`. Rust `src/v1/stage0` has no hand-written EdgeLabel sites; the generated reflection regenerates. - -## Identity impact (a deliberate change) - -`v2.std.node` `canonical_hash_of_edge_label` gains distinct tags for Structural/Core, Structural/Production and Authored. `Positional` keeps its tag. `label_sort_key` (canonical order) follows. Churn: - -- **Pinned digests:** in `node_hash_protocol_witness_test`, every vector with a named edge changes. The positional and no-edge vectors stay stable, which is the control that only the label arm moved. -- **SCM:** every `object_store` object id derived through `content_hash_of_children` changes. The JSON wire `encode_label` / `decode_named_label` splits its `"named"` tag. No committed fixture carries a `"named"` label. -- **Hermetic fixtures keyed by a content-hash locator** change. Their on-disk store is still to be located before step 2. -- **Invalidate only, nothing pinned:** the test-claim cache digest (`05_eval`), the parse memo grammar digest (`02_parse`), and the bootstrap closure hash. - -## Step-2 controls (the cut) - -1. For each of the four readers, an authored name spelled like its structural label (for example an authored `dag_surface_module_header`) reaches none of them. -2. The real structural labels still route (positive control over the real ingest path). -3. A mutation that re-keys a reader on text makes the control red. -4. Gating: base-vs-head over shape consumers, neat-boar-16's srv1 per-file native census, and self-host. - -## Step 2 as landed - -Arm names differ from the sketch above because `.dag` names are corpus-global and `Structural`, `Production` and `Core`-prefixed spellings already name other types: the arms are `Authored { name }`, `StructuralLabel { label: StructuralEdgeLabel }` and `Positional`, with `StructuralEdgeLabel = CoreMarker { marker: CoreEdgeLabel } | ProductionEdge { language, edge }`. - -- **Root first.** `Named { name: Symbol }` was deleted and every site renamed to `Authored` in the same change; the compiler's exhaustiveness refusals were the census of every reader that had to decide what a structural edge means at its site. -- **Converted, every minter and reader:** the core markers (arrow body and signature order; loop bound, carrier and domain; match arm pattern and body; cast target, type annotation, type params, type body, type alias; declaration reference; field projection base and field) and the dag surface productions the four readers need (module header and its qualified name, where-refinement clause, import decl, import block, import decl block, import decl qualified name). `v2.std.node` `Path` steps and `ReferenceSite.position` carry whole labels. -- **Deviation, stated:** the positional-payload field `"0"` stays Authored. It is consumed as `DeclaredField.name` like every authored field, so making it a marker would fork the field-name authority. -- **Frontier:** grammar-emitted labels no converted reader matches stay on the Authored arm. The population (by minting declaration) and the capability trigger are recorded on `gunbc.recurring_failure_mode` `behavior_named_edge_label_validated_not_constructed`. -- **Identity:** an authored label keeps the tag every Named edge had, so only nodes holding a structural label change identity. The two pinned vectors in `test.claim.node_hash_protocol_witness` that carry converted labels were re-derived by an independent reference implementation that first reproduced their previous values. -- **Coordination:** the construct tag (#12714) landed first, so this cut converted it to `ConstructTagEdge`. `^loop_realized_declaration_edge` (#12550) is not on main; whichever lands second adds its arm. diff --git a/docs/plans/edge-label-coproduct/census.md b/docs/plans/edge-label-coproduct/census.md deleted file mode 100644 index 771acdd1e7a..00000000000 --- a/docs/plans/edge-label-coproduct/census.md +++ /dev/null @@ -1,24 +0,0 @@ -# EdgeLabel `Named` census — scoping findings, not an instrument - -This file keeps only the FINDINGS that need a ruling. The one-off lexical scan that located them is not committed, and neither are its counts: the repo admits no `*.py`, and DESIGN §6 forbids transcribing an instrument's output. The step-2 census is the fail-closed one DESIGN §3 names. Deleting `Named { name: Symbol }` first makes every real dependent refuse loudly, under `gunbc test //gunbc/instruments:self-host` and neat-boar-16's srv1 per-file native census. The dependents that refuse are the population the cut is scoped over. Symbols are cited by module and name. - -## Premise correction: other types with a `Named` arm - -Only three types in the corpus declare a `Named` variant: -- `v2.std.node` `EdgeLabel` (the subject) -- `std.algebra` `ContainerSource = SameAsReceiver | Named { name: String }`: string-method rows in `dag/std/algebra.dag`. Excluded. -- `extdeps.formats.spice` `Named { node: NamedNode }`: spice.dag and its fixtures and tests. Excluded. - -`target_model.dag`, `testgen.dag`, `effects.dag`, `algebra.dag` (src/v2/std), and `body_lowering_fold.dag` have NO other `Named` type. All of their `Named {` hits are EdgeLabel (they import `Named` from `v2.std.node`). - -## The four named readers - -| reader (actual symbol) | file | how it reads | disposition | -|---|---|---|---| -| `dag_surface_module_header_metadata_edge(name: Symbol)` (asked-for "header_metadata_edge") | src/v2/extdeps/languages/dag.dag | Symbol equality against `^dag_surface_module_header`, `^dag_surface_module_header_qualified_name`; callers in symbol_index_fill, namespace_graft, 03_name_resolve, reference_site_collector `reference_sites_in_edge`, 03_resolve and zero_metadata_test — every caller first binds `Named { name: sym }` | becomes Structural arm match: take `StructuralEdgeLabel`, match the two arms; callers match `Structural { label }` instead of binding a Symbol | -| `dag_node_is_module_root_conj` | src/v2/extdeps/languages/dag.dag | fallback arm folds children: `Named { name: sym } => sym == ^dag_surface_module_header` | becomes Structural arm `dag_surface_module_header` (Authored arm → false) | -| `d1_edge_names_where_clause` (asked-for "edge_names_where_clause"; it lives in **src/v2/test/claim/parse/d1_declaration_grammar_parse_test.dag**, not module_graph.dag) | test | `Named { name: n } => n == ^dag_surface_where_refinement_clause` | becomes Structural arm `dag_surface_where_refinement_clause`; production minter is `v2.compiler.body_lowering_fold` (`label: Named { name: ^dag_surface_where_refinement_clause }`) | -| `site_is_import_syntax_mention(site: ReferenceSite)` | src/v2/lens/module_graph.dag | `contains(site.position, ^dag_surface_import_decl / ^dag_surface_import_block / ^dag_surface_import_decl_qualified_name)` — no Named match at all; `ReferenceSite.position` is a Symbol path mixing production and authored segments (`v2.compiler.reference_site_collector` `reference_sites_in_edge`) | requires `ReferenceSite.position` to carry label arms; then becomes a Structural-arm membership test. Not a mechanical rename. | - -Related closed roster already present: `v2.compiler.02_parse` `parse_tree_projection_edge`. It is the precedent for `StructuralEdgeLabel` being a closed coproduct. - diff --git a/docs/plans/enrolment-margin-eval-step-denomination.md b/docs/plans/enrolment-margin-eval-step-denomination.md deleted file mode 100644 index bf0dbaa5d2d..00000000000 --- a/docs/plans/enrolment-margin-eval-step-denomination.md +++ /dev/null @@ -1,194 +0,0 @@ -# Denominating the enrolment margin in eval steps - -Governed by DESIGN §5 (fail-closed, oracles, construction over validation) and §4b -(the guarantee ladder). This plan discharges clause (iv) of the §4b(3) drop -`gunbc.rung_drop` `floor_cost_cpu_regression_at_constant_eval_steps`, which is the -only clause of that row gunbc#11195 left standing. - -## The defect this closes, stated as the drop already states it - -gunbc#11195 moved the required floor's **claim ceiling** off CPU and onto eval steps, -and made CPU observed-only for every claim. It did not move every per-subject budget. -`v2.workflow.floor_enrolment_margin` still derives a 302ms budget from -`required_floor_per_subject_cpu_line_ms` and compares an **observed CPU reading** -against it, so a newly enrolled witness can still be refused on a figure that is a -property of the machine rather than of the claim. - -That is exactly what the drop's restoration trigger forbids, in its own words: - -> NO PER-SUBJECT BUDGET MAY CONSUME A RUN-LEVEL READING - -The trigger is stated at **budget grain and not at denomination grain** deliberately, -and gunbc#11195 records why: an earlier revision quantified over the *primitives* a -cost-fidelity report must cover and said nothing about *which budgets* must stop being -vulnerable, so it could have been discharged by denominating one ceiling while another -per-subject budget kept charging a run-level cost to an arbitrary claim. Denominating -the claim ceiling was one step toward the trigger and is not the trigger. - -## Why the first phase is not the enrolment margin - -`floor_enrolment_margin` consumes `gunbc.floor_cost_distribution` `ClaimCostReading` -directly — `EnrolmentCostReading = EnrolmentCostMeasured { reading: ClaimCostReading } | …`. -Every arm of that coproduct is CPU-shaped: - -``` -type ClaimCostReading - = ObservedCpuReading { cpu_ms } - | RightCensoredCpuReading { cpu_lower_bound_ms, censoring_ceiling_ms } - | CpuLowerBoundWithoutCeiling { cpu_lower_bound_ms } -``` - -So the gate **cannot see a step count at all** until the carrier carries one. The step -count exists today only as `FloorCostRow.eval_steps: Int`, a bare scalar beside the -coproduct, and a consumer reaching for it inherits a defect the corpus has already -filed against itself. - -## The filed stall this phase discharges - -`gunbc.guarantee_stall.eval_steps_outside_the_reading_coproduct_stall` — `current: -OutsideTheLadder`, `ceiling: StructurallyImpossible`, `blocker: ClimbableButUnbuilt`, -population `["gunbc.floor_cost_distribution"]`. Its subject, in its own words: the bare -`Int` means a right-censored row's observed-steps-**before-censor** and a completed -row's **performed** steps share one type and one constructor, so a fold may compare -them for equality and manufacture an equal-work cohort out of two observations that -measured different things — a **half-applied construction**, because the same file -already models the CPU half correctly and one field did not follow the decision its -sibling already made. - -Its `next_rung_trigger` is this phase, verbatim: *eval_steps moves INSIDE the -`ClaimCostReading` arms, so an observed step count and an observed-so-far-before-censor -count are reached through different constructors and no fold can compare them.* - -This plan therefore **does not invent its own first phase**. It executes a trigger the -corpus declared before this lane existed. - -## Phase 1 — the carrier (the model) - -``` -type ClaimCostReading - = ObservedCpuReading { - cpu_ms: Millisecond - eval_steps: EvalStepCount - } - | RightCensoredCpuReading { - cpu_lower_bound_ms: Millisecond - censoring_ceiling_ms: Millisecond - eval_steps_before_censor: EvalStepCount - } - | CpuLowerBoundWithoutCeiling { - cpu_lower_bound_ms: Millisecond - eval_steps_before_censor: EvalStepCount - } -``` - -with two accessors mirroring the CPU pair the file already carries, and named for the -same distinction: - -- `observed_eval_steps(reading) -> EvalStepCount?` — `Present` on the observed arm - only. The point question. A bound cannot reach a site that treats it as performed - work without the caller writing an `Absent` arm. -- `eval_steps_at_least(reading) -> EvalStepCount` — total over all three arms. The one - question a censored row can answer soundly: *this claim performed at least N steps*. - -`FloorCostRow.eval_steps: Int` is **deleted**, not deprecated. DESIGN §3's replacement -migrations cut over at the root, and a surviving bare `Int` is the attractor that rule -exists to prevent: while it stands, every nearby question is answered in its vocabulary. - -### Why inside the arms rather than a sibling coproduct - -A sibling `EvalStepReading` beside `cost: ClaimCostReading` would satisfy the trigger's -*letter* — two constructors, no bare `Int` — while leaving the incoherent state -constructible: a row claiming an **observed** CPU reading beside a -**before-censor** step count. Whether the claim reached its end is ONE fact about ONE -occurrence, and it discriminates both readings. Carrying the step count inside the arm -makes disagreement unconstructible, which is the stall's declared ceiling -(`StructurallyImpossible`) rather than one rung below it. - -This is not the clock fusion `std.measure` `measure_clock_basis_note` forbids, and the -distinction is worth stating because gunbc#11195 was reworked twice over exactly it. -Fusion is *comparing* magnitudes read from different clocks. This is *co-locating* -three observations of one occurrence under the discriminator they genuinely share. No -arm compares a step count to a millisecond. - -### Phase 1 blast radius, measured rather than estimated - -Measured on `origin/main` at `3a0f2d4b585`: - -| surface | sites | -| --- | --- | -| `ObservedCpuReading {` | 41 | -| `RightCensoredCpuReading {` | 21 | -| `CpuLowerBoundWithoutCeiling {` | 11 | -| `FloorCostRow {` | 41 | - -across 7 `.dag` files (`floor_cost_distribution` and its witness test and instrument, -`floor_enrolment_margin` and its test, `floor_cost_debt_admission` and its test) plus -the seed mirror in `required_floor_runner.rs`, `cli_run.rs` and `v1_interpreter.rs`. - -The type change admits **no partial state** — every constructor site moves in the same -commit or the corpus does not compile. That is why this is its own phase and why this -PR opens with the plan rather than a half-migration. - -**The step value at each site is authored, not mechanical.** A fixture's step count is -part of what the fixture asserts, so these are not a `sed`. Fixtures whose subject is -the CPU half take a step count that is declared as incidental beside the reading they -actually exercise. - -### Phase 1 evidence - -- **RED** — a fold that compares a performed step count to a before-censor one must - become unwritable. The discriminating probe is the *source* handed to the compiler by - a fixture, per §4b: the invalid program is authorable there even once it is - unwritable in the accepted corpus, and declining the fixture would be - specification-without-execution. -- **CONTROL** — `observed_eval_steps` answers `Absent` on both bound arms and `Present` - on the observed one, executing, so the accessor is not a decoration. - -## Phase 2 — the enrolment margin consumes it - -`floor_enrolment_margin_budget_ms` becomes an `EvalStepCount` derived through the **same** -pinned calibration the claim ceiling uses (`v2.workflow.floor_eval_step_calibration` -`eval_step_budget_for`), from a declared policy in milliseconds of work. It is not a -second calibration and not a literal: §5's oracle rule admits the budget only because it -is grounded in a controlled fixture, and a second rate would be a §3 fork of that -authority. - -`EnrolmentMarginStanding` keeps its arms and changes their denomination: -`EnrolmentWithinMargin` / `EnrolmentOverMargin` carry `EvalStepCount`, and the CPU -reading is **carried beside the verdict as an observation** rather than deciding it — -the same shape gunbc#11195 gave the claim ceiling. - -`EnrolmentBoundWithoutCeiling` survives unchanged in meaning: a preempted row's steps -are a lower bound with no ceiling, exactly as its CPU is. - -### Phase 2 evidence - -- **RED** — a claim whose eval steps exceed the enrolment budget refuses. -- **CONTROL** — a claim far over the 302ms CPU line with in-budget steps is admitted, - with the CPU observation recorded. This is the same pair gunbc#11195 enrolled for the - claim ceiling, and it must be re-authored here rather than cited: a different budget - is a different subject. - -## Phase 3 — retire the CPU line, or say why it stays - -`required_floor_per_subject_cpu_line_ms` exists only because per-subject decisions still -read a CPU clock. Since gunbc#11700 `v2.workflow.floor_cost_debt_admission` compares nothing -(a typed admission is identity and reason); the consumers are both in -`v2.workflow.floor_enrolment_margin`: the margin budget, which must sit below the line, and -the live Roster ground (`enrolment_declared_measured_standing`), which admits only a reading -over it. When Phase 2 denominates the margin, the Roster ground is the remaining consumer, and -Phase 3 decides that one: either it is denominated too and the symbol is deleted with its own -dissolution condition discharged, or the symbol survives with a population of exactly one, -stated. - -**Only when no per-subject budget reads a run-level figure does the drop's clause (iv) -retire**, and the drop is retired by its trigger and by nothing else. - -## What this plan deliberately does not do - -- It does not re-arm a CPU ceiling anywhere. gunbc#11195's evidence stands: three - prose-only heads measured the same floor at 36.7s / 40.8s / 44.7s of summed CPU, and - one identity at an identical 2884 eval steps read 34ms, 455ms and 511ms. A CPU line - refuses on the environment, not on the claim. -- It does not normalise per runner, and does not enrol slow identities as cost-debt - rows. Both are refused designs of record, for the reasons the floor authority states. diff --git a/docs/plans/execution-cell-contract.md b/docs/plans/execution-cell-contract.md deleted file mode 100644 index 119236fde94..00000000000 --- a/docs/plans/execution-cell-contract.md +++ /dev/null @@ -1,92 +0,0 @@ -# The execution-cell contract (owner directive 2026-09-25) - -Supersedes the "crash-era residue" framing of the Group B start failure. - -## The ruling, verbatim - -Treat the Group B failure as a missing execution-generation dependency, not as -incidental residue. The new unit's cleanup hooks are downstream of systemd start -admission, so they cannot repair start-rate state or orphan resources that -prevent the start. Move prior-generation inventory, quiescence, disposal, -manager-state settlement, and readback ahead of preparation. - -Prepare every rank before activating any. A four-rank arm is one cohort: -preparation failure starts none; partial activation rolls back all started -members; success requires one exact generation across every rank. - -Extract this as a realization-neutral execution-cell contract. Keep -systemd/Docker as the current Group B realization and adopt Firecracker next for -disposable harness workers. Do not claim that microVMs provide idempotency by -themselves — the host controller must still own operation identity, CAS, -resource conservation, disposal, and readback. - -Preserve the current wet run as measurement evidence only. Even if it succeeds -after manual cleanup, prove the repaired convergence route against planted -failed-unit and orphan-container residue before restoring persistent service. - -## What this means structurally - -### The phase order is law - -``` -inventory(prior generation) - → quiescence(prior generation) - → disposal(prior generation) - → manager-state settlement (rate counters, failed state, unit-table) - → readback(the prior generation is actually gone) - → preparation(new generation, every member) - → activation(cohort) -``` - -Anything in the new generation's cleanup hooks is unreachable for failures of -start admission; cleanup of the PRIOR generation must precede preparation of -the new one. The Sep 25 failure is the canonical witness: start-rate counters -and an orphan container blocked admission before the unit's own ExecStartPre -could run. - -### Cohort semantics - -An N-rank arm is ONE execution cell, not N units: - -- preparation is per-member but all-or-nothing: any member's preparation - failure activates none; -- activation is sequenced with rollback: a member that fails after others - started disposes the started members; -- success is a single exact generation (identity of the rendered intent) - observed across every member — never "three ranks on generation G, one on - G-1". - -### Realization neutrality - -The contract names phases, not tools. systemd/Docker is the current Group B -realization; Firecracker is the next realization for disposable harness -workers. A realization supplies the mechanisms for inventory / quiescence / -disposal / settlement / readback / prepare / activate. Idempotency is NEVER a -property of the substrate: the host controller owns operation identity, -compare-and-set on admission, resource conservation (nothing leaks, nothing is -double-consumed), disposal, and readback, for every realization. - -### Evidence discipline - -The 2026-09-25 wet run (manual pre-cleanup by the operator session) is -measurement evidence only — its memory-profile receipts feed the fit proof, -but it does NOT discharge the convergence-route proof. Before persistent -service is restored, the repaired route must pass against PLANTED failure -state: a failed-unit (tripped start-rate counter) and an orphan container, -planted per host, with the route required to inventory, settle, and read back -their absence before activation. That planted-residue witness joins -`fleet-slot-retirement-wet-proof` as a reopening precondition. - -## Work queued from this ruling - -1. Execution-cell contract module (realization-neutral): the phase vocabulary, - cohort type, operation identity + CAS, conservation laws. -2. systemd/Docker realization of the contract, replacing the current apply - bracket's prepare-then-start shape (shared by the experiment bracket and - the persistent serving apply). -3. Planted-residue witness: failed-unit + orphan container, per host, repaired - by the route itself. -4. Conjunctive reopening gate amendment: + execution-cell route proven against - planted residue. -5. Firecracker realization for disposable harness workers (the microVM node), - owning identity/CAS/conservation/disposal/readback in the host controller. diff --git a/docs/plans/extdeps-disputed-row-classification.md b/docs/plans/extdeps-disputed-row-classification.md deleted file mode 100644 index 60f47c242b6..00000000000 --- a/docs/plans/extdeps-disputed-row-classification.md +++ /dev/null @@ -1,265 +0,0 @@ -# Disputed extdeps rows: a two-axis classification template, tested on the adjudicable subset - -Operator ruling (2026-10-05): placement batches pause because about half of -placements were rejected on review — the classification rule is not yet ready to -be used as a template. This document records **two judgments per case**, because -the disputes of the last several batches were two different questions being -answered with one label: - -- **Semantic standing** — who owns the semantic information the declaration - carries: *exact upstream fact* (a versioned upstream artifact states the - surface and the declaration transcribes it field-for-field), *shared formal - semantics* (recorded somewhere nameable but not one product's artifact), - *product policy* (the product's own choice: dispatch algebras, serializer - policies, normalizing tables, partial grammars, transports), or *unsupported* - (nothing states it and no product purpose holds it up). -- **Consumption standing** — per DESIGN 3c's three honest states: *executing* - (consumed by execution in this tree), *declared frontier with trigger* (a - named consumer lands in a named later change, trigger stated beside it), or - *dangling* (nothing consumes it — red regardless of how well modeled it is). - -The **disposition follows from both axes together**: scope (fact + executing), -re-home or re-label (policy + executing), delete or name-a-consumer (fact or -unsupported + dangling), repair (a mixed module whose subjects must be separated -before either axis can be judged honestly). No single axis decides. - -## The evidence must fit the surface - -A standing is only as good as its evidence, and the evidence has to be of the -kind the surface admits: - -- **Data shapes are judged by field/constructor diff** against the cited - artifact. If the declaration adds, drops, reshapes or renames anything the - artifact does not state, the standing is not exact-upstream-fact — the diff - is how the judgment is made, not a formality after it. -- **Decision procedures are judged by behavioural conformance** to the - artifact's stated rule. A precedence rule, an ordering, a matching policy: the - question is whether the implementation orders/matches the way the artifact - says on its cases — not whether its fields resemble the artifact's. A field - diff cannot establish conformance, and a passing field diff cannot excuse a - failing behaviour. -- **Consumption is established only by an executing route** — a fold, an entry - point, a realization, an emitted artifact that reads the declaration. A grep - hit names a candidate site; it cannot establish that the site executes. A - name-keyed pattern establishes nothing: a predicate that reads no module - content is a classification, not a consumer. A mirror's definition of a - declaration is not a consumer of it. A witness that exercises a call inside a - test is not a production route. -- **Both consumption labels are positive claims at this grade.** `executing` - asserts a route exists; `dangling` asserts no route exists across the decoded - surfaces. Grep-grade evidence supports neither — it supports **unadjudicated: - grep-grade only**, and that is what this document records wherever its own - evidence is grep-grade. The one exception in the sample is `semver_scheme`, - whose `dangling` label below is a recorded ruling of the side chat (review - 5424597802), kept with its evidence grade attached and marked as a ruling, - not an adjudication. - -**Misfile shapes (the recurring review failure).** A citation can be live and -real and still not own the declaration. Observed, each a real pulled row: a -tool's CLI citing the standard the tool implements; a repo-normalized table -citing the spec it normalizes; a dispatch algebra citing one tool it drives; a -repo extension inside an otherwise transcribed enumeration silently breaking the -predicate; a repo plan citing fragments of a CLI grammar it does not fully -reproduce. - -## The recorded cases - -Seven cases with both axes recorded. Semantic standings were decided against -the cited artifacts (the instruments those surfaces admit); every consumption -standing below is grep-grade, so each carries its evidence kind in place — -the four `unadjudicated` labels are this document's own grade, and the three -`dangling` labels are recorded rulings of the side chat (reviews 5423944315 and -5424597802), kept with their grade attached. Dispositions that would change -with the consumption walk are marked provisional and name what would settle -them. - -**1. `docker/cli.dag` — `DockerCreateSpec` and its flag types.** -Semantic standing: **product policy.** The type is a repo-normalized plan: its -flag grammars are partial (Docker's CLI reference owns `--restart` fully — -`on-failure:N` cannot be expressed by `DockerRestartPolicy` as written, cli.dag -lines 32-39 — and `DockerNamespaceMode` covers only host/private, line 43), so -it borrows CLI grammar fragments rather than transcribing the cited surface. -Consumption standing: **unadjudicated: grep-grade only** — candidate consumer -sites (grep hits): the `dag/gunbc/spark/*` serving modules and -`docker/container_inspect.dag` name `DockerCreateSpec`; no execution-grade walk -has run. -Disposition: **repair** (consumption-independent). Either complete the grammars -field-for-field against Docker's CLI reference — after which the (a) predicate -is judged again on the diff — or re-label the plan as repo vocabulary in the -product layer. What the walk decides is the module's longer-term standing -(kept-and-consumed vs delete-or-declare); that waits. - -**2. `bmc/openbmc_operation.dag` — `OpenBmcOperation`.** -Semantic standing: **product policy.** Owner: this repo — the dispatch algebra -over file ops, systemctl, busctl, jq, mkdir/rm/cp, sleep is our vocabulary of -what a BMC operation is; the busctl man page owns only busctl's argv semantics. -Consumption standing: **unadjudicated: grep-grade only** — candidate consumer -sites (grep hits): `openbmc_fan_control` and `openbmc_password_ssh_transport` -name it. -Disposition: provisional **re-home** to the product layer. The algebra is -product vocabulary regardless of the walk — that part is consumption- -independent; what the walk decides is whether the row leaves as re-homed -(kept-and-consumed) or as deleted. The internal consumers, if the walk confirms -them, keep depending on it — they should, it is the product's own vocabulary. - -**3. `languages/json/grammar.dag` — `json_production_rows`.** -Semantic standing: **product policy.** Owner: this repo — the columns -(`rfc_section`, `lhs`, `rhs`, `value_variant`) are a repo-normalized grammar -whose `lhs` names a local `literal` nonterminal and whose `value_variant` maps -to repo AST names; the raw RFC 8259 production strings embedded in the rows are -upstream fragments, not the surface the table models. -Consumption standing: **dangling, as ruled** (side chat, review 5423944315; -grep-grade: no production consumer found; the parse path is `json/parse.dag`, -scoped separately). -Disposition: **delete or re-home** — the table is a documentation artifact in -code; both dispositions are live and the call belongs to the operator. - -**4. `languages/markdown.dag` — `commonmark_gfm_spellings`.** -Semantic standing: **product policy.** Owner: this repo's serializer policy — -choosing which dialect's spellings to emit over CommonMark 0.31.2 and GFM -extensions (tables, task lists) is a product decision; no single upstream owns -"both dialects at once." -Consumption standing: **unadjudicated: grep-grade only** — candidate consumer -sites (grep hits): `truth_table_projection` and the markdown tests name the -spellings. -Disposition: **re-label** as product policy (consumption-independent); the row -stops requesting a citation that cannot exist. What the walk decides is kept- -vs-deleted; the serializer behavior itself is untouched. - -**5. `tools/curl.dag` — `curl_cli_tool` and its policy rows.** -Semantic standing: **mixed — repair required.** The exit-code table (6/7/28/35/ -52/56) is an exact upstream fact owned by curl's own man page, which the anchor -cites correctly. The pinned minimum version (>= 7.68), the localhost timeout -rows, and the apt install realization are product policy — choices, not curl -facts. -Consumption standing: **unadjudicated: grep-grade only** — candidate consumer -sites (grep hits) name the tool rows. -Disposition: **repair — split** the policy rows out from under the -external-authority anchor (consumption-independent); the CLI-fact side is then -re-judged field-for-field against the man page on its own diff. Kept-vs-deleted -waits on the walk. - -**6. `extdeps/ebay/mock_corpus.dag` — dangling repo data; the deletion-safety -check.** -Semantic standing: **unsupported.** The module is repo-authored -`PublishedMockCase` hermetic-replay data; no upstream states it. -Consumption standing: **dangling, as ruled** (side chat, review 5424597802). -The census (2026-08-22, re-derived 08-26) -classifies it STILL-UNCONSUMED, and the v1 mirror's -`ends_with(".mock_corpus")` predicate (in -`external_authority_is_clean_tree_roster_excluded_for_module_path`, -`src/v1/stage0/src/cli_run/external_authority.rs` line 211) does not change -that: the predicate reads no module content, so it is a classification, not a -consumer. -Disposition: **delete vs declared future consumer** — operator's, between -deletion and re-homing to a fixture namespace with a named trigger. The -convention scan survives only as a **deletion-safety check** (before deleting, -confirm no compiled logic keys on the module's name in a way that reads its -content), not as consumption evidence — and it is the check the census's -mention scan does not collect. - -**7. `dag/extdeps/docker/container_stats.dag` — dangling and not -field-for-field.** -Semantic standing: **product policy over upstream fragments.** It adds a -repo-authored `cpu_percent` field and reshapes `networks`, so it is not the -Engine API's stats payload; owner of the real surface: Docker's Engine API -stats reference. -Consumption standing: **dangling, as ruled** (side chat, review 5423944315; -grep-grade: no production caller; the #13304 review confirmed it -independently). -Disposition: **delete or name a consumer** (DESIGN 3c). Repairing the -transcription — dropping `cpu_percent`, restoring the API shapes, re-diffing — -is worthwhile only once a consumer or a declared frontier with a trigger -exists; on today's tree the honest move is deletion or a named trigger. - -## Set aside — not adjudicated at execution grade - -Five cases from the sample could not be adjudicated on both axes now. They are -recorded so the work is not lost, with their consumption standings marked -**unadjudicated: grep-grade only** — grep hits name candidate sites and cannot -establish that the site executes (the one recorded ruling in this section, -`semver_scheme`'s dangling, is attributed above). - -- **`cloud/gcp/errors.dag` — `GcpRpcCode`**: semantic standing **non-exact**. - The module adds `GcpRpcCodeOther { raw: NonEmptyStr }` (errors.dag line 32) - beside the 17 transcribed `google.rpc.Code` values, so there is no - exact-upstream subject until the enumeration is split from the repo variant - and the enumeration alone is diffed against - `cloud.google.com/apis/design/errors#http_mapping`. Consumption: - unadjudicated (grep-grade: only its grounding witness test references the - module). -- **`crypto/hash.dag` — `HashAlgorithm`/`Digest`**: **FIPS-derived fragments - plus local carriers.** `Sha256`/`Sha512` is not FIPS 180-4's family naming — - the carriers are repo-local and sit beside `sha256sum(1)` machinery (coreutils - manual owns the tool). There is no exact-upstream subject until the FIPS - facts are split out and diffed against FIPS 180-4, and the verifier machinery - is judged against the coreutils manual. Consumption: unadjudicated - (grep-grade: the sccache pin and live-deploy paths name the verifier). -- **`ssh/session.dag` — `service ssh.Session`**: **mixed subjects.** The module - quotes ssh(1)'s exit-255 contract (an exact upstream fact owned by the - OpenSSH client manual — not RFC 4254, which owns channel messages) but also - carries repo transports, session records and mocks. Until the subjects are - separated (one module per authority), neither side's semantic standing can be - judged. Consumption: unadjudicated (grep-grade: repo consumers name the - machinery subjects). -- **`version/semver.dag` — `semver_scheme`**: an **upstream-owned decision - procedure** (semver.org section 11 precedence) whose fitting evidence is - **behavioural conformance** — does `compare` order identifiers the way §11 - says — not a field diff. That conformance has not been established; the - dispatch was lexical (`semver_identity_compare`), which is the recurring - failure mode filed as - `a_citation_attests_a_decision_rule_the_code_does_not_implement` (#13235). - Consumption standing: **dangling, as ruled** — the side chat's ruling - (review 5424597802): only the witness test calls `semver_scheme.compare` - (`extdeps_version_semver_witness_test.dag`), and the v1 stage0 mirror defines - `semver_scheme()` — a definition is not a consumer. Recorded with its - evidence grade attached: under this document's own rule the witness-only - grep supports `unadjudicated`, so the ruled label is kept as a ruling, and - any delete-or-scope decision on this module should not fire until an - execution-grade walk confirms it. Whether the type family passes a field - diff against semver.org's grammar is likewise unverified and not asserted - here. -- **The `github/*` family**: **dropped from this document.** The per-row rule - (each surface judged against its specific REST page, field-for-field; each - row's consumption its own) is carried as a rule, but no row of the 19 was - adjudicated here — none has had a field-for-field diff or an execution-grade - consumer walk run for it in this work. - -## What this template changes for bulk work - -1. **Two judgments, two instruments — and the instrument must fit the - surface.** Field/constructor diffs for data shapes; behavioural conformance - for decision procedures; an executing route for consumption. An anchor alone - decides nothing: an anchor is exactly the thing product-policy rows can - borrow, and a grep hit, a mirror definition or a name-keyed pattern is - exactly what a dangling row can borrow. -2. **Dispositions are derivable, not argued** — from admissible evidence only: - fact + executing → scope; fact + dangling → delete or declare a trigger; - policy + executing → re-home or re-label; unsupported + dangling → delete - (safety-checked); mixed → repair by splitting subjects, then re-judge each - side. Where the evidence grade is below the axis, the axis is recorded - unadjudicated — and a disposition that would depend on it is recorded as - provisional, naming what would settle it (the execution-grade walk or the - fitting diff), rather than fired on the lower-grade evidence. -3. **Shared formal semantics stays a discipline.** Among the cases recorded - here, none finally landed there. That is a statement about this sample, not - about the frontier. The procedure stands: when a row looks shared, hunt for - the recording source; if none exists, the row is product policy or - unsupported, never settled as shared by default. -4. **Unconsumed is a delete-or-declare decision, not a scope** (DESIGN 3c) — - and "unconsumed" itself is only decided at the evidence grade above: a row - whose consumption is unadjudicated is not thereby dangling, but it is not - scopeable either; the execution-grade walk has to run first. - -## Verification status of this document - -Every consumption claim above is **grep-grade**: no executing route has been -established at identity grade for any case in this document, so the four -`unadjudicated` labels are the grade's honest reading and the three `dangling` -labels are recorded rulings of the side chat with that grade attached — none -of them is presented as execution-established. The semantic standings of the -recorded cases were decided against the cited artifacts (the docker/cli line -numbers are quoted from the module); standings in the set-aside section are -recorded with their missing evidence named. The census numbers are its own (2026-08-22 / re-derived 2026-08-26) and -carry that clock. No code, no tsv, and no roster is changed by this PR; it is -a classification template, and bulk work waits on the operator's ruling on it. diff --git a/docs/plans/fabric-switch-100g-convergence-gap-analysis.md b/docs/plans/fabric-switch-100g-convergence-gap-analysis.md deleted file mode 100644 index 77d3ff8d71b..00000000000 --- a/docs/plans/fabric-switch-100g-convergence-gap-analysis.md +++ /dev/null @@ -1,237 +0,0 @@ -# Fabric switch 100G convergence — gap analysis - -Subject: bringing the eight DGX Spark fabric legs on the MikroTik CRS812-8DS-2DQ-2DDQ-RM from -50GBASE-CR2 to 100GBASE-CR2, end to end, by execution rather than by hand. - -Authorities read: `extdeps.mikrotik.crs812`, `extdeps.ethernet.link_mode`, -`gunbc.spark.fabric_switch_desired`, `gunbc.spark.fabric_switch_observed`, -`gunbc.spark.fabric_switch_assessment`, `gunbc.spark.managed_access_bootstrap`, -`gunbc.spark.grant_privileged_operation`, `gunbc.fleet_ssh_locus`, -`product.breakout_leg_programming`. - -## 0. The standing observation, and what changed under it - -`gunbc.spark.fabric_switch_observed` records the legs as QSFP28-coded — extended compliance byte 192 -= `0x0B`, byte 116 = `00h` — so nothing attests 100GBASE-CR2, and the ConnectX-7 derives -`50G_2X` and refuses `100G_2X` (mstlink opcode 16). - -**The operator re-flashed the legs with an FS BOX V4 on 2026-09-17.** That invalidates the EEPROM -half of the observation above; it does not replace it, because nothing has re-read the bytes. - -Read live on 2026-09-17 against `spark-a3ee` (192.168.1.226) as `gunbc-automation`: both fabric -netdevs (`enp1s0f1np1`, `enP2p1s0f1np1`) are **up, autoneg on, 50000Mb/s**. - -That reading is **not** evidence the re-flash failed, and must not be recorded as such. Two causes -are live and unseparated: - -1. the coding did not change, or -2. the coding is correct and **nothing has forced the mode on either end** — - `crs812_passive_dac_requires_forced_mode` is `true`, and the host is currently on autoneg. - -The discriminator is the EEPROM bytes plus the NIC's supported-speed set. Neither is readable today -(§1). Establishing which cause holds is the **first** work item; everything downstream is premised -on it. - -> **SUPERSEDED 2026-09-17 (see G12).** This §0 was written before the read grant landed and the -> bytes were read. They have since been read: byte 192 = `0x40` (attests 100GBASE-CR2) and the NIC -> reports `Supported Cable Speed: 100G_2X`. So cause (1) is ruled out — **the re-flash took** — and -> "the first work item" is done. The current standing fact lives in **G12** and the actuation log; -> the two paragraphs above are retained as the original framing, not a current open question. The -> live blocker is now the switch-side QSFP-DD end, not the host coding. - -## 1. Gap: the host end cannot be read or set - -`sudo -n -l` on spark-a3ee, 2026-09-17, shows `gunbc-automation` holds exactly: - -``` -(root) NOPASSWD: /usr/bin/loginctl enable-linger gunbc-automation -(root) NOPASSWD: /usr/bin/systemctl reboot -``` - -So `ethtool -m` (EEPROM read), `ethtool -s` (forced speed) and `mstlink` (supported-speed set, -`100G_2X` enable) are all unreachable by the principal the fleet runs as. - -Missing, in dependency order: - -| # | Gap | Home | -|---|---|---| -| 1.1 | No `extdeps` authority for the `ethtool` binary — name, path, verbs | new `extdeps/ethtool/` | -| 1.2 | No `extdeps` authority for `mstlink` / the MFT tools | new `extdeps/mellanox/` | -| 1.3 | `SparkManagedGrant` has no arm for a module read or a link-mode set | `gunbc.spark.managed_access_bootstrap` | -| 1.4 | No disclosure standing for what `ethtool -m` returns | same, `spark_managed_grant_disclosure_standing` | - -On 1.4: the existing arms are `GrantReturnsNoHostData` (reboot) or `GrantDisclosureUnestablished` -(container inspect, which is why that grant is **desired but not installable**). `ethtool -m` returns -module vendor / part / serial and the EEPROM page — host data, but with no analogue of `Config.Env`. -It needs its own classification; it should not borrow `GrantReturnsNoHostData`, and it is not the -hard case `InspectServingContainer` is. - -**A read grant and a set grant are two grants, not one.** Reading the EEPROM is what settles §0; -forcing the mode is an actuation on a live fabric. They should be installable independently so the -discriminating read does not require authorizing the mutation. - -## 2. Gap: the switch has never been read - -Better than expected — the REST surface is already modeled in `extdeps.mikrotik.crs812`: -`routeros_rest_authority`, and paths for `/rest/interface/ethernet`, `/rest/interface/ethernet/monitor`, -`/rest/ip/neighbor`, `/rest/system/identity`, plus `crs812_factory_address` (192.168.88.1/24) and -`crs812_factory_credential_location`. - -And the assessment vertical is genuinely complete: `gunbc.spark.fabric_switch_assessment` carries -intent, observed, per-lane divergence with causes, typed refusals, expectation-driven unknowns, and a -`GoalInspection`. `gunbc.spark.fabric_switch_desired` correctly derives the desired lane speed from -the cable plant rather than declaring it, and refuses rather than degrades when the plant does not -converge. - -**The gap is the transport, and it is total.** `fabric_switch_subject()` passes `readings: []`. -`observe_fabric_switch_attempt` takes readings as a **supplied** argument. Nothing in the corpus has -ever performed an HTTP request against the switch, so today every lane resolves to -`Crs812LaneUnknown` and the assessment is structurally incapable of returning anything else. - -| # | Gap | Home | -|---|---|---| -| 2.1 | No RouterOS REST **transport** — nothing turns a modeled path into a request | new realization handler | -| 2.2 | No producer of `List` from a live response | `gunbc.spark.fabric_switch_observed` or a new observer | -| 2.3 | No management address for the switch (only `crs812_factory_address`) | `gunbc.fleet.fleet_intent_network` | -| 2.4 | No credential standing for the switch | new, mirroring `gunbc.spark.bootstrap_credential` | -| 2.5 | No apply/actuation path — assessment can diverge but nothing converges | new, after 2.1–2.4 | - -Note the ordering constraint from DESIGN §3d: selection precedes convergence, and the assessment -fold must not grow a realization. 2.1 is a **bound handler**, peripheral to the interface — not an -edit to `assess_fabric_switch`. - -## 3. Gap: the switch credential - -`crs812_factory_credential_location` already records that the credential is on the underside label, -user `admin`. What is missing is a **carried** `SecretRef`. - -Per the operator ruling of 2026-08-07 recorded in `gunbc.spark.grant_privileged_operation`, secret -names are **carried, never derived** — a computed name makes the secret namespace a function of how a -host is spelled today. And a bare `SecretRef` asserts a secret exists, which is why -`SparkCredentialStanding` models `CredentialMaterialized` against `CredentialMaterializationPending`. - -Minted now so the operator can upload against an exact name, in project `gunbai-secrets` -(`fleet_secrets_gcp_project`), following the established kebab convention -(`bmc-srv3-admin`, `spark-administrator-password`, `fleet-automation-ssh-key`): - -- **`fabric-switch-factory-admin`** — the factory label credential, treated as **one-time - authorization material** exactly as `OperatorBootstrapCredentialRequired` treats the Spark - bootstrap admin. Not a serving credential, not retained. -- **`fabric-switch-admin`** — the rotated credential the fleet actually runs under, generated during - bootstrap. - -Bootstrap is therefore **not** "log in with the sticker password". It is: authorize once with -`fabric-switch-factory-admin`, mint and store `fabric-switch-admin`, and leave the factory credential -dead. Leaving the device on its factory password would be a standing exposure the model would be -silent about. - -These rows land **with their first consumer**, not before — a `SecretRef` nothing reads is the -dangling declaration DESIGN §3c makes red. - -## 4. What is NOT a gap - -Worth stating so the work does not re-invent it: - -- the desired-speed derivation (cable-plant-driven, refuses rather than degrades) — done; -- the divergence / unknown / refusal algebra over lanes — done; -- lane interface naming, lane-speed and FEC wire encodings, the forced-mode fact — done; -- the SSH credential locus and known-hosts anchor for the host end — done, and verified working; -- the human cost of the re-flash, and the part that removes it (`extdeps.transceiver.fs_145681`) — done. - -## 5. Sequence - -**A. Settle §0 before building anything in §2.** Land 1.1 + 1.3 + 1.4 (read grant only), install it, -read bytes 116 / 192 / 222 and the mstlink supported set off a re-flashed leg. One read decides -whether the fabric work proceeds or returns to the cable plant. - -**B. If the coding attests the mode:** the switch address and `fabric-switch-factory-admin` upload, -then 2.1–2.4 — read the switch for the first time, which turns eight `Crs812LaneUnknown`s into real -readings and makes the existing assessment say something. - -**C. Then converge,** both ends together: 1.2 + the set grant on the host, 2.5 on the switch, forced -100GBASE-CR2 with matched FEC. - -**D. Re-read and confirm** `100G_2X` actually enabled and the link at 100000Mb/s, then update -`gunbc.spark.fabric_switch_observed`. Per that module's own standing, this is an **experiment**: only -the post-change read establishes anything about the copper. If the legs are correctly recoded, both -ends are forced, and the link still will not come up at 100G, `extdeps.ethernet.link_mode` is explicit -that operating at 50GBASE-CR2 never certified 100GBASE-CR2 — that outcome is a real possible answer, -not a failure of the procedure. - ---- - -## 6. Modeling gaps & incompatibilities found during actuation (2026-09-17) - -Discovered by actually driving the switch and hosts (see fabric-switch-actuation-log.md). -Each is a place the `.dag` model does not yet express something the real path required. - -### Switch model (extdeps.mikrotik.crs812 / gunbc.spark.fabric_switch_*) - -- **G1 — no autonegotiation dimension.** The lane model carries speed and FEC but NOT - auto-negotiation on/off. Copper (BASE-CR) bring-up is entirely governed by autoneg - state; the whole experiment turned on it. `Crs812LaneIntent` needs an autoneg field. -- **G2 — the FEC enum names FEC by RouterOS label, not by codeword, and the correct 100G - codeword is UNWITNESSED.** `Crs812FecMode = FecAuto|FecOff|Fec74|Fec91` carries RouterOS's - strings, not the RS codeword (528,514 vs 544,514) each implies — a modeling nicety. Two - §4d errors to NOT repeat: (a) an earlier head asserted 100GBASE-CR2 needs RS(544,514) from - the IEEE 50G-PAM4 lane FEC and concluded the CRS812 might not expose it — an inference - stated as a fact about this switch, retracted; (b) a later head asserted "MikroTik - documents fec91 for 100G-baseCR2" (from review, no cited vendor document) and on that - strength deleted the converge's FEC-capability refusal arm — the same move in the other - direction, also retracted. NEITHER codeword claim is grounded. What IS grounded: the eight - fabric legs are OBSERVED running fec-mode fec91 today (at 50G). So `fec91` is the desired-FEC - CANDIDATE (the FEC the plant already runs), the converge KEEPS its refusal arm rather than - assuming the candidate trains 100G, and grounding the codeword needs a cited MikroTik doc, - not an annotation. -- **G3 — the live link OUTCOME is unmodeled.** The assessment models intent-vs-observed - lane divergence, but the observed reading needs: negotiated speed, link state - (up / polling / down / auto-init-failed), and FEC-locked, read from BOTH ends. RouterOS - `status` (link-ok/auto-init-failed) and `eeprom-checksum` are unmodeled. -- **G4 — a link is two-ended; the model treats the switch lane as the subject.** The - switch reporting `link-ok/100Gbps` is a LOCAL claim; the host was in `Polling`. Link-up - is a bilateral fact requiring both ends to agree. The observed model joins both ends for - the CABLE receipt but not for the live LINK state. -- **G5 — QSFP-DD (switch-side) module is unmodeled.** The switch reads the QSFP-DD end, - which is CMIS/SFF-8024, NOT the SFF-8636 the host QSFP56 end uses. `sff8636_*` decoders - don't cover it. The `eeprom-checksum: bad` on the Generic-coded DD end has no home. -- **G6 — no RouterOS REST transport / reader / apply.** Still the whole of step B: nothing - turns crs812_rest_* paths into requests; `fabric_switch_subject()` passes `readings: []`. -- **G7 — no switch credential standing landed.** `fabric-switch-factory-admin` exists in - Secret Manager but no SecretRef rows; factory->rotated bootstrap unmodeled. -- **G8 — the fabric legs are on qsfp56-dd cages, not qsfp56.** Confirm desired/assessment - target the qsfp56-dd interfaces (the 50G breakout legs), per the observed roster. - -### Host model (gunbc.spark.*) - -- **G9 — host link control is via mstlink (MFT), which has NO extdeps authority.** The - NVIDIA firmware tool surface (mstlink, mstconfig, mstflint) is entirely unmodeled. Host - link force/FEC/lane-count lives there, not in ethtool. -- **G10 — ethtool cannot express 2-lane (CR2) forcing on ConnectX-7.** `ethtool -s speed - 100000` is lane-count-ambiguous and selects CR4; only mstlink can pin 100G_2X. A modeled - host-force MUST use mstlink, and mstlink's flags `-s`, `--link_mode_force`, `-k` are - mutually exclusive per invocation (separate calls required). -- **G11 — no host-side SET/force grant.** This PR modeled only the EEPROM READ grant. - Forcing the host link needs a mstlink grant (root), unmodeled and uninstalled. -- **G12 — fabric_switch_observed is now STALE on main.** It states legs are 0x0B-coded and - the NIC refuses 100G_2X. Post-recode: byte 192 = 0x40, NIC Supported Cable Speed = - 100G_2X. The observed authority must be updated with the new reading (and should model - the recode as an event, not overwrite silently). - -### Operational / workflow - -- **G13 — spark_grants dispatch cannot fan out.** Dispatching 8 fleet-converge runs 3s - apart CANCELLED 4 via the workflow concurrency group. Multi-target grant install must be - serialized (one run completes before the next) or the workflow needs a fan-out mode. -- **G14 — spark_grants installs ALL missing grants, not a scoped one.** No dispatch-level - way to install only the EEPROM grant vs also InspectServingContainer. -- **G15 — dropin filename drift.** Host holds `/etc/sudoers.d/gunbc-gunbc-automation` for - the linger grant while the model derives `gunbc-enable-linger`. The installed filename - and the modeled `spark_managed_grant_dropin_name` disagree; re-install would write a - second file. (Noticed earlier, unrelated to this PR, still real.) - -### Credential containment (process, not model) - -- **G16 — actuation ran on ad-hoc curl with pasted GCP tokens.** The token expired - mid-experiment and left srv5 down with no recovery path in-session. The modeled path - (WIF on a runner, credential materialized and removed in-step) exists for grant install - and must be the ONLY actuation path; ad-hoc curl from a session is the scaffold to kill. diff --git a/docs/plans/fabric-switch-actuation-log.md b/docs/plans/fabric-switch-actuation-log.md deleted file mode 100644 index b855a680fcb..00000000000 --- a/docs/plans/fabric-switch-actuation-log.md +++ /dev/null @@ -1,118 +0,0 @@ -# Fabric switch 100G — out-of-band actuation log - -Operator-approved workaround (2026-09-17): the switch and host link modes were driven -directly over the RouterOS REST API and the host's `mstlink`, from a session on the LAN, -ahead of the modeled convergence path (docs/plans/fabric-switch-convergence-build-plan.md). - -**This file is an INPUT to the modeled apply, and its dissolution trigger** -- it is NOT an -exact transcription of a successful configuration, because no bilateral 100G configuration -worked. It records the facts established, the NORMALIZED operation shapes attempted (with -placeholders like ``/``/``, and RouterOS CLI equivalents for REST -mutations), and the verdict. A future receipt may carry the exact REST method, resource -identity, request body, real interface/PCI identifiers, timestamps and pre/post -observations; that is safely deferred. The -scaffold is retired when that modeled path lands and reproduces this end state. Round-by- -round exploration (wrong turns, corrected framings) is intentionally NOT preserved here; -git history carries it, and this file states each conclusion once, at witnessed confidence. - -## Facts established - -- **Switch:** MikroTik CRS812-8DS-2DQ-2DDQ, RouterOS 7.20.8, management 192.168.1.240, - identity `gunbc-fabric-switch`. REST API live (401 unauth). Credential in Secret - Manager: `fabric-switch-factory-admin`. -- **Host recode confirmed** (read on spark-a3ee, 2026-09-17): EEPROM byte 192 = `0x40` - (was `0x0B`), attesting 100GBASE-CR2; `mstlink` reports `Supported Cable Speed: 100G_2X` - (was `50G_2X` only); the former "Cable speed not enabled" refusal is gone. All 8 - ConnectX-7 NICs advertise `100000baseCR2/Full`. This is carried on the modeled carrier: - gunbc.spark.fabric_switch_observed `fabric_leg_eeprom_observations` (spark-a3ee row). -- **The fabric legs are on the switch's `qsfp56-dd` (400G breakout) cages**, comment - "gunbc fabric leg": qsfp56-dd-{1,2}-{1,3,5,7}. Each was forced to `50G-baseCR2`, - autoneg off, `fec-mode fec91`. `crs812_passive_dac_requires_forced_mode = true`. -- Lane roster (cage:lane -> host): 1:1 srv5, 1:3 srv8, 1:5 srv7, 1:7 srv6, 2:1 srv10, - 2:3 srv12, 2:5 srv9, 2:7 srv11. - -## Normalized operation shapes attempted (an input to the modeled actuator) - -Switch, per lane, over `/rest/interface/ethernet/` (RouterOS CLI form): -``` -/interface/ethernet/set speed=100G-baseCR2 auto-negotiation=no fec-mode=fec91 -/interface/ethernet/disable ; /interface/ethernet/enable # bounce to retrain -``` -Host (ConnectX-7, PCI 0000:01:00.1), forced 2-lane 100G via mstlink (ethtool -s cannot -pin CR2 — it ambiguously selects CR4; the flags below are mutually exclusive per call): -``` -mstlink -d --link_mode_force -s 100G_2X --yes -mstlink -d -k RS --fec_speed 100G_2X --yes -``` -Restore to known-good 50G: switch `speed=50G-baseCR2 auto-negotiation=no fec-mode=fec91` -+ bounce; host `mstlink -d -s 100G_2X,50G_2X,50G_1X,25G,10G,1G --yes` (re-enables -AN) then `ip link set down; ip link set up` (the host needs its own bounce to -retrain after the mstlink change). - -## Verdict (witnessed confidence) - -The QSFP56 host recode succeeded and is recognized: the ConnectX-7 advertises 100GBASE-CR2 -and no longer refuses the cable speed. On the CRS812 breakout legs, **no bilateral 100G -link was established** under either tested production configuration — automatic (both-ends -autoneg) or forced (both-ends forced, via mstlink for the host), across every switch FEC -mode. The host sits in `Polling`; the switch reports its own side `link-ok/100Gbps` -locally while the host never trains. On the autoneg path, RouterOS reports -`auto-init-failed` and `eeprom-checksum: bad` for the still-Generic-coded QSFP-DD end. - -This makes **switch-side QSFP-DD coding/compatibility the leading hypothesis, NOT a proven -cause**: the current tests do not isolate it from signal integrity, lane grouping, -firmware, or endpoint interoperability. The tested srv5 leg was the mutated specimen and -was restored and verified at 50G; all eight legs were verified at their known-good 50G state -after the experiment. - -## FS support escalation (2026-09-18) - -FS support asked us to follow the CRS812 breakout documentation: force the port speed with -auto-negotiation off (`auto-negotiation=no speed=100G-baseCR2` on the odd sub-ports). That -is the configuration already tested above. They also asked whether the ConnectX-7 was -actually up when the switch reported link-ok. To answer, one operator-approved retest ran on -the srv5 leg only (`qsfp56-dd-1-1`, 12:55:07-12:56:00 UTC). Both credentials were loaded -before the change and the restore was armed to run on exit, which closes the expired-token -failure above. - -- **FS's exact configuration was applied** on the switch with `fec91` and a bounce. The - host was forced to `100G_2X` with RS-FEC and bounced. -- **Both ends were read at +10s and +30s, with the same result each time:** - - CRS812: `link-ok`, `rate=100Gbps`. - - ConnectX-7: `mstlink State: Polling`, speed N/A. - - Linux: `NO-CARRIER`, carrier 0. `ethtool`: `Link detected: no (Autoneg, No partner - detected)`, active FEC None. - - Ping across the fabric link: 100% loss. -- **Answer to FS: no, the ConnectX-7 was not operationally up.** The switch's 100G reading - describes only its own side. -- **RouterOS reports `eeprom-checksum: bad` for the QSFP-DD end at the healthy 50G state - too.** On its own, the flag therefore does not explain the 100G failure. -- **The restore was verified from both ends:** - - Switch: 50G-baseCR2, fec91, link-ok. - - Host: Active, 50G, 2 lanes, RS(528,514), carrier 1. - - Ping to .14 and .12: 0% loss. -- **Operator sent the evidence to FS the same day** (after redacting it): the CRS812 system - information and hide-sensitive export, the port and module information, the ConnectX-7 - mstlink/ethtool/module EEPROM baselines, and simultaneous captures from both ends for the - forced test and the restore. Awaiting FS engineering's review. Cable: FS - QDD-400G-4QPC015, host-end serial S2630771509-2. Switch: RouterOS 7.20.8. NIC firmware: - 28.45.4028. - -The verdict above stands unchanged. The leading hypothesis is still unproven, and the next -step is still the substitution control below, unless FS identifies a configuration error. - -## Next step: a substitution control, not more configuration - -The single experiment that isolates cable/coding from switch/NIC/signal-integrity is a -hardware substitution: a known-good dual-compatibility cable (FS SKU 145681 / 101806) in -the same ports, OR the same cable with a known-good 100GBASE-CR2 endpoint pair. Until that -runs, contacting FS is reasonable but "cause proved" is not. Further out-of-band actuation -is paused pending credential containment. - -## Cross-references - -- Credential/restore incident (token expired mid-run, srv5 left down): filed as - gunbc.recurring_failure_mode - `a_rollback_depends_on_a_credential_that_can_expire_mid_actuation`. -- Modeling gaps found while driving the hardware: gap-analysis §6 (G1–G16). -- The modeled path this scaffold dissolves into: fabric-switch-convergence-build-plan.md. diff --git a/docs/plans/fabric-switch-convergence-build-plan.md b/docs/plans/fabric-switch-convergence-build-plan.md deleted file mode 100644 index b151cb7d779..00000000000 --- a/docs/plans/fabric-switch-convergence-build-plan.md +++ /dev/null @@ -1,90 +0,0 @@ -# Fabric switch convergence — build plan (the normal path) - -Goal: when the correctly-coded QSFP-DD end arrives, converge the 8 CRS812 fabric lanes to -100GBASE-CR2 through a MODELED, repeatable path — no ad-hoc curl. This defines that path -and sequences it into reviewable PRs. It dissolves the actuation scaffold in -fabric-switch-actuation-log.md and closes gaps G1-G16 in -fabric-switch-100g-convergence-gap-analysis.md. - -## Layering (DESIGN §3: interface / realization / policy are three facts) - -- INTERFACE — `extdeps.mikrotik.crs812` already owns the REST paths, lane speed and FEC - wire values, and the forced-mode fact. Extended here with the autoneg axis and a live - link-outcome shape (G1, G3). This is what RouterOS's API IS, not what we do with it. -- REALIZATION — RouterOS REST operations declared as `transport rest` operation rows - (method × path × basic-auth), realized by the shared HTTP-client machinery and - fixture-tested via `RestExchangeFixture`. Precedent: `extdeps.bmc.http` Redfish ops. -- POLICY — `gunbc.spark.fabric_switch_*` (workflow layer): which lanes, forced 100G-CR2 + - RS-FEC, the credential, the converge order. This is our business decision, not a fact - about MikroTik. - -## Credential (G7) - -Mirror `gunbc.spark.bootstrap_credential` / `grant_privileged_operation`: -- `fabric-switch-factory-admin` (exists in Secret Manager) as one-time bootstrap material. -- `fabric-switch-admin` (rotated) as the operating credential. -- A `FabricSwitchCredentialStanding = CredentialMaterialized{ref} | CredentialMaterializationPending`. -- Fetched runner-side over Secret Manager REST (as spark_grants does), never in argv. -- Bootstrap = rotate factory -> admin; the factory credential is not a serving credential. - -## PR sequence - -- **PR-1 (model only, safe, this session's target): domain + credential.** - - Add `auto_negotiation` to the lane intent/reading (G1). - - Add `Crs812LinkOutcome` (negotiated speed, link state: - LinkUp|Polling|AutoInitFailed|Down, fec-locked) as a two-ended live fact (G3, G4). - - Carry the FEC codeword the RouterOS label maps to (G2), a modeling nicety. The desired - FEC is `fec91` as a CANDIDATE -- the FEC the eight legs are observed running today -- not - a deduced fact: neither the earlier RS(544,514)-is-needed claim (IEEE inference) nor the - later fec91-per-MikroTik claim (uncited) is grounded, so the converge intent KEEPS a - FEC-capability refusal/observe arm rather than assuming `fec91` trains 100G (§4d). - - `FabricSwitchCredentialStanding` + the two SecretRef rows. - - Update `gunbc.spark.fabric_switch_observed` (G12): the 2026-09-17 recode is an EVENT - (byte 192 0x0B->0x40, NIC Supported Cable Speed 50G_2X->100G_2X) plus the live 50G - link reading; do not silently overwrite the prior observation. - - Witnesses for each new type/derivation; fixtures from the real REST/mstlink output - already captured in the actuation log. - -- **PR-2: the read path.** RouterOS REST read operations (GetEthernetInterfaces, - MonitorLane) as `transport rest` rows, a reader producing `List` from - the JSON, fixture-tested against captured responses. Wire `fabric_switch_subject()` to a - live read so `assess_fabric_switch` stops being vacuous (readings: []). Read-only. - -- **PR-3: the converge path, as ONE two-ended transaction.** The mutating actuator is a - single transaction over BOTH endpoints, never a switch-only mutation. Its safety shape is - fixed now even though the successful parameter tuple and the implementation defer: - 1. obtain and validate ALL forward AND recovery authorization before the first effect; - 2. capture host and switch pre-state; - 3. apply the complete candidate to both endpoints; - 4. accept success only on bilateral agreement on the required link state (switch monitor - AND host both report the negotiated speed, never one end's local "link-ok"); - 5. on every other terminal -- refusal, timeout, interruption, `Polling`, producer - disagreement -- restore both captured pre-states WITHOUT fetching new authorization; - 6. read both endpoints back and make VERIFIED restoration part of the terminal result; - 7. remove credentials only after confirmed success OR confirmed rollback. - It mirrors `gunbc.spark.managed_access_apply` for the per-step typed-outcome/receipt - shape, run via `fleet-converge.yml`. The desired FEC is the `fec91` candidate (G2); the - intent KEEPS a FEC-capability refusal/observe arm -- whether `fec91` trains 100G is - unwitnessed, so the actuator observes and refuses rather than assuming it works. - **Sequencing rule (the design commits to this now):** because forcing the HOST end needs - PR-4's mstlink set grant, the mutating switch effect and the host effect are components of - ONE actuator -- PR-3 and PR-4 land together, or the switch converge stays READ-ONLY until - both exist. The design does NOT authorize "mutate the switch now, add host actuation and - rollback later": that terminates with the endpoints intentionally divergent, which step 5 - forbids. Reading negotiated speed is unprivileged (`ethtool`), so OBSERVING both ends is - available before PR-4; only the two-ended MUTATION requires it. Forced-both is the - MikroTik-documented production candidate, not an established working config (the one - mstlink run of it did NOT train -- host stayed in Polling -- fabric-switch-actuation-log.md), - so PR-3+PR-4 let us RE-TEST it once the QSFP-DD coding is fixed rather than achieve - convergence by themselves. - -- **PR-4 (host side): mstlink authority + set grant.** `extdeps.mellanox` for mstlink - (G9), the 2-lane-force fact (G10: ethtool cannot pin CR2), and the host-side set grant - (G11) so the host end is forced through the modeled grant path, not sudo-over-SSH. - -## What none of this can do until the hardware is right - -The model + fixtures do not need a live 100G link to be correct and tested. But end-to-end -CONVERGENCE to 100G cannot SUCCEED until the QSFP-DD end links at 100G (pending FS / -substitution). PR-3's converge will correctly REFUSE or report Polling until then — which -is the honest behavior, not a failure of the model. diff --git a/docs/plans/fleet-revision-relation-fixture.md b/docs/plans/fleet-revision-relation-fixture.md deleted file mode 100644 index 1f9e7b9dc9d..00000000000 --- a/docs/plans/fleet-revision-relation-fixture.md +++ /dev/null @@ -1,118 +0,0 @@ -# The fleet-revision-relation fixture repository - -Rebuild recipe for the controlled git repository behind -`dag/test/claim/fleet_revision_relation_wet_matrix_test.dag`. - -## Why a fixture repository at all - -The pure merge-base fold is already witnessed by authored exit codes, and -`git.Inspect.MergeBase` itself is witnessed by record/replay. Neither covers the -*composition* — `observe_fleet_revision_relation_in` → `MergeBase` → -`fleet_revision_relation_from_merge_base` — where a swapped operand, a dropped `trim`, or a -mis-decoded stdout would live. A fold fed authored inputs cannot catch those: authoring the -inputs is the step under test. - -## The graph - -``` - B main - / - A ──┤ base-a - \ - C sibling - - D orphan an independent root, no common ancestor with anything above -``` - -Every commit carries a branch **on purpose**. `C` was originally created on a detached -HEAD and so unreachable; `merge-base` still answered only because the object had not been -collected yet. A fixture whose correctness depends on gc not having run is not a fixture. - -## Recipe - -Identities and timestamps are fixed because the object ids participate in the operation -input hash: a machine-dependent id makes the recorded fixtures unportable. - -```sh -FX=target/test-fixtures/fleet-revision-relation -rm -rf "$FX"; mkdir -p "$FX" -export GIT_AUTHOR_NAME=fixture GIT_AUTHOR_EMAIL=fixture@invalid -export GIT_COMMITTER_NAME=fixture GIT_COMMITTER_EMAIL=fixture@invalid -export GIT_AUTHOR_DATE="2020-01-01T00:00:00+0000" -export GIT_COMMITTER_DATE="2020-01-01T00:00:00+0000" -git -C "$FX" init -q -b main -echo a > "$FX/f"; git -C "$FX" add -A; git -C "$FX" commit -q -m A -git -C "$FX" branch base-a -echo b > "$FX/f"; git -C "$FX" add -A; git -C "$FX" commit -q -m B -git -C "$FX" checkout -q -b sibling base-a -echo c > "$FX/f"; git -C "$FX" add -A; git -C "$FX" commit -q -m C -git -C "$FX" checkout -q --orphan orphan -echo d > "$FX/f"; git -C "$FX" add -A; git -C "$FX" commit -q -m D -git -C "$FX" checkout -q main - -mkdir -p target/test-fixtures/not-a-repository # case 7 needs a real non-repository path -``` - -Verified reproducible: two independent builds produced identical `show-ref` output. - -## Object roster - -| ref | commit | oid | -|---|---|---| -| `base-a` | A | `3728e5635c811256194971bcaed367914a6040e6` | -| `main` | B | `3ad80d79bd1f64a7f75e121d7323aed053a81f0b` | -| `sibling` | C | `d0d3a7f6e323428bd771a32b1a19554481e1ae7c` | -| `orphan` | D | `8286843fce4511d0157573ba33b48ba46e48958a` | -| — | absent, decodable | `1111111111111111111111111111111111111111` | - -The absent id must be **decodable**, or the probe is never dispatched and the case proves -nothing about exit 128. - -## The seven cases, and what each one is for - -| # | current, accepted | exit | relation | why it is in the matrix | -|---|---|---|---|---| -| 1 | B, B | — | `SameRevision` | short-circuits before any dispatch | -| 1b | B, B (non-repository path) | — | `SameRevision` | the short-circuit does not depend on the repository existing | -| 2 | A, B | 0, base = A | `DeployedIsAncestor` | ordinary forward advance; base **is** current | -| 3 | B, A | 0, base = A | `CandidateIsAncestor` | superseded; base **is** accepted | -| 4 | B, C | 0, base = A | `UnrelatedHistories` | diverged **with a shared ancestor** — the case whose receipt used to claim a disjoint history | -| 5 | B, D | 1 | `UnrelatedHistories` | no common ancestor; exit 1 is an **answer** | -| 6 | B, absent | 128 | `RelationUnverifiable` | could not look — must not collapse into 5 | -| 7 | B, C (non-repository path) | 128 | `RelationUnverifiable` | a second route to 128; if it ever answered a relation, the observation is reading some other repository | - -Cases 5 and 6 are additionally asserted as one row -(`no_merge_base_and_could_not_look_stay_distinguishable`) so the distinction cannot be -half-satisfied. A consumer reading the operation's `success: Bool` instead of its exit code -cannot tell them apart at all — the live residual recorded on `MergeBaseOutcome`. - -## Record, replay, and the controls - -Commands are in the exclusion row's dissolution description -(`gunbc.ci_layer_roots`, pattern `fleet_revision_relation_wet_matrix_test.dag`) so the recipe -sits beside the reason a reader looks it up. - -Measured results: - -- **record (wet)** — 9/9 PASS, 6 distinct fixtures written for `git.Inspect.MergeBase` - (six, not seven, because case 5 and case 6 each appear twice across the nine rows). -- **replay (hermetic, exact store)** — 9/9 PASS. -- **control: empty store** — the two short-circuit rows still PASS; the other seven FAIL - with `missing recorded fixture`, each naming a **distinct** input hash. This proves the - passing replay consumed recorded observations rather than reaching live git, and separately - that the short-circuit is real. -- **control: wrong input hash** — one fixture renamed to a bogus hash, the rest left in - place. Its case refuses with its own exact hash while a sibling case still passes from - the same store. The store is an exact observation map, not a nearest-match catalog. - -The recorded `inputs` array includes `repository_path`, which is the direct evidence that -the repository participates in the key rather than being ambient. - -## What this does not cover - -The file does not run in the required floor: `git.Inspect.MergeBase` publishes no -`mock_response`, and recorded shell observations cannot reach a floor run — -`WitnessEvaluationFrame` carries only `rest_fixtures`, and `--fixture-store` is a -`claim_batch` flag with no `claim_executor` equivalent. The exclusion row carries the -substantiated reason and the dissolution trigger — a shell/service fixture arm on -`WitnessEvaluationFrame`, after which these rows enroll unchanged. diff --git a/docs/plans/floor-aggregate-cost-authority.md b/docs/plans/floor-aggregate-cost-authority.md deleted file mode 100644 index 82759f3305c..00000000000 --- a/docs/plans/floor-aggregate-cost-authority.md +++ /dev/null @@ -1,121 +0,0 @@ -# The floor's aggregate cost has no authority - -Status: proposal. No code yet. - -Raised from reading two required-floor runs (35365418267 merge_group, -35366842458 pull_request, 2026-09-18). Those IDs say WHERE the reading was -taken and are deliberately not doing the work of a producer: nothing in this -document quotes a duration, a population or a ratio as a standing quantity. -The producer for a floor run is the `required-witnesses-floor` job of -`gunbc.witness_floor_workflow`, and this proposal's whole point is that the -quantity it is about has no carrier yet -- so where a figure would go, it names -what would emit it instead (DESIGN §6: name the instrument, never transcribe -its output). - -## The gap - -The floor has a rich **per-item** cost model and no **population** model. - -Per item, today: `std.evaluation_budget` bounds one evaluation on two clocks; -`v2.workflow.floor_enrolment_margin` requires a newly enrolled witness to reach -a verdict with derived headroom; `v2.workflow.floor_cost_debt` withholds -identities whose marginal cost exceeds -`v2.workflow.required_floor` `required_floor_per_subject_cpu_line_ms`; -`gunbc.floor_cost_distribution` `implied_clean_run_budget` derives that line -from measured run-to-run inflation as an order statistic. - -Every one of those is denominated in ONE witness. None is a statement about the -run. So the floor's total is a quantity no declaration carries, no gate reads, -no refusal can cite, and no change is charged against. - -Each added witness is individually justified, individually inside its margin, -individually admitted -- and the aggregate is nobody's finding. That is §5's -externalization at authoring time: the cost is real, it is caused by a specific -change, and it is paid by every session that later waits on CI. - -## Why a per-item budget cannot be tightened into a population budget - -Tightening `required_floor_per_subject_cpu_line_ms` prices the TAIL, not the -SUM. A per-subject line low enough to bound the sum would withhold a large -population of individually cheap, individually correct witnesses -- a §4d -over-prohibition, forbidding more than the evidence supports. Sum and maximum -are two facts; one authority cannot carry both. - -## What is missing, stated as a modeling obligation - -Per §3b, a domain whose home does not exist is a modeling obligation, not yet a -conformance row. The obligation is an observation carrier and a disposition. - -**The observation must be attributed, not a single wall figure.** A lump total -would blame whichever witness's window happened to contain a shared cost -- -the same attribution defect `floor_cost_debt` already discovered per-claim. -The grains are: fixed preparation demand; shared-fill demand; marginal claim -work; orchestration; and an explicit **unattributed remainder**. The remainder -is a declared field rather than a silent residue, because on the measured runs -on the run this proposal was raised from it was the largest single component -- -a ONE-OFF reading, taken by summing the `done in` stage lines against the -`[floor-phase]` seam markers in one required run's log, with no entry point that -re-derives it. It is named as a one-off rather than quoted as a quantity, and -the carrier's `unattributed_work` field is what makes the figure a produced -output instead of a remembered one and an authority that cannot say how much it cannot explain is -reporting a number it has not earned. `measurement_completeness` travels with -the observation so a disposition derived from a partial reading is refusable -rather than quietly authoritative. - -**The policy carries three facts, not one budget.** - -- **desired target** -- 20 minutes. -- **hard steady-state ceiling** -- 30 minutes. -- **declared migration debt** -- NOT a figure carried here. It is read from the - `required-witnesses-floor` job of `gunbc.witness_floor_workflow` at the revision - the authority lands, by the producer the observation names. A number transcribed - into this proposal would be the baseline the whole policy is denominated in, - decaying without anyone touching either end (DESIGN §6). - -The three are separate because the floor is ALREADY over the ceiling. A policy -that refused everything above 30 today would deadlock the repository, and a -policy that silently adopted the observed figure as the budget would let the -temporary baseline replace the target. So during migration the ordinary obligation is -**non-worsening against the declared debt**, while the performance lanes lower -the debt; once the floor is under 30 the ceiling ratchets down and becomes an -ordinary refusal boundary. The target stays visible throughout. - -**An increase is a separately authorized act.** An envelope any cost-adding -change may raise is accounting, not governance -- the change cannot grant -itself the room. An amendment names the added capability, its measured cost, -the authorizing operator, and a retirement or offset condition, and it raises -the ceiling without raising the target. - -## What this does NOT license - -It does not license deleting evidence. §4b(4) requires a repaired class's -discriminating RED and positive control to **remain enrolled**. What it does -not require is one permanent authored file and one independently paid compile -per specimen: evidence may be consolidated into a data-driven matrix, carried -by one stronger discriminating construction, evaluated over a shared prepared -closure, or subsumed by a stronger wall that retains the required RED and -positive control. Consolidation of the physical representation is therefore a -legitimate lane for lowering the debt; retirement of the obligation is not. - -If an envelope ever forces the choice, the ladder wins and the envelope is -amended -- which is the whole reason amendment exists. - -## An open quantity this proposal deliberately does not assert - -Whether, and how steeply, floor runtime grows with authored witnesses. The authored -population is re-derived by `git grep -cE '^\s*test fn ' -- '*.dag'` over -any two revisions, and it grows steeply. But the topology filters hard: the -floor's own `required-floor: declared=N offered=N routed=N ...` census line -(`v1_compiler.bin.claim_executor`) is the producer for what survives to run, and -it reports a small fraction of the declared population. Declaration count is -therefore NOT the demand unit, and a -claim that a speedup would be consumed by growth needs a measured slope from -added identities to routed closures, compile groups and critical-path time. -That slope is the first instrument this authority needs and is not yet -measured. Until it is, growth is a motive for building the authority and not -evidence for any particular envelope. - -## Relation to the performance lane - -An aggregate authority does not make the floor faster. It makes the floor's -speed a governed quantity instead of a side effect. diff --git a/docs/plans/floor-cost-distribution-near-the-ceiling.md b/docs/plans/floor-cost-distribution-near-the-ceiling.md deleted file mode 100644 index 42592b5e0ad..00000000000 --- a/docs/plans/floor-cost-distribution-near-the-ceiling.md +++ /dev/null @@ -1,437 +0,0 @@ -# The floor cost distribution near the 500ms ceiling - -*Measurement, not a repair. Commissioned by tidy-lynx-804 after gunbc#10133 to decide whether -further per-producer work is a treadmill. The question is whether the population near the ceiling -is dense, whether the rows now crossing share a root, and what an honest margin policy would be.* - -## The instrument - -**Every figure below is produced by `tools.floor_cost_distribution_instrument` -`floor_cost_distribution_report`, and the derivations it composes are -`gunbc.floor_cost_distribution`.** Re-derive rather than trust this page: - -``` -# one download per run id in `floor_cost_sampled_runs`, into `floor_cost_artifact_root`: -gh run download -n required-floor-claim-cost -D /tmp/floor-cost/ - -gunbc run --source-root dag --source-root src/v2 \ - --entry dag/gunbc/instruments/floor_cost_distribution_instrument.dag \ - --function floor_cost_distribution_report -``` - -The first version of this memo named `gunbc.witness_floor_workflow` as its instrument. That -workflow produces the raw rows and performs none of the analysis — the ten-run intersection, the -band histogram, the counterfactual replay and the inflation percentiles existed only as prose plus -numbers, so a reader could check the arithmetic of nothing. Review 58983 refused it on exactly that -ground. The figures that remain below are illustrative of what the instrument returned on the runs -it names; the instrument is the authority, and where the two disagree the instrument is right. - -**The input needs no floor run.** `required-floor-claim-cost` is uploaded by every required-floor -run and carries the COMPLETE executed population — the `[over-cost]` lines in the job log are a -25-row preview of the same data and say so. The runs are named as data in -`floor_cost_sampled_runs`, because a measurement over "whatever happened to be downloaded" is not -re-derivable: a second reader would get different numbers with no way to tell a real change from a -different sample. - -**THE TEN SAMPLED RUNS REFUSE AS OF THE DISJOINT-COLUMN ARTIFACT CHANGE, AND THIS INSTRUMENT WAS -ITSELF THE SPECIMEN THAT MOTIVATED IT.** Every run named in `floor_cost_sampled_runs` predates that -change: their `required-floor-claim-cost` carries the old shared `cpu_ms` column, in which a -completed cost and the lower bound of a claim a safety deadline preempted sit in one field with -only an adjacent `verdict_reached` to separate them. This analysis resolved `cpu_ms` by name, -refused correctly on a *missing* column, and never asked for that discriminator — so every band, -every worst-cost fold and every inflation ratio on this page consumed bounds as costs. The bound's -magnitude is approximately the ceiling that stopped the row, so the "population dense near the -ceiling" that a histogram over those rows shows is partly manufactured by the ceiling itself. - -`claim_cost_columns` no longer resolves the old header, so those runs load as `RunRefused` — **not -`RunEmpty`**, which is a distinction the reader must not lose: an artifact whose measurements cannot -be read is not a run that measured nothing. The cause is typed and named -`UnsupportedSharedCpuMsVintage`, and `floor_cost_distribution_check` refuses the whole sample. - -That is the correct fail-closed answer rather than a regression, but the honest reason is narrower -than the one this page used to give. It said the two populations "are not separable in those bytes -at all". **That is overstated.** The old artifact carries `outcome` and `verdict_reached`, and this -very instrument treats `outcome=budget_interrupted` as an interruption signal — so an identifying -signal exists. The accurate statement is that the vintage is *known and unsupported*: one `cpu_ms` -column held both populations, and this version declines to reconstruct the split rather than being -unable to detect that one is needed. Nothing here obligates a legacy migration; it only forbids -claiming the bytes are silent when they are not. **The figures this page describes as illustrative should be read as carrying -that defect, not merely as stale.** Re-pointing `floor_cost_sampled_runs` at runs from the new -artifact vintage restores the instrument; the derivations are unchanged and their witness runs on a -hand-built fixture, so nothing but the live figures is waiting on the re-sample. - -**An incomplete sample refuses.** `floor_cost_distribution_check` exits non-zero naming every run -that failed to load. One artifact in an eleventh run (`33667468330`) downloaded empty during the -original analysis and silently emptied a ten-way identity intersection before it was caught — a run -that reads as zero rows is indistinguishable from a run in which nothing crossed, which is the -absorbing fallback DESIGN §5 forbids. Both refusal arms are witnessed in -`test.claim.floor_cost_distribution_witness`, along with a discriminating red for each derivation -below. - -## 1. The band is dense, and the tail is a plateau - -Post-repair run `33691893294`, CPU basis, 3,498 rows: median 2ms, mean 34ms, p90 86ms, p99 397ms. - -| CPU band | rows | cumulative from top | -| --- | --- | --- | -| ≥500 | 5 | 5 | -| 400–500 | 27 | 32 | -| 300–400 | 55 | 87 | -| 200–300 | 89 | 176 | -| 100–200 | 148 | 324 | -| <100 | 3,174 | 3,498 | - -**There is no gap below the ceiling.** The decision-relevant form of that is what happens if modules -are repaired top-down. Taking the worst row remaining across all ten runs, removing modules in -max-cost order after the three gunbc#10133 already fixed: - -| modules repaired | worst row remaining | runs refused /10 | -| --- | --- | --- | -| (the 3 from #10133) | 533 | 1 | -| +`rust_produced_decl_emit` | 525 | 1 | -| +`emit_host_field_access_equals_eval` | 511 | 1 | -| +`emit_host_fold_closure_equals_eval` | 490 | 0 | -| +`semantic_decl_serialize_parity` | 487 | 0 | -| +`emit_host_meet_join_equals_eval` | 486 | 0 | -| +5 more modules | 429 | 0 | - -The first three modules buy 43ms. The next seven buy 61ms — roughly **10ms per module**. That is the -treadmill, quantified: below the top of the band each repair returns about one fiftieth of the -ceiling. - -## 2. The new crossers do NOT share a root the way the #10133 three did - -`eval_steps` is a cheap structural fingerprint of shared work, and it separates the two cases -cleanly. - -The three modules gunbc#10133 repaired reported **612,662–658,336** eval steps — a 1.07x spread -across seventeen rows in three modules with unrelated subjects. That uniformity *is* the signature of -one shared dominant producer, and it was: `compile_phase_frontier_standing`. - -The families now near the ceiling report **63,196–197,227** eval steps — a 3.1x spread. They are -three distinct shapes rather than one: - -| family | rows ≥200ms | eval_steps | wall/cpu | -| --- | --- | --- | --- | -| `v2.test.execution.emit_host_*_equals_eval` | 33 | 63k–174k | **2.05** | -| `v2.test.emit.semantic_decl_*` | 27 | 78k–141k | 1.01 | -| `v2.test.emit.rust_*` / `produced_decl_*` | 48 | 77k–197k | 1.00 | - -The `emit_host_*` family spends half its wall clock off-CPU, which is the region -`v2.workflow.required_floor` already carries a §4b rung drop for (`OpaqueHostCallUnbounded`). - -**There is one shared-producer lead, and it is not a plan.** `required-floor-cross-claim-demand` -ranks `bind_outcome` (`v2.std.diagnostic`, unkeyed, 679 claims / 108,085 evals / 183 modules) at the -top by a factor of 13, and its module samples reach both the emit and the execution families. But -that artifact's own header states `cost_columns=inclusive_of_callees_do_not_sum`, and its column -totals 1,736,654ms against the run's actual `claim_cpu_total_ms=120,177` — a factor of 14. It ranks -candidates; it cannot quantify a saving, and it must not be cited as one. - -## 3. The margin, measured - -Run-to-run variation on the **same identity** across the sample, restricted to rows with enough -signal that integer milliseconds are not noise: - -- median **1.16x**, p90 **1.35x**, p95 ~1.43x, max ~**2.0x**. - -Two mechanism hypotheses were tested and both are refuted, which is what makes a single global margin -defensible: - -- **Expensive rows do not inflate more.** By baseline-cost bucket the median inflation is 1.18, 1.13, - 1.19, 1.17, 1.07, 0.92 for the 20–50 … 400–500ms buckets. -- **Host-call-heavy rows do not inflate more.** By wall/cpu class the median inflation is 1.16 (pure - CPU), 1.13, 1.16 (host-heavy ≥1.8). - -Run-level contention alone spans only 0.92x–1.15x across the ten runs, so most of the spread is -per-row rather than per-run. And run `33688140761` **refused with a below-median run factor of -0.96**, which is why a refusal cannot be read as evidence that the fleet was busy. - -**PR runs are harsher than `main` runs.** All four modules reported crossing on PR runs -(#10109/#10122/#10123) have `main`-run medians of 314–391ms and `main`-run maxima of 417–487ms, so -the PR path reaches roughly 1.6x on a median row. - -At 1.6x a row must sit under **312ms** on a clean run to be safe; at the observed 2.0x, under -**250ms**. **87 rows exceed 300ms today.** - -## 4. What this corrects - -The framing that prompted this measurement was that gunbc#10133 promoted the next rows down the -ranking into the margin. **The promotion mechanism is refuted, and the repair was disproportionate -rather than one step of a treadmill.** - -*Refuted:* rank does not cause a crossing, absolute cost does, and removing rows above a row does not -make it slower. Measured directly, the five rows that crossed on the post-repair run went **above -their own pre-repair maxima** — pre-repair median 343ms and max 472ms over 45 observations, against -504–533ms post-repair. They did not inherit a margin; they got slower, on the most contended run in -the sample (run factor 1.15, the highest of the ten). - -*Disproportionate:* replaying all ten runs with the three repaired modules excluded — - -| | runs refused | -| --- | --- | -| as observed | **5 / 10** | -| with the #10133 modules excluded | **1 / 10** | - -Across the nine pre-repair runs, **no row outside those three modules ever crossed 500ms.** Those -three modules were 17 of the 22 distinct identities that crossed anywhere in the sample. They were an -outlier — one shared 612k-step producer — not a sample of the plateau. - -## 5. What follows - -The plateau is real and per-producer repair is the wrong unit for 87 rows returning ~10ms each. Two -remedies are sanctioned by `v2.workflow.required_floor`: reduce what the witness reaches for, or -enroll it in a lane declaring its own dated ceiling **and** naming the row as an executing consumer. - -**Raising the ceiling is not a third option and should not be re-proposed.** That module records the -value being raised 500→1552→5000 and both raises being repudiated: 1552 was measured-max plus a -scheduler quantum on the same current tree, which is the oracle DESIGN §5 forbids by name, and 5000 -rode in as a passenger on a ruling about something else. The population that accumulated under that -drift is frozen in `v2.workflow.floor_cost_debt` precisely so it is not granted a higher ceiling. - -Which of the two remedies fits the emit and execution families — and whether a `bind_outcome` repair -buys real margin or only a rank change — is a decision this measurement informs and does not make. - -## 6. The premise is measured false: there is no fixed above-ceiling identity set, and the budget is not mis-set - -*Added after the §5 question was worked. It is recorded here rather than in the thread that produced -it because the belief it refutes is the one a reader re-forms from the same symptom — a red floor -lane naming a handful of emit rows — and re-deriving it costs a night.* - -**The claim being retired: "three (later seven) self-host witness modules sit above the 500ms hard -CPU ceiling." Measured, no fixed above-ceiling identity set exists.** Completed green runs place this -family's band below 500ms; run variance can carry its top across, and four completed-over-cost rows -were observed at 506–515ms. What is retired is that the family is *intrinsically* above the ceiling — -a row does not stably "sit" on either side of an attempt-safety boundary — not the crossings -themselves, which are real and recur. Both remedies §5 names are -refused for this family on evidence, and the ceiling is correctly denominated. What remains is a -different subject, stated at the end. - -### How to re-derive this, because there is no instrument module for it - -The §5 instrument reads one artifact per run. The join below needs a second axis — the same -identities measured off-CI — and **no authority owns it today**, which is a gap and not a style -choice. The recipe, at identity grain: - -``` -# CI side: per-claim identity/outcome/cpu_ms for a completed run -gh run download -R gunb-ai/gunbc -n required-floor-claim-cost -D /tmp/ci/ - -# local side: ONE function per process, which matches the floor's fresh-frame-per-claim -claim_batch --source-root dag --source-root src/v2 \ - --eval-budget-ms 300000 --entry --functions -``` - -Join on identity. Use runs whose rows **completed**; prefer green runs for the baseline factor. - -### Three readings that are wrong, and the control that exposes each - -**An `interrupted_before_verdict` figure is the budget, not the row.** The deadline preempted the -measurement, so the cost is unbounded above 500. Every interrupted row reports approximately whatever -ceiling stopped it, so a set of them looks like a tight cluster "just over" no matter what the true -costs are. Only `completed_over_cost_requirement` rows carry costs. Raising `--eval-budget-ms` on a -probe is how you measure one; that is instrumenting, and is not the ceiling-raise §5 repudiates. - -**The crossing set is not the expensive set.** Measured with the budget raised, the row in -`v2.test.emit.rust_body_add_emit` that *passed* on CI is the most expensive of its four, and the -three that were interrupted are all cheaper — a single-digit-ms spread separates survivor from -casualty. Repairing "the rows that crossed" therefore fits noise. **Always measure the non-crossing -siblings**; they are what reveals the band. - -**A one-anchor local→CI factor does not transfer.** A factor taken from one identity predicted -another row comfortably under the ceiling that CI had in fact interrupted. Take the factor from -many identities across several runs, or not at all. - -### What the join shows - -Joined over the emit and execution rows of this family across three main runs, the local→CI cpu -factor is **stable across green runs and materially higher on a failed one** — the same tree and the -same rows, with every row inflated together. At the green factor the family's whole band lands well -inside the ceiling; at the failed run's factor its top crosses. That is CI run-to-run variance, and -it independently corroborates the inflation distribution §3 measures from the other direction — two -instruments built for different purposes agreeing on one phenomenon. - -`v2.test.execution.emit_host_fold_closure_equals_eval` is the worked example: interrupted at -`cpu_at_least=522ms` on one job, it completes under budget on every green run in the join. - -**The host-independent form of this, which is stronger and needs no factor at all.** `eval_steps` is -carried in the same artifact and does not depend on the machine. Across the three runs, **every row -of this family that completed has a byte-identical step count** — identical work — while its -`cpu_ms` swings by up to the full green-to-failed ratio. Milliseconds move; the work does not. That -settles the question without any local→CI conversion, and it is the axis to use. - -The single row whose step count differs is the one that was **interrupted** on the failed run: its -count is truncated where the deadline stopped it. So the one apparent exception is a second -demonstration of the first misreading above — an interrupted row is not measured, in steps or in -milliseconds. *(The `eval_steps`-as-control technique is `gunbc#10158`'s; see the caveat below.)* - -### Why reducing the cost is the only lever, and what the cost is - -Both §5 remedies are refused for this family: - -- **Reduce what the witness reaches for** — refused by measurement. Decomposing `rust_target_model` - component-by-component (each in its own process, against a baseline replicated across four - independently written probe modules) shows every component except the bundle costs nothing, and - the bundle is what the emission reads. There is no oversized subject to trim, the test fixture is - free, and both cost-shape defects were looked for and are absent — the module fold is linear, and - the two `list_snoc_item`-in-a-fold sites operate over ~8 and 3 elements. -- **Enroll it in a lane declaring its own dated ceiling** — no such lane exists. Every candidate - either withholds rows from execution (`v2.workflow.floor_cost_debt`, whose own header states - membership deletes coverage, and which is shrink-only and closed to new crossers) or names a - cadence with no workflow (`FalsifierSubstrateLongLane`; no workflow in the tree carries a - `schedule:` trigger). Taking that arm would mean *building* a lane. - -The per-claim cost is a **fixed floor plus a small per-declaration term**, not per-declaration work: -the second declaration emitted in a frame costs a fraction of the first, and the module fold adds -nothing over separate calls. The fixed part splits into `rust_target_model` construction, the -arrow-body projection, and two smaller serialize steps. The projection term is **measured at the -`v2.std.compilers.target_model` `target_project_arrow_body_to_value_expression` frame and attributed -by elimination** to the primitive-apply step beneath it — the neighbouring candidates -(`dag_value_expression_projection`, `dag_surface_operator_canonicalization_member`, the -translation-rules lookup, the inner-arrow construction) each measure zero, the last two tested with -inputs that would have exposed a table build even on a miss. - -**One live lead, left undetermined on purpose.** The first projection in a frame costs; subsequent -ones do not, *regardless of which declaration body is projected*. That is equally consistent with a -memo keyed above the body and with a one-time warm of a shared structure that any first caller pays. -Those want different providers, so the mechanism must be separated before any provider is proposed — -that ambiguity, not the cost, is the open question. Two prior sharing attempts on this surface are -already refused: a `data`-row promotion cannot reach the floor at all (`required_floor_runner` builds -a fresh evaluation frame per claim, so `data_cache` never spans claims), and `rust_target_model` was -enrolled in `v2.workflow.floor_pure_producer_share` and withdrawn after measuring this exact cluster -*worse* — read that roster's header before re-proposing either. - -**One caveat on that withdrawal, added because `gunbc#10158` landed while this section was being -written.** The roster's rust-row measurements were normalised against *rows in no consumer module of -any enrolled key*, and #10158 shows that control is **biased by composition** — it demonstrated the -point by splitting a subject's own rows on `eval_steps` into those the serve reached and those doing -byte-identical work, and finding the supposedly untouched group moved almost as much. Rows elsewhere -are an assumption about the corpus; rows doing byte-identical work are an observation about the row. -That does not overturn the withdrawal, and the roster's own next trigger still governs — but the -strength of "measured worse" now rests on a control known to be biased, so a re-proposal should -re-measure against the step-split control rather than treat the question as closed. - -A value check was run before treating any of this as a caching opportunity: the projections of -`x + y` and `y + x` are compared and **differ**, carrying both a positive control that the equality -works and a by-construction-false row proving the harness reports failures at all. A cheaper-looking -result that lost operand order would have been a correctness defect, not a saving. - -### The subject, restated - -The floor is not flipping because a few rows are too expensive. **The emit surface sits at roughly -two-thirds to four-fifths of the per-claim budget on a normal run, and CI run-to-run variance is -large enough to carry its top across the line** — so which rows cross is a property of the run, not -of the rows. Reducing the shared fixed cost raises the margin against that variance. Nothing else -currently on the table does, and per-row repair aimed at whichever identities crossed last is -repairing the sample rather than the population. - -## 7. The margin of §3 is a two-run point estimate; the between-run envelope is wider - -*Commissioned after `v2.test.emit.rust_produced_decl_emit.rust_produced_decl_name_discriminates` -passed at 406ms against the 500ms ceiling on green `main` and refused at `cpu_at_least=516ms` on an -unchanged tree — a required-floor refusal with `claims_failed=0`.* - -*This section and §8 are the quantitative form of §6's conclusion. §6 establishes from a local→CI -join that no fixed above-ceiling identity set exists; these two measure how wide the variance that -makes membership unstable actually is, from the CI artifact alone.* - -**§3's figures index over rows inside one pair of runs, not over runs — and its prose says -otherwise.** §3 reads "run-to-run variation on the same identity across the sample", but the -producer behind it, `identity_inflation_permille`, takes a `base` and an `other` and is driven with -exactly two named runs (`floor_cost_baseline_run`, `floor_cost_contended_run`). Its percentiles are -therefore computed across ROWS inside one pair, and describe how unevenly one host-pair ratio lands -over different rows. Two runs are one sample of the between-run quantity and one sample has no -spread, so the pairwise median is a point estimate wearing a distribution's clothes. The instrument -was right and the sentence over it was not, which is the failure the "name the producer" rule exists -to make detectable. If the admitted execution envelope set is wider than the -pair sampled, it is a **lower bound**, and a budget set from it moves the cliff rather than closing -it. §3 is not withdrawn — it measures what it measures — but it may not be used as a budget input. - -**The producer for this section is `gunbc.floor_cost_distribution` `identity_envelopes` / -`complete_envelopes` / `work_invariant_envelopes` / `worst_envelope_permille` / -`run_extreme_census`, driven by `tools.floor_cost_distribution_instrument` -`floor_cost_envelope_report`.** The sample is twelve green `main` runs of `witnesses.yml` on twelve -distinct runner registrations across three hosts, named with their runners in -`floor_cost_envelope_sampled_runs` — the runner is not in the artifact and is acquired from the -run's `required-witnesses-floor` job. Re-derive rather than trust this page; where the two disagree -the instrument is right. - -`floor_cost_envelope_check` is the exit-code actuator and refuses an incomplete sample by name; -`floor_cost_envelope_report` returns the lines, and `gunbc run` renders them through its -entry-point refusal because the host requires a `ProcessExit` from an entry function — the sibling -report in §1 has the same shape. - -One row's envelope is its max over the twelve runs against its min over the same twelve, computed -only over identities present in every run with a verdict and a baseline at or above 50ms, and -restricted to rows whose `eval_steps` were identical in all twelve so the remaining movement is -inflation rather than a tree change. - -**Between-run envelope, work-invariant population (n=398 of 400 complete rows, 3,628 identities -observed across the twelve runs):** median **1.367x**, p90 **1.653x**, p95 **1.819x**, p99 -**2.046x**, worst observed **2.280x** — the instrument's own order statistics, which select an -observed member and never interpolate. §3's pairwise median of 1.16x understates the median by a -fifth and the worst case by more than half. - -**That step equality is a filter, not a finding, and it must not be read as evidence of -invariance.** Selecting rows whose counts agree across all twelve runs removes tree movement from -the sample so the residual is inflation; it says nothing about whether `eval_steps` is invariant in -general. It is not: the rung-drop row's missing item (b) has since been measured properly on one -identical tree (gunbc#10228) and **refutes** — `eval_steps` disagreed on 18 of 3,519 joined rows -against `cpu_ms`'s 1,394. Far more stable, and not invariant. - -**No cost trend.** Restricting the population by baseline leaves the envelope essentially flat — -p50 1.37 / p90 1.65 at ≥50ms, 1.35 / 1.58 at ≥100ms, 1.36 / 1.64 at ≥200ms, 1.47 / 1.51 at ≥300ms -— which confirms §3's refutation of "expensive rows inflate more" over runs rather than over one -pair, and is what makes a single global margin defensible. - -## 8. §6's "property of the run" conclusion, quantified — and it reaches host grain - -§6 concludes from a local→CI join that "which rows cross is a property of the run, not of the rows". -This section reaches the same conclusion from the other direction — the CI artifact alone, no -local→CI conversion — and carries it one grain further, to the machine. - -A median run factor cannot answer this: it is robust exactly where the envelope is driven. The -discriminator is `run_extreme_census`, which counts how often each run holds a row's maximum and its -minimum. Under per-row jitter each of twelve runs is the extreme for about a twelfth of the -population. - -It is not close to that. Over the 398 work-invariant rows, run `33766436293` (**srv4-09**) holds the -minimum for **367**; run `33775106554` (**srv1-19**) holds the maximum for **160**. At host grain -srv1 holds the maximum for **279 of 398 rows from 3 of 12 runs**, against 83 for srv3 (5 runs) and 36 -for srv4 (4 runs). The extremes are concentrated on particular machines, so a row near the line is -adjudicated by which host dequeued the job. - -**An earlier reading of this measurement said the opposite and is corrected here rather than -deleted.** Per-run *median* factors span only 0.878–1.118 and per-host medians are nearly identical -(srv1 1.069, srv3 0.985, srv4 0.992), which reads as "the host does not matter". It is the wrong -statistic for the question: a ceiling is crossed by the extreme, not by the median, and the extreme -census shows the concentration the median averages away. - -## 9. What this bounds, and what it does not - -**The subject row.** `rust_produced_decl_name_discriminates` measured **313–444ms across the twelve -runs** (envelope 1.42x, `eval_steps` 169,297 in every one — the work never changed). Its minimum, -313ms, is above the clean-run budget the measured p90 implies (500 / 1.653 = **302ms**) and well above -the worst-case budget (500 / 2.280 = **219ms**). The row is inside the variance cliff by measurement, -not by anecdote, and the 516ms refusal is an ordinary member of this distribution. - -**The bound is a floor, not a bound.** Twelve runs on three hosts are a subset of the admitted -execution envelopes. A wider sample can only find a larger worst case, so 2.280x may only rise and the -implied budget may only fall. - -**Raising the ceiling remains not an option** for the reasons §5 gives. What this measurement -supplies is a re-derivation the authority already asked for: `docs/design-rung-drops.md`, *Per-claim -cost qualification is unavailable at the subject grain the gate consumes*, sizes its attention -constant as the ceiling over the largest inflation floor observed to date and states that the -constant **must be re-derived the moment a larger floor is measured**. The floor it was sized against -was 1.777 from a single 501→282 pair. This sample measures **2.280**, so the constant falls from 280ms -to **219ms**, and the row is updated accordingly. Restricting the derivation to rows at or above -200ms — where whole-millisecond quantisation cannot dominate — gives 1.874 and a constant of 266ms, -so the direction of the re-derivation does not depend on the small-baseline tail. - -**Which remedy the subject family takes is not decided here, and §6 has already narrowed it.** §6 -refuses both of §5's sanctioned arms for this family on evidence — there is no oversized subject to -trim, and no lane with its own dated ceiling exists to rehome into — leaving the shared fixed cost as -the only lever. What this section adds is the size of the gap that lever has to close: at the p90 -envelope the family needs to come down to 302ms on a clean run, and at the worst observed to 219ms. -The subject's cheapest of twelve observations is 313ms, so the required reduction is real and -quantified rather than "some". diff --git a/docs/plans/floor-memory-envelope-2026-09-12.md b/docs/plans/floor-memory-envelope-2026-09-12.md deleted file mode 100644 index 1226f59b5f6..00000000000 --- a/docs/plans/floor-memory-envelope-2026-09-12.md +++ /dev/null @@ -1,310 +0,0 @@ -# The floor cannot account for the phase memory it refuses on - -The floor refuses on memory, but its cost receipts publish evaluation time and -no resident-memory reading for the terminal-ledger phase. The refusing run -publishes neither cost artifact. **The exact terminal-ledger typecheck peak and -its run median are UNOBSERVED.** This is an instrumentation gap: the available -cgroup readings establish throttle contact but cannot account for the phase's -memory demand. No cap change is justified by this finding. - -The strongest available evidence is the natural experiment: the assignment -reports #11115 refused twice before a genuine floor SUCCESS at 42m23s, while -#11139 refused three times and never reached a verdict. The inspected jobs below -independently confirm a same-merge-subject refusal/success pair for #11115 and all -three refusals for #11139. The workload is marginal, not universally unable to -complete. These selected outcomes are not an estimate of the fleet's pass rate. - -This is a stopped-line measurement report under DESIGN §§2, 4d and 5, not a repair -or a claim that the requested phase measurement has been completed. No refusal, -merge-admission wiring, OOM classification, or runner allocation changes here. - -## Throttled slot envelopes: producer and subject - -The observations below come from `floor_cgroup_envelope` in -`src/v1/stage0/src/cli_run/required_floor_runner.rs`: its `[floor-cgroup]` rows -read `memory.high`, `memory.max`, `memory.current`, `memory.peak`, and event -counters from the named cgroup and its ancestors. Only the **runner service's** -rows enter this table, never the aggregate parent slice's peaks. - -| PR / attempt within run | CI job (raw log producer) | runner | largest printed service `memory.peak`, bytes | terminal result / refusal site | -| --- | --- | --- | ---: | --- | -| #11115 / 1 | [103493724795](https://github.com/gunb-ai/gunbc/actions/runs/34670663495/job/103493724795) | srv3-16 | 16106778624 | refused / `extdeps.git.object_store` | -| #11115 / 2 | [103498744149](https://github.com/gunb-ai/gunbc/actions/runs/34670663495/job/103498744149) | srv1-13 | 16114794496 | success | -| #11139 / 1 | [103491056928](https://github.com/gunb-ai/gunbc/actions/runs/34670582182/job/103491056928) | srv4-19 | 16106856448 | refused / `v2.workflow.floor2_prepared_subject` | -| #11139 / 2 | [103496272864](https://github.com/gunb-ai/gunbc/actions/runs/34670582182/job/103496272864) | srv4-21 | 16106946560 | refused / `v2.workflow.floor2_prepared_subject` | -| #11139 / 3 | [103505983106](https://github.com/gunb-ai/gunbc/actions/runs/34670582182/job/103505983106) | srv4-10 | 16107003904 | refused / `std.primitives` | - -All five read `memory.high=16106127360` (15 GiB) and -`memory.max=17179869184` (16 GiB). Their service-local high events grow; -the printed service OOM and OOM-kill counters remain zero. The refusal is -`MemoryStallRefusedPageThrash`, not evidence of an OOM kill. - -The checked subjects are the merge commits, verified from each job's checkout: - -- #11115: `fb82d4c10374e61a135f4038ef4f7b414bbcaa7d`, head - `53fc8e668b7812bd5f64f3d86feab18c6bb8b52b`. -- #11139: `45a4956c998e9b4ce11ca0c41e9dbb9907fdfbca`, head - `c54441d2a45d0e67c2162be5f01f7c45d276bde0`. -- Both merge subjects use base `6cdebf53c4b1648e282de2f5ed1896b434b15b34`. - -This is a five-job convenience sample, not a census of every attempt mentioned in -the assignment. In particular the selected #11115 run contains one refusal and -one success; this report does not claim to have found its other reported refusal. - -The median of the five **largest printed service high-water readings** is -16106946560 bytes, or 15.000763 GiB: 0.78125 MiB **above** `memory.high`. -The range is 15.000607–15.008072 GiB. These are observations of a reclaim-throttled -slot, not a distribution of uncensored working-set demand. `memory.high` is a -throttle and can be crossed. Calling this median “the typecheck fits within -0.8 MiB” would invert the evidence. - -There is no reset at the terminal-ledger boundary, no terminal-boundary cgroup -read in these logs, and no process RSS peak specific to this phase. The last -printed peak can even include work after ledger publication. Therefore neither -subtracting the threshold from this peak nor subtracting two cumulative peaks -answers the requested phase-memory question. Even the successful job is not an -uncensored measurement of demand on an unconstrained host. - -## What the cost receipts can and cannot decide - -For successful run `34670663495`, downloaded artifacts: - -- `required-floor-claim-cost` (artifact `10292490653`): - `required_floor_claim_cost.tsv`, summary `executed=3771`, columns for identity, - outcome, observed/censored wall and CPU time, eval steps, and safety limits. -- `required-floor-cross-claim-demand` (artifact `10292285976`): - `required_floor_cross_claim_demand.tsv`, summary `claims_absorbed=3771`, - `claim_cpu_observed_total_ms=42837`, `claim_cpu_censored_rows_excluded=0`. - Its header explicitly says `cost_columns=inclusive_of_callees_do_not_sum`. - -Neither schema has an RSS column, and neither file contains -`terminal_ledger` or `floor2_prepared_subject`. They observe claim evaluation, -not the later ledger resolve. The artifact list for refusing run `34670582182` -contains measurement and outcome receipts but neither of these cost artifacts. -An absent artifact is unavailable evidence, not zero cost. No cost-partition -instrument was run; no `OverAttributed` result is asserted here. - -The passing job does carry a phase-local **time** producer: -`publish_terminal_ledger` in `cli_run/terminal_ledger_publish.rs` prints -`phase=terminal-ledger-publish state=completed resolve_ms=31100 total_ms=32485 -rows=3771` at `2026-09-12T05:07:51.2992978Z`. -That measures one resolve, not 3,771 repeated resolves. - -## Why it costs what it costs: established facts and remaining discriminator - -`build_ledger_wire_ctx` uses `process_shared_index(source_roots)` and -`resolve_entry_with_index_for_discovery_corpus` for the exact entry -`src/v2/workflow/floor_terminal_ledger_wire.dag`. `run_required_floor` invokes -publication once after its claim fold. Its prepared graph is still live; -`prepared.subject_digest` also has later consumers. The shared parse/typecheck -index survives. This is **a fresh context beside retained floor state**, not a -fresh empty process. The source explicitly chose rebuilding over retaining an -additional context across the fold; whether that tradeoff still saves memory -needs measurement, not reliance on that comment. - -The named module is where the governor detects the stall. The four refusals -name three different modules, including `std.primitives`; this rules out treating -`floor2_prepared_subject` as a demonstrated unique allocation culprit. It does -not rule out a costly common dependency or retained state elsewhere. - -The specific recurrence repaired by [#10992](https://github.com/gunb-ai/gunbc/pull/10992) -and [#11032](https://github.com/gunb-ai/gunbc/pull/11032) was fixture preparation -re-derived across isolated claim frames. That evidence does not transfer to this -once-per-publication resolve. Re-typechecking an already prepared fact is a -separate possible recurrence; the index's cache hits, misses and retained graph -ownership must distinguish it from new work. No allocation profile here proves -either “quadratic cost shape” or “genuinely irreducible large closure.” - -There is an existing, more specific cost-shape lead: -`gunbc.recurring_failure_mode.entry_scoped_typecheck_rss_tracks_the_name_census`. -It records sleek-swift-789's 2026-09-11 arm64 `gunbc compile --entry -src/v2/workflow/floor2_prepared_subject.dag --target dag` observation: 16 resolved -sources, 5385 census-indexed modules, peak 9964140 KiB from `/proc//status` -`VmHWM`, and exit 0. **That is prior evidence from its named producer, not a new -measurement of this entry or an amd64 requirement.** The row does not pin an exact -source SHA for its binary; this report does not promote its provenance. Its -isolated peak cannot be added to an independently observed floor RSS: shared -pages, caches and lifetimes overlap. - -Current source confirms `tree_bare_census_for_root` builds the whole-root compile -closure's symbol census and caches it per root in `MultiEntryIndex`. The existing -`GUNBC_EDGE_INDEX_CENSUS_TRACE=1` miss trace identifies both root and index. Joining -those identities across preparation and ledger resolve is a concrete discriminator -for repeated census construction. The old isolated observation and current source -make this a stronger lead than assuming the small named module is intrinsically -large, but do not establish the allocation owner in these five runs. - -The entry is **already scoped**. The wire imports identity, disposition and safety -vocabulary; `v2.workflow.required_floor` also imports roster/policy modules. -Narrowing further means separating those concepts at their existing authority -boundaries, not adding an `--entry` flag that is already present or omitting -required witnesses. A smaller import closure may still pay the whole-root census; -source size or an import grep is not a measured byte saving. - -## Ranked repair experiments and costs - -These are ranked by boundedness and expected diagnostic value. Savings are -unmeasured unless explicitly labeled otherwise; implementation costs are estimates. - -| Rank | Candidate | Required discriminating observation | Cost / risk | -| --- | --- | --- | --- | -| 1 | Remove unnecessary co-residence at the terminal boundary | Read current RSS, cgroup charge and live graph/cache ownership immediately before resolve; compare the same subject after releasing only objects with no remaining consumers | Small ownership audit; potentially substantial memory benefit, presently unquantified. Do not clear reusable caches indiscriminately or retain another whole context across the fold. | -| 2 | Investigate the existing whole-root name-census cost-shape lead | Join census misses by root and index; attribute allocations and retained maps across preparation and this resolve | Small diagnostic exercise first; a compiler repair could be medium-to-large. Prior scoped evidence identifies a lead, not a measured saving on this floor. The earlier per-claim fixes are not its evidence. | -| 3 | Narrow the wire's dependency closure through existing concept boundaries | Exact compiler-produced closure census plus matched before/after phase peak and identical rendered ledger/refusals; establish that census cost also falls or is already shared | Small-to-medium model move with import consumers to update. Could reduce the measured 31.1s resolve and its memory increment; neither saving is established. | -| 4 | Size/place the runner for measured co-resident demand | Completed amd64 phase peaks and cgroup anonymous/file/kernel charge under an externally enforced, characterized envelope, including repeat variance | Operational allocation and reduced fleet concurrency; no supported GiB request yet. Legitimate if demand remains after ranks 1–3 are ruled out, not a cap-only green. | -| 5 | Split execution at the terminal boundary | Show that serial release plus a separately executed ledger producer preserves subject binding, all terminal identities and all refusal arms | Largest complexity: process/artifact boundary, startup and parse costs, ownership and failure propagation. Premature without proving retained co-residence is unavoidable. | - -**Plain answer on sizing:** the current slot demonstrably reclaims at 15 GiB on -both heads. We cannot yet say how large a box this phase requires. There is no -evidence here for declaring 16, 18 or 20 GiB sufficient. - -## Did #11078 measurably increase cost? - -**No consistent wall-time increase is visible in this small before/after sample; -the census's isolated CPU and typecheck increment remain unobserved.** This is -not a finding that its incremental cost is zero. - -[#11078](https://github.com/gunb-ai/gunbc/pull/11078) merged at -`2026-09-12T03:15:29Z` as `f4b63e3b80ba142d8650e2d2d033881e7ba04a9e`. -It expanded the per-claim-scope ambiguity report from counts to named rows. -That report and `tree_bare_census_for_root` are different producers; sharing the -word “census” does not make the older RSS attribution apply to this change. - -Two nearby completed main runs before the merge and two after it give: - -| Main run / floor job | source SHA prefix | relation to #11078 | completed fold wall, ms | completed ledger resolve wall, ms | observed claim CPU total, ms | -| --- | --- | --- | ---: | ---: | ---: | -| [34665537725 / 103478308337](https://github.com/gunb-ai/gunbc/actions/runs/34665537725/job/103478308337) | `b2bda4edda7d` | before | 526589 | 44878 | 44027 | -| [34668064603 / 103484007739](https://github.com/gunb-ai/gunbc/actions/runs/34668064603/job/103484007739) | `c25ab6d87477` | before | 524850 | 34747 | 46789 | -| [34669916077 / 103489226361](https://github.com/gunb-ai/gunbc/actions/runs/34669916077/job/103489226361) | `6cdebf53c4b1` | after | 558781 | unobserved: refused | unobserved: no summary | -| [34672874238 / 103499666826](https://github.com/gunb-ai/gunbc/actions/runs/34672874238/job/103499666826) | `88c67ace130ef` | after | 487936 | 40831 | 42202 | - -Membership is verified with `git merge-base --is-ancestor` against #11078's -merge commit, not inferred from job completion time. The before runs also lack -the added `names_distinct` field; the after runs print it. Cancelled runs are -excluded and this convenience sample is not a population trend estimate. - -Producers: `run_required_floor` prints the completed -`floor: evaluating N / N (Xms)` fold span; `publish_terminal_ledger` prints -`resolve_ms`; the `[cross-claim-demand] claims_absorbed=...` summary prints -`claim_cpu_observed_total_ms`. The CPU totals cover 3754, 3781 and 3760 completed -claims respectively, with zero censored claim rows in those three summaries. -They are marginal claim-evaluation CPU, **not whole-floor CPU or census CPU**; -they cannot measure the cost of scope construction or reporting outside the -claim window. Nor is ledger `resolve_ms` a pure typecheck timer. - -The completed post-merge ledger resolve, 40.831s, lies inside the pre-merge -34.747–44.878s range. Post-merge fold times straddle the pre-merge range; the -completed post-merge claim CPU total is lower. Hosts and source populations -differ, and one post-merge ledger resolve is censored. These observations do not -identify a regression caused by #11078, nor rule out a smaller increment hidden -by those differences. Its specific cost needs a producer-local bracket; -whole-run wall/CPU cannot supply that attribution. No retained-state experiment -or new heavy run was added for this comparison. - -## Next-rung capability and reproduction - -**Next-rung trigger:** the floor's own receipts publish per-phase resident-memory -readings on refusing runs as well as successful ones, sufficient to compare the -terminal-ledger typecheck phase's peak with the admitted budget. The readings must -carry subject identity, phase boundaries, resource/budget scope and unavailable -or censored standing. A named file or access grant alone does not discharge this -capability. The existing failure-mode row -`gunbc.recurring_failure_mode.instrument_blind_spot_anti_correlated_with_severity` -carries this specimen; it does not assert that the memory behavior is repaired. - -Fetch each of the five jobs once, preserving its id in the filename: - -```sh -gh api repos/gunb-ai/gunbc/actions/jobs/103498744149/logs --allow-escape-sequences > 103498744149.log -gh run download 34670663495 -n required-floor-claim-cost -D cost -gh run download 34670663495 -n required-floor-cross-claim-demand -D demand -``` - -For each job log, select `[floor-cgroup]` lines whose `level` ends in the runner -`.service`, take the maximum printed `peak`, then the median across the five job -values. Convert bytes to GiB by division by `2**30`; convert the difference from -`high` to MiB by division by `2**20`. Preserve the measurement grain stated above. -Read the actual refusal lines, not the echoed shell grep that classifies them. - -A new measurement must retain the real floor's baseline, bracket **typecheck** -within this entry resolve (not all preparation), record current and peak process -RSS separately from cgroup charge, retain missing/censored distinctions, and -name exact source and binary identities. An isolated entry run is a useful -closure-increment control, but cannot replace the in-floor observation. Repeat -the same subject to report a phase-peak median; a refused attempt cannot supply -its unobserved completing demand. Keep the existing MemoryStall refusal active. - -Two execution routes were checked in this session and could not provide that -measurement: - -- [BuildBuddy envelope probe](https://app.buildbuddy.io/invocation/b6c1a5db-8f85-4197-a915-cea73a5745ee) reached amd64, but `/proc/self/cgroup` read `0::/` and both - `/sys/fs/cgroup/memory.high` and `memory.max` were absent. The command stopped - before building or running the workload. A separate - [invalid-Cargo-flag control](https://app.buildbuddy.io/invocation/e56f48bc-d69a-4d8c-a6fc-d02fcf2c079d) - reached remote Cargo and failed as expected; remote transport itself - was working. No budget was invented to pass admission. -- The parent's bounded local alternative was - `systemd-run --user --scope -p MemoryHigh=15G -p MemoryMax=16G ...`. - This container has no `systemd-run`, its cgroup directory is not writable, and - it has no built local interpreter. Its visible 31 GiB container limit is not - permission to consume the shared host slice. No local heavy run was started. - -An arm64 scoped control, if subsequently enabled, establishes a cost-shape lead; -it does not establish an amd64 cap. The exact runner phase peak and median remain -the outstanding measurement, explicitly reported to `bright-eagle-728`. - ---- - -## Outstanding state of the sizing response, 2026-09-14 - -This report concluded that no cap change was justified *by its own findings*. The -sizing change was authorised separately and is carried on `session/slot-sizing-26-25` -(PR #11204). It is **not converged**, and this section records what remains so the -state is readable without reconstructing it from the branch. - -**Live fleet, unchanged.** srv1 carries `MemoryHigh=16106127360` over 21 units. No -host effect has been performed by this work: no cap write, no slot retirement, no -canary. Every claim below is about source and CI, never about a host. - -**What the branch establishes in source.** The slot ceiling moves to 26 GiB -`MemoryMax` / 25 GiB `MemoryHigh`; `cores_per_slot` rises 6 → 10, so CPU binds every -host at 12 and the fleet commits 36 rather than 68. A raise is gated behind a -retirement transaction: the required-retiree set is derived from a declared prior -width (`gunbc.runner_width_transition`) and reads no observation, so it cannot shrink -when a unit stops being listed; operator interruption authority is checked as -*coverage over* that obligation and never contributes members to it; `mask --now` is -rendered into the hashed fleet artifact and executed only by fleet apply; and -completion comes from an independent readback of `UnitFileState`, `ActiveState` and -the real cgroup. Retirement disposition participates in the plan/apply fingerprint, -so a plan built while a unit read complete refuses if apply reobserves it populated. - -**srv2 is refused, not narrowed.** Its width resolves to a typed refusal and no -`RunnerHostDeploy` is constructed for it at all; slot planning, cap construction and -the cap writer all consume that same admission, and its generated sudoers projection -renders teardown and host-wide grants only — no slot-install, no activation, nothing -that could widen a host nobody can size. - -**What is NOT established, and is the work remaining.** - -1. **No wet proof.** Nothing has executed `mask --now` on a host. Every retirement - result is hermetic. The srv4 canary — interrupt → observed retirement → guarded - raise → effective-limit readback → no-change second pass — is the first real - proof and has not been run or authorised. -2. **No terminal green.** The last exact-head required run was red. Two failures are - measured as inherited rather than assumed, each reproduced at the committed head - with the branch's later changes stashed: - `witness_fabric_cell_absent_address_plans_add_and_never_change` and - `srv4_sccache_prereq_pins_version_and_digest`. -3. **One acceptance condition is specific and unmet.** - `test.claim.runner_slot_provision.no_deploy_row_out_commits_the_memory_budget` - must appear in the terminal disposition as `planned` (or - `planned_as_changed_witness`) **and** `passed`. At `890c264be36` it was - `declined_outside_required_gate` — its whole module carried no admitted - prefix — so the aggregate `planned=3938 executed=3938` was true and said nothing - about it. The prefix is now enrolled; the run proving it selected has not happened. - -**The instrumentation gap this report opened is still open.** Nothing here measures -the terminal-ledger phase peak. The sizing change was justified by the floor's -*uncensored demand* reading, not by the phase measurement this document says is -missing, and raising the ceiling does not close that gap. diff --git a/docs/plans/floor-shared-fill-ledger.md b/docs/plans/floor-shared-fill-ledger.md deleted file mode 100644 index 952bd2e7a66..00000000000 --- a/docs/plans/floor-shared-fill-ledger.md +++ /dev/null @@ -1,226 +0,0 @@ -# The floor's shared-fill ledger — measuring what a slow witness actually costs - -gunbc#8455 established that the floor's per-row wall time is not always the row's cost and named -the repair it did not perform: *attribute shared cost to the entry rather than its first row*. -This is that repair, plus the defect's mechanism root-caused to source. - -## The mechanism, named - -The floor builds a **fresh evaluation frame per claim**, so the sharing #8455 measured is not an -eval memo (a per-claim memo cannot survive between claims) but a population of -**process-global corpus-scan caches in the seed**, each filled at most once per process and read -free thereafter: - -| cache | what the fill is | -|---|---| -| `reference_edges` | one O(corpus) tokenize+parse pass over the whole pool | -| `module_graph_facts` | the live module graph over the pool roots | -| `module_path_index` | module → path over the source roots | -| `inert_carrier_data`, `complexity_linearity_audit`, `complexity_linearity_wildcard`, `fallback_arm_census`, `non_fold_residue`, `doc_graph_report`, `test_migration_behavior_discovery`, `test_migration_debt` | whole-corpus census reports | - -The #8455 specimen resolves exactly onto the first row. `g2_data_reference_under_selection` -calls `specimen_projection` → `v2.lens.live_read_classification` -`live_read_selection_manifest_live` → `dependency_resolution_facts_live`, whose seed realization -`cli_run.rs` `reference_resolution_facts` performs that whole-pool parse behind -`REFERENCE_EDGE_CACHE`. The first claim pays the pass (12197ms); the second reads the map -(31ms). Same call, 393x, 35ms apart. - -## Why the existing number cannot answer the paring question - -Nothing is computed twice — the caches are correct and the sharing is the saving. What is wrong -is the **attribution**, and it matters because the ceiling refuses per row: - -- A refusal names the **first toucher**, whose own cost may be milliseconds. -- **Splitting** such a row moves the charge to whichever fragment runs first - (`gunbc.witness_row_cost` `witness_decomposition_does_not_reduce_entry_cost_note`, observed - rather than predicted). -- **Removing** such a row does not remove its cost either: the next claim to touch the fill pays - the same seconds, so a quarantine decided on the per-row number can trade a named refusal for - an unnamed one in a module that was fast before. - -## What the ledger records, and the quantity it yields - -`cli_run/shared_fill.rs` records, per fill: the cache, the key it is declared on, what the fill -cost, which claim paid it, and every later claim and module that read it. `[floor-shared-fill]` -lines are emitted once at the end of the fold. The disposition on each line is derived by -`gunbc.witness_row_cost` `shared_fill_disposition` from the payer and the consumer breadth — -three states because three remedies: - -- `outside-fold` — preparation's cost. No witness can be pared to recover it. -- `exclusive` — one module consumed it. It is removable with that module. -- `shared` — more than one module consumed it. It survives any single removal. - -From that, joined against the `[floor-witness-slow]` rows the floor already prints: - -``` -what removing module M saves = M's rows' wall time - - fills M paid that are `shared` - (+ nothing for `outside-fold` fills at all) -``` - -## What this is not - -It refuses nothing, skips nothing, changes no verdict, makes no witness faster, and takes no -position on which rows to pare; it prints a table (DESIGN §5's sanctioned stopped-line audit — it -reports, it does not green) measuring the quantity that decision needs and nobody currently has. - -`unattributed_hits` on the total line is the honest bound: reads served by a fill this ledger did -not observe. Nonzero means the shared figure is a **lower** bound on the sharing. - -## Two bounds on reading the output - -**Run-to-run variance is real and is not this ledger's subject.** `where_refinement_cast_literal_oci_other_digest_algorithm_accepts` measured 1505ms, 1274ms and 1364ms — the first two on *byte-identical* input (a rerun of one frozen merge ref) — an ~18% spread straddling a 1500ms ceiling (sharp-raven-273, 2026-08-18, runs 32155779798 / 32153078487). For a row within ~20% of its ceiling, which side it lands on is partly a fact about the runner; a smaller fill difference is not readable from one run. - -**A right-censored row cannot be decomposed.** Exclusive-vs-shared subtracts from a *completion* -cost, so a row whose figure is its interrupt point has nothing to decompose. On main run -32177951514, 84 of the 97 budget-refused rows sit pinned at 1552–1560ms and must be excluded from -any ranking; the deadline is cooperative (polled every 4096 evals), so the other 13 ran past it -with real overshoots — up to `grounding_lens_whole_tree` at 21868ms. The `[floor-witness-slow]` -channel is the better census (720 rows, all with measured elapsed), on the same condition: its -~85 rows in the pinned band are censored, the rest are measurement. - -**The signature to look for, and the one that refutes the model.** A shared fill looks like *one -large row and cheap siblings* — the g2 pair's 12197ms then 31ms. Several siblings at roughly the -*same* cost is the opposite evidence: nothing was amortized, each pays its own way. The eight -`effect_reach_test` rows clustered at 1788–2134ms are therefore a prior *against* shared fill in -that module, and the ledger says so directly rather than by inference. - -## Receipt — run 32192150969, 9425 claims, one fold - -19 fills. The red is main's inherited (`failed=0 stale_quarantine=5 budget_refused=102`), not -this diff's; the ledger reported from a complete fold. - -**Read `fill_ms` as SELF time.** The first receipt exposed an instrument defect, fixed in the same -PR: these caches compose, so an outer fill's wall contains its inner fills'. A 17890ms -`module_graph_facts` fill contained a 12054ms `module_path_index` fill and a 5141ms -`reference_edges` fill — ~695ms of its own. The first receipt's `TOTAL fill_ms=302362` was an -inclusive over-count; the corrected self-time total is **~180s**. The per-fill -payer/consumer/disposition columns were never affected. - -### The finding: one row's entire cost is a fill 29 other modules read - -``` -cache=module_path_index key=dag+src/v2 fill_ms=51508 - paid_by test.claim.dissolution_census_witness_test.unbound_dissolution_empty_literal_refuses - read by 139 claims across 29 modules disposition=shared -``` - -That payer is one of the five rows in the corpus with an *exact* cost: **51620ms**, of which the -fill is 51508ms. So **99.8% of that witness's measured cost is a computation 29 other modules -consume**, and it is the run's only `shared` fill. Removing the witness recovers ~112ms and hands -51.5s to whichever of the 29 runs first — the paring question answered at identity grain, on the -row where it matters most. - -### And the opposite finding, which matters just as much - -The 86741ms row — `extdeps_scope_placement_gate_loudness_witness` -`red_seed_runner_failure_detail_projects_located_receipt`, the most expensive exact row in the -corpus — paid a 29017ms `module_graph_facts` fill and a 24965ms `reference_edges` fill, **both -`exclusive`**: no other module reads either. Its cost is its own and it is a real paring target. -Two rows adjacent on a slow list, opposite answers; per-row wall time cannot distinguish them. - -### Preparation's share, which no paring can recover - -Three fills totalling ~108s inclusive are `outside-fold` — a 55392ms `module_graph_facts`, a -35060ms `module_path_index`, a 17646ms `reference_edges`, all paid before any claim ran. That -cost is preparation's; quarantining every witness on the floor would not move it. - -### Second receipt — run 32196069889, 9001 claims, 8 fills: the relocation, at identity grain - -The first receipt reported consumer *counts*; this one names them, and caught the same fill -changing hands. - -``` -run 32192150969 cache=module_path_index key=/dag + /src/v2 fill_ms=51508 - paid_by dissolution_census_witness_test.unbound_dissolution_empty_literal_refuses - read by 139 claims across 29 modules disposition=shared - -run 32196069889 cache=module_path_index key=/dag + /src/v2 fill_ms=45075 - paid_by emitter_nested_refinement_cast_witness_test - .single_refinement_carrier_emits_no_unsupported_cast - read by 94 claims across 20 modules disposition=shared -``` - -Same cache, same key, `paid_by` changing hands after #8457 quarantined the first payer. The second -payer is **main's only failing witness**: 45784ms on this run, of which 45075ms is this fill — -~98.5%. **Pin that share to the run**: the same row measured 45784, 45941, 46714, 50532 and 52982 -across five runs the same day, so the fill *fraction* is robust and the denominator is not. Under -every reading, nearly all of the remaining red is one build that 20 other modules read for free. - -### The consumer set shrank, which prices the quarantine itself - -The two receipts also price what #8457's quarantine bought on this axis — close to nothing for -part of the population: - -``` -pre-quarantine read by 139 claims across 29 modules -post-quarantine read by 94 claims across 20 modules -``` - -Nine modules and 45 claims left the consumer set. A consumer is a **free rider** — it reads a fill -someone else built, so its marginal cost for that fill is ~zero. Quarantining a rider removes -coverage and recovers nothing here; quarantining the *payer* handed the bill to the next module and -produced main's sole red. - -**The honest limit:** the first receipt reported consumer *counts* only, so those nine modules -cannot be named retroactively — the delta is consistent with riders having been quarantined, not -proof of which. That is why the line now names consumers; from here the question is a set -difference, not an inference. - -The 20-module list is two lists at once: who inherits the bill if the current payer is quarantined, -and who is first to come **back** once the fill belongs to preparation. - -Evaluation order is alphabetical and the riders are named, so the prediction is checkable: -quarantining this payer hands ~45s to `emitter_optional_payload_cast_witness_test` next — the -fourth hop of a chain that has already run three times (`dissolution_census` → `emitted_lib_rs` → -this row). - -It remains the **only** `shared` fill — 8 fills this run, one with more than one consuming module. -The fix is `gunbc#8470`, which pays this index during preparation; the self-verifying -post-condition is that this row reappears as `paid_by= disposition=outside-fold`. - -The nesting correction shows here too: `module_graph_facts key=dag` reports -`fill_ms=2990 inclusive_ms=55048` — 52s of it was the path-index and reference-edge scans it -triggered, which the first receipt's total counted twice. - -### The prediction, confirmed on an experiment nobody had to build - -When #8457 quarantined 102 over-budget witnesses, this model predicted the cost would not leave -with them — the next module to touch the fill would pay it. Main before (run 32177951514) against -main after (run 32193032348), **one module, one run, one tree**: - -``` -single_refinement_carrier_emits_no_unsupported_cast 746ms -> 45941ms 61.6x -nested_refinement_carrier_deficit_remains_observable 728ms -> 763ms flat -``` - -Neither row changed; 38 modules were removed from the fold ahead of them. Were the 45.9s the row's -own work it would have been 45.9s before; were it a general slowdown the sibling would have moved -too. It is the first-touch signature exactly: the fill that row now pays was previously paid by -one of the quarantined 102, and the sibling rides it free in both eras. **Quarantining a first -toucher relocates the bill; it does not retire it.** (Measured by tidy-lark-471. It landed in the -failures channel rather than the refused one, which is why a scan for new refusals read the run as -refuting the prediction.) - -### What `exclusive` does and does not establish - -`exclusive` means **no other module in the executed population read this fill on this run** — -not that none ever would. Remove the payer and a would-be consumer could pay the same fill next -run: the first-toucher trap one level up, invisible from inside one run. - -Two things bound that doubt. The `key` is the fill's *declared inputs*, so an exclusive fill on a -key no other consumer requests is exclusive by construction, not luck — the 29017ms -`module_graph_facts` above is keyed on `dag+src/v2` roots that the shared 51508ms -`module_path_index` fill also serves, while the 17890ms one is keyed on a four-root pool only its -own module asks for. And the answer is decidable by execution: run the fold with the payer absent -and read the ledger again. Until then an `exclusive` row is one run's observation, and a recovery -estimated from it is an upper bound. - -A second edge corrects a reading of the same rows from isolation. A witness whose SUBJECT is the -corpus is not made cheap by shrinking the corpus: the 71s row measures 70–85ms in a 67-module -closure and 71060ms against the floor's 2376-module subject, with the witness unchanged. -Isolation structurally cannot price that class, which is why the ledger measures on the floor -path (quiet-ibex-39, 2026-08-18). - -`unattributed_hits=1`: one read was served by a fill this ledger did not observe, so the shared -figure is a lower bound. Small, but reported rather than rounded away. diff --git a/docs/plans/floor-time-attribution-2026-10-02.md b/docs/plans/floor-time-attribution-2026-10-02.md deleted file mode 100644 index 6cb311473d2..00000000000 --- a/docs/plans/floor-time-attribution-2026-10-02.md +++ /dev/null @@ -1,156 +0,0 @@ -# Where the required floor's time goes (2026-10-02) - -Status: investigation and plan. No code lands from this note; the repairs are dispatched separately. - -Trigger: #12506 (N7-2) was refused by `v2.workflow.floor_enrolment_margin` (55 of 96 newly enrolled -identities over margin). The question asked was where the floor's time goes, and whether that -overage is one case of a general cause. Raising the budget is not on the table. - -## Instrument and subject - -The subject is main at `d8eebaea3f`, merge_group run 37070592712, `floor` job 111048851355. The -#12506 reading is run 37071586760, job 111052035724. - -**No new instrument was minted.** The floor already prints every carrier this needs. They appear -in the job log: - -| carrier | grain | producer | -| --- | --- | --- | -| `[floor-seam-cpu] seam= thread_cpu_ms=` | cumulative thread CPU at each seam boundary | `claim_executor --required-ci` | -| `[floor-shared-fill] claim= marginal_cpu_ms= fill_cpu_ms=` | per claim, split into its own work and the shared fill it paid for | `cli_run::shared_fill` | -| `[floor-shared-fill] cache= key= fill_ms= consumer_claims=` | per shared computation, with the count of claims that read it | `cli_run::shared_fill` | -| `[cross-claim-demand] producer=

claims= evals=` | per producer, recomputation across claims | `claim_executor` | -| `compile. done in` | each host compile stage | seed compile pipeline | - -To re-derive, fetch the log with -`gh api --allow-escape-sequences repos/gunb-ai/gunbc/actions/jobs//logs`. Then difference -consecutive `floor-seam-cpu` values, and sum `fill_cpu_ms` and `marginal_cpu_ms` per claim and per -module. - -**Instrument defect found along the way.** The two durable carriers are no longer uploaded: -`required-floor-claim-cost` and "the cross-claim demand TSV this run uploads" (that phrase is the -log's own). On both runs above, the only artifact is `required-ci-measurement-receipt`. -`tools.floor_cost_distribution_instrument` therefore has no input, and the log's sentence about the -upload is false. Restoring the upload is PR 3 below. Until then the attribution can only be read -from the log, which is a scrape. - -## Structural predictions, stated before the confirming read - -I wrote these down from DESIGN §2, §3 and §6b before differencing the seams: - -- P1. Very little floor time is claim marginal work. Most of it is computation shared by many - claims, paid either in preparation or by whoever touches it first. -- P2. The witness families that compile fixtures pay a compile of the fixture's whole live import - closure once per distinct fixture, even though preparation has already compiled that closure. -- P3. The N7 overage is the same shape one layer up: a pure function of module-constant text, - evaluated once per claim, with no provider joining the claims. -- P4. Some preparation work is demanded by the gate closure and read by no claim the run plans. - -## Attribution (one reading; the carriers above are the authority) - -Floor job wall time was about 47.5 minutes. Thread CPU, differenced by seam: - -| slice | CPU | what it is | -| --- | --- | --- | -| build + parse + declarations | ~4m wall + ~61s | `cargo build`, parse of 7347 files | -| diff planning (base decl census, edits, nominal seeds) | ~100s | | -| **prepare-closure-resolve** | **~646s** | seed compile of the 2585-module gate closure; `compile.reconcile` alone took 8 minutes wall | -| **prepared-subject-warm** | **~377s** | 343 `cross_claim_pure_share` fills ≈ 316s, plus effect inputs | -| bare-reference edge index + discovery + site projection | ~120s | includes a second full parse of 7286 files (`reference-reading-parses full_parses=7286`) | -| **claim-evaluation-fold** | **~715s** (732s wall) | 673 claims | -| publication (wet witnesses) | ~6.5m wall, ≈0 thread CPU | `[local-repo-wet]` rows | - -Inside the fold, the 197 claims with fill lines account for about 667s. Of that, about 666s is -`fill_cpu_ms` and about 1s is marginal. The fill is host compiles: 90 `compile.emit` runs (~539s) -plus the frontend and reconcile runs of 244 fixture compiles. Fourteen v1-checker witness modules -pay all of it, led by `test.claim.declared_type_inhabitance_direct_call_witness` (~210s), -`infer_function_value_argument_arrow_witness_test` (~103s) and -`declared_type_expected_type_path_witness` (~84s). Summed across all claims, the run's own -`claim_cpu_observed_total_ms` is about 10s. - -Of the 343 pure-share fills paid in preparation, **298, totalling about 277s, report -`consumer_claims=0`**: no claim this run planned read them. The 45 that some claim did read total -about 39s. - -P1, P2 and P4 hold on this reading. P3 holds on #12506's `[cross-claim-demand]` rows: -`cref_assemble claims=6 evals=6`, `fps_assemble claims=5 evals=5`, `mbp_assemble claims=2 evals=4`. -Each claim re-runs `assemble_program_from_ingest` on the same module-constant fixture, at roughly -0.6–0.8s per evaluation and about 250k–325k eval steps per claim. The new-witness budget is 72,300 -steps. - -## Causes, ranked by floor time removed - -**C1. A fixture compile re-compiles the live closure the floor has already prepared (~10 min).** -The earliest unjustified boundary is `cli_run::emit_host` `compile_dag_diagnostic_census_uncached` -and its sibling `compile_dag_rust_emit_check`. For each distinct fixture they rebuild -`build_module_path_index_from_witness_roots`, resolve the fixture's imports from disk, and run -`compile_sources` over the whole closure. The contract only requires the fixture module to be new; -the closure is the prepared subject, which is already compiled under the same inventory digest -these memos key on. The memo is keyed per source, so it only absorbs repeats of the same text and -cannot reach the shared work across sources. -Repair class: **share a context at the least common ancestor.** Compile the fixture module against -the prepared closure's resolved and reconciled state, so only the new module is compiled. This is -#11401's R1 at fixture grain. Keep one claim that runs a fixture compile cold, as the inhabitance -claim for the real path. - -**C2. The whole gate closure is reconciled for a small diff (~8–11 min).** Here `seeds=259` -produced `closure=2585`, and `compile.reconcile` took 8 minutes. I have not located the boundary -yet. The reconcile cost overlaps the type_env PR-2 slowdown, which calm-pike-525 owns (#13008, -#13009). Take that attribution as the input here; I am not redoing it. The open question for this -plan is narrower: does any planned claim demand a reconciled closure, or only a resolved one? If -only resolved, reconcile beyond the planned claims' reach is redundant demand. -Repair class: **delete redundant demand**, if that question confirms it. - -**C3. The warm phase fills producers no planned claim reads (~4.6 min).** The roster -`v2.workflow.floor_pure_producer_share` is warmed for everything in the gate closure, but the run -plans 673 of 26,150 declared claims. The earliest unjustified boundary is the warm phase's -demand: it is keyed on the roster intersected with the closure, not on the roster intersected with -the planned claims' reach. -Repair class: **delete redundant demand.** Warm only producers that some planned claim reaches. -`body-reach-selection` already computes that reach. -Before cutting, one caveat needs checking. `unattributed_hits=399` counts hits outside the fold, -so confirm the zero-consumer rows are not being read by preparation itself. - -**C4. Small items.** There is a second full parse of the pool after preparation (~1 min; share -the parse). The ~6.5 minutes of wall-bound publication and wet-witness time is not analysed here. - -## Is #12506's overage a general cause? Yes: C1's shape, one layer up - -The N7 witnesses intentionally execute the real front end over a fixture; their header argues -that the red is not authorable against a supplied index. A real path is legitimately demanded once -per fixture. What is not legitimate is paying it once per **claim**. The floor isolates claims, so -every claim is an isolated consumer of a pure function of module-constant text. DESIGN §2 says -such consumers create **one reuse obligation at their least common visible ancestor**. Today that -obligation is discharged only by hand. An author must make each producer nullary, roster it, and -re-verify, which is the restructure swift-wren-597 and bright-ant-369 are now doing. The roster -has 343 entries, and that number grows with every PR in the class. The next N7 PR will hit the -same wall for the same reason. - -Two facts make it general. - -1. **The obligation is derivable but authored.** `[cross-claim-demand]` already computes the - signal: claims ≥ 2, keyed arguments, module-constant input. Rostering by hand is a second - authority over a fact the demand graph carries (§3). -2. **The margin charges the shared fill to every claim.** `floor_enrolment_margin` is an honest - policy budget under §5. It is denominated in deterministic eval steps, from a declared p90 - envelope. But it is applied to marginal work plus fill. Without a provider, every claim of the - module re-pays the fill. So splitting a witness into more claims multiplies cost rather than - isolating it, and the budget measures the absent provider rather than the claim. - It is the first-toucher defect of gunbc#8455. - -## Recommended first PRs - -1. **C1: compile fixtures against the prepared closure** in `emit_host`. This is the largest - removal, about 10 of 12 fold minutes. Postcondition: the 14 modules' `fill_cpu_ms` collapses - to the fixture module's own compile, and `[floor-shared-fill]` shows one closure fill - `paid_by=`. -2. **C3: scope the pure-share warm to the planned claims' reach.** This is small and about 4.6 - minutes. Postcondition: `prepared-subject-warm` reports no `consumer_claims=0` fills. -3. **Derive the pure-share obligation from `[cross-claim-demand]` and restore the upload of the - claim-cost and cross-claim TSV artifacts.** This also restores the named instrument this note - had to read from the log. Postcondition: an N7-class witness module whose claims share one - fixture is admitted with no rostering edit, and `floor_cost_distribution_instrument` runs - against a current run. - -Item 3 is the one that releases the N7 lane in general. Items 1 and 2 are the larger time -removals. diff --git a/docs/plans/gcp-iam-bootstrap.md b/docs/plans/gcp-iam-bootstrap.md deleted file mode 100644 index 0f16550d167..00000000000 --- a/docs/plans/gcp-iam-bootstrap.md +++ /dev/null @@ -1,280 +0,0 @@ -# Commission the approval-gated GCP IAM workflow - -The existing `gcp_iam_converge` fleet workflow needs its own cloud identities -before it can request approval and configure other workflows. This bootstrap -commissions that foundation from the existing IAM declarations. It does not -depend on the workspace allocation branch. - -## Entry points - -`gunbc.auth.gcp_iam_bootstrap.iam_bootstrap_plan` prints the exact roles, -permissions, bindings, deny permissions, and workflow admission condition without -cloud effects. `iam_bootstrap_apply` uses an explicitly supplied operator token -file and a fresh receipt identity: - -```bash -export GUNBC_GCP_ACCESS_TOKEN_FILE=/path/to/private/operator-token -export GUNBC_IAM_BOOTSTRAP_RECEIPT=operator-bootstrap-unique-attempt -export GUNBC_IAM_BOOTSTRAP_AUTHORITY_ID=stable-authority-operation -export GUNBC_IAM_BOOTSTRAP_AUTHORITY_EXPIRES_AT=2026-09-28T23:00:00Z # illustrative; set once for the intended operation -export GUNBC_IAM_BOOTSTRAP_PROBE_ID=stable-probe-operation -export GUNBC_IAM_BOOTSTRAP_PROBE_EXPIRES_AT=2026-09-28T23:00:00Z # illustrative; set once, at most one hour away -systemd-run --user --scope -p MemoryMax=6G -p MemorySwapMax=0 --quiet \ - ./target/release/gunbc run --source-root dag --source-root src/v2 \ - --entry dag/gunbc/auth/gcp_iam_bootstrap.dag \ - --function iam_bootstrap_apply -``` - -`iam_bootstrap_plan` (same roots and entry, `--function iam_bootstrap_plan`) prints the -operator-supplied intent variables the run reads; this block illustrates them. - -The token file must be private and short-lived. No token belongs in a command -argument, source file, receipt, or GitHub variable. The ongoing workflow never -falls back to this operator token. No service-account key is created. - -The missing deny-policy permission is a modeled dependency, not a console task. -Under the operator's explicit authorization, the organization IAM writer can publish a -time-limited `roles/iam.denyAdmin` grant to `user:brian@gunb.ai`, install the -protection policy, then remove exactly that grant. GCP documents -[Deny Admin](https://docs.cloud.google.com/iam/docs/deny-access) and -[time-limited conditional bindings](https://docs.cloud.google.com/iam/docs/managing-conditional-role-bindings). -The account used for continuing automation never receives this grant. - -The operation ID and absolute UTC expiry identify one desired authority lease. -The maximum remaining duration is one hour. Retries retain both values and the -journal; they do not recalculate the expiry. A new lease requires a new explicit -intent. The current credential must be able to read/write organization IAM; if it -cannot, convergence reports that upstream dependency. This is an observed -boundary, not a declaration that organization IAM authority must forever be manual. - -## What bootstrap owns - -1. Observe and enable the declared APIs with independent readiness readback, then - create or exactly read back the dedicated IAM workflow pool/provider and its - observer/apply accounts. Create only bare pools and accounts for declared - target federations, so resource-local administrative roles can be attached. -2. Create or exactly read back the existing four apply role definitions and - three resource-specific observer roles plus one single-permission bootstrap probe role. Existing drift refuses adoption. -3. For an absent deny policy, converge the temporary operator authority, install - and read back the policy, and retire the exact temporary grant. Cleanup is - attempted on ordinary failure too; later capabilities and workflow trust require - successful cleanup. Existing exact deny policies need no new authority grant. -4. Add declared capability bindings with policy etags, preserving foreign cells. -5. Converge a time-bounded probe grant for the verified operator on only the - observer/apply accounts. The custom role contains only - `iam.serviceAccounts.getAccessToken`. Check access using 600-second tokens: - observer reads must work, and the apply account must receive HTTP 403 reading - a credential that the observer just successfully read. -6. Remove and independently verify absence of both temporary probe grants. Cleanup - also runs after ordinary failure and has a standalone recovery entry. Issued - tokens retain their original 600-second lifetime; deleting a grant is not - revocation of an already minted token. -7. Only after these checks and cleanup, add workload-identity impersonation - bindings for the existing, pinned IAM workflow. - -Target providers, workload impersonation, and target secret-access grants remain -in the existing approval-gated convergence plan. Bare target containers do not -activate those workflows. The target roster comes from `gcp_iam_converge_targets`, -including its current private-publisher and Namecheap entries; it is not copied -from the older allocation branch. - -Permission probes are diagnostics, not an authorization oracle. Configuration -readback and the real observer/apply secret-read control are separate checks. -Successful bootstrap still requires a real GitHub OIDC/approval/apply run before -the ongoing workflow can be called operational. - -## Recovery and evidence - -Each invocation creates `target/gcp-iam-bootstrap--started.txt` before -effects, then a separate receipt for every attempted stage. A refusal stops -later stages. A process loss or asynchronous creation may require a new attempt -identity; it rereads named resources and their current policies. A started -receipt is not completion evidence. Existing policy drift never authorizes -automatic privilege expansion. - -The existing `fleet_converge_workflow.dag` job already performs observer WIF, -request/approval, apply WIF, live approval recheck, and independent readback. -Bootstrap supplies its missing cloud prerequisites rather than adding another -approval workflow or a pasted-token path to secret consumers. - -## Validation - -The witness `test.claim.auth.gcp_iam_bootstrap_witness_test` is discovered and -executed by the required floor lane (`claim_executor --required-ci --required-lane -witnesses`) on every pull request and merge group that touches its closure; no -hand-assembled source root is needed. Qualification must also record -the binary hash, source revision, pure plan, missing-credential refusal, cloud -stage receipts, and a real approval-workflow run when available. - -API contracts follow Google Cloud's official references for -[custom roles](https://docs.cloud.google.com/iam/docs/reference/rest/v1/projects.roles/create), -[project policy updates](https://docs.cloud.google.com/resource-manager/reference/rest/v1/projects/setIamPolicy), -[workload identity pools](https://docs.cloud.google.com/iam/docs/reference/rest/v1/projects.locations.workloadIdentityPools), -and [deny policy creation](https://docs.cloud.google.com/iam/docs/reference/rest/v2/policies/createPolicy). - -## Current commissioning standing - -Bootstrap completed successfully at source `85bdb4e19` on 2026-09-29. All ten -stages passed: required API readiness, identities, roles, deny protection, -temporary deny-authority cleanup, capabilities, temporary probe authority, -effective access checks, probe-authority cleanup, and workflow trust. The observer -read the pinned approval credential and the writer received exact HTTP 403 for it. -Both temporary probe grants and the earlier Deny Admin grant were verified absent. -The operator token file was removed. See -`receipts/gcp-iam-services-2026-09-29/README.md` for evidence and limitations. - -The existing approval-gated workflow was then dispatched on main as run -`36505895733`. Bootstrap completion does not establish that workflow's approval -or apply acceptance, nor VM commissioning. Earlier refusal receipts remain as -historical evidence of the dependencies repaired along the way. - -## Temporary-authority dependency and recovery - -```mermaid -flowchart TD - P[Read project ancestry; verify pinned organization] --> R[Read organization IAM] - R --> W[Organization IAM write authority] - W --> I[Persist fixed operator lease intent] - I --> E[Elect one publication] - E --> G[CAS grant and read back exact conditioned cell] - G --> D[Install and read back deny policy] - D --> C[CAS remove owned cell and read back absence] - G --> F[Deny installation refuses or lease expires] - F --> C - C --> T[Persist retirement; continue bootstrap] - E --> U[Interrupted or unreadable publication] - U --> O[Reobserve the same intent and cell] - O --> C - U --> Q[Absent cell with unresolved write: keep obligation outstanding] -``` - -`target/gcp-iam-authority--*.txt` records the exact project, organization, principal, -role, condition, and expiry before effects. Keep these records with the operator -recovery workspace; they are local bootstrap state, not yet protected fabric -storage. Intent drift or unreadable records refuse. A create-only election means -at most one grant request is issued for this operation, even across retries. -An acknowledged successful publication or a grant observed present closes that publication; cleanup can recover a crash -between removal and recording retirement. A definitive publication HTTP 4xx also -closes the no-effect attempt. Other unresolved write outcomes remain outstanding -when a read sees absence. Neither a timeout nor expiry proves physical cleanup. -The cloud condition independently bounds effective access after process loss. - -The shared policy reconciler preserves unrelated grants and conditions, uses -version 3, and requires etags. Cleanup owns only the exact conditioned cell; -unconditional pre-existing administrator access survives. A retired operation -cannot publish again. `iam_bootstrap_authority_cleanup` drives withdrawal alone, -using the same authority ID, expiry, journal, and operator token-file input. - -The downstream account-impersonation readback still requires its actual authority. -That is a separate dependency to classify when reached, not permission to invent -an approval or give the continuing workflow self-elevation rights. - - -### Role grant scope correction - -The first dependency attempt was rejected with HTTP 400 because Deny Admin cannot -be granted on a project. The current model uses the role's documented -[organization-only grant scope](https://docs.cloud.google.com/iam/docs/roles-permissions/iam#iam.denyAdmin). -A read-only Resource Manager observation established that active -`projects/582015116396` (`gunbai-secrets`) belongs to -`organizations/266638272282`. The authority declaration pins that organization; -publication rereads the project and follows validated folder parents, refusing -unknown, changed, cyclic, or incomplete ancestry. No project move or organization -creation is inferred from missing ancestry. - -This temporary role is organization-scoped and time-limited; it is not represented -as project-scoped access. Only the named human operator receives it. The modeled -actuator still installs only the exact project deny policy. Cleanup addresses the -pinned organization even if the project later moves. These local journals support -one shared operator recovery workspace; they are not a distributed election across -independent workspaces or hosts. The original project-level attempt was rejected, -not an effective grant, and its historical receipt remains preserved. - - -A newly read-back authority binding may not yet be effective. Deny installation -permits at most 30 attempts separated by ten seconds, only after an explicit HTTP -403 and while the original lease remains live. Transport/server faults are not -retried as if nothing committed. An accepted create is followed by at most seven -readbacks separated by ten seconds, with no additional creation request. Exhaustion -or clock/deadline refusal enters the same authority cleanup obligation. - - -Before publishing temporary authority, the native Google UserInfo operation must -establish subject `116084671989231734979` (previously confirmed by the operator), -verified email, and the pinned IAM member `user:brian@gunb.ai`. Live UserInfo readback -identified this primary email; the earlier `briansrls@gunb.ai` spelling is not used -as the authority identity. A different subject with the same email, missing claims, -or changed transport email refuses publication. Cleanup does not require the old -operator credential's identity: an authorized recovery credential may remove the -already-pinned exact cell. No access token is put in a URL or command argument. - -## Exclusive initialization of an empty pool policy - -On 2026-09-29 the operator confirmed that no other administrator will update -these pool policies during bootstrap. This supplies an operational exclusivity -assumption, not a provider-enforced CAS token. Normal policy updates still require -an observed nonempty etag. - -The one-off initializer is limited to pool resources already derived by -`iam_bootstrap_capability_bindings`: `github-heal-publisher`, -`github-heal-publisher-private`, `github-mtcollins1-boot`, -`github-namecheap-dns`, and `github-gcp-iam-converge`. Each initial policy contains -all and only that pool's declared capability cells. The last pool receives -observer capability only; the apply principal cannot administer its own trust. -It does not install target OIDC providers or account impersonation grants. - -An invocation must explicitly select the confirmed window using -`GUNBC_IAM_BOOTSTRAP_POOL_WINDOW=exclusive-pool-policy-initialization-20260929`. -The native initializer verifies the same stable Google administrator identity, -requires an empty policy without a revision, elects one publication through a -create-only local journal, and performs independent readback. Completion requires -exact declared cells and a returned revision before later CAS updates are allowed. -A failed or ambiguous write retains the journal and cannot issue another -unconditional request. Recovery can close it only after matching readback. -Keep the same journal workspace across retries; do not delete election records or -run independent copies. The operator's exclusive window must still be in force -at actual execution. This is not a standing permission for future initializations. - -This slice has not been applied to Google yet. An approval receipt does not -create an administrator credential. The normal `gcp-iam-converge` workflow first -federates as `iam-observe`, files its request, and only after approval federates as -`iam-converge`. Both trust grants are deliberately the final bootstrap stage. -Consequently that workflow cannot bootstrap its own initial trust. An existing -administrator credential is a remaining explicit dependency; this environment -has neither gcloud nor a configured readable GCP token file. No credential or -refresh token is stored in these sources or receipts. - - -## Temporary probe authority - -The live pool run completed all capability grants, then the administrator token -received HTTP 403 for `iam.serviceAccounts.getAccessToken` on `iam-converge`. -That is now another explicit bootstrap dependency, not a console instruction. -`GUNBC_IAM_BOOTSTRAP_PROBE_ID` and `GUNBC_IAM_BOOTSTRAP_PROBE_EXPIRES_AT` pin its -operation and deadline, at most one hour away. The two exact-account conditional -grants name the verified human identity. They share the deny authority's journal, -publication election, etag update and cleanup machinery. No signing, key-creation, -policy-write or project-wide impersonation permission is included. - -Google documents the [access-token permission](https://docs.cloud.google.com/iam/docs/service-account-permissions). -Permission propagation retries accept only explicit HTTP 403, at most 85 attempts -with five-second waits (seven minutes of waiting), and stop at the original deadline. Other failures are not -converted to propagation. `iam_bootstrap_probe_cleanup` can recover both exact -grants without rerunning bootstrap; keep its journal and original environment. -Trust requires cleanup success. A retired probe operation cannot grant again. - -The first probe lease was removed after 30 token-mint refusals over roughly one -minute. That did not establish a permission-design defect: Google documents policy -propagation as typically two minutes, potentially seven or longer. The bounded -retry now permits seven minutes within the original lease; no role is widened. - -### Re-running after an interrupted probe-authority stage - -The probe stage first reads back the operator identity, and only then does -`iam_bootstrap_cell_authority_reconcile` pin an intent or elect a publication. A run that -stops inside that readback therefore leaves no `gcp-iam-probe-*` journal and no grant. -Re-run with a fresh `GUNBC_IAM_BOOTSTRAP_RECEIPT` (the started receipt is create-only): -earlier stages read back and adopt what exists (the deny stage elects no temporary -authority when its policy already reads back), and the probe stage elects fresh under -the same or a new probe intent. Any `gcp-iam-probe-*` or `gcp-iam-authority-*` journal -that does exist must be kept with its original environment, as above. diff --git a/docs/plans/git-plumbing-extdeps-authority-design.md b/docs/plans/git-plumbing-extdeps-authority-design.md deleted file mode 100644 index f81f0bd5381..00000000000 --- a/docs/plans/git-plumbing-extdeps-authority-design.md +++ /dev/null @@ -1,233 +0,0 @@ -# Git plumbing as an extdeps authority - -LANDED, in part, by `GIT-PLUMBING-0`. This note scoped the dissolution of a split Git authority; -the operation family and the whole devboot migration are now in tree, and §7 records exactly which -acceptance rows landed and which did not, so the note reads neither as a plan for done work nor as -a receipt for undone work. - -## 1. The finding - -`extdeps.git` models Git's **porcelain and read** surface. `gunbc.devboot` privately models Git's -**object-store plumbing** surface, as raw argv. Neither fact is written down, and together they are -one authority split at the surface grain (DESIGN §3), not one un-lifted operation. - -Measured against the current tree: - -- `extdeps.git` `git` service declares ~35 operations — `CommitInRepo`, `MergeNoEditInRepo`, - `RevParseInRepo`, `LsFilesUnmergedInRepo`, the diff family, config, fetch. It declares **no** - `write-tree`, `commit-tree`, `hash-object`, `update-ref`, `checkout-index`. The sole `update-ref` - spelling in `extdeps/` is an argv helper that hardcodes `FETCH_HEAD` and carries no expected-old. -- `gunbc.devboot` carries the plumbing as argv: `update-ref` ×6, `hash-object` ×5, `cat-file` ×5, - `read-tree` ×3, `write-tree` ×2, `unpack-file` ×2, `rev-parse` ×2, `commit-tree` ×1. -- `extdeps.git.object_store` `git_model_update_ref_exact` is the **pure** exact-old transition, and - it is already rich: recursive symbolic-ref dereference with the symbolic ref retained, stale, - missing, duplicate, symbolic cycle, repository-invalid, format mismatch, target unavailable, and - target-kind-refused arms. Its own `git_modeled_ref_transition_boundary_note` already rules that - P3 must consume an executing transport observation and never promote the model to `Applied`. - -So the pure model exists and is good; the *operation* does not; the only realization in the tree -is private to one service. - -## 2. Why this is a mis-aimed dissolution, not merely missing work - -`gunbc.devboot.transport` is disciplined: all 30 raw argv sites are registered `RetainedWitnessRun` -rows with a reason, and the module note claims no unmarked `WitnessBin.Run` call survives review. -Every row declares the same dissolution target — `gunbc.host_effect_realize` `host_effect_apply`, -shell→intent Phase 2. - -That target is correct for roughly 13 rows (`rm_rf`, `mkdir`, `cp`, `mv`, `chmod`, `sleep`, `uname`, -`ldd`, `sha`, `cargo_build`, `rustc_version`, `execute_artifact`, `rm_file`) and **wrong for -roughly 17** (the git plumbing above). A Git operation's terminus is a modeled `extdeps.git` -service operation — DESIGN §3 (a), the dependency's interface shape, which extdeps owns — while -`host_effect_apply` is §3 (b), a transport. Routing `git write-tree` into a generic host-effect -actuator dissolves the *transport* and leaves Git's interface permanently unmodeled. - -The roster thus conflates two dissolution classes under one target, and the git half's trigger -cannot fire correctly. This PR discharges declared debt rather than adding surface. - -## 3. The operation family - -One closed mutation family, three arms. Create, exact advance and exact delete are distinct -transitions with distinct preconditions in Git's own interface, not distinct authorities. - -``` -GitExactRefMutation - = GitCreateRefIfAbsent { name, new } - | GitAdvanceRefIfExpected { name, expected_old, new } - | GitDeleteRefIfExpected { name, expected_old } -``` - -`expected_old: Optional` is **refused**. An absent expected-old would conflate four -states with different remedies — create-only, update unconditionally, the caller did not supply the -fact, the caller does not know the current value — the state-space conflation DESIGN §5 names. The -empty string devboot passes today is a transport encoding of Git's protocol, never a semantic value, -and disappears from the model. - -The pure model generalizes to the family while preserving every existing arm; `git_model_update_ref_exact` -becomes the advance arm's caller-facing spelling, not a second authority. Create and delete must -inherit — not re-derive — repository decode, symbolic dereference, format consistency and -target-kind validation. - -### Outcome and read-back - -The operation does not decode Git's stderr into semantic causes. Devboot establishes why: losing a -race and failing to reach the store both exit 128 (`produce.dag` records this by measurement), so -exit codes do not discriminate and messages are not a structural interface. The outcome is decided -by **observation**, not by the process result alone: - -``` -exit 0 + desired state observed => Applied -nonzero + expected state no longer held => PreconditionNotHeld -nonzero + expected state still held => ExecutionRefused -any exit + state not observable => ObservationRefused -exit 0 + desired state not observed => ReadBackMismatch -``` - -`Applied` is unreachable without an independent read-back, as the existing boundary note already -requires of P3. Consumers project their own remedies from the common fact — a refused advance whose -observed value differs is a stale parent and re-evaluates internally; a refused create whose ref is -present is an attach. - -## 4. The migration is the discriminator - -Replacing devboot's argv sites is the point of the PR, not a cleanup tail. Each site must declare -which transition it is; one already fails that test: - -**`gunbc.devboot.build` `publish_produced_tree` writes the produced-answer ref unconditionally.** -The ref is per-request-token and holds one build answer; the function is reached from both the -success path and `serve_refusal`. A re-serve of one token therefore silently overwrites a previous -answer, and a client that read the old answer and one reading the new disagree with no signal. The -ref is write-once by intent, so the correct transition is `GitCreateRefIfAbsent`, and a second -publish for one token is a real conflict that must refuse loudly. - -No unconditional arm is added to preserve that call. A site that can name neither create nor exact -advance refuses until its intended transition is modeled. - -## 5. The cut is the whole plumbing surface, not one operation - -DESIGN §3's replacement-migration rule is greedy root-first: attempt the maximal cut and descend -only on an exact refusal — Y-incomplete, an escaping consumer, or opaque — because a separate -deeper cut pays admission surfaces, receipts and X-compatibility work the root cut never pays. - -An earlier draft cut at ref mutation alone and left the other seven plumbing families private to -`gunbc.devboot`, with no refusal behind it. Ref mutation's interesting transactional guarantee is a -reason to sequence the *proof* there, not to leave the authority split standing. Descoping would -have left devboot the private authority for Git plumbing while each later family paid its own -admission round — the attractor the rule exists to prevent. - -So the cut is the split itself: **no `gunbc.devboot` declaration constructs a Git plumbing argv -list, and the corresponding `RetainedWitnessRun` rows delete rather than being repointed.** The -census in §1 is the deletion population, not a work-list for later. - -The upstream-shaped surface, joining `extdeps/git/`'s existing decomposition rather than arriving -as one generic operation: - -``` -object database HashObjectWrite · CatFileExists · CatFileSize · CatFileRead · UnpackObject -index and tree ReadTreeIntoIndex · CheckoutIndexToPrefix · WriteTreeFromIndex -commit CommitTree { tree, parents: NoParents | Parents { first, remaining }, message } -ref store ObserveRef · CreateRefIfAbsent · AdvanceRefIfExpected · DeleteRefIfExpected -``` - -Repository-scoped operations that already exist — `RevParseInRepo`, `CatFileBlobInRepo` — are -consumed, never reintroduced under a plumbing module. - -Acceptance: - -1. The three-arm pure ref model in `extdeps.git.object_store`, preserving every existing refusal arm - (repository-invalid, stale, missing, duplicate, symbolic cycle, format mismatch, target - unavailable, target-kind refused). Create and delete inherit that decode and dereference; they do - not re-derive it. -2. Repository, index path, ref, OID, parent set and output path are typed operation inputs. Git's - empty-old encoding exists only in the `CreateRefIfAbsent` transport projection. -3. Executed against real repositories, with independent read-back on every success: create succeeds; - a second create refuses with the original value intact; exact advance succeeds; stale advance - refuses; exact delete succeeds; stale delete refuses. -4. Devboot's behavior survives unchanged except where the migration intentionally exposes a defect - (§4). -5. The ~17 Git rows leave `gunbc.devboot.transport`; the ~13 host-effect rows remain, still pointing - at `host_effect_apply`. - -Out of scope: any SCM behavior. This PR makes the plumbing an authority with one real consumer cut -over; M0, in its own PR, is the second consumer and proves the surface is not devboot-specific. - -## 6. Deliberately undecided - -Whether `gunbc.devboot` should adopt `std.materialization_provider` is **open**, and the roster -argues against assuming it. All seven rows of `gunbc.materialization_provider_targets` are -compiler-internal caches identified by a `CacheInterfaceId` from a cited catalog row; devboot is a -cargo-build artifact service with no such row and a different grain. The roster already carries a -declared exclusion of this exact shape — `extdeps.realization.artifact_store_fs` sits *under* the -contract as a transport and cannot inhabit the target's cache field at all — and devboot's Git -object database plausibly occupies the same position. `ArtifactRequest` is additionally a closed -coproduct with an explicit `request_fold`, so a new arm edits every consumer. - -Two facts found while scoping make the question not merely open but currently **unanswerable in -the adoption direction**, recorded so the next author need not rediscover them. Devboot decides -reuse by `build_subject_equal` over the whole `BuildSubject` after the digest narrows to a candidate -— the digest is documented there as an index, not a decision — whereas `provider_serve` decides on -`ContentHash` equality alone. And `MaterializedArtifact` retains only `request_key` plus its parts, -so the contract holds no subject that could differ from a request's: the same-key-different-subject -state is not representable, which is why the provider's witness suite has no collision test. -Routing devboot into the contract as it stands would lower a wall rather than share one. - -That is a finding about `std.materialization_provider`, not about devboot or this PR, and belongs -to its own lane with its own operator ruling — the module is contract-stable after four review -passes and seven consumers are scheduled to adopt it. Nothing here depends on its outcome: if the -contract later grows an exact-comparison door, devboot's fold is the reference implementation; if -not, devboot remains a build-domain materializer whose storage realization is Git. Either way its -uniformity work is exactly §5's cut and nothing more. - -## Dissolution trigger (DESIGN §6) - -This note dissolves into the carriers it names when the family lands: the model and operations in -`extdeps.git`, and the deleted rows in `gunbc.devboot.transport`. The §6 materialization finding -does not dissolve with it; it is owed a lane of its own. - -## 7. What landed, and what did not (added when `GIT-PLUMBING-0` merged) - -Landed: - -- `extdeps.git.plumbing` — `service git.Plumbing`, seventeen operations covering the object - database, index and tree, commit, repository init and the ref store. Repository addressing is one - parameter (`GitRepositoryAddress`, worktree `-C` versus store `--git-dir=`) rather than two - operation families; `GIT_INDEX_FILE` is an operation-owned transport prefix rather than an `env` - spelled at a call site. Every operation reports `exit_code` and `stderr`, never a `success` Bool, - and none declares an `exit` block — both per the notes in `extdeps.git` that already ruled it. -- `extdeps.git` gains `FetchForcedRefInRepo` and `PushForcedRefInRepo`. These are not plumbing and - are not publication; they are the repository-addressed forced-refspec exchange devboot's git-only - channel runs on, which no operation in the corpus could express. -- `GitExactRefMutation` — the three arms, with `expected_old` refused as an `Optional` exactly as - §3 argued. Git's empty-string old value now appears once, inside `CreateRefIfAbsent`'s transport - row. -- The outcome table of §3, as `git_ref_mutation_outcome`, decided from an independent read-back - rather than from the exit code. `Applied` is unreachable without the read-back. -- All thirty-five git argv sites in `gunbc.devboot` are gone; the seventeen `RetainedWitnessRun` - rows are deleted rather than repointed. The thirteen host-effect rows remain, still pointing at - `host_effect_apply`. -- §4's discriminator: `publish_produced_tree` wrote the per-token answer ref unconditionally and now - creates it, so a re-serve of one token refuses loudly instead of silently replacing an answer a - client may already have read. It returns a typed `ProducedPublication` rather than a Bool, because - the Bool could not carry which of the five outcomes occurred. The lease take and the three - artifact bindings are creates for the same reason; the stranded-lease break is an exact delete. - -Did NOT land, and is not a stall: **acceptance row 1** — generalizing the *pure R0 model* -`git_model_update_ref_exact` in `extdeps.git.object_store` to the three-arm family. Left undone -deliberately, not forgotten: that model simulates a repository state, nothing in this cut consumes -it, and devboot decides from an executing observation instead, so generalizing it here would have -added authority with no consumer — the §6 tell this repository treats as a finding. -NEXT TRIGGER: the first consumer that simulates a ref transition rather than performing one, which -is the P3 boundary `git_modeled_ref_transition_boundary_note` already names. Until then the R0 model -answers exactly the question it has always answered, and the execution-side family answers the other -one. - -**Acceptance row 3 (execution against real repositories with independent read-back on every -success) is likewise NOT claimed by this PR; the bound is stated rather than left to be inferred -from a green witness suite.** What executes is the DECISION: `git_ref_mutation_outcome` -takes the three values a caller holds after an attempt, so all five arms — including the pair that -share exit 128 — are reachable hermetically and are claimed in -`test.claim.git_plumbing_ref_mutation_witness_test`. What does not execute is any -`git.Plumbing` operation, so a change that broke a transport argv while leaving the decision intact -would keep those witnesses green. The residue is the transport spellings; the wet receipt that would -close it is the same shape as -`test.manual.git_upstream_model_execution`'s and belongs with a live devboot run. diff --git a/docs/plans/glm-group-b-workload-qualification.md b/docs/plans/glm-group-b-workload-qualification.md deleted file mode 100644 index 89578838827..00000000000 --- a/docs/plans/glm-group-b-workload-qualification.md +++ /dev/null @@ -1,163 +0,0 @@ -# GLM group B: workload qualification after the variant-e capture - -Status 2026-09-28. Supersedes the "per-step timing experiment" proposal. External review -2026-09-27 (serving mechanisms) supplies the corrections this plan encodes; the variant-e -capture (2026-09-27, watchdog ts=1790547523) is the evidence base. - -## What the capture established — and what it did not - -Established: - -- Six concurrent cold 262K submissions: five completed (~5 min apart), the sixth's 1800s - client bound expired while the engine was still making progress. Zero preemption log lines - anywhere; the preemption/recompute-storm hypothesis is DISPROVED for this arm. -- The watchdog stacks show the engine core waiting on workers with a worker on-CPU — no - deadlock shape. -- The budget arithmetic closes the observation: 6 × 262,144 = 1,572,864 fresh tokens at the - observed ~874 tok/s aggregate ≈ 30 minutes of engine work; the five completions plus the - bound expiry fit inside it. - -Corrections now law (from the review): - -- Six cache seats are SIX REQUEST STATES, not six compute lanes. TP4 splits every operation - across the four ranks; "the engine serializes seats" is not a mechanism, it is one shared - compute pool under one shared 2,048-token step budget (running-before-waiting admission in - the pinned scheduler). -- A max_tokens=1 completion latency is a COMPLETION latency (input processing, queueing, - prefill, sampling, delivery), not an instrumented GPU-prefill duration. -- Completion order alone cannot distinguish driver-serial from budget-starved from - shared-throughput. Every timing claim binds to the frozen run plan / launch receipt, never - to the live spark_active_workload_variant selector (a mid-flight flip or a second launcher - — both happened 2026-09-27 — must not be able to relabel a run's mode). Runs interrupted by - another controller are CONTENDED evidence and excluded from latency claims. - -The remaining open question: is ~874 tok/s aggregate (≈2.3s per 2,048-token step) an -EFFICIENT execution of that work on this realization? That is an attribution question — -sparse-indexer, attention, collectives, host gaps — and it is answered by operation-level -timing on representative steps, not by more repro campaigns. - -## Corrections adopted 2026-09-28 (review of the W1 reading) - -1. **W1's observation is narrower than first reported.** One cold 262K completing in 323.16s - without six-way contention establishes that the slow near-full-window request OCCURS - without the cohort. It does not establish compute-bound execution and does not make - ~5 minutes an immutable price. The observation stays bound to its exact - runtime/profile/workload subject; W1b attribution remains REQUIRED. -2. **The "26k tok/s" engine log lines are reconciled and retired as a progress signal.** The - pinned metric producer credits prompt tokens at FIRST-OUTPUT processing divided by the - logging interval — completion accounting. Per-step progress evidence must come from - scheduled-token/per-step observation (W1b), never from those lines. -3. **gap_mib=2 does NOT discharge any rank fragmentation obligation.** The present producer - measures head-host maximum-process footprint growth above a post-prefill reference — not - allocator fragmentation — and observes the head host only, not the other ranks. The - observation is preserved under that actual meaning; the instrumentation-to-obligation join - needs repair (an allocator-level, per-rank measurement) before any fragmentation obligation - can close. -4. **Wet work is strictly sequential under exclusive cohort ownership.** W1b and other - investigations are prepared independently, but no second run may replace the engine whose - cache or timing another run is observing (the 2026-09-27 mid-flight teardown is the named - counterexample). -5. **W2 is an explicit cache-reuse qualification, not a two-seat latency comparison.** Cold - prime, exact replay, genuine suffix continuation, and a controlled prefix-miss leg, all - bound to the same admitted engine/cache incarnation, recording ACTUAL cached and computed - token counts (the response's prompt_tokens_details.cached_tokens, not inferred reuse) plus - request-level timing. A fast replay alone does not establish free general follow-ups. - -## The frozen objective and the unified qualification (2026-09-28 steering) - -**Objective: restore GLM at a qualified 262,144-token x 8-seat configuration, then stop.** One -deliverable: an operational eight-seat service with its exact acceptance bundle and persistent -readback. DeepSeek reuse continues independently and never becomes a deployment prerequisite. - -The closing qualification is ONE frozen eight-seat candidate and ONE exclusively owned -execution — not separate campaigns: - - freeze candidate (eight requested; executable derived; the old summary-based six-seat - derivation retained as floor only) - -> all-rank startup + resolved allocation evidence (the eight-seat candidate's OWN - allocation plan and per-rank phase bounds; the six-seat run's 1,035-block inventory is - a conditional expectation, never the verdict) - -> W2 cache-reuse legs (cold prime / exact replay / suffix continuation / prefix miss) - BEFORE any unrelated churn can evict the prefix under test - -> occupancy high-water evidence: live request identities, resident context/block demands, - concurrent decode/workspace shape, per-rank memory standing — eight submitted - max_tokens=1 requests do NOT establish eight simultaneously resident full-window - contexts; the run records the population it actually exercised - -> real decode + at least one real harness protocol interaction (one-token completions do - not establish tool-call parsing, multi-turn continuation, or sustained decode) - -> settlement: cancellation, its observed completion, deterministic stop - -> promotion through persistent convergence; deployed configuration, generation, and route - verified after the existing readiness/access gates - -The execution budget covers the WHOLE sequence (startup + readback + queued and executing -requests + reuse controls + settlement). A client deadline is never an engine failure, and a -client's disappearance is never proof its engine work or reservation was released — -cancellation and its observed completion ride the shared request lifecycle. - -Deployment gate vs follow-on (the release distinction): - - REQUIRED before normal serving: exact runtime/checkpoint/config/rank identity; complete - applicable memory bounds including the allocator allowance; ENFORCED context/concurrency - limits (the first operational admission policy is conservative about NEW cold-prefill - work, and the restriction is enforced, never aspirational); correct generation and - required protocol behavior; exclusive ownership, settlement, restart, readback; route - withdrawal and cancellation/recovery; any latency/reuse guarantee actually advertised. - - FOLLOW-ON unless explicitly promised: W1b performance attribution (blocks only if it - uncovers a correctness/resource defect or an explicitly required operating contract); - maximizing concurrent cold-prefill throughput; broad benchmark matrices; an unadvertised - "nearly free warm continuation" objective (a cache miss is not a deployment failure when - cold execution is safe); DeepSeek's complete deployment. - -Safety-term closure order for each open term: derive a defensible bound from the mechanism; -use a justified conservative bound where exact derivation is unnecessary (a conservative bound -covers the candidate's actual allocation behavior — never an arbitrary allowance beside a -green result); measure the specifically unresolved term where needed. - -## The three qualification workloads (replace the single cold stress test) - -W1. ONE COLD FULL-WINDOW REQUEST — the attributable cold-prefill cost. One 262K request, - nothing else admitted. Per-request timing: submission, engine arrival, first scheduled - work, prefill completion / first output, request completion, scheduled tokens per step, - plus operation-level durations and rank waits for a small number of representative - steps. The output is the cold-prefill service time with its dominant term named - (compute / indexer / communication / host / cache-recompute / unresolved). - -W2. CACHE-REUSE QUALIFICATION, four legs on one admitted engine/cache incarnation: (1) COLD - PRIME — a full-window body A, the cold reference; (2) EXACT REPLAY — A again, the pure - reuse measurement; (3) SUFFIX CONTINUATION — A plus a small new suffix, the genuine - follow-up shape; (4) PREFIX MISS — a disjoint body B, the control that must NOT reuse. - Each leg records the response's actual cached/computed token counts - (prompt_tokens_details.cached_tokens) and driver timing. Verdict shape: replay near-free - AND suffix near-free AND miss cold-priced means reuse is real and general for this - subject; a fast replay with a cold suffix means replay-only reuse; a fast MISS means the - measurement is lying. No leg's speed alone establishes free general follow-ups. - -W3. COLD PREFILL ALONGSIDE ONGOING DECODES — does admitting new work damage useful - interactive progress? N decode sessions established and streaming; one cold 262K prefill - admitted; the decode-latency standing before/during is the verdict's subject. - -## Service-promise shape this feeds - -The capacity intent stops being "262144 × N uniform seats" and becomes separate quantities: - - resident context capacity (the CacheFitProved wall — done: 6 proved, 8 hard ceiling) - active decode population (W3's subject) - new-prefill admission policy (W1's price, W3's coexistence standing) - per-step token budget (2048 today; raising it is an OPTIMIZATION CANDIDATE that - changes the activation/workspace subject — it goes through - the memory model before any service promise reads it) - cold-start latency objective - decode-latency objective while prefill is active - -The serving-liveness verdict (ArmServingLivenessVerdict, one open obligation) consumes W1–W3 -evidence; the watchdog remains capture-only instrumentation and never mints a verdict. The -stall predicate ("no forward progress within T under admitted occupancy") is a named modeled -term over the engine's own counters before any further watchdog-armed run — see the owner -directive 2026-09-27 on watchdogs not substituting for modeling. - -## Non-goals - -No preemption-flag tuning, no max_num_batched_tokens change, and no service-shape restoration -decision until W1 attributes the cold cost. Group B stays down in the interim; the arm stays -Unestablished. diff --git a/docs/plans/google-workspace-identity-convergence.md b/docs/plans/google-workspace-identity-convergence.md deleted file mode 100644 index 6f462549ca9..00000000000 --- a/docs/plans/google-workspace-identity-convergence.md +++ /dev/null @@ -1,108 +0,0 @@ -# Google Workspace identity/profile convergence (Cut 4) - -Owner directive 2026-09-27: the Google-side configuration the tracker depends on is modeled -and converged — "we need to model this via convergence"; the owner performs only the literal -initial clicks. External review 2026-09-27 (Workspace onboarding) supplies the acceptance -boundaries; this plan is its convergence-shaped reduction. - -## Law - -1. Every Google-side setting the product depends on is a DECLARED DESIRED FACT in the model. -2. Where Google exposes a supported READ, the setting is OBSERVED through it and the - observation is what standing claims consume. -3. Where Google exposes a supported WRITE, the setting is CONVERGED through the fleet's - approval-gated credential path (org_admin_credential, gcp_secret_access, the PR #12421 - IAM pattern). -4. Where Google exposes NEITHER, the setting is a MANUAL-ADMINISTRATION OBLIGATION with a - readback receipt — never a fabricated convergence API, never "established because login - worked once". -5. Authentication and profile acquisition are SEPARATE operations. A tenant selects ONE - profile source by policy: UserInfo (ordinary OIDC) or the authorized Directory source - (managed Workspace org). Never a runtime fallback chain. -6. The principal stays the only identity. Names/photos are mutable profile projections with - explicit source, provenance, freshness, and display-audience policy. - -## Surfaces and their convergence class - -| # | Google-side fact | Read API | Write API | Class | -|---|---|---|---|---| -| 1 | OAuth client registration (client id/secret) for the tracker | no supported read found in the reviewed Auth Platform documentation (2026-09-28) | none | MANUAL obligation (owner's initial clicks); the secret rides Secret Manager via the existing gcp_secret_access path; the deployment config is already modeled (oidc_deployment_config) | -| 2 | Auth Platform audience = Internal (project inside the gunb.ai org) | console readback (documentation reviewed 2026-09-28) | console-only | MANUAL obligation + readback receipt | -| 3 | API controls: THIS client's app access — Specific Google data (openid/email/profile scopes), top OU | console readback (documentation reviewed 2026-09-28) | console-only | MANUAL obligation + readback receipt | -| 4 | Directory sharing: authenticated-user basic profile fields (minimum); org-data sharing ONLY if assignee directory discovery requires it — the reason is recorded either way | Cloud Identity Policy API: `directory.external_directory_sharing` | no supported mutation | MANUAL change obligation + effective Policy API readback | -| 5 | Profile-editing policy (photo) org-wide | console readback (documentation reviewed 2026-09-28) | console-only | MANUAL obligation (already set 2026-09-27) + readback receipt | -| 6 | Managed user photos (assignee avatars, header) | Admin SDK users.photos.get (read-only scope admin.directory.user.readonly) | n/a (read-only source) | OBSERVED: periodic observation → profile projection refresh | -| 7 | Directory people discovery (assignee picker population) | People API people.listDirectoryPeople (directory.readonly) | n/a | OBSERVED: same projection, pagination + deletion semantics preserved (incremental sync lags writes; a completed sync is not a post-change readback) | - -The authorized backend identity for 6/7 (service account + domain-wide delegation, if chosen) -is a SEPARATELY APPROVED integration — never silently acquired, never required for basic -login. - -## Model shape - -- extdeps.google.workspace grows from a citation anchor into the external model of these - admin controls and directory/profile operations — Google's documented semantics only, no - gunbc policy inside. -- extdeps.auth.oidc keeps claims + UserInfo protocol + the sub-equality admission (a UserInfo - result whose sub differs from the ID token's sub is rejected whole). -- gunbc profile projection: revisioned, source-bound, with a declared refresh policy; a new - login MAY update it but is not its only producer; identity history is stable across profile - change. -- Photo vocabulary preserved end-to-end: PhotoReturned / ProviderDefaultPhotoReturned / - PhotoNotReturned / PhotoReadRefused / PhotoNotObserved. PhotoNotReturned never becomes - AccountHasNoPhoto; omission is never labeled PolicyDenied without a policy observation. -- Display audience is an access-policy term: self-header vs assignee-picker vs comment-thread - viewers are different audiences; admitted images serve through a controlled application - route with bounded caching — no tokens in image URLs, no open URL-fetch proxy. - -## Acceptance (behavioral matrix, from the external review) - -Admin-managed non-public photo retrievable via the authorized Directory path or a located -access refusal (never "make it public"); ID token omitting picture leaves authentication -valid with no photo-absence inference; UserInfo sub mismatch rejects the profile; API -denial/timeout is a distinct refusal, never stored as absence; provider default-photo marker -preserved; photo lost between serialization and readback fails that boundary's propagation -control; photo change/removal updates under the declared refresh policy; revoked profile -access follows the retention policy; child-OU/group overrides reported as effective policy; -second tenant isolated; email change keeps principal history; avatar absence never blocks -issue/approval authorization. Tested with at least one NON-ADMIN managed account. - -## Sequencing - -C4.1 — Workspace external model + convergence declarations for rows 1–5 (manual-obligation -machinery with readback receipts; VERIFY each row's API standing before classing it). -C4.2 — Profile projection: revisioned store, refresh policy, the five-valued photo -vocabulary, session mint becomes one producer. -C4.3 — UserInfo profile operation (sub-equality admission) for OIDC tenants. -C4.4 — Directory source (rows 6–7) with the separately-approved backend identity; assignee -picker consumes the projection. -C4.5 — Display-audience policy + controlled image route; the header, picker, and comments -share the one projection. -C4.6 — Receipt completion: the structured propagation trace (payload member standing → -parsed claim → verdict → projection → persisted readback → render selection → image-load -standing), plus the positive end-to-end control. - -## Done in this lane already (2026-09-27) - -- Login claims receipt: post-verdict writes only, typed publication standing - (LoginClaimsReceiptPublished/PublicationRefused), observed-boundary comment. -- Session profile projection carries name + picture as optional members end-to-end; the - header prefers the display name and paints the photo with the initials fallback. -- The direct evidence discipline held the diagnosis: three consecutive receipts established - provider-side omission before any account-level claim was made. - -## C4.1 API qualification and receipt boundary (2026-09-28) - -The source and limits of this classification are recorded in -[the C4.1 research receipt](receipts/google-workspace-admin-api-2026-09-28.md). -`extdeps.google.workspace` owns the external subjects, values and API standing. -`gunbc.auth.google_workspace_admin_convergence` declares the five obligations and feeds -subject-bound, time-bounded readback into `std.goal_assessment`. It does not mutate Google -configuration or alter login. Rows 6–7 remain separate integrations. - -The receipt producer must authenticate its source and persist the referenced evidence before -calling the assessor. These types and tests establish the assessment contract, not a deployed -observer or proof that the live organization matches it. No live admin receipt is supplied by -this change. Missing observations, unread overrides, wrong tenant/client/OU and expired -receipts refuse assessment. The photo obligation is scoped to the declared OU: an organization-wide -claim additionally needs resolved child-OU/group coverage; a top-OU receipt alone is insufficient. diff --git a/docs/plans/group-a-authority-establishment-plan.md b/docs/plans/group-a-authority-establishment-plan.md deleted file mode 100644 index 827a030fe7c..00000000000 --- a/docs/plans/group-a-authority-establishment-plan.md +++ /dev/null @@ -1,144 +0,0 @@ -# Establishing Group A's pair-serving authority on the fabric log - -Plan for the D0 prerequisite. Nothing here has been run. Every claim cites the symbol it was read from. -Anything not read from the live store on srv1 is marked as an inference. - -## What refused, and why - -Run 36220729318 (fleet-converge `spark_v41_engram_access_probe`, srv1) read -`heads/pair-serving-authority-group-a.1` and `…-group-b.1`, and refused srv8 with -`gunbc.spark.host_commitment` `group_standing_over`'s `GroupStandingUnread` arm. That arm fires when a -group's partition folds to `AuthorityUnestablished` **and** `gunbc.spark.pair_serving_authority` -`pair_serving_authority_for` finds a source row for the group. - -- **group-a:** `spark_pair_serving_authorities` declares - `PairServingActive { group: FabricGroupA, exact_realization: PairRealizationUnestablished {…} }`. The - log has no `AuthorityEstablished` event for it, so its standing is Unread. -- **group-b:** the roster has **no** group-b row. So an unestablished group-b partition reads - `GroupStandingUnclaimed`, and the probe doesn't refuse on it. **This corrects the brief:** the probe - output doesn't show that group-b is *established*. It only shows group-b isn't claimed. (Inference: no - repository route has ever written group-b's partition except the witness fixtures.) - -**Why group-a was never established.** There's no route for it. The only entry point is -`gunbc.spark.pair_serving_authority_log` `pair_serving_authority_establish_wet`, and its annotation says -"run once per group by an operator on an executor with the event log placed". No fleet-converge mode -invokes it: the mode list in `.github/workflows/fleet-converge.yml` has `pair_serving_d0` but nothing for -establishment, and no run has ever carried it. The roster row landed in #11501 and the log route in -#11555, and the one-time actuation between them was never done. - -## What the run requires - -``` -gunbc run --source-root dag --source-root src/v2 \ - --entry dag/gunbc/spark/pair_serving_authority_log.dag \ - --function pair_serving_authority_establish_wet --arg group=group-a -``` - -Inputs: only `group`. The authority is the source row, never an argument. The actor is the executor's -short hostname (`observe_executor_reach`). The store is `gunbc.fabric_event_log_host` -`event_log_store_for_host`, so it must run on a host with a declared dashboard instance. For in-process -access to `/opt/gunbc/fabric-storage`, that host is srv1. - -Preconditions, all enforced by `pair_serving_authority_establish` and `preparation_refusal`: - -1. The group-a partition folds to `AuthorityUnestablished`. A second run refuses at `established`. -2. `establishment_refusal`: the row commits hosts, and at least one is named (srv5, srv6, srv7, srv8 via - `fabric_group_hosts`, cage 1). The row holds no lease. -3. On `host-placement`: no pending group-a preparation, and group-a's live commitment is empty (no - finalization yet). -4. **No live host-effect claim on srv5–8.** A live claim, such as an in-flight V4.1 build, checkpoint - materialize or row-store encode, makes the run refuse at `host-effects`. Run it when those modes are - idle. -5. No srv5–8 host is fenced by group-b. - -## What it writes (a saga across two partitions) - -1. `host-placement`: `PlacementPrepared { operation: "establish group-a", previous: [], next: [srv5..8] }` -2. `host-placement`: `PlacementAppendClaimed { preparation, intent }` -3. `pair-serving-authority-group-a`: `AuthorityEstablished { authority: , placement: }`, - compare-and-set at `HeadAbsent` -4. `host-placement`: `PlacementFinalized`, with up to 3 attempts. If finalization fails, the run exits - non-zero and says to run `host_placement_finalize_wet`. A stale write or a refused write aborts the - run's own preparation (`saga_abort_own`). - -## Consequence to decide before consenting - -After step 4, srv5–8 are **fenced** by group-a (`group_fenced_hosts` ⊇ `group_live_commitment`). -`host_effect_admit_at` then refuses **every** new host-effect claim on those hosts with -`committed: … is fenced by an authority: group-a`. This covers the `spark_v41_*` modes that claim a -Group A host, such as `v41_runtime_image_converge`. - -**Establishing the authority does not unblock the Engram probe on srv8.** It changes the refusal from -"unread" to "committed". (Inference: the exact wording depends on the probe's admission route, which -wasn't traced to its end.) So establishment is correct for D0. For the V4.1 staging modes it's a -**regression unless they admit through the group's authority** (`admit_host_held_by_subject`) rather than -through a free-host claim. Parent: sequence staging work against this. - -## Operator authorization - -The code has **no consent gate** on this route. `pair_serving_d0_door`, by contrast, files an escalation. -It is still an operator-consented act, because: - -- It is a one-off, irreversible, privileged write to the shared production log. The only way out of the - state is a transition, never a re-establishment. -- It fences four production hosts against all other lanes. - -Under §3b's authorization-pattern selection (`gunbc.auth.authorization_pattern_selection`), a one-off -effect is **one operator approval**. The two ways to run it both need the operator: - -- **(A) Recommended.** The operator runs the command above on srv1 as the serve principal (briansrls). - briansrls writes under its own umask, so the file-mode defect is avoided. Actor = `srv1`. -- **(B)** Add a fleet-converge mode for it. This is a new modeled route in the workflow emission and - needs operator sign-off. It also writes as ghrunner under `umask 077`, so **it must wait for #12342** - and #12342's area-ensure repair. Otherwise the serve side can't read the four new objects or the - group-a head: they'd be 0600. - -Under (A), #12342 still matters downstream. D0 runs as ghrunner in CI (`pair_serving_d0`), and its later -appends are written 0600 until #12342 lands. - -## Verification - -- The command exits 0. -- Re-dispatch the read-only admission that refused: the Engram probe's admission, or D0's - `pair_serving_d0_admit_executor`. It must now read group-a as `GroupStandingEstablished`, generation 0, - `PairServingActive`. It must not say "has not been established". -- On srv1: `heads/pair-serving-authority-group-a.1` exists and names the `AuthorityEstablished` object. - The `host-placement` head has advanced by three events, ending in `placement-finalized`. -- Run the command a second time. It must refuse at `established`. This is the discriminating control. - -## Sequencing (parent ruling) - -Establishment fences srv5–8, so it runs **after** the remaining V4.1 staging: the production image -build on srv8, distribution to srv5–7, and the image probe. The operator runs it on srv1 at that point, -under route (A). - -## What follows Established, for the V4.1 launch - -The launch admits through `gunbc.spark.pair_serving_authority` `pair_serving_successor_may_launch`. The -states below are read from `gunbc.spark.pair_serving_d0` (`d0_transaction`, `d0_settle`, `d0_decide`). - -1. **Established:** `PairServingActive`, generation 0 (the genesis event). may_launch = **false**. -2. **D0 first write:** a transition at the exact head that was read, to - `SuspensionPendingReconciliation { hosts: grant.subject.hosts, transaction, authorization_binding, lease }`. - - The lease epoch is minted for generation + 1. - - No grant is carried yet. - - D0 refuses first if the grant's hosts are not the group's current population (an identity join). - - may_launch = **false**. -3. **D0 settle:** records an entry-state receipt, then makes **one** compare-and-set from the pending - state to one of three terminals: - - `SuspendedForAuthorizedSuccessor { hosts, authorization: , exact_candidate_realization: PairRealizationKeyed { key: grant.subject.successor }, lease, cleanup }`. - This is reached only when the incumbent didn't answer, the head is quiet, and the declared occupant - has drifted. Only this terminal carries a `LeaseGrant`: - - `reference` = transaction - - `fence` = (the admitting pending event, the pending generation) - - `max duration` = `grant.subject.term` (`d0_lease_policy`) - - `PairServingActive`, restored unchanged, when the incumbent answers as the declared realization. - may_launch = false. - - `FencedRefusal` for any unread or unidentified occupancy. may_launch = false; only - `pair_serving_may_finish` is true. -4. **Launch admission:** true only for `SuspendedForAuthorizedSuccessor` whose lease's **observed** - state is `LeaseRunningExpected`, which maps to `Converged`. The observed state is derived **at read - time** from the grant and the current instant (`lease_observed_at`). The launch must therefore read - `current_pair_serving_authorities` (the log), never the source row. An expired term reads - `LeaseRunningStale`, which maps to `Drifted`. That blocks START, while cleanup continues through - `pair_serving_may_finish`. diff --git a/docs/plans/harness-work-lifecycle.md b/docs/plans/harness-work-lifecycle.md deleted file mode 100644 index bb6c39c1651..00000000000 --- a/docs/plans/harness-work-lifecycle.md +++ /dev/null @@ -1,634 +0,0 @@ -# Harness work lifecycle (sketch → structure) - -Status: DRAFT v2 — owner-approved direction, external review folded in (2026-09-20). -Born of the srv2-deploy dispatch experiment: infrastructure proven end-to-end; the -missing thing is an authoritative lifecycle for coding work, so the known-mandatory -journey is owned by the workflow and never depends on an agent remembering it. - -Guiding line: **make the known obligations unavoidable, keep agent judgment -flexible, and make every claimed improvement visible in the real execution path.** - -## Axiom - -Work is **delivered** per the delivery contract declared at intake: - -- a **code-change task** is delivered only when its submitted candidate is - published to its PR; -- an **orientation task** delivers an orientation result; -- a **review task** delivers a review. - -The worker may not redefine a code-change task as "analysis completed" to escape -publication; orientation and review processes are never forced to manufacture PRs. - -The five evidence states are distinct and never collapse: - -**Ready ≠ Verified ≠ Published ≠ Approved ≠ Merged** - -## Anchors - -| Anchor | Owner | Structural (workflow) | Judgment (worker) | -|---|---|---|---| -| Intake | roadmap authority | accepted task + delivery contract + context bundle + base sha | — | -| Orientation | workflow (centering contract) | task, source pointers, constraints, exemplars, verification route; a typed way to report missing context | understand the problem; name uncertainty | -| Scoped editing | worker | bound workspace + permitted effects; nothing outside the scope is reachable | implementation, local debugging, local test iteration | -| Candidate handoff | worker submits, workflow binds | the handoff operation CAPTURES the candidate; the harness materializes its commit identity and carries the acknowledgment onto exactly that candidate | readiness claim + explanation + limitations; or blocked/needs-context | -| Verification | workflow (belt) | declared oracle runs against the captured head in a detached checkout; receipt retained | interpreting failure (feeds the next editing pass) | -| Publication | workflow (helper, credentialed) | push captured head, create-or-update the one PR per logical work item, receipt bound to the OBSERVED PR | PR body explanation | -| Review | owner/fleet | feedback bound to the reviewed head; changes route back as a new candidate | approval/merge decision (human) | - -## Rules - -1. **Delivery obligation.** A code-change task is never reported complete while a - publication obligation is outstanding. `Ready` (submitted) enables publication - and leaves the obligation open; passing verification is not publication; - approval and merge are later, separate states. -2. **Submission ≠ checkpoint, and binds the CAPTURED candidate.** The belt may - commit a dirty tree as a checkpoint (preservation only, no readiness claim). - A submission is a declared act whose handoff operation captures the candidate - as it stands THEN; the acknowledgment binds to that capture — never to - whatever a later belt tick sweeps up. Budget exhaustion without a submission = - `Yielded`, not ready. "A later edit inherits nothing" means old - candidate-specific evidence does not admit the new candidate; the old evidence - is PRESERVED, current eligibility recomputed. -3. **Head binding.** Verification and review bind to an exact commit. Revisions - update the SAME PR (the obligation belongs to the logical work item). -4. **Authority allocation is modeled and enforced by effects.** Who may commit, - publish, approve is a model fact; the worker's available effects, credentials, - the execution path, AND the rendered instruction all derive from it. Generating - "the worker may not publish" enforces nothing by itself — the selected effects - must make the restriction true; the prompt only explains it. -5. **The worker may iterate; the workflow owns obligations.** Local test/debug - loops are the worker's business. The authoritative verification, its receipt, - publication, review routing, and per-failure-class retry ownership are the - workflow's. Failure classes route by owner: CandidateFault → editing - (continuation with verdict); VerificationInfra → retry/repair the verifier; - PublicationInfra → blocked-on-publication with a named owner and next action. - Never fold all three back onto the worker. -6. **Typed blockers have owners.** `BlockedOnPublication(cause)` preserves the - pending obligation across restart, owns retry/escalation, and reconciles - uncertain remote success before creating anything: if the PR was created but - the local process died before recording it, resumption DISCOVERS the existing - PR — it never creates a duplicate. -7. **Tests are behavioral, at the real execution boundary.** The suite must show: - terminal response without submission is not ready; submission + pass discharges - into publication with no discretionary request; publication failure stays - visible and resumable; uncertain remote success reconciles without a duplicate - PR; a later edit cannot reuse a prior head's evidence; the worker cannot invoke - an effect the model assigns to the harness. Marker/projection comparisons are - secondary. -8. **Agent-facing documents are a justified argument, not just generated text.** - Once documentation is an input to execution, its correctness is part of the - work's correctness. Every consequential instruction must have traceable - support: WHY it must happen (applicable requirement/dependency), WHO may/must - (modeled authority allocation), WHEN it applies (task/role/stage/conditions), - HOW (the actual operation and admitted route), WHAT ESTABLISHES it happened - (observation bound to its subject), and WHAT IF it cannot (typed blocked/ - repair/retry/escalation). Explanatory prose may stay authored, but it may not - introduce an unmodeled permission, obligation, exception, or success claim. - Deriving prompt and docs from one source removes drift; it does NOT remove - consistent-wrongness — composition is checked, not just generation. -9. **The composed context must be consistent, not just each document.** The load - phase admits an applicable instruction set; individually valid documents can - compose into contradiction (case on file: worker prompt forbids claim_batch, - DESIGN.md teaches it — the repair is declaring the SCOPE distinction in the - model: the verifier uses the batch route, the worker submits candidates and - never runs authoritative verification; both documents then express it - correctly). Ordering one instruction before another does not repair meaning. - The check covers brief, orientation, stage instructions, available tools, and - observations: "publish the PR" is not a plan for a worker with neither - publication authority nor a handoff to its owner. -10. **Policy, observed fact, and plan never render as the same standing.** - "Publication is required", "publication succeeded", and "we intend to - implement publication" are three different premises. Stage instructions - reflect what is established NOW, not the aspirational pipeline. (The - experiment's own trace called oracle-green "the full loop" with the PRs - unopened — that class of unsupported conclusion is what this rule exists to - expose.) -11. **Contradiction reporting is a first-class, consumed route.** The worker - contract: when applicable instructions conflict, or a required conclusion - lacks its stated support, report the conflict BEFORE acting on anything that - depends on resolving it — never silently choose a convenient interpretation - or claim satisfaction. The report names the conflicting statements and their - sources, the affected operation, and the missing decision/evidence; a - suggested reconciliation is a proposal until the responsible authority - accepts it. The workflow preserves the affected obligation, surfaces the - blocker, and routes it to the owner of the contradictory instruction or - missing capability — a warning buried in a transcript is not consumption. - Two complementary protections: modeled contradictions are caught BEFORE - dispatch where possible; ambiguities found in execution are returned during - it. The agent is an additional reviewer of the supplied argument, not the - sole consistency checker, and it must never absorb our defect and disguise - it as successful execution. - -## The load phase (required dependency of every model-backed session) - -Before a model-backed process starts — worker, planner, or reviewer — the harness -resolves its declared context manifest against the applicable source snapshot, -assembles the required contents in the declared order, and supplies that assembly -to the ACTUAL model request. - -Honest boundaries (conceptual, not new modules): - -- **Context declared**: which sources and instructions are required. -- **Context assembled**: those contents were resolved from the source snapshot, - in order. -- **Context supplied**: the real provider request consumed the assembly (verified - against the request as sent, including provider rendering and tool definitions — - not against an intermediate prompt string). -- **Orientation produced**: the task-specific interpretation produced its output. - Load is the common foundation; orientation applies it to this task. Neither - proves the agent understood anything. - -The initial manifest: (1) full DESIGN.md (the canonical READING surface; -AGENTS.md/CLAUDE.md/README.md symlink to it), (2) genuinely additional standing -instructions — NOT a duplicate of the operational-essentials section already -inside (1), (3) the anchor-relevant context for this session. The reported file -size is a reason to measure token usage and context fit, not authorization to -substitute a smaller subset. - -Ordering law: stable project prefix first, role- and task-specific material last, -wherever the provider request format permits. - -## Alignment ladder (owner direction, 2026-09-21): cadenced alignment at every level - -Alignment is a RECURRING workflow responsibility across three scopes: the root -principles, the parent project's purpose and approach, and the task's outcome. -Functional decomposition exists so this is checkable: a decomposition must -PRESERVE each task's contribution to the levels above it — every node carries not -just its own contract but the derived statement of what that contract contributes -to its parent's intent. Alignment checks at any level are readings against that -carried contribution, never a fresh interpretation invented at check time. - -A budget is NOT a failure measure — it is the alignment cadence. When a turn -exhausts its budget the question is never "did it fail" but "is the work still -aligned." Orientation establishes the initial grounding; alignment MAINTAINS that -grounding as the work develops. Verification, publication, and review remain -distinct obligations — an alignment check never discharges them and they never -discharge it. - -The levels — each a different authority, and a check at one level never -substitutes for another: - -- **L0 root principles** — DESIGN.md (the axioms and what follows from them). - Question: does the work's approach still conform (single authority, fail-closed, - displaced cost, no workaround)? Carried by the load phase. -- **L1 project intent** — the decomposition node's declared purpose, approach, and - this task's carried contribution to it. Question: does the work still serve the - arc's intent, by its declared approach? -- **L2 task contract** — the node's boundary / red control / handback plus the - pinned witness. Question: is the current diff advancing THIS contract, or - drifting (scope creep, adjacent improvements, goalpost edits)? -- **L3 the live work** — the candidate and its submission against L2: the - submission's explanation is the worker's own alignment claim; verification and - goal audit are the independent ones. - -Cadence points — each cheap, each evidence-based, each producing an alignment -receipt: - -1. **Dispatch** — orientation + load phase (initial alignment). -2. **Each work interval / continuation turn** — a brief direction check. It - CONSUMES the actual work and its observations (the current diff, the receipts - produced so far, the worker's own account), never the worker's self-report - alone. Its verdict is a MAGNITUDINAL standing — a Fermi estimate, order-of- - magnitude and evidence-cited, never false precision (owner direction, - 2026-09-21): **ALIGNED (+0/−0)** — no measurable drift; **MISALIGNED - (direction)** — a named drift with a named correction; **EXTREMELY - MISALIGNED** — the work is off the task/project contract; **CATASTROPHICALLY - MISALIGNED** — the work violates a level above it (a root principle, the - project's declared approach). Each magnitude maps to exactly one action: - ALIGNED → justified continuation (evidence named); MISALIGNED → concrete steer - binding the next interval; EXTREMELY → escalate to the supervisor (plan-defect - territory; the cadence interval drops to its floor); CATASTROPHIC → stop the - line (the supervisor is convened immediately; work does not continue on the - candidate). The outcome AFFECTS the next work performed: a check whose answer - cannot change what happens next is ceremony, and ceremony here is worse than - absence because it reads as diligence. -3. **Budget checkpoints** — the Yielded path: the supervisor's adjudication IS the - alignment verdict (Nominal = aligned but slow; Plan defect = misaligned by plan; - Stuck = misaligned by reality). -4. **Submission and review/audit** — the worker's claim, then the independent - verdicts. - -Rules: - -- **A direction check is never pressure to manufacture completion.** The check - measures alignment and direction, not proximity to done; a check that punishes - an honest "not yet converging" teaches workers to fabricate convergence, which - is the same failure as budget-as-failure wearing a friendlier face. "Aligned - and still working" is a complete, acceptable answer. -- Alignment content is DERIVED from the modeled artifacts — DESIGN.md from - gunbc.design_document, arc intent and task contribution from the node's declared - fields and its parent's, the task contract from the node and its pinned - contract — never hand-typed prose that can drift (the submission-schema defect - is the standing specimen of prose drift). -- **The cadence is adaptive — a step function over verdicts (owner direction, - 2026-09-21).** Each level's interval starts at its magnitudinal base (scaled by - the chain's depth — more levels above a task means more to stay aligned to), - then steps with the VERDICT'S MAGNITUDE: ALIGNED widens that level's interval by - a declared step (+2 turns), MISALIGNED shortens it by the same step (sooner - re-check), EXTREMELY drops the interval to its floor outright, and - CATASTROPHICALLY stops the line rather than tuning anything. A RoutedConflict - (instructions disagree — not a worker signal) does not tune cadence at all. Every - level's interval stays bounded: never rarer than its magnitudinal ceiling (root - principles still re-check at their epoch regardless of streaks), never more - frequent than a declared floor that keeps probe cost subordinate to work. The - cadence state is per attempt-lineage, recorded on the alignment receipt (the - interval used and the verdict that moved it), so a later reader can replay why - the checks came when they did. The step reads only typed verdicts — never - vibes — and verification/publication/review gates are untouched by it: cadence - tunes HOW OFTEN we look, never what advancing requires. -- Alignment is not re-planning: the probe measures drift against existing - contracts; re-deriving the plan is the supervisor's job on escalation. -- What it is not: not a per-round interrupt, not a second full orientation, and - never a vibe check that can stall progress unbounded. - -First mechanical slice: the continuation brief carries the projected three-level -preamble; the answer rides in the submission's explanation; the belt records the -cadence point. Deeper slices: alignment answers as a typed per-turn artifact -consuming the diff and receipts, not just self-report; supervisor verdicts citing -alignment receipts. - -## Warm-prefix reuse - -**Warm-prefix reuse is an optimization to qualify and measure, not a guarantee.** -vLLM prefix caching matches processed token prefixes of resident blocks; eviction -and data-parallel cache scoping mean actual reuse depends on the serving route -and cache state, and must be OBSERVED. A canonical shared prefix makes sibling -requests ELIGIBLE for reuse — that is the whole claim. Consequences: - -- No warm-up agent, no conversation-fork mechanism: the first real request - supplies the initial demand; reuse shares eligible prefix computation, never - another session's mutable conversation or conclusions. -- **Missing required context blocks the request. A cache miss must never weaken - the context contract** — the declared cold route is taken when admitted. -- Cache savings are reported only when measured; prefill savings do not bound - generation cost. -- No "stability class" field until it has a concrete consumer; exact source - identity + declared order are what matter now. - -## Slice 3's authority shape (the extdeps distinction) - -DESIGN.md is the reading surface, not a second command authority. The intended -derivation is: - -**modeled operation + applicable policy → executable invocation, documentation, -and worker-facing explanation** - -— not one paragraph copied into both DESIGN.md and the prompt while execution is -authored elsewhere. Batching rules, witness population selection, wet-execution -requirement, executable provenance, and memory-budget requirements govern the -chosen execution ROUTE; the docs and prompt explain that same route. External -interface shapes stay in extdeps; the harness lifecycle composition and policy -stay in the workflow; the lifecycle does not move into extdeps to look modeled. -Slice 3's acceptance is the real execution boundary: at least one control fails -when the production connection between model facts and the session request is -removed. - -## Resource policy (subordinate, not organizing) - -**Budgets flow down the decomposition DAG; they are never flat.** A root carries -a declared bandwidth (rounds/tokens/wall-clock — the total effort the owner -allocates to that arc). A decomposition node allocates fractions of its -bandwidth to its children; a leaf's worker budget is what its parent dealt it, -not a global constant. Escalation sessions run one to two magnitudes above the -leaf budget they supervise — the supervisor must be able to re-read the -checkpoint, adjudicate, and still have runway to act, which a same-size budget -cannot do. Effort expended is therefore proportional to the project's bandwidth -by construction: big arcs afford big attempts; a leaf of a small chore node -gets a small one. Every budget figure is a declared allocation on the node, -visible on the dashboard — never a literal buried in harness code. - -Budget exhaustion is NEVER a bare "task failed". It produces a defined outcome: - -1. **Checkpoint** — the attempt state (transcript, worktree, receipts-so-far) is - preserved as a checkpoint. If the worker left a submission, it advances - normally; without one the attempt is `Yielded`, not failed. -2. **Escalation** — a Yielded attempt escalates to the supervisor session for - the node it was dispatched under (leaf → its parent decomposition node's - supervisor; the root arc's supervisor is the top). The supervisor resumes - from the checkpoint (the attempt's warm context/state — a KV checkpoint, - not a cold re-read) and adjudicates one of three routes: - - **Nominal** — the worker was progressing; issue a continuation turn with a - renewed budget. (Cheap tasks legitimately finishing late take this route; - attempt 1 of wave 1 would have.) - - **Plan defect** — the brief/centering sent the worker wrong (scope too big, - exemplar missing, contradictory instructions). Routes to the node's owner - as a plan defect — the worker is not re-dispatched against a known-bad - plan, and rule 11's contradiction machinery carries it. - - **Genuinely stuck** — the task itself can't advance (missing capability, - external blocker). Typed `Blocked` with the supervisor's stated cause. -3. **Supervisor verdicts are evidence**: the route, its basis, and the - continuation count are recorded per attempt. Continuation caps remain - tunable policy attached to the anchor; a cap hit escalates again rather than - failing silently. - -The supervisor is a modeled session role (like worker/reviewer/auditor), not a -human pager: it runs on the same harness, with its own guidance contract and the -load phase applied. Escalation depth is bounded by the decomposition DAG — a -supervisor that itself yields escalates to its parent, terminating at the root. - -## Metrics - -Outcome metrics: cost per delivered code-change result, publication success rate, -operator interventions per delivery, review rework per delivery. Rounds-per-diff -is retained as a DIAGNOSTIC (it is how orientation/repair cost movements are -observed); it is no longer a success metric. - -## Substrate ruling (owner, 2026-09-20): gunbc SCM inside, git at the edge - -The internal process substrate uses gunbc SCM (`dag/gunbc/scm/*`: object store, -write spine, staging, proposals) for everything it can cover: candidate capture, -the submission's head binding, attempt state identity, and the fanin merge of -worker candidates into the project's integration head (SCM proposals, typed -conflicts, modeled merge verdict — not textual three-way git merge). Git is used -ONLY at the final publish layer: translating the project's integration head to a -git push + GitHub PR. The repo being git-hosted and GitHub publication are the -edge; nothing internal speaks git where an SCM operation exists. Belt commit/ -verify/publish seams sit behind an explicit interface so the SCM realization is -the authority and the git realization is edge-only. - -## Delivery structure: recursive decomposition (owner ruling 2026-09-20) - -There is no separate "project" mechanism. A project IS a node — one whose work is -the completion of its children. The work graph is a DAG with the same structure -as the substrate itself: any node may decompose into child nodes (recursively), -a child may serve more than one parent (DAG, not tree — the import graph's law, -acyclicity, is the only structural rule), and every dispatchable node must have -at least one parent. Totally flat items — no parent — are not dispatchable work; -dispatch admission refuses them with a typed cause. - -Consequences: - -- **Fanout**: dispatching a decomposition node dispatches its ready descendants - (capacity-bounded), each attempt producing a verified candidate. -- **Fanin is a fold, recursively**: a decomposition node's own candidate is the - integration of its children's verified candidates (SCM proposal merges, typed - conflicts); its evidence is complete when every child is delivered. The fold - bottoms out at leaf nodes (ordinary single-worker tasks). -- **Delivery obligation binds at the grain you publish**: a leaf is a - decomposition of one (the degenerate case — per-attempt PR behavior falls out - without special-casing); a batch like the shell→dag ten publishes ONE PR at - the decomposition node's integration head; a whole arc could publish one PR - at its root. -- Evidence states still bind per-candidate-head at every grain; a parent head is - Published only when its integration carries every included child's - verification receipt. - -Slice 2's publication machinery is unaffected in mechanism — the obligation -subject is "the node whose delivery contract was dispatched", at any depth. - -## Distribution layer (owner direction, 2026-09-20): stateless executors, fabric-resident state - -Agents are stateless step functions over stored state; any conforming executor on -the fabric can run the next slice of any attempt. The worker's signature: - -> input: (attempt identity, transcript head ref, workspace ref, budget slice) -> output: appended events + typed terminal (Continue | Yielded | Submitted | Blocked) - -Nothing an attempt needs may live only in an executor's local filesystem. Attempt -state maps onto the fabric's storage tiers — each class into the store that is -already shaped for it, never a new per-lane persistence invention: - -| State class | Fabric storage home | Status (2026-09-20) | -|---|---|---| -| Transcript events (append-only, ordered, replayed on resume) | Fabric event log — the transcript becomes a hash-chained event stream with a head ref | Log exists; attempt transcripts not yet written to it | -| Indexes over events (transcript heads, attempt-by-node, executor fencing) | The fabric DB (gunbc#11723 — shared dependency with the group-A seat pool) | In flight | -| Workspace / candidate / receipt objects (immutable, content-addressed) | SCM object store — the belt's candidate capture already writes here | Landed (e9f0683adb) | -| Bulk artifacts (worker logs, large dumps) | Artifact store (artifact_store_fs realization today; storage-tiering policy later) | Exists | -| Executor leases / seats | Fabric cells (fabric_cell_acquire) | Exists for fleet work, not yet for dispatch | - -Rules: - -1. **Exactly one active executor per attempt**, fenced by a fabric lease. Two - executors continuing one transcript is the corruption case; the fence, not - politeness, prevents it. -2. **The belt observes fabric state, not host state.** Today it polls - attempt-state dirs on the dashboard host; the same facts read from the store - make it restart-safe and host-independent (a belt on srv1 cannot see srv2's - disk). -3. **Placement is a claim, not a hardcode.** Dispatch mints work + state refs; - executors (systemd units today, microVMs later) claim seats through the cell - machinery. A microVM worker is pool member N behind this seam — never a - bespoke spawn path. -4. **Executor switches pay cold prefill, stated honestly.** Prefix caches are - per serving engine; a resumed attempt on a different executor recomputes its - prefill. The slice-3 stable load-phase prefix is what keeps that cheap; it is - a cost to measure, not a reason to pin attempts to hosts. -5. **Transcripts compact.** Long attempts append a compaction event - (summary + reference to the compacted range) rather than growing unboundedly; - the resume reads the compaction head. -6. **Transcripts carry private facts.** Store placement and access for attempt - state inherit the private-facts boundary that ctrl/ used to be; this is a - placement decision, not an afterthought. - -The supervisor/escalation design (Resource policy) rides this substrate: a -supervisor session is just another executor reading the same refs — "resume -from the KV checkpoint" is, mechanically, load transcript head + workspace ref + -a warm prefix. - -Graduated path, each step independently useful: - -1. **State into the store** — transcripts and receipts content-addressed - alongside candidate capture; the belt dual-reads (store first, disk - fallback). -2. **Checkpoint/resume on one host** — a budget-exhausted worker exits Yielded - with a transcript head; a fresh unit resumes from the ref. (The continuation - routing built 2026-09-20 is this step's decision layer.) -3. **Executor pool seam** — spawn targets a claimed seat instead of the - dashboard host by name. -4. **Second executor + microVM** — another host or a microVM joins the pool; - nothing above this line changes. - -## Workflow execution (owner ruling, 2026-09-21): durable obligations, replaceable processes - -Repair the current failure, but the global belt pass is NOT the permanent -architecture. The durable facts are: accepted work, each outstanding obligation -(capture, verification, publication, review, audit, alignment check), its state, -its due time, and its last result or located failure. Processes, systemd units, -timers, and page bodies are replaceable realizations over those facts — never -the facts themselves. - -1. **Per-item advancement, per-operation persistence.** Each work item's - operations advance when INDEPENDENTLY eligible — not when a global pass - reaches them. Every operation's result AND its located failure persists at - the moment it is produced, never at pass end. A blocked capture is an - item-scoped fact by construction: it must never prevent unrelated - verification, publication, or alignment from advancing. And a required - operation never disappears as a legacy skip — an unperformable operation - stays outstanding with a typed cause and an owner, because a skip that sinks - an obligation is a silent drop wearing a receipt. -2. **Scheduling: wake-ups, due times, bounded reconciliation.** Targeted - wake-ups (submission written, worker terminal, receipt landed) advance the - affected item's next operation promptly — the timer is not the critical - path. Due times live ON obligations (retry-after, the alignment cadence) so - anything time-driven is data, not a loop's period. Bounded reconciliation - (the sweep) is the RECOVERY backstop that re-derives from durable state — - never the primary driver. Missing a notification or restarting a controller - is a non-event: recovery replays from the durable obligations and no user - action is ever required to resume. -3. **The page: fast, honest, decoupled.** Observations are decoupled from - execution success — a failed, poisoned, or hung item still renders - truthfully. Staleness is EXPLICIT (rendered-at and observed-at per region), - never implied by a fresh-looking body. -4. **Acceptance demonstrations.** (a) One submitted change reaches its PR - while another attempt is poisoned or hung. (b) Restart and executor-loss - recovery through the same path — kill the controller mid-obligation and - watch recovery resume it without user action. - -5. **The contract (owner ruling, 2026-09-21): durable, versioned handoff of - responsibility across execution boundaries.** This generalizes clauses 1-4 - beyond the belt - and it does NOT mean queueing every local function call. - The authority is a modeled module in the repo (lands as - `gunbc.workflow.durable_handoff` with its first real consumer); this document - REFERENCES it and must never become the only place the rule exists. The - contract: - - **Inbound**: a handler performs bounded admission, then confirmed durable - acceptance, and returns an operation reference - it finishes there. The - accepted work, its required inputs, and its continuation survive the caller - and the executing process. - - **Processing**: consumes identified inputs and advances state - conditionally; settlement records an attributable result, a refusal, or a - durable transfer of responsibility. - - **Three separate facts**: transport acknowledgment, execution-resource - release, and historical retention are distinct - a delivery may be settled - once durable responsibility transfers, but settlement never erases the - obligation or the information recovery needs. - - **Outbound**: the same contract applies to effects at the upstream - boundary, with guarantees limited to what the upstream API actually - provides - modeled as observed guarantees, never invented ones. - - **Composition, not invention**: the contract composes the existing work, - storage, identity, ownership, and observation authorities. -6. **The missing conformance question lands with the first real inbound - consumer.** The §3b roster (gunbc.design_argument conformance_domains -> - roadmap_review_criteria) has no row whose question is "does this boundary - hand off responsibility durably - bounded admission, durable acceptance, - conditional advance, honest settlement?" - so this week's failure classes had - no mechanical reviewer: a pass-killing item failure, a hung tick freezing a - pipeline, an undecidable artifact sinking a candidate, a fresh-looking body - hiding stale observations. The boundary with neighbors: leasing (§3b) owns - the lease as a PRIMITIVE; fabric/compute owns allocation; the new row owns - the handoff architecture that uses them. Per §3b's own rule a row names homes - that EXIST: the durable_handoff authority lands first with its executable - controls, and the conformance row is added naming it plus the lease - authorities - until then this is a modeling obligation, not a reviewer. These - classes are its seed tells. - -## The belt demoted (owner ruling, 2026-09-22): wake-up and anti-entropy only - -The belt tick is NOT the workflow authority, NOT the unit of execution, and NOT -the source of current state. It is demoted to two duties: delivering wake-ups -and bounded anti-entropy repair. Every accepted issue, attempt, candidate, and -external observation has its OWN durable authority and version — that is where -state lives. - -The working shape: - -- An event or a due refresh recomputes the SMALLEST affected projection, - conditionally publishes it (publish only when the version moved — never - republish unchanged), and creates ONLY the next eligible obligation. -- Duplicate, lost, and out-of-order wake-ups are harmless: every consumer is - idempotent and version-checked, so a wake-up is a hint, never a fact. -- The expensive operations (verification, escalation convening, integration, - publication) are per-item obligations invoked on wake-up — never phases of a - monolithic pass whose completion bound the corpus scale will always - eventually break. The 90-minute tick that dies unfinished is the standing - specimen. - -## The frontend as an issue tracker (owner ruling, 2026-09-22) - -**Issue intake is fabric-resident (owner ruling, 2026-09-23).** Created issues -never touch the git tree: content versions go to the SCM object store, intake and -state-change events to the fabric event log, and the current-state index -(issue id → current version + standing) to the fabric DB (the shared in-flight -dependency — group-A seats and attempt-state indexing consume the same -substrate). The roadmap projection consumes the fabric-resident intake as an -overlay onto the .dag-authored nodes; the two populations present identically -downstream. `POST /issues` is the first real inbound consumer of -`gunbc.workflow.durable_handoff`: bounded admission (fields valid, a parent -required — the no-flat-items ruling), durable acceptance, an issue reference -back. - -The frontend IS an issue tracker over the same authorities — and the interface -itself is an external authority, modeled in extdeps with its real vocabulary: -**Google's Issue Tracker (Buganizer)** (`extdeps.google.issue_tracker` — Issue, -Component, Status, Assignee, CC, Star, Upvote, Hotlist, SavedSearch, -BookmarkGroup, BlockingRelation, and its stock sidebar views: Assigned to me, -Starred by me, Upvoted by me, CC'd to me, Collaborating, Reported by me, To be -verified, plus Hotlists and Browse components). The roadmap frontend is a -MAPPING from our domain authorities onto that modeled interface (interface in -extdeps; mapping and policy in the workflow) — never a homegrown tracker shape. - -- **Components express domain placement; parent issues express decomposition; - blocking edges express dependencies; attempts express execution history.** -- The default view is a searchable issue table. Each issue has a detail page - and a separate Work tab. A project-tree view uses nested title-in-progress - bars (the landed taskbar component, promoted to the tree grammar). -- **Four facts stay separate**: planning status, the current workflow - obligation, the attempt outcome, and the delivery evidence. The page derives - a concise PRESENT-TENSE explanation from them ("verifying candidate - 2ccf4dc72f", "blocked: turn 2's artifact undecodable; turn 3 re-declared") — - it NEVER flattens them into one label like "Submission failed" plus a row of - misleading pending stages. A pending stage names what it waits on, not just - that it waits. - -## Review ruling (2026-09-23): branch held on two blockers; the conservation laws - -The first full review of the integration branch returned HOLD with two blockers -(the launch census reads only tmux while production attempts run in systemd -units — an invisible-population defect on the live admission path; and -ContinuationFresh conflating "no lineage" with "lineage active/unresolved" — -an effectful spawn answer for non-fresh states) plus high findings (alignment -model parallel-not-authoritative; submission capture not self-authenticating; -launch admission not an atomic cross-invocation reservation; the actuation root -too broad to audit). The laws it minted are now doctrine: - -1. **Observation conservation**: every identity discovered at an external - boundary survives into exactly one modeled output arm — classified, - unclassified/unknown, foreign, or malformed. No arm may mean "drop it." - Classification failure weakens what is KNOWN about the identity; it never - erases the identity. -2. **No refusal-to-neutral conversion**: Refused or Unknown must never become - [], 0, false, or none at a safety boundary. -3. **Current policy does not identify a historical occurrence**: an existing - attempt is read through the provider/realization/profile it was launched - with (its persisted spawn identity), never through the currently selected - policy. -4. **Adjacency is not a join**: a digest, subject, locator, or authority - supplied beside a payload must be derived from it or checked against it. -5. **Weaker evidence cannot improve admission**: replacing an observed fact - with unread/unknown keeps the verdict equal or more restrictive. - -The recurring-failure-mode row to file on the public side: "an observed member -disappears when classification refuses," with the static census tripwire over -helper-layer shapes (Refused => [], Unknown => none, Absent => accumulator). - -**The reopening gate is conjunctive (review ruling, 2026-09-24):** model-backed -dispatch resumes only when ALL of: the population-conservation + continuation -composition proof is accepted (an active systemd attempt reaches zero spawn -effects), the memory proof reaches FitProved for the declared capacity intent, -and the exact combined revision is deployed and read back. Until the launch -lease lands, launch authority stays single-controller/manual — a bounded -intermediate state, never evidence of cross-invocation exclusivity. - -## Slices and their production exit criteria - -| Slice | What must be true before calling it complete | -|---|---| -| 1. Orientation requirement (in flight: agent-13, roadmap_centering.dag) | The real dispatch path consumes the applicable orientation input; absent or inapplicable orientation cannot be bypassed by a presence flag. | -| 2. Handoff and delivery | A submitted candidate is captured, authoritatively verified, and published to its associated PR without another discretionary request. Yield, publication failure, restart, and uncertain remote success all remain truthful. | -| 3. Justified instruction contract + load phase | The real session request consumes the ordered, source-bound context. Consequential build/test and submission/delivery claims derive from the same modeled facts that govern execution (traceable support per rule 8); the composed instruction set is consistent per rule 9 (the claim_batch scope distinction is the demonstration case, declared in the model and expressed correctly in both surfaces); the worker has the rule-11 contradiction route and it is consumed. At least one control catches a DELIBERATELY INTRODUCED conflict. | -| 4. Evidence-driven refinement | Actual dispatch records show where cost and failures occur. Improvements are evaluated against those records, including unsuccessful attempts and review rework. Cache savings reported only when measured. | - -Serialization note: `roadmap_belt_actuate.dag` edits serialize behind agent-13; -preparation, source inspection, acceptance-case design, and non-conflicting work -do not. - -## The review deliverable - -The owner-facing PR contains the harness changes ACTUALLY EXERCISED (identifying -the source head that ran), links the resulting task PRs and evidence, and shows -at least one real dispatch reaching its task PR: this dispatch used this source -and context, submitted this candidate, verified this head, created/updated this -PR, reached this review state — plus which failure controls were exercised and -which capabilities remain unproven. A dashboard restart is an operational step, -not evidence. Plan text and dashboard status are not the deliverable. diff --git a/docs/plans/host-network-attachment-converge-design.md b/docs/plans/host-network-attachment-converge-design.md deleted file mode 100644 index d5d5ae53456..00000000000 --- a/docs/plans/host-network-attachment-converge-design.md +++ /dev/null @@ -1,115 +0,0 @@ -# Host network attachment as a converged fact — retiring the router click-path - -**Status:** design, operator-directed 2026-08-27 ("can we figure out how to do this stuff -via fleet converge in the future? i don't really want to poke at my router anymore"). - -## 1. The displaced cost - -Paid on 2026-08-27: moving two DGX Sparks from ethernet to wifi meant hand-editing DHCP -reservations in the CR1000A web UI — delete two entries, wait on a stale dynamic lease the UI -refuses to remove, re-author two static rows against MACs read off the hosts by hand. Each act -is a fleet fact, none is expressible in the model, and the model's record was wrong in a way -nobody could catch from inside the repo — `dgx_procurement.dag` asserted two -`StaticDhcpReservation` rows while the router had been handing out ordinary dynamic leases the -whole time. A fact the repo cannot read is a fact the repo will eventually misstate. - -The cost to displace: **a host's address is currently owned by a device the fleet cannot -observe, cannot converge, and cites only through a user-guide PDF.** - -## 2. Two paths, and why one is declined - -### Path A — the host owns its address (RECOMMENDED) - -Take DHCP out of the loop for fleet hosts. A host's address becomes a netplan fact, authored -from the model and applied over the existing `host_effect_apply` path — the transport that -already converges hostname, toolchain, runner slots and build cache. The router's only remaining -involvement is a **one-time** DHCP-pool shrink so the static range cannot collide; after that it -is never touched. - -This fits the machinery already in tree: - -- `extdeps.netplan.netplan` already models the applier half, including the load-bearing - fact that presence in `/etc/netplan` is sufficient for application regardless of writer. -- `host_effect_apply` already delivers typed effects to these exact hosts. -- `membership_reconcile` is the spine: desired vs observed attachments, keyed by - `(host, interface)`. Added/Modified → upsert, Removed → teardown-or-refuse. A new medium is - a new instantiation, not a forked reconcile. -- **No new transport.** §3 permits one as a Realization handler, but the cheapest correct - move needs none. - -### Path B — model the CR1000A admin transport (DECLINED, recorded) - -Converge DHCP reservations by driving the router. `Cr1000aAdminTransport` today is -`WebUiManual | UnknownTransport`, so a real handler is the structurally sanctioned addition. -Declined on cost and on §5: - -- The CR1000A's local admin API is undocumented. The module's cited authority is a Verizon - user-guide PDF describing the UI, not an API — any handler would be reverse-engineered from - a SPA's traffic and **could not be cited**, the grounding requirement `extdeps/` exists to - hold. -- Firmware-fragile: a Verizon-pushed update can change it with no notice and no version to - declare. -- Buys strictly less than Path A: the address still lives in a device that is not the fleet, - and the model still has to transcribe it back. - -Recorded rather than left implicit because the next reader will otherwise re-derive it: the -router *is* the obvious place to converge a DHCP reservation, and the reason not to is not -obvious. - -## 3. What is missing - -Four gaps, in dependency order. None is large; the first is what everything else waits on. - -**(a) A per-host interface roster, and its producer.** No model of "srv6 has `enP7s7` -(ethernet, dark) and `wlP9s9` (wireless, up)". `InterfaceObservation` is a shape with **no live -producer** — `host_network_diagnosis.host_network_observation` returns `none` for every host, -and the module says so, counting the whole fleet as an honest deficit. Landing the producer is -not new scope: an existing model is already waiting for it, and its tally falling is the -receipt. - -**(b) Wireless in netplan.** `extdeps.netplan.netplan` models the renderer and the interface, -not config *content* — no `addresses:`, no `wifis:`/`access-points:`. The wifi block needs an -SSID and a key. **The PSK is a secret and must be a `SecretRef`**, never an inline literal, on -the reasoning `nvidia_dgx_spark_setup` already applies to the per-unit sticker credential: a -shared home SSID password in source is the FactoryLogin mistake with a different subject. - -**(c) MAC grounding.** `MacAddress` is still the anemic `NonEmptyStr where brand` in -`extdeps.dhcp.v4`, with a named dissolution to an unwritten `extdeps/network/mac.dag`. Two MACs -per host makes this load-bearing: the model must say *which interface's* MAC a row binds, and -today it cannot. The 2026-08-27 incident is the witness — a reservation silently kept naming a -dead port. - -**(d) A medium axis on the attachment.** `NetworkPortMedium` carries `WirelessLan`, but as a -*hardware catalog* fact (what the box ships with), not an *attachment* fact (what the host is -using). `product.network_topology.NetworkLocality` has `Lan` with no wired/wireless -distinction, so the topology cannot express the change that just happened. - -## 4. Staging - -- **Phase 0 — read.** Land the `InterfaceObservation` producer over `host_effect_apply`: - name, permanent MAC, admin state, carrier, medium, addresses. Discharges the counted - deficit in `host_network_diagnosis` and makes (c) and (d) authorable from measurement - instead of transcription. No writes. -- **Phase 1 — ground.** `extdeps/network/mac.dag` (hex-octet parse, dissolving the brand); - medium on the attachment; the roster keyed by `(host, interface)`. -- **Phase 2 — write, wired first.** Netplan `addresses:` content + apply, converged through - `membership_reconcile`. Wired is the safe pilot: a mistake leaves the box reachable on - wifi. Requires the one-time pool shrink. -- **Phase 3 — wireless.** `wifis:`/`access-points:` with the PSK as a `SecretRef`. - -**The standing hazard:** every phase after 0 can strand a host. These two boxes have no BMC and -both ethernet ports are now dark, so a bad wireless apply is a physical trip. Wireless converge -must land behind the same read-back-and-revert discipline as the rest of the converge path, and -Phase 2's wired-first ordering exists so there is a second way in before the first is edited. - -## 5. Acceptance - -- Phase 0 RED: a host whose observation cannot be produced answers `UnknownRefused` and is - **counted**, never defaulted to a plausible interface list. -- Phase 2/3 RED: an attachment whose desired address collides with the router's live DHCP - pool **refuses** rather than applying — decidable from the pool bound and the desired set, - so a construction wall, not a post-check. -- The flagship: srv5/srv6's addresses become derived from converged attachment rows, and - `dgx_procurement.dag`'s reservation rows stop being the address authority — at which - point the CR1000A rows are observation-only, and the click-path is retired by having - nothing left to click. diff --git a/docs/plans/human-identity-authorization.md b/docs/plans/human-identity-authorization.md deleted file mode 100644 index edb4213b1fa..00000000000 --- a/docs/plans/human-identity-authorization.md +++ /dev/null @@ -1,194 +0,0 @@ -# Human identity and authorization (owner ruling 2026-09-25) - -> **Network placement decides who can reach an endpoint. Google OpenID Connect -> establishes which human is present. Application policy decides what that -> principal may do.** - -Tailnet no longer authenticates people for the tracker or approvals app. It -remains transport: development network, TLS/reverse-proxy, a reachability -boundary. A `Tailscale-User-Login` header is optional transport/audit -metadata; it never mints an application principal and never authorizes a -mutation. (The tailnet model itself flags the fragility: the header is -unsigned and trustworthy only when the proxy is provably the sole route.) - -## Three separate concepts - -```text -Transport: Direct HTTPS | Tailnet proxy | local development -Authentication: Google Workspace principal -Authorization: assign | comment | approve | administer | merge -``` - -## One qualified-principal authority - -```text -PrincipalRef { authority, namespace, subject } - -principal:google-oidc/accounts/ -- authority https://accounts.google.com, subject = ID-token sub -principal:gunbc/workflows/fabric -- authority gunbc, namespace workflows -``` - -Email, display name, picture are `PrincipalProfile` facts with a -profile_revision — never identity inputs. `sub` is the durable identifier -(email can change; Workspace membership comes from the `hd` claim, not email -parsing). Consequences: email changes don't mint people; email reuse doesn't -inherit authority; avatar failures can't weaken auth; one human = one -principal across tracker, web approvals, native approvals. - -The known contradiction at a3fd5eb is BANNED going forward: -`principal_from_email` setting `subject = email` keeps the selector as -durable identity. Comments must not copy that shape. - -## Authority placement - -Buganizer extdeps keeps `User { email }` (the interface's visible user). -gunbc's principal/profile/auth authorities live OUTSIDE extdeps: - -```text -extdeps.google.openid_connect -- Google's protocol + claim vocabulary -extdeps.google.workspace -- Workspace org/domain interface facts -gunbc.identity.principal -- PrincipalRef, workflow principals, profile refs -gunbc.auth.google_workspace_login -- gunb.ai policy over Google OIDC -gunbc.auth.session -- application session identity + lifetime -gunbc.auth.request_authentication -- raw HTTP request → authenticated request -``` - -(The existing oauth2.Google.Refresh is API-token refresh, not a login model.) - -## Authentication vs authorization (Google's own split) - -Login requests ONLY `openid email profile`. No Gmail/Contacts/Directory/Drive -scopes for login. Profile enrichment for the assignee picker is a separately -authorized directory producer feeding the internal principal-profile cache. -Avatar order: internal profile → Workspace directory → cached → initials; -absent/unread never blocks auth, assignment, comments, or approval. - -## Web flow (tracker + web approvals) - -Authorization-code flow with PKCE (implicit is deprecated-insecure). Routes: -`GET /auth/login`, `GET /auth/google/callback`, `POST /auth/logout`, -`GET /auth/session`. Steps: state+nonce+PKCE verifier persisted as a -short-lived transaction → redirect → verify state → exchange code → verify ID -token (signature/JWKS, iss, aud, exp, nonce, email_verified, hd == gunb.ai) → -mint opaque `AuthenticatedSession` → redirect only to an admitted relative -return path. - -Browser gets an opaque cookie: Secure, HttpOnly, SameSite=Lax, bounded -lifetime, rotated on authentication. State-changing forms additionally require -application CSRF proof (login state ≠ CSRF token). - -Redirect URIs: exact-match owned domains (`tracker[-dev].gunb.ai`, -`approvals[-dev].gunb.ai`); dev names may resolve only inside the tailnet. -Separate OAuth clients per app × environment × platform (tracker web -dev/prod, approvals web dev/prod, approvals iOS/Android); one admitted -audience roster, no silent cross-app token acceptance. - -## Approvals: three proofs, never one - -```text -Human authentication — Google OIDC principal -Decision authority — one-time approval capability + approval policy -Device/app integrity — App Attest / Android equivalent / enrolled device -``` - -GET confirmation page never decides; POST decision is the only mutation, -requiring: authenticated session + CSRF proof + one-time capability + -expected current decision state + approval authorization (an explicit -approver roster/role — `hd == gunb.ai` alone is org membership, not approval -authority; first production cut = an exact stable-principal roster, even of -one). Native apps: platform Google Sign-In → ID token to the backend -validated against the native client audience; the mutation requires Google -principal + device integrity + capability. - -## Event-log migration - -`roadmap-event/v3`: `author: PrincipalRef`; `Claimed { assignee: -PrincipalRef, return_to: PrincipalRef }`; `CommentCreated { ..., author: -PrincipalRef }`. Historical v1/v2 email strings decode as -`LegacyEmailPrincipal { email, authentication_unestablished }` — readable, -never authorizing, NEVER auto-matched to a current `sub`. New v3 writes -require an authenticated principal; missing/invalid session refuses. - -Assignee picker stays email-centric in display, but suggestion rows submit -the stable PrincipalRef. Typed email → directory lookup → exactly one -admitted principal or typed refusal (none / multiple / external domain / -unverified). Fabric: selector `fabric@gunb.ai`, stored -`principal:gunbc/workflows/fabric`. - -## Comments and planning behind the seam - -Order: stable principal model → Google OIDC authentication → authenticated -comment append → Ask Fabric / planning work requests. The comment CARRIER may -be built against PrincipalRef immediately; its production POST stays blocked -until Google session auth is wired. The one-principal one-work-request design -stands: Issue (durable outcome) / Comment (narrative or explicit request -origin) / IssueWorkRequest (the exact work requested now) / -DeliverableContract (code candidate | issue-revision candidate | -investigation report | review verdict). No planner role, no Planning type. - -## The canonical work graph - -```text -A. Human identity and authorization - A1 PrincipalRef authority - A2 Google OIDC external model - A3 server session + CSRF boundary - A4 tracker assignment migration to event v3 - A5 durable comments - A6 issue-revision/planning work contracts - A7 web approval migration - A8 native approval-client identity - A9 directory/avatar enrichment -B. Stage0 driver iteration (identity split, immutable cache + private - overlay, A/B qualification, freeze, incremental closure, fixed point) -C. Group B serving (cache proof → prefill bound → FitProved intent → - persistent convergence/readback) -D. SCM fanin (conversion → generation/CAS save → structural locator → - integration-head advance) -E. Workflow reliability (population, continuation, alignment, capture, - launch lease, actuation split) - -A1→A2→A3; A3→A4→A5→A6; A3→A7→A8; A1→A9. -B independent of A. C independent of A,B. D independent of A,B,C. -E constrains broad autonomous dispatch. -``` - -The stage0 (B) acceptance contract is preserved verbatim from the ruling: -never hand-edit the tracked stage0 mirror; scratch driver rendered by -interpreting emit_source_root_eval_driver_main_rs; one exact materialized -library identity; Library/DriverA/DriverB mutation receipts; immutable -content-addressed library + private mutable overlay (overlay output is never -authority until copied under the full content key); freeze the rendered main -digest after qualification (instrumentation lives at the modeled runtime -boundary, not in 05_emit_rust.dag); landing = regen-round receipt + stage0 -partition rebuild decision; final acceptance = A′/B′ parallel on two hosts, -full two-build fixed point paid once, generated driver digest == fast-loop -qualified digest. - -## Acceptance controls - -- A forged Tailscale-User-Login establishes no application principal. -- Direct HTTPS and tailnet-proxied requests authenticate to the same - principal. -- Wrong iss/aud/exp/nonce/hd or unverified email refuses. -- Same sub + changed email = same principal; same email + different sub ≠ - same principal. -- Comment events store PrincipalRef, not email. -- Historical email-only events readable but unverified. -- Valid capability + wrong principal refuses; GET never decides; POST - requires capability + principal + authorization + CSRF. -- Avatar/directory failure renders fallback, never affects authorization. -- Service/machine writers cannot use a human Google session as workload - identity. -- Tailnet loss changes reachability, not stored-event identity semantics. - -## Immediate sequencing (owner-set) - -1. Amend the comments lane: author is PrincipalRef, not email/Tailnet. -2. HOLD deployment of the assignment POST at a3fd5eb until the v3 migration - lands there. -3. Build A1–A3 (principal authority, OIDC model, session/CSRF). -4. Cut tracker assignment + comments over (A4, A5). -5. Cut the web approval broker over (A7), then native audiences (A8). -6. Demote tailnet identity to transport observation on user-facing routes. -7. B, C, D continue independently. diff --git a/docs/plans/infer-checking-mode-plan.md b/docs/plans/infer-checking-mode-plan.md deleted file mode 100644 index c636899ed37..00000000000 --- a/docs/plans/infer-checking-mode-plan.md +++ /dev/null @@ -1,140 +0,0 @@ -# Checking mode in v2 infer: the expected type reaches a row before it decides - -Owner: smart-newt-725. Status: plan only; nothing here is built. It is sequenced after -eager-newt-412's site-keyed facts store (branch `eager-newt-412/facts-site-key`, -[keying-relation-design.md](keying-relation-design.md) §3e), because both restructure the same fold. - -## The defect this answers - -`v2.compiler.infer` is one bottom-up fold (`v2.std.node` `fold_node` in `infer_entries_for_tree`). -A row decides from its children alone. A generic record construct is typed from its field values -alone. When no field value fixes a type parameter, the construct stays untyped and counted, even -when the position consuming it declares the instantiation. That happens for a phantom parameter -(`Ph { n: Int }`), or for a parameter that occurs only in a field whose value is itself such a -construct (`Two { a: Ph, b: T }`). - -The controls are the two claims in -`v2.test.claim.compiler.infer_expected_type_record_instantiation_witness_test`, enrolled -expected-red (`v2.workflow.floor_expected_red` `floor_expected_red_chunk_infer_checking_mode`), -plus the discriminating refusal below, which this work ENROLLS when it lands. It is done when all -three pass. - -The refusal is not enrolled expected-red today because, while it is red, infer does not refuse the -program and runs the whole member, which costs about 75.8k eval steps against the 72.3k floor -budget for a new witness. Once checking mode refuses at the field, the same claim stops early and -fits. Its specimen, to enroll with this work as `etr_a_field_value_disagreeing_with_the_contexts_instance_refuses_holds`: - -``` -module v2.test.etr_two_bad - -import v2.std.logic { Bool } - -type Ph { - n: Int -} - -type Two { - a: Ph - b: T -} - -data t: Two = Two { a: Ph { n: 1 }, b: 3 } -``` - -Infer only the `data` member `t` (handed the module's own indexes) and require a refusal with -`record_field_value_does_not_inhabit` at `b`. The nested `a: Ph` must stay: it is what leaves -`T` fixed only by the context, so the claim discriminates checking mode rather than plain -field-value instantiation. - -## The rule that shapes everything: deliver before deciding, never revise - -The site-keyed store keeps one fact per site. A row that enters a different second fact at an -occupied site refuses `infer_facts_key_conflict`. So the expected type cannot be applied after a -child has decided, by re-judging it or overwriting its fact. It must reach the child's row before -the row decides. - -A consumer-side re-judgment that worked after the fact was built and withdrawn (#12935). It would -have been a second route that this plan deletes. - -## The mechanism: the expected type is fold context, not a fact - -`v2.std.node` already has the fold this needs: `fold_node_topdown` with -`NodeFoldTopDown { init, child_context, step }`. `child_context(parent, ctx, edge)` computes what a -child is told from its parent and the parent's own context. Checking mode runs infer's gather on -that fold, with the context carrying at most one expected type. - -- **It is an input, not a fact.** It is never stored in the facts trie, and no site gets a second - entry because of it. -- **A child's site is the parent's site plus one step**, which the site-keyed store makes directly - addressable. That is why this goes second. - -## Where an expected type enters (`child_context` arms) - -Each arm states the one edge it speaks for. Every other edge passes **no** expectation: context is -never inherited blindly through a node that does not declare a type for that child. - -| parent | edge | expected type for the child | -|---|---|---| -| bodied Arrow | `^arrow_body_edge` | the declared return, at its denotation, unless it mentions the Arrow's own type parameters | -| annotated Bind (`let x: T = e`) | the bound value | the annotation | -| record construct | a field edge | the field type the record declares. If the construct itself has an expected `R` of its own record, the record's binders are first instantiated to `A..`. | -| list introduction | an element | `T`, when the list's own expected type is `FreeMonoid` | -| application | an argument | the formal's declared type, only while the callee's Arrow is known (inline today; named calls after #12506) | - -The record-construct arm is the one that matters for the controls, and it is recursive. The -construct's own expectation instantiates its binders, which instantiates the field types, which -become the expectations of nested constructs. - -## Which rows consume it (first slice: one) - -Only **the record construct row** consumes the expected type in the first slice. When its context -is an Instantiation of its own record with every argument present, it starts the existing -instantiating walk (`infer_judge_formal_args`) from those instances, not from empty. A field value -that disagrees with an instance the context fixed is refused at the value by the same relation and -reason as any field. The construct forms `R` itself, so the consuming position's existing -judge sees an equal type and decides. - -That one row needs two supporting changes, both written once in #12935 and withdrawn with it: - -- a starting-instances parameter on `infer_judge_formal_args`; -- the instantiating walk judging a formal that mentions an already-instanced type parameter at that - instance (`inner: Ph` with `T` fixed reads as `Ph`). - -Other rows that could consume an expectation are later slices, each with its own red. Two examples: -an empty list literal `[]`, which is on the frontier today, and an unannotated `let`. - -## What does not change - -- **There is no second judgment.** The consuming position still judges produced against declared - through `v2.std.inhabitance` `declared_type_inhabitance`. Checking mode only lets the child - decide with more information. -- **No type is guessed.** A declared type that is a declaration reference (possibly an alias), an - Instantiation of a different record, the wrong arity, or one mentioning an enclosing function's - type parameters gives no expectation. The construct stays counted exactly as today. -- **String literals stay out of scope.** They are untyped until work item adhoc-3e4eee5a-d66, so - the map-literal case stays counted on that item. - -## Cost shape - -`child_context` runs once per edge. For a construct, the record's declared fields must not be -re-derived per field edge, which would be quadratic in the field count. The context for a -construct's children carries the instantiated field-type map, built once when the construct's own -context is computed. Reading the record declaration is a guarded index lookup -(`v2.std.symbol_index` `symbol_index_lookup`) against the subject's `resolved_declarations`. - -## Evidence when built - -- The two enrolled controls pass unchanged and leave `floor_expected_red` in the same change, and the - refusal above is enrolled green, as - that roster's monotone arm requires. -- Existing record-construct, declared-return, application-argument and optional-at-required - witnesses stay green. -- A native-route census, base against head: N reach / M changed, at identity grain. -- A cost reading on a construct with many fields, showing the field map is built once. - -## Sequencing - -1. eager-newt-412's site-keyed facts store lands. It converts the gather rows to per-site entries. -2. Checking mode: the gather moves to `fold_node_topdown`, adds the `child_context` arms above, and - changes the construct row as the only consumer. One PR, with the controls flipping in it. -3. Later slices add other consumers one at a time, each with its own red. diff --git a/docs/plans/issue-boundary-cuts-review.md b/docs/plans/issue-boundary-cuts-review.md deleted file mode 100644 index 360f3ae9964..00000000000 --- a/docs/plans/issue-boundary-cuts-review.md +++ /dev/null @@ -1,86 +0,0 @@ -# Issue boundary cuts and project focus — review index - -Base: `e286b29962c3b89904277c64d0e0c7cc25ddb2d0` (PR #12387). -Branch: `codex/issue-cut1-toposort`. Implementation is isolated from the serving checkout. -Nothing in this branch is merged or deployed. The auth flow, compiler, fleet configuration and -held allocation PR #12465 are untouched. - -## Review order - -1. `d446000e2d6` — Cut 1: command/state boundary. Private carrier snapshots, exact remote-ref - CAS, atomic event + operation receipt, original-result retry, native URL-encoded forms and - truthful detail-history refusals. [Detailed receipt](receipts/issue-cut1-command-boundary-2026-09-28.md). -2. `b09db59f677` — Cut 4.1: five Workspace administration obligations over the existing goal - assessment vocabulary. Row 4 has a supported Policy API read, but manual mutation. The - others use bounded console readback. No Google setting or credential was changed. - [API qualification](receipts/google-workspace-admin-api-2026-09-28.md). -3. `abd2d6857f2` — Cut 2: independent nesting, shared by worker alignment, supervision, - integration and presentation. A single event transition produces standing and assignment; - progress actors and attempt existence do not assign ownership. Typed ticket fields survive - presentation; description uses the brief; fleet placement leaves Google's extdeps model. -4. `8e8796ea0d5` — Project focus: clicking a title focuses its recursive project, displays the - recent three completed items and upcoming work in dependency order, and offers “…” for - earlier work. Open selected issue retains detail navigation; modifier-click keeps native - link behavior. Undo restores order/focus/expansion, column sorting exits topological mode, - and cycles refuse before changing the view. - -The first authored nesting migration is shell → DAG batch 1: the ten `shell-dag-*` work items -belong to `shell-typed-invocation`, as the existing batch contract explicitly states and the -operator selected for this acceptance case. Other reverse dependency links were **not** copied -into ownership. Their scheduling meaning remains intact. Additional projects require authored -membership; issues with no nesting stay independent. Both ends of nesting are the same work-item -kind. There is no separate Epic/Task hierarchy; the redundant hardcoded Task chip is removed. - -The old tree's shared-prerequisite links remain dependency links, but do not inflate owned-child -progress counts. This prevents the old “0 of 1” project claim for work owned somewhere else. - -## Validation boundaries - -The changed workflow has focused pure, actual carrier and browser controls. The full page -suite still reports the pre-existing ticket-brief budget failure. A run against an untouched -worktree at the base reproduced these six violations: `2-scm-native-authority-program` (134), -`floor-ceiling-roster-cut-completeness` (123), `frontend-eval-training-program` (177), -`fleet-mtcollins1-first-host` (153), `fleet-slot-retirement-wet-proof` (116), and -`cardinality-vertical-slice` (119). The limit is 100 words. This branch does not weaken it or edit -unrelated ticket contracts. - -The full serve witness closure previously exceeded the unchanged 6 GiB limit (exit 137). -Focused passes are not a green integrated floor. This branch remains a draft for review, not a -merge/deployment recommendation. Each receipt names the executed boundary; a typecheck is not -reported as an executed HTTP acceptance test. - -Known boundaries retained from the handoff: first-time Fabric assignment still invokes the -existing inline launch path; this cut does not install a durable launch-obligation consumer. -Per-issue attempt observation remains unavailable with an explicit rendered reason. Workspace -readback types are not a deployed Google observer; receipt producers must authenticate and -retain their evidence. Broad protected-storage cutover remains in the held allocation lane. - -## Reproducing the narrow controls - -Each control is a `.dag` entry, run with `gunbc run --source-root dag --source-root src/v2 ---entry --claim-run`. The Git CAS/race/replay control is -`test.manual.issue_command_wet`. The emitted clients come from `test.manual.issue_assignment_client` -(`write_issue_sort_script`, `write_assignment_client_script`). The form codec is -`test.claim.http.form_urlencoded_witness_test`. The Python browser drivers and closure copier that -were used locally had no `.dag` authority and no consumer, so they were removed rather than landed -as unmodeled harnesses (DESIGN §6). The browser-side controls have no committed instrument until -they are modeled. - -## Final local results - -- Page: 83 PASS; 1 FAIL, the six brief overruns reproduced on the untouched base. -- Presentation: 57 PASS. Alignment: 42 PASS. Continuation: 22 PASS. Submission: 34 PASS. -- Event-state: 19 PASS. Tracker: 34 PASS. Fleet component projection: 2 PASS. -- Recursive nesting: 3 PASS. Workspace administration: 5 PASS. Form codec: 2 PASS. -- Actual Git CAS/race/replay commands: PASS. Native Chromium form → DAG decoder: PASS. -- Emitted assignment retry client: PASS. Emitted project-focus/sort client with served CSS: PASS. - -These results are re-derived by the entries named above; the transcribed logs are not committed -(DESIGN §6: name the instrument, never transcribe its output). The late detail-parameter forwarding and whitespace cleanup are -small follow-ups to the executed page snapshot; they do not change the default served inputs. - -The ordinary push was rejected by `.githooks/pre-push`: inherited Rust formatting drift at -`src/v1/stage0/src/cli_run.rs:20257`. `cargo fmt --all --check` reproduces it on the untouched -base. The hook explicitly documents `git push --no-verify` as its override. That override is -used only to publish this draft for review; Rust source and the hook are unchanged. No landing -check is represented as passing because the push was allowed. diff --git a/docs/plans/issue-page-architecture.md b/docs/plans/issue-page-architecture.md deleted file mode 100644 index 15e7959ef9a..00000000000 --- a/docs/plans/issue-page-architecture.md +++ /dev/null @@ -1,108 +0,0 @@ -# The issue page architecture (owner mandate 2026-09-25) - -The per-issue page copies Buganizer's information architecture nearly -literally, retaining gunbc's stronger workflow evidence. The page must answer -at first glance: what is this, who owns it, what blocks it, what changed, what -happens next. It feels alive because people and Fabric visibly participate in -one narrative — not because more diagnostics are on screen. - -## Layout law - -- Title is the primary object/link; the canonical slug is secondary identity. -- Header carries ONLY planning status + type + priority (`[Done] [Task] [P2]`). - No competing status strings (`done`, `claimed by…`, `accepted`, standing - lines all at once is the defect). -- Default tab is **Comments**: description pinned above a chronological - comments/activity stream with author avatars, comment numbers, timestamps, - and a real composer. Tabs: `Comments · Dependencies · Work · History` - (Duplicates later, when duplicate semantics exist). -- Right metadata rail: status, assignee (the editable control), reporter, - component, parent, blocked-by/blocking, delivery, return-to, modified, - observation. -- Authored fields are not seven equal rows: Description (brief/motivation/ - outcome), Acceptance criteria (red control, hand-back), Current plan - (current state, first slice — collapsed once done), Scope (collapsible), - Work (implementation instructions and evidence). An empty - `verified · last verified` row never renders. -- Work tab: attempts, incarnations, provider selection, candidate heads, - verification receipts, raw diagnostics. Comments tab: discussion, assignment - changes, important blockers, publication, review handoff, status changes. - Never raw provider/belt noise in Comments. - -## One qualified-principal model - -```text -QualifiedPrincipal { subject, primary_email, display_name, - kind: Human | Workflow, avatar } -``` - -Email is the user-facing selector, never the durable identity — aliases can -change without rewriting history. The assignee control is an email -autocomplete (search, recents first from durable assignment history, avatar + -display name + email + kind, keyboard navigation, dedupe by stable principal, -`fabric@gunb.ai` pinned near top). NO model names anywhere in the assignment -UI (no glm/kimi/codex); Fabric picks the worker through capability/cost/ -capacity/serving authorities. The realization may appear in the Work tab. - -Avatar resolution order: internal principal profile → Google Workspace -directory/Contacts photo → cached prior → deterministic initials. Served via -`/avatar/` with TTL/ETag. Standing: -`AvatarResolved | AvatarAbsent | AvatarUnread`; absent and unread render -initials and NEVER refuse assignment, comments, or rendering. Fabric gets a -stable service avatar with a workflow badge — it must not masquerade as human. - -## Assignment IS the execution entry point - -```text -AssignIssue { issue, expected_issue_revision, assigned_by, assignee, return_to } -``` - -- Assign to human → transfer responsibility, no dispatch. -- Assign to `fabric@gunb.ai` → transfer responsibility + durably request - execution (`IssueAssignedToWorkflow → WorkRequested → launch admission`). - No Start Work button anywhere. -- Admission refusal does NOT undo assignment: the issue stays assigned to - Fabric and renders the located waiting condition ("blocked — no admissible - serving offer; retry when an offer becomes available"), not "assignment - failed". -- Re-assigning the same issue revision to Fabric joins the existing request - rather than minting another attempt. - -## Return-to-merge - -Record `requester`, `assignee`, `return_to/merge_owner` explicitly (default -`return_to = requester` — never derived from whichever event was first). On -OBSERVED publication: durable publication event → one compact automated -comment with PR/head → status Ready for review → assignee becomes return_to. -Merge remains the human completion action; publication never closes the issue. - -## Comments are durable events - -`IssueCommentCreated / Edited / Deleted` — each comment carries issue id, -comment id, author principal, body (or body ref), created/edited times, -visibility. Edits/deletes append events, never invisible mutation. Stable deep -links `/issue/#comment-7`. Composer with markdown/code/@mentions; retries -never duplicate. The stream interleaves only meaningful lifecycle events -(assignment, work accepted, attempt started, blocking decision needed, -candidate submitted, PR published, review requested, reassigned for merge, -closed/reopened). - -## Dependencies stay dimensional - -Dependencies tab preserves separate sections: parent/children, blocked -by/blocking, related, supersedes/superseded-by — never one flattened list. -Header may carry a compact summary (`Blocked by 2 · Blocking 1 · 4 children`) -that opens the tab. - -## Acceptance controls (12) - -1. Title primary link, slug secondary. 2. Assignee field opens avatar-backed -email autocomplete with fabric + humans + recents. 3. Human assignment never -dispatches. 4. Fabric assignment = exactly one work request, no Start Work. -5. Serving/capacity refusal leaves Fabric assigned with the located blocker. -6. Reload preserves assignments and comments. 7. Comment retries never -duplicate. 8. Missing avatars render initials, never block operations. -9. Publication posts one handoff comment and reassigns to the stored merge -owner. 10. Dependencies separate from parent/child. 11. No raw provider/belt -noise in Comments. 12. Work tab keeps exact attempt/candidate/verification/ -publication/incarnation evidence. diff --git a/docs/plans/kv-capacity-proof-abstract.md b/docs/plans/kv-capacity-proof-abstract.md deleted file mode 100644 index 5de1b94a1ff..00000000000 --- a/docs/plans/kv-capacity-proof-abstract.md +++ /dev/null @@ -1,129 +0,0 @@ -# KV capacity proof: the model-agnostic abstract (for the DS4.1 lane) - -Distilled from the GLM-5.3-Flash / group-B work (2026-09-24/25). Everything -here except section 4 is model-agnostic; section 4 is the worked GLM instance -to imitate. Governing doctrine: docs/plans/moa-memory-modeling.md. - -## 1. The question the proof answers - -Given a qualified subject — model, runtime, topology, parallelism, allocator, -workload, generation — decide, WITHOUT launching: - -```text -CacheFitProved / CacheFitRefused / CacheFitUnestablished -``` - -for a workload intent (sequence_length × seats) against every rank's allocator -inventory. The whole-arm verdict (ArmMemoryFit*) is a CONJUNCTION of subproofs -of which this is one: static residency, cache capacity, prefill transient, -decode transient, load transient, supply reserve. CacheFitProved never implies -ArmMemoryFitProved while another subproof is open. - -## 2. The one algebraic law - -A hybrid model's persistent cache is NOT a fungible token inventory. It is a -sum over cache GROUPS with different growth shapes: - -```text -PersistentCache(rank, length, seats) = - seats × Σ(fixed-per-sequence groups) (recurrent/state groups) - + seats × Σ(per-token groups) × length (full attention) - + seats × Σ(windowed groups) × min(length, window) - + seats × Σ(pooled-index groups) × ceil(length / pool_every) - + allocator grouping / padding (per-group block rounding) -``` - -The four growth shapes (extdeps.vllm.kv_layout's KvGroupKind): - -| shape | scales with | example | -|---|---|---| -| FullAttention | length × seats | dense attention layers | -| SlidingWindow | min(length, window) × seats | windowed attention | -| Mamba/KDA recurrent | seats ONLY | KDA/linear-attention state | -| KPoolIndex | ceil(length / pool_every) × seats | compressed sparse index | - -Demand: `seats × cold_admission_blocks(exact_layout, length)` per group, -folded against `allocator_blocks` and `arena_bytes` on EVERY rank, joined by -the LIMITING rank (ranks are asymmetric — measure or derive per rank, never -average). - -### The three forbidden moves - -1. **No full-context-equivalent division.** The engine's printed "GPU KV cache - size: N tokens" is a projection at one profile, not an inventory; - `N / new_length` is a CacheCapacityCandidate, never a proof. -2. **No linear rescale of allocated arena.** "KV memory in use" is the - RESIDUAL allocation (envelope − weights − activation − graphs), not demand - of the configured seat. -3. **No negative or composite terms as credits.** A negative component - observation is an attribution refusal (lower 0, upper open); - `weights + non-torch` is a composite observation, not a placement receipt. - -## 3. The proof machinery (all reusable) - -- **MemoryProofSubject** (7 axes): model@checkpoint-digest, runtime@source-sha - +image-digest, topology@host-census-revision, parallelism policy, - allocator@configuration-digest, workload@intent-revision, generation. -- **MemoryTerm**: { component, placement_law, scaling_law, phase, lifetime, - bound, derivation } — every byte names its mechanism. -- **Receipt-subject compatibility**: computed FROM the scaling law. Static - residency transfers across workloads; per-sequence-fixed transfers across - lengths at equal seat counts; every workload-sensitive term refuses across - subjects; a TP4 receipt refuses TP2 at the topology axis. Generation is - provenance, not a gate. -- **Measurement standings**: DerivedExact / DerivedConservativeInterval / - MeasuredUpperBound / ObservationOnly / ModelFalsified. A point sample never - becomes MeasuredUpperBound by being the largest seen once. -- **The allocation plan as the durable instrument** - (ResolvedKvAllocationPlan): the engine emits its group roster — kinds, - layer identities/counts, block sizes, page bytes, tokens-per-state, - grouping/padding — and the plan MUST reproduce the engine's printed capacity - as an independent control. After that, any workload point evaluates offline; - wet runs become falsification, not sizing. -- **Discriminating controls** (witness these in any new instance): - same arena + different roster → different capacity; - missing group → Unestablished; - pool_every 4→8 → derived capacity changes; - cross-topology receipt → refused; - cross-workload transient receipt → refused; - negative observation → cannot improve headroom. - -## 4. The GLM worked instance (what to swap for DS4.1) - -GLM-5.3-Flash @ TP4 (GB10 unified pool, fp8 KV, 0.8567 envelope): - -```text -architecture (from config.json, cited): - 45 layers = 34 KDA linear-attention + 11 sparse-attention (~3:1 interleave) - KDA: 64 heads × dim 128 → recurrent state ∝ seats (≈1 MiB/layer/seq/rank) - sparse: 512-dim MLA latent (∝ tokens), index_kpool 4 (∝ tokens/4), per-req tail -measured (bounded experiment, subject-bound per rank): - pools 119.69–121.69 GiB; weights+nontorch 77.0–81.6 (composite, asymmetric); - limiting rank TP3 (pool 119.69, arena 17.03 GiB) -engine control print: 1,805,689 tokens @ 64K profile → 27.55x at 65,536 -candidate (NOT a verdict): floor(1,805,689 / 262,144) = 6 seats at 262K -open: 262K prefill transient (sparse-indexer buffer ≈ 40 × 262,144 × 132 B - ≈ 1.289 GiB/rank, formula from the pinned runtime source) -``` - -For DS4.1, swap exactly: the architecture facts (layer census by attention -kind, head counts/dims, latent widths, any pooling), the pinned runtime -revision, the topology/host census, and the envelope policy. The fold, the -verdicts, the standings, the compatibility law, and the controls do not -change. DeepSeek-family specifics to resolve in the roster: MLA latent -dimensions, whether a pooled/index state exists, MoE expert placement under -the chosen EP policy (active-params is compute, NOT residency). - -## 5. Module map (reuse, don't fork) - -- `dag/extdeps/vllm/kv_layout.dag` — growth shapes, cold_admission_blocks, - layout mint refusals. Add DS4.1's group roster HERE as cited architecture - facts beside GLM's. -- `dag/extdeps/vllm/kv_group_metadata.dag` — the emission-gap model; the - ResolvedKvAllocationPlan rung. -- `dag/gunbc/spark/arm_memory_fit.dag` — the three-arm verdicts, MemoryTerm - census, receipt join, compatibility law. -- `dag/gunbc/spark/arm_memory_experiment_observed.dag` — the wet-bundle - binding pattern (how receipts admit with standings). -- `dag/gunbc/spark/group_b_serving_capacity.dag` — the capacity-intent → - derived-profile pattern (intent is the owner's decision; profile derives). diff --git a/docs/plans/lowering-occurrence-projection-design.md b/docs/plans/lowering-occurrence-projection-design.md deleted file mode 100644 index d08c8ae4a08..00000000000 --- a/docs/plans/lowering-occurrence-projection-design.md +++ /dev/null @@ -1,176 +0,0 @@ -# Lowering occurrence projection (XL-2 prerequisite `LoweringOccurrenceProjection`): design - -Status: design only. No compiler behaviour changes in the PR that lands this file. Tracked by -`gunbc.recurring_failure_mode.lowering_rebuilds_an_authored_atom_without_its_occurrence`, carrier arm -`gunbc.compiler_frontend_program_status` `LoweringOccurrenceProjection`. - -## 1. What was measured, and how - -**The instrument for the totals** is the census: -`v2.compiler.reference_conservation_census` `reference_conservation_census_for_paths`, run over -`reference_conservation_stratified_sample_paths` in batches of 25 paths. The whole sample in one -process does not fit in memory. Its per-module summary is the authority for `conserved`, -`locus_erased`, `dropped` and the channels, and this document does not copy those numbers. The -figures that were measured when this design was written are a dated receipt in the PR that landed -it, not a fact kept here. - -**The per-site split has no committed instrument yet, on purpose.** Nothing on a lowered node says -which function built it, so the census cannot attribute an erased atom to a site. The split in the -PR receipt came from a one-off instrumentation: - -- Each synthetic-minting site on the normalize route was tagged with a site id carried in - `OccurrenceProjected`, which the census treats exactly like `OccurrenceSynthetic`. -- A probe recorded which pool entry each locus-erased atom consumed: a rebuilt atom with its tag, - a `Named` edge label, or an atom lowered from the wrong source. - -That tagging is an edit to the compiler, so it cannot be an entry point, and it was not committed -(DESIGN §6: a one-off is not an instrument). **Nothing below depends on its numbers.** §3's -exclusion rests on grammar role alone, and §4's order uses only a coarse size comparison. PR 3 of -§4 makes the split re-derivable without tagging: the census reports each erased atom's grammar role -and its encoding (label, rebuilt atom, or wrong-source atom) as typed dispositions. - -### The sites, by grammar role and encoding - -Each row names the function that builds the node and how the locus is lost. The classification -is structural, from reading the lowering: which grammar role the atom has, and whether the -lowering encodes it as a label or as a rebuilt atom. - -| site | how the locus is lost | grammar role | XL-2 population? | -|---|---|---|---| -| `v2.std.node_query` `construct_tag_edge`, called from `body_lower_try_record_literal` (record tag `Uri { .. }`) and `body_lower_pattern_suffix_lowered` (pattern constructor `Present { .. }`) | the atom exists but is lowered from the **enclosing captured shell**, so it carries the shell's occurrence instead of the tag token's | constructor or type reference | **yes** | -| `v2.std.qualified_name` `qualified_name_spine_node`, reached from `body_lowering_fold` `body_lower_qualified_name_spine_node` | every segment atom is built with `node_synthetic`; the `occurrence_id` parameter reaches only the spine's root, and the input is already `List`, so there is no segment node to carry | dotted reference or type path | **yes** for the head segment and type paths; tail field projections are not | -| `body_lower_field_init_edge`, `body_lower_field_pattern_edge` | the name becomes a `Named` edge label | field name | no | -| the module body's `Named` edges for `data`, `fn`, `test fn` and `type` names; parameter lists; field declarations; variant declarations; named-argument labels | label | binder or label | no | -| `body_lower_let_expr` let binders | atom lowered from an enclosing shell | binder | no | -| module-header segments (namespace graft; the census matches them against its `header_pool`) | header pool | the module's own name | no (see §3; to be proven in PR 3) | -| `body_lower_method_operator_atom` (`.first` and similar method selectors) | `node_synthetic` | method selector, resolved through the receiver's type | no | - -**The reading that matters, and it holds without the counts:** - -- Only two constructors rebuild a *reference*. Everything else in the table is a binder or a - label. -- The largest reference site does not use `node_synthetic` at all. Its atom **is** lowered from a - source, just the wrong one. -- In the one-off measurement, `node_synthetic` inside `body_lowering_fold` itself was a negligible - site. The loss is in two `v2.std` constructors and in the readers that feed them. -- On the pinned sample, `dropped` is larger than `locus_erased`. The census shows this directly: - most of it is in modules that normalize refuses, which belong to other `xl2_prerequisites` - arms, and the rest is atoms `absent` from modules that normalize accepts. - -## 2. #11985's mechanism, and the one this carrier reuses - -#11985 added `v2.std.node` `node_lowered_from(kind, children, source: Node)`. It builds the new -node with `source.occurrence_id`, taken whole. A lowered node stands for the same authored text at -the same position, so it keeps that occurrence. A source that had no authored origin gives a node -that still has none, so the constructor can only preserve attribution and never invent it. - -**`OccurrenceProjected { caused_by }` is not the right carrier, and the RFM row's ceiling text is -wrong to name it.** `std.occurrence_identity` reserves `OccurrenceProjected` for a node minted in a -*different allocator*, meaning an insertion with no counterpart in the base, whose cause must be a -`ScopedOccurrenceRef`. It states outright that "a carried-forward node gets no arm here". Lowering -within one graph owns its source's occurrence, so using `OccurrenceProjected` there would create a -second identity for a fact `OccurrenceMinted` already carries. That is the parallel identity the -brief rules out. - -**The one mechanism:** every lowering site that rebuilds an authored atom builds it with -`node_lowered_from(source: )`. The reused type is -`std.occurrence_identity` `NodeOccurrenceIdentity`, the `OccurrenceMinted` arm, carried unchanged. -No new type, arm, or index is added. The measurement shows two ways a site gets this wrong, and the -same mechanism fixes both: - -1. **The source is the enclosing shell, not the terminal** (rank 3). `construct_tag_edge(tag: - Symbol, source: captured)` has a source, but it is the whole captured literal. The fix passes the - tag's own terminal node. That means `body_lower_record_literal_tag_optional` and the - pattern-suffix reader return the tag **node** instead of `Symbol`, and the tag atom is built with - `node_lowered_from(source: tag_node)`. -2. **The input no longer holds the nodes** (rank 4). `qualified_name_spine_node(qn: QualifiedName)` - receives `List`. By then there is nothing to carry, and `node_synthetic` is the honest - answer. Re-deriving the chain (DESIGN §6b) puts the earliest unjustified boundary at the reader - that turns segment nodes into symbols (`body_lower_postfix_chain_read` and its `segments`), not - at the spine builder. The fix carries the segment terminals to the builder, and the builder makes - each segment atom with `node_lowered_from(source: segment)`. - - `v2.std.qualified_name` `declaration_reference_node`, the marked carrier that resolve mints, keeps - the occurrence on its marker only, and does so on purpose. That is a separate post-resolve - decision and this design leaves it alone. - -**Climbing from mitigation to construction.** Once each reference site takes a `Node` source, the -constructors that only take a `Symbol` leave the lowering surface. A lowered atom is then built by a -constructor that *requires* its authored source, and "rebuilt without its occurrence" cannot be -written on the lowering route, with no lens needed. `body_lower_type_atom_node(sym, source)` already -has this shape. The remaining writable path is `node_synthetic` used with an `Atom` kind inside -lowering, and the last build PR closes it for reference positions. - -## 3. Binders and edge labels: not in the XL-2 population - -The XL-2 residual is the set of **resolve refusals after the typed import strip** -(`gunbc.compiler_frontend_program_status` `ExactOccurrenceResidualCensus`). XL-5's waves rewrite -those references. An atom is in the population only if removing imports can make resolve refuse -*at it*. - -- **A binder** (the name of a `fn`, `data`, or `type`, a variant declaration, a parameter, a field - declaration, a `let` name) introduces a name. Stripping imports cannot make it refuse, and no wave - rewrites it. -- **A field or argument label** (`Uri { uri: .. }`, `f(path: ..)`, `R { value: v } =>`) resolves - against the declaration of its type or callee. When that declaration is imported and stripped, - resolve refuses **at the type or callee reference** (rank 3 or 4), not at the label. A namespace - wave renames modules and declarations; it never renames a field or a parameter. -- **Module-header segments** are the module's own name. The graft consumes them, not resolve. - -So `EdgeLabel` (`v2.std.node` `Named { name: Symbol }`) does **not** need an occurrence for XL-2. -Adding one would change a substrate type that participates in structural equality and content -hashing, for a population XL-2 never reads. - -There is one real caveat. An occurrence-exact rename of a *declaration*, which XL-5 is not, would -need binder occurrences. That is a separate future carrier and should be modelled when a consumer -exists, not before (DESIGN §3c). - -Two consequences for the tracking rows, both corrected in this PR: - -- The RFM trigger "`locus_erased` reads zero on the stratified sample" is **over-scoped**: most of - that count is binders and labels, which no XL-2 fix will or should move. The capability is - stated as: *every reference-position atom that lowering rebuilds carries its authored terminal's - occurrence*. -- Deleting the declaration-scoped spelling pool in `v2.compiler.reference_conservation` needs a - second thing: binders must be told apart from references **by a grammar fact**, namely the atom's - role in its production, instead of by a spelling match. Until then the pool can shrink to binders - and labels, but it cannot be deleted. That work is PR 3 below. - -## 4. Rollout order and the regression check - -In order of size and risk: - -1. **Constructor tags: `construct_tag_edge` sources.** Change one shared constructor and its two - call sites: record literal and pattern suffix. It goes first because it is the smallest change. - In the one-off measurement it was about the same size as the spine site. -2. **Qualified spines: segment nodes reach `qualified_name_spine_node`.** This is larger: the chain - reader's result type changes, and so does a `v2.std.qualified_name` constructor that other - callers use. -3. **Census and closure.** Split the census's `locus_erased` into a typed binder/label channel - (counted, like `import_channel`) and a reference-position count, using the grammar role of the - atom. Take the `Symbol`-only atom constructors off the lowering route. Shrink the spelling pool. - -**The check: one controlled-fixture control per site, not a ratchet over the live sample.** Each -build PR brings a fixture module that exercises its site. A claim runs -`reference_conservation_of_subject` on it and asserts that the site's reference atoms are -`conserved`. The number is grounded in the controlled fixture, so it is a real oracle (DESIGN §5). -The red is run before the fix: on the base, the same claim reports the atom as `locus_erased`. The -positive control is a fixture atom that already conserves, such as a type atom (#11985). - -A conserved-share floor over the pinned sample is **not** recommended. That literal would be copied -from the current tree, which makes it a change detector. It is also not a closed universe: six of -its 315 paths have already been deleted. The sample census remains the observation that ranks the -sites, and PR 3 lets it report the split without the probe. - -## 5. Size - -- **In-population sites to change: 3** (the record-tag call, the pattern-constructor call, and the - qualified spine), in **2 constructors**: `construct_tag_edge` and `qualified_name_spine_node`. - Readers that change type: `body_lower_record_literal_tag_optional`, the pattern-suffix tag reader, - and `body_lower_postfix_chain_read`. -- **Out-of-population sites, recorded but not changed:** 8 label and binder encodings, the - let-binder atom, and the method-selector atom. -- **PRs: 3**, as in §4, each with its site fixture and a pre-fix red. PR 2 is the largest because - it changes a `v2.std.qualified_name` signature, so it has to enumerate that constructor's other - callers first. An optional fourth PR could cover the 7-atom method-selector site, but it is not in - the population. diff --git a/docs/plans/machine-intake-design.md b/docs/plans/machine-intake-design.md deleted file mode 100644 index b04e8ad6888..00000000000 --- a/docs/plans/machine-intake-design.md +++ /dev/null @@ -1,413 +0,0 @@ -# Machine intake: hardware qualification, provisioning, placement commissioning - -Operator ruling of 2026-08-29, transcribed as the authority for the `gunbc.machine_intake_*` -lane. The `.dag` modules cite this document; this document does not restate what they declare. -Where a section below names a type, the type is the authority and this is its rationale. - -Status: **INTAKE-0 and BOOT-DELIVERY-0 modeling in review** (gunbc#9690), reworked under the -operator's second ruling of 2026-08-29, the third ruling of 2026-08-30 -([machine-intake-ruling-3.md](machine-intake-ruling-3.md)) and the fourth ruling of 2026-08-30 -([machine-intake-ruling-4.md](machine-intake-ruling-4.md), whose ten-item final approval gate is -the merge gate: neither slice is recorded complete until they hold). Under ruling 4 the request's -`BootDeliveryTarget` (subject × protocol-neutral `BmcControllerEndpoint`) is BOUND to the access -context before any candidate is judged (`bind_boot_delivery_target`; subject and controller -mismatches are separate refusals), every transport-local standing (configfs via -`target_bound_reinstall_path`, whose observations are bound to a target at the observation and whose standing is derived by the producer — an empty population is Unestablished; UEFI HTTP; network boot) carries the target it was established for -and is refused as `CandidateEvidenceForOtherTarget` otherwise; the profile's capability row is -the exact-release `BmcFirmwareReleaseCapabilityRow` so raw "0.32" inhabits a valid profile (a -positive control proves it); a promoted profile binds only beside `ProfilePromotionVerified`, -which no producer can construct until INTAKE-AGENT-0A's evidence store — so every promoted -profile refuses today; the Redfish probe receipt is one coherent reading (payload-free ladder arm at the floor AND a nonempty `admitted_protocols`; a receipt saying one without the other is refused as a contradiction), boot CONTROL is established on its own evidence (`SurfaceBootControl` in the profile/observation intersection plus exactly one OBSERVED control route — `BmcBootControlRouteObservation`, its bytes in the evidence manifest, the route named by the profile — carried on the plan as `boot_control_route`; no observation refuses, an observed route the catalog does not name refuses, two observed routes are a typed ambiguity, never a catalog preference) and the observation -receipt carries an evidence MANIFEST that must name both the discovery bytes and the nested -probe's bytes; the plan's provenance preserves the selected candidate's own evidence. The -changed-witness execution sublane #9717 requires is dispatched as its own CI PR (child work -item of this lane). Under ruling 3 the solver reads a -`BootDeliveryRequest` (target unit and attempt, exact `BootArtifact`, staging offers each naming -the digest they serve), a `BoundBootDeliveryEvidence` whose access context -`gunbc.machine_intake_access` has already bound to this attempt (profile ∩ observation, capability -row read off the profile, observation receipt digest checked), and a `BootDeliveryPolicy`; it -answers with a `BootDeliveryPlan` that repeats target and artifact, derives the Redfish locator and -protocol from the probe RECEIPT, requires the MegaRAC route's `image_redirection` fact, and names -the profile provenance and observation receipt its eligibility came from. The capability-only -legacy solver, its `FirmwareUpdateThenVirtualMedia` shortcut, `srv3_install_mechanism`, -`fleet_install_server_specs` and the ProxyDHCP artifact path are deleted; `gunbc.os_install_mechanism` -is a compatibility projection that refuses at `InstallAccessProfileNotLookedUp`. Firmware release -identity is the raw vendor string (`BmcFirmwareReleaseIdentity`, so "0.32" is representable); -`FirmwareTransition*` lives in `gunbc.bmc_firmware_transition` and `NetworkBoot*` in -`gunbc.network_boot_delivery`. Under ruling 2: the Mt. Collins family binding is an EXPECTATION -(`MtCollinsBmcImplementationExpected`) until a workflow-layer `BmcImplementationObserved` -receipt exists; the one solver reads `BmcAccessProfile × BmcAccessObservation` through a total -profile lookup (`BmcAccessProfileStanding`), plans the Redfish arm only at -`TransferProtocolAdmitted` or above on the probe ladder, and takes its transport preference as an -explicit policy input; `derive_fleet_admission` consumes only a `ValidatedIntakeReceiptLedger` -(one genesis, linked, one subject and attempt, monotone); the in-substrate receipt hash is named -a structural fingerprint, with SHA-256 reserved for the durable producer; and -`gunbc.bmc_onboarding` is quarantined as non-authoritative legacy vocabulary. Executed -Mt. Collins and ASRock boots reaching the nonce-bound intake-agent callback are BOOT-DELIVERY-0's -acceptance and are still owed; INTAKE-AGENT-0, MEMORY-0 and SOAK-ADMISSION-0 are unbuilt. - -## 0. The ruling, in one paragraph - -The correct system is one hardware-independent qualification transaction with multiple -platform-specific access and boot-delivery realizations. "Virtual CD" is not a phase. Redfish, -OEM NBD/WebSocket, OpenBMC configfs USB gadget, UEFI HTTP, and PXE are alternative constructors -for one obligation: *deliver this content-addressed boot artifact to this identified host for one -boot, prove that the intended artifact executed, and restore the prior boot-control state.* -(`gunbc.boot_artifact_delivery`.) - -Four corrections to the original brief: - -1. Board serial is the durable unit key but is not enough to key a qualification. A repaired - machine keeps its board serial while every DIMM changes. Qualification is keyed to the board - serial plus an assembly-manifest digest (`gunbc.machine_intake_subject`). -2. Hardware qualification, OS provisioning, and placement commissioning are separate - transactions. They share boot delivery and receipts; success in one must not fabricate success - in another (`gunbc.machine_intake_phase`). -3. NFS traffic, a redirection session, boot-rail activity, KVM pixels, and a consumed boot - override prove activity, not test success. A memory verdict needs a machine-readable report or - an intake-agent receipt (`attest_diagnostic_boot`). -4. BMC-reported watts are not automatically wall watts, and EDAC reports are not automatically - DIMM-attributed. Both need explicit provenance types (SOAK-ADMISSION-0, MEMORY-0). - -Operationally: run all return-window-sensitive hardware testing immediately on arrival, before -shelving; run a second placement-specific commissioning soak after installation in its actual -power, cooling, and network environment; nothing serves until both have succeeded. srv2 is the -current candidate realization of an abstract staging service, not its identity -(`gunbc.machine_intake_staging`). - -## 1. The subject being qualified - -``` -QualificationSubject = UnitKey × AssemblyManifestDigest × FirmwareManifestDigest -UnitKey = validated unique baseboard serial -AssemblyManifest = chassis identity × CPU × DIMM × accelerator × storage × NIC × PSU identities -FirmwareManifest = BMC implementation/version × BIOS/UEFI × CPLD/SCP/platform firmware × configuration digests -MachineIntakeSubject = QualificationSubject × IntakeAttemptId -``` - -| Event | Consequence | -|---|---| -| BMC IP changes | Same unit; no identity change | -| Machine moves racks | Same unit and assembly; placement receipt becomes stale | -| One DIMM changes | New assembly subject; previous admission cannot apply | -| BIOS/BMC firmware changes | New firmware subject; affected qualification must be repeated | -| Motherboard changes | New unit key | -| Board serial absent, duplicated, or contradictory | Identity refuses; no fleet key is fabricated | - -The chassis part number is a first-class identity fact (platform-variant discriminator), not the -unit key. A physical DIMM label is bound to its SPD identity (`PhysicalDimmLabelBinding`); an -absent or duplicated SPD serial is preserved as a limitation, never papered over by the label. - -## 2. Platform facts vs live access observations - -`extdeps.bmc.access_profile`: `BmcAccessProfile` is platform/catalog data keyed by baseboard × -`ChassisApplicability` (an explicit arm, never absence-means-all) × implementation × exact raw -firmware release (`BmcFirmwareReleaseIdentity`), carrying its routes as inhabitants -(`BmcAccessRoute`: Redfish locators, IPMI lanplus, the MegaRAC REST route with its typed -`image_redirection` fact, the OpenBMC shell, the OpenBMC NBD WebSocket path) so a no-Redfish -build is representable without inventing locators, plus the capability row that release carries -(a catalog row whose row firmware disagrees with its key is refused at lookup). Provenance is -`ProfileFromExternalAuthority | ProfilePromotedFromObservation`, never the rendering declaration. -`BmcAccessObservation` is per endpoint with its raw response digest and a resource-bound -`RedfishVirtualMediaProbeReceipt?`; the lookup answers `Known | Uncatalogued | Ambiguous | -CatalogRowRefused` (external facts only). The controller is named protocol-neutrally -(`BmcControllerEndpoint { host }`, ruling 4 §3): one Mt. Collins boot drives one controller over -MegaRAC REST for delivery and IPMI for boot control, so an identity carrying a protocol has -already selected a route. The capability row a profile carries is the exact-release -`BmcFirmwareReleaseCapabilityRow` (ruling 4 §2), so a release with no semantic reading can be -catalogued. Which attempt the observation belongs to, whether the lookup ran, whether the -evidence manifest names every blob read, and whether a promoted profile's receipts were -verified are WORKFLOW standings: `gunbc.machine_intake_access.bind_bmc_access_context` produces -the one `BoundBmcAccessContext` the solver may read, and `ProfilePromotionVerificationStanding` -refuses every promoted profile until the INTAKE-AGENT-0A evidence store produces -`ProfilePromotionVerified` (ruling 4 §4). - -`BmcFirmwareFamily` gained `AmiMegaRac` (`extdeps.bmc.endpoint`, gunbc#9678); the implementation -module is `extdeps.bmc.megarac` and the vendor row `extdeps.vendor.ami`. The family-keyed -dispatchers moved out of `extdeps.bmc.openbmc` into `gunbc.bmc_implementation_dispatch` so a -MegaRAC change never edits the OpenBMC module (DESIGN §3 external upstream decomposition). -Family-grain MegaRAC facts are cited (admin/admin published default, IPMI-only protocol floor); -build-scoped facts live on the build binding `extdeps.ampere.mt_collins_product_brief.bmc` — -the observed Foxconn Mt. Collins build (BMC 0.32) serves NO Redfish and boots through the -proprietary REST remote-media surface, so the Mt. Collins delivery arm is -`MegaRacRestRemoteMedia`, not the DMTF pull. A profile/capability row per exact firmware is -still to be authored from that observation. - -## 3. The boot boundary - -Boot control (set one-shot target × reset × observe override consumption × restore) and artifact -delivery (visible to firmware × bytes proven × intended host consumed it × detach) are -independent obligations. `BootArtifactDelivery` is the coproduct -`RedfishVirtualMediaPull | MegaRacRestRemoteMedia | OpenBmcNbdProxyWebsocket | -OpenBmcConfigfsUsbGadget | UefiHttpBoot | PxeChainBoot`; `extdeps.bmc.virtual_media` gained the -`RedfishVirtualMediaTransport` and `MegaRacRemoteMediaTransport` arms and -`extdeps.bmc.redfish_virtual_media` models the DMTF shape. Boot purpose is data -(`BootPurpose`); the artifact set is per architecture (`IntakeArtifactSet`), never a bi-arch -invariant. - -Platform mapping: - -| Platform observation | Candidate delivery | -|---|---| -| MegaRAC build with live-observed Redfish VirtualMedia | RedfishVirtualMediaPull | -| Mt. Collins MegaRAC (observed build 0.32: no Redfish) with live-observed REST remote media | MegaRacRestRemoteMedia | -| ASRock/OpenBMC with live-observed OEM NBD WebSocket | OpenBmcNbdProxyWebsocket | -| ASRock/OpenBMC with BMC SSH, NBD client, configfs gadget, usable UDC | OpenBmcConfigfsUsbGadget | -| Firmware with observed UEFI HTTP support and reachable artifact service | UefiHttpBoot | -| Firmware with all network-boot requirements established | PxeChainBoot | - -`gunbc.os_install_mechanism` is a compatibility projection of the delivery solver with no solver -of its own; its consumers migrate to `BootDeliverySolution` and it is then deleted (its frozen note -carries the trigger). A `BootDeliveryPlanned` result is a candidate PLAN bound to target and -artifact — insert, host consumption, eject and detach are established only by execution receipts. -The boot-control target is protocol-neutral (`BootControlTarget`); which BMC operation sets it is -realization bound to the route the profile exposes. The request's `BootDeliveryTarget` is bound -to the access context first (`BoundBootDeliveryTargetContext`, ruling 4 §1) and the transport-local -standings each carry their own target, so a plan can never name a subject or controller other -than the one its evidence was gathered for; the plan's `BootDeliveryEligibilityProvenance` names -the profile provenance, the evidence manifest, the selected candidate's own evidence -(`SelectedCandidateEvidence`) and every candidate's standing. - -## 4. The workflow: one `MachineIntake`, three transactions - -**A — arrival hardware qualification** (immediately after delivery or repair, before shelving): - -| Phase | Required result | -|---|---| -| AccessDiscover | BMC implementation, exact firmware, supported protocol surfaces, BMC clock observed | -| IdentityBindProvisional | Board/chassis/BMC identities collected without contradiction | -| PriorLifeBoundary | Existing logs archived, then cleared or a precise immutable baseline cursor established | -| BmcSecure | Unique managed credential works; published factory credential no longer works | -| BootDeliveryEstablish | One eligible delivery transport selected from live observations | -| DiagnosticBootAttest | Intended intake artifact calls back with the attempt nonce and repeats machine identity | -| InventoryConform | BMC, SMBIOS/OS, and platform slot-table observations reconcile | -| MemoryFastQualify | Parseable pre-OS memory-test result | -| MemoryAuthoritativeQualify | Linux stress plus ECC/RAS before-and-after delta | -| SubsystemQualify | CPU, storage, network, accelerator, PSU, cooling obligations for the expected component set | -| AcceptanceSoak | Return-window heat/load test succeeds | -| ArrivalCleanup | Media detached, override cleared, temporary access revoked, desired power state restored | - -A machine that completes this is HardwareQualified. It still cannot serve. - -**B — provisioning:** OsArtifactDelivered → OsInstalled → InstalledSystemBootedFromLocalTarget -→ RuntimeIdentityMatchesIntakeSubject. OsInstalled needs a post-install producer (the installed -agent calling back after a normal local boot); a consumed override, a read ISO, or a DHCP lease -is not sufficient. - -**C — placement commissioning** (in the serving location): PlacementIdentityBound → -ActualCoolingAndFanPolicyObserved → ActualNetworkPathsQualified → PlacementLoadSoakQualified → -WallPowerMeasured → ServiceRuntimeAdmitted → FleetAdmitted. Moving the machine, changing its PSU -feed, cooling, or fan policy invalidates this receipt without invalidating memory qualification. - -`gunbc.bmc_onboarding` combines factory credentials, rotation, OsInstalled and FabricJoined in -one vocabulary; extracting those into the three transactions is a follow-up, not a second -lifecycle. - -## 5. Identity and prior-life handling - -Identity is provisional until the Linux intake environment repeats collection from the host -side and BMC identity agrees with host-visible SMBIOS/SPD/PCI identity agrees with platform -expectations. Disagreement is a named finding: BoardSerialDisagrees, ChassisPartNumberDisagrees, -BmcAndHostMemoryPopulationDisagree, DuplicateDimmSerial, ExpectedComponentAbsent, -UnexpectedComponentPresent (InventoryConform, unbuilt). - -Prior-life boundary: before clearing anything, preserve BMC clock, SEL/event logs, Redfish log -collections, audit/account events, current boot override, virtual-media state, power state, and -a sensor snapshot as an immutable content-addressed capture. Then -`PriorLifeBoundaryEstablished = LogsArchivedAndCleared | LogsArchivedWithBaselineCursor`; the -second arm only where platform policy explicitly admits an uncleared append-only boundary. The -receipt carries the pre-clear archive digest and the post-clear observation. - -## 6. Credential qualification - -Factory credentials are a bootstrap mechanism, not an ordinary credential state. BmcSecure: -probe published bootstrap credential → authenticated session → create/rotate unique per-unit -credential → verify new works → verify published fails → record secret reference and credential -epoch. The receipt never contains the password. A non-working factory credential is an access -state (CredentialStateUnknown, PreviouslyRotatedCredentialRequired, -AccountManagementUnavailable) → PartsHold, not ReturnWindow, absent other evidence. - -`gunbc.machine_intake_bmc_secure` is the authority for that standing: `derive_bmc_secure` folds -one `BmcCredentialRotationObservation` — the bootstrap reading, the rotation application, and the -two post-rotation readbacks — into a `BmcSecureStanding`, and `bmc_secure_phase_receipt` projects -that onto the receipt spine so `derive_fleet_admission` reads it like any other phase. The -standing is `sole_constructor` and carries the `MachineIntakeSubject` it was adjudicated for, and -the receipt reads its subject off the standing rather than taking one: a standing derived for one -attempt has no argument left to swap, so it cannot be re-addressed as another attempt's evidence. -Both post-rotation readbacks must be ordered after the rotation they claim to verify — an ordering -among the supplied readings, which establishes nothing about whether a BMC was probed — and a -readback that did not establish a credential state refuses rather than widening into the good arm. -No refusal cause is owned by `UnitHardware`. The module observes nothing; the effectful rotation -actuator that produces the observation is the declared frontier, and it is the same producer the -ruling gates on the `gunbc.bmc_onboarding` split. Evidence: -`test.claim.machine_intake_bmc_secure_witness_test`. - -## 7. Proving the diagnostic environment booted - -Each attempt mints a nonce; the intake agent returns `IntakeAgentBootReceipt { attempt_nonce, -artifact_digest, architecture, board_serial, assembly_observation, firmware_observation, -booted_at }`. That receipt, and nothing else, is what a future attestation would rest on — -but nothing constructs DiagnosticBootAttested today, and -`gunbc.machine_intake_receipt.attest_diagnostic_boot` does not: a matching singleton returns -DiagnosticBootCompletenessUnestablished, because an observed population is not a closed one. -The positive arm waits on an acquisition producer that CLOSES the callback population — bound -issuance, single ingress, close-before-snapshot, gap-free transcript, evidence from that same -acquisition. The module is the authority for which arms exist. NFS plateaus, redirection sessions, -power signatures, reset completion, SOL streams and KVM pixels are supporting observations that -ride in the refusal. KVM is optional forensic capture. - -## 8. Memory qualification (MEMORY-0, unbuilt) - -Fast gate: `FastMemoryQualified { artifact, report_digest, pass_count, tested_bytes, error_count } -| FastMemoryRefused`; a delivery session plus power activity without a report is -`MemoryExerciseObserved`. Authoritative gate: before snapshot, test plan, after snapshot -(SMBIOS/DMI, SPD, Redfish Memory, EDAC topology, EDAC CE/UE before/after, rasdaemon, MCE/APEI/ -GHES, stressapptest, stress-ng); deltas during the interval, not lifetime counters; thresholds in -policy rows (new UE → defect; new CE → refuses, swap diagnosis; zero → gate may hold). -Attribution is a separate standing: `DimmAttributed | RankAttributed | ChannelAttributed | -ControllerAttributed | Unattributed` — a channel-level error fails the gate but cannot construct -DimmDefective. Swap-pass diagnosis binds every stick label to SPD first; every physical swap -changes the assembly digest and starts a new subject. - -## 9. Beyond memtest (SOAK-ADMISSION-0, unbuilt) - -`ExpectedSubsystemSet` derives from platform and assembly inventory; an expected component cannot -silently disappear from testing, and a platform with no accelerator derives no accelerator -obligation rather than a Skipped row. Per-subject observations: CPU topology/load/throttling/RAS -deltas; memory population/stress/CE-UE/temperature; storage identity/SMART/self-test (destructive -only on declared-disposable storage); NIC MAC/link/errors/throughput to staging; accelerator -PCIe/device memory/compute/ECC-Xid-AER/thermal; fans tach presence and response; PSU inventory, -telemetry, external wall measurement where required; BMC sensor completeness, clock, log service, -account state. - -## 10. Load soak and power (SOAK-ADMISSION-0, unbuilt) - -Two policy rows: ArrivalAcceptanceSoak (return-window defects) and PlacementCommissioningSoak -(environment-specific). Both record load recipe digest, duration, ambient, sample interval, -temperature maxima, fan extrema, throttling, RAS deltas, power samples, cooldown. -`PowerMeasurementPoint = WallAcInput | PsuAcInput | PsuDcOutput | BoardRail | BmcUnspecified`. -Only a WallAcInput observation (or a justified calibrated conversion from a precisely identified -point) may retire the economics model's 350 W wall-power assumption; without one, emit -WallPowerUnestablished. `PowerTelemetryAgreement { wall_ac, bmc_reported, difference }`. - -## 11. Disposition - -`MachineIntakeDisposition = Admitted | ReturnWindow | PartsHold` with -`QualificationRefusalOwner = UnitHardware | BmcAccess | StagingInfrastructure | PlatformModel | -QualificationPolicy | OperatorAction`. Mapping (`gunbc.machine_intake_disposition`): observed -hardware defect ∧ window open → ReturnWindow; defect ∧ window closed → PartsHold{RepairOrCull}; -qualification unestablished → PartsHold{cause, owner}; placement pending → -PartsHold{PlacementPending}; all gates ∧ subject current ∧ cleanup confirmed → Admitted. -`FleetAdmissible` is derived from receipts; a hand-authored Admitted row cannot substitute. - -## 12. Receipt architecture - -Every phase emits one normalized envelope (`IntakeReceiptEnvelope`) plus evidence refs by -digest. Wire schema `gunbc.machine-intake.receipt/v1`; raw archive -`/srv/bmc/intake/artifacts//`, `attempts//manifest.json`, `phase/*.json`, -`evidence/`. The JSON is an observation transport, not a second decision authority. -Required properties: canonical digest; attempt identity and exact subject; tool/agent/artifact/ -policy identities; start/end; evidence by digest; success or typed refusal; hash-chain link; -staging identity; no credentials or one-time tokens. - -## 13. The `intake ` executable (unbuilt) - -One operator command: acquire endpoint lock → discover access surface → bind provisional identity -→ acquire unit/attempt lock → ask the modeled solver for the next phase plan → execute through the -selected adapter (redfish-standard, ipmi-fru, ami-megarac, openbmc-nbdproxy, openbmc-configfs, -uefi-http, pxe) → append receipt → resume until terminal disposition. Phase order and disposition -live in `.dag`, not in `if chassis == ...` branches. Needs idempotent resume, per-endpoint then -per-unit locks, one-shot callback tokens, content-addressed artifacts, cleanup/compensation after -every failed delivery, no secrets in logs, a dry plan mode, a typed terminal receipt on staging -failure. It emits observations; it does not author `.dag` rows. Dissolution: delete it when the -modeled fleet-ops executor consumes the same plan, performs every effect, and produces -byte/schema-equivalent receipt envelopes for both the Mt. Collins and ASRock transport fixtures. - -## 14. Integration map - -| Existing authority | Change | -|---|---| -| `extdeps.bmc.endpoint` | `AmiMegaRac` arm (landed via gunbc#9678 and this PR) | -| `extdeps.bmc.megarac`, `extdeps.vendor.ami` | implementation module (gunbc#9678: cited admin/admin default, IPMI floor, proprietary REST remote-media operations, executed Mt. Collins boot recipe) and vendor row | -| `extdeps.bmc.access_profile` | profile/observation carriers and intersection (landed) | -| `extdeps.bmc.redfish_virtual_media`, `extdeps.bmc.virtual_media` | DMTF shape; `RedfishVirtualMediaTransport` arm (landed) | -| `gunbc.boot_artifact_delivery` | the one delivery solver: request × bound evidence × policy → plan (landed, ruling 3 §1-§2) | -| `gunbc.machine_intake_access` | attempt-bound access context producer; `BootDeliveryTarget`, evidence manifest, promotion verification standing (landed, ruling 3 §1, ruling 4 §1/§4/§5) | -| `extdeps.bmc.endpoint`, `extdeps.bmc.capability` | `BmcControllerEndpoint`; `BmcFirmwareReleaseCapabilityRow` (landed, ruling 4 §2-§3) | -| `gunbc.install_transport_qualification` | `TargetBoundReinstallPath` — the host transport standing joined to an intake target (landed, ruling 4 §1) | -| CI changed-witness execution sublane | dispatched as its own PR (child work item; ruling 4 "#9717 is not an unrelated CI incident") | -| `gunbc.bmc_firmware_transition`, `gunbc.network_boot_delivery` | standings cut out of the solver into their own authorities (landed, ruling 3 namespace ruling) | -| `gunbc.os_install_mechanism` | compatibility projection; capability-only solver and `FirmwareUpdateThenVirtualMedia` deleted (landed, ruling 3 §3) | -| `gunbc.os_install`, `gunbc.generated_artifact` | `fleet_install_server_specs`, `InstallServerSpec`, `gunbc.install_server_emit` and the `ProxyDhcpDnsmasqArtifact` path deleted (ruling 3 §3) | -| `gunbc.bmc_implementation_dispatch` | family dispatch moved out of `extdeps.bmc.openbmc` (landed) | -| `gunbc.machine_intake_*` | subject, phase, receipt, disposition, staging (landed) | -| `gunbc.bmc_onboarding` | extract BMC access/security, provisioning, fabric joining (follow-up) | -| `gunbc.nbd_proxy_virtual_media_install` | realize `ActuatorRedfishInsertMedia` through the Redfish arm (follow-up) | -| `extdeps.bmc.capability` | inventory, log, telemetry, cleanup capabilities (follow-up) | -| private `strategy.*` | per-unit normalized receipts and return-window facts (follow-up) | - -srv1 vs srv2 as staging host is resolved by observation (`intake_staging_realization_today` -is `StagingUnobserved`), not by prose. - -## 15. Implementation sequence - -- **INTAKE-0 (modeling in review; ruling 3 gate 5 — lifecycle-valid ledger, admission - provenance — in this PR):** subject, manifests, attempt, phase standing, refusal owner, - disposition, fleet-admission derivation, receipt envelope; controls in - `test.claim.machine_intake_subject_witness_test` and - `test.claim.machine_intake_disposition_witness_test`. -- **BOOT-DELIVERY-0 (modeling landed; execution owed):** MegaRAC identity, Redfish VirtualMedia - transport, solver consuming configfs/WebSocket, UEFI HTTP split from PXE; controls in - `test.claim.boot_artifact_delivery_witness_test`. Acceptance: one executed Mt. Collins boot and - one executed ASRock boot reaching the same nonce-bound intake-agent callback. -- **INTAKE-AGENT-0A (next, per ruling 3):** deterministic x86-64 and AArch64 artifacts with - recorded content identities; a callback carrying attempt nonce, exact artifact digest, - architecture, raw board serial, assembly/firmware observation digests and boot time; replay and - duplicate-callback refusal; wrong-nonce/artifact/unit/architecture REDs; canonical durable receipt - bytes and their SHA identity; raw EDAC/RAS, inventory and sensor capture only; local execution of - the callback contract without a BMC. Then ACCESS-OBSERVE-0 / BOOT-DELIVERY-0E on hardware: - observation → profile lookup → plan → execute → agent callback → cleanup, on Mt. Collins and ASRock. -- **MEMORY-0:** fast report ingestion, authoritative plan, EDAC/Redfish deltas, attribution, - label/SPD binding, swap-pass diagnosis. -- **SOAK-ADMISSION-0:** subsystem recipes, thermal/fan policy, wall-power producer, two soak - profiles, final fleet-admission consumer. - -The smallest useful MVP reaches the Linux agent, inventory conformance, and authoritative memory -evidence; automating media attachment while the verdict stays manual is not it. - -## 16. Discriminating controls - -| Perturbation | Required result | Where | -|---|---|---| -| Change one DIMM while retaining the board serial | Old admission no longer applies | subject + disposition witnesses | -| Leave factory credential working after rotation | BmcSecure refuses | bmc secure witness | -| Remove Redfish VirtualMedia from live observation, keep it in the profile | Redfish transport not selected | boot delivery witness | -| Stage a URI serving digest B for a request naming digest A | `CandidateStagedArtifactMismatch`, no plan | boot delivery witness | -| Hand the solve an observation receipt from another attempt | `AccessContextObservationForOtherAttempt`, no plan | boot delivery witness | -| Receipt manifest omits the observation's raw response | `AccessContextObservationEvidenceMismatch` | boot delivery witness | -| Receipt manifest names the discovery bytes but not the nested probe's | `AccessContextProbeEvidenceMismatch` | boot delivery witness | -| Request target for attempt two, context bound for attempt one (same controller) | `DeliveryTargetSubjectMismatch`, no plan | boot delivery witness | -| Request target for controller .61, context bound for .60 (same subject) | `DeliveryTargetControllerMismatch`, no plan | boot delivery witness | -| Configfs path / network boot established for another target | `CandidateEvidenceForOtherTarget` | boot delivery witness | -| Profile promoted from an observation, receipts unresolvable | `AccessContextPromotedProfileUnverified`, no plan | boot delivery witness | -| Raw release "0.32" with an exact-release OEM row and matching observation | context binds, MegaRAC plan (positive) | boot delivery witness | -| Probe arm below the floor while the population lists HTTPS | `CandidateRedfishProbeContradictory`, never resolved by preferring one side | boot delivery witness | -| Live observation lacks `SurfaceBootControl` while catalog names override and media is live | `DeliveryBootControlSurfaceUnestablished`, no plan | boot delivery witness | -| Profile names Redfish and IPMI; boot control observed over IPMI only | plan carries IPMI, never Redfish | boot delivery witness | -| No route-specific boot-control observation / both routes observed | `DeliveryBootControlRouteUnobserved` / `DeliveryBootControlRouteAmbiguous`, no plan | boot delivery witness | -| Boot control observed over MegaRAC REST (named by the profile, no control operation) | `DeliveryBootControlRouteNotControlCapable` | boot delivery witness | -| Control-route observation bytes absent from the manifest | `AccessContextControlRouteEvidenceMismatch` | boot delivery witness | -| Empty configfs observation population | `CandidateConfigfsUnestablished` with all eight probes missing — no authored establishment | boot delivery witness | -| Every current phase in the three rosters | exactly one rank, all distinct (positive) | disposition witness | -| Catalog row whose capability firmware differs from its key | lookup refuses the catalog | boot delivery witness | -| Probe admitted NFS only, staging offers HTTPS | `CandidateRedfishProtocolNotAdmitted` | boot delivery witness | -| MegaRAC route with `image_redirection` Unset | `CandidateOemParameterUnset`, no plan | boot delivery witness | -| Remove virtual media and provide no network-boot evidence | No implicit PXE | boot delivery witness | -| Preserve NFS/media activity, remove the report/callback | Boot verdict refuses | disposition witness | -| Make EDAC unavailable | Memory standing unestablished, not zero-error | MEMORY-0 | -| Channel-attributed error only | Gate fails; no DIMM convicted | MEMORY-0 | -| BMC watts without wall meter | Wall power unestablished | SOAK-ADMISSION-0 | -| Fail the staging artifact service | PartsHold{StagingInfrastructure}, never ReturnWindow | disposition witness | -| Leave media attached or override active | Cleanup refuses; admission impossible | ArrivalCleanup (unbuilt) | -| Re-run from a stale attempt after a repair | Receipt subject mismatch refuses | disposition witness | -| Manually write Admitted without receipts | Derivation refuses | disposition witness | diff --git a/docs/plans/machine-intake-ruling-3.md b/docs/plans/machine-intake-ruling-3.md deleted file mode 100644 index 577e67a7dd2..00000000000 --- a/docs/plans/machine-intake-ruling-3.md +++ /dev/null @@ -1,421 +0,0 @@ -# Machine-intake ruling 3 — review of #9690 @ d4b1e6a104 (2026-08-30) - -Transcribed verbatim from the design side-chat (turn c6f87ffa). Citations stripped. - -# Review verdict - -**REQUEST CHANGES / HOLD at `d4b1e6a104ce8bf78e147642742c259e98994a59`.** The major repairs are real, but ruling 2 is not fully discharged. I would not yet record either **INTAKE-0 complete** or **BOOT-DELIVERY-0 modeling complete**. - -The following parts now satisfy the ruling: - -- `MtCollinsBmcImplementationExpected` no longer presents the family as an Ampere-authored platform fact. -- The solver returns `BootDeliveryPlanned`, not an execution establishment. -- `derive_fleet_admission` accepts only `ValidatedIntakeReceiptLedger`. -- `intake_staging_realization_today` remains `StagingUnobserved`. -- The `bmc_onboarding` split may remain a follow-up. fileciteturn35file0L2-L2 fileciteturn31file0L2-L2 fileciteturn46file0L2-L2 fileciteturn50file0L2-L2 - -Four ruling-level blockers remain. - -## 1. The new solver is not bound to **this artifact on this host** - -`BootArtifact` correctly names purpose, architecture, digest, format, entrypoint, and build identity. But `BootDeliveryEvidence` contains no `BootArtifact`, target unit, target host identity, or intake attempt, and `BootDeliveryPlanned` contains only a transport plus the considered verdicts. The selected Redfish URI, MegaRAC image name, NBD export, or network-boot evidence is therefore not proven to represent the `BootArtifact` defined earlier in the same module. The plan also drops the endpoint from which eligibility was derived. fileciteturn51file0L2-L2 fileciteturn31file0L2-L2 fileciteturn52file0L2-L2 - -This admits a straightforward false plan: - -```text -requested artifact digest = A -Redfish image URI actually serves digest = B -profile + probe + params are otherwise eligible -result = BootDeliveryPlanned -``` - -Nothing in the solver compares A and B because A is not an input. - -The solver needs a request and a result closer to: - -```dag -type BootDeliveryRequest { - target: BootDeliveryTarget - artifact: BootArtifact - access: BoundBmcAccessContext - staging: BootArtifactStagingBinding -} - -type BootDeliveryPlan { - target: BootDeliveryTarget - artifact: BootArtifact - delivery: BootArtifactDelivery - boot_control: BootControlPlan - eligibility_provenance: BootDeliveryEligibilityProvenance -} -``` - -Every transport-specific staging binding must name or derive the exact artifact digest. The eventual callback then repeats the digest from the same plan. - -### The access evidence can also be spliced across identities - -`BootDeliveryEvidence` independently accepts: - -```text -BmcFirmwareCapabilityRow -BmcAccessProfileStanding -BmcAccessObservationStanding -``` - -The capability test reads `ev.row`; surface eligibility reads only the profile and observation. There is no check that: - -```text -row.firmware -= profile.key.firmware -= observation.identity.firmware -= observation.firmware -``` - -A capability row for firmware A can therefore contribute `CapabilityVirtualMedia` while a profile and observation for firmware B establish the surface. `BmcFirmwareCapabilityRow` itself carries only firmware and a capability list—not board identity or source provenance. fileciteturn31file0L2-L2 fileciteturn47file0L2-L2 - -`BmcAccessObservation` also carries the firmware twice—inside `identity.firmware` and again as `firmware`—with no constructor proving they agree. It is described as per-attempt, but carries neither an attempt ID nor a qualification subject, and it contains no raw-response evidence digest. An observation from a previous attempt can therefore be handed to the current solve as long as its interpreted identity still fits. fileciteturn30file0L2-L2 - -Required shape: - -```dag -type BoundBmcAccessContext { - subject: MachineIntakeSubject - endpoint: BmcEndpoint - identity: BmcObservedIdentity - capability_row: BmcFirmwareCapabilityRow - profile: BmcAccessProfile - profile_provenance: BmcAccessProfileProvenance - observation_receipt: EvidenceRef -} -``` - -Its producer must prove all identity joins before the delivery solver can run. Better still, place the exact capability row inside `BmcAccessProfileCatalogRow`, with a constructor proving that its firmware equals the profile key. Then remove the independently supplied `row` from `BootDeliveryEvidence`. - -Add RED controls for: - -- capability-row firmware different from the profile; -- `observation.firmware != observation.identity.firmware`, if the duplicate field remains; -- a live observation from another attempt; -- a profile whose declaration exists but whose underlying observation receipt is absent. - -### The real Mt. Collins firmware cannot currently inhabit the key - -The retained historical report calls the Mt. Collins firmware `0.32`, while `bmc_firmware_version_from_wire` accepts exactly three numeric components and every profile/observation key requires `BmcFirmwareVersion`. A live wire value of `0.32` therefore either refuses or must be fabricated as `0.32.0`. fileciteturn35file0L2-L2 fileciteturn47file0L2-L2 fileciteturn30file0L2-L2 - -Preserve an opaque exact vendor release identity: - -```dag -type BmcFirmwareReleaseIdentity { - family: BmcFirmwareFamily - raw_version: NonEmptyStr - semantic_version: FirmwareSemanticVersion? -} -``` - -Exact profile lookup should use the raw vendor identity. Semantic parsing is an optional derived view, never a prerequisite for observing the firmware. - -## 2. A planned transport can disagree with the route that was observed - -### Redfish protocol and resource are unbound - -`TransferProtocolAdmitted` carries the admitted protocol, but `redfish_virtual_media_candidate` discards that payload and merely checks that separate `RedfishVirtualMediaParams` are present. Those params independently carry a media locator and protocol. Thus: - -```text -probe admitted NFS -params request HTTPS -``` - -still produces `BootDeliveryPlanned`. The same is true of the media member: the probe carries no locator, while the params may name any member. `InsertSucceeded` and `EjectSucceededAndDetached` are also accepted as plan-floor evidence even though those arms have lost both locator and protocol. fileciteturn32file0L2-L2 fileciteturn31file0L2-L2 fileciteturn44file0L2-L2 - -The probe must be resource-bound: - -```dag -type RedfishVirtualMediaProbeReceipt { - locator: RedfishVirtualMediaLocator - progress: RedfishVirtualMediaProbe - admitted_protocols: List - raw_response: EvidenceRef -} -``` - -The solver should derive the locator and protocol from that receipt. It should accept only the artifact URI from staging, rather than accepting complete final params that can contradict the observation. - -### The MegaRAC constructor omits the parameter its own authority calls required - -`extdeps.bmc.megarac` says the start-media request **must** carry `image_redirection: 1`. The design document likewise says that OEM quirk belongs in the access profile. Yet `MegaRacRemoteMediaParams` has no such field, the solver never examines `profile.oem_parameters`, and the positive Mt. Collins control uses `oem_parameters: []` while still selecting `MegaRacRestRemoteMedia`. fileciteturn36file0L2-L2 fileciteturn37file0L2-L2 fileciteturn44file0L2-L2 fileciteturn34file0L2-L2 - -That is a direct false-green against the observed operation contract. Encode the requirement structurally, preferably as a typed field rather than a generic name/value pair: - -```dag -type MegaRacRemoteMediaBinding { - ... - image_redirection: MegaRacImageRedirectionEnabled -} -``` - -The positive test must fail when that binding is absent. - -### The access-profile shape cannot honestly represent the no-Redfish platform - -Every `BmcAccessProfile` is required to provide three nonempty Redfish locators, even for the Mt. Collins build whose retained report says Redfish is absent. The synthetic control therefore invents `/redfish/...` locators for an OEM-only profile. Meanwhile `BmcEndpoint.protocol` has only `Redfish | Ipmi`, and the no-Redfish MegaRAC OEM-REST fixture is labeled `protocol: Redfish`. fileciteturn30file0L2-L2 fileciteturn33file0L2-L2 fileciteturn34file0L2-L2 - -Replace mandatory Redfish fields with route inhabitants: - -```dag -type BmcAccessRoute - = RedfishRoute { ... } - | IpmiLanplusRoute { ... } - | MegaRacRestRoute { ... } - | OpenBmcShellRoute { ... } - | OpenBmcNbdWebsocketRoute { ... } -``` - -The BMC host identity should be separate from the routes it exposes; one controller may expose IPMI and OEM REST simultaneously. The boot-control target should also be protocol-neutral. Presently every delivery, including the IPMI-controlled no-Redfish MegaRAC path, projects to `RedfishBootSourceOverrideTarget`. fileciteturn31file0L2-L2 - -Also replace `chassis_part_number: None means match every chassis` with an explicit applicability: - -```dag -type ChassisApplicability - = ExactChassisPartNumber { part_number: NonEmptyStr } - | AllChassisVariants -``` - -Absence must not silently mean universality. - -## 3. There are still two delivery solvers - -The frozen projection is not the offending path. The offending path is the still-live original solver: - -```dag -fn solve_install_mechanism(...) -``` - -It selects virtual media from a capability row alone and defaults to PXE in its final `else`. `srv3_install_mechanism` still calls it, and `gunbc.os_install.fleet_install_server_specs` still consumes that result. The generated-artifact registry then consumes the install-server roster for `ProxyDhcpDnsmasqArtifact`. This remains an independent live modeled answer beside `solve_boot_artifact_delivery`. fileciteturn28file0L2-L2 fileciteturn29file0L2-L2 fileciteturn23file0L2-L2 fileciteturn25file0L2-L2 - -That chain must be deleted or migrated now: - -```text -solve_install_mechanism -srv3_install_mechanism -fleet_install_server_specs_for -fleet_install_server_specs -the stale ProxyDHCP artifact path, if its consumer census remains empty -``` - -Do not retain a second solver merely because the generated registry cannot express a refusal. Given that the proxy-DHCP artifact is currently `NotCommitted`, deleting the dead consumer is preferable to growing an additional standing solely to preserve it. fileciteturn25file0L2-L2 - -There is a second bypass inside the purported projection: `firmware_transition_precursor` returns `FirmwareUpdateThenVirtualMedia` when a transition is established and **some** catalog row supports VM. It ignores the exact `to` version and performs no post-transition profile lookup or live observation. A transition to an unrelated, non-VM release can therefore select the combined mechanism. fileciteturn29file0L2-L2 - -A firmware transition is not a delivery. The result should be: - -```text -FirmwareTransitionRequired / FirmwareTransitionPlanned -``` - -After executing it, the workflow must re-observe the endpoint and call the one delivery solver again. It may not preselect “then virtual media.” - -### Ruling on `AccessProfileNotLookedUp` - -**The refusing projection is honest and should not be deleted blindly.** `srv3_os_install_actuate_scope` and `gunbc.nbd_proxy_virtual_media_install` still consume its refusal-bearing solution and currently fail closed because of it. fileciteturn26file0L2-L2 fileciteturn27file0L2-L2 - -Keep that compatibility adapter until those consumers read `BootDeliverySolution` directly. Delete the capability-only total solver now. Also rename: - -```text -InstallVirtualMediaLiveSurfaceUnestablished -``` - -because `AccessProfileNotLookedUp` is not a live-surface observation failure. `InstallBootDeliveryEvidenceUnestablished` or a specific `InstallAccessProfileNotLookedUp` arm would preserve the actual cause. - -## 4. The receipt ledger is link-valid, but not yet lifecycle-valid - -The raw-list admission defect is fixed: admission now receives only `ValidatedIntakeReceiptLedger`, and the validator rejects empty, disconnected, differently linked, mixed-subject/attempt, and decreasing-start-time ledgers. The FNV value is also honestly named a structural fingerprint. Those are substantial corrections. fileciteturn38file0L2-L2 fileciteturn39file0L2-L2 fileciteturn46file0L2-L2 - -But the prior ruling also required valid phase ordering or explicit retry semantics. The validator does not read the declared phase order at all. It checks only subject, predecessor, and nondecreasing `started_at`; admission later searches the entire ledger independently for each required phase. fileciteturn38file0L2-L2 fileciteturn39file0L2-L2 fileciteturn40file0L2-L2 fileciteturn46file0L2-L2 - -A counterexample that currently admits: - -1. Put every required receipt in reverse phase order. -2. Give them increasing timestamps. -3. Recompute every predecessor fingerprint. -4. Validate the ledger. -5. Admission finds one success for every required phase and returns `FleetAdmissible`. - -A subtler counterexample is a complete successful lifecycle followed by a new successful `AccessDiscover`. Last-wins makes the new access receipt current, while all downstream qualifications still predate that new discovery. - -Add either a declared prerequisite relation or a monotone phase rank. For a serial first implementation, the simplest rule is: - -- phase rank may remain the same for an immediate retry; -- phase rank may advance; -- phase rank may never regress after a later phase has been recorded; -- retrying an earlier phase requires a new attempt or explicitly invalidates all descendants. - -The validator also needs: - -- `started_at <= ended_at`; -- a declared time-order rule across receipts; -- policy-digest compatibility across the attempt; -- producer-version compatibility or a typed producer-transition receipt; -- a RED for each violation. - -### The generic ledger still cannot validate the promised SHA chain - -The comment says the eventual durable producer will emit SHA-256, but validation always calculates the expected predecessor with `intake_receipt_structural_fingerprint`. Cross-family comparison refuses. Consequently, a genuine SHA-linked ledger cannot pass this validator. The receipt note also still names the nonexistent `intake_receipt_digest` and calls the ordering immutable. fileciteturn38file0L2-L2 - -Either: - -- explicitly name this `StructurallyValidatedIntakeFixtureLedger` and do not claim it is the durable admission ledger; or -- introduce `IntakeReceiptRecord { envelope, identity }`, have the producer establish the canonical SHA identity, and validate links against the supplied current-record identities. - -Finally, `FleetAdmissionReceipt` drops the ledger’s terminal identity and the exact receipt identity for each satisfied phase. It carries only subject, phase names, and derivation time. The derivation reads the producer ledger, but its output does not preserve which producer ledger it read. Add the terminal ledger identity, policy identity, and phase-to-receipt bindings. fileciteturn46file0L2-L2 - -# Layer-placement rulings - -## `BootDeliveryPreference` - -**Correctly belongs in `gunbc`, not `extdeps`.** It is product/workflow policy, not a vendor or protocol fact. Keeping it in `gunbc.boot_artifact_delivery` is acceptable while there is only one local policy. - -However, it should not be a field of a type named `BootDeliveryEvidence`. Separate fact from policy: - -```dag -fn solve_boot_artifact_delivery( - request: BootDeliveryRequest, - evidence: BoundBootDeliveryEvidence, - policy: BootDeliveryPolicy, -) -> BootDeliverySolution -``` - -Move it to `gunbc.boot_delivery_policy` only when a second workflow or policy row needs independent governance. - -## `BmcAccessProfileStanding` - -The current type fuses two layers. Split it: - -**Keep in `extdeps.bmc.access_profile`:** - -```text -BmcAccessProfile -BmcAccessProfileKey -BmcAccessProfileCatalogRow -BmcAccessProfileLookup = - Known | Uncatalogued | Ambiguous -``` - -**Move to a workflow module such as `gunbc.machine_intake_access`:** - -```text -ProfileLookupNotRun -AccessUnobserved -AccessObservationCaptured -ProfileBoundToObservation -ProfileObservationIdentityMismatch -``` - -`AccessProfileNotLookedUp` is workflow progress, not an external fact. `AccessProfileIdentityMismatch` is presently not produced by `bmc_access_profile_for` at all; the lookup produces only known, uncatalogued, or ambiguous, while a different function produces the profile-observation mismatch. That arm should either acquire a real producer or disappear from the lookup standing. fileciteturn30file0L2-L2 - -Also, `DeclarationRef` is not sufficient profile provenance. It identifies the catalog declaration—the renderer—not the observation or cited producer that made the profile admissible. Preserve both: - -```dag -type BmcAccessProfileProvenance - = ProfileFromExternalAuthority { authority: ExternalAuthority } - | ProfilePromotedFromObservation { - observation_receipt: ContentHash - promotion_receipt: ContentHash - } -``` - -# Namespace ruling - -The 31 rows use the correct **mechanical disposition** for the movement that actually occurred: the referenced declarations changed target, so `TargetChanged` is the right classification. fileciteturn43file0L2-L2 - -The target cut is not final, however. - -### `FirmwareTransition*`: move to its own module now - -This one is mandatory. `boot_artifact_delivery` itself says a firmware transition is **not** a delivery, and the new delivery solver does not consume `FirmwareTransitionStanding`; only the frozen legacy projection does. It belongs in something like: - -```text -gunbc.bmc_firmware_transition -``` - -That module should own the six requirements, the establishment producer, exact target-row binding, and execution receipt. fileciteturn31file0L2-L2 fileciteturn29file0L2-L2 - -### `NetworkBoot*`: separate module is the cleaner final cut - -I would also move the network-boot establishment vocabulary to: - -```text -gunbc.network_boot_delivery -``` - -Its four evidence axes have independent producers—client firmware mode, serving infrastructure, boot artifacts, and boot control—and form an independently useful standing consumed by the compositor. `gunbc.boot_artifact_delivery` should import that standing and select the `PxeChainBoot` constructor. - -This is less categorical than the firmware-transition move: keeping it beside its sole constructor would not violate ruling 2. But the separate module is the better final authority boundary and avoids turning the 605-line solver into the home of every candidate’s evidence lifecycle. - -**Do not preserve the current 31 rows and then move the subjects again.** Make the final module cut, rerun the required namespace instrument, and transcribe the resulting final-target admissions. - -# `bmc_onboarding` quarantine - -Deferring the split remains approved. The header quarantine says the correct things. fileciteturn42file0L2-L2 - -The new witness is not discriminating, though. It proves: - -```text -legacy lifecycle reaches FabricJoined -empty receipt list does not validate -``` - -Those are independent facts. A future function converting `FabricJoined` into an intake receipt could be added and the witness would remain green. fileciteturn41file0L2-L2 - -Replace or supplement it with a dependency/producer wall that derives and requires: - -```text -consumers of BmcOnboardingPhase in gunbc.machine_intake_* = [] -producers from BmcLifecycleState to intake receipt/standing/disposition = [] -``` - -That correction is small and does not require doing the full split now. - -The `MtCollinsBmcImplementationExpected` correction itself is accepted, but the adjacent strings still call the unpreserved `0.32` report an “observation,” say it was “grounded by execution,” and describe fallback to a platform default. The same module now admits that the raw producer was not preserved. Rename those to an explicitly unverified historical operator-report note and remove any claim that a workflow may use them as fallback authority. fileciteturn35file0L2-L2 - -# Next slice - -**INTAKE-AGENT-0 is next. Not MEMORY-0, and not the full `bmc_onboarding` split.** - -MEMORY-0 would otherwise model report ingestion, attribution, and verdicts before the producer capable of returning the report exists. The project’s own sequence places the minimal multi-architecture agent before MEMORY-0, and BOOT-DELIVERY-0 acceptance explicitly requires both physical transports to reach that common nonce-bound callback. fileciteturn45file0L2-L2 - -I would make the next serial gate: - -## INTAKE-AGENT-0A — artifact and callback contract - -Exit requires: - -1. Deterministic x86-64 and AArch64 artifacts with recorded content identities. -2. A callback carrying attempt nonce, exact artifact digest, architecture, raw board serial, assembly-observation digest, firmware-observation digest, and boot time. -3. Replay and duplicate-callback refusal. -4. Wrong-nonce, wrong-artifact, wrong-unit, and wrong-architecture RED controls. -5. Canonical durable receipt bytes and their SHA identity. -6. Raw EDAC/RAS, inventory, and sensor capture only—no memory pass/fail verdict yet. -7. Local execution of the callback contract without needing a physical BMC. - -Then run a narrow **ACCESS-OBSERVE-0 / BOOT-DELIVERY-0E** slice on hardware: - -```text -Mt. Collins observation → profile lookup → plan → execute → agent callback → cleanup -ASRock observation → profile lookup → plan → execute → same callback → cleanup -``` - -Only after those producers exist should MEMORY-0 consume the real agent evidence. The `bmc_onboarding` split remains a gate before the general effectful `intake` executor or before live `BmcSecure`, `OsInstalled`, or `ServiceRuntimeAdmitted` producers—not before building the diagnostic agent artifact. - -## Merge gate from this review - -Before approval of #9690: - -1. Bind target and exact `BootArtifact` into the solve and plan. -2. Replace the independently supplied capability/profile/observation tuple with one validated, attempt-bound access context carrying real producer provenance. -3. Bind Redfish locator/protocol and MegaRAC OEM requirements into the selected transport. -4. Remove the capability-only solver and the `FirmwareUpdateThenVirtualMedia` shortcut; keep only the fail-closed compatibility projection until its real consumers migrate. -5. Add phase/prerequisite, interval, policy, and producer compatibility to ledger validation; preserve ledger provenance in admission. -6. Split `FirmwareTransition*`, preferably `NetworkBoot*`, and regenerate namespace admissions. -7. Replace the onboarding tautology with a dependency/producer wall and quarantine the unpreserved Mt. Collins prose as historical report only. - -This review is against the requested exact head. Current `main` has since advanced to `1b59e7d3de3d11d1de7e1551cfbac092067660a7`, so after the architectural corrections the branch must be brought current and the namespace census and claims rerun on the final integrated head. fileciteturn2file0L12-L16 fileciteturn48file0L2-L2 diff --git a/docs/plans/machine-intake-ruling-4.md b/docs/plans/machine-intake-ruling-4.md deleted file mode 100644 index d8e7490ed08..00000000000 --- a/docs/plans/machine-intake-ruling-4.md +++ /dev/null @@ -1,265 +0,0 @@ -# Machine-intake ruling 4 (2026-08-30) — reviewer second pass on head cdbf4611bb - -Transcribed verbatim from the side chat (https://chatgpt.com/c/6a933560-5590-83ea-bc5c-731d17bfa2e3). The two short interim notes precede the ruling. - -> The structure is much stronger, but one splice remains: `request.target` is not proved equal to the bound access subject/endpoint. I’m checking transport-local evidence for analogous joins. -> The deleted solver chain and module cut look sound; keep `LedgerPhaseUnranked` and the fail-closed projection. #9717 instead exposes that this PR’s changed witnesses never ran. - -## Ruling - -**HOLD / request changes on exact head `cdbf4611bbc472275130722cd6dc3da3d12fea42`.** This is substantially closer than the prior head: the second solver is gone, the compatibility layer is honestly fail-closed, the ledger now enforces lifecycle ordering, and the authority split is mostly right. The remaining blockers are narrow joins and provenance gaps rather than another redesign. - -Also, all seven gate items are not yet present on this exact head. The onboarding dependency wall and Mt. Collins prose correction are still absent; the current witness remains the earlier “legacy terminal plus empty ledger” test, and the current Mt. Collins module still describes the unpreserved run as an executed observation and a fallback platform default. fileciteturn76file0L2-L2 fileciteturn77file0L2-L2 - -## What is accepted - -The deletion decision is accepted. `solve_install_mechanism`, `FirmwareUpdateThenVirtualMedia`, the generated proxy-DHCP path, and the capability-only default-to-PXE chain are gone. `gunbc.os_install_mechanism` now contains only projections of `BootDeliverySolution` and per-host fail-closed standings. **Keep that projection until its two real consumers migrate; do not delete it merely because it currently refuses.** It is no longer a second solver. fileciteturn65file0L2-L2 - -The module cut is accepted. `gunbc.network_boot_delivery` is the right home for network-boot establishment, and `gunbc.bmc_firmware_transition` correctly states that a transition is not a delivery and is not consumed by the delivery solver. Its unbuilt producer can remain a declared next rung because nothing uses an `Established` value to authorize delivery today. fileciteturn62file0L2-L2 fileciteturn73file0L2-L2 - -The ledger climb is accepted at its stated rung. It now checks one subject and attempt, one genesis, links, interval direction, nondecreasing start time, phase rank, policy identity, and producer identity. Admission preserves the terminal ledger fingerprint, policy digest, and exact receipt selected for every phase. The source is also explicit that this is structural-fingerprint validation, not the future durable SHA-256 ledger. fileciteturn68file0L2-L2 fileciteturn70file0L2-L2 - -**Keep `LedgerPhaseUnranked`.** Its current refusal is unreachable because every present `IntakePhase` is enrolled, but it is the fail-closed arm that becomes reachable when a future phase variant is added without being placed in the required ordering. That is a legitimate totality guard, not speculative product state. Add a positive witness that every current phase has exactly one rank; an authorable negative fixture is not required. fileciteturn69file0L2-L2 - -## 1. The request target is still not bound to the access context - -`BootDeliveryRequest.target` carries a subject and endpoint. `BoundBmcAccessContext` independently carries a subject and endpoint. The access binder correctly joins the observation receipt to the subject it was asked to bind, but the delivery solver does not compare the resulting context with `request.target`; it uses the context to select a transport and then copies the request target into the output plan. The existing witnesses always build both sides from the same helper, so they do not discriminate this splice. fileciteturn59file0L2-L2 fileciteturn58file4 fileciteturn58file5 fileciteturn64file0 - -As written, this can plan: - -```text -access observation and profile for subject/endpoint A -+ -boot-delivery request for subject/endpoint B -= -plan that names B but was admitted using A -``` - -Add a binding step before candidate evaluation: - -```dag -type BoundBootDeliveryTargetContext - = DeliveryTargetContextBound { - target: BootDeliveryTarget - access: BoundBmcAccessContext - } - | DeliveryTargetSubjectMismatch { ... } - | DeliveryTargetControllerMismatch { ... } -``` - -The solver should consume only the bound arm. Required REDs: - -1. same endpoint, different attempt or qualification subject; -2. same subject, different BMC controller. - -The same target binding is needed for the other evidence inputs. `ReinstallPathEstablished` is currently accepted while its `qualified_by` population is ignored; UEFI HTTP evidence carries an artifact digest but no target; network-boot establishment carries four evidence references and a digest but no unit, endpoint, or attempt. Evidence from one machine can therefore authorize another machine’s request even after the access-context join is repaired. fileciteturn58file0 fileciteturn62file0L2-L2 - -Each established transport standing should carry the `BootDeliveryTarget`, or consume a target-bound observation type whose sole producer performs that join. Artifact-digest agreement is necessary, but it does not bind the host. - -The plan’s positive provenance should also preserve the evidence particular to the selected candidate. The generic `profile_provenance + observation_receipt + considered` product is sufficient for the profile-shaped candidates, but it drops configfs `qualified_by` evidence and UEFI HTTP’s evidence list. A `CandidateEligible` verdict alone does not tell an executor or reviewer what made that candidate eligible. - -## 2. `"0.32"` is representable but still cannot inhabit a valid profile - -The opaque `BmcFirmwareReleaseIdentity` repair is right. `"0.32"` remains exactly `"0.32"` and has no fabricated semantic patch component. However, every `BmcAccessProfile` still embeds a semantic `BmcFirmwareCapabilityRow`, and catalog admission requires the raw release’s optional semantic view to equal that row’s semantic firmware. For `"0.32"`, the semantic view is absent, so the match always refuses. fileciteturn72file0L2-L2 fileciteturn60file0L2-L2 - -The new witness demonstrates exactly that: `"0.32"` is representable, but both profile lookup and access-context binding refuse it. That is a useful RED, but it means the observed Mt. Collins firmware can never progress to `AccessProfileKnown` under this model. fileciteturn64file1 fileciteturn64file2 - -Introduce an exact-release capability carrier: - -```dag -type BmcFirmwareReleaseCapabilityRow { - release: BmcFirmwareReleaseIdentity - capabilities: List -} -``` - -and make the new access-profile path consume it. The legacy semantic capability row can remain for existing OpenBMC consumers. - -The required positive control is: - -```text -raw release "0.32" -+ exact-release OEM-remote-media capability row -+ matching profile -+ matching synthetic observation receipt -→ bound access context and MegaRAC delivery plan -``` - -That is a fixture proving inhabitability, not a seeded fleet observation. - -## 3. Controller identity is still fused to one protocol - -The profile now correctly carries route inhabitants, including Redfish, IPMI lanplus, and MegaRAC REST. But `BmcEndpoint` itself remains `{ host, protocol: Redfish | Ipmi }`, and that route-specific value is used as the intake target and access-context endpoint. fileciteturn71file0L2-L2 - -One Mt. Collins boot needs at least two routes against one controller: - -```text -MegaRAC REST for artifact delivery -IPMI lanplus for boot control/reset -``` - -A single `protocol` field cannot identify that controller without selecting one route in advance. The current fixture demonstrates the problem indirectly: the supposedly no-Redfish OEM profile is observed through a `BmcEndpoint` constructed with `protocol: Redfish`. fileciteturn64file0L2-L2 - -Do not expand `BmcProtocol` with another arm. Introduce a protocol-neutral intake identity, for example: - -```dag -type BmcControllerEndpoint { - host: NonEmptyStr -} -``` - -Use it in `BootDeliveryTarget`, `BmcAccessObservation`, and `BoundBmcAccessContext`. The profile’s `BmcAccessRoute` population remains the authority for how that controller is reached. Existing legacy users of `BmcEndpoint` need not migrate in this PR. - -## 4. Promoted profiles must refuse while their provenance is unverified - -You are right not to fabricate a receipt store. The conclusion, however, is not that `ProfilePromotedFromObservation` may produce `AccessProfileKnown` while the gap is merely described. - -At present, two arbitrary hash-shaped values can be authored as `observation_receipt` and `promotion_receipt`, placed on a catalog row, and accepted by lookup and access binding. The positive fixtures do exactly this with synthetic content hashes. No producer establishes that either receipt exists or that the observation describes the profile being promoted. fileciteturn60file0L2-L2 fileciteturn64file0L2-L2 - -No store is required to fail closed now. Put verification in the workflow layer: - -```dag -type ProfilePromotionVerificationStanding - = ProfilePromotionVerified { ... } - | ProfilePromotionUnverified { - observation_receipt: ContentHash - promotion_receipt: ContentHash - } -``` - -`bind_bmc_access_context` may bind: - -- a profile grounded directly in an external authority; or -- a promoted profile accompanied by `ProfilePromotionVerified`. - -Without the future store, every promoted profile produces `ProfilePromotionUnverified` and refuses. That gives the missing discriminating control today without pretending that receipt lookup exists. - -INTAKE-AGENT-0A can later introduce the store-backed producer that constructs `ProfilePromotionVerified`. - -## 5. The Redfish probe still has two protocol authorities - -`RedfishVirtualMediaProbeReceipt` carries both: - -```text -progress = TransferProtocolAdmitted { protocol: ... } -admitted_protocols = [...] -``` - -The solver uses the progress arm only to decide whether the plan floor was reached, then decides protocol eligibility from `admitted_protocols`. Consequently, the receipt can claim `TransferProtocolAdmitted { NFS }`, list only HTTPS, and admit an HTTPS offer. fileciteturn63file0L2-L2 fileciteturn58file1 - -Use one representation: - -- make the progress arm payload-free and let a nonempty admitted-protocol population establish the plan floor; or -- put the admitted protocol population directly on the progress arm and remove the sibling field. - -The nested probe’s `raw_response` also needs to be part of the observation receipt’s verified evidence manifest. The access binder currently checks the overall observation `raw_response` against one `EvidenceRef`, but the transport plan derives its locator and protocols from the nested probe receipt’s separate hash. There is no structural statement that this second blob was among the captured evidence. A receipt manifest containing all referenced evidence digests is the clean correction. - -## Ledger and durable-receipt ruling - -`IntakeReceiptRecord` may remain deferred to **INTAKE-AGENT-0A**. The current code is honest that `ValidatedIntakeReceiptLedger` is an in-substrate structural ledger and cannot validate the future SHA-linked wire ledger. That is sufficient for the INTAKE-0 semantic model, but it is not sufficient for a live admission producer. fileciteturn68file0L2-L2 - -The PR body is now stale: it still claims a “canonical digest and hash-chain check” and still describes the superseded live-surface refusal rather than `InstallAccessProfileNotLookedUp`. Update it only after the final code head is fixed. fileciteturn79file0L8-L8 - -The gate before any effectful intake/admission executable is: - -```text -IntakeReceiptRecord { - envelope - canonical_bytes_identity: Sha256Digest -} -``` - -with links checked against those supplied record identities, not recomputed FNV fingerprints. - -## Onboarding and Mt. Collins worker - -The proposed worker direction remains correct, but it must land before this review can close. - -The dependency wall must inspect the live producer/consumer relation—no path from `BmcOnboardingPhase` or `BmcLifecycleState` into intake standings, receipts, disposition, or admission. The exact-head witness currently proves only that the legacy state reaches `FabricJoined` and that an unrelated empty receipt list does not validate; adding a conversion function would not make that witness red. fileciteturn76file0L2-L2 - -For Mt. Collins, retain `MtCollinsBmcImplementationExpected`, but rewrite or delete the adjacent rows that currently say: - -- the stack was “established by execution”; -- `0.32` and no-Redfish are an “observation”; and -- workflows may fall back to this platform default. - -The raw producer was not preserved, so these may survive only as an explicitly unverified historical operator report with no authority path into lookup or planning. fileciteturn77file0L2-L2 - -## Namespace ruling - -The final module cut is approved, but do not transcribe admissions from the present intermediate head. The exact head still carries the earlier 31-entry roster describing the old move directly into `boot_artifact_delivery`. Those are not the final targets after the split into `network_boot_delivery` and `bmc_firmware_transition`. fileciteturn74file0 - -The sequence is: - -1. land the target/context, raw-release, endpoint, provenance, probe, and onboarding corrections on the branch; -2. merge current `main`; -3. run the namespace instrument against that exact integrated head; -4. transcribe only the resulting final `TargetChanged` identities; -5. rerun once to prove no unadjudicated or stale admissions. - -`main` has already advanced to `61c6dfde780430ea6fd20ac3985a72f5cdbd9384`, well beyond the PR’s recorded base, so the final census cannot be taken from `cdbf4611bbc472275130722cd6dc3da3d12fea42`. fileciteturn80file0L2-L2 - -## #9717 is not an unrelated CI incident - -The red is truthful. #9717 was specifically built because prior PRs added witnesses that passed remotely but executed **zero** times in the required floor. It now blocks every changed witness whose required-floor disposition is declined, missing, or nonterminal. fileciteturn81file0L8-L8 - -The 2026-08-29 compiler-floor ruling and #9717 can coexist: - -```text -static global required-gate roster = compiler floor only -dynamic per-PR changed-witness lane = exactly the changed witness identities -``` - -The honest repair is a changed-witness execution sublane: - -1. reuse #9717’s already-derived changed identity set; -2. execute exactly that set, without adding product prefixes to the global compiler gate; -3. write terminal outcomes into the same disposition ledger; -4. let #9717 project those outcomes. - -Do **not**: - -- weaken #9717; -- classify `DeclinedOutsideGateClosure` as green; -- add a blanket exception for #9690; -- expand the standing global compiler gate with every product namespace; -- convert the 53 identities to expected-red or transition admissions. - -The reported 74 remote greens are valuable development evidence, but they are not yet an exact-head required-CI receipt. They could substitute only if imported as subject-bound, identity-complete execution receipts that the required gate itself consumes. - -At the time I checked, the exact-head GitHub run was still nonterminal: Rust unit tests had succeeded, while the required build and floor jobs were still running. Thus there is not yet a final CI verdict on `cdbf4611bbc472275130722cd6dc3da3d12fea42` to classify. fileciteturn82file0L2-L2 - -The changed-witness execution repair can land as its own CI PR ahead of #9690. Until it does, #9690 remains correctly blocked rather than “green except for infrastructure.” - -## Next slice - -After these corrections and #9690’s merge, **INTAKE-AGENT-0A remains next**. - -That slice should produce the missing common substrate: - -1. deterministic x86-64 and AArch64 artifacts; -2. canonical SHA-256 `IntakeReceiptRecord` identities; -3. an evidence manifest/store capable of resolving observation and promotion receipts; -4. nonce-bound callback with artifact, architecture, unit, assembly, firmware, and attempt identity; -5. replay and duplicate-callback refusal; -6. raw inventory, EDAC/RAS, sensor, and access-observation captures without memory verdicts; -7. local callback-contract execution before hardware use. - -That slice supplies the producer needed to verify `ProfilePromotedFromObservation` rather than forcing #9690 to invent a generic receipt store. Then execute the Mt. Collins and ASRock access/boot callbacks. **MEMORY-0 follows those real agent receipts.** The full `bmc_onboarding` split remains required before the general effectful intake executor or live `BmcSecure`, `OsInstalled`, or `ServiceRuntimeAdmitted` producers, not before the agent artifact. - -## Final approval gate - -Approval requires one final head containing: - -1. request target ↔ access-context subject/controller binding; -2. target-bound configfs, UEFI HTTP, and network-boot standings; -3. a positive exact-raw `"0.32"` capability/profile path; -4. protocol-neutral BMC controller identity; -5. one Redfish protocol authority plus bound probe evidence; -6. fail-closed unverified promoted-profile provenance; -7. the real onboarding dependency wall and historical-report rewrite; -8. namespace admissions generated from the final current-main composition; -9. updated PR body; -10. terminal required CI in which every changed witness has an executed disposition. - -The architecture no longer needs another broad rewrite. These are the remaining seams where independently valid values can still be paired to manufacture a plan about the wrong subject, controller, release, route, or producer. diff --git a/docs/plans/managed-host-untangle.md b/docs/plans/managed-host-untangle.md deleted file mode 100644 index 5a0506e0b3a..00000000000 --- a/docs/plans/managed-host-untangle.md +++ /dev/null @@ -1,744 +0,0 @@ -# Managed-host untangle: mtcollins1 → unit / platform / silicon / BMC stack / fleet procedure - -*Work item adhoc-81aa03f8-bc8 (session warm-crane-577), measured at `26e99a9c7f`. Governing doctrine: DESIGN §3 (single authority; replacement migrations cut over at the root, consumers enumerated before deleting a root that is outside the required gate), §3b, §3c, and [the replacement migration doctrine](replacement-migration-doctrine.md).* - -## Why - -A second Ampere Altra platform, Mt. Jade 2U (unit `mtjade1`), is arriving. Most of what is named for `mtcollins1` is not specific to Mt. Collins: it is Ampere silicon knowledge, AMI MegaRAC knowledge, or fleet procedure for any BMC-managed host, with the unit hardcoded through a handful of constants. As long as the procedure is `mtcollins1_*`, Mt. Jade has two options: fork the procedure (a §3 nickname) or wait. The fix is the root seam below, plus one cut per authority. - -## How the census was taken (the instrument) - -This is an import census over every `.dag` module under `dag/` and `src/v2/`, not a grep for consumers: - -- **Population**: every module whose name or text matches `mtcollins`. That is 269 modules: 81 witnesses and 188 production/reference modules. -- **Consumers**: the reverse `import` edges into each module. -- **Gate**: transitive import closure from `v2.workflow.required_floor` `required_gate_prefixes`. -- **Constants**: the `mtcollins|mt_collins|MtCollins` symbols each module imports from another module. These are the unit/platform constants it depends on. - -The method is under **Census method** below. It is a one-off that each cut re-runs at its own head. - -### Findings that shape the plan - -1. **No layer-1/2/3/5 machine-intake module is in the required gate.** The 12 gate-reachable members are generic modules carrying prose or a single row: - - `extdeps.bmc.megarac` - - `extdeps.boards.types` - - `gunbc.fleet_intent_network` - - `gunbc.secret_provision` - - `gunbc.ci_layer_roots` - - `gunbc.durable_cas_file_store` - - `gunbc.floor_demand` - - `gunbc.roadmap_authority` - - `gunbc.runner_registration_labels` - - `gunbc.runner_throughput_qualification` - - `v2.workflow.floor_route_gap` - - `test.claim.action_use_admission_witness` - - **So every cut below deletes a root outside the gate.** Per §3, each child brief carries its consumer list by name, from the tables below, and must run each named consumer's witnesses itself. A green required run proves nothing about these modules (`docs/rung-drops/required_lanes_do_not_resolve_product_layer_modules_2026-09-20.txt`). - -2. **The unit enters through about ten roots.** These are the most-imported unit symbols: - - | Symbol | Home | Importers | - |---|---|---| - | `mtcollins1_endpoint` | `gunbc.machine_intake_mtcollins1_access_observation` | 10 | - | `operator_host_mtcollins1` | `gunbc.fleet_intent_network` | 7 | - | `mtcollins1_firmware`, `mtcollins1_capability_row`, `mtcollins1_access_observation` | access observation | 7 / 5 / 4 | - | `mtcollins1_bmc_gunbc_secret_ref` | `gunbc.secret_provision` | 6 | - | `mtcollins1_secured_account` | `gunbc.machine_intake_mtcollins1_bmc_secure_observation` | 6 | - | `mtcollins1_unit_hold_key`, `mtcollins1_unit_hold_store_host` | `gunbc.machine_intake_mtcollins1_maintenance_hold` | 6 / 4 | - | `mtcollins1_cdrom_selection` | `gunbc.machine_intake_mtcollins1_actuate` | 5 | - | `mtcollins1_host_nic_mac` | `gunbc.machine_intake_mtcollins1_netboot_client_observation` | 7 | - | `mtcollins1_boot_export_dir` | `gunbc.machine_intake_mtcollins1_boot_artifact` | 6 | - | `mt_collins_channel_population_roles`, `mt_collins_channel_connector_pairs`, … | `extdeps.ampere.mt_collins_*` (the board figure) | 10 / 5 | - - **Fleet procedure is unit-specific only through these roots.** Thread a host record through them and the procedure modules become host-generic without restating their logic. - -3. **`gunbc.host_reset_subject_roster` `ResetSubjectCandidate` is already the per-host record in embryo.** It carries: - - `host` (a `HostIdentity`) - - `observation` (a `BmcAccessObservation`, which carries the endpoint) - - `capability_row` - - `username` - - `boot_selection` - - `unit_hold` - - Its note says a second host joins "the day it authors an observation". But it is named and scoped for *reset*, and the boot, hold and census procedures each re-import the same unit constants independently. Those are four consumers holding four copies of one row. - -4. **`gunbc.machine_intake_subject` is not the home.** `MachineIntakeSubject` / `QualificationSubject` is the *identity of a unit under qualification*: unit key, assembly manifest, firmware manifest and attempt. It has no BMC endpoint, credential or stack. It stays what it is, and the new record references it rather than absorbing it. - -5. **`mtjade1` has no `HostIdentity` row.** `gunbc.fleet_intent_network` names `operator_host_mtcollins1` but no `mtjade1`. What exists for the unit: - - `gunbc.machine_intake_mtjade1_access_observation` carries `FamilyUnobserved`, a refused unit key, and `SecretLocusReservedNotOnAccessorRoster`. - - `gunbc.machine_intake_bmc_firmware_family_discriminator` and `bmc_first_contact_standing` already model "BMC family not yet observed". - -6. **The platform home exists.** `extdeps.boards.types` `BaseboardModel` / `ServerBaseboard` is in the gate, has 25 importers, and is where the board figure is keyed. - -7. **`.github/workflows/fleet-converge.yml` is a projection.** Its authority is `gunbc.fleet_converge_workflow` (271 references, 4,446 lines). It carries 8 `mtcollins1_*` mode literals in conditions and about 30 `mtcollins1_*` step/job identifiers. Renaming any mode needs operator sign-off. - -## The root seam (revised after the #13046 review) - -**Name.** The name was chosen by DFS of existing vocabulary. The corpus already says *managed* for "a host this fleet drives through its baseboard controller" (`gunbc.host_reset_subject_roster` contrasts it with "executor fleet host"). No `ManagedHost` type exists, and none of the existing `Managed*` types is the same concept. - -**The record is a projection over standings that already exist, not a new bundle.** Two joined authorities already cover most of it: - -- `gunbc.machine_intake_access` `BoundBmcAccessContextStanding` binds the `MachineIntakeSubject`, endpoint, observed identity (which includes baseboard and BMC family), capability row, profile, observation and evidence manifest. -- `gunbc.machine_intake_bmc_secure` `BmcSecureStanding` / `ManagedCredentialReference` carry the secured account, role, `SecretRef` and credential epoch. - -Restating any of that as loose `baseboard` / `bmc_stack` / `credential` fields would be a second authority and would lose the capability row that `reset_subject_admission` needs. So: - -``` -module gunbc.managed_host -type ManagedHost { - host: HostIdentity // gunbc.fleet_intent_network, the sole naming authority - access: BmcAccessObservationStanding // endpoint and observed identity (baseboard, BMC family) - capability_row: BmcFirmwareReleaseCapabilityRow // the pair oob_boot_handoff_admission consumes today - secure: BmcSecureStanding // managed credential: account, role, SecretRef, epoch - unit_hold: ResetUnitHold // moved here from host_reset_subject_roster -} -fn managed_hosts() -> List -fn managed_host_binding(h: ManagedHost) -> ManagedHostBindingStanding // THE join; effectful consumers read only this -``` - -**The seam is a join, not a bundle** (side-chat review, 2026-10-03). There are two invariants. - -1. **One joined standing.** The subject is derived from `secure.subject` and never stored again. `managed_host_binding` refuses, with a typed and located cause, unless all three hold: - - `access` is observed; - - the observed endpoint equals the `BmcSecured` endpoint; - - the secured subject's unit is bound to the row's `host`. - - Every effectful consumer (reset, boot, hold) reads the credential and the endpoint only through the bound result. Without the join, unit A's secured account could be used against unit B's controller. The discriminating RED uses a secure standing for endpoint A and an access observation for endpoint B. -2. **Operation admission includes the operation's policy.** A host is a member of an operation's roster only if that operation's policy is decided for it. For reset, one reset-specific authority takes the bound `ManagedHost` plus the reset boot selection and produces an admitted reset subject. The roster lists exactly those hosts, and `gunbc.host_reset_return_run` consumes the selection from that admitted subject with no second lookup. The RED is a capable host with no reset selection, which must be absent from the roster. Every later cut that adds an operation (boot, hold) follows the same shape. - -Rules for the record: - -- **Baseboard and BMC family are read through `access` (its observed identity), never stored beside it.** A procedure that needs MegaRAC matches the bound identity and refuses otherwise. -- **The silicon field arrives in cut 1** as a standing authored there, not as a placeholder in cut 0. -- **Cut 0 has exactly one row, `mtcollins1`.** Its `subject` and `secure` come from `gunbc.machine_intake_mtcollins1_bmc_secure_observation` (`mtcollins1_intake_subject`, `mtcollins1_bmc_secure_standing`). Its `access` and `capability_row` are the unit's existing `mtcollins1_access_standing` and `mtcollins1_capability_row`. -- **Why `access` is the observation standing plus the capability row, not `BoundBmcAccessContextStanding`** (measured by cut 0, 2026-10-03): - - `gunbc.machine_intake_access` `bind_bmc_access_context` has no production caller. - - There are no production `AccessObservationReceipt` values. - - No `BmcAccessProfileCatalogRow` exists outside type declarations. - - Binding mtcollins1 would require authoring a catalog row whose provenance cannot be honestly decided today. `ProfileFromExternalAuthority` has no source, and `ProfilePromotedFromObservation` binds only beside a `ProfilePromotionVerified` that no producer makes. - - So the row carries the pair that the consumed admission (`oob_boot_handoff_admission`) actually reads today. This is a **declared frontier, not a second authority**. Its trigger: the first production producer of a `BoundBmcAccessContextStanding` for a managed host, which needs a catalog row with a decided provenance. In that change, `access` and `capability_row` collapse into the bound context and these two fields are deleted. - -- **`mtjade1` gets no row yet.** It has no `MachineIntakeSubject` (its unit key is refused), no endpoint, no family and no hold decision. Fabricating any of them to populate a row is the §5 defect the review named. -- **mtjade1's absence is the honest standing.** Every procedure refuses a host that is not in `managed_hosts()`. -- **mtjade1 joins on a trigger, not by edit.** It becomes a row the day first contact authors its subject and access context standing (`gunbc.machine_intake_jade_first_contact_frontier`) and a hold decision is made. That is the trigger, and it is not a cut of this program. -- **`ResetSubjectCandidate` dissolves.** `gunbc.host_reset_subject_roster` becomes a filter over `managed_hosts()` with the same admission fold, and `boot_selection` returns to the reset arm as reset policy. - -**Operation-keyed policy is not host identity** (ruling (b), 2026-10-03). The boot federation is policy for the pair (managed host, operation): - -``` -ManagedHostBootFederationStanding {host, operation, pool, provider, service_account, environment, claim_pins, secret_grants} -``` - -- `mtcollins1` is bound to the existing live names (`github-mtcollins1-boot` pool and provider, the `mtcollins1-boot` service account and GitHub environment, and the grants), which are not renamed. -- Any other host is explicitly unprovisioned, and the generic job refuses it. -- None of these ids appear on `ManagedHost`. - -## Rulings recorded (operator, via eager-gull-22 side-chat review, 2026-10-03) - -- **(a)** The fleet-converge mode recut is approved in principle as the **last** cut: one atomic transition, no aliases, the eight old literals deleted in the same change. - - Do **not** overload the existing `host` input, which is the executor host. Add a distinct managed-subject input and keep the executor axis. - - Names are derived after cuts 2–6 expose the axes. Census-QEMU modes are executor-toolchain operations, census image publish/readback are artifact operations, and boot/fan/UI/KVM are managed-host operations. - - The final names still need operator sign-off. -- **(b)** Keep the live GCP IAM names. Model them as the operation-keyed standing above. - -## Ordered cut list (re-cut vertically) - -**The governing rule** (review item 3): *a module may not acquire a generic name while its answer is still transitively fixed to mtcollins1.* Each cut therefore moves a module only after everything it transitively imports from the `mtcollins1` population is either already generic or is a layer-1 unit observation that the generic module now receives as a **parameter** or reads off the `ManagedHost` row. - -The boot vertical is large. Its transitive closure from `gunbc.machine_intake_mtcollins1_boot_run` reaches 50 census modules, including: -- the layer-1 `mtcollins1_memory_census_observation`; -- the layer-3 `mtcollins1_smpro_observation`; -- the layer-4 `mtcollins1_kvm_still`, `mtcollins1_bmc_sensor_observation` and `mtcollins1_bmc_secure_observation`. - -So the BMC and silicon leaves it needs move first. - -| # | Cut (one authority) | Root deleted | Sequencing | -|---|---|---|---| -| **0** | **Seam**: `gunbc.managed_host` as above, `mtcollins1` row only; dissolve `ResetSubjectCandidate`. | `reset_subject_candidates`, and the roster's direct `mtcollins1_*` imports | Now. Disjoint from #13025 and #13041. | -| **1** | **Silicon**: Ampere-generic SMpro/PMpro, boot-stage, `CP:` and CCIX decoding moves out of `mtcollins1_smpro_observation` into `extdeps.ampere.*`, next to the existing `ampere_{dram,socket}_console_observation`. Adds the silicon standing to `ManagedHost`. | the generic decoders inside the unit module | After #13025, and after cool-ant-760's error-record decoder lands. Public-tree rule: no SCP UM, no CHANGELOG.txt, nothing disassembled. | -| **2** | **Maintenance hold**: host-generic over `ManagedHost.unit_hold`. | `mtcollins1_unit_hold_*`, `mtcollins1_maintenance_hold_{take,release}` | After 0. The env var `GUNBC_MTCOLLINS1_MAINTENANCE_REASON` is a workflow surface: keep it until cut 7. | -| **3** | **Boot-critical BMC leaves**: `mtcollins1_kvm_still`, `mtcollins1_media_attach` (the MegaRAC virtual-media half), `mtcollins1_bmc_sensor_observation`, and the secured-account projection of `mtcollins1_bmc_secure_observation`. These become MegaRAC-scoped modules that take a `ManagedHost` and refuse a non-MegaRAC bound identity. The unit's own readings stay unit-named. | those modules' `mtcollins1_` procedure roots | After 0. | -| **4a** | **Boot subject leaves**: artifact, image fetch, milestone, phase timing, admission, authorization, actuate. `MtCollins1BootSubject` becomes a boot subject over `ManagedHost`. | `MtCollins1BootSubject`, `mtcollins1_boot_subject*`, `mtcollins1_cdrom_selection`, `mtcollins1_boot_export_dir` as procedure inputs | After cuts 1 and 2. **Not after cut 3**: measured on main 2026-10-04, the 4a modules import none of the cut-3 modules. The census-image import in the boot authorization becomes the boot **medium parameter**, so 4a does not wait on 4b either. The runner-image medium arm and its terminal milestone (quiet-stag-623's lane) land on top of 4a. | -| **4b** | **Census boot image chain**: the 7 `mtcollins1_census_*` modules. Toolchain and QEMU modules are executor-host operations, so they are keyed by executor host, not managed host. | the `mtcollins1_census_*` procedure roots | After 4a. | -| **4c** | **Boot federation standing**: `ManagedHostBootFederationStanding` replaces `gunbc.auth.mtcollins1_boot_federation*`, live names kept as the `mtcollins1` binding (ruling b). | `mtcollins1_boot_*` federation decls | After 0. Independent of 4a/4b. | -| **4d** | **Boot run, dry realization, diagnostic bundle**: the vertical's root. Unit observations it reads today (`mtcollins1_memory_census_observation`, `mtcollins1_access_observation`) become row reads or parameters, so the dependency direction is procedure ← unit. | `mtcollins1_boot_run`, `_boot_dry_realization`, `_boot_diagnostic_bundle` | After 1, 4a, 4b, 4c. Likely split run/dry and bundle at dispatch. | -| **O1** | **Arrival convergence, factory state through an admitted managed host** (operator requirement and rulings, 2026-10-03). PRs: O1a route standing, O1b state-shaped `BmcSecure` and observer clock, then O1c in three parts (the shared step-sequence fold with its first consumer; the prior-life archive; `BmcSecure` wiring and the admission population). See its own section below. | the family-keyed rotation route; the rotation-event-only `BmcSecure` derivation; the supplied `BmcSecureStanding` in the firmware convergence; the source-roster `managed_hosts()` | After 4c. Does not need the generic boot run. Each unit's live credential write needs its own operator go-ahead; mtcollins1 goes first. | -| **O2** | **Boot enters through the arrival convergence** (replacement migration): the `mtcollins1_boot` mode is re-rooted onto the convergence in one transition, and the old boot entry is deleted. | the boot run's own entry (the root that assumes `BmcSecured`); the caller-supplied password path into the boot wrappers | **After 4d.** The convergence is host-generic, so it may not call a boot run whose answer is still fixed to mtcollins1. | -| **5** | **Remaining BMC-stack observations**: fan observe, UI bundle observe, KVM observer, SOL notice, served-UI catalog, BMC fan, which the boot does not need. | those `mtcollins1_` procedure roots | After 3. | -| **6** | **Platform**: physical orientation, DIMM connector and platform observation become logic over the baseboard read through `ManagedHost.access`. The Mt. Collins figure stays in `extdeps.ampere.mt_collins_*`. | the board bindings in those modules | After #13025 (it edits `mtcollins1_physical_orientation`). | -| **7** | **Workflow modes**, per ruling (a). | the 8 `mtcollins1_*` literals and their steps in `gunbc.fleet_converge_workflow` and its projection; `mtcollins-canary.yml` dispositioned in the same change | Last. Operator sign-off on names first. | - -### Cut O — arrival convergence (plan delta, 2026-10-03; revised for the side-chat review of `b42944791e`) - -**Requirement** (operator, via eager-gull-22). Boot is to be entered from onboarding, starting at **factory state**. Today's boot run assumes `BmcSecured` already holds. First subject by requirement: `mtjade1` (MegaRAC at 192.168.1.246, factory `admin`/`admin`, BMC clock reading the year 2000). First **wet** subject by ruling: `mtcollins1`. - -**Framing (operator ruling, 2026-10-03): a consolidation, not a new mode beside the boot.** It is a replacement migration under DESIGN §3. -- The arrival convergence becomes the one entry for boot, and no second boot route survives beside it. -- No new mode, no new job, no renamed mode. The literal `mtcollins1_boot` and its job stay byte-identical until cut 7. -- Boot is the gap-intolerant boundary, so the staged form applies: the convergence is built and witnessed first (O1), then **one transition** re-roots the mode and deletes the old entry together (O2). - -**Module name.** `gunbc.machine_intake_arrival_converge` (approved). It avoids "onboarding" because `gunbc.bmc_onboarding` is quarantined legacy (ruling 2026-08-29). That module is not imported, and the quarantine witness keeps holding. - -#### Scope: a prefix of the existing `ArrivalPhase` order, exactly - -`gunbc.machine_intake_phase` `arrival_phases_all` orders twelve phases. Cut O is the **prefix through `BootDeliveryEstablish`**: five phases, in the authority's own order, with none omitted and none added. The later phases (`DiagnosticBootAttest` through `ArrivalCleanup`) are not in this cut and keep their present standing. The fold is over `arrival_phases_all`, cut at `BootDeliveryEstablish` by `intake_phase_rank`. It is not a second, shorter list. - -Each phase yields one result and one receipt. Supporting operations are **nested under the phase that consumes them**; they are not phases. - -| Phase | Goal, read back independently | Effects, and whether each is a write | Nested support | -|---|---|---|---| -| `AccessDiscover` | access observation, firmware family, **and the sealed route standing** (below) | read-only probes | the controller's own clock reading, recorded as an observation and never used for ordering | -| `IdentityBindProvisional` | the exact `MachineIntakeSubject` (below) and its `HostIdentity` binding | read-only: FRU, firmware versions | subject construction; host allocation lookup | -| `PriorLifeBoundary` | `LogsArchivedWithBaselineCursor`: the prior-life carriers are archived as content-addressed evidence and a cursor is derived from that archive (below) | **read-only arm**: archive, derive the cursor, clear nothing. **`LogsArchivedAndCleared`**: a write with its own admission and operator sign-off, **not in this cut**. | per-carrier completeness standings | -| `BmcSecure` | the existing conjunction: the managed credential is accepted **and** the published credential is refused on every LAN member of the channel census | **write**: the typed account action, operator-gated per unit | secret generation, store and exact-version fetch **before** the write (below) | -| `BootDeliveryEstablish` | **one eligible delivery transport selected from live observations**: a `gunbc.boot_artifact_delivery` `BootDeliveryPlan`, bound to subject, controller and artifact. **This is not the boot run.** | read-only observations; the selection | managed-host admission (below) | - -`PriorLifeBoundary` has no implementation today: the phase is declared and nothing produces it. A factory or repaired unit cannot skip it, so O1c builds its read-only arm. That is new work this plan had omitted. - -**What the `PriorLifeBoundary` read-only arm is** (`docs/plans/machine-intake-design.md` §5: `PriorLifeBoundaryEstablished = LogsArchivedAndCleared | LogsArchivedWithBaselineCursor`). It is an **archive**, not a bare cursor. -- **Carriers archived**, each as content-addressed evidence with its own completeness standing: the BMC clock, the SEL and event logs, the Redfish log collections, audit and account events, the current boot override, virtual-media state, power state, and a sensor snapshot. -- **Cursor:** derived **from that archive**. -- **Nothing is cleared.** -- **Incomplete archive:** a required carrier that cannot be archived makes the phase refuse, or records a typed gap. It is never silently omitted. -- **Policy admission:** the design admits this arm "only where platform policy explicitly admits an uncleared append-only boundary". That admission is an authored policy row per platform. It is a decision this cut must state for Mt. Collins and Mt. Jade, not assume. -- **Control:** only the SEL final record id, with the other carriers absent → not established. - -**What `BootDeliveryEstablish` is, and is not.** It is phase 5: a delivery plan or standing. The boot run is not part of it, and establishing it completes no later phase. -- **O2:** consumes the established delivery to enter the existing boot run. -- **Controls:** - - delivery selected → `BootDeliveryEstablish` established **and `DiagnosticBootAttest` unestablished**; - - no `BootDeliveryEstablish` → the boot entry cannot start. - -**O1 and O2.** O1 runs the prefix through `BmcSecure` and ends at an admitted managed host. O2 adds `BootDeliveryEstablish` (the delivery standing) and re-roots the boot mode so that the existing boot run is entered only from an established delivery. It lands only after cut 4d has made the boot run host-generic. The boot run itself, and every phase after phase 5, stays what it is. mtcollins1's wet `BmcSecure` control needs no boot, so it runs at O1. - -#### The subject (`IdentityBindProvisional`) - -A FRU serial yields only a `UnitKeyStanding`. A `MachineIntakeSubject` is a `QualificationSubject` (unit key, assembly-manifest digest, firmware-manifest digest) plus an `IntakeAttemptId`. Each part has a named producer: - -| Part | Producer | Evidence | -|---|---|---| -| unit key | `gunbc.machine_intake_subject` `bind_unit_key` over `BoardSerialObservation`s | committed FRU capture | -| provisional assembly manifest | `AssemblyManifest` of the `ComponentIdentity` rows readable before boot (board, BMC, and what the FRU and the BMC inventory expose), digested by `assembly_manifest_digest`. Provisional is the phase's own word: `InventoryConform` later re-derives it from the booted host. | the same captures | -| live firmware manifest | `FirmwareManifest` of the versions read back from the controller now, digested by `firmware_manifest_digest` | committed version readbacks | -| attempt identity | one `IntakeAttemptId` minted per convergence run | the run's receipt | -| subject | `qualification_subject_of`, then `MachineIntakeSubject` | the above | - -**That exact subject flows through `BmcSecure`, managed-host admission and boot.** A firmware reflash changes the firmware-manifest digest, so `compare_qualification_subject` reports `FirmwareManifestStale`. A `BmcSecureStanding` bound to the old subject is then not current and is never reused: re-entry after a reflash re-derives the subject and re-runs the phases against it. - -**Host identity.** A `UnitKey` is not a fleet-operation identity. The `HostIdentity` comes from `gunbc.fleet_intent_network` (naming) and `gunbc.hostname_allocation` `gunbc_fleet_hostname_allocations` (the allocation row). The binding of a `HostIdentity` to a subject is recorded in the admission record below. mtjade1 has neither a name row nor an allocation today, and both are authored decisions, not derived. - -#### The route standing (`AccessDiscover`), corrected first (O1a) - -`gunbc.bmc_implementation_dispatch` `rotation_route_for_family(AmiMegaRac)` answers `RotationRouteUnavailable` for a whole family. That is wrong in the direction the evidence shows, and the replacement must not be another family-keyed answer. The evidence is exactly this, and no wider: - -- **mtcollins1**: an IPMI user-management rotation was **executed** on that controller and build (`artifacts/bmc/mtcollins1-bmc-user3-route-2026-09-13.txt`). -- **mtjade1**: a Redfish Manager **read** is committed (#13065). It proves a Redfish surface. It does not prove a Redfish AccountService write, nor an IPMI user-management request, on that controller. - -So the route is a **sealed standing bound to an endpoint, a firmware build and evidence**, derived **inside** the convergence after `AccessDiscover`. It is not passed in, which withdraws this plan's earlier "selected before and passed in" sentence: that would have been a second observation authority. The arms: -- **grounded by an executed request on this controller and build** (carries the evidence); -- **grounded by a cited source for this build** (carries the citation); -- **ungrounded.** - -An ordinary `BmcSecure` Apply consumes **only a grounded standing**. mtcollins1's IPMI route does **not** authorize mtjade1, whose route is ungrounded today. - -**Grounding a route is its own effect, not the Apply.** The Apply cannot produce the premise that admits it, so there is a bootstrap with its own authorization: - -`RouteUngrounded` → `RouteQualificationCandidate { endpoint, firmware_build, request shape, evidence }` → `RouteGroundedByExecutedRequest { endpoint, firmware_build, request receipt, response receipt }` - -- The transition is made by a **separately authorized route-qualification effect**. It is classified as its own privileged effect through `select_authorization_pattern`, and it has its own operator go-ahead per controller. -- The candidate names the exact request shape and the evidence that makes it worth trying (an observed surface, a public standard's operation). It authorizes nothing by itself. -- Only the grounded arm enters ordinary `BmcSecure` Apply. -- **RED:** `RouteUngrounded` + operator approval of the rotation + ordinary Apply → refuse. - -This is the MegaRAC rotation operation that the runner-bringup gap analysis lists first. The route standing is keyed by endpoint and build and carries request and response receipts, so the same model serves that backlog's other consumers without a second route vocabulary. - -Controls: -- two `AmiMegaRac` controllers with different observed and executed surfaces produce different route standings; -- a Manager GET alone cannot construct an account-write route. - -`rotation_route_consumption_frontier` stands until the held O1b transition: O1b supplies the consumer's code (`gunbc.machine_intake_bmc_secure` `plan_bmc_account_action` computes each write's admission with `admit_rotation_apply` and refuses on every other arm), but not yet its production route. - -#### Request shapes, and what a grounding establishes (O1a-2, ruled 2026-10-04) - -The route standing is per **request shape**, per controller and build. Two shapes exist. - -- **Set User Password** (`IpmiSetUserPassword`). mtcollins1's committed execution grounds it **at BMC 0.32 only**. The controller is now on 0.45.3, so for the current build this route is another build's and the Apply refuses (`RouteForAnotherBuild`). -- **Set User Access, privilege-limit-only form** (`IpmiSetUserPrivilegeLimit`). It is needed because the `BmcSecure` conjunction requires the published credential refused on every LAN member, and a password write cannot set a channel privilege to no-access. mtcollins1 is a **candidate** only, and nothing is grounded. - -What the model holds: -- **One shape never admits another.** A route grounded for one request shape does not admit an Apply of the other. -- **A grounding records what it established.** `EffectObserved`, or `AcceptedEffectUnobserved`, computed from before and after readings. A privilege-**lowering** write is admitted only over an effect-observed grounding. -- **The lockout guard runs before the first dangerous write, including the qualification write itself.** The live transport may be driven only from a sealed admitted qualification request. For the privilege-limit shape, that request requires a sealed, evidence-bound readback from the same run showing the goal's managed account at administrator on that channel. The readback must be for the same controller and build, with one unambiguous row. -- **mtcollins1's qualifying request is non-destructive.** It re-asserts the value already there. That grounds acceptance only, so it does **not** admit closing the factory administrator. - -**What mtcollins1's wet transition therefore needs, in order.** Each item is an operator-gated act; eager-gull-22 batches them into one go-ahead. -1. A committed read-only post-reflash probe: firmware version, channel info for every channel, and user access and user name for every user id on each LAN channel. The last of these is what lets a candidate name a spare slot. -2. Qualify Set User Password at 0.45.3. -3. Converge the managed account and read it back. -4. Qualify the privilege-limit shape with a **discriminating, harmless** request: a spare user slot changed, read back, then restored. -5. Only then, the closing write on the factory administrator. -6. The independent re-read from which `BmcSecure` is derived. - -The convergence refuses at each step that is not yet grounded. It never widens. - -#### mtjade1's wet sequence (operator ruling, 2026-10-06) - -**The ruling.** Given by the operator directly in eager-gull-22's session and relayed in dashboard message `msg_7402f8df`: live operations on mtjade1 are authorized whenever and indefinitely, with no per-run approval. That covers BMC reads and writes (history archive, login and credential change), boots, and firmware qualification on Mt. Jade. **The scope is mtjade1 only**: mtcollins1's wet steps still need their own go-ahead. - -**Where it lives.** The ruling is recorded as a standing grant in `gunbc.auth.standing_operator_grant`, not here. That module's `StandingOperatorGrant` is scoped today by a Spark `FabricGroup`, so its scope is re-derived to also name a managed-host subject, with Group A's meaning unchanged. The grant carries only effects that a gate consumes (§3c). Each covered effect is classified by executing `gunbc.auth.authorization_pattern_selection` and gets its row in `gunbc.auth.privileged_effect_census`. An effect whose consuming gate has not landed is a declared frontier, not a row. - -**What the ruling does not change.** It discharges the per-run approval only. Every other wall stands: -- the route standing; -- the lockout guard before the first dangerous write; -- the sealed, evidence-bound readbacks; -- the per-build grounding; -- **real readings only.** Anything unmeasured stays a refusal; nothing is defaulted or transcribed from another unit or another build. - -**Order.** Each step lands as a committed, digest-cited capture or receipt, taken from srv1 the way the 2026-10-04 Manager and FRU reads were. A step whose consuming code has not landed waits for it. - -| # | Step | Consumed by | Waits on | -|---|---|---|---| -| J0 | The standing grant row for mtjade1 | the gates below | its own PR (no hardware) | -| J1 | Read-only probe at the current build (Wiwynn/AMI MegaRAC SPX, firmware `2.11.104000`): channel info for every LAN channel; user access and user name for every user id; SDR record names and readings; the controller clock | O1a route standing, O1b `BmcSecure` observe, `gunbc.megarac_operation_standing` `BuildReading` | J0 (reads only) | -| J2 | **Prior-life archive**, read-only on the BMC: all eight carriers captured from the box, archived and read back by digest; the cursor derived from the archive | O1c-2 `PriorLifeBoundary` | #13419 landed | -| J3 | **Per-build MegaRAC operation evidence** at `2.11.104000`: virtual-media attach, a KVM canvas still, SDR socket naming; each an executed receipt, never a recovered or transcribed one | `gunbc.machine_intake_megarac_operation_evidence`, `admit_megarac_host_at_current_build` | J1 | -| J4 | **Login and credential change**: `BmcSecure` Apply through the route O1a grounds at this build, the lockout guard first, then the independent re-read from which `BmcSecure` is derived; then mtjade1's `ManagedHostAdmission` record | O1c-3 (the gate discharged by the J0 grant; the admission population) | O1c-3 landed, J1 grounding the routes | -| J5 | **Board figure and BMC-stack readings measured on the box**: SMBIOS type 17 locators and the DIMM population; fan and sensor names | a Mt. Jade figure for cut 6's board selection, from the cited Mt. Jade guide joined to these readings; cut 5's family-selected fan policy | J1, and the board-identity decision below | -| J6 | **Boots** | the host-generic boot run | cuts 4b-ii and 4d, then O2 | - -**mtjade1's board is not `GigabyteMp72Hb0`.** Its FRU capture (`artifacts/bmc/mtjade1-ipmitool-fru-print-192.168.1.246-2026-10-04T110207Z.txt`) reads Board Mfg `WIWYNN`, Board Product `Mt.Jade Motherboard`, part `B81.03010.0041`. `extdeps.boards.types` `GigabyteMp72Hb0` is a different board: a candidate reference row with no fleet instance. - -Cut 6's selection is keyed by the closed `BaseboardModel`. `extdeps.ocp.mt_jade.platform` deliberately keeps Mt. Jade out of that enum: adding a board must not edit a generic product enum (§3, external upstream decomposition). So J5 has two prerequisites, and it does not add an enum arm: -- **Board identity:** decide how a bound host's board identity reaches the per-board authority (`extdeps.ocp.mt_jade` and `extdeps.ampere.mt_jade_getting_started_guide`), read from that host's own FRU. -- **Re-key the selection:** cut 6's figure selection moves to that identity. If it stays on the closed enum, the closed enum is a §3 hub. - -Until both are done, mtjade1's orientation refuses. - -**What stays a refusal until measured:** -- Mt. Jade's DIMM figure: none is authored from the guide alone, and nothing comes from proprietary documents. -- A MegaRAC operation not executed at `2.11.104000`. -- A route not grounded at this build. -- mtjade1's managed-host membership, until J4's record exists. - -#### `BmcSecure` as a desired state (O1b) - -Today `derive_bmc_secure` is rotation-event shaped. A rejected bootstrap credential is `PreviouslyRotatedCredentialRequired`, an accepted one with no rotation is `CredentialRotationNotApplied`, and only `RotationApplied` with an advanced epoch can mint `BmcSecured`. **So a correctly secured controller cannot be observed as a Noop through the existing fold.** The phase is therefore decomposed in `gunbc.machine_intake_bmc_secure`, with its conjunction unchanged: - -1. **Observe** the current managed-account and published-account state: the managed probe, plus the published probes joined to the channel census. This is independent of whether this run rotated anything. -2. **Noop only when the full existing conjunction already holds.** -3. Otherwise **Apply** a typed account action over the grounded route. -4. **Re-read independently** and derive `BmcSecure` from that readback. - -**Secret order, structurally.** The new credential generation is generated and stored under the unit's `SecretRef`, and its **exact resolved version is fetched**, before the account write and before the managed probe. Later consumers materialize that same `SecretRef` generation through `gunbc.auth.secret_ref_credential` `fetch_secret_ref_credential`. The earlier table's separate "managed secret materialized" row after `BmcSecure` was wrong: the rotation path cannot first obtain the secret after it has supposedly completed. - -Controls: -- already secured → Noop; -- managed accepted and factory accepted → not Noop; -- reflash-restored factory access → the same Apply and readback path, with no second recovery authority. - -**Ruling on mtcollins1's standing (eager-gull-22, 2026-10-04): staged, option C.** -- **Measured by O1b.** The committed `BmcSecured` standing is the 2026-09-13 observation on BMC 0.32. The 2026-10-02 reflash to 0.45.3 changed the subject and re-enabled the factory `admin`. No post-reflash readback is committed. So the honest standing for the current subject is not secured, and landing that alone would empty `managed_hosts()` and stop boot, reset, hold and federation admission. -- **O1b lands in shadow.** Boot is gap-intolerant, so O1b lands the state-shaped derivation, the carriers, the clock and the controls, with mtcollins1's live observation and the rotation-event path untouched and frozen. -- **One later transition** lands the honest re-expression and deletes the old path, **together with** a committed read-only post-reflash probe and the operator-approved wet `BmcSecure` Apply on mtcollins1. -- **Declared drop.** Until then, main's stale claim is a declared drop under `gunbc.rung_drop`, whose trigger is that capability. -- **What the state-shaped fold refuses** (side-chat reviews of #13221). The firmware build that admits a route is read from the observation, never supplied. The published account's replacement is the goal's exact break-glass generation, checked before the write, in the assessment and in the post-read. An unaddressed LAN channel, or channel access not closed, is refused with a typed cause naming the missing operation; it is not planned as a password write. -- **`rotation_route_consumption_frontier` stays open** until the planner is on a production path. This shadow cut supplies the consumer's code, not its production route. -- **Order on the hardware:** the operator's DIMM change, then cut 2 (#13131) landing, then the probe and the wet `BmcSecure`, then the census boot the runner lane needs, on a secured BMC. - -**mtcollins1 is not assumed satisfied.** This withdraws the earlier sentence that it "enters at its satisfied state and every step is a Noop". After the 2026-10-02 reflash the `gunbc` user and IPMI admin were restored by hand and the factory `admin` was not disabled (eager-gull-22, 2026-10-03). The readback is therefore expected to find managed accepted and factory accepted: not Noop. - -**Firmware re-entry.** `gunbc.fleet.mtcollins_firmware_converge` `BmcCredentialReestablishment.ipmi` is today a supplied `BmcSecureStanding`. It becomes this phase's result, for the post-reflash subject. - -#### Managed-host admission: a durable, named population (O1c) - -`managed_hosts()` is today a source roster with one construction-confined row. A runtime convergence cannot add a row, and the earlier sentence "mtjade1 joins by converging, not by edit" is withdrawn. Membership needs a durable authority, and the repository already has the mechanism: an intake fact is a **git-tracked observation consumed at mint time**. So: - -- **`ManagedHostAdmission`**: one committed record per unit. It is authored from that unit's arrival receipts in the unit's own observation module, the way the mtcollins1 observations are today. It retains: - - `HostIdentity` ↔ the exact `MachineIntakeSubject`; - - the access and capability standing; - - the `BmcSecureStanding`; - - the unit-hold decision; - - the receipt identity and its currency. -- **`managed_hosts()` becomes a projection** over the admission records. A unit is a member only when its record is complete, its standings are bound to the same subject, and that subject is current. -- **Absent, not defaulted.** A secured unit with no hold decision, with no host allocation, or with a receipt for another subject, is absent. -- The boot and reset consumers enumerate that same population (they already read `managed_hosts()`). -- `managed_host_binding` stays what it is: a pure join over a row. It is not the membership event. - -So mtjade1 joins when its receipts are committed and its admission record is authored, which is an edit of evidence and never of the roster's logic. - -#### Receipts and clock: domain carriers, not `gunbc.ensure` alone (O1b, O1c) - -`gunbc.ensure` says of itself that its before and after observations and its satisfaction Booleans are caller-supplied, that one observation can be passed twice, and that it does not prove the decided plan is the effect that ran. So "ensure-style" is the decision shape only. For every effectful phase, the effect authority mints a **construction-confined domain carrier** that binds: - -`subject + phase + admitted plan + application receipt + independent post-read + evidence` - -The dry realization over `gunbc.bmc_model` `BmcWorld` exercises those carriers, not only the generic Noop / Apply / Refuse arms. - -**Observer clock.** `bmc_secure` carries bare `EpochMs`, so a comment cannot make a controller-sourced time unconstructible. O1b introduces an observer-clock timestamp whose only producer is `gunbc.clock_read`, and every ordering field in the phase consumes it. The controller's year-2000 clock is preserved as an `AccessDiscover` observation and can never be the ordering clock. Control: a controller-sourced year-2000 timestamp cannot satisfy the rotation and readback ordering, and an observer timestamp can. Setting the BMC clock is a write and is out of scope. - -#### The convergence fold: one shared home, landed with its first consumer (O1c-1) - -**Why this is here** (eager-gull-22, 2026-10-03). The Spark serving bring-up (valiant-crab-775) needs the same thing cut O does: several effectful steps run as one convergence instead of separately dispatched modes that a human sequences. The operator pointed that lane at this one. The home lands **once**, with cut O and the Spark arm as its consumers. It does not go inside `gunbc.machine_intake_arrival_converge`. - -**What already exists (DFS before naming anything).** -- **The per-step lifecycle has a consumed home.** `gunbc.host_convergence_protocol` is the canonical protocol from the CONVERGENCE-ONE program (`docs/plans/convergence-one-program.md`): select, observe goal-blind, assess, plan, admit, apply, independently read back, terminal verdict. - - `HostEffectDomainProjection { observe, assess, decide }`, `inspect_via_host_effect_projection` and `reconcile_from_read_attempt` are the step. - - `gunbc.ensure` is a projection of it. - - That program's own rule: "anything that calls itself convergence without running that lifecycle is a second convergence algebra". -- **The roster of persistent host effects has a home.** `gunbc.host_convergence_census` `host_convergence_census_rows`. Each arrival phase's effect, and each Spark step, is a row there. It is not a parallel list. -- **The sealed receipt is the domain's.** `std.realization_reconcile` states that std cannot seal the relation decided plan ↔ performed plan ↔ evidence without accepting a law from the caller it would be checking, and that the binding "is the DOMAIN's obligation, discharged by a domain carrier minted only by the authority that performed the effect" (worked example: `gunbc.typed_remote_file_write` `RemoteFileConverged`). So there is **no generic sealed receipt** to build. O1b's `BmcSecure` receipt and Spark's receipts stay domain carriers. - -**What is still open in that home, stated honestly** (side-chat review of `5044d42`). Ordered composition is not the only missing piece. -- **The protocol's apply side is an awaiting frontier.** `host_effect_decide_apply_frontier` is `FrontierConsumerAwaiting`: `converge_apply_for_host` bypasses `HostEffectDomainProjection` and calls `host_effect_apply` directly. That is CONVERGENCE-ONE's unbuilt C8. -- **`gunbc.ensure` `ensure_reconcile` does not execute the lifecycle.** It classifies reports. - -**The boundary this plan takes: (B).** The fold **composes already-executed, domain-owned step outcomes**. It does not execute the protocol and does not claim to. -- Each domain runs its own step (observe, decide, apply, read back) and mints its own sealed receipt. -- The fold orders step instances, checks preconditions against those receipts, and produces the run's ledger. -- **C8 stays open.** It is discharged only by a production route through the projection's apply side. Nothing in cut O claims to discharge it. If an O1c PR does route a real apply through the projection, it says so and retires the frontier by its own trigger. - -It goes in the workflow layer, `gunbc.fleet`. It does not go in std, because lanes, principals and resumability are fleet policy. The module is named by DFS when built. - -**The runtime steps are joined to the census, so the census stays the roster.** -- `StepInstanceKey { run, step_identity, subject }` identifies a step instance. -- Every runtime step maps to **exactly one** `HostConvergenceCensusRow`. -- A missing, extra, duplicate or unresolved identity refuses. -- A duplicate `(run, step, subject)` also refuses. - -Without that identity join the runtime list would become the real roster and the census would be commentary. - -**Its shape, tested against two real consumers:** cut O's five arrival phases, and the Spark arm's steps S1–S9 (image produce and distribute, checkpoint seed and replica, sudo grants, claim recovery, supersede, launch). valiant-crab-775 reviewed the shape on 2026-10-04, and its six corrections are in the table. - -| Requirement | Shape | Driven by | -|---|---|---| -| A step | an identity; a `HostEffectDomainProjection`; its domain receipt type; its preconditions. **A step instance is step × subject**: the Spark steps are per (host, artifact), the arrival steps per unit. | both | -| Preconditions | step identities **with a quantifier over subjects**: all-of or any-of, each over a stated subject set. So "3 of 4 hosts converged" is representable as partial progress and is not read as a refusal. | Spark S9 (S3, S5, S6 on all hosts); S5 (S4 verified on some peer) | -| Order | derived from preconditions by an identity join (an unknown identity or a cycle refuses). **Where an order authority already exists, each step is bound to one member of it and the derived order must agree with that authority's ranking, or refuse.** For cut O, each step is bound to one `IntakePhase` and checked with `intake_phase_rank`. The roster is a projection of the authoritative order (2 phases in O1c-1, 4 in O1, 5 with O2), not the 12-phase list itself. | arrival (authority), Spark (derived) | -| Per step | the canonical protocol, unchanged: Noop / Apply / Refuse, independent readback, the domain receipt minted by the effect authority | both | -| Precondition satisfied | only by a **readback in this run** of the precondition instance: a receipt for the same subject. Never by an earlier step's return value. **Which readback is sufficient is the domain's declaration**, and the fold does not force the most expensive one. A domain may declare a cheap identity readback that binds to an earlier full-verification receipt, and it must then state what that cheap readback does and does not establish (§4b rung honesty). | both; Spark S5, whose full verify re-hashes the whole checkpoint on every host | -| Refusal | names the unmet step by identity. The fold may take that step **only if it is in the roster and ungated**. A gated step stops with a typed refusal naming the discharge it needs. | Spark S9 naming S6/S7; the arrival credential write | -| Gate | a step is ungated, or gated on an authorization discharged for that subject and that run. The pattern comes from `select_authorization_pattern`, selected **before** the fold (§3d: the fold chooses nothing). | arrival `BmcSecure`; Spark S7 | -| Principal | per step, from the same selection. A step whose principal is not bound refuses. | Spark S6/S7 vs S9 | -| No fallback arms | a step with no admissible realization refuses; it never widens (§5) | Spark S5: no verified peer, no Hub fallback | -| Uncertain completion | an applied step whose readback does not yet ground is **pending**, distinct from converged and from refused. A rerun re-observes and reattaches. A pending instance never satisfies a precondition. **Only its dependents wait.** Independent instances proceed in the same run; otherwise every run degenerates to one step. | Spark S2/S4 (hours, resumable) while S3 and S6 proceed; arrival archive | -| Deadline | every effect leg carries a deadline. A leg with none is unconstructible. | Spark's ssh leg that hangs instead of refusing | -| Lane | the set of hosts a run may mutate is derived from its step instances' **mutated subjects**, one lane per affected host. **The executor host that dispatches the run is not a lane key.** | Spark, where every mode dispatches from srv1 and collides there; the arrival unit hold | -| Verdict | one ledger per run: each step instance converged, pending, refused, or not reached. **A refusal stops the line. A pending instance does not.** | both | -| Selected before the fold | everything that is a choice: the route, the authorization pattern, the principal, and **roles** such as seed versus replica. A role derived inside a step is how a silent fallback happens. | arrival route (O1a); Spark S4/S5 roles | -| Not a step | a **measurement** is not a persistent host effect. It consumes a converged verdict and produces an observation receipt, outside the fold and outside the census. Otherwise "converged" comes to mean "benchmarked". | Spark S10 (load) | - -**Workflow surface.** Lane derivation changes concurrency groups in the generated workflow, which is an operator-visible surface. Cut O's use changes none: it runs inside the existing `mtcollins1_boot` job and its unit hold. The Spark arm's reshaping of its modes is that lane's proposal to the operator, not part of this cut. - -**§3c, and the pairing obligation: each capability lands with its first REAL consumer.** A fold with no consumer is a dangling declaration, and a capability exercised only by a designed fixture has no inhabitance claim (`DESIGN.md` §3, "a witness discriminates at one interface"). So the fold is not built whole in one PR. Each row of the shape table arrives in the PR whose consumer really exercises it: - -| PR | Real consumer | Capabilities it brings | -|---|---|---| -| **O1c-1** | the arrival prefix's two **read-only** phases, `AccessDiscover` (with the O1a route standing) and `IdentityBindProvisional` | the read-only scheduler core: `StepInstanceKey`; the census join and its refusals; step-to-`IntakePhase` binding and the rank check; preconditions satisfied by an in-run readback receipt; refusal naming the unmet step; the run ledger. **No apply, gate, lane, deadline or pending arm.** | -| **O1c-2** | `PriorLifeBoundary`'s archive (an effect that writes evidence and can be incomplete) | an applied step with its domain receipt; per-leg deadlines; incomplete-versus-refused | -| **O1c-3** | `BmcSecure` (O1b's receipt) and the `ManagedHostAdmission` population | the authorization gate and its discharge; principal per step; the mutation lane (the unit hold on the store host); apply followed by independent readback | -| **Spark arm** (valiant-crab-775's lane) | S1–S9 over four hosts | subject quantifiers (all-of, any-of); pending that blocks only dependents; the domain-declared cheap readback; multi-host lanes keyed on mutated subjects | - -**Controls land with the capability.** -- O1c-1's are: - - a step whose phase rank disagrees with the authority refuses; - - an unknown precondition refuses; - - a runtime step with no census row, or two rows, refuses; - - a duplicate `(run, step, subject)` refuses; - - a precondition is not satisfied by a receipt for another subject. -- The quantifier, pending and lane controls arrive with the consumer that inhabits them, not earlier with a fixture. - -valiant-crab-775 reviews O1c-1's shape before it lands. `DESIGN.md` §3d names the fleet admission spine as this cycle's first consumer, so the shape must not preclude it, and O1c-1's PR says how it fits. - -**What O1b changes.** Nothing in its scope. Its observe, assess and decide go through the canonical projection (`HostEffectDomainProjection`), and its effect is a census row. Its apply and readback are the domain's own, with its own sealed receipt, because the projection's apply side is the open C8 frontier. - -#### Authorization and what lands - -- **The rotation is a privileged effect.** It is classified by executing `gunbc.auth.authorization_pattern_selection` `select_authorization_pattern` over its real attributes, and it gets its row in `gunbc.auth.privileged_effect_census`. The pattern is what the selection returns. -- **The write is gated inside the single route.** The `BmcSecure` Apply arm refuses unless that authorization is discharged for that unit and that run. A run that finds the unit unsecured with no discharge stops with a typed refusal at `BmcSecure`. It never boots on a factory credential and never widens. -- **O2 preserves every refusal** the current boot entry makes (admission, authorization, maintenance hold, artifact, medium readback). Each is listed and witnessed in that PR. -- **The O1 PRs land** the route standing, the state-shaped `BmcSecure`, the observer clock, the convergence through `BmcSecure`, the admission population, and the dry realization with the controls above. **No live credential write** is part of any PR; each unit's write gets its own operator go-ahead, mtcollins1 first. -- **Password-path frontier.** The remaining wrappers (media attach, KVM, SOL, fan, UI bundle) cut over in cuts 3, 4a, 4d and 5. `mtcollins1_managed_secret_fetch_frontier` is retired when the last one refuses a caller-supplied path. -- **Workflow surface:** none added. O2's generated-workflow diff is shown in its PR. - -#### Decisions (eager-gull-22 for the operator, 2026-10-03) - -- **(i) Module name:** `gunbc.machine_intake_arrival_converge`. Approved. -- **(ii) Read-only reads of mtjade1 with the factory credential:** no separate sign-off; the operator approved hands-on access to the unit on 2026-10-03. **The rotation and any account write do need sign-off.** -- **(iii) First wet subject:** mtcollins1, with its unsecured state treated as likely real. -- **(iv) Workflow mode:** approved as a **re-root of the existing `mtcollins1_boot` mode**, not a new mode. The live credential write on each unit still gets its own go-ahead. - -### Terminal receipt owed by every cut (review item 5) - -These modules are outside the required gate, so a green required run proves nothing about them. Each PR's description carries all of the following: - -1. **Consumer witnesses, run by name.** The cut's complete consumer roster, recomputed at the PR head with the census method below and included in full in the PR description. Each named witness is executed with `gunbc run` against the head, with binary path and build sha printed. -2. **A same-path RED.** On the acceptance path the cut's witness actually runs, remove the binding the cut introduced (the `ManagedHost` row, the standing arm, the parameter) and show the named witness refusing. Then restore it and show it accepted. A RED reached by a different route does not count. -3. **A corpus sweep.** `git grep` at the head for every deleted module name and symbol, across `.dag`, `.rs`, `.yml`, `docs/` and `artifacts/`. Every remaining hit is classified with the dispositions in the non-import census below. -4. **No aliases.** No module re-exports, wraps or renames-through the deleted root, and no `mtcollins1_*` function is a one-line call into the generic one. - -## Non-import census (review item 4) - -These occurrences do not refuse through an import edge, so the import census above cannot see them. They are measured at `26e99a9c7f` and dispositioned by surface. **migrate** means the owning cut changes it. **receipt** means it is deliberately unit-named and stays. **residue** means it is historical prose and stays as written. - -| Surface | Population | Disposition | -|---|---|---| -| `.github/workflows/fleet-converge.yml` (projection of `gunbc.fleet_converge_workflow`) | 118 occurrences: 8 mode literals and about 30 step/job ids | **migrate**, cut 7 only, regenerated from the authority. Never hand-edited. | -| `.github/workflows/mtcollins-canary.yml` | a hand-authored scaffold by its own header; `runs-on: [self-hosted, mtcollins]` | **migrate or retire** in cut 7, and only with the operator's ruling. The runner label is a live registration (`gunbc.runner_registration_labels`). | -| `"mtcollins1_*"` string literals in `.dag` (mode names, step ids, effect rows) | 34 files. The largest are `gunbc.fleet_converge_workflow` (26), `gunbc.auth.privileged_effect_census` (11), `gunbc.ci_spec` (11), `mtcollins1_boot_run` (10), `mtcollins1_memory_census_observation` (10) | **migrate** in the cut that owns the named step. The privileged-effect rows follow cut 4c and 7. Unit-observation literals are **receipts**. | -| Entry-path strings `"dag/gunbc/machine_intake/mtcollins…"` in `.dag` | `gunbc.non_fold_residue` (45), `gunbc.ci_spec` (11), `v2.workflow.floor_unimported_bare_provider_debt_roster` (4), and 5 single occurrences | **migrate**: every cut that moves a file updates these rows in the same PR. Each is a path a deletion does not refuse. | -| `artifacts/bmc/mtcollins1-*` (17 files) | captured evidence; paths cited by unit observations | **receipt**: never renamed, because the content hashes bind the path. | -| `docs/probes/mtcollins*` (12), `docs/rung-drops/*` (1), `docs/design-rung-drops.md` | dated observations and declared drops | **receipt**. | -| `docs/plans/*` (14 files other than this one), `docs/recovered/*`, `ROADMAP.md` (2) | dated plans; the roadmap goal "Bring Mt. Collins unit 1 into service" is correctly unit-named | **residue / receipt**. Not edited by these cuts. | -| `src/v1/stage0/src/cli_run.rs` (1) | a comment | **residue**. | -| The IAM condition description string beginning `gunbc.auth.mtcollins1_boot_federation: the one version …` (kept by cut 4c in `gunbc.auth.managed_host_boot_federation`) | part of a **live** IAM binding condition | **receipt**: it must stay byte-identical until a deliberate re-provision. A name sweep must not "fix" it, although it names a deleted module. | - -Exact token `mtcollins1_boot`: 19 files at `26e99a9c7f`, all inside the populations above. - -## Census method: a one-off, re-run by each cut (not an instrument) - -No entry point in the tree derives an import-graph consumer roster, and building one is outside this program. So the census is a **one-off measurement** (DESIGN §6), and no snapshot of its output is the authority for anything. - -**Each cut re-runs it at its own PR head** over its own deleted roots, and puts the **complete** consumer roster in that PR's description, bound to that head sha. That roster is the §3 "enumerate the consumers by name before you delete" receipt for the cut, and it lives with the deletion it licenses, where it cannot go stale. - -The method: -1. Walk `dag/` and `src/v2/` for `.dag` files. -2. Read each `^module` and each `^import `. -3. The population is the modules whose name or text matches `mtcollins` (case-insensitive). -4. Consumers are the reverse import edges. -5. Gate membership is the transitive import closure from the modules matching `v2.workflow.required_floor` `required_gate_prefixes`. -6. Imported unit symbols are the names inside each module's `import … { … }` lists that match `mtcollins|mt_collins|MtCollins`. - -It is a regex over source text, so it censuses *import shapes*. The terminal receipt's corpus sweep and the non-import census above cover what an import edge cannot see. - -## Census - -The layer is assigned per module from its subject. -- **R** = mentions the unit only in prose or receipts. -- **W** = witness. - -Consumers list production importers by name, truncated after six with a count, plus the number of witness importers. **This table is a dated reading aid at `26e99a9c7f`, used to plan the order. It is not a consumer receipt: each cut recomputes its own roster at its head (see Census method).** - -### Layer 3 — silicon (3 modules) - -| module | lines | refs | unit/platform constants imported | consumers (import census) | gate | -|---|---|---|---|---|---| -| `gunbc.machine_intake_ampere_dram_console_observation` | 314 | 2 | — | `gunbc.machine_intake_ampere_socket_console_observation`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle` · 1 witness | no | -| `gunbc.machine_intake_ampere_socket_console_observation` | 503 | 2 | — | `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_smpro_observation` | 395 | 36 | `mtcollins1_endpoint` | `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_boot_phase_timing`, `gunbc.machine_intake_mtcollins1_boot_run` · 3 witness | no | - -### Layer 5 — fleet procedure (53 modules) - -| module | lines | refs | unit/platform constants imported | consumers (import census) | gate | -|---|---|---|---|---|---| -| `extdeps.provisioning.ubuntu_seeded_install_media` | 196 | 1 | — | `extdeps.provisioning.ubuntu_seeded_install_media_remaster`, `gunbc.machine_intake_megarac_media_attach`, `gunbc.machine_intake_mtcollins1_boot_authorization`, `gunbc.machine_intake_mtcollins1_census_image`, `gunbc.machine_intake_mtcollins1_census_image_publish`, `gunbc.machine_intake_mtcollins1_census_medium_readback` +4 more · 7 witness | no | -| `extdeps.tools.xorriso` | 151 | 1 | — | `extdeps.provisioning.ubuntu_seeded_install_media_remaster`, `extdeps.provisioning.ubuntu_seeded_install_media_toolchain`, `gunbc.host_effect_realize`, `gunbc.machine_intake_mtcollins1_census_member_readback` · 2 witness | no | -| `gunbc.auth.fleet_secret_accessor_roster` | 95 | 6 | `mtcollins1_bmc_gunbc_secret_ref` | `gunbc.auth.approval_mac_key_provision`, `gunbc.spark.secret_access_ensure` · 1 witness | no | -| `gunbc.auth.gcp_iam_converge` | 893 | 4 | `mtcollins1_boot_dedicated_federation` | `gunbc.auth.gcp_iam_converge_run`, `gunbc.fleet_converge_workflow` · 1 witness | no | -| `gunbc.auth.gcp_iam_converge_federation` | 67 | 1 | — | `gunbc.auth.gcp_iam_converge`, `gunbc.fleet_converge_workflow` | no | -| `gunbc.auth.heal_publisher_provision` | 500 | 1 | — | `gunbc.auth.gcp_iam_converge`, `gunbc.auth.mtcollins1_boot_federation_provision`, `gunbc.namecheap.federation_provision` · 2 witness | no | -| `gunbc.auth.mtcollins1_boot_federation` | 157 | 43 | `mtcollins1_bmc_gunbc_secret_ref` | `gunbc.auth.gcp_iam_converge`, `gunbc.auth.mtcollins1_boot_federation_provision`, `gunbc.auth.privileged_effect_census`, `gunbc.fleet_converge_workflow` · 1 witness | no | -| `gunbc.auth.mtcollins1_boot_federation_provision` | 39 | 28 | `mtcollins1_boot_secret_grants`, `mtcollins1_boot_service_account`, `mtcollins1_boot_service_account_display_name`, `mtcollins1_boot_service_account_id`, `mtcollins1_boot_service_account_member` … | `gunbc.auth.gcp_iam_converge` · 1 witness | no | -| `gunbc.auth.privileged_effect_census` | 1060 | 54 | `mtcollins1_boot_service_account_member` | `gunbc.census_closure_frontier` · 3 witness | no | -| `gunbc.fleet_converge_workflow` | 4446 | 271 | `gunbc_ci_mtcollins1_boot_admit_invoke`, `gunbc_ci_mtcollins1_boot_invoke`, `gunbc_ci_mtcollins1_boot_job_backstop_timeout_minutes`, `gunbc_ci_mtcollins1_census_image_publish_invoke`, `gunbc_ci_mtcollins1_census_image_step_timeout_minutes` … | `gunbc.generated_artifact_emit` · 11 witness | no | -| `gunbc.fleet_workflow_steps` | 929 | 4 | — | `gunbc.fleet_converge_workflow` · 2 witness | no | -| `gunbc.host_reset_return` | 1034 | 2 | — | `gunbc.ci_spec`, `gunbc.fleet_converge_workflow`, `gunbc.host_reset_return_run` · 2 witness | no | -| `gunbc.host_reset_return_run` | 787 | 3 | — | `gunbc.ci_spec`, `gunbc.fleet_converge_workflow`, `gunbc.machine_intake_mtcollins1_boot_run` · 1 witness | no | -| `gunbc.host_reset_subject_roster` | 140 | 22 | `mtcollins1_access_observation`, `mtcollins1_bmc_username`, `mtcollins1_capability_row`, `mtcollins1_cdrom_selection`, `mtcollins1_unit_hold_key` … | `gunbc.fleet_converge_workflow`, `gunbc.host_reset_return_run` | no | -| `gunbc.hostname_allocation` | 111 | 3 | `operator_host_mtcollins1` | `gunbc.deployed_intent_v1`, `gunbc.host_reset_return_run`, `gunbc.network_identity_subsumption` | no | -| `gunbc.machine_intake_host_capture_envelope` | 749 | 5 | — | `gunbc.machine_intake_host_capture_historical_binding`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_boot_milestone`, `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_census_image` · 6 witness | no | -| `gunbc.machine_intake_host_capture_historical_binding` | 348 | 14 | `mtcollins1_census_stages`, `mtcollins1_host_capture_artifact_path`, `mtcollins1_host_capture_byte_count`, `mtcollins1_host_capture_digest` | — · 2 witness | no | -| `gunbc.machine_intake_host_resource_observation` | 403 | 4 | — | `gunbc.machine_intake_mtcollins1_topology_goal` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_actuate` | 196 | 41 | `MtCollins1BootStart`, `mtcollins1_access_observation`, `mtcollins1_boot_start_of`, `mtcollins1_capability_row`, `mtcollins1_secured_account` … | `gunbc.host_reset_subject_roster`, `gunbc.machine_intake_mtcollins1_boot_authorization`, `gunbc.machine_intake_mtcollins1_boot_run` · 3 witness | no | -| `gunbc.machine_intake_mtcollins1_boot_admission` | 52 | 15 | — | `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_kvm_observer_observe` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_boot_artifact` | 58 | 5 | — | `gunbc.machine_intake_mtcollins1_boot_authorization`, `gunbc.machine_intake_mtcollins1_boot_image_fetch`, `gunbc.machine_intake_mtcollins1_census_image`, `gunbc.machine_intake_mtcollins1_census_image_publish`, `gunbc.machine_intake_mtcollins1_census_medium_readback`, `gunbc.machine_intake_mtcollins1_census_member_readback` +1 more · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_boot_authorization` | 105 | 61 | `mtcollins1_boot_export_dir`, `mtcollins1_boot_image_sha256`, `mtcollins1_cdrom_selection`, `mtcollins1_census_image_stem`, `mtcollins1_census_volume_id` … | `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_census_medium_readback`, `gunbc.machine_intake_mtcollins1_census_member_readback`, `gunbc.machine_intake_mtcollins1_media_attach` · 9 witness | no | -| `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle` | 2204 | 214 | `MtCollins1BootPhaseTiming`, `mtcollins1_boot_ipmi_kill_after`, `mtcollins1_boot_ipmi_read_deadline`, `mtcollins1_boot_phase_timing_not_taken`, `mtcollins1_boot_phase_timing_text` … | `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_media_attach` · 8 witness | no | -| `gunbc.machine_intake_mtcollins1_boot_dry_realization` | 730 | 100 | `mtcollins1_sol_notice_ready_suffix`, `mtcollins1_sol_notice_token_env`, `mtcollins1_sol_notice_watcher_path` | — · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_boot_image_fetch` | 119 | 37 | `mtcollins1_boot_export_dir`, `mtcollins1_diskless_image_name` | `gunbc.machine_intake_mtcollins1_boot_authorization` | no | -| `gunbc.machine_intake_mtcollins1_boot_milestone` | 133 | 28 | `mtcollins1_nproc` | `gunbc.machine_intake_host_capture_historical_binding`, `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_census_image` · 4 witness | no | -| `gunbc.machine_intake_mtcollins1_boot_phase_timing` | 255 | 92 | — | `gunbc.machine_intake_mtcollins1_actuate`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_boot_run` · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_boot_run` | 2734 | 571 | `MtCollins1BootDiagnosticBundle`, `MtCollins1BootMedium`, `MtCollins1BootPhaseTiming`, `MtCollins1BootRoute`, `MtCollins1BootRunRecord` … | `gunbc.ci_spec`, `gunbc.fleet_converge_workflow`, `gunbc.machine_intake_mtcollins1_boot_dry_realization`, `gunbc.machine_intake_mtcollins1_sol_notice` · 8 witness | no | -| `gunbc.machine_intake_mtcollins1_census_image` | 220 | 76 | `mtcollins1_boot_export_dir`, `mtcollins1_census_stages`, `mtcollins1_diskless_image_name` | `gunbc.machine_intake_mtcollins1_boot_authorization`, `gunbc.machine_intake_mtcollins1_census_image_publish`, `gunbc.machine_intake_mtcollins1_census_medium_readback`, `gunbc.machine_intake_mtcollins1_census_member_readback`, `gunbc.machine_intake_mtcollins1_media_attach` · 9 witness | no | -| `gunbc.machine_intake_mtcollins1_census_image_publish` | 267 | 46 | `mtcollins1_boot_export_dir`, `mtcollins1_boot_export_host_target`, `mtcollins1_census_image`, `mtcollins1_census_image_stock_path`, `mtcollins1_census_pinned_image` | `gunbc.ci_spec`, `gunbc.fleet_converge_workflow` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_census_medium_readback` | 260 | 74 | `MtCollins1BootMedium`, `MtCollins1CensusMedium`, `MtCollins1StockInstallerMedium`, `mtcollins1_boot_export_dir`, `mtcollins1_boot_export_host_target` … | `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_census_member_readback` · 3 witness | no | -| `gunbc.machine_intake_mtcollins1_census_member_readback` | 483 | 34 | `MtCollins1BootMedium`, `MtCollins1CensusMedium`, `MtCollins1StockInstallerMedium`, `mtcollins1_boot_export_host_target`, `mtcollins1_boot_medium` … | `gunbc.ci_spec`, `gunbc.fleet_converge_workflow`, `gunbc.machine_intake_mtcollins1_census_image_publish` · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_census_qemu_host_observe` | 294 | 11 | — | `gunbc.ci_spec`, `gunbc.fleet_converge_workflow`, `gunbc.machine_intake_mtcollins1_census_qemu_toolchain_converge` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_census_qemu_toolchain` | 172 | 1 | — | `gunbc.machine_intake_mtcollins1_census_qemu_host_observe`, `gunbc.machine_intake_mtcollins1_census_qemu_toolchain_converge` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_census_qemu_toolchain_converge` | 41 | 8 | — | — | no | -| `gunbc.machine_intake_mtcollins1_census_refusal_hypotheses` | 356 | 13 | — | — · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_diskless_boot_receipt` | 144 | 12 | — | — | no | -| `gunbc.machine_intake_mtcollins1_handoff_probe` | 63 | 14 | `mtcollins1_access_observation`, `mtcollins1_capability_row`, `mtcollins1_firmware` | — | no | -| `gunbc.machine_intake_mtcollins1_maintenance_hold` | 763 | 58 | `operator_host_mtcollins1` | `gunbc.fleet_converge_workflow`, `gunbc.host_reset_return_run`, `gunbc.host_reset_subject_roster`, `gunbc.machine_intake_mtcollins1_actuate`, `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_kvm_observer_observe` +2 more · 5 witness | no | -| `gunbc.machine_intake_mtcollins1_media_attach` | 427 | 111 | `MtCollins1BootSubject`, `MtCollins1MediaAttachRecord`, `mtcollins1_boot_chassis_power`, `mtcollins1_boot_export_dir`, `mtcollins1_boot_ipmi_kill_after` … | `gunbc.machine_intake_mtcollins1_boot_run` · 4 witness | no | -| `gunbc.machine_intake_mtcollins1_memory_census_observation` | 916 | 172 | `MtCollins1ControllerFruIdentity`, `mtcollins1_controller_fru_identity` | `gunbc.machine_intake_host_capture_historical_binding`, `gunbc.machine_intake_mtcollins1_bmc_secure_observation`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_boot_milestone`, `gunbc.machine_intake_mtcollins1_topology_goal` · 3 witness | no | -| `gunbc.machine_intake_mtcollins1_netboot_client_observation` | 73 | 11 | — | `gunbc.machine_intake_pre_os_capture_replay`, `gunbc.machine_intake_pre_os_observer`, `gunbc.site_uefi_arm64_pxe_edge` · 6 witness | no | -| `gunbc.machine_intake_oob_boot_handoff` | 542 | 2 | — | `gunbc.host_reset_return_run`, `gunbc.host_reset_subject_roster`, `gunbc.machine_intake_mtcollins1_actuate`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_boot_phase_timing`, `gunbc.machine_intake_mtcollins1_boot_run` +1 more · 5 witness | no | -| `gunbc.machine_intake_pre_os_bringup_attempt` | 370 | 2 | — | `gunbc.machine_intake_mtcollins1_32dimm_bringup_observation`, `gunbc.machine_intake_mtcollins1_sixteen_module_restore_observation` · 1 witness | no | -| `gunbc.machine_intake_pre_os_bringup_verdict` | 633 | 1 | — | `gunbc.machine_intake_mtcollins1_32dimm_bringup_observation`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_sixteen_module_restore_observation`, `gunbc.machine_intake_pre_os_capture_replay`, `gunbc.machine_intake_pre_os_observer` +2 more · 6 witness | no | -| `gunbc.machine_intake_pre_os_capture_replay` | 175 | 27 | `mt_collins_channel_population_roles`, `mtcollins1_first_own_image_boot`, `mtcollins1_host_nic_mac`, `mtcollins_3ds_capture_path`, `mtcollins_3ds_capture_sha256` … | — · 2 witness | no | -| `gunbc.machine_intake_pre_os_observer` | 139 | 1 | — | `gunbc.machine_intake_pre_os_capture_replay`, `gunbc.machine_intake_pre_os_inventory_assessment`, `gunbc.machine_intake_pre_os_timeline` · 3 witness | no | -| `gunbc.machine_intake_pre_os_timeline` | 392 | 3 | `mtcollins_dram_cycle_start` | — · 1 witness | no | -| `gunbc.machine_intake_sel_stimulus_listener` | 124 | 2 | — | `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_pre_os_capture_replay`, `gunbc.machine_intake_pre_os_replay` · 2 witness | no | -| `gunbc.machine_intake_terminal_transcript` | 18 | 2 | — | `gunbc.machine_intake_host_capture_envelope`, `gunbc.machine_intake_pre_os_capture_replay`, `gunbc.machine_intake_pre_os_observer` · 1 witness | no | -| `gunbc.seeded_install_media_policy` | 28 | 1 | — | `gunbc.machine_intake_mtcollins1_census_image`, `gunbc.srv3_seeded_install_media_artifact` | no | -| `gunbc.site_uefi_arm64_pxe_edge` | 339 | 7 | `mtcollins1_host_nic_mac` | `gunbc.census_closure_frontier`, `gunbc.site_pxe_edge_converge` · 2 witness | no | -| `gunbc.verified_archive_install` | 235 | 1 | — | `gunbc.machine_intake_mtcollins1_census_qemu_toolchain`, `gunbc.runner_browser_toolchain`, `gunbc.runner_microvm_host_ready` · 3 witness | no | - -### Layer 4 — BMC stack (22 modules) - -| module | lines | refs | unit/platform constants imported | consumers (import census) | gate | -|---|---|---|---|---|---| -| `extdeps.bmc.http` | 350 | 1 | — | `gunbc.fleet_health_observe`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.srv3_boot_once_cd`, `gunbc.tools.bmc_health_reader_converge`, `gunbc.tools.bmc_intake_first_contact`, `gunbc.tools.bmc_onboard` | no | -| `extdeps.bmc.ipmi` | 410 | 1 | — | `ctl.pos_emit`, `ctl.pos_service`, `gunbc.machine_intake_bmc_fan_observation`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_boot_dry_realization`, `gunbc.machine_intake_mtcollins1_boot_run` +4 more · 1 witness | no | -| `extdeps.bmc.ipmi_sel` | 83 | 1 | — | `gunbc.machine_intake_mtcollins1_32dimm_bringup_observation`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_sixteen_module_restore_observation`, `gunbc.machine_intake_pre_os_bringup_verdict`, `gunbc.machine_intake_sel_stimulus_listener` · 2 witness | no | -| `extdeps.bmc.ipmitool_observed_output` | 67 | 6 | — | `gunbc.bmc_dry_realization`, `gunbc.machine_intake_mtcollins1_boot_dry_realization` | no | -| `extdeps.bmc.megarac` | 664 | 5 | — | `extdeps.bmc.access_profile`, `extdeps.bmc.virtual_media`, `gunbc.bmc_implementation_dispatch`, `gunbc.bmc_megarac_web_adapter`, `gunbc.boot_artifact_delivery`, `gunbc.machine_intake_megarac_media_attach` +3 more · 7 witness | **yes** | -| `extdeps.bmc.megarac_observed_output` | 73 | 1 | — | `gunbc.bmc_dry_realization` | no | -| `extdeps.bmc.phosphor_fan_control` | 126 | 2 | — | — · 1 witness | no | -| `extdeps.bmc.redfish_memory_inventory` | 459 | 2 | — | `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle` · 1 witness | no | -| `gunbc.bmc_model` | 409 | 2 | — | `gunbc.bmc_dry_realization`, `gunbc.bmc_megarac_web_adapter`, `gunbc.machine_intake_mtcollins1_boot_dry_realization` · 5 witness | no | -| `gunbc.machine_intake_bmc_fan_observation` | 519 | 2 | — | `gunbc.machine_intake_mtcollins1_fan_observe` · 1 witness | no | -| `gunbc.machine_intake_bmc_rotation_route` | 117 | 1 | — | — · 1 witness | no | -| `gunbc.machine_intake_megarac_media_attach` | 2107 | 9 | — | `gunbc.bmc_dry_realization`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_media_attach`, `gunbc.machine_intake_mtcollins1_ui_bundle_observe` · 5 witness | no | -| `gunbc.machine_intake_megarac_served_ui_catalog_observation` | 20 | 2 | — | `gunbc.machine_intake_megarac_ui_features`, `gunbc.machine_intake_mtcollins1_ui_bundle_observe` · 3 witness | no | -| `gunbc.machine_intake_megarac_ui_features` | 403 | 3 | — | `gunbc.machine_intake_megarac_media_attach`, `gunbc.machine_intake_mtcollins1_kvm_still` · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_bmc_secure_observation` | 275 | 92 | `mtcollins1_bmc_gunbc_secret_ref`, `mtcollins1_controller_fru_identity`, `mtcollins1_endpoint`, `mtcollins1_firmware`, `mtcollins1_subject_binding` | `gunbc.machine_intake_mtcollins1_actuate`, `gunbc.machine_intake_mtcollins1_fan_observe`, `gunbc.machine_intake_mtcollins1_kvm_observer_observe`, `gunbc.machine_intake_mtcollins1_media_attach`, `gunbc.machine_intake_mtcollins1_platform_observation`, `gunbc.machine_intake_mtcollins1_ui_bundle_observe` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_bmc_sensor_observation` | 96 | 25 | — | `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_fan_observe` | 264 | 39 | `mtcollins1_endpoint`, `mtcollins1_secured_account` | `gunbc.ci_spec`, `gunbc.fleet_converge_workflow`, `gunbc.machine_intake_mtcollins1_kvm_observer_observe`, `gunbc.machine_intake_mtcollins1_ui_bundle_observe` | no | -| `gunbc.machine_intake_mtcollins1_kvm_observer_observe` | 276 | 71 | `mtcollins1_bmc_firmware`, `mtcollins1_boot_host_admission`, `mtcollins1_endpoint`, `mtcollins1_fan_credential_path`, `mtcollins1_fan_credential_path_env` … | `gunbc.fleet_converge_workflow` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_kvm_still` | 1180 | 43 | — | `gunbc.ci_spec`, `gunbc.fleet_converge_workflow`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle`, `gunbc.machine_intake_mtcollins1_boot_dry_realization`, `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_kvm_observer_observe` · 6 witness | no | -| `gunbc.machine_intake_mtcollins1_sol_notice` | 438 | 52 | `mtcollins1_boot_sol_capture_path_env`, `mtcollins1_boot_sol_pid_path_env`, `mtcollins1_sol_client_diagnostic_path`, `mtcollins1_sol_delivery_suffix`, `mtcollins1_sol_incident_path` … | `gunbc.ci_spec` · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_ui_bundle_observe` | 364 | 61 | `mtcollins1_boot_ipmi_kill_after`, `mtcollins1_boot_ipmi_read_deadline`, `mtcollins1_endpoint`, `mtcollins1_fan_credential_path`, `mtcollins1_fan_credential_path_env` … | `gunbc.fleet_converge_workflow` · 1 witness | no | -| `gunbc.machine_intake_sol_hold` | 344 | 17 | — | `gunbc.ci_spec`, `gunbc.machine_intake_mtcollins1_boot_dry_realization`, `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_sol_notice`, `gunbc.owned_process` · 2 witness | no | - -### Layer 2 — platform (16 modules) - -| module | lines | refs | unit/platform constants imported | consumers (import census) | gate | -|---|---|---|---|---|---| -| `extdeps.ampere.mt_collins_1u_product_brief.specifications` | 228 | 23 | `MtCollins1UPublicProductBrief`, `mt_collins_1u_brief_authority`, `mt_collins_1u_public_product_brief` | `gunbc.machine_intake_mtcollins1_expansion_attachment`, `gunbc.machine_intake_mtcollins1_expansion_candidates`, `gunbc.machine_intake_mtcollins1_expansion_observation` · 2 witness | no | -| `extdeps.ampere.mt_collins_1u_product_brief.subject` | 102 | 8 | — | `extdeps.ampere.mt_collins_1u_product_brief.specifications` | no | -| `extdeps.ampere.mt_collins_2u_user_guide.subject` | 68 | 7 | — | `gunbc.mt_collins_dimm_physical_identity`, `gunbc.specification_citation_read_provenance` · 1 witness | no | -| `extdeps.ampere.mt_collins_getting_started_guide.dimm_layout` | 92 | 32 | `mt_collins_gsg_authority`, `mt_collins_gsg_model_scope` | `gunbc.machine_intake_mtcollins1_dimm_connector_observation`, `gunbc.machine_intake_mtcollins1_physical_orientation`, `gunbc.machine_intake_mtcollins1_socket1_investigation_observation`, `gunbc.mt_collins_dimm_physical_identity`, `gunbc.mtcollins_memory_placement` · 3 witness | no | -| `extdeps.ampere.mt_collins_getting_started_guide.subject` | 92 | 3 | — | `extdeps.ampere.mt_collins_getting_started_guide.dimm_layout`, `extdeps.ampere.mt_collins_product_brief.memory_population`, `extdeps.ampere.mt_collins_product_brief.platform` · 1 witness | no | -| `extdeps.ampere.mt_collins_product_brief.bmc` | 59 | 5 | `MtCollinsPublicProductBrief`, `mt_collins_public_product_brief` | — | no | -| `extdeps.ampere.mt_collins_product_brief.memory_population` | 387 | 85 | `MtCollinsPublicProductBrief`, `mt_collins_gsg_authority`, `mt_collins_public_product_brief` | `gunbc.machine_intake_mtcollins1_dimm_connector_observation`, `gunbc.machine_intake_mtcollins1_physical_orientation`, `gunbc.machine_intake_mtcollins1_spare_screen_prediction`, `gunbc.machine_intake_pre_os_capture_replay`, `gunbc.memory_configuration_evaluation`, `gunbc.mt_collins_dimm_physical_identity` +3 more · 9 witness | no | -| `extdeps.ampere.mt_collins_product_brief.platform` | 102 | 12 | `MtCollinsPublicProductBrief`, `mt_collins_gsg_authority`, `mt_collins_public_product_brief` | `gunbc.memory_provisioning` · 1 witness | no | -| `extdeps.ampere.mt_collins_product_brief.subject` | 77 | 8 | — | `extdeps.ampere.mt_collins_product_brief.bmc`, `extdeps.ampere.mt_collins_product_brief.memory_population`, `extdeps.ampere.mt_collins_product_brief.platform` · 1 witness | no | -| `extdeps.boards.types` | 89 | 1 | — | `extdeps.bmc.access_profile`, `extdeps.boards.asrock_rack`, `extdeps.boards.gigabyte`, `extdeps.cooling.dynatron`, `extdeps.cooling.types`, `extdeps.firmware.types` +13 more · 6 witness | **yes** | -| `gunbc.machine_intake_mtcollins1_dimm_connector_observation` | 105 | 22 | `MtCollinsDimmDiagramLegend`, `MtCollinsDimmPopulationIdentity`, `MtCollinsDimmPopulationRow`, `mt_collins_population_16_dimms` | `gunbc.machine_intake_mtcollins1_expansion_candidates`, `gunbc.machine_intake_mtcollins1_expansion_observation`, `gunbc.machine_intake_mtcollins1_physical_orientation` · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_physical_orientation` | 856 | 293 | `MtCollins1ChassisEnd`, `MtCollins1CpuRelease`, `MtCollins1DiagnosisHypothesis`, `MtCollins1FirmwareSocketSummary`, `MtCollins1HypothesisStanding` … | — · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_platform_observation` | 225 | 33 | `mtcollins1_unit_key_standing` | — · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_topology_goal` | 18 | 7 | `mtcollins1_nproc` | — · 1 witness | no | -| `gunbc.machine_intake_power_supply` | 25 | 1 | — | `gunbc.machine_intake_mtcollins1_platform_observation` · 1 witness | no | -| `gunbc.mt_collins_dimm_physical_identity` | 173 | 44 | `MtCollins2uMemoryTopologyCitation`, `MtCollinsChannelConnectorPair`, `MtCollinsChassisFigureOrientation`, `MtCollinsDimmDiagramLegend`, `MtCollinsDimmFigureBank` … | `gunbc.machine_intake_mtcollins1_dimm_connector_observation`, `gunbc.machine_intake_mtcollins1_physical_orientation` · 1 witness | no | - -### Layer 1 — unit (17 modules) - -| module | lines | refs | unit/platform constants imported | consumers (import census) | gate | -|---|---|---|---|---|---| -| `gunbc.machine_intake_mtcollins1_32dimm_bringup_observation` | 695 | 114 | `AmpereMtCollins`, `mtcollins1_firmware`, `mtcollins_training_capture_path`, `mtcollins_training_capture_sha256` | `gunbc.machine_intake_mtcollins1_sixteen_module_restore_observation` · 3 witness | no | -| `gunbc.machine_intake_mtcollins1_access_observation` | 294 | 45 | `AmpereMtCollins` | `gunbc.host_reset_subject_roster`, `gunbc.machine_intake_mtcollins1_32dimm_bringup_observation`, `gunbc.machine_intake_mtcollins1_actuate`, `gunbc.machine_intake_mtcollins1_bmc_secure_observation`, `gunbc.machine_intake_mtcollins1_boot_authorization`, `gunbc.machine_intake_mtcollins1_boot_diagnostic_bundle` +8 more · 3 witness | no | -| `gunbc.machine_intake_mtcollins1_connectx4lx_observation` | 108 | 19 | `operator_host_mtcollins1` | `gunbc.machine_intake_mtcollins1_expansion_candidates` · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_expansion_attachment` | 112 | 6 | `MtCollinsUnitExpansionEvidence`, `mt_collins_1u_ocp_slot` | `gunbc.machine_intake_mtcollins1_expansion_candidates` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_expansion_candidates` | 87 | 8 | `mt_collins_1u_ocp_slot` | — · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_expansion_observation` | 186 | 18 | `MtCollins1UExpansion`, `mt_collins_1u_expansion`, `mt_collins_1u_ocp_slot` | `gunbc.machine_intake_mtcollins1_expansion_attachment`, `gunbc.machine_intake_mtcollins1_expansion_candidates` · 2 witness | no | -| `gunbc.machine_intake_mtcollins1_memory_prediction` | 156 | 8 | — | — · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_sixteen_module_restore_observation` | 304 | 44 | `mtcollins1_pre_os_capabilities`, `mtcollins1_sol_idle_capture`, `mtcollins1_sol_reading`, `mtcollins1_sol_session_banner_digest` | — · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_socket1_investigation_observation` | 389 | 139 | `MtCollinsDimmFigure` | `gunbc.machine_intake_mtcollins1_physical_orientation` · 1 witness | no | -| `gunbc.machine_intake_mtcollins1_spare_screen_prediction` | 247 | 16 | `MtCollinsDimmPopulationRow`, `mt_collins_population_16_dimms` | — · 1 witness | no | -| `gunbc.memory_configuration_evaluation` | 396 | 11 | `AmpereMtCollins`, `MtCollinsChannelPopulationRoleStanding`, `MtCollinsPopulationRoleRefusal`, `mt_collins_channel_population_roles`, `mt_collins_socket_dimm_sizes_supported` … | `gunbc.memory_change_evaluation`, `gunbc.memory_change_report` · 2 witness | no | -| `gunbc.mtcollins_3ds_memory_change` | 132 | 59 | `MtCollinsChannelPopulationRole`, `MtCollinsPopulationRoleRefusal`, `mt_collins_channel_population_roles`, `mtcollins_dram_cycle_start`, `mtcollins_hynix_part` … | `gunbc.machine_intake_pre_os_capture_replay` · 1 witness | no | -| `gunbc.mtcollins_memory_change` | 197 | 57 | `AmpereMtCollins`, `MtCollinsChannelPopulationRole`, `MtCollinsPopulationRoleRefusal`, `mt_collins_channel_population_roles`, `mt_collins_socket0_channel_pairs` … | `gunbc.machine_intake_mtcollins1_32dimm_bringup_observation`, `gunbc.machine_intake_pre_os_capture_replay`, `gunbc.machine_intake_pre_os_timeline`, `gunbc.mtcollins_3ds_memory_change` · 4 witness | no | -| `gunbc.mtcollins_memory_placement` | 22 | 4 | `MtCollinsChannelPopulationRole` | `gunbc.memory_configuration_evaluation`, `gunbc.mtcollins_memory_change` · 2 witness | no | -| `gunbc.rung_drop.mtcollins1_boot_accepts_socket1_absent` | 46 | 27 | — | `gunbc.rung_drop.roster` | no | -| `gunbc.rung_drop.mtcollins1_boot_matrix_enrolment_dead_band_observed_only` | 44 | 15 | `mtcollins1_boot_matrix_enrolment_dead_band_observed_only`, `mtcollins1_boot_matrix_native_witness_capability` | `gunbc.rung_drop.roster` | no | -| `gunbc.rung_drop.mtcollins1_boot_matrix_new_witness_eval_step_cost` | 58 | 11 | — | `gunbc.rung_drop.mtcollins1_boot_matrix_enrolment_dead_band_observed_only`, `gunbc.rung_drop.roster` | no | - -### Layer R — reference only (keeps prose/receipt; no layer move) (77 modules) - -| module | lines | refs | unit/platform constants imported | consumers (import census) | gate | -|---|---|---|---|---|---| -| `extdeps.linux.edac` | 214 | 1 | — | `gunbc.machine_intake_mtcollins1_memory_census_observation` · 2 witness | no | -| `extdeps.linux.mlx5_core` | 33 | 1 | — | `gunbc.machine_intake_mtcollins1_connectx4lx_observation` · 1 witness | no | -| `extdeps.network.dell_0r887v` | 23 | 1 | — | `gunbc.machine_intake_mtcollins1_expansion_candidates` | no | -| `extdeps.network.ibm_01ft753` | 40 | 1 | — | `gunbc.machine_intake_mtcollins1_expansion_candidates` | no | -| `extdeps.network.mcx4121a_acat` | 53 | 1 | — | `gunbc.machine_intake_mtcollins1_connectx4lx_observation`, `gunbc.machine_intake_mtcollins1_expansion_candidates` | no | -| `extdeps.network.mcx4411a_acan` | 24 | 1 | — | `gunbc.machine_intake_mtcollins1_expansion_candidates` | no | -| `extdeps.network.mcx4421a_acan` | 24 | 1 | — | `gunbc.machine_intake_mtcollins1_expansion_candidates` | no | -| `extdeps.network.mcx4621a_acab` | 24 | 1 | — | `gunbc.machine_intake_mtcollins1_expansion_candidates` | no | -| `extdeps.standards.nic_attachment` | 62 | 1 | — | `extdeps.ampere.mt_collins_1u_product_brief.specifications`, `extdeps.network.dell_0r887v`, `extdeps.network.ibm_01ft753`, `extdeps.network.mcx4121a_acat`, `extdeps.network.mcx4411a_acan`, `extdeps.network.mcx4421a_acan` +6 more · 3 witness | no | -| `gunbc.ci_layer_roots` | 2236 | 3 | — | `dag.test.claim.offline_local_recipe_witness`, `gunbc.ci_spec`, `gunbc.commit_workflow`, `gunbc.contributor_onboarding_path`, `gunbc.decoder_execution_identity`, `gunbc.derived_obligation_row` +34 more · 9 witness | **yes** | -| `gunbc.ci_spec` | 3825 | 194 | `mtcollins1_bmc_gunbc_secret_ref`, `mtcollins1_boot_sol_capture_path_env`, `mtcollins1_boot_sol_pid_path_env`, `mtcollins1_census_image_receipt_path`, `mtcollins1_fan_credential_path_env` … | `gunbc.compiler_gate_workflow`, `gunbc.fleet_converge_workflow`, `gunbc.fleet_release_bins_key`, `gunbc.fleet_workflow_steps`, `gunbc.generated_artifact_emit`, `gunbc.heal_publisher_workflow` +10 more · 10 witness | no | -| `gunbc.doc_graph_roots` | 1757 | 3 | — | `v2.test.lens_doc_reachability.doc_reachability_test` · 3 witness | no | -| `gunbc.durable_cas_file_store` | 856 | 1 | — | `gunbc.auth.approval_assertion_counter`, `gunbc.auth.approval_decision_store`, `gunbc.auth.approval_device_redemption`, `gunbc.auth.approval_keyring_converge`, `gunbc.auth.approval_store_receipt`, `gunbc.authorization_claim_slot` +9 more · 8 witness | **yes** | -| `gunbc.durable_exclusive_hold_file_store` | 369 | 1 | — | `gunbc.fabric_control_plane`, `gunbc.fabric_required_build_cell`, `gunbc.machine_intake_mtcollins1_maintenance_hold`, `tools.fabric_control_plane_live_probe` · 7 witness | no | -| `gunbc.fleet_intent_network` | 461 | 3 | — | `gunbc.approve_ios_project`, `gunbc.auth.approval_broker_endpoint`, `gunbc.auth.approval_device_enrolment_code_issue`, `gunbc.auth.approval_keyring_converge`, `gunbc.auth.approval_ntfy_deployment`, `gunbc.auth.approval_request_submission` +90 more · 101 witness | **yes** | -| `gunbc.floor_demand` | 1870 | 1 | — | `gunbc.ci_floor_measurement`, `gunbc.fabric_control_plane`, `gunbc.fabric_floor_dispatch`, `gunbc.fabric_witness_run`, `gunbc.floor_cold_build_receipt`, `gunbc.floor_memory_demand` +5 more · 8 witness | **yes** | -| `gunbc.guarantee_stall.pci_address_rendering_equivalence_stall` | 17 | 2 | — | `gunbc.guarantee_stall.roster` | no | -| `gunbc.non_fold_residue` | 2029 | 55 | — | — · 1 witness | no | -| `gunbc.owned_process` | 175 | 1 | — | `gunbc.bmc_megarac_web_transport`, `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_kvm_observer_observe`, `gunbc.machine_intake_mtcollins1_kvm_still` · 3 witness | no | -| `gunbc.recurring_failure_mode.a_derivation_key_that_does_not_pin_bytes_across_build_hosts` | 26 | 4 | — | — | no | -| `gunbc.recurring_failure_mode.a_pending_member_vanishes_from_a_terminal_refusal` | 28 | 3 | — | — | no | -| `gunbc.recurring_failure_mode.a_termination_contract_is_met_by_a_trimming_transport` | 51 | 5 | — | — | no | -| `gunbc.recurring_failure_mode.absent_reads_identically_to_never_looked` | 70 | 3 | — | — | no | -| `gunbc.recurring_failure_mode.ad_hoc_hardware_probe_produces_no_receipt` | 26 | 1 | — | — | no | -| `gunbc.recurring_failure_mode.an_unspawnable_program_aborts_the_run_instead_of_refusing` | 22 | 2 | — | — | no | -| `gunbc.recurring_failure_mode.collector_armed_after_the_transition_it_observes` | 14 | 2 | — | — | no | -| `gunbc.recurring_failure_mode.control_plane_acknowledgement_minted_as_effect` | 14 | 1 | — | — | no | -| `gunbc.recurring_failure_mode.else_less_if_statement_has_no_lowered_form` | 28 | 2 | — | — | no | -| `gunbc.recurring_failure_mode.empirical_association_promoted_to_decode` | 14 | 1 | — | — | no | -| `gunbc.recurring_failure_mode.enforcement_gate_positioned_after_the_effect_it_guards` | 15 | 1 | — | — | no | -| `gunbc.recurring_failure_mode.enrolment_dead_band_has_no_representable_standing` | 17 | 2 | — | — | no | -| `gunbc.recurring_failure_mode.gunbc_run_pays_a_large_fixed_pre_entry_cost_per_invocation` | 23 | 4 | — | — | no | -| `gunbc.recurring_failure_mode.identity_hashed_from_a_shared_mutable_path` | 44 | 2 | — | — | no | -| `gunbc.recurring_failure_mode.rest_response_nested_from_key_ignored` | 32 | 2 | — | — | no | -| `gunbc.recurring_failure_mode.subject_and_its_digest_as_independent_parameters` | 22 | 2 | — | — | no | -| `gunbc.recurring_failure_mode.the_checker_admits_a_cast_the_evaluator_refuses` | 17 | 2 | — | — | no | -| `gunbc.recurring_failure_mode.unbound_interpolation_in_service_argv_accepted_until_runtime` | 22 | 1 | — | — | no | -| `gunbc.recurring_failure_mode.upstream_non_endorsement_consumed_as_physical_prohibition` | 17 | 3 | — | — | no | -| `gunbc.recurring_failure_mode.wet_witness_keyed_to_the_runner_not_its_subject` | 28 | 1 | — | — | no | -| `gunbc.roadmap_authority` | 5415 | 4 | — | `gunbc.floor_non_verdict_enrollment`, `gunbc.generated_artifact_emit`, `gunbc.roadmap_belt`, `gunbc.roadmap_belt_actuate`, `gunbc.roadmap_closing_contract_authoring`, `gunbc.roadmap_forecast` +11 more · 15 witness | **yes** | -| `gunbc.rung_drop.roster` | 240 | 9 | `mtcollins1_boot_accepts_socket1_absent`, `mtcollins1_boot_matrix_enrolment_dead_band_observed_only_drop`, `mtcollins1_boot_matrix_new_witness_eval_step_cost` | `gunbc.design_ledgers`, `gunbc.jurisdiction_drop_accounting`, `gunbc.ledger_row_coherence`, `v2.workflow.floor_subject_seed_test` · 5 witness | no | -| `gunbc.runner.runner_bound_attempt_receipt` | 184 | 4 | — | — | no | -| `gunbc.runner.runner_cgroup_placement_receipt` | 169 | 1 | — | — | no | -| `gunbc.runner.runner_checkout_receipt` | 109 | 1 | — | — | no | -| `gunbc.runner.runner_first_job_receipt` | 237 | 3 | — | — | no | -| `gunbc.runner.runner_guest_egress_attempt` | 209 | 1 | — | `gunbc.runner.runner_cgroup_placement_receipt`, `gunbc.runner.runner_cpu_bandwidth_receipt` | no | -| `gunbc.runner.runner_guest_network_receipt` | 186 | 1 | — | — | no | -| `gunbc.runner.runner_host_first_boot_receipt` | 127 | 5 | — | `gunbc.machine_intake_pre_os_capture_replay` · 2 witness | no | -| `gunbc.runner.runner_host_hardware_observation` | 216 | 9 | — | — · 1 witness | no | -| `gunbc.runner.runner_host_jailed_kvm_receipt` | 200 | 2 | — | — | no | -| `gunbc.runner.runner_host_kvm_probe_receipt` | 122 | 5 | — | — | no | -| `gunbc.runner.runner_host_media_independence_receipt` | 137 | 10 | — | — | no | -| `gunbc.runner.runner_isolation_admission` | 292 | 2 | — | — · 1 witness | no | -| `gunbc.runner.runner_live_census_receipt` | 185 | 1 | — | — | no | -| `gunbc.runner.runner_observed_version_check` | 129 | 3 | — | — | no | -| `gunbc.runner.runner_qualification_receipt` | 177 | 4 | — | — | no | -| `gunbc.runner.runner_signed_envelope_receipt` | 114 | 3 | — | — | no | -| `gunbc.runner.runner_two_attempt_receipt` | 180 | 3 | — | — | no | -| `gunbc.runner_attempt_launch` | 1044 | 1 | — | `gunbc.runner_microvm_lifecycle`, `gunbc.runner_microvm_lifecycle_realize`, `gunbc.runner_microvm_slot_controller`, `gunbc.test.claim.runner.runner_microvm_lifecycle_witness_test` · 2 witness | no | -| `gunbc.runner_browser_toolchain` | 1381 | 3 | — | `gunbc.machine_intake_mtcollins1_boot_run`, `gunbc.machine_intake_mtcollins1_kvm_observer_observe`, `gunbc.machine_intake_mtcollins1_kvm_still`, `gunbc.wet_host_premise_readback` · 8 witness | no | -| `gunbc.runner_host_grants` | 479 | 5 | `mtcollins1_unit_hold_store_host` | `gunbc.fleet_converge_plan`, `gunbc.generated_artifact_emit`, `gunbc.host_credential_custody_converge`, `gunbc.runner_host_file_converge`, `gunbc.runner_microvm_network_apply`, `gunbc.runner_microvm_network_observe` · 4 witness | no | -| `gunbc.runner_jit_mint` | 159 | 1 | — | `gunbc.runner.runner_jit_perform`, `gunbc.runner_attempt_launch` · 1 witness | no | -| `gunbc.runner_registration_labels` | 94 | 1 | — | `gunbc.fleet_runner_connectivity`, `gunbc.runner_browser_toolchain`, `gunbc.runner_connectivity_recovery`, `gunbc.runner_unit_file` · 3 witness | **yes** | -| `gunbc.runner_throughput_qualification` | 1027 | 2 | — | `gunbc.runner_throughput_qualification_route`, `gunbc.runner_throughput_selection`, `product.fabric.node_qualification` · 1 witness | **yes** | -| `gunbc.runner_throughput_qualification_route` | 383 | 27 | `operator_host_mtcollins1` | — · 1 witness | no | -| `gunbc.secret_provision` | 603 | 6 | — | `gunbc.auth.approval_decision_store`, `gunbc.auth.approval_device_redemption`, `gunbc.auth.approval_mac_key_provision`, `gunbc.auth.approval_ntfy_deployment`, `gunbc.auth.approval_request_submission`, `gunbc.auth.approval_store_receipt` +13 more · 8 witness | **yes** | -| `gunbc.spark.pair_serving_d0_door` | 355 | 1 | — | `gunbc.fleet_converge_workflow` · 3 witness | no | -| `tools.approval_store_live_probe` | 236 | 1 | — | — | no | -| `v2.compiler.reference_conservation_census` | 816 | 1 | — | `v2.test.claim.namespace_xl0.reference_conservation_census` | no | -| `v2.test.floor_enrolment_margin` | 717 | 4 | `mtcollins1_boot_matrix_enrolment_dead_band_observed_only` | — | no | -| `v2.workflow.floor_cost_debt_admission` | 155 | 12 | — | `v2.workflow.floor_enrolment_margin` | no | -| `v2.workflow.floor_enrolment_dead_band` | 97 | 15 | — | `gunbc.rung_drop.mtcollins1_boot_matrix_enrolment_dead_band_observed_only`, `v2.test.floor_enrolment_margin`, `v2.workflow.floor_enrolment_margin` | no | -| `v2.workflow.floor_enrolment_margin` | 649 | 1 | — | `v2.test.floor_enrolment_margin` · 1 witness | no | -| `v2.workflow.floor_eval_step_cost_drop` | 416 | 31 | — | `gunbc.rung_drop.app_attest_interpreted_crypto_new_witness_eval_step_cost`, `gunbc.rung_drop.app_attest_verifier_new_witness_eval_step_cost`, `gunbc.rung_drop.dag_emit_round_trip_new_witness_eval_step_cost`, `gunbc.rung_drop.fleet_build_job_membership_new_witness_eval_step_cost`, `gunbc.rung_drop.live_deploy_apply_render_new_witness_eval_step_cost`, `gunbc.rung_drop.live_deploy_dark_install_render_new_witness_eval_step_cost` +7 more · 1 witness | no | -| `v2.workflow.floor_route_gap` | 1999 | 24 | — | `gunbc.native_frontier_ratchet`, `gunbc.witness_v2_native_route`, `v2.test.claim.local_repo_wet_terminal_test`, `v2.workflow.floor_terminal_ledger`, `v2.workflow.floor_terminal_ledger_wire`, `v2.workflow.floor_terminal_ledger_wire_test` +1 more · 1 witness | **yes** | -| `v2.workflow.floor_unimported_bare_provider_debt_roster` | 2209 | 7 | — | `v2.test.claim.floor_unimported_bare_provider_debt`, `v2.workflow.floor_unimported_bare_provider_debt` | no | -| `v2.workflow.local_repo_wet_terminal` | 2562 | 55 | — | `gunbc.wet_host_premise_readback`, `v2.test.claim.local_repo_wet_terminal_test`, `v2.workflow.required_floor`, `v2.workflow.witness_admission` · 1 witness | no | - -### Witnesses (81 modules) - -Each witness moves with the subject it imports; none is in the required gate except `test.claim.action_use_admission_witness` (3 prose refs only). - -`test.claim.action_use_admission_witness`, `test.claim.approval_request_submission_witness_test`, `test.claim.authorization_pattern_selection_witness`, `test.claim.boot_artifact_delivery_witness_test`, `test.claim.build_fulfillment_witness`, `test.claim.build_selection_witness`, `test.claim.durable_exclusive_hold_witness`, `test.claim.fan_policy_standing_witness`, `test.claim.fleet.fleet_converge_checkout_pin_witness_test`, `test.claim.fleet.site_pxe_edge_converge_witness`, `test.claim.gcp_secret_access_witness_test`, `test.claim.heal_publisher_provision_witness`, `test.claim.host_memory_qualification_witness_test`, `test.claim.host_reset_return_witness_test`, `test.claim.install_media_remaster_grub_cmdline_witness`, `test.claim.linux_edac_topology_authority_witness`, `test.claim.machine_intake.ampere_dram_console_observation_witness_test`, `test.claim.machine_intake.ampere_socket_console_observation_witness_test`, `test.claim.machine_intake.host_capture_sol_crlf_witness_test`, `test.claim.machine_intake.host_capture_sol_nul_fill_witness_test`, `test.claim.machine_intake.host_resource_observation_witness`, `test.claim.machine_intake.kernel_module_decompression_observation_witness_test`, `test.claim.machine_intake.megarac_media_convergence_witness_test`, `test.claim.machine_intake.megarac_ui_features_witness_test`, `test.claim.machine_intake.mtcollins1_bmc_sensor_observation_witness_test`, `test.claim.machine_intake.mtcollins1_boot_acceptance_matrix_test`, `test.claim.machine_intake.mtcollins1_boot_authorization_witness_test`, `test.claim.machine_intake.mtcollins1_boot_diagnostic_bundle_witness_test`, `test.claim.machine_intake.mtcollins1_boot_phase_timing_witness_test`, `test.claim.machine_intake.mtcollins1_boot_run_witness_test`, `test.claim.machine_intake.mtcollins1_census_image_publish_witness_test`, `test.claim.machine_intake.mtcollins1_census_medium_readback_witness_test`, `test.claim.machine_intake.mtcollins1_census_member_readback_witness_test`, `test.claim.machine_intake.mtcollins1_census_qemu_host_observe_witness_test`, `test.claim.machine_intake.mtcollins1_census_refusal_hypotheses_witness_test`, `test.claim.machine_intake.mtcollins1_kvm_observer_observe_witness_test`, `test.claim.machine_intake.mtcollins1_kvm_observer_protocol_wet_witness`, `test.claim.machine_intake.mtcollins1_kvm_still_witness_test`, `test.claim.machine_intake.mtcollins1_maintenance_hold_witness_test`, `test.claim.machine_intake.mtcollins1_physical_orientation_witness`, `test.claim.machine_intake.mtcollins1_sixteen_module_restore_witness_test`, `test.claim.machine_intake.mtcollins1_smpro_observation_witness_test`, `test.claim.machine_intake.mtcollins1_stale_power_off_witness_test`, `test.claim.machine_intake.mtcollins1_ui_bundle_observe_witness_test`, `test.claim.machine_intake.mtcollins1_unit_hold_forged_probe_witness_test`, `test.claim.machine_intake.oob_power_action_witness`, `test.claim.machine_intake.pre_os_bringup_witness_test`, `test.claim.machine_intake.pre_os_sol_crlf_witness_test`, `test.claim.machine_intake.sol_hold_stdin_wet_witness`, `test.claim.machine_intake_ethernet_witness`, `test.claim.machine_intake_mtcollins1_bmc_secure_standing_witness_test`, `test.claim.machine_intake_predictive_claim_witness_test`, `test.claim.megarac_spx_ui_surface_artifact_integrity_witness`, `test.claim.memory_change_evaluation_witness`, `test.claim.memory_multiple_findings_witness`, `test.claim.micron_dram_module`, `test.claim.modeled_filesystem_witness_test`, `test.claim.mt_collins_dimm_physical_identity_witness`, `test.claim.mt_collins_population_role_witness`, `test.claim.mtcollins1_candidate_attachment_witness_test`, `test.claim.mtcollins1_census_image`, `test.claim.mtcollins1_census_image_local_wet`, `test.claim.mtcollins1_connectx4lx_observation_witness_test`, `test.claim.mtcollins1_expansion_witness_test`, `test.claim.mtcollins1_memory_census_witness_test`, `test.claim.mtjade1_access_witness`, `test.claim.nbd_proxy_virtual_media_install`, `test.claim.pci_identity_observation_witness`, `test.claim.pre_os_capture_replay_witness`, `test.claim.pre_os_observer_witness`, `test.claim.pre_os_replay_witness`, `test.claim.product.cohort_observation_witness`, `test.claim.refinement_cast_wall_witness_test`, `test.claim.runner_isolation_admission_witness`, `test.claim.runner_throughput_qualification_witness`, `test.claim.site_uefi_arm64_pxe_edge_witness`, `test.claim.srv3_os_install_actuate`, `test.claim.unit_hold_store_provision_witness`, `test.claim.workflow_dispatch_input_witness`, `test.claim.xorriso_path_list_witness`, `test.probe.unit_hold_proof_forged_probe` diff --git a/docs/plans/micron-mta36ads2g72pz-identification.md b/docs/plans/micron-mta36ads2g72pz-identification.md deleted file mode 100644 index ee15c525fa7..00000000000 --- a/docs/plans/micron-mta36ads2g72pz-identification.md +++ /dev/null @@ -1,209 +0,0 @@ -# MTA36ADS2G72PZ-2G1A1: what Micron says it is, and what the Ampere documents say about it - -Survey date: 2026-09-12. Lane: clever-heron-234, work item `node://adhoc-677e2a53-ae7`. -Landed authority: `extdeps.memory.micron` (rows `mta36ads2g72pz_2g1a1_catalog`, -`mta36asf2g72pz_2g1a2_catalog`), `extdeps.vendor.micron`, and the third value of -`extdeps.memory.types` `DramDieStacking`. This document carries the reading; the rows carry the -facts. Nothing observed on Mt. Collins unit 1 is authored into `extdeps`. - -## Verdict, in the brief's order - -1. **MTA36ADS2G72PZ-2G1A1 is a 16 GB, 2-rank, x4, DDR4-2133 CL15, ECC, registered, - VERY-LOW-PROFILE (18.75 mm) RDIMM built from 18 DUAL-DIE packages of two 4 Gb dies each.** - Every word of that is Micron's, from two documents (part-numbering decode and the SPD image - Micron publishes for the part), and the two agree at byte grain. Status: obsolete. -2. **ADS versus ASF is `DS` = "VLP Dual-Die w/Temp Sensor" versus `SF` = "FBGA w/Temp - Sensor".** Micron's DDR4 Module Part Numbering System (Rev. 05-Dec-2018) defines the - module-options field as a two-letter code where the first letter is the PCB height class - (`S` standard, `D` VLP, `H` 2U, `L` 4U) and the second the DRAM package (`F` FBGA - single-die, `S` dual-die, `Q` quad-die, `E` octal-die). The letter difference IS the answer: - the ADS part is the VLP dual-die build of the same 36-die, 2 Gig x 72, RDIMM, -2G1 family. -3. **"2DRx4" reads as "2 ranks, dual-die, x4", and that is verified, not adopted.** The - operator's hypothesis was that a VLP module needs dual-die packages to reach 16 GB with x4 - devices in 18.75 mm. Micron's SPD byte 6 for the part is `0x91`: non-monolithic, 2 dies, - MULTI LOAD STACK, i.e. the JEDEC class a DDP belongs to. Byte 128 (`0x04`, nominal height - ≤ 19 mm) confirms the VLP outline. The catalogue's "Component Config: 2Gb x4" names the - 8 Gb dual-die package; "Number of Components: 36" counts dies, matching the `36` in the - part number, whose field the numbering sheet labels "Number of Die". -4. **Ampere publishes nothing that admits or refuses a dual-die (multi-load stack) RDIMM.** - Altra Rev A1 Datasheet Issue 1.55 §2.10 lists "RDIMMs, LRDIMMs, and Stacked (3DS) RDIMMs" - and "8 Gb and 16 Gb DRAM devices"; the words DDP, dual-die, multi-load and 4 Gb do not - appear in the document. Neither does the Mt. Collins GSG, the OCP Mt. Jade spec, or the - AVL say anything about package construction. The OCP Table 3 "Different DRAM DIES" = Yes - cells govern MIXING die types across modules; they do not say a given die construction is - supported, and this survey did not let them. Note also that the datasheet's device list - (8 Gb, 16 Gb) does not name the 4 Gb die that BOTH Micron quartets use, and the monolithic - quartet trained; so "unlisted device density" is a fact about both quartets and cannot be - what separates them. The corpus now models the DDP class as *not among the listed classes* - (`altra_admits_module_class` answers false for Registered × DualDiePackage) and the - controller module's own prose keeps that distinct from a withdrawal such as UDIMM's. -5. **Neither part is on the Ampere Altra Family AVL Issue 1.15 (December 2025).** Table 3's - Micron rows are MTA18ASF2G72PDZ (16 GB 2Rx8), MTA18ASF2G72PZ-2G9E1 (16 GB 1Rx4), - MTA36ASF4G72PZ (32 GB 2Rx4, three suffixes) and MTA36ASF8G72PZ-3G2E1 (64 GB 2Rx4) — all - `SF`, all 8 Gb/16 Gb dies, all 2666 or faster. There is NO 16 GB 2Rx4 row from any vendor - and no 2133 row at all, so the AVL is equally silent about the ADS part and the ASF part - that trained. An AVL omission is not a prohibition and this survey does not read it as one. - -**Firmware corroboration, received from hw-first-host after this survey was drafted (SOL capture -`artifacts/bmc/mtcollins1-sol-dram-training-2026-09-12.txt`, sha256 `e5044f71…8a4c`, on that -lane's branch).** The Altra DRAM-init firmware prints the ADS part as -`RDIMM[2c:80] 16GB 2133 ECC 2R x4 RCD[b3:80] 36ADS2G72PZ-2G1A1` — rank count 2, exactly as -Micron's byte 12 says, the `D` being a package letter and not a rank — and refuses the mixed -socket with `ERR: CHANNEL Mismatch Byte 6 SLOT0[00] EXP[91] @MCU[4]` followed by -`ERROR: Non-identical DIMM mixture NOT supported!`. Two things follow, both about the observing -layer and neither authored into `extdeps`: (a) the module's SPD byte 6 reads `0x91` on the -board, agreeing with Micron's published image; (b) what the firmware enforces on that socket is -**byte-6 agreement across the socket's channels**, i.e. a MIXING rule, so the failure with -4 × ADS + 12 monolithic is a mixed-population refusal and says nothing about a uniform ADS -population, which has not been run. The trained quartet's label reads `36ASF2G72PZ-2G1A2II`, -matching the catalogue SKU carried here plus process code `II`. The firmware's `RCD[b3:80]` / -`RCD[32:86]` per-module variation is the same variation Micron's record carries across process -codes (bytes 133–135), which is why register vendor is not a field of the catalogue row. - -**Which of the three differing axes is operative — read carefully, because three bytes differ -and only one is named.** The refusal quotes byte 6. Bytes 128 (height) and 130 (raw card) -differ between the two parts just as much and are not mentioned. The strongest honest reading: -the axis the firmware is known to compare is package type / die count / signal loading — the -byte `DualDiePackage` is grounded on — and VLP height and raw card are not shown to matter. -What the message does NOT establish is that they are unchecked: a comparator that stops at its -first mismatch would print byte 6 whether or not it also compares 128 or 130 later in its -order. So "VLP is a passenger" is the reading the evidence favours, at inference strength; -proving it needs a population that differs at 128/130 but agrees at 6 (an `SS`/`DF`-coded part -against a monolithic full-height one), which has not been run. - -**What Micron's data leaves as the only differences between the two quartets:** package -construction (dual-die multi-load stack vs monolithic), PCB outline (18.75 vs 31.25 mm) and, with -the outline, the JEDEC raw-card reference design (SPD byte 130 `0x08` vs `0x01`) and the -module-attribute byte 131 (`0x05` vs `0x09`). Everything else — vendor, generation, capacity, -speed bin, CAS latency, ECC, buffering, device width, package-rank count, die density, DRAM -addressing — is byte-identical between the two SPD images. Which of those the Mt. Collins -training outcome tracks is not decided by any document read here; the firmware line above -names byte 6 directly, and that receipt belongs with the lane that ran the crossovers. - -## Documents read - -| Document | Revision | Locator | Digest / standing | What it establishes | -|---|---|---|---|---| -| Micron part catalogue record, MTA36ADS2G72PZ-2G1A1 | database record fetched 2026-09-12 | `www.micron.com/products/obsolete/obsolete-vlp-rdimm/part-catalog/part-detail/mta36ads2g72pz-2g1a1` (renders `…/_jcr_content.products.json/getproductinfo/-/-/-/en_US/-/mta36ads2g72pz-2g1a1`) | HTTP 200, no credentials, `"data-source": "DATABASE"`, `"sub-category": "obsolete-vlp-rdimm"` | Details table and full DDR4 SPD image (12 process codes JG/HK/JJ/JI/HG/HJ/II/HI/IG/IJ/IK/JK; they differ only at register-vendor bytes 133–135 and the bytes-128..253 CRC). | -| Micron part catalogue record, MTA36ASF2G72PZ-2G1A2 | same | `www.micron.com/products/obsolete/obsolete-rdimm/part-catalog/part-detail/mta36asf2g72pz-2g1a2` | HTTP 200 | The control's details table and SPD (process codes KI/KJ/KK/KG/II/IG/IJ/IK). The bare `mta36asf2g72pz-2g1` record answers with no process codes. | -| Micron DDR4 Module Part Numbering System | Rev. 05-Dec-2018 | `siewert-kau.info/micron/wp-content/uploads/2020/05/Nomenklatur_Micron.pdf` (distributor copy; `micron.com/numbering` → `assets.micron.com` answers "Request Rejected" to this session) | sha256 `7d52c2ad…5c6a2` | Field decode: family, die count, voltage, module options (`DS` = VLP Dual-Die w/Temp Sensor, `SF` = FBGA w/Temp Sensor), configuration, module type (`P` = 288-pin RDIMM), package code (`Z`), speed grade (`-2G1` = DDR4-2133, PC4-2133, 15-15-15, component -093E), die and PCB revision. | -| Micron 288-Pin DDR4 RDIMM Core Product Description | Rev. B 12/2021, CCM005-341111752-10541 | `media.digikey.com/pdf/Data%20Sheets/Micron%20Technology%20Inc%20PDFs/DDR4_SDRAM_RDIMM_Core_RevB_Dec2021.pdf` (DigiKey copy of Micron's PDF; Micron's own host rejects) | sha256 `ea054b3d…8abc` | Figure 1 RDIMM 31.25 ± 0.15 mm; Figure 2 VLP RDIMM 18.75 ± 0.15 mm; Table 12 module ↔ component speed grades; C0–C2 pin text distinguishing "a traditional DDP package, which uses CS1_n, CKE1, and ODT1 to control the second die" from single-load 4H/8H stacks. It says the per-part organization lives in the MPN-specific addendum. | -| Micron per-part addendum for MTA36ADS2G72PZ | — | **Not found.** Probed `…/data-sheet/modules/{vlp_rdimm,rdimm,parity_rdimm}/ads36a2gx72pz.pdf` and variants (404); search engines index no addendum for the ADS family; the catalogue page's download resource answers 404. | — | The SPD image is the strongest first-party organization statement reachable; the addendum would add placement and thermal figures, not organization. | -| Ampere Altra Rev A1 Datasheet | Issue 1.55, 2025-04-08 | `extdeps.cpu.ampere` `ampere_altra_rev_a1_datasheet_authority` (v1.55 locator) | 90 pages, read as text | §2.10 feature list; no DDP / dual-die / multi-load / 4 Gb text anywhere in the document. | -| Ampere Altra Family AVL | Issue 1.15, December 2025, AMP 2025-0116 | `extdeps.cpu.ampere` `ampere_altra_avl_authority` | sha256 `6199ae84…8d0d` (matches swift-bat-341's read) | Table 3 Micron rows listed above; no 16 GB 2Rx4 row; no 2133 row; §1.3 test suite is homogeneous 1DPC/2DPC. | -| OCP Mt. Jade Motherboard Spec | Rev 1.0 | `extdeps.ocp.mt_jade.memory_mixing` | already modelled | Table 3 is a MIXING matrix; "Different DRAM DIES" = Yes is not a construction-support statement. | -| swift-bat-341 survey | 2026-09-11 | [altra-cross-channel-rank-mixing-survey.md](altra-cross-channel-rank-mixing-survey.md) | — | Starting point for the AVL and the platform-guide reads; nothing there is contradicted here. | - -### Reading the two SPD images side by side - -| Byte | Meaning (JESD21-C Annex L) | ADS-2G1A1 | ASF-2G1A2 | -|---|---|---|---| -| 2 | DRAM device type | `0C` DDR4 | `0C` | -| 3 | Module type | `01` RDIMM | `01` | -| 4 | Density and banks | `84` 4 Gb/die, 4 BG × 4 banks | `84` | -| 5 | Addressing | `21` 16 rows, 10 columns | `21` | -| **6** | **SDRAM package type** | **`91` non-monolithic, 2 dies, multi-load stack** | **`00` monolithic** | -| 12 | Module organization | `08` 2 package ranks, x4 | `08` | -| 13 | Bus width | `0B` 64 + 8 ECC | `0B` | -| 18 / 125 | tCKmin / fine offset | `08` / `C2` → 938 ps (2133) | same | -| 24 | tAAmin | `6C` 13.5 ns → CL15 | same | -| **128** | **Nominal height** | **`04` ≤ 19 mm** | **`11` ≤ 32 mm** | -| 129 | Max thickness | `11` | `11` | -| **130** | **Raw card** | **`08`** | **`01`** | -| **131** | **RDIMM attributes** | **`05`** | **`09`** | -| 329–349 | Part number | `36ADS2G72PZ-2G1A1` | `36ASF2G72PZ-2G1A2` | - -Raw card letters and bytes 131/133–135 are recorded as bytes, not decoded: the JEDEC raw-card -letter table and the JEP106 register-vendor table are not authorities the corpus carries yet, -and reading them from memory would be exactly the uncited transcription §4d forbids. A JEDEC -SPD authority module (`extdeps.memory.jedec` currently carries only the generation ceilings) is -the named follow-up that would make the decode above typed rather than prose. - -## The OEM SEL family (second half) - -Payloads observed on Mt. Collins unit 1, all manufacturer `3a cd 00`, record type `C0`, raw -bytes preserved exactly as the brief carries them: - -``` -0fde7033ff10 0fde703bff10 2026-08-19, same timestamp, adjacent record IDs -0fde70330102 (x19) 0fde70330502 (x2) -0fde70331102 0fde703b1102 -``` - -### What is public - -The only public serializer of a `3a cd 00` / `C0` OEM SEL record with this byte shape is -Ampere's OpenBMC layer, `meta-ampere/meta-common/recipes-ampere/host/ac01-boot-progress/dimm_train_fail_log.sh` -in `github.com/ampere-openbmc/openbmc` (read at `f68588807957ffbc07371634ed41e534e7b416c2`, -2026-01-22; the script has three commits in its history and was created 2023-04-15 already -carrying the constants below — there is no earlier revision with a different sensor number). -It emits, via `IpmiSelAddOem` with OEM id `0x3a 0xcd 0x00` and SEL type bytes -`0x00 0x00 0x00 0xC0`: - -``` -byte 0 SENSOR_TYPE_SYSTEM_FW_PROGRESS = 0x0F -byte 1 SENSOR_BOOT_PROGRESS = 235 = 0xEB -byte 2 EVENT_DIR_ASSERTION | OEM_SENSOR_SPECIFIC = 0x00 | 0x70 -byte 3 (channel << 4) | (socket << 3) | BOOT_SYNDROME_DATA(=4) -byte 4 syndrome bits 15:8 (the SoC-BMC "Boot Stage Error Syndrome" byte 1) -byte 5 syndrome bits 7:0 (byte 0: [1:0] failure type, [4:2] physical rank, [7:5] syndrome 0) -``` - -The syndrome bytes it copies are defined by the Ampere Altra Family SoC-BMC Interface -Specification, Issue 1.43 (2023-05-30), Table 16 registers `0xB4` (slot select) and `0xB5` -(Boot Stage Error Syndrome): failure type 1 = PHY training, 2 = DIMM training; syndrome 0 for -PHY = setup / write leveling / read gate leveling / read leveling / software training; -syndrome 0 for DIMM = VREFDQ / LRDIMM DB / LRDIMM DB software; byte 1 for write leveling = -slice number and per-nibble "no rising edge" bits. The specification is a `connect-admin` -download that answered HTTP 200 without credentials (sha256 `34ccdcd2…`, 56 pages). The -companion "Altra Family Monitoring Events" v0.60 (2023-06-05, sha256 `23d84c2c…`) is a -Redfish message registry and defines no SEL byte layout. - -### What is not public, and therefore not decoded - -- **Sensor `0xde` (222) is not in any public Ampere source.** `ampere-openbmc/openbmc`, - `ampere-ipmi-oem`, and the Mt. Jade / Mt. Mitchell IPMI sensor YAMLs were searched; 222 and - 235 appear there only as ordinary temperature/current sensor numbers unrelated to boot - progress. Mt. Collins runs AMI MegaRAC (`extdeps.ampere.mt_collins_product_brief.bmc`), not - this OpenBMC layer, and AMI's Ampere port is not published. So the `0xde` serializer is - **not found**. -- **Byte 3's low nibble is `3` where the public schema writes `4`**, and the sensor number - differs, so the OpenBMC layout is a *related* schema and no byte of ours may be read through - it as a decode. Under that layout byte 3 = `0x33` would be channel 3, socket 0; `0x3b` - would be channel 3, socket 1 — which is consistent with the operator's inference that bit 3 - moved with the socket holding the intervened population across the two crossovers, and is - still an inference: the companions moved with it. -- **The tail is ambiguous even under the cousin schema**, and the ambiguity is why it is not - promoted. Reading `11 02` as `[syndrome hi][syndrome lo]` gives failure type 2 (DIMM - training), rank 0, syndrome0 = 0 with byte 1 = `0x11`; reading it as `[lo][hi]` gives failure - type 1 (PHY training), rank 4, syndrome0 = 0, byte 1 = `0x02` (write-leveling slice 2). The - two readings name different training steps and different ranks. Only a decode of register - `0xB5` on this platform, or the AMI serializer, settles it. The `ff 10` pair from 2026-08-19 - reads as failure type 3 (Reserved) under one ordering and failure type 0 (N/A — no failure) - under the other, neither of which is a syndrome a training-failure record should carry, which - is further evidence that the `0xde` family is not simply the `0xeb` family under another - sensor number. - -So the payloads stay raw. `gunbc.host_memory_qualification` `TrainingEvidenceStanding` already -has the right arms for what a receipt may claim: `AssociatedByIdenticalSignature` at best -(signature = the raw six bytes, source = the meta-ampere script as the related schema), never -`DecodedFromPlatformSyndrome`, until `0xB5` is read on the unit. - -## What this lane did not land, and why - -- **No `HostMemoryStanding` or `CausalExperimentReceipt` rows for the eight Mt. Collins - configurations.** The brief summarises them (durations, wattage, temperature, payloads) but - carries no instants, no DIMM masks, no slot locators; authoring receipts from a summary would - fabricate `observed_at` values and put prose in the mask field — the exact defect that module's - own notes record. The rows belong to the lane that holds the raw BMC log; they now have a - catalog row to bind to (`ObservedDimmCataloged { catalog: mta36ads2g72pz_2g1a1_catalog }` - reconciles, exercised by `test.claim.micron_dram_module`). -- **No typed SEL/OEM-record carrier.** One night's payloads under an unpublished schema are - not an upstream authority. A carrier is warranted when either the AMI serializer or a `0xB5` - read exists to ground it. -- **No `form_factor`/height field on `DramModuleCatalogRow`.** The VLP fact is carried in the - row's annotation and here; the field lands with its first consumer (a chassis DIMM-clearance - check), per §3c. -- **The Micron-hosted locators.** Both Micron PDFs are cited through distributor copies with - digests because `assets.micron.com` rejects this session's fetches; re-cite to `micron.com` - when reachable. The catalogue records ARE Micron-hosted. diff --git a/docs/plans/microvm-and-floor-wind-down-state.md b/docs/plans/microvm-and-floor-wind-down-state.md deleted file mode 100644 index 34dcc0dc7c1..00000000000 --- a/docs/plans/microvm-and-floor-wind-down-state.md +++ /dev/null @@ -1,310 +0,0 @@ -# microVM and floor: wind-down state, 2026-09-21 - -Written on operator instruction to wind down and record remaining items, so that v1 -performance and v2 migration can be prioritised. **This is a state record, not a plan**: it -says what is true on `origin/main`, what is owed, and what is blocked, so that resuming any -lane does not begin by re-deriving it. - -Every claim below was verified against `origin/main` at `7a145ef9ca` or read from the lane -that produced it. Where a claim is **contested**, it is marked, and neither version is -asserted — DESIGN §4d: a bet is typed as a bet, and the reader who consumes it as a fact is -the defect. - -**Consumer, and a declared frontier (DESIGN §3c).** This page is cited by path from the two -re-entry briefs that govern the parked programs, which is where a resuming session reads it. -It is **not** linked from `DESIGN.md`, and it deliberately does not add a -`gunbc.design_document` row to become so: it is a dated state record rather than a doctrine -a DESIGN section governs, and editing that authority is not a wind-down act. It therefore -joins the orphan population already rostered as `gunbc.guarantee_stall` -`doc_graph_orphan_population_stall` (`current: OutsideTheLadder`), and this paragraph is the -admission rather than a silent addition. **Trigger for the frontier:** whichever funded lane -resumes a program named here either links this page from the section that governs its work, -or supersedes it and deletes it. A wind-down record that outlives the wind-down is debt. - -**On the numbers below.** Where a figure is load-bearing it is stated with the producer that -re-derives it (DESIGN §6: name the instrument, never transcribe its output). Where a figure -is a lane's reported reading rather than a re-derivable instrument, it is attributed to that -lane and marked as such — those are receipts of a past run, not facts a reader may refresh, -and they are written so that staleness is visible rather than plausible. - ---- - -## 1. The floor reports SUCCESS over its own refusal — the highest-value open item - -`.github/workflows/witnesses.yml:67`: - -``` -if [ "$GUNBC_FLOOR_CLASS" = structural ] && [ "$GUNBC_FLOOR_EXIT" -eq 0 ]; then GUNBC_FLOOR_CLASS='none' -``` - -`claim_executor` **exits 0 over its own typed refusal**, so the class is downgraded to `none` -and the job concludes success. A job can therefore report SUCCESS over a run that executed -**zero witnesses**, with its own log saying `phases_failed=1`. - -**This is not merely a hidden defect — it admitted a new one.** #11731 landed a file that -could not parse *because* the floor was refusing and reporting success. The hollow gate is -upstream of the breakage, not downstream of it (§5: a failure arm must refuse, never widen; -the downgrade is an absorbing fallback executed in YAML). - -**Current standing.** The parse class is **closed**: the indented-annotation census is 0 -corpus-wide and the floor selects and executes its full planned set, with `not_attempted=0` -and `claims_failed=0`, where previously **zero** witnesses were selected. Closed by #11896, -#11897, #11880, #11920, #11842. **Re-derive rather than trust this sentence:** the producer -is the required floor itself — `claim_executor --required-ci --source-root dag ---source-root src/v2 --required-lane witnesses` — whose run prints its own -`planned/executed/not_attempted/terminal/passed` summary line. The counts move with every -landed witness, so a number copied here would rot within a day. - -**CONTESTED — do not propagate either reading.** Whether the downgrade swallows failures -*per phase* is disputed by two lanes: - -| reading | source | consequence if true | -|---|---|---| -| per-phase: parse reds the job honestly, declarations passes | `nimble-swift-273` | once parse cleared, a **declarations defect is invisible** | -| no such ordering | four run observations | the discriminator is unknown | - -The four observations: main `0de0b6137c7` 13 parse FAILs → **SUCCESS**; #11803 `70faed05501` -340 → **SUCCESS**; #11842 343 → **FAILURE**; #11884 317 → **FAILURE**. No ordering by count, -and parse failures did **not** always red the job. `eager-swift-412` independently reports -the discriminator as **cannot-tell from the logs**. - -**The discriminating test nobody has run**, and it is cheap: read `$GUNBC_FLOOR_LOG` — a -**file** the floor writes, *not* the job log, where every `class=` line is echoed script — or -the classifier itself, on **one PASS and one FAIL of the same phase shape**. Counting -signature strings in the GHA job log is **not** discriminating: a fired signature sets an env -var rather than printing, so identical counts appear whether the hypothesis holds or not. - -**#11836 and #11829 are competing repairs. Only one should land.** Neither is owned. - -## 2. microVM: the part that decides is built; the part that acts is not - -`gunbc.runner_microvm_lifecycle_realize` **landed** (#11803) and has executed against real -processes on srv1: the lane reported every claim group green at its landing head, including -the wet receipt under `--wet` and the argv-binding wall. That is a **lane receipt of a past -run**, not a standing fact — re-derive it with `claim_batch` over the module's witness -modules at the current head before relying on it. #11677 closed both launch frontiers, so -the JIT credential mint and jail staging are realized. - -**Five declared frontiers, all live in that one landed module.** Read them from the file, -not from here. - -| # | frontier | status | what is missing | -|---|---|---|---| -| 1 | `controller_main_pid_consumer_frontier` | **dispatchable, start here** | **nothing calls `run_controller`.** Until a slot unit execs it as MainPID the module is a library, not a controller. Every other frontier is worth less until this clears. | -| 2 | ~~`attempt_cleanup_realization_frontier`~~ | dissolved | `gunbc.runner_microvm_lifecycle_realize clean_attempt_resources` runs between the stop and the readbacks, only after a quiescent stop: it detaches every mount at or under the jail (deepest first, read from `/proc/self/mountinfo`), deletes the credential device, and removes the jail and attempt directory with `rm -rf --one-file-system`. It then runs the slot's own `runner_slot_teardown_flush_commands`. The existing absence readbacks and network lists stay the verdict, so an incomplete cleanup quarantines. | -| 3 | ~~`guest_bring_up_channel_frontier`~~ | dissolved (channel); live half re-declared | The controller binds the console at launch: the unit's Environment must name this attempt's invocation_id, and the unit's systemd InvocationID is captured then. After the terminal trigger it reads only that invocation's journal entries (`journalctl -u … _SYSTEMD_INVOCATION_ID=…`) and advances GuestBooted / RunnerListening through `advance_bring_up`. The receipt carries the phase. Reading it live, to decide SERVING while the attempt runs, is `slot_serving_live_readiness_frontier`; its consumer is the deferred warm pool. | -| 4 | `slot_network_readings_producer_frontier` | **BLOCKED — security** | no converged slot network on any host; every reading is `Unreadable`, which quarantines. | -| 5 | `workflow_effect_sequencing_frontier` | **not a lane — a design question** | `std` carries **no** effect-sequencing authority. Bigger than microVM: every effect sequence in the corpus rests on the answer. | - -**The consequence that governs planning:** the module states it itself — *"no cell settles -CellReady through this module today."* **A first full run quarantines by construction, and -that is not a bug in the readbacks.** A warm pool is impossible until (4) clears. - -> Do not let anyone "fix" (4) by omitting `SlotNetworkQuiescent` from the required facts. -> That is the empty-observation narrow `product.fabric.sanitation` exists to refuse. - -### Why (4) is blocked, stated so the reason survives - -**The converge principal IS the job principal.** Granting it install+restart over files it -also *stages* is granting root: `install /etc/systemd/system/…` -followed by `systemctl restart` is root-equivalence, however narrowly spelled. So -`microvm_network_granted_operations` deliberately carries **readbacks only**. - -To unblock, an administrator principal must (1) own the staged source so the job user cannot -write what root will install and execute; (2) install the nft loader unit and ruleset and -`daemon-reload` + restart it; (3) set `ip_forward` and create the bridge; (4) create and -destroy per-attempt taps — and be **unimpersonable by the job user**. That last is the whole -point, and is why the frontier trigger names a *capability* rather than an artifact (§4b(3)). - -#11751 (`microvm_network_observe`) landed. It **installs nothing**, reads every fact with its -own UNREAD arm, and mints `ConvergedNetwork` only when every fact was actually read. On every -host today it will **refuse**, because nothing has installed a tap or the table — that is -honest, not a failure. - -**It has now been run, and it refused exactly there.** fleet-converge run 35646413870, mode -`microvm_network_observe`, host srv1, 2026-09-21: the whole receipt is -`microvm-network-observe REFUSED: slot srv1-01: tap disable_ipv6 could not be read`. srv1 -carries no `gunbc-tap1`. That is the first end-to-end reading of the mode against a real host -and it establishes the mode works and the host is unconverged; it is **not** a -`ConvergedSlotNetwork` (that row stood until the converged run below). - -**The administrator principal has landed: `gunbc.runner_microvm_network_apply`.** It is the -apply half of (4), a fleet-converge mode of its own (`microvm_network_apply`, -`FleetSshKeyConsumed`), executing as `gunbc.fleet_bootstrap_principal` -`executor_bootstrap_principal` over the fleet SSH edge — the same edge -`gunbc.runner_host_file_converge` already uses for root-evaluated files. It refuses when that -administrator IS the job user on the host; it refuses unless the dispatch names the revision -the tree is checked out at, because the bytes it installs are rendered from that revision; it -verifies **every ancestor** of the staging root root-only far-side before staging a byte; it -writes each staged file `root:root 0600` through `converge_typed_remote_file` (stage, mode, -atomic rename, far-side content comparison); it verifies the staged files' own ownership; -then it runs the modeled install operations in order, stopping at the first failure, and -reads the ruleset back. The staging root **moved out of the job user's own tree** in the same -change — it was under `gunbc_fleet_converge_state_dir`, which `runner_host_grants` creates -with `owner=job`, which is precisely what the frontier says does not satisfy it. The job-user -grant roster is **unchanged**: the installs run under a different principal, and re-admitting -them to the job user's sudoers because someone else performs them would grant exactly the -pair the frontier refuses. - -**(4) has executed, and srv1's slot network is converged.** fleet-converge run 36043010087 -(`microvm_network_apply`, srv1, 2026-09-24) staged, installed and read the ruleset back under -the administrator principal (listing digest `da7a00c5…9099`); run 36045264008 -(`microvm_network_observe`, srv1) then minted a `ConvergedSlotNetwork` at generation -36045264008 carrying the same digest, fourteen taps with v6 disabled, and zero helpers. -`microvm_network_host_mutation_principal_frontier`, `host_ruleset_installer_frontier` and -`converged_slot_network_producer_frontier` were deleted on those receipts. - -**And the premise of this whole section is false on srv1 today, which is the finding that -lane produced.** `/etc/sudoers.d/gunbc-deploy` — GENERATED by `gunbc.ci_deploy_sudoers` from a -roster of bare binary paths — grants `ghrunner ALL=(ALL) NOPASSWD:` over `/usr/bin/install`, -`/usr/bin/systemctl`, `/usr/bin/rm`, `/usr/sbin/visudo`, `/usr/sbin/usermod`, -`/usr/sbin/useradd`, `/usr/bin/apt-get` and `/usr/bin/tailscale`, path-unrestricted (verified -live, 2026-09-21). That is the install+systemctl pair the frontier refuses to grant, already -held by the job user, unbounded. The job user is root-equivalent on srv1 **today**, and "CI -jobs cannot become root" was an unrecorded false belief. Rostered as `gunbc.rung_drop` -`the_job_user_holds_a_path_unrestricted_root_grant` and classed as -`gunbc.recurring_failure_mode` -`the_grant_a_frontier_refuses_is_already_held_from_another_authority`. It does not weaken the -frontier — it means satisfying it is **necessary and not sufficient**. **Closing those grants -is a separate, operator-gated item**, and it is what gates untrusted-PR qualification of the -microVM host: legacy jobs on the host consume them today, so each needs a consumer census -before it can be removed, and no lane may narrow them silently. - -### The floor's own job does not fit a cell - -**Update 2026-09-23 — the stall retired.** `runner_microvm_floor_fit_stall` was deleted when its -trigger held: `gunbc.floor_demand`'s standing moved to `gunbc_floor_memory_stat_receipt_2026_09_23` -(a run carrying gunbc#12081 and gunbc#12088), and `gunbc_runner_microvm_shape` now resolves on -production inputs at the unchanged 26 GiB cell row. The executed evidence is -`test.claim.runner_microvm_witness_test` -`the_production_shape_resolves_on_the_memory_remainder_since_the_2026_09_23_receipt` and the -either-state witness beside it. The paragraphs below are the state as it stood before that and are -kept as history; the demand is a single warm run's sampled maximum, so a later receipt can move it -back over the line, and the either-state witness is what would say so. - -Before 2026-09-23: `runner_microvm_floor_fit_stall` remained rostered: the floor's measured held-set -peak exceeded `gunbc.runner_slot_desired` `gunbc_runner_slot_memory_max_bytes` less the -realization reserve. Both sides are derivable rather than transcribed — the cap is that declaration, and -the demand is produced by `gunbc.floor_memory_demand`, whose qualification against readable -limits is what the stall cites. The stall row itself carries the figures it was written -against; read them there, where they sit beside the reading that produced them. - -**Grounding it is an operator decision with a fleet cost**, not an engineering task: raise -per-slot `MemoryMax` to ~28 GiB, which **lowers each host's memory-admitted width**, or -reduce floor demand. The stall explicitly rules out deleting the allowance, reserve or -receipt, and rules out using a fixture cell. - -### The milestone that would settle the cutover question - -**One cell settling `CellReady` once, on one host, after one real job.** Nothing before that -is evidence. If that cannot be reached, the remaining work is unbounded. - -## 3. Dynamic per-VM memory — two compiling, unreconciled implementations - -Design ruled and approved by the side chat; **build parked**. Six items; item 1 is built -**twice, independently, blind to each other**. - -**Neither branch is uncompiled.** The uncompiled attempts are the two *closed* PRs, #11849 -and #11850. - -| branch | sha | evidence | carries uniquely | -|---|---|---|---| -| `session/bright-ram-63` | `125659f70df` | lane-reported: all claims green under `claim_batch`, plus five mutation controls, tree restored green after each | the **executed** authority-token wall | -| `session/royal-moth-544` | `80ec5065822` | lane-reported: all claims green under `claim_batch`, two mutation controls executed | `std.measure round_up_to_grain` (resolves the unowned rounder fork), ceiling-as-parameter, permit-in-provenance | - -The same three file paths exist on both with different contents, so **merging them is a -textual conflict on every file, not a union.** Pick one base and port the other's evidence. - -**Both PRs (#11883, #11885) were found NON-DRAFT on 2026-09-21, although both lanes believed -they had left them draft** — so a parked program was consuming CI and reviewer attention on -every push. Both were converted back to draft at wind-down. Keep them draft while parked: -ready buys coverage that was never approved. *The lesson is the gap itself* — "I left it -draft" is a belief about a past action, and the dashboard opens PRs on a lane's behalf, so -it must be re-read rather than remembered. - -**The defect, stated correctly** (it is a bypass, not an absence). All three verified against -`origin/main`: - -- `CellReserved` holds exactly four fields — offer, slot_key, generation, account. **No - requirement, no grant, no envelope.** -- `admit_executor_capacity` takes a whole `ExecutionRequirements` and reads exactly **one** - field, `requirements.capacity_class`. **Cell admission does not consider memory.** -- `unmet_memory_axis` returns `[]` when `needed.envelope.memory` is `Absent`, so **unstated - memory is admitted** by the supply screen rather than refused. - -So memory *is* screened before reservation and the runner *does* read the requirement; what -is missing is that memory is never converted into a **grant**, never **atomically reserved** -against live host use, and never **carried on `CellReservation`**. - -`MemoryGrant` and `HostMemoryLedger` return **zero matches anywhere on main**, so items 3-6 -have no landed symbols to integrate against. The coordination hazard for item 4 (#11625, -static-vs-dynamic `MemoryHigh`/`MemorySwapMax`) is still **open**, but its head has moved to -`8e75da2aa48` — re-read it before item 4 rather than trusting any recorded sha. - -**Topology precondition, a gate rather than an item:** exact per-Work sizing is valid only -where Work is bound **before** the VM boots. A generic prebooted GitHub runner does not know -its eventual job. Do not let a guest discover its Work after boot and call that dynamic -allocation. - -**Amendment worth not re-litigating:** a per-host **CAS-linearized** ledger is required. -Per-cell records cannot conserve a host-wide sum — A and B each read 60 of 100 free, each -reserve 30, both succeed, 120 committed. One CAS commits cell occupancy and memory claim -together. - -## 4. Owed evidence - -**#11845's discriminating RED was never executed.** The repair is real and approved, and it -lands on a green that is hollow for the two reasons in §1. Branch -`verify/11845-red-mutation` (`2499194f4b2`) is pushed and is exactly one hunk off the merge -head: the `test -e` defect put back inside the new classifier. **Both of its blockers have -since cleared**, so the pair is now runnable — expect green at the merge head, red at -`2499194f4b2` on `an_unread_path_reaches_unobservable_and_never_absent`. **If the mutation -passes, the control does not discriminate and the repair needs a better one.** Delete that -branch afterwards; `verify/11845-red-control` is plain main and can go now. - -**Two unowned repairs**, both one-liners, both real: - -- `v2.lens.reference_derived_residency_reading` has zero occurrences of - `construction_justification`, while its siblings import it from `v2.lens.common`. -- `dag/test/claim/machine_intake/jade_first_contact_model_witness_test.dag:3` imports `Nat` - from `std.types`; `Nat` is declared in `dag/std/nat.dag`. - -**`required_gate_prefixes` has no `dag/test/claim/runner/` row**, so that file is never -resolved by CI unless a diff touches it — a symbol deletion can strand it silently, as -#11762 did. Likewise no row matches `test.claim.fabric.`, so the dynamic-memory witnesses are -discovered and declined. - -## 5. The P0 that opened this session is contained but not closed - -srv2 still carries `ExitType=cgroup` in -`/etc/systemd/system/actions-runner@.service.d/70-fleet-teardown.conf` — the configuration -that left PID 56295 orphaned holding 16 GB for 2.7 days. srv1 and srv3 are converged to -`ExitType=main`. - -**Residual risk, precisely.** srv2-01..05 are **masked**, so the P0 cannot recur on them -today. But the drop-in lives in the **template** directory and the template is *not* masked, -so any **new** instance (`actions-runner@srv2-06`) inherits `ExitType=cgroup`. The remedy is -to run the modelled fleet converge on srv2; it has simply never run there. - -srv4 is off the reach chain (container → srv1 → srv2-lan → srv3) and `gunbc.fleet_reach` -carries no row for it. - -## 6. Method findings worth keeping - -- **A suite of controls that invoke a pure decision with supplied values is blind to - everything after the decision** — the cleanup leg, the receipt the withheld arm writes — - and stays blind however many are added. Two post-sign-off findings on #11902 both lived in - that one gap. **38/38 is not route coverage.** -- **A repair of a fail-open is a prime site for the same class one layer down.** #11902's - own (d) repair discarded the located cause on its withheld arm and wrote a constant. Grep - every declaration your repair *adds* for a call site before pushing, and re-read every new - refusal arm for whether it names which fact failed. -- **At any power-of-two grain, a checked-add rounder and a subtraction rounder refuse the - same set**, because 2^63 is a multiple of the grain. A gibibyte-grain mutation control - stayed green under the mutation. Use a non-power-of-two grain (1000). Two authors asserted - the opposite from reasoning alone; both were wrong. -- **A refusal-arm claim tests that the arm fires, not that the right inputs reach it.** That - is how two lanes and a reviewer converged on bounding by the maximum instead of by - representability. -- **`claim_batch` exits 0 over its own refusal.** A `*Refused` line is a **non-answer** — - neither pass nor fail — and exit status must never be read as the verdict. diff --git a/docs/plans/moa-memory-modeling.md b/docs/plans/moa-memory-modeling.md deleted file mode 100644 index ad9df46b125..00000000000 --- a/docs/plans/moa-memory-modeling.md +++ /dev/null @@ -1,158 +0,0 @@ -# Mechanism-of-allocation memory modeling (owner directive 2026-09-25) - -Amends the serving proof doctrine. Reading: **MoA = mechanism of -action/allocation** — which architectural feature allocates each byte, how that -quantity scales, which ranks hold it, during which phase it exists. The MoA -decomposition is the authority. Measurements serve only three purposes: - -1. validate a derived bound; -2. fill a narrowly named term whose implementation exposes no derivation; -3. falsify the model when observation falls outside its claimed interval. - -## The correction that prompted this - -`floor(1,805,689 / 262,144) = 6` was on its way to `FitProved`. Withdrawn: - -- The engine's printed "GPU KV cache size: N tokens" is a - **full-context-equivalent projection**, not a fungible token inventory. - `extdeps.vllm.kv_layout` already says so: hybrid layouts have - length-dependent groups (full attention scales, windowed groups cap) plus - fixed-per-sequence groups (KDA/Mamba state), and `cold_admission_blocks` - refuses to linearly rescale a full-context figure. -- Six seats is therefore a **CacheCapacityCandidate**, not a proof. The cache - subclaim proves only by direct group fold: - `6 × cold_admission_blocks(exact_layout, 262144) ≤ allocator_blocks_on_limiting_rank` - and independently - `6 × cold_admission_bytes(exact_layout, 262144) ≤ admitted_kv_arena_bytes on EVERY rank`. -- The result vocabulary splits: `CacheFitProved / CacheFitRefused / - CacheFitUnestablished`, and the whole arm stays `FitUnestablished` while any - other subproof (prefill transient, decode transient, load transient, static - residency, supply reserve) is open. - -## GLM-5.3-Flash's four scaling laws (the MoA) - -45-layer hybrid decoder: 34 KDA linear-attention layers + 11 sparse-attention -layers (interleaved ~3:1); 64 KDA heads × dim 128; index_kpool = 4; 512-dim -MLA latent; sparse MoE (~320B total, ~18B active — active-count is a COMPUTE -fact, not a residency fact; all experts are resident unless expert offload or -EP placement says otherwise). - -1. **Weights: static, placement-dependent.** Derive from checkpoint manifest × - TP/EP/PP placement × loader transformation rules. The observed 77.0–81.6 - GiB/rank spread says placement is not an even quarter — build the exact - rank-placement projection; do not enshrine measured constants. -2. **KDA state: ∝ sequences, NOT ∝ length × sequences.** Mamba-style recurrent - state per layer per sequence per rank (illustratively 16 local heads × - 128×128 × 4B = 1 MiB → ~34 MiB over 34 layers, before conv state and - speculative additions). Six seats ≈ hundreds of MiB, not a linear rescale. -3. **Sparse MLA + KPool index: token-dependent.** 11 sparse layers: MLA latent - cache (∝ tokens), KPool compressed index (∝ tokens/4), small per-request - tail state (fixed). Persistent cache law: - `seats × (KdaState + TailState) + seats × SparseMla(length) + seats × KPool(⌈length/4⌉) + allocatorGroupingAndPadding`. -4. **Prefill/decode workspaces: phase-specific transients.** Derivable, not - discoverable: e.g. the upstream sparse-indexer prefill buffer carries 40 - entries per model token × 132 bytes (128-dim FP8 vector + scale) → at - 262,144 tokens ≈ 1.289 GiB per applicable rank — check against the exact - deployed revision. **Read the allocator formula; never learn it from OOMs.** - -## Admissibility of observations - -- A resident-demand component can never be negative. TP1's `−2.54 GiB` graph - figure is preserved raw but admitted as `GraphAttributionUnestablished` - (lower bound 0, upper open) — the profiler interval includes a release. - Never clamped silently, never a credit. A negative observation improving a - verdict violates weaker-evidence-cannot-improve-admission. -- `weights + non-torch` is a COMPOSITE profile term (rank-specific - model/runtime residency at that experiment phase), NOT a checkpoint- - placement receipt. It does not separate checkpoint tensors from - runtime-fixed, allocator, communication, or other non-Torch allocations. -- Measurement standings: `DerivedExact / DerivedConservativeInterval / - MeasuredUpperBound / ObservationOnly / ModelFalsified`. A point sample never - becomes MeasuredUpperBound merely by being the largest seen once. - -## The unified proof subject - -Every term binds to one exact qualified subject (qualified-subject-identity.md): - -```text -MemoryProofSubject { - model: model:zai/GLM-5.3-Flash@checkpoint-digest - runtime: runtime:vllm@source-sha+image-digest - topology: topology:fleet/group-b/tp4@host-census-revision - parallelism: parallelism:tp4/pp1/ep-policy/dcp-policy - allocator: allocator:vllm/hybrid-kv@configuration-digest - workload: workload:glm-serving/262144x6@intent-revision - generation: generation:group-b/ -} -``` - -A 64K×1 receipt discharges only terms invariant across the move to 262K×6. - -Every memory term names its mechanism: - -```text -MemoryTerm { component, placement_law, scaling_law, phase, lifetime, bound, derivation } -``` - -Whole verdict = conjunction of subproofs: `StaticResidencyFit`, -`CacheCapacityFit`, `PrefillTransientFit`, `DecodeTransientFit`, -`LoadTransientFit`, `SupplyReserveFit`. `FitProved` only when every applicable -rank × phase proves. - -## The allocation-plan projection (the durable instrument) - -Patch or wrap the pinned engine to emit its allocation plan — introspection, -not tuning: - -```text -ResolvedKvAllocationPlan { - runtime revision / image digest, cache format, allocator block count, - groups: [ identity, spec kind, layer identities, block size, page size bytes, - tokens per state, max memory usage bytes, - blocks required at the requested sequence length ], - grouping/padding decision -} -``` - -The plan must reproduce the engine's printed capacity as an independent -control. Production admission then evaluates any workload point offline from -the group roster; wet execution becomes falsification + compatibility check, -not the sizing algorithm. (`kv_group_metadata` documented the emission gap: -layer counts and page bytes absent — this closes it at the source.) - -## Discriminating controls (required witnesses) - -```text -same arena bytes + different group roster → different capacity -same printed token figure + changed KDA count → no proof reuse -missing sparse-indexer tail group → CacheFitUnestablished -index_kpool 4 → 8 → derived capacity changes -TP4 receipt applied to TP2 → refused -64K×1 activation receipt applied to 262K×6 → refused -negative component observation → cannot improve headroom -all cache groups prove, prefill open → CacheFitProved + ArmMemoryFitUnestablished -``` - -## What remains legitimately measured - -Foreign/host pool occupancy; driver allocations with no deterministic API; -fragmentation with no conservative construction bound; backend workspaces with -no formula nor sizing function; realized behavior that violates the model -(falsification). Everything else derives. - -## Current honest standing (2026-09-25) - -```text -Per-rank pool supply: observed -Rank-specific composite residency: observed (composite, not placement) -KV arena by rank: observed -Limiting rank: TP3 / srv11 (that launch) -262K × 6 cache cost: CacheCapacityCandidate -262K × 6 prefill transient: unestablished -Whole-arm verdict: FitUnestablished -``` - -262K is entirely plausible on this topology — the hybrid architecture exists -precisely to avoid dense KV growth on all 45 layers — but the durable answer -is an argument from architecture + pinned allocation laws + topology + -workload, not a fitted line through one experiment. diff --git a/docs/plans/mt-collins-dimm-physical-identity.md b/docs/plans/mt-collins-dimm-physical-identity.md deleted file mode 100644 index e5e95368eca..00000000000 --- a/docs/plans/mt-collins-dimm-physical-identity.md +++ /dev/null @@ -1,156 +0,0 @@ -# Mt. Collins DIMM physical identity: documentary findings - -Read 2026-09-11 for `node://adhoc-ca3ce4b5-182`. The upstream findings below use -only publisher documents. No machine was accessed. A later operator report is -carried separately in gunbc, as described below; it supplies no extdeps fact. -The starting citations were `docs/plans/altra-cross-channel-rank-mixing-survey.md`. - -## What can be located without reading silkscreen - -Ampere's **Mt. Collins GSG Issue 1.05 Figure 9 (p.11)** labels all 32 connectors and -places their banks around CPU0 and CPU1. **Figure 10 (p.13)** labels the processors -Socket0 and Socket1 in a chassis photograph, with the NVMe backplane and six fans -at the top and PCIe risers at the bottom. With that orientation, Socket0 is left -and Socket1 is right. They are side by side, not front and back. - -The model carries the Figure 9 bank order in -`extdeps.ampere.mt_collins_getting_started_guide.dimm_layout.dimm_figure_banks`. -The processor grouping and channel identity are resolved by J-number against -`extdeps.ampere.mt_collins_product_brief.memory_population.mt_collins_channel_connector_pairs`; -no second channel table is authored. `gunbc.mt_collins_dimm_physical_identity.resolve_dimm_diagram_identity` -refuses missing or repeated keys and disagreements in processor socket. - -The **document-derived positional reading** is: view from above with the front/backplane -away from you and the rear/riser end toward you; then use Figure 9's order within -each CPU's left and right banks. Joining that order with the existing Table 11 -16-DIMM row selects alternating connectors starting at the **outermost connector -away from each CPU** on both sides. This is a spatial inference from Figures 9 -and 10 plus Table 11, not a vendor sentence instructing an installation order, -a photograph of the operator's board, or a physical colour rule. The model exposes -the source bank order and population join rather than minting this inference as -an independently authored slot list. - -## Colour: three distinct facts - -1. **Drawing legend:** GSG Issue 1.05 Figure 8 (p.10), 1U User Guide Issue 1.00 - Figure 10 (p.39), and 2U User Guide Issue 1.00 Figure 19 (p.52) all use **yellow - for slot#0, used at 1DPC**, and **blue for slot#1, added at 2DPC**. The numbered - J annotations agree with the existing odd-J 1DPC population. The model derives - each connector's legend role from the existing population-role join. -2. **Photographed plastic:** GSG Issue 1.05 Figure 12 (p.36) visibly contains blue - and black connectors/latches. It is a cropped board-revision photograph, not - an annotated complete DIMM colour map. This visual fact is carried at its - actual grain in `motherboard_photo_colours`. -3. **Meaning of physical colour:** **not found in the inspected documents.** - None establishes that physical blue means slot#0, slot#1, or the 1DPC set. - The drawing's blue cannot be translated into the chassis's blue. A readable - photograph identifying actual connector positions, or explicit vendor - documentation of the physical colour scheme, is still needed for that answer. - -This does not establish why a processor failed to train, and does not select a -hypothesis about the running machine. - -## Silkscreen: not established for all connectors - -GSG Figure 9 supplies **diagram annotations** J1–J32, not a silkscreen view. -Figures 10 and 12 show installed DIMMs and small/obscured board text. The 1U guide -Figures 11 (p.40), 24–25 (pp.52–53), and the 2U guide Figures 20 (p.53), 42–43 -(pp.74–75) illustrate replacement without an all-connector readable silkscreen -map. No inspected source establishes the literal text printed beside **each** -DIMM connector. No row therefore claims that a connector's physical silkscreen -reads its J-number. The old table-only comment saying connectors were named -on the board is narrowed to what it actually knew: the guide's identifiers. - -## Mt. Jade is not a physical orientation substitute - -Mt. Jade GSG Issue 1.00 Figure 9 (p.10) explicitly marks chassis front/top and -rear/bottom, with **Socket1 left and Socket0 right**; Figure 10 (p.12) repeats -that grouping in a photograph. Its within-socket numbered bank pattern resembles -Collins, but its processor placement is reversed. No Jade orientation or DIMM -silkscreen spelling is imported into the Collins model. OCP Mt. Jade Rev 1.0 is -already a separate platform authority in `extdeps.ocp.mt_jade.subject`; this -change does not assert that a shared processor or channel topology makes their -physical boards interchangeable. - -## Another figure boundary - -The right-side MCU/channel leader labels in the topology cartoon run 4,5,6,7 -against connector pairs displayed 16/15,14/13,12/11,10/9 (and the corresponding -socket-1 keys). The existing GSG Tables 12–13 authority assigns those pairs -MCU7,6,5,4. This change uses the cartoon only for its slot#0/slot#1 legend, and -Figure 9 for connector positioning. It does **not** replace the established -pair table with the cartoon's MCU labels. Resolving that upstream discrepancy -is outside this physical-identity change. - -## Documents and bytes read - -All four publisher downloads returned PDF bytes without credentials. Full text -was extracted with PyMuPDF; the cited topology, chassis and board-replacement -figures were visually inspected. Figure 12's embedded 1220×1626 photograph was -also inspected at native resolution. No document PDFs or photographs are committed. - -| Document | Revision/date | Location and inspection scope | SHA-256 | -|---|---|---|---| -| Mt. Collins DVT/PVT/MP Getting Started Guide, AMP 2021-00511 | Issue 1.05, 2026-07-20 | [Ampere publisher](https://connect-admin.amperecomputing.com/api/secure-file-download/download-regular/?file=Mt_Collins_DVT_PVT_MP_GSG_v1_05_20260720_52a0348e87.pdf&type=technical-document&documentId=z6zenr250qdpa8eq4irolgds); Figures 7–10 pp.9–13, Figure 11 p.32, Figures 12–13 pp.36–37; Tables 11–13 pp.11–12 | `1c41b4f088fa9c8b760d21d57ae93b0d1a68ee67a740d4eb7ac893753fc580b3` | -| Mt. Collins 1U User Guide and Hardware Maintenance Manual | Issue 1.00, 2022-03-30 | [Ampere publisher](https://connect-admin.amperecomputing.com/api/secure-file-download/download-regular/?file=new-private-files/94/tech/Mt._Collins_1U_Users_Guide_v1.00_20220330.pdf&type=technical-document&doc_id=669); §4.2.7 Figures 10–11/Table 3 pp.39–40; Figures 24–25 pp.52–53 | `7f351cc3bf8a9e2d1e6809a9e5337e527feac61a8ff3c4e4d6514f5e59311e7c` | -| Mt. Collins 2U User Guide and Hardware Maintenance Manual | Issue 1.00, 2022-03-30 | [Ampere publisher](https://connect-admin.amperecomputing.com/api/secure-file-download/download-regular/?file=new-private-files/94/tech/Mt._Collins_2U_Users_Guide_v1.00_20220330.pdf&type=technical-document&doc_id=649); §4.2.7 Figures 19–20/Table 6 pp.52–53; Figures 42–43 pp.74–75 | `a8102a2a661fca508c079ba460da2f701360216517ee090a036e533bbf078bbf` | -| Same 2U guide, operator-supplied **third-party mirror** | Same printed issue/date | [Doslab Electronics mirror](https://repo.doslabelectronics.com/misc/Mt-Collins-2U-Users-Guide-v1-00-20220330.pdf); fetched with curl, HTTP 200, 16,148,131 bytes; byte-identical to Ampere's copy. Initial Python HTTP request returned 403; browser tool timed out. The successful download is the cited read, not those failed attempts. | `a8102a2a661fca508c079ba460da2f701360216517ee090a036e533bbf078bbf` | -| Mt. Jade PVT/DVT (NVMe) Getting Started Guide | Issue 1.00, 2022-04-12 | [Ampere publisher](https://connect-admin.amperecomputing.com/api/secure-file-download/download-regular/?file=new-private-files/94/tech/Altra_Family_Mt._Jade_PVT_DVT_NVMe_GSG_v1.00_20220412.pdf&type=technical-document&doc_id=631); Figures 8–10 pp.8–12 | `f6528f70dec97b8838bd67413566287fc7c465addbf07182e9bdd1d48874c69f` | - -The 2U document has its own `extdeps.ampere.mt_collins_2u_user_guide.subject` -authority, including its publisher URL, separately named mirror URL, issue, -date, digest and Figure 19 citation. The two independent read receipts live in -`gunbc.specification_citation_read_provenance`; the publisher uses the -publisher-fetch arm and the live third-party mirror uses -`DocumentMirrorLocatorFetchedAndTextIngested`. Its observation date is not an -invented archive-capture date. `mt_collins_2u_citation_read_comparison` derives -agreement from subject identity, response status, byte count and digest, and -retains both receipts. Unread or wrong-subject records are incomparable; -different recorded bytes/digests differ. No mirror read is called a publisher -read, and no agreement is an authored boolean beside the DIMM join. - -## Consumption and remaining boundary - -`gunbc.mt_collins_dimm_physical_identity.sixteen_dimm_diagram_identity` is an -executable inspection entry point into `population_diagram_identity`. It carries -ordered banks, canonical channel identity, drawing legend, chassis orientation, -2U guide citation/mirror provenance, and explicit physical identification gaps. -Unknown/duplicate connector references, socket disagreements and invalid population -rows refuse without returning partial slot instructions. The published 32-DIMM -row still has 31 keys; the projection reports the mismatch rather than silently -adding J32. The existing correction remains the authority for that separate inference. - -The later **Mt. Collins rack-facing DIMM population projection** is declared by -`operator_projection_frontier` beside the join, including the capability required -to close it. This change lands the documentary model and executable join, not a -rack UI or a claim that physical identification gaps have been closed. That future -projection must preserve the distinction between figure-based positioning and -verified silkscreen/colour. A clear chassis photograph is the next evidence route -when the operator cannot read the markings. - -## Later operator report: unit 1 only - -Dashboard message `msg_ecd92656-ea35-4746-9e3f-f67d12706ec5`, relayed by -`eager-owl-205` on 2026-09-11, reports the operator's by-eye inspection of -**unit 1, a Foxconn Mt. Collins 1U chassis**: physical blue connectors are the odd-J set used at -1DPC; the silkscreen supplied no useful slot-identification information. -No photograph accompanied the report to this session. We did not independently -inspect the chassis, and the report is not an Ampere statement. - -`gunbc.machine_intake_mtcollins1_dimm_connector_observation.mtcollins1_blue_connector_observation` -records that report, its date, observer, relay message and unit scope. The -reported set references the canonical 16-DIMM population rather than repeating -its J-number list. The upstream documentary findings above remain unchanged. - -`mtcollins1_colour_meaning_divergence` is the third fact: it joins the reported -physical blue set to the documentary roles and finds that blue on this chassis -has a different role from blue in the schematic legend. This is evidence that -the diagram colour does not predict this chassis's colour, not a contradiction -in a vendor physical-colour rule (none was found). - -`colour_evidence_for_unit` returns this evidence only for the existing unit-1 -intake subject. Another unit receives `NoColourObservationForUnit`, and the -returned documentary projection still carries both upstream identification -gaps. The later rack UI must preserve date and by-eye/report provenance; this -receipt does not generalize to other units, the 2U chassis, or the fleet, and it -does not explain or validate processor training. diff --git a/docs/plans/mt-collins-gpu-augmentation-program.md b/docs/plans/mt-collins-gpu-augmentation-program.md deleted file mode 100644 index 546c4dd30ae..00000000000 --- a/docs/plans/mt-collins-gpu-augmentation-program.md +++ /dev/null @@ -1,181 +0,0 @@ -# Mt. Collins GPU augmentation — program - -**Status: contract phase, no geometry authored.** This is a SECOND product program with its own -authority (`product.mt_collins_gpu_augmentation`), deliberately not folded into -`product.printed_chassis`. It pressures different parts of the model than the ALTRAD8UD cassette: -mixed purchased and fabricated structure, a heavy high-value expansion card, PCIe signal integrity -and cable strain, independent power and protective earth, two interacting airflow domains, sustained -thermal evidence, and a prototype-material versus deployment-material distinction. - -The standing rule that **cable routing and grounding apply at every controlled layer** carries over -unchanged. - -## Architecture ruling: companion bay first, hood second - -``` -MtCollinsGpuServingUnit -├── original sealed 1U server (lid, fan wall, CPU/DIMM shrouds untouched) -├── adjacent 1U GPU companion bay -│ retention/load path · airflow · dedicated PSU unless the PDB is proven · PE bond -├── purchased qualified PCIe riser/cable -└── modeled cable, service and thermal interfaces -``` - -The companion bay is the better first experiment because it preserves the stock thermal system, -keeps GPU weight off the riser connector, allows a dedicated PSU and independent intake/exhaust, is -reversible, and separates the server's cooling result from the GPU enclosure's. A modified 2U lid or -hood is a legitimate *comparison* candidate, not the starting point. **Card orientation is not yet -admitted** — exact card dimensions and usable 2U geometry decide it, and a horizontal arrangement may -be the only viable one for a full-height card. - -## Census ANSWERED — this program is experimental, not the critical path - -**~8 of the 28 boxes are at least 2U, some 4U, and take a GPU with no fabrication at all.** So the -1U retrofit is an experiment to run if it is ever wanted, not the route to a served GPU. Everything -below stands as the design if the retrofit is attempted; none of it is on the critical path, and the -program should not consume printer time that the ALTRAD8UD cassette needs. - -The live question moved with it: not "how do we get a GPU into a 1U" but **"what changes in the -ALTRAD8UD stackable chassis when one of its nodes carries a GPU"** — recorded in -[printed-chassis-program.md](printed-chassis-program.md) under the GPU-bearing node, because that is -a variant of the node contract rather than a second product. - -## The census, retained for the reasoning - -The brief's own MTC-GPU-5 lists "purpose-built 2U GPU server" as a comparison candidate. Per the -operator's hardware discussion, **the 2U Gigabyte G242-P31 is the Ampere platform built for exactly -this** — four dual-slot GPUs, front-to-back airflow, PSUs sized for it — while **a 1U Mt. Collins -cannot take a dual-slot full-height card at all**; the 1Us are CI boxes. - -So the cheapest possible first action is an **exact-model inventory census of all 28 boxes**. If any -are the 2U variant, the companion-bay program is not the cheapest route to a served GPU — it is a -workaround for a chassis constraint that a machine already owned does not have. A program that can -be deleted by one inventory query should face that query before any geometry is modeled. - -This is not a reason to discard the brief: the companion bay remains the answer if the fleet is -all-1U, and the ruling's decomposition stands either way. - -## Design for the production process, from the first printed part - -The operator's stated intent is **prototype by printing, produce by manufacturing** — specifically -laser-cut and bent sheet aluminium, which for a colo is worth more than its price: fire-rated by -nature, grounds to the shelf, no tooling, ~$40-60 at 20-100 units. Printing does not scale into -production here; it is the design loop. - -Two consequences bind the geometry **now**, before any part is modeled: - -- **Model bend-friendly, flat-ish panels** — uniform material thickness, bend reliefs, no geometry - that only survives because a printer tolerates it. *A print that only works because printing - tolerates anything is a prototype of nothing.* -- **Freeze before you tool.** The readiness signal is not "it works" but "the last three revisions - were cosmetic." - -This also splits process qualification into **two targets**: the printer–spool process (what the -hole-diameter coupon measures, for the prototype loop) and the sheet-metal vendor process (what the -production parts are actually made by). These are different `ProcessQualificationIdentity` subjects -and must not share one. The coupon program is not invalidated by this — it qualifies the loop that -produces the prototypes — but it does not qualify a production part. - -## Phases - -| Phase | Terminal evidence | -| --- | --- | -| **MTC-GPU-0** contract | Exact GPU SKU, count, bay-vs-hood candidate, rack allocation, prototype-vs-deployment target, power architecture. No generic "Blackwell", "x16 riser" or "2U hood" stands in for a selected product. | -| **MTC-GPU-1** measure | Chassis external/usable envelope; lid, shroud, riser, rear-slot geometry; GPU envelope, mass, bracket, connectors, airflow; PDB connectors and ratings; candidate PSU; rack depth and cable-service envelope — with uncertainty and provenance. | -| **MTC-GPU-2** PLA cold-fit | Rear pass-through/blanking adapter, riser strain relief, bracket-location gauge, cradle prototype, duct prototype. Surrogate mass before the real card sits on printed retention. Fit only — no power or thermal claims. | -| **MTC-GPU-3** powered bay | Independent card load path, admitted PSU, PE, fans/ducting, full cable routing. PCIe link trains at required width and generation, no relevant link errors, GPU idles safely. | -| **MTC-GPU-4** thermal admission | The isolated matrix below. Combined load within exact product limits, no unexplained host regression, no printed-part thermal failure. | -| **MTC-GPU-5** production verdict | Compare custom bay vs integrated hood vs commercial expansion chassis vs purpose-built 2U server on cost, rack occupancy, thermals, reliability, service time, fabrication effort. | - -## Load path — printed parts are never the retention - -The GPU's load terminates in the bay or rack, **never** in the PCIe edge connector, the riser card, -the flexible cable, the printed duct, or the original server lid. - -``` -rack rails / metal bay shell -> metal GPU bracket -> metal or reinforced cradle - -> optional printed alignment pads and duct -``` - -Printed: card-location adapters, anti-sag supports, blower intake ducting, cable strain relief, -connector guards, fan mounts, blanking pieces, geometric identifiers. -Metal: rack-width shell, primary retention, long beams, PSU enclosure, PE path, fire and impact -containment. A rack-width enclosure is not one A1-mini print in any case — this is another -"print the joints and ducts, buy or bend the straight metal" design. - -## Cable and grounding — independent modeled routes - -PCIe data (host riser slot → pass-through → cable → GPU edge) · GPU DC power (pod PSU or admitted PDB -→ auxiliary connectors) · AC (rack supply → bay PSU) · cooling/control (fan power, PWM/tach) · -**protective earth** (AC PE → PSU enclosure → bay metal → rack bond) · functional/chassis reference, -separate from PE. - -Each route carries endpoints, connector orientation, bend volume, moving/fixed classification, strain -relief, hot-surface separation and disconnect order. **The PCIe cable is never retention, never -chassis grounding, never an uncontrolled hinge, and never a service loop with an undefined bend -radius.** The rear pass-through must preserve the host pressure boundary as far as practical — a -large unblanked opening can disturb host airflow even with the lid on. - -## Thermal — two coupled domains - -``` -HostCoolingDomain front fan wall -> CPU/DIMM shrouds -> rear exhaust -GpuCoolingDomain dedicated ambient intake -> GPU cooler/blower -> declared exhaust destination -``` - -Primary law: **`GpuCoolingDomain` must not materially impair `HostCoolingDomain`.** - -Test sequence, each change isolated: stock sealed 1U under CPU/DIMM load (host baseline) → bay -installed, GPU unpowered (passive obstruction, recirculation) → GPU idle (fan/idle-air interaction) → -GPU load only → combined load (terminal case) → declared fan degradation or elevated ambient -(failure margin). - -Observed: CPU temps and throttle state, DIMM temps, BMC-available VRM/board sensors, host fan speeds, -GPU core/hotspot/memory/fan/power/throttle reason, host inlet and exhaust, GPU inlet and exhaust, -printed-part surface temperature at the hottest locations. - -Admission requires: no vendor limit exceeded ∧ no unexplained throttling ∧ host regression within a -declared budget ∧ GPU sustained at target power ∧ printed material within its admitted service -envelope ∧ no recirculation mode left unmeasured. - -## Material policy - -PLA **may** establish dimensional fit, card and bracket location, riser-cable path, interference and -service clearance, room-temperature duct topology, fan and connector placement. - -PLA **may not** establish sustained structural retention near the GPU exhaust, long-term anti-sag, -deployment thermal durability, flame behaviour, or colo suitability. - -A powered PLA prototype is defensible only when the PLA parts are not the sole card support, are not -the mains enclosure or PE path, and are temperature- and deformation-monitored under supervision, -with a secondary metal retention path if the plastic softens. **PETG is not automatically -sufficient** — measure actual part temperatures first. - -## Facts that stay OPEN until measured - -Exact GPU SKU, dimensions, mass, slot width, power, connector locations, airflow direction · rear -riser electrical width and generation · exact qualified riser cable and usable routing length -(signal integrity is not inferable from "Gen4 x16 cable") · PDB GPU-power capability · stock -lid/shroud thermal role · GPU-bay intake/exhaust relationship to server exhaust · available rack -depth and rear service clearance · printed-part operating temperatures · colo/site rules. - -The 60-80 °C blower exhaust figure is a **hypothesis until measured** on the exact card and airflow -arrangement, and "under 80 °C for an hour" is **not** the terminal rule — the exact GPU's vendor -limits, throttle state, hotspot and memory limits, and the server's CPU/DIMM limits govern. - -## Workload model stays separate - -The KV-cache analysis is not mechanical authority. It determines a `SelectedGpuDeployment` -(gpu_sku, gpu_count, target active session population, parked session population, storage tier) -which this program **consumes**. The chassis program must not independently encode "two cards" or -"64 sessions". The load-bearing distinction is that **parked-session capacity is not simultaneous -active decoding capacity** — that may decide whether the augmentation is worth building at all, and -it must not silently decide card count before the active-fraction requirement is selected. - -## Extraction discipline - -This becomes the **second real consumer** that could justify extracting generic facilities currently -inside the ALTRAD8UD program — process qualification, model-to-CAD realization, generated-artifact -materialization, spatial measurement, cable routing, protective earth, thermal observation, -fabrication receipts. Extract when both products ask the **same typed question**, never because the -names look reusable. diff --git a/docs/plans/mtcollins1-boot-observe-publish-capacity.md b/docs/plans/mtcollins1-boot-observe-publish-capacity.md deleted file mode 100644 index 3fbc02da2c9..00000000000 --- a/docs/plans/mtcollins1-boot-observe-publish-capacity.md +++ /dev/null @@ -1,96 +0,0 @@ -# mtcollins1 boot: observe and publish capacity; health separate from the boot verdict - -Status: APPROVED by eager-owl-205 (2026-09-26) with the frontier answer below; work item adhoc-0e07aa33-b20. -Off the #12362 → publish → repin → boot critical path: nothing here lands before #12362. - -## The defect, read as a chain (DESIGN §6b) - -`gunbc.machine_intake_mtcollins1_boot_run` `mtcollins1_boot_qualifications` folds six -qualifications into ONE terminal verdict: `nproc`, `numa`, `boot-media`, `edac-before`, -`workload`, `edac-after`. Two of them (`nproc`, `numa`) compare the observation to an -INTENDED topology (`gunbc.machine_intake_mtcollins1_boot_milestone` -`mtcollins1_boot_topology_expectation`), so an intent mismatch is reported as "did not boot". -The earliest unjustified boundary is not the 160/80 literal; it is that the verdict type answers -four questions with one arm set. Patching the literal (the #12325 milestone + the rung drop -`mtcollins1_boot_accepts_socket1_absent`) was the symptom-link patch: it kept the conflation and -made the intended configuration a boot precondition. - -## The four questions and their homes (DESIGN §3b — inhabit, do not mint) - -| Question | Home (existing) | What changes | -|---|---|---| -| 1. Did it boot | `gunbc.machine_intake_mtcollins1_boot_run` terminal verdict | Verdict narrows to evidence the census image reached its interfaces: envelope closed/sections terminated, `boot-media` (the image we delivered is the one running). `nproc`/`numa`/`edac-*`/`workload` leave the verdict. | -| 2. What is available | `gunbc.compute.host_capacity` (`HostMemoryObservation`, the host `Pool` over `product.capacity.pool`), `product.fabric.envelope` `ResourceEnvelope`, `product.fabric.work` `HardRequirements { threads: HardwareThreadCount }` | A capture-sourced observation reading of CPU set (count + per-NUMA-node CPUs), and MemTotal/MemAvailable and `/dev/shm` size, each field `Observed \| Unknown{cause}` — malformed never becomes zero. Published regardless of (3). No new capacity vocabulary: threads are `HardwareThreadCount`, memory `Kibibyte`, the pool is `product.capacity.pool`. | -| 3. Health | `product.host_health` (`MemoryErrorCounters`, `CounterWindowStanding`, `HostHealthGoal`, assessed via `std.goal_assessment`) | EDAC before/after become a `CounterWindowStanding` over `MemoryErrorCounters`; workload digest stability a health signal; "observed topology vs intended (2 sockets × 80) and vs earlier observations" becomes a `GoalDiverged` deviation, never a boot refusal. Findings carry a consequence (restrict / quarantine), never delete the (2) observation. | -| 4. Eligibility | `product.fabric.supply` `offer_covers_shape` / `unmet_*_axis`, `product.fabric.capacity_admission` | Requirements are judged against the (2) allocatable envelope plus a health policy input from (3). No mtcollins1-specific eligibility code. | - -Open question for the parent on (2): the capture is a BOOT-TIME observation on a machine not yet -enrolled as a fabric host (no `HostDashboardInstance`). I propose the observation lands as a -typed reading in machine_intake that is *shaped* as `ResourceEnvelope` + `HostMemoryObservation` -so enrolment consumes it unchanged; the actual pool publication is a declared frontier (§3c) -triggered by mtcollins1's fabric enrolment. If you want it published to a fabric partition now, -I need the enrolment home named. - -## Memory test sizing (the #12362 residue) - -Sizing follows the observation, never socket count, under an explicit policy row: -`reserve` (bytes held back from the test), `minimum_useful` (below which the test REFUSES as -"not run", not "passed"), and the size attempted = min(MemAvailable − reserve, /dev/shm free). -The receipt records exact bytes attempted and coverage = attempted / MemTotal. A bounded run is -typed `BoundedCoverage { attempted, of }` and can never read as full qualification; full -qualification is a separate arm requiring coverage at the declared threshold. The #12362 -"one-socket 16 GiB profile" dissolves into this policy once #12362 has landed. - -## The cut (DESIGN §3 replacement migration, delete-first) - -The root is `gunbc.machine_intake_mtcollins1_boot_milestone`. Its consumers, enumerated by name -(this root is not in the required gate prefixes' blast radius alone, so named, not inferred): -`gunbc.machine_intake_mtcollins1_boot_run`, `test.claim.machine_intake.mtcollins1_boot_run_witness_test`, -`gunbc.rung_drop.mtcollins1_boot_accepts_socket1_absent` (+ `gunbc.rung_drop` roster, -`docs/design-rung-drops.md` projection). - -One PR, one motion: -1. DELETE `mtcollins1_boot_milestone.dag` and the `nproc`/`numa` qualifications and their causes - (`SocketPlacementMismatch`, `SocketPlacementUnread`, nproc mismatch) from the terminal verdict. -2. RETIRE the rung drop by construction: its subject ("boot qualification accepts socket 1 absent") - no longer exists because the boot verdict no longer asks about sockets. The gate it lowered is - not lost — it moves UP to health, where an unexpected reduction is a `GoalDiverged` finding - against the intended 2-socket configuration. Nothing is weakened, so no new drop. -3. KEEP `mtcollins1_nproc = 160` untouched: a true receipt about the census boot it was read on; - it becomes one of the "earlier observations" health compares against. -4. The numa-line parser (`numa_node_cpus_line`) moves into the capacity observation, not copied. -5. EDAC/workload move from verdict into the health assessment in `product.host_health`. - -Witnesses: an 80-CPU single-socket capture BOOTS, publishes 80 threads, and carries a health -deviation; a malformed nproc section boots (if boot-media held) with threads `Unknown`, never 0; -a capture missing boot-media does not boot; EDAC UE>0 boots but health restricts; the route claim -exercises the real `mtcollins1_boot_terminal_verdict` path (§3 pairing obligation). - -No live hardware is touched. - -## Correction after reading the homes: in-band health is not `product.host_health` - -`product.host_health` is built on a standing rule that its signals are read OUT OF BAND ("a count -the BMC does not expose is an unknown, never a host-OS read"). nproc, numactl and in-band EDAC -from the census image are host-OS reads. So the topology health assessment is a sibling -instance of the shared authority, `std.goal_assessment`, over an in-band subject, in -`gunbc.machine_intake_host_resource_observation`. It is a stated divergence from -`product.host_health`, not a silent fork. In-band EDAC follows the same route at the cut (below): -`product.host_health` `MemoryErrorCounters` / `counter_window_standing` are reused as values, -without the out-of-band epoch claim. - -## Staging (parent ruling: nothing touches the #12362 → publish → repin → boot path until that boot has run) - -- **PR 1 (this change).** Questions 2–4 as new modules: - `gunbc.machine_intake_host_resource_observation` (observation → `host_topology_assessment` → - `topology_health_admission` → `host_offered_shape` → `host_workload_eligibility` over - `product.fabric.supply` `unmet_shape_axes`), and `gunbc.machine_intake_mtcollins1_topology_goal`. - The numa reading has its own strict parser (node and CPU ids, duplicates refused). The milestone module's count-only parser is frozen and deleted at the cut. -- **PR 2 (the cut), after the #12362 boot has run.** Delete the milestone module and the - nproc/numa verdict arms. Retire the rung drop. `mtcollins1_boot_run` feeds its envelope's - sections into `host_resource_observation`, so the boot run becomes the production consumer. - EDAC and workload move out of the verdict. -- **PR 3, after #12362 lands.** Memory-test sizing policy. -- **Declared frontier.** Pool publication into `product.capacity.pool` via - `gunbc.compute.host_capacity`. Trigger: mtcollins1's fabric enrolment (a `HostDashboardInstance` - naming it). No enrolment home is invented here. diff --git a/docs/plans/namecheap-secret-manager.md b/docs/plans/namecheap-secret-manager.md deleted file mode 100644 index d2063a4a638..00000000000 --- a/docs/plans/namecheap-secret-manager.md +++ /dev/null @@ -1,135 +0,0 @@ -# Namecheap credential custody - -The owner reports `projects/582015116396/secrets/namecheap-api-key` provisioned. -The project number matches `fleet_secrets_project_number`; the project ID comes -from `fleet_secrets_gcp_project` (`gunbai-secrets`). No new version is provisioned -by this lane, and no key is requested in chat or in a persistent local file. - -The owner also supplied account `briansrls` and an API allowlist entry named `dev` -for `96.224.201.7`. These are typed operator reports in -`gunbc.namecheap.account`, not evidence of an authenticated API call or a -permanent DNS-controller placement. - -`gunbc.namecheap.credential` binds the container once. Its `latest` reference identifies the -container for IAM and is resolved once by the discovery observer, which records -the returned numeric version. The numeric selector controls apply to future -reviewed mutation consumers, not this discovery path: an approval over a DNS -mutation must not silently switch credential generations through an alias. `gunbc.namecheap.credential_read` uses the existing -`fetch_secret_ref_credential` with `WorkloadIdentityToken`; the shared reader -checks the response resource against the requested project, secret and version. -It does not print or persist the payload. - -The dedicated `namecheap-dns` federation joins `gcp_iam_converge_targets`. Its -trust is pinned by the shared fleet job claim authority to this repository, the -fleet-converge workflow at main, a dispatch on main, and the `namecheap-dns` -environment. Its only secret grant is accessor on `namecheap-api-key`. The -existing IAM workflow plans as iam-observe, files a request in the approval app, -impersonates iam-converge only after approval, rechecks the approved plan, applies -and independently reads back. Enrollment is not evidence of an applied grant. -The new target also changes the resource-local bootstrap bindings needed by the -IAM controller; those must be observed before running its apply. -This preserves the repo's distinction between routine per-secret federated -reads and operation-specific human approvals. For DNS mutations, the consumer -must use the existing `approval_gate` and scoped authorization over the frozen -zone plan; secret possession alone never authorizes a zone replacement. This -increment does not yet connect that DNS mutation consumer. - -Live status: secret creation and the allowlist are operator-reported. No enabled -version or applied IAM binding has been independently observed. This local shell -has no WIF token or gcloud installation. No provider request, IAM write, key -access, or DNS mutation has been executed. The `namecheap_observe` fleet mode now has a dedicated job that supplies WIF -and records the exact secret version plus authenticated getHosts readback. It -still must land on main and its federation must converge before a live dispatch -can pass the provider's main-only claim pins. -The earlier DNS model lives on `work/daily-end-to-end`; this lane is based on the -current repository so it can reuse the landed approval app rather than copying -its newer authorities into that older checkout. The previous local `api_key_file` -onboarding proposal is superseded by this Secret Manager reference. - -Validation on the current main base: a combined full-root run evaluated all -21 IAM convergence controls, five credential controls, 15 XML/provider controls, -and four workflow controls successfully, then regenerated the workflow through -`tools.generated_artifact_gate.main_wet_one`. Its receipt counts 46 because it -also ran one redundant aggregate IAM check, subsequently removed; the retained -45 controls all ran in that invocation. The generated YAML was independently -parsed and checked for the dispatch mode, dedicated identity, event SHA checkout, -SSH exclusion, and success-only receipt upload. The self-contained loopback -transport harness passed and observed the fixture query arriving without the -fixture key in curl's argv. These are local checks, not live GCP or Namecheap -observations. - - -## Read-only verification workflow - -`fleet-converge` mode `namecheap_observe` uses the selected fleet runner and the -`namecheap-dns` environment. The build and observer both check out the event SHA; -`expected_revision` cannot substitute another revision under the trusted WIF -identity. The shared fleet job is excluded, and this job receives no SSH key. - -The entry checks public IPv4 egress against the owner-reported allowlist, fetches -one Secret Manager version through the shared checked reader, and issues only -`namecheap.domains.dns.getHosts`. The encoded query travels to curl via stdin, -not argv or a persistent credential file. There is no setter or configurable -command in this provider interface. Every read has a timeout. Transport and -provider refusals omit raw response bodies, and a response that contains the -credential is withheld before decoding; the decoded retained result and host -attributes are checked again before the observation can reach the receipt. - -The XML subset reader refuses unsupported syntax and ambiguous envelopes rather -than guessing: DTDs and external entities, duplicate attributes, extra documents, -namespace overrides, malformed records, duplicate provider record IDs, and a -response for another domain/command cannot yield an observation. It preserves -all provider result and host attributes, including unknown ones. Unsupported XML features -(including numeric character references) are a located read refusal; this is not -a claim to implement every XML document. - -`target/namecheap-observation.json` records the run ID, attempt, revision, start -time, exact credential version, account, observed public egress, domain and -returned result and host fields. The job uploads it only after success, so failure cannot -publish a previous run's receipt. `mail_mode=unobserved` and -`write_authority=withheld` are deliberate: getHosts does not independently prove -the console's mail mode, and this observer grants no DNS mutation authority. - -The remaining live sequence is: land the reviewed code on main; observe/update -the existing IAM controller's resource-local bootstrap reach for the new target; -run `gcp_iam_converge` and approve its exact plan in the existing app; then run -`namecheap_observe` on a runner whose public egress is allowlisted. Secret -possession, a modeled grant, and a pure witness are not substitutes for those -readbacks. Full-zone DNS mutation, DNS-01 renewal coordination, and dashboard -fleet cutover remain separate unfinished consumers of this observation. - -Provider authority: [Namecheap getHosts](https://www.namecheap.com/support/api/methods/domains-dns/get-hosts/). - -Landing boundary: the active main ruleset requires the `witnesses` status and -merge queue. The IAM authority also requires reviewed main code; the feature -branch cannot impersonate the dedicated main-pinned identity. A read-only query -of the latest 100 fleet workflow dispatches found no `gcp_iam_converge` run. That -is limited history, not proof that bootstrap never happened. The bootstrap -reach and independent readback are still unverified. - -## Review 5331195485 corrections - -The writer, generated console display and artifact uploader now consume -`gunbc.namecheap.observation_artifact.namecheap_observation_receipt_path`. The -workflow shell control reproduces the original unmatched receipt read on the old -generated YAML, then exercises the regenerated success path with only the -declared JSON output. Its planted unmatched read must fail. - -The production client keeps the raw response exclusion and also checks all -decoded retained result and host attribute names and values, including unknown -fields, before returning an observation. Six publication controls cover raw, -URI-encoded, and XML-entity-encoded echoes plus ordinary entity preservation. -Disabling the decoded guard in an isolated test copy makes the three XML echo -controls fail while raw/URI and ordinary-value controls still pass. Refusals -contain no offending value and never claim a partially redacted snapshot. - -The previous head's CI floor identified bare algebra-provider references and an -ambiguous shorthand `domain` binder. List operations now use their native method -forms, and the observer uses an explicit binder. Uppercase `Host` remains unchanged. - -Correction validation: the combined full-root run passed 51 controls and -regenerated the workflow. After the CI-reference corrections, all 15 parser and -six publication controls passed again. The regenerated emitted shell passed the -clean-root success control, left the exact upload artifact, and rejected the -planted unmatched receipt read. `git diff --check` passed. Exact-head CI is the -remaining repository-wide check; no live provider or IAM actuation was used. diff --git a/docs/plans/native-route-parallel-realization.md b/docs/plans/native-route-parallel-realization.md deleted file mode 100644 index dba126a842d..00000000000 --- a/docs/plans/native-route-parallel-realization.md +++ /dev/null @@ -1,70 +0,0 @@ -# Native-route parallel realization (Phase 1 model) - -Durable record of the Phase 1 model sent to `eager-raven-113` and accepted with four binding conditions (dashboard GO, 2026-09-14). This document is the artifact to keep. Implementation is not landed under operator wind-down. - -Independent program beside A (#11224, frozen, untouched) and B (#11217, royal-hawk owns B and the producer counts). Nothing here may make B's AFTER measurement harder to interpret. A's frozen subject is never altered or delayed. - -Gatekeeper: eager-raven-113. Host for later builds/experiments: srv1 (`ssh srv1-lan`; `systemd-run --user --scope -p MemoryMax=24G`; `CTRL_BUILD_MODE=local`). srv2 is reserved for A. Receipt class 3/4 (native driver/observer) → pre-landing exact-head receipt when a later change actually lands code. - -## Not credited - -Do not claim later: - -- `3x` -- `five minutes` -- the serial projection -- semantic equivalence of fierce-lark's merged shard surface - -The shard experiment refuted identity volume, memory pressure, allocator return, and host-load tail as causes. The five-shard signature is not yet module-caused (rotation discriminator first; fierce-lark runs it on srv1). - -## Homes (cite, do not fork) - -Homes accepted as written, not forked: - -- `std.realization` — `Placement.LocalChildProcess` (fifth arm). Serial `SourceRootEvalDriver` remains `LocalInProcess`. A prepare worker is a local OS process with its own address space, not a filesystem cache, not a remote host, not an accelerator. Do not nickname Placement as `WorkerKind`. Do not put the arm in `gunbc.compute`. Worker `RealizedStep` = `ExecuteEffect` + `LocalChildProcess` + `Recompute`. Parent merge stays `LocalInProcess` plus `ReadEffect` over worker artifacts (condition 3). Slice 1 does not take a Share cache obligation across siblings. -- `std.realization_width` — envelope is `process_memory_aware_spawn_width` (cores ∩ memory ∩ pids). This is the bound fold, not a second knob. Do not mint `NATIVE_SHARD_COUNT` / `--width`. `NATIVE_SHARD_INDEX` is an observation label only. **Precondition, fail-closed (landing side chat, 2026-09-15):** that helper is not the envelope on its own — it returns `conservative_fallback_width` when the memory or PID observation is zero, and clamps to one worker when the memory or PID budget fits none, so it can answer with a width outside the observed envelope or admit one worker when physical fit is zero. This realization may call it only after a fail-closed contract has established nonzero, readable CPU, memory and PID inputs and capacity for at least one worker; missing inputs or zero fit produce a named refusal (`NoFeasibleRealization`), and the helper's conservative-fallback and minimum-one arms are not admissible answers here. If a stricter existing operation with exactly those semantics is found at implementation time, bind that instead of wrapping this one. -- `std.decision` — width *choice* is `select_realization` with a named `DecisionSubject` and the two funded axes from `width_fold_objective_goals` (wall, peak memory; both LowerIsBetter). A Pareto front is not a winner. Requested N above the envelope is `NoFeasibleRealization` (later named `PrepareWidthRefusedAboveEnvelope` in the prepare-shard contract), never a silent clamp. Width is not computed inside `std.goal_assessment` / `ensure`. -- `gunbc.compute.work_request` / `work_provider_local` — **do not extend** `WorkOperation`. Prepare shards are native-driver work (`NativeTestContext` / `native_test_prepare_module`), not cargo `CompileEntry`. -- Instrument authority = `scratch/deep-cat-655-span-bisect@5b41958d07d9` (`[native-module-residual]`). Reuse; do not re-derive. Do not retouch `[native-prepare-split]`. Named children subtracted from module parent: prepare, eval, row_accumulation, arm_exit, release, meter. Resolve and infer are reported and nested inside prepare; they are not subtracted. Residual uses checked arithmetic and refuses on negative / missing / duplicate / unmatched. Prepare parent = fixed + Σ module parents + residual; if parent is not nested, refuse. - -## Slice 1 construction (approved shape; not landed) - -One immutable context in the parent (`NativeTestContext` via `native_test_context_from_ingest`). Fork after this. Context is read-only for workers. - -Complete module manifest from that context + `universe.modules`. Assignment: **contiguous slices** over stable module-identity order (condition 4). The same function is used for W=1 and W=N. - -Each worker (`LocalChildProcess`) prepares only its assigned modules and writes one artifact. - -Named merge step: `prepare_shard_merge`. Law: bidirectional identity+payload join against serial (arm i). Added ∪ removed ∪ payload-changed ∪ duplicate ⇒ refuse the equivalence claim. Completeness is the join, not a count equality. Parent exclusive rows gain a named merge span; it is not stuffed into prepare residual. - -## Binding conditions (eager-raven-113 GO) - -1. **Acceptance is three arms, not two.** On one pinned quiet-host subject measure: - - (i) in-process serial (today's production `SourceRootEvalDriver`); - - (ii) W=1 via `LocalChildProcess` + `prepare_shard_merge` (placement price: fork, serialization, merge); - - (iii) W=N. - Report all three with total CPU, aggregate memory, process count, parent/worker partitions, merge span named. `(ii)−(i)` is the placement price; `(iii)−(ii)` is the width effect. Merge-law join runs against (i). -2. **Compose on the row-set reshape (#11374).** Add one variant + one roster entry on the keyed exclusive-row collection. Do not add a field to the old literal `NativeDriverExclusiveRows` struct. Build on a branch that includes #11374's head; rebase when it lands on main. -3. **Worker transport:** artifacts under `RUNNER_TEMP`, one file per worker, content-identified by digest; parent `ReadEffect`; missing / malformed / digest mismatch **refuses** (no partial merge). Digest join is part of the merge law. -4. **Assignment:** contiguous slices over stable module-identity order (printable interval; rotation can move whole sets). Same function for W=1 and W=N. - -Everything else as in the model, including: no second width knob; `NATIVE_SHARD_INDEX` is an observation label; slice 2 declarations land with slice 1 but implementation waits on slice 1's green three-arm receipt; receipt class 3/4; srv1 only. - -RED list for a first implementation push (not this wind-down PR): merge-law added/removed/changed; residual refuse arms; width > envelope refuses; missing worker artifact refuses. - -## Slice 2 — modeled with slice 1, implemented after proof - -Context map/reduce: partition ingest reads by source-root file identity. Mappers produce partial fold state; reduce concatenates roots, merges indices fail-closed on duplicate module names, concatenates file_refusals. Placement: same `LocalChildProcess` arm. Width: same envelope + `select_realization`. Merge name: `context_shard_reduce`. The reduce is not a fallback to serial context. Implementation waits on slice 1's merge law + instrument + three-arm receipt existing and green. - -## Honesty vs a later implementation attempt - -A construct attempt existed on `session/crisp-hawk-831` / #11378 and is **not** the landed program. Known gap against this model: children that re-exec and rebuild context are not fork-after-context; `(ii)−(i)` would be contaminated until workers inherit the parent's context (COW fork or a serialized context artifact) without re-ingest. - -## Where bytes would live (when implementation is authorized again) - -- `dag/std/realization.dag` — `LocalChildProcess` -- exhaustive `Placement` matches (accelerator demo and peers) -- `std.compiler_entry` keyed exclusive roster — `ExclusivePrepareShardMerge` / wire name `prepare_shard_merge` -- `gunbc.native_prepare_shard` — contract (envelope, selection, assignment, merge, artifacts, residual law, slice 2 standing) -- `v1.compiler.emit_rust` `SourceRootEvalDriver` — production default remains in-process; child path is opt-in via placement + selected width (selection receipt, not a second knob) -- witnesses: `dag/test/claim/native_prepare_shard_witness_test.dag` and exclusive-key exhaustive matches including `ExclusivePrepareShardMerge => 0` diff --git a/docs/plans/native-scm-cas-fit-and-consumer-cut.md b/docs/plans/native-scm-cas-fit-and-consumer-cut.md deleted file mode 100644 index c825dedd8c9..00000000000 --- a/docs/plans/native-scm-cas-fit-and-consumer-cut.md +++ /dev/null @@ -1,81 +0,0 @@ -# Native SCM: CAS semantic fit and first consumer cut - -Phase 0 ruling, 2026-09-13. This records the dependency contract for Phase 1; it declares no SCM carriers and implements no transition. The consumer is the Phase 1 implementation and its composition evidence. This record remains the semantic boundary for subsequent authoring and landing routes. - -## Shared authority and payload fit - -The leasing/locking home is `std.durable_compare_and_set`, realized by `gunbc.durable_cas_file_store::file_compare_and_set`. `CasOutcome` needs no additional arm for stale generation, absent target, or unexpectedly occupied target: - -- `CasPreconditionFailed { expected: ExpectSlotGeneration { generation }, observed: CasReadableAbsent }` preserves a missing expected slot. -- `CasPreconditionFailed { expected: ExpectSlotAbsent, observed: CasReadablePresent { version } }` preserves an unexpectedly occupied slot. -- `CasPreconditionFailed { expected: ExpectSlotGeneration { generation }, observed: CasReadablePresent { version } }` preserves a generation disagreement. `CasSlotVersion` carries the observed generation, content hash, and value. - -The successful arm retains the committed version. `CasStoreRefused` retains `CasUnreadableSlot`, including malformed state, missing referenced content, and refused reading. `cas_decide` preserves the readable/unreadable distinction before evaluating the expectation. No SCM-local synonym sum may replace this authority. - -Exact native parent identity and slot generation are different facts. A generation expectation alone does not identify the native parent: the consumer must bind the proposed native transition to the parent held by the observed slot version. Git SHA, PR number, and CAS generation are not native commit identity. - -## Realization findings: payload sufficiency is not execution sufficiency - -Inspection of `file_compare_and_set` confirms that its initial eligibility branches preserve all three precondition cases above. `cas_probe_as_readable` supplies generation, recomputed content hash, and value for `ProbedHead`. - -The realization does **not** preserve the full refusal partition on every path: - -1. `cas_probe_from` treats every unsuccessful `Filesystem.Read` as the chain end. Permission or I/O refusal can therefore become absence or an earlier apparent head. Its comment names typed read failure as unavailable, but `extdeps.filesystem.filesystem_io::Filesystem.Read` now exposes `error_kind`, and `filesystem_exact_read` distinguishes absent, unreadable, and unrecognized failure kind. The adoption capability exists; this consumer has not adopted it. -2. `cas_commit_at` maps every unsuccessful `Filesystem.WriteOwnerOnly` to `CasPreconditionFailed`. It does not establish that another writer won. An operational write refusal can therefore be mislabeled as a precondition failure, even when the re-observed state still satisfies the expectation. -3. That same failed-write branch calls `cas_probe_as_readable`, whose `ProbedBeyondBound` arm deliberately returns `CasReadableAbsent` for its documented reporting use. The defect is consuming that projection to construct a decision payload without establishing a lost race. In contrast, the initial `file_compare_and_set` branch and `observe_cas_slot_state` preserve a bound refusal as `CasUnreadableMalformed`. - -These are defects in consumption of the existing authority, not evidence for a fourth CAS outcome. Phase 1 must repair the realization at its existing home and preserve transport refusal through the shared outcome. `WriteOwnerOnly` currently omits the host `error_kind` output that `Read` and `WriteCreateNew` expose; its consumer must obtain typed write classification without parsing error prose. - -No new wet execution was performed for this ruling. The findings above are source-contract findings, not a claimed rung or proof of durable publication. Exclusive creation must not be confused with atomic visibility of complete bytes or crash durability. The Phase 1 composition evidence must establish those publication properties before a mutable slot may name an immutable object. - -The transport distinction is concrete: `v1_compiler.v1_interpreter::write_file_owner_only` opens the final path exclusively and then writes its content. A post-open failure can leave partial bytes at the slot path. The adjacent create-new contract explicitly treats owner-only exclusion as incidental to permissions, whereas `extdeps.filesystem.filesystem_io::Filesystem.WriteCreateNew` promises create-only publication and its realization publishes complete content through a sibling file. Phase 1 must bind the CAS realization to contractual exclusive, complete publication and establish durability; correcting error classification alone is insufficient. The transport realization home is `extdeps.filesystem.rust_realization`, not an SCM-specific filesystem algorithm. - -## Read-back is a separate relation - -Independent read-back consumes a committed receipt and observes the same target. It cannot rewrite `CasCommitted` into a claim that the CAS did not commit. Store refusal, precondition failure, committed-with-read-back-unavailable, and committed-with-read-back-disagreement remain distinguishable. Only the last two require composition over a committed receipt; neither extends or replaces `CasOutcome`. - -A later target generation also cannot erase the receipt: read-back must distinguish subsequent advancement from disagreement about the committed version. A verified transition requires evidence bound to the exact committed subject. A caller-authored expected value is not independent observation. - -## Exact Phase 1 dependency and consumer cut - -Existing producer symbols to consume: - -Before either SCM slot consumer relies on the store, repair these prerequisites in order: - -1. Bind CAS to contractual exclusive, complete, durable publication at the existing homes `extdeps.filesystem.rust_realization` and `gunbc.durable_cas_file_store`. The finding is `gunbc.recurring_failure_mode.consumer_relies_on_incidental_realization_property::consumer_relies_on_incidental_realization_property`. An SCM-local safe-write helper would fork the existing authority. A correctly classified refusal over torn slot bytes is still unsafe, so this prerequisite comes first. -2. Repair refusal-as-contention and unreadable-as-absence at `gunbc.durable_cas_file_store`, consuming the existing typed filesystem observations. The finding is `gunbc.recurring_failure_mode.store_refusal_reported_as_contention::store_refusal_reported_as_contention`. - -These are source-verified readings of contract mismatches, not executed reproductions. The repair lane owes the discriminating REDs. Both defects affect the existing compute control-plane consumer; absence of an SCM caller does not make them prospective. - -- `std.durable_compare_and_set::{CasAttempt, CasExpectation, CasGeneration, CasSlotVersion, CasOutcome, cas_attempt, cas_decide}`: the shared conditional-publication vocabulary and decision. -- `gunbc.durable_cas_file_store::{admit_cas_attempt, VerifiedCasAttempt, file_compare_and_set, observe_cas_slot_state}`: verified attempt admission, store-owned conditional publication, and independent slot observation. `VerifiedCasAttempt` verifies the encoded payload/hash pair; it does not verify the transitive SCM objects named by that payload. -- `extdeps.filesystem.filesystem_io::{filesystem_exact_read, filesystem_create_new}`: existing typed transport-result classification. Upstream filesystem facts remain at that authority, conforming to `extdeps.external_authority::ExternalModelScope`; SCM policy belongs downstream. -- `gunbc.scm.object_store::{ScmObject, CorpusManifestObjectRef}` and `gunbc.scm.ancestry::RepositoryCommitRef`: existing SCM object and commit reference subjects. Their current locator semantics must not be silently promoted into durable cross-party content identity. - -The first consumer carriers to be declared **in Phase 1**, not here, are a repository-target generation slot and a workspace-scoped stage/base slot. These are semantic names for the cut, not claims that declarations already exist. - -The repository-target slot binds one repository and target to its native parent/commit reference and CAS version. Its conditional advance consumes the exact observed generation and a candidate bound to that parent. The workspace slot binds one independently mutable workspace to its selected base and staged manifest, with its own CAS version. Two workspaces share immutable objects and repository targets, but never one mutable stage/base slot. Workspace identity is independent of author identity. - -For each slot, the consuming route is immutable object put-if-absent publication, durable independent verification of the referenced closure, slot payload encoding, `admit_cas_attempt`, `file_compare_and_set`, independent read-back, then composed transition standing. Any unavailable capability refuses at its own boundary. The first two steps cannot be substituted with an in-memory object-table insertion. - -`gunbc.scm.repository_envelope::RepositoryEnvelope` currently carries one document's stage selection; it is not the multi-workspace slot authority. `gunbc.scm.repository_save::save_repository` persists a checked document unconditionally and must not become the conditional publisher. Phase 1 consumes the shared CAS home instead of cloning it or hiding mutable workspace selection inside that document. - -The cut closes only when wet evidence executes the entire object-to-verification-to-CAS-to-read-back composition for both slot subjects, including contention and independent workspaces. Re-proving the isolated linearizer, declaring these carriers, or landing a ticket does not satisfy that trigger. - -## Mandatory Phase 1 provider gate - -2A, publication integrity, precedes 2B, outcome fidelity. Success means one exclusively published final generation containing the complete proposed value. Failure before publication means no final generation path ever became visible. An occupied target leaves its existing complete generation untouched. `WriteCreateNew` is the nearest surviving construction, but fsync of a private staging file does **not** establish durability of the final directory entry: either the contract and implementation cover the publication metadata, or the claimed durability rung stays lower. - -For 2B, real occupancy or a genuine race yields `CasPreconditionFailed` with the re-observed winner. Permission, storage, IO, staging, sync, or publication failure yields `CasStoreRefused`. Error prose is never parsed to choose between them. Finding no readable winner on a second observation cannot widen a store refusal into contention. - -The repair lane owes these wet controls: - -- Inject failure after staging writes begin: no final generation exists. -- Inject failure before publication completes: no committed CAS outcome is returned. -- Race two eligible writers: exactly one commits; the other reports precondition failure naming the observed winner. -- Cause a non-contention store refusal: preserve `CasStoreRefused`, never precondition failure. -- Restart and independently read back: exact generation, content identity, and value agree. -- Mutate the realization back to direct final-path open-then-write: the partial-publication control turns red. -- Mutate the fold to classify every failed publication as contention: the store-refusal control turns red. - -Phase 1 may not declare the CAS realization suitable for native SCM until **both 2A and 2B pass their wet controls**. This gate is a prerequisite to SCM consumption, not an assumption its tests may make. The three discovery records remain source-verified findings, not executed closures. diff --git a/docs/plans/native-scm-census-coverage.md b/docs/plans/native-scm-census-coverage.md deleted file mode 100644 index bb64a61d809..00000000000 --- a/docs/plans/native-scm-census-coverage.md +++ /dev/null @@ -1,45 +0,0 @@ -# Native SCM census: discovery and coverage standing - -This is an incomplete, source-verified census and an unconnected terminal policy. It is not an executing required-lane import wall. No complete Git-use population, successful policy-control run, or Git-free SCM execution guarantee is claimed. - -The appendable use rows live under `gunbc.git_use_census`; `gunbc.git_use_census.roster::git_use_rows` supplies them to `gunbc.git_use_authority::git_import_authority_verdict` in the pure controls. Adding one use does not regenerate DESIGN. These six enrolled uses are all classified: three workspace/source-authority uses, two proposal/review/publication uses, and one history/diff/gate-observation use. There are zero enrolled bootstrap/import, deployment/fleet-identity, compatibility-projection, or unclassified rows. That does not mean those populations are empty. The Rust population currently has three named rows, and the configured-process population one; neither roster is complete. - -## Discovery is unfinished - -Read-only textual searches nominated candidates for source inspection. Those searches are not a closed discovery oracle, do not establish reachability, and do not authorize a clean census verdict. Each enrolled row was checked against its consuming declaration. Unexamined candidates have not been disguised as typed `UnclassifiedGitUse` refusals. That arm is reserved for an observed use whose role cannot be decided, with its reason recorded. - -The remaining population is **unknown for two of the three surfaces and DERIVABLE for the third**, and an earlier blanket "unknown" here was wrong. Rust uses and configured `.dag` process invocations have no producer that enumerates them, so their remainder is genuinely unbounded at this revision. But `ParsedDagGitDependency` — the one surface this policy covers — is derivable *from the same import fact the policy consumes*: the non-fixture `.dag` modules under `dag/` and `src/` carrying a direct module-level `import extdeps.git*` or `import extdeps.github*`, partitioned by whether the module is itself inside `dag/extdeps/` (the authority side) or outside it (the consumer side). That recipe is the instrument, and its output is deliberately not transcribed here: a count copied into prose is unreachable from the thing that owns it and rots without anyone touching either end (DESIGN §6). Standing the recipe up as an entry point that re-derives the consumer-side population, and joining it to this ledger at identity grain, is the first obligation of the census-completion lane — it is what turns this surface's remainder from an assertion into a closed identity join. The two surfaces without a producer keep their existing next-rung triggers. - -One reading worth recording because it is a positive result rather than a gap: **no module under `dag/gunbc/scm/` carries a direct `extdeps.git` or `extdeps.github` import**, by that same derivation. That is the wall's own subject measured clean today — and it is exactly as narrow as it sounds, since it says nothing about the configured-process bypass, which no import fact can see. - -The existing `v1_compiler.cli_run::run_dag_parse_sweep` discovers paths before parsing and refuses unreadable directories, directory entries, source reads, and parse diagnostics. Its existing parsed `DeclarationIndex` is the specified import producer; this ticket must not add a second scan or parse. However, `DagParseSweep` currently returns the index and a count, not the independent pre-parse source-path roster. The proposed policy input requires that roster separately from the parsed module records. `git_import_authority_verdict` joins their path identities in both directions; an omitted expected source or unexpected parsed source refuses, even when counts agree. An unavailable or explicitly incomplete producer also refuses. Copying successful module paths into both inputs would violate the producer contract, not establish completeness. These declarations do not prove that a producer currently supplies the independent input. - -The remaining capability is to carry the existing required walk's source identities through its parsed-index projection, preserving every failure, then execute the same policy on the required acceptance path. Hand-Rust projection/invocation remains subject to the pending external scaffold approval. No host wiring is authored here. The existing line scanner `v2.std.layer_import_scan::layer_import_facts_live` is expressly unsuitable: its unreadable-source omission is recorded separately under `gunbc.recurring_failure_mode`. - -## The wall's actual population - -The policy declaration itself names all three coverage populations: - -- Direct `.dag` Git/GitHub imports: policy coverage only today. After required-lane enrollment and discriminating controls, closure can be reported mechanically preventable; the forbidden import remains writable. The structural next capability is construction of source dependencies that cannot express a forbidden edge. -- Rust Git/GitHub uses: unreached, reported as use-identity rows in the verdict. The next capability is a Node-derived dependency producer whose source scope includes Rust. -- Configured or opaque `.dag` process invocation: a **KNOWN BYPASS** of the direct-import policy. `gunbc.roadmap.roadmap_event_carrier::carrier_exec` executes `layout.git_program` without a use-site Git import. Closing this bypass requires Node-derived process provenance through capability bindings and caller-supplied arguments. No claim is made that merely finding Nodes establishes that provenance analysis already executes. - -In particular, the policy refuses direct Git/GitHub imports in `gunbc.scm.*`; it does not establish absence of Git execution there. Roster growth approval is another subject: review diligence only, mitigatable, until externally originated approval evidence for the exact changed obligation set is verifiable at the gate. A row cannot attest its own approval. - -## Mode and landing construction - -`NativeAuthorityMode` and the row disposition pair are a declared consumption frontier; the selector that read them is NOT in this PR and lands with its consumer. Their planned consumer is `gunbc.git_use_census.report::report_git_uses`, to be supplied by the separate census-completion follow-up: read `git_use_rows`, select each row's disposition, and render it beside the use identity. That entry does not exist today. Its trigger is execution of the report for both modes on the enrolled population. The removed selector test merely compared each projection with its input field; it established no production consumption. The production authority route belongs to ticket 6, not Phase 1's CAS consumption. It does not make the existing `gunbc.native_scm_interaction_contract` Landed presentation unwritable without evidence. The missing capability is an independently observed exact Git merge/read-back and imported native baseline producer, bound to the candidate, with an exclusive bridge-landing mint. No validator is claimed as construction. - -Bridge publication must deterministically project an exact native candidate to one idempotently maintained PR, independently observe and read back the merge, and import the next native baseline before reporting native landing. A legacy GitHub-main advance imports explicit Git provenance and makes proposals on the previous baseline stale for mechanical re-evaluation. Native-authority landing is the native exact-parent advance; Git projection follows it, and an outage records projection repair pending without rollback or Git fallback. Native identities and Git identities are mapped, never equated. These are future route obligations, not implemented routes in this phase. - -## Evidence and remaining acceptance work - -The first recurring-failure row compiled in a targeted remote closure. The remote compiler build for the pure control suite succeeded, but execution refused with `HostBudgetUnreadable`: that worker exposed no enforceable cgroup memory limit. Thus there is no successful pure-control execution receipt, let alone a required-lane receipt. The targeted policy-closure compile completed with two blocking diagnostics: `bridge` and `native` were not found in scope. Source inspection locates the rejection in `v1.compiler.infer::infer_expr_body`: its local-callable branch uses nonempty parameter cardinality to recognize callability, rejecting the declared zero-argument function parameters. This is a substrate blocker, reported to the parent for repair at its authority; no selector spelling workaround is authored. The requested local control invocation also did not execute: the checkout has no built interpreter, and the shim refused to substitute another revision. No local compiler build or budget override was used. The CAS and producer findings are SOURCE-VERIFIED readings of contract mismatches, not executed reproductions. - -The exact-path controls still owed are forbidden-import refusal; legal adapter and rostered-legacy acceptance; refusal after withdrawing or misclassifying either authorization; incomplete/unavailable producer refusal; a host mutation ignoring the policy refusal detected by a control; and a policy mutation admitting the forbidden edge detected by a control. Pure fixture tests do not discharge those required-lane obligations. The full production census, adapter inventory, source identity producer, host connection, and these execution receipts remain unfinished. - -The CAS ruling and ordered Phase 1 producer/consumer cut are in `native-scm-cas-fit-and-consumer-cut.md`. Its provider repairs are prerequisites, not work implemented here. - -The parent amended the Phase 0 exit gate to land these six rows explicitly incomplete. Closing the production census population is a separate follow-up lane owned by the parent, not an implicit addition to Phase 1. Required-lane wall wiring remains a declared frontier; the present identity-join input contract is not a receipt from an independently populated executing producer. - -The data-versus-callable decision is separate from the inference defect. Each constant disposition already lives in a `data GitUseRow`. The generic selector accepts deferred computations so only the selected route is invoked; those parameters are not nullary declarations returning constants. Replacing them with result values would change selection-before-execution into selection between already supplied results. That is not the intended route contract even with a working compiler. The selector specimen remains unchanged and blocked on the parent's separately dispatched nullary-callable inference repair. That lane owns the root-cause fix, its execution evidence, and its failure-mode row. diff --git a/docs/plans/native-succession-consumer-cutover.md b/docs/plans/native-succession-consumer-cutover.md deleted file mode 100644 index 307cb896ed1..00000000000 --- a/docs/plans/native-succession-consumer-cutover.md +++ /dev/null @@ -1,52 +0,0 @@ -# Native succession and operational cutover - -This is the L4 execution design, not an acceptance receipt. `v2.compiler.self_host.promotion_admission` owns promotion; `gunbc.v1_consumer_census` owns consumer relations and projects them into `gunbc.replacement_cut`. No generation has been promoted or consumer switched by this change. The native executable and compile interface are the L2 handoff, not assumed inputs that this document can supply. - -## Consumer trace - -The additional relations are produced by `gunbc.v1_consumer_census` `v1_native_cutover_selector_consumers` and `v1_release_pack_consumers`. Their discovery method and inspected revision/tree are carried by `producer_native_cutover_selectors`. The older producers retain their own pins. The pack relations derive from `gunbc.ci_release_bins` `ci_floor_required_artifact_names`; they are not a copied executable roster. That function enumerates required build/pack dependencies, which does **not** establish that each packaged executable runs. - -| Operational path | Existing selector / dispatch | Dependency that cutover must remove | -| --- | --- | --- | -| Cold CI and fleet builds | `gunbc.repo_self_build` `repo_self_build_package`, read by `repo_self_build_command` and `repo_self_build_all_bins_command` | Builds package `v1-compiler`; installing a native `gunbc` alone leaves this route live. | -| Missing witness executable | `tools.host_prelude` `witness_bin_ensure_built_typed` → `witness_bin_build_args` | Builds the enrolled binary from `v1-compiler` when the release path is missing. A warm run does not exercise this branch. | -| Claim dispatch | `tools.host_prelude` `run_gunbc_claims_ready` → `claim_batch` | The binary links the v1 interpreter. Required witness identities and their positive/refusal contracts must survive replacement. | -| Required build and witness lanes | `gunbc.witness_floor_workflow` `build_lane_run_step`, `witness_floor_run_step` | Both execute `claim_executor`; these relations already existed in the census. | -| Generated-artifact healing | `gunbc.witness_floor_workflow` `heal_regen_step`, `heal_repair_declaration_step`, `heal_repo_local_git_config_step` | Executes `gunbc run` for generation, agreement, pre-mutation repair observation and repository configuration. | -| Ordinary CLI run | `v1_compiler.main` `run_verb` → `v1_compiler.v1_interpreter` `run_in_context_with_args` | Linked interpreter execution remains even if a separate native compiler handles `compile`. | -| Required seed regeneration | `v1_compiler.required_regen_host` `compile_stage0` → `v1.compiler.compile` `compile_sources_selected` | Calls the v1 implementation in-process over the regeneration source population. | -| Rebuilt-seed second pass | `v1_compiler.required_regen_host` `run_built_seed_regen` → rebuilt `claim_executor --required-regen` | The executable digest is checked, but the executable is still the seed. This route is not evidence for seed-independent G1→G2. | -| Release pack, including product transports | `gunbc.ci_release_bins` `ci_floor_required_artifact_names` | Each target receives its own consumer relation. In particular, the Codex stdio transport's product contract cannot be retired as a compiler test. | -| Git hooks | `gunbc.githooks_pre_commit_emit`, `gunbc.githooks_pre_push_emit`, `gunbc.generated_artifact_merge_driver` | Current pre-commit/pre-push scripts configure git and format; the merge driver refuses and prints a repair recipe. Printed regeneration commands are not an automatically executed compiler path. The existing historical recipe relation does not prove hook execution. | - -These are source-body and caller traces, not measurements of executed processes. The census remains `PopulationMeasuredLowerBound`. Three gaps remain explicit: unclassified DAG argv rows outside the selected roots; dependencies internal to the deletion set beyond the traced regeneration and workspace-member relations; and whole-repository reachability. Neither a missing lexical match nor a present pack member settles reachability. Historical `Retire` rows are not proof that the current corresponding path has disappeared. - -The census's lower-bound standing must survive the eventual promotion/cutover composition. An empty blocking set in an incomplete population is not terminal admission. Likewise, a live pack projection observed against a newer tree does not refresh the discovery pin: re-run the manifest and caller joins before using those relations as cutover evidence. The name-to-main mapping in `v1_release_bin_main` was checked against the manifest at the declared discovery pin; future manifest path changes require remeasurement. - -For the next census pass, start from actual entrypoints and follow both the present-artifact and missing-artifact branches. Join workflow argv to its emitting declaration, binary name to the Cargo target/path, target to linked implementation, and runtime dispatch to the invoked operation. Retain the witness or input selecting a conditional branch. Report unresolved dynamic invocations as unmeasured edges, never as absent consumers. Cargo targets outside the release pack also need their own demand/disposition evidence before the population can become complete. The manifest inspection found `carrier_realization_census` and `type_occurrence_binding_census` outside that pack; both link v1 and remain unclassified here. Their absence from the pack is not evidence that their capability obligations can be deleted. - -## Proposed seed-exclusion boundary - -The producer must establish unavailability, not report that a trace happened to contain no seed call. This design is not yet an implemented sandbox or a receipt. - -1. Materialize a disposable execution root from an explicit input manifest: the pinned authoritative source closure S, the exact native G1 artifact, and the declared external toolchain and system dependencies. Do not mount the repository checkout, seed executables, seed source/build workspaces, shared Cargo target outputs, host home, Docker socket, or the oracle's filesystem. Remove inherited descriptors and credentials, prohibit network access, and constrain filesystem/process access to this root. An empty PATH alone is insufficient: an absolute path, inherited descriptor or helper could still reach the seed. -2. Build in a new isolated workspace/target directory using the emitted implementation. Join the complete Cargo dependency graph and every build-script/source input against the admitted build manifest. Refuse unknown dependencies and any retired implementation dependency. Package-name bans and `ldd` output alone cannot establish this: renamed packages, static linkage, or copied seed bodies evade them. The L1/L2 declaration-to-produced-source coverage and producer provenance are prerequisites for the claim that G1 itself contains the replacement implementation. -3. Launch G1 on S inside that boundary to produce G2 source; build G2 under the same input restrictions. The seed oracle runs separately and supplies comparison observations only. It cannot be a build dependency, subprocess, service, or fallback available to G1/G2. -4. Bind an enforceable memory cgroup for the launched process and its descendants and read the effective limit back before execution. A declared planning number is insufficient. The executor may be BuildBuddy or a CI runner; the capability, source pin and artifact provenance decide admission, not the venue. A sized BuildBuddy runner with a writable delegated child cgroup is an available realization to investigate, so no new required CI job is implied. -5. Exercise the boundary with deliberate attempts to execute a seed path, open a seed artifact, resolve a seed Cargo dependency, and invoke a fallback. Require the expected located denial from each attempt beside a permitted native/toolchain control. A failing process without the denial observation is unavailable evidence, not proof that exclusion worked. - -The correctly hashed rebuilt-seed specimen is filed in `gunbc.recurring_failure_mode.authority_substitution`. Its distinguishing control is a candidate wrapper whose self-launch succeeds while its build still links the retired compiler; replacement admission must refuse that wrapper. This control complements the external-access denials, since an in-process seed makes no external call to deny. - -The general host isolation and launch operations must be modeled at their owning `std`/`extdeps` layer before implementing a transport. L4 must not put an unmodeled sandbox into hand-Rust and then bless it with a Bool. - -## Binding evidence to the launched artifact - -`BehavioralComparisonObservation` currently carries outcomes and a corpus, and its receipt projections accept a caller-supplied `GenerationIdentity`. That API alone cannot prove which executable ran. For native promotion, the execution observer must obtain identity from the immutable artifact it actually launches and carry the observed launch binding through receipt assembly. - -A candidate realization is a sealed executable object or an immutable content-store object held by the launcher. The same object is hashed using the existing observed artifact digest carrier and executed; hashing a pathname and later reopening it leaves a replacement race. The launcher owns this binding, captures process completion and case output, and rejects any mismatch with the declared artifact before producing receipts. A read-only mount is only sufficient if its backing object cannot be replaced or modified for the run's duration. Dynamic loaders and shared libraries belong in the admitted dependency manifest too. This is a proposed transport contract, not a claim that current `WitnessBin.Run` enforces it. - -The remaining generation axes come from the pinned production inputs: producer artifact, source closure, target model, toolchain and build configuration, including build location or a verified path-remapping policy. G2 provenance joins the exact G1 launch, exact S input, emitted population and resulting G2 artifact. The caller cannot turn an unrelated successful observation into evidence for this chain merely by naming G1 or G2 at assembly time. - -Behavioral evidence must join required **case identities and outcomes**, including positive cases and intentional refusals. Equal counts are insufficient, as the census's same-size substitution control demonstrates for consumer identities. Planted faults need a reported disagreement at the expected case; process failure alone is not detection. The corpus used for G1, G2 and the fault control must be the same admitted population. Consume `gunbc.explicit_witness_admission` `known_red_probe_execution_roster` through its existing self-host projection; expected-red quarantine for a missing required capability remains open. Consume `seed_retention_frontier_roster` for retained obligations. Discharge each through execution or an explicit changed contract; do not copy either population into an L4 backlog. - -Only after those observations exist can they feed `admit_promotion`, and only after named consumer executions select its admitted artifact and old operational routes are retired can the terminal cutover claim hold. Required generated-file byte agreement remains a separate live obligation throughout; behavioral promotion does not waive it. diff --git a/docs/plans/nominal-type-declaration-plan.md b/docs/plans/nominal-type-declaration-plan.md deleted file mode 100644 index 6a41a0fb660..00000000000 --- a/docs/plans/nominal-type-declaration-plan.md +++ /dev/null @@ -1,260 +0,0 @@ -# PLAN — nominal types as the first consumer of the coercion frontier; delete refinements - -**Status: PLAN, revision 5 — design only. Do not re-enqueue.** Revision 4's O3, O4 and O5 are RULED (2026-10-02); O1, O2 and C5 are recut here on the operator's ruling; O6/C4 is a separate lane. Earlier history: Revision 3 (one declaration modifier -per point in the visibility plane, plus a `derives` list) was approved and then withdrawn by the -operator on 2026-10-02: too many keywords, and the nominal question is really a coercion question. -This revision recasts it as the **first consumer of the coercion frontier** that -[v2 compiler architecture](v2-compiler-architecture.md) §1.4 and its frontier line ("Coercion and -named conversions") name. §8 places revision 3 (option A) side by side with this model. - -Governing sections: DESIGN §4 (operations from inhabitance; the coercion ruling, `bool_indicator`), -§3 (replacement migrations), §4b, §5; architecture plan §1 (completion law, residual addressees), -§1.4. - -Every count is **provisional** — line greps at main `dd0614551b5`, not the census — and is not a size -(DESIGN §6). M0, the resolved census (`gunbc.instruments.type_declaration_use_census`, being built by -tidy-koi-264), is the only admissible size. - -> **PARKED (operator ruling, 2026-10-03).** M0 landed as an instrument -> (`//gunbc/instruments:type-declaration-use-census`, gunbc#13093) with **no M2 sizing**. Its receipt, -> instrument-dispatch run [37119024146](https://github.com/gunb-ai/gunbc/actions/runs/37119024146), is -> no observation (exit 2): native resolve reaches only part of the corpus and decides no casts, so the -> migration cannot be sized. The program resumes when a re-run of the instrument shows enough native -> resolve and infer coverage. S1 and P1–P3 are moot until then. Re-run the instrument for any figure; -> none is carried here. -> -> **Open items from M0's census** (posted on gunbc#13024 by tidy-koi-264; to be settled when this -> resumes): -> -> 1. **Bodyless declarations that are not `nominal_opaque`** (`type MachineWidth` and similar) -> have no row in the declaration table, and the census reports them as `BodylessUnclassified`. The -> plan must say whether they are phantom/index types, abstractions with no view, or a further class. -> Under rev 5's model they are the bodyless form, but whether every one of them needs modeled -> operations (§3) is not decided. -> 2. **Existing one-field records.** The census classes them as `UnconstrainedNominal`. Under rev 5 a -> one-field record *is* a nominal (records are nominal), so that reading agrees with §2. Whether they -> should still be reported as a separate row, so that authored brands and pre-existing records stay -> distinguishable in the migration, is open. -> 3. **Opaque's second disjunct** ("a nominal whose view no consumer outside its module reads") is not -> computed. Only `nominal_opaque` decides `Opaque`. Computing it needs view-read evidence, a -> representation read outside the declaring module. Rev 5's §3 relies on exactly that evidence -> (`EffectivePosixPrincipalName` → record), so the generated cutover needs it. -> 4. **`OperationWorkaround` for encoding** cannot be classed while encoding's capability home (S2) is -> unnamed. Until then, encode-shaped strips go to the exception list with the callee named in their -> evidence. - -## 1. What stays as ruled - -- `type X = Y` is a transparent alias: X and Y are the same type. -- Refinements (`where `) are deleted. Every invariant becomes a checked construction one layer - down. -- No typecase, and no conditional design for one. Matching is on constructors and closed sums. -- M0, the resolved semantic census, sizes the migration. -- The cutover is one atomic, generated PR that deletes `where`, `brand`, `nominal_opaque`, the v1 - spelling matches and hand tables, `gunbc.where_refinement_predicate_vocabulary` with - `WherePredicateGrounding`, and #12506's parked row together (the full consumer list is revision 3 §6, commit 97dea7c55fd, and stands unchanged). -- Checked construction returns `Result` residue plus a typed cause (`std.error_primitives` `Result`). - -## 2. The model: four forms, no new words - -> alias = equality · record = visible constructed data · bodyless = abstraction · functions = explicit progress - -| form | meaning | in | out | operations | -|---|---|---|---|---| -| `type X = Y` | alias; X and Y are the same type | — | — | Y's | -| `type X { value: Y }` | a new type with visible data | its constructor (confinable by `sole_constructor` + `admit_callers`) | field projection | product inhabitance (§4) | -| `type X` (bodyless) | an abstraction; the declaration is all a client sees | only by the module's modeled operations | only by the module's modeled operations | only modeled operations | -| `fn` | explicit progress between any two types | — | — | — | - -The brand (`where brand("…")`), the refinement and `nominal_opaque` stop being declaration facts: - -| old mode | becomes | -|---|---| -| brand | one-field record, public constructor | -| refinement | one-field record, `sole_constructor`, plus a public checked function `Y -> Result` | -| `nominal_opaque` | bodyless `type X` whose operations its module models (§3), or a record when the census shows nothing needed hiding (as for `EffectivePosixPrincipalName`, §3) | - -No crossing between a nominal and its representation is implicit (§5). A crossing is a residual -addressed to the call site (O4, ruled: the addressee home is `v2.std.residual`), and the developer -answers it with an ordinary function call (O5, ruled). - -**Routes are proven, never inferred from a signature (O1 as ruled).** A signature proves neither -exactness nor injectivity: `id.value`, `concat("roadmap:", id.value)` and `sha256(id.value)` all have -type `RoadmapNodeId -> String`. So every function remains a valid **explicit** conversion when it is -called. A function becomes a **compiler-discoverable route** (one a residual may name as its answer, -and one operations may be derived along) only when the compiler derives a structural homomorphism -witness for its body: - -- `X -> Y` whose body projects the sole field; -- `Y -> X` whose body applies the sole constructor; -- `Y -> Result` whose success arms construct `X` from the input by the sole constructor. - -Two proven routes for one crossing refuse as `AmbiguousTargetCandidate`. There is no keyword and no -route table: the witness is the authority. - -## 3. Bodyless types, proved against every current `nominal_opaque` site - -The bodyless form already exists and is already the abstraction/target-realized form: `std.types` -declares `type Unit`, `type Json` and `type Bytes` with no body, and `v2.std.type_binder` wraps a -bodyless declaration (`type_opaque_wrapper`). There are two `nominal_opaque` declarations today. - -**`std.types` `Secret` (`nominal_opaque = String`).** Today it is opaque in name only. The corpus has -`as Secret` casts in (M0 `NominalIntroduction` rows targeting `Secret`; most in `test.claim.*` fixtures; production mints in -`extdeps.cloud.gcp.adc_document`, `extdeps.cloud.gcp.secret_manager`, `gunbc.spark.glm_canary_converge`), -and `as String` casts out, e.g. `extdeps.cloud.gcp.secret_manager` -`encode_sm_access_version_payload_wire` and `extdeps.cloud.gcp.secret_ref` (M0 counts -them). Both directions are unconfined, so DESIGN §4b's "cosmetic until construction enforces it" is -literally its state. As `type Secret` (bodyless) it needs: - -- **trusted construction**: the mints are upstream reads (secret-manager payload decode, the ADC - document, credential reads) plus test fixtures. Each becomes a call to a modeled operation of - `Secret`, e.g. `secret_of_upstream_text(s: String) -> Secret`, confined by `admit_callers` to the - named upstream decoders and the named fixture modules (precedent: `extdeps.apple.app_attest` - `attestation_verification_from_implementation` admits named test fixtures); -- **trusted elimination**: the wire encoders and transports call a modeled `secret_wire_text(s: Secret) - -> String`, likewise confined; -- **its target realization**: `gunbc.rust_source_type_bindings` already has a row binding `Secret` to - `std::string::String`. It is `StillBareNameDebt`, waiting on "the same threading capability as Bytes". - -**`extdeps.access.posix_effective_principal` `EffectivePosixPrincipalName` (`nominal_opaque = NonEmptyStr`).** -It has one mint (`effective_posix_principal_observation`) and no read outside its module. Nothing is -hidden from anyone, so it is a **record** with `sole_constructor`, not a bodyless type. M0 confirms -there is no outside read before the cutover generates it. - -**The capability that does not exist yet: realized operations of an abstract type.** Today a -bodyless type's operations are host seams: `std.bytes` `utf8_encode_bytes` and `bytes_octets` are -self-calling bodies that the interpreter intercepts by **name**, and the Rust emitter realizes them from -a name-keyed bridge table (`extdeps.languages.rust.emit`). `std.bytes` marks that arrangement itself as -a `Scaffold` (`bytes_seam_host_realization_marker`). So `type Secret` has no trusted -construction or operation support that is not a name-keyed seam. Per the ruling, that capability is -what this program names and builds, rather than a word: - -> **AbstractOperationRealization:** an operation of a bodyless type is a function whose body is a -> target-realization row keyed by the function's **declaration** (not its spelling), whose -> mint and elimination are confined by `admit_callers`, and whose realization per required target -> (interpreter and Rust today) is declared beside the type's own declaration-keyed binding row. - -It dissolves the `Bytes` seam scaffold and the `Secret` `StillBareNameDebt` row with one mechanism. -It is also the "threading capability" that the row's restoration trigger already names. Its home is -the existing realization-binding authority (`gunbc.rust_source_type_bindings` and its interpreter -counterpart), extended by declaration key. It is not a new vocabulary. - -## 4. Operations: equality and hash from product inhabitance - -- A **record** has equality and hash when every field does. That is product inhabitance, so a - one-field record over `String` is comparable and hashable because `String` is - (`std.algebra` `algebra_profile_equality_extensional`; `std.content_hash` `HashFamily`). - `RoadmapNodeId == RoadmapNodeId` works, and `RoadmapNodeId` where `String` is declared still refuses: - the operation is derived, and the value does not cross. -- A **proven embedding** (a route with a derived witness, §2) may also carry equality and hash. A - signature never does. -- **Ordering never by default** (O3, ruled). It requires an operation the module writes. -- A **bodyless** type gets only its modeled operations. `Secret` has no display, JSON or equality unless - its module models them. Constant-time comparison is a modeled operation, realized per target. - -No `derives` list exists in any form. - -## 5. What `v2.std.coercion` and `v2.std.inhabitance` must gain (model-first) - -What exists today: `CoercionResult { target, quality: Identity | Exact | Widened, witness }`; -`CoercionMismatchKind = NoTargetCandidate | AmbiguousTargetCandidate | StructuralMismatch | -WouldLoseInformation`; `find_witness` under `TargetSelectionPolicy`; `declared_type_inhabitance` over -`DeclaredTypePosition`. `v2.std.refinement_widening_predicate` is representation value-set widening -(`rust i32 → python int`). It is not the `where` machinery and is kept. - -1. **C1: residual addressee** in `v2.std.residual` (O4, ruled). It is minted once and consumed by - `v2.std.coercion` first, then by completion (architecture §1.5). Each `CoercionMismatchKind` maps - totally to its addressee. -2. **C2: `ChangesInterpretation`**, a mismatch kind for an exact, lossless crossing that drops or adds - meaning (`RoadmapNodeId → String`, `true as Int`). Its addressee is the call site. -3. **C3: proven route discovery.** It derives the §2 homomorphism witness from a function body - (sole-field projection, sole-constructor application, success-arm construction), reusing - `v2.std.witness` `HomomorphismWitness`. A residual names the proven routes as its answer. Two proven - routes give `AmbiguousTargetCandidate`. -4. **C4: native `sole_constructor` minting and enforcement plus `admit_callers`.** This is **a separate - lane**, dispatched by sharp-raven-357 on 2026-10-03, closing both native drops - (`gunbc.rung_drop.admit_callers_discarded_on_the_native_route`, and `sole_constructor` neither minted - nor enforced natively). It is referenced, not built here. -5. **C5: product inhabitance for equality and hash** in `v2.std.inhabitance`: a record inhabits - equality or hash when its fields do. A proven-embedding arm is second. There is no signature arm. -6. **C6: AbstractOperationRealization** (§3), with `Secret` and `Bytes` as its first two consumers. -7. **C7: the first instrument** named by the architecture frontier: one core module emitted to the - Rust and TypeScript targets, with the per-target mismatch list checked against M0's rows. - -The migration is then generated from M0: brand → one-field record; refinement → `sole_constructor` -record plus checked function; `Secret` → bodyless with modeled operations; `EffectivePosixPrincipalName` -→ `sole_constructor` record; aliases untouched; introduction casts → constructor or modeled-mint calls; -workaround casts (strip to compare or hash) → the derived operation; true raw consumers → explicit -projection or modeled elimination. - -## 6. Controls (expecting red first) - -The six arms of `test.claim.brand_nominal_identity_witness_test` are kept, re-spelled over records: -COLLISION and MISMATCH refuse; CONSTRUCT, STRIP and DUAL accept; UNDECLARED_SITE refuses at parse. -These controls are added: - -| control | expected after | today | -|---|---|---| -| `RoadmapNodeId` at a declared `String`, no call | residual `ChangesInterpretation`, call site, names the proven projection | no addressee: **red** | -| `concat("roadmap:", id.value)` as a function of type `RoadmapNodeId -> String` | valid when called; **not** named as a route | **red** until C3 exists | -| two proven `X -> Y` routes | `AmbiguousTargetCandidate` | **red** | -| `Port { value: 0 }` outside the module | refuse on the v2 route | **red** (C4 lane's control) | -| `port_of(n: 0)` / `port_of(n: 80)` | `Err` with residue and cause / advances, by execution | positive control | -| `RoadmapNodeId == RoadmapNodeId` | accept by product inhabitance | M0 measures | -| `s as Secret` for a String | refuse; only `secret_of_upstream_text` from an admitted caller | **red** (cast admitted today) | -| `secret_of_upstream_text` from an unadmitted module | refuse | **red** | -| `Secret` displayed, JSON-encoded or compared, nothing modeled | refuse | **red** | -| `Secret` on the Rust target through the declaration-keyed row | emits and runs | **red** (`StillBareNameDebt`) | - -None of these retires (DESIGN §4b(4)). - -## 7. Rungs (DESIGN §4b) and §4's coercion law - -- **Identity:** structurally guaranteed. Records are nominal, and a bodyless declaration is its own type. -- **Invariants and confinement:** **no invariant is called structural until both native drops in the - C4 lane are closed.** Until then the v2 route is mechanically preventable at best, stated per type. -- **Secret:** structural only once C4 and C6 both hold. Before that it is cosmetic, as it is today. -- **Coercion law:** unchanged. The only silent route is the alias's identity. Every nominal crossing is - an explicit call, and a missing one is a residual with an addressee. - -## 8. Revision 3 (option A) against this model - -Populations are M0's rows, named by class; none is transcribed here (DESIGN §6). - -| | option A (rev 3) | rev 5 | -|---|---|---| -| **new words** | `nominal`, `sealed`, `opaque`, `derives(…)` = 4 | 0. Bodyless, `sole_constructor` and `admit_callers` already exist | -| **TrueAlias** | untouched | untouched | -| **UnconstrainedNominal** (old brand) | `type T nominal = Y` | `type T { value: Y }` (constructor in, projection out) | -| **CheckedConstructionStage** (old refinement) | `sealed` + checked ctor | `sole_constructor` record + checked function | -| **Opaque** | `opaque` | bodyless `type X` + modeled operations (needs C6) | -| **introduction sites** (M0 `NominalIntroduction` rows) | rewritten to a constructor call | same rewrite, generated; identical burden | -| **projection sites** (M0 `NominalProjection` and `OperationWorkaround` rows) | rewritten to `.value` | rewritten to explicit projection; **workaround sites** (compare, hash) **vanish** under C5 product inhabitance instead of being rewritten, so this model's burden is lower by exactly M0's `OperationWorkaround` count | -| **capabilities** | `derives(…)`: author-listed, a second vocabulary to keep in step with `std.algebra` | derived by product inhabitance; nothing listed; a bodyless type has only modeled operations | -| **invariant rung** | structural once `sealed` is enforced | structural once the C4 lane closes both native drops: **the same precondition**, already modeled and with a seed implementation | -| **`RoadmapNodeId` as `String`** | refuses; author projects `.value`, with no stated reason | residual to the call site naming the proven projection: the acknowledgment the operator asked for | -| **needs that do not exist** | 3 modifiers + derives parser/infer, visibility semantics, capability derivation | C1 addressee, C2 kind, C3 proven routes, C5 product inhabitance, C6 AbstractOperationRealization (C4 is its own lane) | -| **reusable beyond nominals** | no | yes: C1–C3 and C6 are frontiers the architecture plan already owes (`bool_indicator`, target emission mismatches) | - -The deciding row is the last one. Option A built a nominal-only mechanism next to an unfinished -coercion model. Revisions 4–5 finish the coercion model, and nominals fall out of it. That is §2's -test that net concepts must not grow by re-invention: `sealed` was a re-invention of -`sole_constructor`, which the revision 3 DFS missed. - -## 9. Decisions - -Ruled (2026-10-02/03): O3 (no ordering by default), O4 (`v2.std.residual`), O5 (answer by function -call); O1 recut to proven routes (§2); O2 recut to the bodyless form (§3); C5 recut to product -inhabitance (§4); C4 a separate lane. Left for the operator: - -- **P1: AbstractOperationRealization's home.** Recommend: extend the existing declaration-keyed - realization binding (`gunbc.rust_source_type_bindings` and its interpreter counterpart) from types to - a bodyless type's operations. Do not open a new module. -- **P2: Secret's fixture mints.** Most `as Secret` sites are `test.claim.*` fixtures. Either each test - module is listed in `secret_of_upstream_text`'s `admit_callers` (explicit and long), or a separate - confined `secret_fixture_of` operation admits a fixture prefix. Recommend: **per-module - `admit_callers`**, generated by the cutover from M0. A prefix is a policy hole any new test module - falls into silently. -- **P3: sequencing.** C1 → C2 → C3 → C5 land model-first, beside the C4 lane. C6 lands after C4 (its - confinement needs C4). The generated cutover lands after all of them. M0 runs independently. - Recommend: as listed. diff --git a/docs/plans/one-namespace-hierarchy.md b/docs/plans/one-namespace-hierarchy.md deleted file mode 100644 index 503531d453d..00000000000 --- a/docs/plans/one-namespace-hierarchy.md +++ /dev/null @@ -1,67 +0,0 @@ -# One namespace hierarchy (owner direction 2026-09-25) - -## The ruling - -There is ONE hierarchy, and every context uses it. - -A node's position is its path in the namespace graph `x.y.z`. Walking that path -from a node to the root yields every alignment level: the issue's own node, -each named ancestor, the implied nodes (the repo level — "gunbc" — and every -folder/namespace segment in between), up to the root. - -- **Alignment is the namespace walk.** Working an issue nested in the - hierarchy means aligning to each parent in turn, up from your own node. The - alignment ladder's chain (task → projects → root) becomes a derivation over - the namespace path, not a separately maintained relation. -- **Implied nodes are first-class levels.** "gunbc" (the repo) and every - intermediate namespace segment exist as alignment levels even when no issue - or project row names them. A level's existence does not depend on a row; - rows attach to levels, levels come from the namespace. -- **A project is an instantiation of a time-bounded namespace chunk of work** — - usually one touching a namespaced program (e.g. `gunbc.spark.*`). A project - is not a parallel tree; it is the namespace slice - `(subtree root, time window, work intent)`. -- **Issue hierarchy is the same graph.** Upstream/downstream (parent/child) - relations between issues are contiguity in the namespace; a child issue's - default parent is its namespace parent. Blockers remain the SEPARATE - must-complete-before-start dimension (dependency edges, not hierarchy). - -## What this unifies (the three graphs we currently carry) - -1. The module/file namespace (`gunbc.roadmap.roadmap_page`, folder paths) — - today implicit. -2. The alignment ladder's hand-maintained task→project→root rows - (`roadmap_alignment.dag`) — today a parallel relation. -3. The issue tracker's parent/child fields (`extdeps.google.issue_tracker`) — - today a third relation. - -Target: one namespace graph; (2) and (3) become projections/annotations of it. - -## Consequences queued - -- The alignment chain derivation becomes: walk the node's namespace path, - materialize implied levels, overlay declared rows (projects/issues) at their - levels. `AlignmentChainRowUndeclared` then means "a row references a - namespace level that does not exist," and ambiguity means "two declared - owners for one level." -- The issue hierarchy (nested tickets, tracker lane) derives its nesting from - the same walk — the nested-menu rendering is the namespace tree with issues - attached. -- Dispatch alignment (the pending authority consolidation) consumes this: - the brief renders every level from the node to the root, each with its - alignment standing. -- Witness discipline: one derivation, three projections — a change to the - namespace graph must move all three projections together; a projection that - can move independently is a derivation defect. - -## Open questions (owner, when convenient) - -- Namespace identity for implied levels: is `gunbc.spark` a first-class node - with its own standing/state, or only a waypoint that aggregates its - descendants' standing? (Affects the cadence math and the dashboard's tree.) -- Do filesystem folders and dag module segments share one namespace by law, - or are they two namespaces with a declared mapping? (They are currently - close but not identical: `dag/gunbc/roadmap/` ↔ `gunbc.roadmap.*`.) -- Project time-bounds: does a project's window attach to the namespace slice - itself, or to one instantiation row, allowing sequential instantiations of - the same slice? diff --git a/docs/plans/org-admin-credential-acquisition.md b/docs/plans/org-admin-credential-acquisition.md deleted file mode 100644 index 4576c11c741..00000000000 --- a/docs/plans/org-admin-credential-acquisition.md +++ /dev/null @@ -1,129 +0,0 @@ -# Org-admin credential acquisition, custody, and read path - -Status: modeled; no credential has been obtained, stored, or read. The storage-class and freshness -ruling is from the parent relay of the 2026-08-30 pricing-study memo. Upstream capability and token -facts are owned by `extdeps.github.org_admin_auth` and cite GitHub's documentation there. - -## Decision - -The terminal credential is a GitHub App installation access token. The durable secret is the app -private key; the actuator mints a narrowly scoped installation token for each invocation and lets -that token expire after one hour. This makes the broadly useful bearer token short-lived and -programmatically renewable. - -The acceptable interim is a human-created fine-grained personal access token whose resource owner -is the organization and whose permissions are organization `Self-hosted runners: write` and -`Administration: write`. Store it with an explicit expiry and rotate-before date. A classic PAT -with `admin:org` works but is broader than the fine-grained PAT. An OAuth App token also works after -interactive web or device authorization, but is user-bound and long-lived by default; it adds an -application and refresh lifecycle without improving this interim. Neither is selected. - -## Acquisition census - -| Shape | Converge access | Acquisition | Lifetime and rotation | Revocation | -|---|---|---|---|---| -| Fine-grained PAT | `organization_self_hosted_runners` read/write covers runner groups, selected repositories, and organization runner registration tokens; `organization_administration` read/write covers the organization Actions policy | Human creates it in GitHub's web UI; organization approval may be required | Configurable expiry; rotate before expiry and revalidate after replacement | Token settings, organization policy/approval, user removal, leak detection | -| Classic PAT | `admin:org` covers the same organization endpoints (and more) | Human creates it in GitHub's web UI | User-selected expiry; GitHub may revoke on expiry, exposure, or prolonged non-use | User credential settings, OAuth authorization API, organization/enterprise incident controls | -| GitHub App installation token | Installation with organization `Self-hosted runners: write` and `Administration: write` covers the converge surface | App registration/key and installation require an owner decision; each access token is then minted programmatically from the app key | Installation token expires after one hour; mint per invocation, never rotate a stored installation token | Suspend/uninstall the installation, reduce permissions/repositories, or revoke/rotate the app private key | -| OAuth App user token | `admin:org` covers the organization endpoints while the authorizing user remains an org admin | Interactive authorization-code web flow or device flow | Long-lived by default; optional eight-hour access token plus refresh token | User revokes authorization, app owner revokes token/authorization, org/enterprise policy, user loses admin standing | - -The GitHub endpoint authorities are `extdeps.github.org_admin_auth.runner_groups_citation`, -`actions_permissions_citation`, `installation_token_citation`, and `oauth_flow_citation`. The model -does not bind any of them to `gh`: CLI, REST, and SDK are interchangeable realizations. - -## Custody and read path - -`gunbc.auth.org_admin_credential.SecretMaterial` is a custody descriptor, not secret bytes. It -records one store, an allow-list of readers, rotation, credential standing, and the validation -receipt. Actual bytes remain behind the existing -`gunbc.auth.materialized_secret.with_materialized_secret` bracket; this plan does not create a -parallel reader. - -For CI, use a GitHub Actions organization secret restricted to the repository and workflow lane -that performs org observation/apply. A repository secret is an acceptable narrower placement when -only one repository runs the actuator. For an operator session, use a named local credential-store -profile and expose it to only the validation or converge process. Do not put a token in argv, a -worktree file, committed configuration, a receipt, or this plan. - -Bytes-present and working-credential are intentionally different facts. `SecretMaterial.store` and -`CredentialCustodyStanding` answer custody. `CredentialOperationStanding` answers whether an -executed probe proves one exact capability on one target through the required horizon. A runner- -groups read receipt never authorizes mutation. `admit_org_admin_credential` refuses absent or -wrong-principal material, incomplete genealogy, a due probe, insufficient exact capability, and an -expired operation horizon. No arm turns absence into a skip. - -## Acquisition genealogy and runtime dependency - -`CredentialAcquisitionTransaction` is multi-input over a closed `AcquisitionRequirement` sum: -credential proofs, human authorization ceremonies, authority resources, grants, approvals, -authenticator bindings, attestations, and custody use. `CredentialGenealogyStanding` is immutable -audit history and is complete only with explicit roots, including the human ceremony and external -account-recovery boundary for privileged GitHub bootstrap. A ceremony receipt records the -authenticator classes GitHub accepted; it never records passwords/OTPs and never becomes current -authentication merely because it is recent. - -Present use is a separate `CurrentUseRequires` graph with a typed -`DependencyInvalidationEffect`; historical edges are `HistoricalOnly`, app-key loss can -`BlocksFutureMinting`, and only provider-evidenced relationships `InvalidatesCurrentUse`. -`RenewalRequires`, `CredentialRenewalCandidate`, and `CredentialContinuityStanding` form a third -graph, so an operator renewal obligation is not confused with an outage. This prevents an expired -browser session or revoked creation-time PAT from poisoning an otherwise usable app credential. - -## Fleet-converge consumer - -The existing `gunbc.fleet_converge_workflow.fleet_converge_job` owns the first consumer; there is no -parallel workflow. Its `org_actions_observe` mode invokes -`gunbc.fleet.org_actions_inspection.org_actions_inspect_wet`. The entry refuses a missing secret, -then composes the read through `std.goal_assessment.inspect_goal`: the goal-blind observer -`observe_org_actions` executes the read-only runner-groups call, strictly decodes the response and -reads each group's selected repositories through `extdeps.github.org_actions.CliOrgRunnerGroups`; -`gunbc.fleet.org_actions_standing.assess_org_actions` then compares the observed groups with the -desired goal. A refused or malformed read, or a repository-read failure, is a -typed observation refusal, and any desired-state divergence is a located `GoalDiverged`; each is a failing -typed outcome. It never silently skips and it has no write/fix-divergence arm. - -The validation receipt is uploaded as a workflow artifact only after the probe and joined reads -succeed, and it records the assessment verdict. The artifact contains identity, endpoint, organization, -verdict and time—not a token, header, -response body, or token fingerprint. Until the operator creates `GUNBC_ORG_ADMIN_TOKEN`, dispatching -the mode witnesses the intended missing-credential refusal. Once the secret exists, the same route -performs the live read without a code-path switch. - -## Interim human handoff - -**Superseded 2026-09-05, never taken.** The `org_actions_observe` step now mints a one-hour installation -token in-run from the existing `gunbai-ci` App key (Secret Manager `ci-github-app-private-key`, read -through the same WIF path as the fleet key), so no personal token is created and no Actions secret -holds a credential. The steps below are kept as the record of the design that was replaced. What -remains of the terminal migration is narrowing to a dedicated least-privilege App; the read path and -custody contract are already the terminal ones. - -1. In GitHub's fine-grained token UI, the operator selects `gunb-ai` as resource owner, grants - organization `Self-hosted runners: write` and `Administration: write`, and chooses a bounded - expiry. No repository content permission is needed for the org-settings probe itself. -2. The operator pastes the value directly into the chosen store: the restricted Actions secret for - CI, or the named local credential profile for a supervised session. The value is never pasted - into an issue, PR, shell history, command argument, or receipt. -3. Before any converge depends on it, the validation lane materializes it as an environment-bound - secret and executes the read-only `GET /orgs/{org}/actions/runner-groups` probe (the - `ListOrganizationRunnerGroups` endpoint). A 2xx response proves organization subject and - self-hosted-runner read scope without changing the subject. The later actuator must separately - require the write grants declared by the credential shape; a successful read is not fabricated - proof of write access. -4. The observer writes a `CredentialCapabilityProbeReceipt` containing only material identity, - credential kind, exact read capability, endpoint, organization, producer, effects, and - observation time. `CredentialOperationUsable` binds it to the target and validity horizon. It - contains no token, response body, headers, or recoverable token fingerprint. -5. Expiry, revocation, owner change, scope change, a missing receipt, or a receipt outside its - horizon changes standing away from `CredentialOperationUsable`; observe and apply both refuse before - mutation. Rotation repeats steps 2–4 and then deletes/revokes the prior material according to - the selected store's deletion semantics. - -## Terminal migration - -Register and install a dedicated GitHub App with only organization `Self-hosted runners: write` and -`Administration: write`. Store its private key using the same SecretMaterial custody contract and -existing materialization bracket. Mint an installation token per invocation, record its returned -expiry, run the same read-only probe, and discard it after use. Once that path has an executing -positive receipt and absent/revoked negative controls, delete the interim PAT row and revoke the -PAT; do not retain both as fallback authorities. diff --git a/docs/plans/parse-grammar-choice-overlap-residue-finding.md b/docs/plans/parse-grammar-choice-overlap-residue-finding.md deleted file mode 100644 index c7bc0af321d..00000000000 --- a/docs/plans/parse-grammar-choice-overlap-residue-finding.md +++ /dev/null @@ -1,90 +0,0 @@ -# `parse_grammar_choice_overlap_residue` — a named grammar deficiency nothing executes - -Status: finding, filed independently of the census that surfaced it. It outlives that census. - -**The unexecuted law and the unmeasured deficiency are the same fact seen twice.** - -Subject: `a6ca6882d18114b52532c0804dc89f97b441f493`. - ---- - -## The finding - -`v2.compiler.source_authority` `parse_dag_source_ast` refuses a large fraction of authored `.dag` -source, always with one reason: `parse_grammar_choice_overlap_residue`. - -Measured with a positive control in the same run, so a harness fault could not masquerade as a -finding: - -```text -CONTROL_3LINE the tree's own "module m / fn add" fixture => ACCEPTED -REAL_SMALL dag/gunbc/bash_materialized_transport.dag => ACCEPTED -REAL dag/extdeps/shell/exec.dag => REJECTED - reason = parse_grammar_choice_overlap_residue -``` - -Then a random 10-file sample of real corpus files: - -```text -A src/v2/std/constraint_satisfaction_predicate.dag -R dag/test/claim/filesystem_read_hermetic_witness.dag parse_grammar_choice_overlap_residue -R dag/test/claim/wet_hermetic_equivalence_witness_test.dag parse_grammar_choice_overlap_residue -R src/v2/test/claim/.../cargo_fmt_dead_param_test.dag parse_grammar_choice_overlap_residue -A dag/test/fixture/sole_constructor_sealed/admitted_caller.dag -A dag/extdeps/transports/file.dag -A src/v2/workflow/host_discovered_owned_data_manifest.dag -A src/v2/lens/structural_similarity.dag -R src/v2/test/claim/round_trip/source_authority_contract_test.dag parse_grammar_choice_overlap_residue -A dag/extdeps/cache/catalog_placement.dag - -6 accepted, 4 refused — all four the same reason -``` - -**The grouping is the finding, not the rate.** Ten files support no corpus refusal rate, and none is -claimed. They do support that five refusals across two source roots — four sampled at random, plus -`extdeps/shell/exec.dag` found independently — share one named cause: one grammar deficiency with -many victims, not scattered file-specific problems. Group by *why*, not by where the fix would be typed. - -## Why it is invisible - -`parse_dag_source_ast` has **no consumer**. Its only two call sites are inside -`canonical_dag_source_parse_print_law`, whose name occurs exactly once in the tree — its own -definition. It has no callers. - -So the deficiency is not hiding; it is never asked, because the only path that would surface it is -a law nothing executes — DESIGN §5's **specification-without-execution** class, in the compiler's -own source authority: a parse/print round-trip law, written, typed, never run. - -The two halves are one fact: a law nobody runs cannot report the deficiency it would catch, and an -unmeasured deficiency leaves the law looking healthy. Executing the law turned an unknown into a -named reason with a file list. - -## Why this matters beyond the census that found it - -1. **The v2 self-host program depends on this path.** `parse_dag_source_ast_with_model` sits under - `semantic_ir_from_source_with_model` and the normalize/resolve chain — the same - tokenize→parse→normalize→resolve pipeline the `wave1_gate1` body-producer witnesses exercise on - fixtures. Those witnesses pass on hand-authored three-line modules. The refusal population lives - in *authored corpus source*, where self-host has to work. -2. **Fixture-grain evidence is not corpus-grain evidence.** The gap between "parses `module m / fn - add`" and "parses `extdeps/shell/exec.dag`" is the gap between a green witness and a working - compiler; only the first is measured today. -3. **It is a wall on any future consumer of the parse route**, not just this census — including the - corpus-parse alternative considered in the - [projection increment spec](parsed-body-projection-increment-spec.md), which this finding is - part of the reason to reject. - -## What is not claimed - -- No corpus refusal rate. Ten files is a sample that establishes a shared cause, not a proportion. -- No root cause in the grammar. The reason symbol names a choice-overlap residue; which - productions overlap, and whether the fix is one rule or many, is not established here. -- No claim that repairing it makes the parse route viable for corpus-grain work — the separate - memory measurement in the increment spec says it would not. - -## Next step - -Measure the refusal population properly — every file under both production roots, verdict and -reason per file, as a structured artifact — and reduce the refusals to the grammar production(s) at -fault: a bounded measurement on an existing route, no seed change. Repairing the grammar is -downstream of knowing which productions overlap. diff --git a/docs/plans/parsed-body-projection-increment-spec.md b/docs/plans/parsed-body-projection-increment-spec.md deleted file mode 100644 index 0d614310555..00000000000 --- a/docs/plans/parsed-body-projection-increment-spec.md +++ /dev/null @@ -1,156 +0,0 @@ -# Parsed-body projection — increment spec (admission decision input) - -Status: **specification for an admission decision, not an implementation plan.** No work has -started or may start until the operator rules, because part of the increment lands in the frozen -v1 seed (§5). - -Audience: whoever decides admission. States what is broken, what would fix it, the evidence grade -per claim, and — separately, as different admission questions — which parts are v1 seed changes -and which are v2 `.dag` changes. - -Subject measured: `a6ca6882d18114b52532c0804dc89f97b441f493`. -Evidence: → [projection blocker and derived shell seed](shell-dag-census-0a-projection-blocker.md). - ---- - -## 1. What breaks without it - -SHELL-DAG-CENSUS-0A must derive, from parsed bodies, every authored path that can attempt -POSIX/Bash program interpretation, with a merge bar of **zero production parse refusals** and -**zero unexplained unknown routes**. That bar is unreachable — not difficult — on today's fact -surface, and the failure is silent. - -The sharpest statement is not the coverage percentage but this: - -> **A file that was never loaded reads identically to a file that carries no shell route.** - -No per-file `ParseRefused` row counts the difference, because non-import is not an event the -surface reports. So a census here produces not an incomplete answer that announces itself but a -clean, confident population that is silently wrong — DESIGN's **empty-observation narrow**, the -exact artifact this cut was dispatched to prevent. Everything below is downstream of that. - -## 2. The six axes, with evidence grade - -Evidence grade is stated per axis: a code reading and an executed discriminating pair are not the -same strength. - -| # | Axis | Remedy | Evidence grade | -|---|---|---|---| -| 1 | Service operations and transports are absent entirely — `ItemKind::ServiceItem` has no accessor | a `ServiceItem` accessor carrying operations, transport argv, and stdin channels | **Structural, verified independently.** The enum has six variants and exactly three have accessors; confirmed by the dispatching lane | -| 2 | `FnArrowDecl.output` is a wiring-liveness skeleton — a bound-but-unused `let` RHS is dropped | a body projection total over statements: effect positions retained independently of return reachability | **Execution-proven** (discriminating fixture pair, §3) | -| 3 | No named-argument edges — labels erased, literals hoisted to the call node | argument edges labelled with the authored argument name, positional index preserved | **Execution-proven** (same run, §3) | -| 4 | No arm or occurrence identity | stable occurrence identity on body nodes; arm identity on match arms | Read from the marshal (`marshal_generic` emits undifferentiated positional children) | -| 5 | Callee is an authored lexeme, not a resolved identity — a string literal and a constructor are the same atom | resolved declaration identity on callees; a node-kind discriminator separating literal / callee / constructor / variant | Read from the marshal (`atom_identity_node` is the single atom kind) | -| 6 | The registry is the entry's import closure, not the corpus | a corpus-grain denominator that is an enumerated file set, not an import closure | **Execution-measured** (§4) | - -### 3. The execution proof for axes 2 and 3 - -Two functions of identical shape, folded through `fn_arrow_decl_facts_live`, atom identities -collected from each `output`: - -```text -fn fixture_dead_let_shell() -> String { - let unused_result = fixture_sink(program: "echo DEADLETMARKER") - "returned-without-using-unused_result" -} -fn fixture_live_named_args() -> String { - fixture_sink2(program: "echo LIVEMARKER", args: "echo ARGSMARKER") -} - -DECL probe.fixture.fixture_dead_let_shell ATOMS: (empty) -DECL probe.fixture.fixture_live_named_args ATOMS: fixture_sink2 | echo LIVEMARKER | echo ARGSMARKER -``` - -The dead-let arm yields **nothing** — callee identity and program literal both absent; the live -arm yields both. Same construct, opposite verdict, so the loss is the projection's, not the -probe's. The live arm also demonstrates axis 3: three atoms in authored order with **no labels**, -so nothing records which literal was `program:` and which `args:`. - -### 4. The measurement for axis 6, with its counting method stated - -A fold over `fn_arrow_decl_facts_live()` under `--source-root dag --source-root src/v2` reported -**1,698** declarations. - -The denominator depends on the counting method, so it is stated: - -| method | count | note | -|---|---|---| -| line-start `fn` / `func` / `test fn` | **41,965** | the accessor's filter is `ItemKind::FnItem \|\| FuncItem`, and `ItemKind` has no separate test variant, so a `test fn` **is** an `FnItem` — this is the matching denominator | -| line-start `fn` / `func`, excluding `test fn` | 30,851 | the same count with the 11,114 test declarations removed | - -The conclusion is invariant: **1,698 of 41,965 is 4.0%; of 30,851 it is 5.5%.** Either way the -surface is a small single-digit fraction of the tree, and its content is whatever the entry -happened to import. - -This is a declared frontier, already guarded: `v2.lens.affected_set.corpus_dependency_view` calls -`fn_arrow_decl_substrate_is_whole_tree` and, when false, routes to a host refusal reading -`corpus_dependency_view per-PR execution refused … (blocked-on-#6239)`. Fail-closed, correctly. -**A census inherits that refusal**, which is why axis 6 is plausibly not new work — see §5. - -## 5. Which parts are v1 seed changes and which are v2 `.dag` changes - -Separated because they are different admission questions. - -| axis | v1 seed (`src/v1/stage0/src/coproduct_reflection.rs`) | v2 `.dag` | notes | -|---|---|---|---| -| 1 | **yes** — a new `eval_service_decl_facts_live` accessor | yes — the carrier type and its `.dag` surface | additive | -| 2–5 | **yes** — a new non-lossy body marshal | yes — the carrier types | **additive, not a modification** (below) | -| 6 | **probably none** | none | likely already-sanctioned pending work (below) | - -Two properties matter for admission: - -**Axes 2–5 are an additive second accessor, not an edit to the existing one.** The existing -marshal's lossiness is not a defect — dropping a dead `let` RHS is exactly what -`v2.lens.wiring_liveness` needs, and its comments say so; changing it would break that lens. The -increment adds a second projection beside it, keeping the blast radius off every current consumer, -so the freeze question is "may the seed grow a new accessor", not "may its existing semantics -change". - -**Axis 6 is probably not this increment's to fix.** The whole-tree registry path exists, gated -on #6239 with a typed refusal in place; if that lands, axis 6 closes without anything here. -Listed for completeness, not as a request. - -## 6. The cost argument for doing it at ingestion rather than corpus-wide - -The alternative — a corpus-wide fold parsing source in `.dag` via `v2.compiler.source_authority` -`parse_dag_source_ast` — was measured, and fails on **both** axes: - -- **Correctness.** Random 10-file corpus sample: 6 accepted, 4 refused, all four with the same - reason as the earlier `dag/extdeps/shell/exec.dag` refusal — `parse_grammar_choice_overlap_residue`. - One grammar deficiency, many victims. The refusal set contains the census's own seed file, and the - merge bar is zero production parse refusals. Filed separately: → [parse grammar choice overlap - residue](parse-grammar-choice-overlap-residue-finding.md). -- **Cost.** 1 file / 63.1 s; 10 files / 67.5 s; 40 files / **`EXIT=137`, OOM-killed after 34 files**. - Marginal cost ≈0.49 s per file against ≈62.6 s fixed world-acquisition overhead, so time is not - the wall; memory is — and not from large files: the kill came at file ~34 of 40 with only - **94 KB** of source consumed, on ~7.8 KB files, the 212 KB outlier sorted last and never reached. - The mechanism (parse-tree retention vs interpreter heap growth) is **not** established or claimed. - What is established: the process cannot hold the result of parsing 34 small files, against a - subject of 3,733 files and 31.3 MiB. - -DESIGN §6 already rejected this shape: the corpus-wide censuses were deleted in #8140 because -*"the unit of computation was the world, the unit of fact was one module's authorship, and the -price was paid by every consumer that wanted a witness roster and nothing else."* Its declared -next-rung trigger is exactly this increment's shape: a fact *"belongs on the module's own -declaration, checked at ingestion where the module is parsed anyway — one module's facts from one -module's source — rather than reconstructed corpus-wide by a consumer that wanted something -else."* - -## 7. The admission question, stated without advocacy - -The v1 seed is frozen with an admission test of **purpose**: a change is admitted when it serves -the v2 self-host program. This increment does not obviously pass, and the decision is not the -requesting lane's. Both readings are recorded: - -**For admission.** A non-lossy body projection is the substrate capability *any* consumer needs to -read real bodies rather than a wiring skeleton. The existing surface is why `v2.lens.effect_reach` -classifies host-effect sinks by name equality against a remembered callee list — not a chosen weak -principle, but a surface that cannot express a stronger one. Every future lens wanting real bodies -inherits that ceiling. - -**Against admission.** The requesting program is a shell-migration census, not v2 self-host. Seed -growth justified by a neighbouring program is how a freeze with an active-maintenance arm becomes -absorbing, the standing danger `gunbc.v1_maintenance_standing` names. - -If admission is refused, the census stays blocked, honestly. Worse for this lane, correct for the -repository, and preferable to a census whose population is confident and silently wrong. diff --git a/docs/plans/planning-work-contract.md b/docs/plans/planning-work-contract.md deleted file mode 100644 index 974ca2df457..00000000000 --- a/docs/plans/planning-work-contract.md +++ /dev/null @@ -1,77 +0,0 @@ -# The planning work contract (owner directive 2026-09-25) - -Supersedes the planning-ticket-type and Fabric·planner-role sketches from -conversation (never landed). Do NOT add Planning to the issue-type vocabulary; -do NOT introduce a second workflow role or principal. - -## The model - -**Planning is one first-class work contract**: a bounded effect envelope whose -result is an `IssueRevisionCandidate`, an investigation report, or a comment -response. It is the same generic work request assignment mints, with a -different envelope — not a different kind of issue, agent, or principal. - -The durable invariants stay put: - -- the issue remains the durable problem/outcome; -- component remains domain placement; -- assignment remains accountability; -- parent/child and blockers remain separate graph dimensions. - -## Issue changes are versioned candidates with CAS application - -Fabric may: comment, propose field changes, propose relation changes, propose -draft children. Fabric may NOT: directly rewrite the target issue, or edit -code, under a planning contract. - -```text -IssueRevisionCandidate { - issue - base_issue_revision -- the version it was derived against - proposed_field_changes - proposed_relation_changes - proposed_draft_children - author / work-request identity -} - -apply(candidate, expected_revision = candidate.base_issue_revision) - → Applied { new_revision } - | RevisionConflict { current_revision } -- CAS refusal - | CandidateRefused { cause } -``` - -A proposal never mutates the issue until accepted through the CAS route. The -acceptor is the issue's accountable human (or a later explicit automation -ruling) — the candidate mechanism itself grants no write path. - -## Comments are durable narrative events, never implicit commands - -Ordinary prose has no workflow effect. The explicit operation is: - -```text -AskFabric { issue, referencing_comment, asker } - → mints the same generic work request assignment mints - (bounded planning envelope) - → the answer lands as a comment response in the thread -``` - -No mention-parsing, no prose-triggered turns: if it isn't an `AskFabric` -event, nothing runs. - -## The ban - -No operational meaning may be encoded in: planning-flavored components, -Planning issue types, labels, title prefixes, or role-encoded assignee aliases -(e.g. "fabric-planner@…"). Structure carries meaning; names don't. - -Future recurring patterns are promoted only when they carry a DISTINCT effect -envelope, deliverable, admission rule, verification, or application route — -all five named, or it stays an ordinary work request. - -## Queue position - -Durable comments are already in flight (agent-52's vertical). This contract is -the next vertical behind it: the versioned IssueRevisionCandidate + CAS -application route, the AskFabric operation minting the generic work request, -and the bounded planning envelope (comment/report/candidate results only) as -the envelope vocabulary sibling of the execution envelope. diff --git a/docs/plans/power-on-sequence-model.md b/docs/plans/power-on-sequence-model.md deleted file mode 100644 index 62c83258e63..00000000000 --- a/docs/plans/power-on-sequence-model.md +++ /dev/null @@ -1,416 +0,0 @@ -# The Mt. Collins power-on account (power-on sequence model, Mt. Collins first) - -Status: PLAN. It encodes the side-chat rulings R1–R3 and Q1–Q2 and the corrections from the reviews of `e4e8a4a4d4` and `75f0bd113c`, all before any implementation. Owner: calm-lynx-884, under eager-gull-22 (hw-boot). - -## 0. The requirement - -Operator, 2026-10-03, verbatim: "take ALL of the information we've gotten today (and in our existing models) - and construct a monolithic 'this is what actually happens when you turn on the computer' workflow - meaning, our diagnostic process will be 'this is our best understanding of what happened' - basically, the end of the workflow (error, no error, any information we have) - and then, as we add more platforms (mt jade, x86), we will abstract the workflow and plug in the differing pieces as we need - this will give us an idea of how to onboard new platforms". - -The product is one **power-on account** per attempt: `gunbc.machine_intake_mtcollins1_power_on_account`. It covers success, degraded success, stated failure, repeated restarts, transport failure, partial observation and unresolved cause. It is never a pass/fail bit, and it never asserts past the evidence (DESIGN §4d, §5). It is monolithic for Mt. Collins. §9 records the seams for Mt. Jade and x86, and abstraction follows the second platform. - -## 1. Rulings this plan encodes - -- **R1 (side chat, decided): #13041 lands first, and slice A replaces it atomically.** Slice A deletes `gunbc.machine_intake_boot_outcome` `HostBootObservation` and its authored datum `mtcollins1_post_firmware_set_boot_2026_10_03`. In the same change, it makes firmware convergence (`gunbc.fleet.mtcollins_firmware_converge` `boot_verdict`) consume an operation-specific readback projected from the **whole** session (§6). Reaching UEFI is a milestone, not an ending, so the projection never reads the ending alone. The former slice E is folded into A. -- **R2 (decided): the opaque `CP:` reader is public, and it establishes only an opaque sequence relation.** It may say "cycle C of capture S ended after token X; reference capture R (digest, firmware and configuration scope) contains X followed by Y". It may not establish `DdrTraining` or any other `AmpereBootStage`, may not call Y a stage success, and may not locate an ending "at DdrTraining @ X". Semantic attribution belongs to the gunbc-private refinement slice (§8). -- **R3 (decided): the refused SMpro pair belongs to the secondary-join subject and is not decoded there.** Socket 1's `CUR_BOOTSTAGE 0x0001` / `BOOTSTAGE 0x03ff` is `extdeps.ampere.smpro_register` `SmproBootProgressRefused`. It licenses no "stuck at PMpro" claim, no DDR claim, and no other last-stage claim. It is carried inside `gunbc.machine_intake_mtcollins1_socket1_investigation_observation` `MtCollins1SecondaryJoinOutcome` (#13025, on main), which this plan reuses and does not re-derive. It also gets an open question on the join subject. -- **Q1 (decided): a component-owned console line establishes only that component's console milestone.** Examples: AMI `Press or ` gives `UefiConsolePromptObserved`; `UEFI Interactive Shell v2.2` gives the stronger `UefiShellObserved`; `GNU GRUB`, `EFI stub:` and `Linux version` give their exact component milestones. A milestone never establishes an `AmpereBootStage`, stage completion, health or a successful handoff. It carries the exact line and its parser, the cycle, the capture digest and its ordering evidence. -- **Q2 (decided): the account consumes one frozen, attempt-bound `AttemptConfigurationReceipt` taken before power-on** (§3). Historical rows supply topology only, never current occupancy: `mtcollins1_slots` is the 2026-09-10/11 16-DIMM census, and the `mtcollins1_physical_orientation` CPU rows are a 2026-09-23 operator report. -- **Fact correction.** **Socket 0's** CCIX record is GPI-pending (gated): in `smpro-err-20261003T005609Z`, slave 0x9E (socket 0) reads `GPI_RAS_ERR 0x7E = 0x0002` beside the ERR_CCIX_RCA_LINKUP_FAIL record (`0xA6 = 0x0074`, `0xA7 = 0x2244`). **Socket 1's** record is ungated per the retained hand read the side chat cites. In that capture, socket 1's (0x9C) error registers mostly refused (`rsp=0xff`), so the ungated reading rests on the hand read. The first draft had this reversed. - -## 2. What already exists and is reused (§3, §3b) - -| Concern | Owning authority (reused, never forked) | -|---|---| -| Firmware boot stages 0–9 | `extdeps.ampere.scp_diagnostic` `AmpereBootStage`, `AmpereBootStatus` | -| SMpro register pair and its refusal rule | `extdeps.ampere.smpro_register` `smpro_boot_progress_of` → `SmproBootProgress` (`SmproBootProgressRead` / `SmproBootProgressRefused`) | -| Per-socket SMpro reading | `gunbc.machine_intake_ampere_smpro_observation` `SmproSocketStage`, `SmproProbeOutcome`, read through `SmproPassObservation` | -| Second socket's join | `gunbc.machine_intake_mtcollins1_socket1_investigation_observation` `MtCollins1SecondaryJoinReading`, `secondary_join_outcome` → `MtCollins1SecondaryJoinOutcome` | -| SMpro/PMpro error records and GPI gate (#13058) | `extdeps.ampere.smpro_internal_error` `SmproInternalRecord`, `SmproRecordGate` | -| DRAM console block and roster | `gunbc.machine_intake_ampere_dram_console_observation` `AmpereDramConsoleObservation` | -| Firmware summary, inter-socket lines, CPER | `gunbc.machine_intake_ampere_socket_console_observation` `AmpereFirmwareSummaryObservation` | -| NVPARAM dump (#13059) | `gunbc.machine_intake_ampere_nvparam_console_observation` | -| Firmware console statements, SEL cycles | `gunbc.machine_intake_pre_os_bringup_verdict` `FirmwareConsoleReport`, `boot_cycles` | -| SEL vocabulary, Ampere OEM SEL | `extdeps.bmc.ipmi_sel`, `extdeps.ampere.oem_sel` | -| GRUB | `extdeps.bootloader.grub` `GrubConsoleReport` | -| Kernel module refusals | `gunbc.machine_intake_kernel_module_decompression_observation` | -| SOL collector, KVM, virtual media, handoff | `gunbc.machine_intake_sol_collector_observation`, `gunbc.machine_intake_mtcollins1_kvm_still`, the bundle's `MtCollins1MediaAttachRecord` / `MtCollins1HandoffMedia`, `extdeps.bmc.megarac` | -| Harness phases | `gunbc.machine_intake_mtcollins1_boot_phase_timing` `MtCollins1BootPhase`. These are what our harness did, never firmware stages. | -| Slot topology (J-label to socket/MC/slot), never current occupancy | `gunbc.machine_intake_mtcollins1_memory_census_observation` `mtcollins1_slots` (the 2026-09-10/11 census) | -| Firmware versions | #13041 `gunbc.fleet.mtcollins_firmware_baseline` | -| Authored outcome being replaced | #13041 `gunbc.machine_intake_boot_outcome` `HostBootObservation` | -| The host | #13055 `gunbc.managed_host` `ManagedHost` | - -## 3. Shape: one attempt, three orthogonal standings, cycles of subjects - -These three things are independent, and none may stand in for another: - -1. **What the host did** (`HostAccount`): its cycles, and per cycle the standings of each subject. -2. **What we observed** (`ObservationCoverage`): one standing per carrier, saying whether it was read, refused, empty, partial or not taken, and why. An empty SOL capture is a coverage gap, never SoC silence. A BMC authentication refusal is a coverage gap on the BMC carrier, never a failed "BMC came up" stage. -3. **Whether our workflow worked** (`CollectionOutcome`): media attach and handoff, override consumption, power-after, bundle write, credential residue. A media-read failure or a bundle-write failure is a collection outcome, never a host ending. - -Beside these sits the **attempt configuration receipt** (`AttemptConfigurationReceipt`). It is frozen and bound to one attempt, it is taken before power-on, and the account consumes only it. The fold never queries mutable or historical global rows. The receipt separates: - -- **expected configuration and policy**: the expected topology this attempt is judged against; -- **the requested stimulus** (for example "remove the socket-1 CPU", "J1 only", "firmware set 2026-10-02") **from the applied-stimulus receipt**. For manual CPU or DIMM work, the applied receipt is a human completion/inspection receipt the workflow consumes. A workflow-authored request is not evidence that the change happened; -- **the current physical population** per socket and per slot, each field with its standing: `LiveObserved { source }`, `OperatorAttested { receipt }`, `Conflicted { readings }` or `NotRecorded { reason }`; -- **live firmware readbacks** (BMC, SCP, UEFI, CPLD), taken after the physical configuration is set and before actuation; -- **provenance**: subject (the `ManagedHost` row), attempt, timestamp, artifact and digest. - -Static slot and orientation modules may map a label such as `J1` to topology. They may never supply current occupancy. A question that needs a field whose standing is `NotRecorded` or `Conflicted` becomes an open question, never a default. - -### Cycles - -A power-on attempt is a **session of cycles**. The old firmware stopped on cycle 1 and reached UEFI on cycle 2 (run 37062170720). The new firmware repeats one stopped cycle 6 times (steps 1, 2 and 2b). One cycle can have socket 0 reaching Linux while socket 1 never joins. So: - -- **Cycle segmentation does not depend on reaching DRAM.** - - Cycle 1 opens on the admitted chassis-power-on effect. - - Each later cycle opens on typed restart/reset boundary evidence: SEL `System Boot Initiated` / `S5` records, power or reset observation, and console evidence. - - A `DRAM FW version` banner binds a console span to a cycle. It is not the sole authority that a cycle exists, because a host can stop in SMpro, PMpro or ATF before the banner, and an empty SOL can sit beside SEL and SMpro evidence of a boot. - - Each cycle carries its identity (index, boundary evidence, console span if any) and the capture digests. -- **Within a cycle, standings are keyed by subject**, not by one total chain: - - `Platform`: the BMC and chassis power; - - `Socket { k }`: that socket's SMpro progress via `SmproBootProgress`, and its error records via #13058; - - `SecondaryJoin`: `MtCollins1SecondaryJoinOutcome`; - - `DramFirmware`: the console block, roster, and untrained sockets established only by a closed roster; - - `Uefi`: summary, NVPARAM, POST; - - `BootLoader`, `KernelStub`, `Kernel` and `Census`. - -### Stage attribution rule (R2 applied) - -A **firmware stage** (`AmpereBootStage`) is established only by a public `AmpereBootStage` reading, which is an SMpro register pair decoded by `smpro_boot_progress_of` (`SmproBootProgressRead`). Console output establishes **console milestones** (Q1): exact component-owned lines, such as the DRAM firmware block, the firmware summary, `UefiConsolePromptObserved`, `UefiShellObserved`, the GRUB banner, an EFI stub line, the kernel banner and the census marker. A milestone never implies an `AmpereBootStage`, completion, health or handoff. An opaque checkpoint token implies neither. This is structural: only the `Socket{k}` progress reading can carry `SmproBootProgressRead` and so an `AmpereBootStage`, while console subjects carry milestone readings that have no stage field (§5). - -## 4. Mt. Collins: subjects, carriers, and what success, failure and silence look like - -| Subject | Runs on | Carriers | Success | Stated failure | Silence or refusal | -|---|---|---|---|---|---| -| Platform: BMC | MegaRAC on standby | BMC reads, `mc info` | Answers. The reflash resets users and SEL (self-observed, 0.45.3). | Not applicable: a refused read is a coverage gap | Coverage gap | -| Platform: chassis power | BMC → sequencer | Chassis read, SDR rails, SEL `Power Unit` | On, rails ok | Rail not ok (`mtcollins1_sensor_anomalies`) | Power never reads on | -| Socket k: SMpro/PMpro progress | SMpro, PMpro | SMpro registers per pass | `SmproBootProgressRead` advancing | Reported status `Failed` | `SmproBootProgressRefused` is an open question, never a stage | -| Socket k: error records | SMpro, PMpro | 0x7E, 0xA0–0xAD (#13058) | No pending record | Only a `PendingAndDecoded` record. On 2026-10-03, socket 0's `ERR_CCIX_RCA_LINKUP_FAIL` (loc 68, code 116) is the one current stated error | A matching payload under `GpiUnavailable` or ungated (socket 1 on 2026-10-03) is an anomalous raw record plus an open question. It is never a second `DecodedBootError` or `PendingErrorRecord` | -| SecondaryJoin | PMpro, both sockets | Summary active sockets and inter-socket lines, socket-1 SMpro pair | `SecondaryJoined` | `SecondaryJoinTimeout` (a liveness finding, not a fault record) | `SecondaryJoinUnclassified { reason }` | -| DramFirmware | DRAM firmware | Console block, roster, opaque `CP:` tokens | Roster closed with every populated slot listed (against the `AttemptConfigurationReceipt` population) | `FirmwareConsoleReport` training refusal | Last token followed by restart or silence (the ending rule, §5) | -| Uefi | AMI Aptio | Summary, NVPARAM, POST, KVM | Summary printed, active sockets = expected | CPER/BERT records, summary mismatch | — | -| BootLoader | GRUB from virtual media | Console | Kernel loaded | `GrubBootFailed` | `GrubStillTrying` | -| KernelStub | Linux EFI stub | Console (`extdeps.linux.efi_stub`, v6.8) | Initrd loaded | `Failed to load initrd: 0x8000000000000001` (EFI_LOAD_ERROR, run 37069907299) | — | -| Kernel | Linux | Console (`extdeps.linux.boot_console`, v6.8) | Banner, no panic | `Kernel panic - not syncing: …`, module refusal | — | -| Census | Our workload | Host-capture markers | END marker | `TerminalRefused` | `TerminalPending` | - -### Self-observed facts the fixtures carry (public) - -- **The new-firmware loop.** Steps 1, 2 and 2b each ran 6 identical cycles. In each: last token `00001a0a`, only `SK0 MC0` in the roster, socket 1's SMpro B0 stuck at `0002`, zero console error lines (no `ERR:`, `fail`, `timeout` or window lines), SMpro unresponsive about 1.1 s after the last token while chassis power stays on, then a restart. The next cycle's SEL shows `S5/G2 soft-off` + `System Restart` plus Ampere OEM records (`c0`, manufacturer `00cd3a`). The observer-clock period was ~25.8 s. The mechanism is not public. -- **Step 3, positive control.** With the socket-1 CPU removed (J1 only), the new firmware reaches the firmware summary (1 active socket) and the AMI UEFI shell, and stays stable for 8+ minutes. One earlier run restarted once after the summary, and that did not recur. -- **Old firmware.** Run 37062170720: cycle 1 stops after `00001a0a`, cycle 2 continues to UEFI. Run 37069907299: reaches UEFI with 1 socket, GRUB, then initrd EFI_LOAD_ERROR and a VFS panic. -- **The UEFI summary prints `Failsafe status : N` and `Reset status : N`.** They are carried as observed, undecoded summary fields (§7). - -## 5. The account's types - -``` -PowerOnAccount { - attempt: AttemptIdentity, // run id, capture digests - host: HostIdentity, // from ManagedHost (#13055); mtcollins1 is a row, not a constant - configuration: AttemptConfigurationReceipt, // frozen before power-on; the only configuration the fold reads - account: HostAccount, - coverage: ObservationCoverage, - collection: CollectionOutcome, - open: List, -} - -HostAccount { - cycles: List, - milestones: List, // e.g. UEFI reached in cycle 2 -- a milestone, never an ending - ending: HostEnding, - degradations: List, // orthogonal to the ending - errors: List, // every stated failure, with its cycle and subject -} - -BootCycleAccount { index: Int, span: CaptureSpan, standings: List, checkpoints: CheckpointObservation } - -SubjectStanding // the reading's type is fixed by its subject, so no subject can carry another's evidence - = PlatformStanding { part: PlatformPart, reading: PlatformReading } - | SocketProgress { socket: Int, reading: SocketProgressReading } // the ONLY carrier of an AmpereBootStage - | SocketErrorRecords { socket: Int, records: List } - | SecondaryJoinStanding { outcome: MtCollins1SecondaryJoinOutcome } - | ConsoleSubject { subject: ConsoleSubjectKind, reading: MilestoneReading } // DramFirmware | Uefi | BootLoader | KernelStub | Kernel - | CensusStanding { reading: CensusReading } - -SocketProgressReading = ProgressRead { progress: SmproBootProgressRead, pass } | ProgressRefused { pair: SmproBootProgressRefused, pass } - | ProgressStatedFailure { progress: SmproBootProgressRead } | ProgressNotRead { coverage_ref } -SocketRecordReading = PendingDecoded { record: SmproInternalRecord } // a stated error - | UngatedRaw { raw, gate: SmproRecordGate } // anomaly + open question, never an error -MilestoneReading = MilestoneObserved { milestone, line, parser, capture_sha256, order } | MilestoneStatedFailure { error, line } - | MilestoneAnomalous { anomalies } | MilestoneNotObserved - -HostEnding // what the HOST did last; says nothing about errors on the way - = ReachedCensus // may coexist with errors and degradations - | StoppedWithStatedFailure { cycle, subject, error } - | RestartedRepeatedly { cycles: Int, last_cycle_ended_after: CheckpointRelation?, cadence: RestartCadence, - console_errors: Nat, reset_cause: ResetCauseObservation } - | StoppedWithoutRestart { last_cycle_ended_after: CheckpointRelation?, console_errors: Nat } - | HostEndingNotEstablished // only when the remaining HOST evidence establishes no ending - -Degradation = SocketNotJoined{outcome: MtCollins1SecondaryJoinOutcome} | ActiveSocketsBelowExpected{active, expected} - | DimmNotTrained{slot} // only against a receipt field LiveObserved/OperatorAttested - | PendingErrorRecord{socket, record: SmproInternalRecord} // PendingAndDecoded only - -CheckpointObservation { capture_sha256, tokens: List, malformed: List } -CheckpointRelation { // R2: an opaque relation, nothing more - cycle: Int, capture_sha256, last: OpaqueToken, - reference: ReferenceCapture { capture_sha256, firmware: FirmwareSnapshot, configuration: ConfigurationSnapshot }, - continuation: ReferenceContinues{next: OpaqueToken, remaining: Nat} | ReferenceEndsThere | NotInReference | ReferenceUnread{detail} -} - -ResetCauseObservation = ResetCauseUnobserved { searched } | ResetCauseObserved { raw: Word } // decode is private -RestartCadence = CadenceTimed { period: Second, cycles } | CadenceCounted { cycles } // production SOL is not timestamped - -ObservationCoverage { carriers: List } -CarrierCoverage { carrier: Carrier, standing: Read | ReadEmpty | Partial{detail} | Refused{cause} | NotTaken{reason} } -Carrier = Sol | Kvm | SmproRegisters | SmproErrorRecords | Sel | Sdr | ChassisPower | RedfishInventory | VirtualMedia | HostCapture - -CollectionOutcome { media, handoff, override, power_after, bundle_write, credential, phases: MtCollins1BootPhaseTiming } - -OpenQuestion { subject: PowerOnSubject, question: OpenQuestionKind } -OpenQuestionKind = ResetCauseNotPubliclyReadable | RetryStatePersistence | CheckpointMeaningNotPubliclyModeled - | SmproPairRefused{socket} | RestartCadenceNotTimed | ConfigurationNotRecorded{field} - | CarrierNotRead{carrier} | UngatedRecordUnexplained{socket} | ConfigurationConflicted{field} -``` - -Laws, each with a discriminating RED in slice A: - -1. **`ReachedCensus` is orthogonal to errors.** A GPI-pending CCIX record plus a one-socket census yields `ReachedCensus` with `degradations` (`SocketNotJoined`, `PendingErrorRecord`). It is never `StoppedWithStatedFailure`. -2. **Cycles are preserved.** Run 37062170720's console yields two cycles: cycle 1 ended after `00001a0a`, cycle 2 has the milestone "UEFI reached". The ending reflects the last cycle, and the milestone survives. -3. **Coverage and collection never choose the host ending.** An empty SOL, a BMC authentication refusal or a bundle-write failure is recorded in `coverage` or `collection` and nothing else. If another carrier establishes `ReachedCensus`, the UEFI shell or a reset, that host result stands beside the coverage defect. `HostEndingNotEstablished` applies only when the remaining host evidence establishes no ending. Converse RED: `SOL = ReadEmpty` with a valid host-capture END marker yields `ReachedCensus` plus an SOL coverage gap. -4. **No stage from a token.** No law or arm takes an `AmpereBootStage` from a `CP:` token. Mutating the last token changes only `CheckpointRelation`. -5. **The refused pair is not a stage.** `SmproBootProgressRefused` on socket 1 produces `SecondaryJoin` evidence and an `SmproPairRefused` open question, and no `Socket{1}` stage. -6. **Configuration-dependent questions need the receipt.** If the receipt's DIMM population is `NotRecorded` or `Conflicted`, "every populated DIMM trained" is an open question, never assumed. A historical row never stands in: a step-3 attempt whose receipt says "socket 1 CPU absent, J1 only" is judged against that receipt, never against `mtcollins1_slots` or the 2026-09-23 orientation report. -7. **A cycle exists without DRAM.** A chassis power-on plus SMpro progress or SEL boundary evidence, with no `DRAM FW version` banner, yields one partial cycle whose console span is absent. It never yields zero cycles or `HostEndingNotEstablished` on that ground alone. -8. **Ungated is not stated.** A `GpiUnavailable` or ungated record with the same payload as a pending one is `UngatedRaw` plus `UngatedRecordUnexplained`. It adds nothing to `errors` or `degradations`. - -## 6. Consumption and the replacement migration (§3, §3c) - -**Firmware convergence (R1).** `gunbc.fleet.mtcollins_firmware_converge` `boot_verdict` stops taking `HostBootObservation?`. It takes `FirmwareSetBootReadback`, projected from the whole account by `firmware_set_boot_readback(account)`: -- `BootReachedUefiAfterSet { cycle, milestone, ending: HostEnding, degradations, account_ref }`, when any cycle reached a UEFI milestone. The session ending and a reference to the whole account travel with it, so a UEFI milestone cannot erase a later `RestartedRepeatedly` ending. Convergence decides with both in hand. -- `BootProgressLostAfterSet { relation: CheckpointRelation, cadence, reset_cause }`, when no cycle reached UEFI and the ending is `RestartedRepeatedly` or `StoppedWithoutRestart`. -- `BootReadbackUnobservable { coverage }`, otherwise. - -`BootProgressLostAfterFirmwareSet` keeps its role as the convergence refusal and takes the relation instead of an authored checkpoint integer. `HostBootObservation`, `mtcollins1_post_firmware_set_boot_2026_10_03` and the `gunbc.machine_intake_boot_outcome` module are deleted in the same change. The authored 2026-10-03 datum is replaced by an account derived from the committed capture excerpt (step 1), which is that witness's fixture. - -**Consumer and semantic census for every deleted producer.** Each finding gets a typed destination or an explicit retirement: - -| Deleted | Consumers today | Destination | -|---|---|---| -| `…bundle` `mtcollins1_boot_bundle_findings` | bundle `mtcollins1_boot_bundle_text`, `mtcollins1_boot_outcome_after_bundle`; `…boot_run` `mtcollins1_boot_conclude` (`findings=` receipt line); bundle witness | The `PowerOnAccount` rendered through `std.observation` presentation. Receipt `findings=` becomes `account=` (one summary line) plus `coverage=` and `collection=` | -| `…bundle` `mtcollins1_boot_outcome_with_findings` | `mtcollins1_boot_outcome_after_bundle`; bundle witness | Same role. It appends the account summary plus its errors and degradations to a refusal, and never flips the verdict (kept REDs) | -| `bmc_all_failed_text` aggregate (the "unreachable" finding) | bundle findings | `coverage`: BMC carriers `Refused { cause }`. Retired as a host statement | -| `processor_findings`, `memory_findings`, `socket_absent_finding` | bundle findings | `coverage` plus `Degradation` against the `AttemptConfigurationReceipt` (BMC's inventory view vs the expected topology) | -| `sel_findings` | bundle findings | Per-cycle SEL events: memory/processor events go to `DramFirmware` / `Socket` anomalies, and restart records go to cycle segmentation | -| `sensor_findings` → `…bmc_sensor_observation` `mtcollins1_sensor_findings` | bundle; sensor witness | `mtcollins1_sensor_anomalies` (typed, in the sensor module) on `Platform{ChassisPower}` | -| `smpro_findings` → `gunbc.machine_intake_ampere_smpro_observation` `smpro_pass_findings` | bundle (3 passes); SMpro witness | `Socket{k}` standings via `smpro_boot_progress_of`, and `SecondaryJoin` via `secondary_join_outcome`. The "sockets differ" string is retired: the difference is now the two typed standings | -| `console_findings`: `…dram_console_observation` `ampere_dram_findings` | bundle; DRAM witness | `ampere_dram_untrained_sockets` (closed-roster rule, in the DRAM module) feeds `DegradationDimmNotTrained` / `DramFirmware` anomalies; malformed rows become `DramFirmware` anomalies | -| `console_findings`: `socket_summary_findings` | bundle | `SecondaryJoin` via `secondary_join_outcome`, and `ActiveSocketsBelowExpected` against the receipt's expected configuration | -| `console_findings`: `…kernel_module_decompression_observation` `kernel_module_decompression_findings` | bundle; kernel-module witness | `Kernel` `StatedFailure { KernelModuleRefused }` | -| `media_attach_findings`, `handoff_media_findings`, `override_findings`, `power_after_findings` | bundle; `megarac_media_convergence_witness_test` | `CollectionOutcome` (typed records, rendered once) | -| credential-shred string | bundle findings | `CollectionOutcome.credential` | -| `HostBootObservation` and its datum (#13041) | `mtcollins_firmware_converge` `boot_verdict`; its witness | `FirmwareSetBootReadback` (above) | - -**Consumers outside the required gate.** The production consumers are all in this closure: `mtcollins1_boot_run` (its fleet-converge entry) and the bundle. The test consumers are the witnesses of `mtcollins1_boot_diagnostic_bundle`, `ampere_smpro_observation`, `ampere_dram_console_observation`, `mtcollins1_bmc_sensor_observation`, `kernel_module_decompression_observation`, `megarac_media_convergence` and `mtcollins_firmware_converge`. Each one is migrated in slice A to the typed destination above, with each of its discriminating REDs kept. None is deleted without its claim moving. - -## 7. Reset cause, retry state and the summary's status lines - -The SCP keeps a reset-cause record, and no public register window reaches it: the full 0x00–0xFF SMpro sweep (`sweep-20261003T014255Z`) found none. So `ResetCauseObservation` is publicly `ResetCauseUnobserved { searched }`. `RetryStatePersistence` asks whether any failsafe or retry state survives the reset, which decides bounded retries versus an infinite loop. It is open on every `RestartedRepeatedly` ending. On cycles that reach UEFI, the summary's `Failsafe status` and `Reset status` fields are carried as raw observed values and cited as evidence on those two questions, never decoded publicly. - -## 8. The gunbc-private refinement slice (named) - -The private slice is **P: the Mt. Collins power-on account refinement**, in gunbc-private. It consumes the public `PowerOnAccount` and the private evidence (gunbc-private #202/#203: disassembly and SCP notes). It returns a private refinement that: -- attributes `CP:` tokens to stages and sub-steps (e.g. the DDR training eye sweep); -- names the restart mechanism (the SCP boot-complete wait and the watchdog); -- decodes `ResetCauseObserved` and the summary's status fields; -- answers `RetryStatePersistence` where the private evidence can. - -It imports the public account, the public repo never imports it, and nothing private flows back into public. - -## 9. Seams (noted, not abstracted) - -| Seam | Mt. Collins | Mt. Jade (#13065) | x86 | -|---|---|---|---| -| BMC family | AMI MegaRAC 0.45.3 | AMI MegaRAC, Ampere JADE LTS SPX12 | Varies | -| Stage authority | `AmpereBootStage` via SMpro | Same silicon. To verify that the SMpro I2C route is exposed. | No SMpro: port-80/IPMI POST codes, a different authority | -| Second socket | CCIX/2P, PMpro, `MtCollins1SecondaryJoinOutcome` | Same, needs its own join row | UPI/xGMI, MCA | -| DRAM evidence | Ampere DRAM console block, opaque `CP:` | Likely the same, with references per platform | Vendor MRC messages | -| Summary and NVPARAM | AMI Aptio on Ampere | Likely the same | None | -| Console route | MegaRAC SOL (`DiesOnReset`) | MegaRAC SOL | Varies | - -The differences found when Mt. Jade lands become the onboarding checklist. - -## 10. Implementation slices - -1. **A (#13117): the power-on account, replacing every string finding and `HostBootObservation`, after #13041 lands.** Former slice B (#13058's error records) is folded in, and §10a records the decisions. It includes: - - the public readers: opaque `CP:` (`gunbc.machine_intake_ampere_checkpoint_console_observation`), and the EFI stub and kernel lines (`extdeps.linux.efi_stub`, `extdeps.linux.boot_console`, read by `gunbc.machine_intake_linux_boot_console_observation`); - - the `AttemptConfigurationReceipt` (frozen before power-on, including the human completion/inspection receipt for manual changes), and coverage; - - the account; - - the convergence readback; - - the bundle and receipt cutover; - - every census row in §6, plus the REDs of §5. - - Fixtures are committed excerpts from the `mtcollins1-captures-2026-10-03` evidence branch, including step 3 as the new-firmware positive control, and the run 37062170720 / 37069907299 artifacts. A pre-review draft of the readers and a fold exists on `session/calm-lynx-884-slice-a-wip`. It predates these rulings (it uses a scalar ending, stage attribution from the DRAM banner, and its own SMpro pair rule) and will be reworked, not landed as-is. -2. **B: the attempt-configuration receipt's live inputs**: the applied stimulus, current population and pre-power-on firmware readback (decided Q4). The error records originally planned as B landed in A. -3. **C: NVPARAM** as UEFI-subject context, gated on #13059. -4. **D: `ManagedHost`**, gated on #13055. If #13055 lands before A, it is folded into A. -5. **P: the gunbc-private refinement slice** (§8). - -## 10a. Decisions taken while implementing slice A (#13117) - -These were settled while building slice A and through its side-chat reviews. Each one is enforced by a witness in #13117 and recorded here so this plan stays the authority. - -- **Cycles open only on typed boundary evidence.** Each SEL boot record gets a `SelBootBoundaryStanding` from an ordered fold with no look-ahead: - - the first record corroborates the confirmed power-on when it is a power-up; - - the first record is `BoundaryAmbiguous` when it is a restart after a confirmed power-on. It opens no cycle and becomes an open question, because this MegaRAC can log the power-on's own first boot as "System Restart" (step 1); - - every later record, including a second power-up, is a distinct boundary. - - Console banners only bind spans to cycles, and only when the counts agree; otherwise the binding is `ConsoleSpansUnbound`. The BMC clock is never joined to ours. -- **Stated failures are counted per occurrence.** - - Panics and initrd failures are read per printed line. - - Firmware statements, GRUB reports and module refusals are read by their owning readers over each console span's own lines. - - Each error carries its cycle. Only a failure in the last cycle can choose the ending, and the furthest one there wins. Earlier and unbound errors stay as history. -- **A loop is distinguished from progress after a restart.** `RestartedRepeatedly` applies only when the last cycle got no further than an earlier one. Otherwise the ending is `StoppedWithoutRestart { restarts_before }`. -- **Topology, population and absence are judged only against the receipt.** - - The expected topology is `ExpectedSockets`, or `ExpectedTopologyNotRecorded` (production today). - - Processors are checked once per expected socket: missing, duplicated, or unreadable socket ID. - - Memory is checked per socket, over the union of expected-populated and observed sockets, against `SlotRow { label, socket, populated }`. - - Sensor absence counts only for an expected socket. - - Health states reported by the BMC stay unconditional. -- **SMpro.** - - Passes are reported per pass, not placed into cycles. - - #13058's records are placed by their gate (pending → stated error plus degradation; ungated → anomaly plus open question). - - Pending warnings are kept. - - The error-record registers have their own coverage carrier, separate from the boot-stage pair. -- **Each milestone carries provenance.** It has its capture digest and a `DeclarationRef` to its reader. A one-variant nullary sum does not resolve in the substrate, so a declaration citation is used instead. -- **Convergence refuses UEFI followed by a loop.** It reads the whole-account readback. A UEFI milestone followed by a `RestartedRepeatedly` ending refuses as `BootReachedUefiThenRestartedRepeatedly`. -- **#13025's `secondary_checkpoints` is deleted (Q3).** The per-socket CP classification belongs to the private refinement slice. -- **The receipt's live inputs are slice B (Q4).** Slice B adds the boot-workflow input for the applied stimulus (with a human inspection receipt through the operator-attested route), the current population, and the pre-power-on firmware readback. Until it lands, those fields are `NotRecorded` and every question needing them is open. - -## 11. Questions - -Q1 and Q2 were decided by the side chat on `75f0bd113c` and are encoded in §1, §3 and §5. Q3 (delete `secondary_checkpoints`) and Q4 (add the receipt inputs, as slice B) were decided during slice A (§10a). Q5 and Q6 are decided; none is open: -- **Q5 (decided by eager-gull-22, 2026-10-03):** the plan and the inspection receipt are committed JSON under `artifacts/receipts/`, read from the checkout by a fail-closed typed reader that records the file's digest. One dispatch input names the file. Inline JSON in a dispatch input is refused. -- **Q6 (decided, 2026-10-04):** add the `attempt_receipt` input to the fleet-converge `mtcollins1_boot` mode. It was operator escalation msg_1b57e749, approved by default after 15 minutes with no answer, and relayed by eager-gull-22. - -## 12. Slice B: the attempt receipt's live inputs (plan, for review before code) - -Slice B fills the `AttemptConfigurationReceipt` fields that slice A records as `NotRecorded` (decided Q4). The boot run takes its host as a `ManagedHost` / `ManagedHostBinding` (`gunbc.managed_host`), not as mtcollins1 constants; cut 4d of the managed-host untangle will re-root the rest of the run. - -**Two records, each minted only by its checks.** - -``` -AttemptConfigurationPlan (sole constructor) { - subject: ManagedHost, attempt: AttemptPlanId, - expected: ExpectedTopology, requested: StimulusRequest, // expected and requested are stated ONCE, here - fixed_at: OperatorAttestedTime, -} -AttemptInspectionReceipt (sole constructor) { - plan: AttemptConfigurationPlan, - applied: StimulusApplication, cpus: PopulationReading, dimms: PopulationReading, - completed_at: OperatorAttestedTime, evidence: ReceiptEvidence, witnessed_by: NonEmptyStr, -} -OperatorAttestedTime { rendering: NonEmptyStr, attested_by: NonEmptyStr } // a time a person wrote, never an observer clock reading -ReceiptEvidence { path, digest: Sha256FileDigest, commit } // the committed file the run read -``` - -**The attempt identity comes from the dispatch, not from the artifact.** The current run's attempt identity is minted outside the receipt: it is the fleet-converge dispatch's existing `transaction_nonce`, which the operator mints and which the run name echoes. Admission runs before any pre-power read or actuation and joins `receipt.plan.attempt` to that identity. It then consumes the identity in a **create-once slot**: -- The slot is keyed by (managed subject, `transaction_nonce`), in its own attempt-admission namespace, separate from the unit hold. -- `Absent -> Consumed` is the only successful transition, done by compare-and-set against `Absent` (`std.durable_compare_and_set`). -- No hold release or hold cleanup deletes or rewrites a slot. - -So a used identity stays used: it is not blocked by a later one, and it does not block one. A valid receipt authored for attempt A cannot be selected by attempt B, and A cannot be replayed after B. - -Controls, each an implementation RED: -- the first A succeeds; -- a concurrent or repeated A refuses; -- B then succeeds; -- A still refuses after B; -- releasing the unit hold does not erase A's consumed standing. - -Implementation cut obligations: -- The workflow's `transaction_nonce` description changes from "not consumed by any step" to its admission role. -- A named receipt with an empty or absent `transaction_nonce` refuses. - -The run mints an `AttemptInspectionReceipt` only after every check passes, and each failed check refuses as its own typed cause: -- the file's digest matches what was read; -- the subject is the host this run's `ManagedHostBinding` bound; -- the plan's attempt equals this dispatch's attempt identity; -- the attempt identity was not already consumed; -- the plan's identity is the one the receipt carries; -- the attested ordering holds: `fixed_at` is at or before `completed_at`, as attested. - -**Times.** The plan's and receipt's times are what a person wrote, so they are `OperatorAttestedTime`, never `ObserverTimestamp`. They are ordered only among themselves. "Before the power write" is established STRUCTURALLY: admission is a step this run completes before it actuates. It is never established by comparing an attested time with an observer clock. - -**The absent-receipt rule (decided by eager-gull-22).** -- **No receipt named:** the dispatch input is empty. The boot proceeds with the configuration `NotRecorded` and no topology judgement, as in slice A. -- **A receipt named but refused:** a missing file, a digest or parse failure, an empty `transaction_nonce`, a subject, host, attempt or plan mismatch, a consumed identity, or an attested ordering violation. The boot REFUSES before any pre-power read or actuation, with that typed cause. -- REDs, one per arm: - - an empty input reaches actuation with the configuration `NotRecorded`; - - a valid receipt for attempt A dispatched as attempt B refuses before actuation; - - a replayed identity refuses; - - a malformed file refuses. - -The pre-power-on firmware readback is bound to the same plan (`FirmwareReadBeforeActuation { plan, rows }`), so a readback from another attempt cannot fill this one. A receipt that is absent or refused leaves the fields `NotRecorded` or refused with their cause. The boot never proceeds on a guessed configuration. - -**The operator-attested route** follows `std.human_intervention`: -- The inspection is a `HumanIntervention` step with `HumanSurfaceOnly`, because a physical change has no API. -- Its `DischargedAt { evidence }` cites the evidence PRODUCER: the fail-closed reader that mints `AttemptInspectionReceipt` from the committed file, a `DeclarationRef` to that function. It does not cite a flag or a boolean standing. - -**The pre-power-on Redfish population read is not live by default.** A controller reading taken before power-on may be the BMC's cache from the last POST, so it is `PopulationControllerReading { freshness: FreshnessEstablished | CachePossible, rows }`: -- It can corroborate the inspection or conflict with it. A conflict is `PopulationConflicted`, an open question, never resolved by preference. -- Only `FreshnessEstablished` counts as a live reading. Nothing on main establishes that freshness today, so today's reading is `CachePossible`. - -**Field routes.** - -| Field | Route | -|---|---| -| expected topology and requested stimulus | the plan, stated once | -| applied stimulus and population | the inspection receipt; the controller reading only corroborates or conflicts | -| firmware | a new pre-power-on `hpm check` read through `extdeps.bmc.ipmi` (none exists on main; `gunbc.fleet.mtcollins_firmware_converge` only renders a dry argv), plus the SMpro version word where it answers, both bound to the plan | - -**Delivery (Q5, decided by eager-gull-22 on 2026-10-03).** The plan and the inspection receipt are committed JSON under `artifacts/receipts/`, reviewed and versioned like any change. eager-gull-22 authors them from what the operator reports about the physical change. One fleet-converge dispatch input names the receipt file, and the boot run reads it from the checkout with the fail-closed typed reader above. Inline JSON in a dispatch input is refused because it is not reviewable. Adding that input is Q6, decided. - -### 12a. Slice B1 as built - -Slice B1 is `gunbc.host_boot_attempt_admission`. It holds the plan and inspection records, the reader, the create-once attempt slot, and the projection into `AttemptConfigurationReceipt`. It is host-generic: mtcollins1 appears only as its route row (the receipt variables and the slot roster), in the `gunbc.host_maintenance_hold_reason` pattern. It returns one sealed `BootAttemptClearance`. - -**Placement** (agreed with warm-crane-577): the module sits beside the boot authorization. `mtcollins1_boot_under_live_unit_hold` calls `admit_boot_attempt(proof, revision)` right after `UnitHeld`, so admission runs under the hold and **before any controller read**. -- **Baseline:** the SDR cache and SEL baseline (`mtcollins1_boot_baseline`) are taken only after clearance. -- **Refused receipt:** the boot reads nothing from the controller, releases the hold, writes nothing, and fails with the typed cause. The matrix control is `a_refused_named_receipt_reads_no_baseline_and_writes_nothing`. -- **Configuration record:** the frozen configuration travels on the attempt record into the bundle. Slice A's always-`NotRecorded` placeholder is deleted. -- **Untangle cuts:** cuts 4a and 4d move the call site, and O2 carries its refusals. - -**The receipt is a tracked blob, digested.** -- **Revision binding:** admission reads the receipt at the boot's bound revision, not from the worktree. `extdeps.git.inspect` `ListTreeEntryAtPath` finds the entry, decoded by the existing `gunbc.namespace_step0_subject_collector` ls-tree reader. `git show :` reads the content. -- **Refusals:** no entry is `ReceiptNotTracked`. A symlink, gitlink or directory is `ReceiptNotRegularFile`. -- **Evidence:** `ReceiptEvidence { path, digest: Sha256FileDigest, commit }` carries the SHA-256 of exactly those bytes, from `extdeps.tools.sha256sum`. -- **Follow-up:** the step0 decoder belongs in `extdeps.git`. Extracting it is left to the namespace lane rather than forked here. - -**Times** are admitted only as canonical UTC instants, using `gunbc.auth.approval_capability` `utc_instant_is_canonical`, which checks calendar-valid fields. They are ordered with `utc_instant_before`, and equal instants are admitted. - -**Populations join exactly, or the receipt refuses.** -- **CPUs:** the CPU rows name exactly the plan's expected sockets. -- **DIMMs:** the DIMM rows name exactly the host's slot roster. For Mt. Collins that is the Getting Started Guide's 32 connectors (`dimm_figure_banks`), labelled as the guide labels them (its `J` prefix and the connector number) and placed on their bank's socket. Every label must be known and on its roster socket, and every slot must appear, populated or not. -- **Refusal causes:** a missing socket, an unknown label, a label on another socket, and an omitted slot each refuse with their own cause. - -**The slot store** is `/var/lib/gunbc/boot-attempts`, provisioned by `gunbc.runner_host_grants` `unit_hold_store_operations` beside the unit-hold store: same hosts, owner and mode. It is a separate directory, so a hold's release or recovery cannot reach it. Its executed control on the real store host is the first grant convergence followed by a receipt-carrying boot on srv1. - -Where the build differs from the plan above, with reasons: -- **Slot key.** The key is `attempt---`, with the host and nonce admitted only over `[A-Za-z0-9_-]`. It is injective by construction and is not a hash: the corpus's `content_hash_of_value` is a 64-bit structural hash, which does not meet "collision-safe" against a chosen nonce. -- **The plan's identity.** The inspection names its plan by `plan_subject` and `plan_attempt`. Because an attempt identity admits one boot, that pair identifies the plan. - -### 12b. Slice B2 as built (firmware), and B3 (controller population) - -Slice B2 adds one pre-power read, `mtcollins1_boot_pre_power`. It is taken after admission and before `mtcollins1_boot_actuate_held`, and `gunbc.host_boot_attempt_admission` `attempt_configuration_with_pre_power` joins it to the frozen configuration. -- **The read.** `ipmitool hpm check` is a new `extdeps.bmc.ipmi` `HpmCheck` operation. It is parsed by `extdeps.bmc.ipmitool_hpm_check`, which landed with the CPLD route fix (#13254). -- **The rows.** Each component's active cell is kept as rendered, because the auxiliary bytes have no public decode. -- **Binding.** The readback is `FirmwareReadBeforeActuation { attempt, source, rows }`, where `attempt` is the attempt admission bound. An unread or refused table leaves firmware `NotRecorded` with its cause. -- **The acceptance matrix.** The dry BMC (`gunbc.bmc_dry_realization`) answers `HpmCheck` with the retained BMC 0.32 capture, as a layout fixture. - -**B3: `PopulationControllerReading` (declared frontier).** B3 is the pre-power Redfish population read. It is labelled `CachePossible`, and is joined to the inspection as corroborated or conflicted per socket. -- **Why it is not in B2:** its route dispatches `shell.Mktemp.Dir` and `shell.Remove.FileForce` (for the netrc) and `redfish.Http.GetResourceByPath`. The boot dry world does not model these, and no mtcollins1 Redfish response is retained to model them from. -- **Trigger:** a retained mtcollins1 Redfish capture of the service root, Systems, the system, Processors and Memory. eager-gull-22's read-only probe takes it when the BMC is reachable. -- **Caveat:** after the 2026-10-02 reflash, gunbc gets 401 on Redfish because its role is missing. If the capture is 401 bodies, the trigger also needs the login convergence to restore that role. -- **Already written:** the join and folds are in WIP commit `16a67dfb99d` on `session/calm-lynx-884-slice-b2`. \ No newline at end of file diff --git a/docs/plans/prepared-effect-input-carry.md b/docs/plans/prepared-effect-input-carry.md deleted file mode 100644 index 55a41aa5f0c..00000000000 --- a/docs/plans/prepared-effect-input-carry.md +++ /dev/null @@ -1,205 +0,0 @@ -# Prepared effect input carried into nullary warm rows (PR two of the #10994 chain) - -ADJUDICATED AND IMPLEMENTED. The model below was sent to eager-raven-113, fierce-lark-661 and -bright-boar-435 before any code was written; all three approved both decisions (2026-09-14), each -with one condition, and every condition is discharged in the landed change: - -- the carried value's DISPOSITION is printed on the floor's phase line beside its content digest, - so a run whose carrier refused is diagnosable rather than silent (bright-boar's condition on - Decision 1); -- `std.materialization_ladder` is cited IN SOURCE, not only here: the four obligations are typed - values in `v2.workflow.floor_prepared_effect_input_ladder`, decided by the ladder's own fold and - exercised by `v2.test.floor_prepared_effect_input_ladder` (the condition both fierce-lark and - eager-raven attached to Decision 2); -- the binding's absence after the tier is cleared has its own control (eager-raven's condition b). - -Two things the evidence found that this document's first cut did not, recorded because both -changed the implementation rather than the prose: - -1. `run_in_context` calls `call_function` DIRECTLY, reaching neither the cross-claim tier nor the - prepared-input binding. A control that evaluated the producer that way asserted a value - RECOMPUTE also produces — the decoration DESIGN section 4b names. The controls now evaluate - through an ordinary call site and assert an OBSERVED SERVE from the tier's own hit observer. -2. With that fixed, the changed-carrier RED went red on the REAL implementation: the - producer-to-carry map held a CLONE of the carry, so re-binding the acquisition left the key - unchanged and the producer was served a value derived from the previous carrier. It now holds - the acquisition NODE and reads its current binding at every key derivation. - -## The gap, stated at the floor's own vocabulary - -`v2.workflow.floor_pure_producer_share` admits two fill dispositions and only two: - -- a WARM row is NULLARY and preparation-forceable — `install_pure_producer_share` evaluates it - once in a Hermetic frame over its own module scope and the fill is billed to preparation; -- a CLAIM-FORCED row has claim-shaped arguments, so it fills inside the fold on first touch and - the paying claim carries the fill. - -A producer whose value depends on one effectful read of a committed carrier fits neither. It -cannot be warmed as a nullary pure row, because `store_cross_claim_pure_memo` is keyed on -(resolved fn node, portable argument row) and an effect-dispatching nullary call contributes -NOTHING to that key — the read's content is invisible to the key, which is the ctx-blind homonym -class in its content form: two different carrier contents would share one entry. And as a -claim-forced row it re-derives per claim, which is the rostered -`shared_precondition_re_derived_once_per_claim_frame`. - -The live instance: `gunbc.roadmap_authority.roadmap_authority_projection` — one -`Filesystem.Read` of `dag/gunbc/roadmap/roadmap_acceptance_event_history.jsonl` (16 lines, -~10 KB) followed by a pure ~186 ms fold, demanded by seven required-lane claims and by #10994, -whose current blocker is an enrolment margin RED (306 ms then 279 ms against a 302 ms margin) -sitting on top of exactly this shared precondition. - -## One fact that makes the carry legitimate rather than a memo of an effect - -Hermetic mode already classifies this read as INPUT ACCESS, not a host effect: the -checkout-input carve-out in `v1_interpreter::eval_service_call` dispatches a readonly -`Filesystem.Read`/`List` WET when `hermetic_checkout_input_disposition` confirms the path under -the checkout root. The carrier is such a path. So the value is a function of the commit the run -prepared — constant for the whole prepared subject — and carrying it once is not caching an -effect, it is binding an input the run already holds fixed. Anything the disposition cannot -confirm (out-of-root, `.git`/`target`, unresolvable) is refused by that existing wall and is -therefore never carryable here either. - -## The model - -Two declarations in `v2.workflow.floor_pure_producer_share`, beside the two existing rosters. - - type PreparedEffectInput { - // The nullary declaration whose evaluation ACQUIRES the input. Resolved to a fn node at - // preparation, exactly like every rostered producer — never admitted by bare name. - acquisition: String - // Why the acquisition is a run-constant checkout input rather than host state, and the - // consumers the carry serves. Evidence fields, same discipline as RefusedShareCandidate: - // name the instrument, never transcribe its output. - ground: String - measurement: String - } - - type CarriedInputWarmRow { - producer: String // the pure fold, resolved to its fn node - parameter: String // the named parameter the carried value fills - input: String // which PreparedEffectInput.acquisition supplies it - measurement: String - } - - data floor_cross_claim_prepared_effect_inputs: List - data floor_cross_claim_carried_input_warm_rows: List - -### What preparation does - -`install_pure_producer_share` gains one phase, ordered BEFORE the existing warm loop: - -1. Resolve each `acquisition` to its fn node in a frame over the prepared subject (the existing - `floor_authority_frame`, unchanged — Hermetic). An unresolved spelling or a module outside the - subject is a stale row and STOPS THE LINE, exactly as a stale warm row does today. -2. Refuse at install any acquisition whose declaration is not nullary: a carried input with - arguments is a different concept and must not be smuggled in under this one. -3. Evaluate it ONCE, under `observe_shared_build`, so the acquisition is billed to preparation - and adjudicated by the same preparation refusal as every other shared build. -4. Reify the value totally to `PortableValue` — the SAME total walk publication already performs - — and take its content identity as the structural digest over that form (`std.content_hash` - `Fnv1a64Structural`). Printed on the `[floor-phase]` line, so the receipt says WHICH content - was carried, not merely that something was. -5. Bind (fn node → carried `Value`) for the run's prepared subject, cleared with the tier by - `clear_cross_claim_pure_memos`. - -Then, for each `CarriedInputWarmRow`, warm the producer with a ONE-ELEMENT argument row -`[(parameter, carried value)]` instead of `[]`. Everything downstream is the existing mechanism -untouched: the store keys on (producer fn node, portable argument row) and serves only after the -full portable argument row verifies structurally equal. - -### What a claim does - -- A claim's call of the ACQUISITION declaration is served the carried value instead of - re-dispatching the read. This is the only genuinely new serve path, and it is deliberately NOT - the pure memo: the pure memo refuses any call that dispatched an effect (`effect_dispatch_count` - guard), and that refusal is correct and stays. Admission here is a separate, roster-declared - binding keyed on resolved fn-node identity, installed for one prepared subject. -- A claim's call of the PRODUCER passes the same value it just received, so the portable argument - row equals the one preparation stored, and the existing cross-claim tier serves it — no new - keying, no new retention, no new lookup rule. - -### Why content identity is not a new mechanism - -The carried value IS the key material: a changed carrier content produces a different portable -argument row, `cross_claim_portable_args_match` fails, and the claim recomputes. Stale serve is -therefore unwritable rather than guarded — the §5 preference for construction over validation. -Roster identity is the resolved fn node on both halves (acquisition and producer), so the -bare-name homonym class stays closed exactly as review 57446 closed it. - -## Refusal arms (never an empty default) - -| state | disposition | -|---|---| -| acquisition module outside the prepared subject | `PreparedEffectInputModuleOutsideSubject` — line stop | -| acquisition spelling resolves to no declaration | `PreparedEffectInputUnresolved` — line stop | -| acquisition declaration is not nullary | `PreparedEffectInputNotNullary` — line stop | -| acquisition evaluation errors (read refused, hermetic refusal, parse error in the fold) | `PreparedEffectInputAcquisitionFailed` — line stop, carrying the interpreter's own located error | -| acquired value fails total reification | `PreparedEffectInputNotPortable` — line stop, with the located path/kind from the refusal itself | -| carried-input warm row naming an unknown input | `CarriedInputWarmRowInputUnknown` — line stop | -| warm evaluates but the store refuses it | the existing `PureProducerShareWarmNotStored` — line stop | - -One arm is deliberately NOT a floor refusal, and it is the one worth arguing about: when the -acquisition SUCCEEDS and returns its domain type's own refusal arm (here -`RoadmapAcceptanceEventHistoryLoadRefused`), the floor carries that value faithfully and every -claim receives the identical typed refusal it would have computed for itself. The floor does not -read domain types and must not decide that one variant of a caller's coproduct means "stop". The -alternative — the floor stopping the line on a refusal arm — would require the floor to know -`gunbc.roadmap_authority`'s vocabulary, which is the layer inversion §3 forbids. Stated here as a -deliberate divergence so it is decided rather than discovered. - -## Home in the materialization domain (§3b), and one stated divergence - -This inhabits the materialization / carried-value home: a computation identity (the resolved -declaration) joined to declared inputs (the carried value's content digest), a provider whose -scope reaches the demands' least common visible ancestor (preparation, which is the LCA of the -seven claims), a key representing every declared input the result depends on, and retention -spanning exactly the obligated lifetime (one prepared floor run). §2's ordering is respected in -the direction it demands: the demand graph is minimized FIRST — the repetition is authored -duplication whose shared-state ancestor is preparation, so the repair is the CARRY, and the -existing cross-claim tier is merely where the carried value is held. - -The divergence, stated: this does not mint a new `std.materialization_provider.ArtifactRequest` -variant. `ArtifactRequest` keys durable, cross-run artifacts by identity digest with a realization -receipt naming the provider; the carried input is an in-process value bound for the lifetime of -one prepared subject, with no store, no path, and no cross-run reuse. Minting a variant there -would put a run-scoped binding into the durable-artifact algebra. If the reviewers read that the -other way, the alternative is a real one and this is the decision to take before code. - -## Evidence - -### Executed at fixture grain, through the real `install_pure_producer_share` path - -`cargo test --release -p v1-compiler --lib pure_producer_share` — 17 passed, 0 failed. Five of -them are this change's: - -- the input is acquired ONCE and its producer warmed over it, with the producer then SERVED - (asserted from the tier's hit observer, not from the value); -- **the discriminating RED**: a changed carrier content is not served the value derived from the - old one. Discrimination receipt — with `cross_claim_key_args` returning `None` (the - empty-argument-row implementation this control targets), this test FAILS; with the real - implementation all 17 pass; -- the prepared-input binding is absent after the tier is cleared, and the producer answers for - itself again; -- a carried row naming an input nobody prepares stops the line; -- a non-nullary acquisition is refused. - -### Still owed at live grain (stated as owed, not as done) - -## Evidence plan (the live arm) - -1. **Identical verdict.** The seven required-lane claims plus #10994's forecast witness produce - the same verdicts with the rows enrolled as without. A carry that changes a verdict is a - defect, not a speedup. -2. **Present-vs-absent, per row, on one tree.** Two `workflow_dispatch` runs on a branch ref - pinned to an exact sha (the corrected instrument recorded in the roster header: a pushed side - branch triggers nothing, and a PR pair measures two different merge refs), reading - `required_floor_claim_cost.tsv` and the `[floor-shared-fill]` ledger. Admission is - serve-below-recompute at identity grain; a row that does not clear it is withdrawn into - `floor_cross_claim_refused_candidates` rather than defended by a family aggregate. -3. **No stale serve (the discriminating RED).** A control that mutates the carrier's content and - shows the previously carried value is NOT served: the portable argument row differs, the - producer recomputes, and the verdict follows the new content. Red goes green only by the key - actually carrying content identity — an entry keyed on the empty argument row passes the happy - path and fails this control. -4. **Acquisition is dispatched once.** The effect-dispatch count over a floor run is 1 for the - carrier read with the rows enrolled, N without. diff --git a/docs/plans/printed-chassis-program.md b/docs/plans/printed-chassis-program.md deleted file mode 100644 index b0e3efd6fd6..00000000000 --- a/docs/plans/printed-chassis-program.md +++ /dev/null @@ -1,803 +0,0 @@ -# Printed node chassis — program plan - -**Pinned plan. Updated 2026-09-02.** Printers land **2026-09-03**. - -Governing statement: - -> Build a low-cost, space-efficient, thermally intentional system of independently removable -> ALTRAD8UD-1L2T server cartridges, using small-format printers for the custom geometry and -> conventional materials only where they are structurally superior. - -The experiment succeeds when the model produces real printer artifacts, a board fits, a node runs, a -middle node is removable without disturbing neighbours, and the cost/space/thermal receipts decide -whether more printers are worth buying. - -## Original motivations (the spine — every decision answers to these) - -1. Cheaper than commodity chassis. -2. More space-efficient — this is not a storage chassis. -3. Deliberately directed airflow, not a case shaped for a different machine. -4. Forcing pressure on the spatial/fabrication model. This is a first-class goal, not a side effect. - -## Fixed facts - -| Fact | Value | Authority | -|---|---|---| -| Node subject | ASRock Rack ALTRAD8UD-1L2T | `extdeps.boards.asrock_rack` | -| Board outline | 267 x 244 mm | vendor manual §1.4, read first-party | -| Intended nodes | 8 (srv1-4 + 4 inbound) | operator | -| Printers | 2 x Bambu Lab A1 mini, scaling to N | operator order relay | -| Build volume | 180 x 180 x 180 mm | A1 mini spec table, operator relay | -| Fan form factor | 80 mm; **count derived**, not assumed | operator + derivation | -| Rack unit | 2x2 block, composable | side-chat ruling | -| Service law | fixed frame, removable cassette | operator requirement R9 | -| PSU placement | cassette-resident | side-chat ruling | -| External bundle | 2 x Ethernet + 1 x AC per node | operator | - -## Standing laws - -- **Missing physical input policy: refuse.** No default, no nominal catalogue figure, no nearby standard. -- **Hidden CAD geometry authority: refuse.** The model owns dimensions *and* geometry-selection laws. -- **Printed polymer is never the mains enclosure and never a conductor in either grounding network.** Protective earth is bolted to metal and survives every removal the design invites; the board-to-chassis bond is functional only. -- **MVP thermal fixture choice carries no authority over deployment layout.** Using the 4U cooler - first to reduce thermal uncertainty must not silently become the rack pitch. -- **Bay pitch derives from measured millimetres.** "2U" and "4U" are catalogue labels, not lengths. - - -## The numbered spine — PRINT-0 .. PRINT-15 - -The six projects are the working-memory units; these are the executable steps. Zero-indexed per the -RLM-N / MEMORY-0 convention already used in this directory. Each step names its terminal evidence, -because a step without one cannot be said to be done. - -| Step | Project | Terminal evidence | State | -|---|---|---|---| -| **PRINT-0** | MEASURE | Board outline typed from the vendor manual (243.840 x 266.700 mm exact, axes corrected); micro-ATX lettered grid under its own standard authority; standoff placement derived from per-location evidence standings | **done** | -| **PRINT-1** | TOOLCHAIN | Build envelope, filament classes, slicer platform claims as separate authorities, **and a concrete A1 mini product row that the coupon fit witness consumes** | **done** (binding mutation-verified) | -| **PRINT-2** | MEASURE | Measurement authority: absent refuses, duplicate-key conflict refuses, precision budget enforced | **done** | -| **PRINT-3** | CAL | Coupon authority: ladder as modeled experimental design, rungs and plate derived | **done** | -| **PRINT-4** | TOOLCHAIN | Realization contract v0: contract derives its own rungs; a wrong-step handler is caught and located | **done** | -| **PRINT-5** | TOOLCHAIN | Raw transport schema admission: unknown version, missing field and unknown operation all refuse before any geometry call | **done** | -| **PRINT-6** | TOOLCHAIN | CadQuery handler + authority wall; sealed instruction plan; walls 1-3 execute, wall 4 (solid readback) is a declared boundary | handler done; materializer + export profile open | -| **PRINT-7** | TOOLCHAIN | STEP/3MF emitted and re-inspected; output conformance re-establishes envelope, holes, walls | | -| **PRINT-8** | TOOLCHAIN | Slicer profile bound or refused; per-printer manifests; ProcessQualificationIdentity minted, not branded | | -| **PRINT-9** | CAL | Coupons printed on BOTH printers; each printer+spool admitted or refused **independently** | needs printers | -| **PRINT-10** | FIT | Adjustable-standoff fixture; real board mounted unpowered; hole map measured back and frozen | needs printers + board | -| **PRINT-11** | CASSETTE | Structural cassette: rails, tray, handle, latch; carries node mass; no seam in layer-separation tension | needs PETG decision | -| **PRINT-12** | CASSETTE | PSU carrier and harnesses; every connector insertable; nothing side-loaded; **PE continuous with board, standoffs and cassette all removed** | needs PSU envelope | -| **PRINT-13** | CASSETTE | 80 mm fan carrier + replaceable duct; powered thermal admitted against a bench baseline | needs cooler choice | -| **PRINT-14** | RACK | One fixed bay; cassette retained in service, removable after disconnect; empty bay structurally complete | | -| **PRINT-15** | RACK | Management-node mount (Raspberry-Pi class) on the rack, carried as a bay peer rather than an accessory | | -| **PRINT-16** | RACK | 2x2 block; an enclosed node extracted with neighbours untouched | | -| **PRINT-17** | VERDICT | Cost, volume, print hours, labour, reprint rate, thermal, service time -> buy-more-printers decision | | - -**THE SEQUENCE IS INTEGERS, AND AN EARLIER REVISION BROKE THAT.** It carried PRINT-11b and PRINT-13b -while still calling itself N = 15, so the spine had two numbering authorities at once and the count -was wrong. Letter suffixes were how cable routing and the management mount got appended as -afterthoughts instead of being placed. Renumbered; new work takes the next integer. - -**CABLE ROUTING IS NOT A STEP.** The operator's requirement is routing at EVERY controlled layer, so -it is a property each cassette and rack step must satisfy to be called done, not one step of its own. -It was PRINT-11b, which was exactly the bolt-on that requirement forbids. Every step from PRINT-11 -onward now carries the cable obligation in its own terminal evidence: endpoints, bend and service -volume, clip positions, strain relief, moving-versus-fixed classification, and declared separation -from blades and hot surfaces. - -**N = 17.** PRINT-0 through PRINT-6 are landed and executing. PRINT-1's reopening is CLOSED: the -build envelope is owned by `extdeps.printing.bambu_lab_a1_mini` and the coupon fit witness reads -`a1_mini_build_envelope` rather than an inline literal. That was verified by MUTATION rather than by -reading the import — shrinking the product row's x from 180 mm to 50 mm flips -`w_the_hole_ladder_coupon_fits_the_a1_mini` from green to red, so the binding is live and not -nominal. The check was run because the module's own annotation asserted "changing this row is what -moves that witness", and an annotation asserting a property the code does not have is exactly what -#10119 had to repair four times. - -PRINT-5 and PRINT-6 landed with #10119, so the pre-arrival critical path is now **PRINT-7 alone**, -and it is BLOCKED rather than merely pending: re-inspecting an emitted STEP/3MF needs a CAD kernel, -and the trigger for that is a base image with glibc >= 2.38 or an x86_64 runner. PRINT-8 is the first -step that needs the hardware itself, and it is not blocked by PRINT-7 — the coupon can be printed and -measured before any kernel readback exists, it just cannot issue an absolute process-qualification -receipt until PRINT-7 closes. - -### What blocks what - -- **PRINT-5..6 are landed.** PRINT-7 is BLOCKED on a CAD kernel — measured, not assumed: CadQuery - installs on this arm64 container and clears `libGL.so.1`, then `casadi` requires `GLIBCXX_3.4.32` - which requires `GLIBC_2.38`, and this box is glibc 2.36. The trigger is a base image or an x86_64 - runner, NOT a pip install. -- **Joint family** (dovetail / tongue-and-groove / keyed slide / pin) gates the - ProductionInterfaceCoupon only — a later half of PRINT-8, not the MachineProcessCoupon. -- **PETG** gates PRINT-10 onward. PLA carries PRINT-8 and PRINT-9 and stops there: no PLA-to-PETG - receipt carry. -- **Deployment envelope** gates PRINT-13..14 layout admission, nothing earlier. -- **Drive count** gates PRINT-11 completion only. - -## The six projects - -| Project | Scope | Terminal evidence | State | -|---|---|---|---| -| **CONTRACT** | Goals, non-goals, authority graph, refusal law, MVP boundary | No assumption represented as a default | deferred — no consumer yet | -| **MEASURE** | Board, cooler, PSU, fan, DIMM, cable, **deployment envelope**, uncertainty | Pack sufficient to generate CAL+FIT | **partial — carrier landed, roster empty** | -| **TOOLCHAIN** | Realization contract, CadQuery handler, authority wall, STEP/3MF, slicer env, printer nodes | Deterministic generation + executed no-parallel-authority controls | not started | -| **CAL+FIT** | Coupons, qualified tolerances, adjustable standoff fixture, real-board fit, hole-map feedback | One unpowered board fits; hole authority admitted | not started | -| **CASSETTE** | Structure -> PSU/cables -> airflow -> powered thermal | Removable node operates and is serviceable | not started | -| **RACK+VERDICT** | One bay -> 2x2 block -> scale/economics | Middle-node service proven; buy-more-printers verdict | not started | - -Consolidation from eleven stages must not collapse the internal gates. CASSETTE keeps -`structural -> PSU/cable -> airflow -> powered thermal`; RACK+VERDICT keeps -`single bay -> populated 2x2 -> economic verdict`. - -## The CAD authority wall (TOOLCHAIN's load-bearing control) - -CadQuery is a **realization handler**. It may map modeled operations to kernel APIs, reorder -provably-equivalent operations, heal representations, triangulate a determined solid, and implement -a *modeled* derivation whose identity and inputs the model already fixed. - -It may **not** choose dimensions, offsets, clearances, wall thicknesses, radii, chamfers, feature -presence, hole patterns, joint topology, rib placement, segmentation boundaries, print orientation, -load-path geometry, duct path or cross-section, fallback values, or one construction algorithm over -another where the physical result differs. - -A numeric-literal scan is **necessary but insufficient** — `fillet()` vs `chamfer()` carries no -literal. The wall is four-part: - -- **A. Geometry-taint dataflow.** Any expression reaching a geometry-affecting call derives only - from the typed contract, a modeled operation, or a proved non-semantic toolchain constant. -- **B. No-default completeness.** Unknown feature kinds, missing fields, unsupported derivations and - schema skew all refuse. No Python defaults for geometry-affecting fields. -- **C. Realization trace.** Every realized feature maps to one modeled source; no unmodeled feature. -- **D. Output conformance.** Re-inspect the emitted STEP/3MF for envelope, hole centres, wall - thickness, mating dimensions, clearances, build-envelope fit, collision/extraction envelopes. - -**Negative control:** hard-code a plausible wall thickness in the handler and prove the wall rejects -it while the part still looks valid. A wall that never flips is a decoration. - -Duct curvature is the boundary case: inlet + outlet + envelope + airflow obligation do **not** -determine a duct. The model must fix the family and the selection law (centerline derivation, -minimum bend radius, wall thickness, transition rule); the handler may implement that law and may -not choose it. - -## Landed so far - -- `extdeps.boards.asrock_rack` — typed 243840 x 266700 um outline, first-party manual read. The - extents are NOMINAL: `asrock_altrad8ud_board_depth_nominal_exact` is the exact ARITHMETIC - conversion of the vendor's published 9.6 x 10.5 in, not the as-built extent of any board here, and - the rename exists to stop "exact" being read as zero physical tolerance by a clearance derivation. - Mounting holes are a standard-location correspondence with a per-location standing, so an inferred - hole and a physically confirmed one are different facts; `asrock_altrad8ud_open_mounting_unknowns` - carries the five unmeasured ones with their discharge instrument. -- `extdeps.standards.micro_atx` — the nine microATX mounting locations own their own authority - rather than sitting inside the ATX 2.2 module, which now carries only ATX 2.2 facts. Coordinate - provenance is `OperatorRelayedDocument` from the shared `DimensionEvidence` carrier, not a prose - string: a citation STATUS is a typed fact, and the string form was a nickname nothing could consume. - **One structural over-claim is recorded and deliberately not half-fixed** — `extdeps_model_scope` - cites the archived ATX 2.2 PDF as `first_citation` for the microATX locations, a machine-readable - claim the provenance row denies, because this session's extraction recovered that PDF's text layer - and not its mounting-location artwork. No microATX document is in hand, so the repair needs a - first-party read or an `ExternalAuthority` arm that can carry a relayed-with-no-document chain; - both are named as triggers rather than guessed at. -- `extdeps.printing.bambu_lab_a1_mini` — the printer as a PRODUCT ROW: build envelope, nozzle - diameters, filament diameter, temperature ceilings, input voltage and frequency range, machine - extents, and a ten-row material suitability roster. The fit witness reads the envelope from here - instead of a literal it wrote itself, so it goes red if the printer authority vanishes. -- `extdeps.vendor.bambu_lab`, `extdeps.printing.fdm` — agnostic FDM shapes; build-envelope fit that - reports every exceeded axis and never searches orientations. -- `extdeps.printing.bambu_studio` — spec-sheet and release-artifact platform claims carried as two - authorities; the Linux disagreement modeled, not resolved. -- `product.printed_chassis.measurement` — SPATIAL-1 binding; absent refuses, duplicate-key conflict - refuses with no precision or recency tie-break. Six witnesses green, two proven to flip. -- Compile-clean: 0 blocking errors in these files; the 57 remaining are pre-existing on main. -- `product.printed_chassis.coupon` — **TOOLCHAIN v0's first consumer.** The hole-diameter ladder as a - modeled experimental design: base, step and rung count are authored, every rung and the plate width - are DERIVED. `PlateDimensions` is its own type so a malformed plate has no zero-extent fallback to - return. Micrometre-to-millimetre conversion rounds UP, because truncation reports a 180.5 mm part - as fitting a 180 mm machine. Eight witnesses green; the conversion proven to flip in both the - arithmetic and the fit path. **V0 is now closed at one operation** (`OpThroughHole`) — see the v0 - population section for why the second one was deleted rather than kept. -- `product.printed_chassis.realization_contract` — the transport wall. Raw JSON from the CAD handler - is admitted through `decode_envelope`, which refuses an unsupported schema version, an unknown - operation kind, and an envelope carrying no operations, each with a typed cause naming what it - received. Conformance reports the EARLIEST divergence rather than the last, and a count mismatch is - its own outcome rather than a silent zip truncation. - - **It compares WHOLE GEOMETRY, not the whole specimen, and the distinction is load-bearing for - PRINT-5.** Compared: plate on three axes, operation count, then every operation field. NOT compared: - specimen identity (carried, single-armed), revision (not a compared subject at all), and feature - identity — operation *i* is "the *i*-th rung" by LIST POSITION, not by an identity the operation - carries. A permutation is refused because position is compared, which is not the same as features - having identities, and the difference reappears the moment two features share a geometry. PRINT-5 - must not read a conforming verdict as adjudicating identity or canonical ordering. - - **The admitted value is unforgeable, and an earlier revision only CLAIMED it was.** That revision - put the geometry on the accepted arm (`EnvelopeDecodedV0 { plate, features }`) and argued admission - was unavoidable "because the refusal arms have no geometry to hand on" — a correct argument for an - insufficient conclusion, since it rules out the raw envelope bypassing the judge and says nothing - about a caller bypassing the raw envelope. A probe compiled the forgery from a foreign module. - `DecodedRealizationV0` is now `sole_constructor`, so only `decode_envelope` can mint one; a foreign - module may name the arm and cannot obtain a value to put in it. - - **That wall now has an executing witness, and the claim that it could not have one was false.** - An earlier revision of this plan said the RED was a compile failure and so could not be enrolled in - a corpus that must compile, and named an expect-compile-refusal harness as the missing capability. - That was DESIGN §4b's own distinction misapplied: a state unauthorable in the ACCEPTED CORPUS may - still be perfectly authorable as SOURCE HANDED TO THE COMPILER BY A FIXTURE, and only the second - boundary decides whether a check's RED exists. The harness was already here — - `gunbc.compile_diagnostic_census`, whose `compile_dag_diagnostic_census` takes source as data and - returns either an observed diagnostic population or a typed `CensusNotRunnable`; the seven-form - precedent against a sealed fixture type is `test.claim.sole_constructor_completeness_audit_probe_test`. - The finding was routed by the side chat, which cited the precedent rather than the claim. - - The witness is `test.claim.printed_chassis_admitted_realization_seal_witness_test`, built on the two - forms that module's own annotation names as exact: a count scoped to diagnostic class AND - `subject_name`, and a differential between two sources differing on one axis. It carries a red - source writing the `DecodedRealizationV0` record literal from a foreign module, a green source with - identical imports and no literal, and the red-minus-green subtraction as a third witness so the - shared imported closure cancels rather than being asserted away. The `CensusNotRunnable` arm answers - `-1`, so every assertion compares against a non-negative quantity and a harness that never ran - satisfies nothing — which is exactly the vacancy that produced the near-false-finding below. - - **The refusal payload could carry a success, and the witnesses were where it showed.** - `RealizationVerdict`'s refusal arm was typed `RealizationRefusedAtWire { admission: EnvelopeAdmission }`, - and `EnvelopeAdmission` includes `EnvelopeDecodedV0`. No route produces that state — - `judge_realization` builds the refusal arm only on the three refusing branches — but reachability is - not occupancy, and the state was writable. The tell was not in the contract at all: four witnesses - carried a dead `EnvelopeDecodedV0 { admitted: _ } => false` arm purely to satisfy exhaustiveness over - a case the route cannot produce, which is what a coproduct too wide for its position looks like from - the consumer side. The refusal reasons are now their own closed coproduct `EnvelopeRefusal`, and - `EnvelopeAdmission = EnvelopeDecodedV0 | EnvelopeRefused { refusal: EnvelopeRefusal }`. The class - climbs from mechanically preventable to structurally impossible, and the four dead arms are deleted - with it — §4b(4) retires the redundant production handling while every discriminating witness stays - enrolled. - - **The trusted-type boundary sat one step early, and the fix turned out to be the name.** The routed - review's objection was that the admitted carrier is minted before conformance runs. I pushed back - that decode-admitted and contract-conformant are two genuinely different facts and so the *stage* - was not wrong. That was true and beside the point: the defect was that an unqualified capability - name — "admitted" — was attached to the earlier fact, while it was also the only geometry-bearing - value a handler could obtain. Renamed `DecodedRealizationV0`. The stage stayed. - - **What the pushback did surface is a second, real defect, deferred deliberately.** `judge_realization` - returns a verdict and no geometry, so a handler must obtain geometry elsewhere and associate it with - the verdict itself. That association is unbound: judge envelope A, retain envelope B's decoded - geometry, hand B to the handler under A's conforming verdict. Every honest caller passes the same - envelope twice and the invalid pairing stays writable — the same class as the refusal payload above, - one level out. The close is a second sealed type returned *from* the judgment that established - conformance, minted from the contract's own canonical specimen rather than from the transported - values that happened to compare equal, so what a handler cuts is the model's geometry and the wire is - reduced to an assertion that was checked. `AdmittedRealizationV0` is reserved for it and unspent. - - It is not built in this revision because nothing actuates geometry yet: with no handler the pairing - has no site at which to go wrong, and minting a sealed carrier with zero consumers to hold a symbol - is what §2 prices as redundant. So it is declared as `realization_v0_open_obligations` — a typed - carrier and not an annotation, because PRINT-5 must not be able to reach a handler without answering - it, and no `Accepted` program can read a comment. - - **The coupon could not be identified from its own geometry, and the fix is an authority change, not - a handler change.** The ladder steps 0.10 mm across eleven rungs, so end to end the holes differ by - 1 mm and are visually interchangeable; rotated 180° the coupon reads as a valid coupon with its - ordinals reversed, and every measurement is attributed to the wrong rung. Routed review found it, - and also corrected its own first proposal — one obligation was doing two jobs: - - - **orientation** — which end of *this* coupon is the 3.00 mm end. A property of the canonical - geometry, and now discharged by making that geometry asymmetric. - - **print-instance attribution** — which printer and spool made *this* piece of plastic. Two - correctly-oriented R1 coupons remain interchangeable. No geometry closes this, because the datum - must be *identical* on both coupons or the two processes stop sharing a subject. It stays open, - routed to the manufacturing manifest and a physical handling route. - - Holding them as one obligation is why the earlier route fold was near-vacuous — a single arm whose - RED could not be authored. Splitting is what let one of them close. `HoleDiameterLadderR1` is the - coupon *design* identity and never the identity of a printed instance. - - **The datum is its own field, not another entry in the feature list.** Appending one more - `OpThroughHole` and remembering the last one is the datum makes "which hole is the datum" a - positional convention — the class this module already deleted once — and it is worse here, because - the datum exists precisely to remove an ambiguous reading. `HoleDiameterCouponGeometry` carries - `plate`, a scalar `orientation_datum`, and `ladder_holes`, which makes four things structural: one - datum exactly (zero and two are unwritable, not refused), the datum cannot become a twelfth rung, - canonical geometry with no datum has no representation, and every consumer must name which - population it means. The wire splits the same way — two *different* populations, not two parallel - lists of one. - - **The near-miss worth recording is inside the derivation.** A first cut read rung zero's x back off - the built feature list to make the correspondence structural. `.first()` returns an option, so it - forced an `Absent` arm for a ladder that cannot be empty — and the arm answered with a fabricated - datum at the margin. An absorbing fallback in miniature, inside the one function the whole - orientation guarantee rests on. It now reads `hole_ladder_margin`, the same row the ladder fold's - `i = 0` term reads: one row, two readers, no unreachable branch to fabricate in. The correspondence - is asserted by `w_the_datum_is_at_rung_zero` rather than constructed, which is honestly one rung - lower — mechanically preventable, not structurally impossible — with the trigger named. - - **Evidence, and it discriminates.** Six controls: canonical datum conforms; datum at the far end - refuses naming `FieldCenterX` (the 180° reading itself); datum on the ladder centre-line refuses - naming `FieldCenterY` (right x, so it passes only if the y is compared); the datum stays out of the - measured population; it clears the ladder band and the plate edge; and it sits at rung zero's x. - A mutation that compares the modeled datum against itself turns the two refusal controls **RED** - while the positive control stays green — so they discriminate the wall rather than merely - exercising it. - - **Probe discipline learned here, recorded because it nearly produced a false finding.** The first - forgery probe returned exactly the baseline error count — consistent with "forgery permitted" — from - a file the compiler had never read, because an added source root was silently ignored. Confirmation - and vacancy produce the same number. Only a deliberate must-fail control in the same file - distinguished them. - -### PRINT-5 — the handler, and the one thing it does not yet do - -The pairing discharge and the CadQuery handler both land: `AdmittedRealizationV0` is sealed and -minted only inside `judge_realization` from the **canonical** specimen, and `realize_admitted` takes -that capability and nothing else. Five witnesses green by execution — per-feature trace with the -datum as a distinct subject, uncentred plate, whole-program equality against a model-rebuilt -expectation, exact millimetre rendering on odd micrometres, and overtravel moving no measured -dimension. - -**Actuation is registered but NOT materialized, and that is the honest state.** -`CouponCadQueryProgramArtifact` is a `gunbc.generated_artifact` variant generated through -`artifact_generate`, and it is `NotCommitted`. Since `main_wet` selects only committed artifacts, -**nothing writes the `.py` to disk today.** The remaining work is a typed workspace materializer and -a pinned CadQuery environment; using `main_wet` to keep an actuation claim alive would have been the -claim outrunning the execution. - -The commit-policy question resolved against my first answer. I had registered it -`CommitRequired { consumer: FabricationToolchain }`, reasoning that `RepoConsumer` already spanned -more than git and CI. The correction, from review 5095012465: `RepoConsumer` answers the narrower -question *why must these bytes persist in a git checkout*, and a CAD kernel reading a materialized -workspace establishes no such requirement — one `FabricationToolchain` arm would have conflated the -program consumer, the solid consumer, the mesh consumer, the slicer and the printer. The arm becomes -justified only when a workflow requires an operator to obtain *this file* from a checkout, and then -it is narrow (`FabricationWorkstationCheckout`) with a receipt. - -Committing it had already produced its own evidence: `.gitignore`'s blanket `*.py` swallowed the -path, so an artifact declared `CommitRequired` could never be committed and the drift gate would -have read an absent file indefinitely. A derived negation section in `gunbc.gitignore_emit` fixed -that and has been reverted with the policy — with no such artifact it is machinery whose -non-vacuity control names a path that no longer exists. **The latent class is real and now -unwalled**: a `CommitRequired` artifact whose path matches a developer ignore pattern is silently -uncommittable. Its trigger is the first such artifact; walling it before one exists would be a check -whose RED is unauthorable. - -**Export is removed from the emitted program rather than profiled.** The -`cq.exporters.export(result, "…stl")` line chose five things the model never stated — STL over STEP -or 3MF, a filename, a working-directory-relative destination, format-by-extension, and CadQuery's -default tessellation policy. The last is not neutral: STL is a mesh format, so every modeled circle -becomes a polygon whose fidelity is set by linear and angular tolerances, and the emitter was -choosing how round the holes this coupon exists to *measure* come out. The program now builds -`result` and stops. `CadQueryExportProfile` is a real obligation and is deliberately unauthored: -with no kernel here its tolerance fields would be literals with no oracle. - -**Four defects the review found, three of which my own annotations had asserted away.** `zip_map` -truncates (`Empty` on either side) while the comment claimed it refuses a length mismatch — the -third appearance of the positional-parallel-list defect here, and the first where a comment supplied -the false guarantee; replaced by one row per substitution, which deletes the correspondence. The -capability wall leaked through `plate_line`/`cut_line`, callable on bare geometry, directly beneath a -header claiming there was "not even a private one" — the emitters now destructure geometry inside -the arms of a sealed `CadQueryInstructionV0` that only `cadquery_emission_plan` can mint. -`AdmittedRealizationV0` had no seal evidence at all while a witness annotation asserted its seal -held; its red/green/differential battery is now enrolled and green. And the overtravel annotation -described a doubled-overtravel diff that no body ever performed — the claim is narrowed to what -executes, with the solid readback recorded as an open obligation. - -The discharged `RealizationV0Obligation` vocabulary is deleted. Keeping an empty roster was argued -on the grounds that it preserves a typed place for the next obligation, but nothing read it — no -gate required emptiness — so the forcing did not exist, and the discharged property is carried where -it executes, in the type of the accepting arm. - -**Toolchain boundary, now measured rather than assumed.** Wall 4 — re-reading the produced *solid* -for plate dimensions, datum location and the eleven ladder holes — is a §4b boundary obligation, not -a rung. Its trigger was written as "a runner with cadquery importable", which was too vague to act -on, so it was probed to termination: - -- CadQuery **does** install from PyPI on this arm64 container (wheels exist for aarch64); the - earlier note that this box has "no CAD kernel" was a missing-bootstrap observation, not an - architecture wall. `pip` bootstraps into a venv via `get-pip.py` (the system interpreter is - PEP-668 externally-managed, so `--user` refuses). -- `import cadquery` then fails on **`libGL.so.1`**, which is satisfiable by extracting - `libgl1`/`libglx0`/`libglvnd0` plus the X client libs. -- Past that, `casadi` requires **`GLIBCXX_3.4.32`**, and a libstdc++ carrying it requires - **`GLIBC_2.38`**. This container is **glibc 2.36**. - -So the real trigger is **a base image with glibc ≥ 2.38 (or an x86_64 runner), not a pip install** — -and assembling one by hand-extracting `.deb`s onto `LD_LIBRARY_PATH` is where a probe turns into the -workaround §5 names, so it was stopped there rather than pushed through. The environment wall 4 needs -is a *pinned, identified* toolchain — Python version, CadQuery version, OCP closure, image digest, -install method — which is the same identity the qualification receipt has to carry anyway. - -**What this costs tomorrow, stated plainly.** Without wall 4 the physical deviation measured on a -printed coupon is the sum of model→source, kernel, tessellation, slicer and printer–spool terms, and -the coupon exists to isolate the last. A print made before wall 4 closes therefore yields operational -evidence (adhesion, gross scale, obvious defects) and a valid **A-vs-B differential** between the two -printer–spool pairs — the upstream terms are common-mode across two prints of the same artifact and -cancel in the comparison — but it **cannot issue an absolute process-qualification receipt** against -the modeled nominal. Relative comparison survives; absolute qualification does not. - -## The two-day cut (printers arrive 2026-09-03) - -**Ruling: a CAL-driven vertical slice, not a general framework and not hand-authored coupon CAD.** - -``` -CalibrationCouponAuthority -> RealizationContract v0 -> four-part wall - -> CadQuery handler -> STEP/3MF conformance -> bound slicer profile - -> per-printer-node manifests -> physical coupon observations -``` - -The coupon is TOOLCHAIN's first real consumer. Governing rule: - -> **Generalize TOOLCHAIN only one consumer ahead.** CAL determines v0; FIT determines the next -> expansion; CASSETTE the next. A new geometric operation arrives only with taint coverage, -> refusal behaviour, trace coverage and output conformance. - -A wall with no handler is only a rule definition — the geometry-taint arm is not commissioned until -it observes real geometry calls, passes the lawful ones and rejects mutations. - -### v0 operation population (closed; a feature belongs only if a chosen coupon consumes it) - -box/prism · cylindrical through-hole · slot · linear or grid repetition · male/female clearance pair · -wall or rib · rigid transforms · boolean union and subtraction - -**`part/revision datum marking` was in this list and has been REMOVED from it, deliberately.** As -`OpDatumMark { text, at_x, at_y }` it fixed text and position and left FONT, GLYPH METRICS, STROKE -WIDTH, ALIGNMENT, ORIENTATION, DEPTH and ENGRAVED-VERSUS-EMBOSSED to the handler — the §3 tell in its -exact form, the handler choosing geometry the model had not fixed. It had already produced a silent -falsehood: `specimen_extent` assumed the mark was engraved and so assumed it could not enlarge the -plate, an assumption stated nowhere and enforced by nothing, and a handler that embossed would make -the envelope-fit answer wrong in the unsafe direction. Determining it fully means modelling fonts, -which is a domain rather than a field, imported to label a calibration coupon. - -The need behind it is real and is tracked, not dropped: several coupons will exist physically and -must be told apart by hand. `coupon_v1_physical_identification_obligation` carries it as a V1 -obligation with the route that looks right — identify the coupon by GEOMETRY, a coded through-hole or -notch pattern, which `OpThroughHole` already determines completely, checked by the same contract the -holes already pass through. - -The ladder itself is a modeled experimental design. "It is only a calibration coupon" is not -permission for Python to choose test dimensions. - -### CAL splits into two coupon classes - -- **`MachineProcessCoupon`** — X/Y deviation, hole and slot deviation, sliding clearance, thin wall - and rib, orientation anisotropy, first-layer behaviour, repeated-feature consistency. Depends on - **no** server or site measurement. This is the first print. -- **`ProductionInterfaceCoupon`** — the exact joint family, insert/captive-nut geometry, fastener - clearance, rail fit. Needs no server dimensions but **does** need the joint family and purchased - hardware identities chosen. Do not invent an M3 pocket or dovetail angle to populate it. - -### The slicer is a SECOND authority boundary - -Scaling, dimensional compensation, line width, first-layer compensation, wall construction, layer -height and orientation all change the physical result. A `.3mf` carrying hidden hand-edited -compensation is as much a parallel authority as a Python literal. Qualification identity: - -``` -printer_node x firmware x material_product x material_spool x installed_nozzle - x slicer_identity x slicer_profile x orientation x support_policy - x coupon_revision x calibration_epoch -``` - -### Wall commissioning needs four negative controls, not one - -1. **Numeric/dataflow** — a plausible Python-derived wall thickness. Taint must reject. -2. **Nonnumeric topology** — a fillet, chamfer or extra rib with no modeled feature identity. Trace - or taint must reject. -3. **Default** — omit a required contract field and let a helper default take over. Completeness - must reject. -4. **Output** — alter an exported hole or envelope after lawful realization. Conformance must reject. - -### Material: PLA does not carry to PETG - -No PLA-to-PETG receipt carry. Distinct admissions: -`ToolchainSmokeAdmitted` · `FitPrototypeProcessQualified` · `StructuralProcessQualified` · -`ThermalServiceQualified`. - -So the PETG decision blocks the **durable/powered CASSETTE path**, not the first CAL print or the -unpowered FIT fixture. Qualify each printer-spool combination independently; never infer that an -observed difference is the printer alone. - -## Open decisions (operator) — none block the first print - -- **Drive count** — a CASSETTE completion obligation. Blocks nothing before Thursday. -- **PETG** — blocks structural/powered parts, not CAL or unpowered FIT. -- **Deployment envelope** — **not on the pre-arrival critical path.** Its absence must refuse - rack-layout admission later, not block calibration now. - -## Next - -1. ~~`CalibrationCouponAuthority`~~ — landed as `product.printed_chassis.coupon`. -2. `RealizationContract v0` — the transport-only, schema-bound feature graph the handler consumes. -3. CadQuery handler + four-part wall executed over v0, with all four negative controls. -4. STEP/3MF generation and conformance; bound slicer profile or refusal. -5. Per-printer manifests for node 1 and node 2; measurement/admission form ready for results. - -**Deferred as definition-only residue until each has a consumer:** `CONTRACT` (needs the generator), -`DeploymentEnvelope` (needs the layout-comparison consumer). The design of both is pinned above. - -## Supply identity — RULED, and it unblocks the filament slice - -The open question was whether a spool is an inventory **lot** or a **PhysicalAsset instance**. Ruled: -**one supply instance per physical spool, allocated at durable physical individuation, each retaining -its source procurement lot.** The dependency runs one way only — - -``` -identity makes a later divergence attributable -NOT: a later divergence earns the object an identity -``` - -so the two day-one spools are separate instances *before* either print begins, even though they came -from one order, name one catalog product, and have no known difference. Waiting for measured -divergence would leave the first divergence with nowhere truthful to live. - -Grains stay distinct rather than collapsing onto the lot: catalog product (what was sold), -manufacturer batch (what the maker says), procurement lot (what was bought), supply instance (which -spool fed this print), condition observation (instance x time). **One order is not one material -batch** — `ProcurementLotIdentity` and manufacturer batch standing stay independent, since one order -may span two production batches and two orders may share one. Moisture at print time is an -instance-and-time observation; a drying cycle mints a new condition observation, never a new spool -identity. - -RFID and operator label are both **evidence routes, not identity kinds**. They may coexist; an RFID -read failure does not create a different spool, replacing a label does not create a different spool, -and disagreement must refuse the attribution rather than letting either route silently win. The RFID -UID is a machine-read manufacturer identifier, not a guaranteed globally unique spool serial, until -something establishes that. - -The lot-to-spool relation must not be authored twice. The existing inventory event already relates -installed assets to the lot whose stock moved, so day one is one install event per spool with -`quantity: 1`. The generic rule refuses only when assets *exceed* quantity, which would admit -`quantity 2, assets [spool_a]`; an attribution-critical supply needs the stronger local law -`asset count == installed quantity AND the requested asset occurs exactly once`. The admitted supply -may carry the derived source lot, and must never accept an independently authored `source_lot` -without comparing it against the event. - -Catalog boundary: `PhysicalAsset` requires a catalog `DeclarationRef` while `ProcurementLot` may -carry an unread catalog standing. Instance-per-spool must not force a fabricated exact filament -product — where only a package description is known, keep the unresolved standing and limit the -receipt to an end-to-end observation. Do not point a spool at the `Pla` polymer class as though a -class were a particular commercial product. - -### Correction landed to the printer slice - -The manifest annotation said the roster is filled by **reading the serial off each machine**. That -conflated two states the carrier already distinguishes: `PhysicalAsset.physical_serial` is optional, -so a printer present on the bench with an unread serial and a printer that has not arrived are -different facts, and collapsing them would report a machine the operator is standing in front of as -absent. A row is allocated at receipt and durable individuation — an operator-applied label suffices -— and the serial **corroborates** that binding rather than establishing it. If a serial is ever -required before attribution, that is an admission refusal arm, visible and countable, never silence. - -### Two coupons per machine is two attempts, not two objects on one plate - -`A1/B1` then `A2/B2`, each pair consuming the same machine artifact, supplies and nozzle unchanged -and no intervening calibration unless recorded. Two copies printed together answer **within-job / -bed-position** variation; two executions answer **print-to-print** variation. Both are useful and -they must not share one receipt name. - -## On arrival (2026-09-03) - -``` -printer setup and identity observation - -> same MachineProcessCoupon on each printer - -> measure without silently averaging contradictions - -> admit or refuse each process identity independently - -> derive qualified FIT clearances - -> generate adjustable FIT fixture - -> mount the real board unpowered -``` - -The duplicate-measurement repair already has the right semantics for this: lookup refuses -contradiction. A future adjudication relation may supersede a measurement, but selection must never -be smuggled into ordinary resolution. - -### The bench procedure, in the order it must physically happen - -Written out because the ordering is the whole content: two steps here capture facts that **no later -measurement can reconstruct**, and both are cheap only while the parts are still on the machines. - -1. **Label both printers before either is powered on.** A physical label, applied by hand — `A` and - `B` is sufficient. This is the durable individuation the roster needs; the identity is allocated - here, not derived from anything read later. Record the machine each label went on. -2. **Label both spools before either is loaded.** Same rule, `PLA-A` / `PLA-B`. Do this while they - are still sealed and distinguishable as objects rather than as "the one in the left printer". -3. **Read the serials, if convenient — and do not wait on them.** The serial *corroborates* the - binding made in step 1; it does not establish it. If a serial is unreadable, the printer is still - registered. If an RFID tag reads, record it as evidence beside the label, never instead of it. - Label and tag disagreeing is a refusal to resolve later, not a coin flip. -4. **Load spool A into printer A, spool B into printer B, and write down which went where.** This - pairing is the treatment variable of the entire experiment. It is not recoverable from the plastic. -5. **Print the same coupon on both machines, concurrently.** Same generated artifact, same profile, - same nozzle. Concurrency matters: it holds ambient conditions roughly fixed across the pair. -6. **Bind each coupon to its printer and spool BEFORE it leaves the bed.** Write on it, bag it and - label the bag, or photograph it in place — any durable mark. **This is the irreversible step.** - The two coupons are geometrically identical *by design*, so once both are off their beds and on - the same table, nothing measured afterwards can tell them apart. Getting this wrong does not - degrade the experiment; it voids it. -7. **Then repeat as a second attempt: A2 and B2**, same supplies, same nozzle, same profile, no - intervening calibration unless it is recorded. A1/B1 versus A2/B2 answers *print-to-print* - variation. Two copies on one plate would instead answer *bed-position* variation — a different - question with a different receipt name, and conflating them is how a machine difference gets - attributed to a corner of a build plate. - -What is deliberately **not** on this list: any dimension read off a coupon, any compensation constant, -any judgement about which machine is better. Those are measurements, and they are all still available -tomorrow. Steps 1, 2, 4 and 6 are the only ones that expire. - - -## The floor-budget cluster, and why this program did not take the coverage loss - -Adding this program's witnesses tipped a rotating subset of ten whole-corpus-reflection claims past -the floor's 500ms per-claim ceiling — claims that already sat at 415-436ms on main and were already -`[over-cost]` flagged. Two runs tipped DISJOINT subsets, so there was never a single slow witness to -chase. - -The interim move was to lift those ten off the required floor into `test.claim.long.`, declared as a -§4b(3) rung drop. That was reverted before it was pushed, and the reason is worth keeping: a child -lane investigating the cause found `deduplicate_identities` building a COPIED ACCUMULATOR inside a -quadratic fold, over a population that is the corpus — roughly 16s of the 19s that one reflection -costs. Rewritten as a set-membership fold it drops the standing to ~4.8s. - -So the ceiling was never the problem and the ten witnesses were never really the subject. Taking -them off the floor would have spent ten executing checks — including the one that EXECUTES the -compile-phase ratchet — to work around a cost-shape defect that §6 says is always fixed regardless of -realized n. The fix lands on main ahead of this program, and this program takes no drop. - -The transferable rule: when a budget refusal names your change as the trigger, the trigger and the -cause are different questions. A rotating victim set is the tell that you have found neither. - -## Grounding — R14, and why it is TWO networks rather than one path - -**Corrected.** An earlier revision of this section said the earth connection is "part of the docking -interface" and that the earth path runs through the standoffs. That is wrong, and wrong in the -dangerous direction: it makes the safety path depend on a mechanism whose whole purpose is to be -disconnected by hand, routinely, by design. The requirement below replaces it. - -There are two networks. They serve different purposes, they have different failure consequences, and -conflating them is what produced the earlier error. - -**1. Protective earth (PE) — a safety network, and never load-bearing on anything removable.** -From the AC inlet's earth pin, through the PSU's own vendor enclosure, to a dedicated bonding point -on the rack's metal member, and from there to every accessible conductive part. It is bolted, not -docked. The witness is stated as three simultaneous conditions, because any one of them alone is a -state the rack will really be in: - -> PE continuity holds with the motherboard removed, AND with every motherboard standoff removed, AND -> with the removable cassette undocked. - -If pulling a node can open the earth network, the design is wrong regardless of how good the contact -is when it is seated. - -**2. Board-to-chassis bond — a functional network, for EMI and reference, not for safety.** -Board mounting hole → metal standoff → cassette metal reference. This one legitimately breaks when -the board is removed, because that is what it is for. It may be a return and reference path; it may -never be the reason a chassis surface is safe to touch. - -The consequences for the build: - -- **Metal standoffs into a metal member.** The hybrid load path already buys aluminium extrusion or - threaded rod — that member is the natural bonding conductor and is present for structural reasons - anyway. It serves network 2, and is a *bonded branch of* network 1, not a segment of it. -- **Printed polymer is never a conductor in either network, never the mains enclosure, and never the - sole earth path.** The PSU stays in its vendor enclosure. -- **The docking interface carries no PE obligation.** A dock is a connector; PE is a bolt. - -The obligation this creates for MEASURE: the PSU's earth-stud or bonding-screw location, the rack -member's bonding point, and the standoff material and thread. None is measured yet. - -The witness set, written now so the design is falsifiable before anything is printed: - -| # | Condition | Required outcome | -|---|---|---| -| G1 | Motherboard absent | All accessible rack metal remains PE-bonded | -| G2 | Every motherboard standoff absent | PE continuity holds | -| G3 | One cassette extracted | PE continuity holds for the remaining rack | -| G4 | Board-hole bonding unknown | **No** board-to-chassis bond is claimed | -| G5 | Any printed polymer segment | Never carries PE continuity | - -G4 is the one that is easy to skip: not knowing whether a mounting hole is bonded to board ground is -a reason to make no claim, not a reason to assume the convenient answer. A dedicated bonding stud, -conductor, terminal and tested connection are part of the rack design. Incidental contact through -rails or mounting screws is never the bond. - -### The witnesses above are not sufficient, and the gap is a sequencing one - -G1-G3 prove that the REMAINING rack stays bonded after something is removed. They say nothing about -whether the thing that was removed was correctly bonded while it was powered — a cassette can be -live, unbonded, and still leave a perfectly continuous rack behind it. Two ordering relations close -that, and both are about the service protocol rather than about geometry: - -- **Power admission requires the bond.** `NodePowerAdmitted -> EveryRequiredAccessibleMetalPartPeBonded`. - A node may not be energised unless every accessible conductive part it brings is already bonded. -- **Bond removal requires the power to be gone.** `PeBondMayBeRemoved -> AcDisconnected AND HazardousEnergyAbsent`. - The earth connection is the last thing disconnected and the first thing connected. - -This is why the bond is a captive bolt in the connect/disconnect sequence even though the docking -interface carries no PE obligation. "A dock is a connector, PE is a bolt" settles what the MECHANISM -is; it does not settle WHEN the bolt is made and broken, and the second question is the one that -decides whether a person servicing a live rack is safe. - -### Front-edge support — admitted only under all five conjuncts - -The 29.21 mm of board hanging past the last mounting row is a cassette input, not a description. A -support there is a *candidate*, and it is admitted only when all of the following hold, because each -one of them independently turns a helpful support into a defect: - -1. The underside keep-out at the contact region is known. -2. The contact region is non-conductive. -3. The support does not obstruct the extraction path (R-middle-node-removal). -4. The support actually reacts connector insertion load — otherwise it is decoration. -5. The support does not become an **unintended electrical bond**, which is where this requirement - meets the grounding correction above: a support that quietly bonds the board underside to the - cassette creates a path nobody modelled and nobody tests. - -## Cable routing — R12, at every layer - -Every run gets endpoints, an access envelope, a minimum bend and service allowance, fixed clip -positions, strain relief, a moving-versus-fixed segment classification, and declared separation from -fan blades and hot surfaces. The service witness stays structural: after disconnecting the declared -external bundle and releasing the latch, the extraction path is collision-free and no neighbouring -cable or node has to move. - -**PST changes the fan harness.** The ARCTIC P8 PWM PST carries a 4-pin connector AND a 4-pin socket, -so fans daisy-chain and fan count is decoupled from the board's five headers. That is a cardinality -fact only: a chain still owes admission against header continuous and startup current, connector and -wire rating, maximum chain length, failure isolation, and tach semantics. At the published 0.09 A a -three-fan chain draws 0.27 A and a five-fan chain 0.45 A steady — neither figure proves a chain safe, -because the header rating and startup behaviour are unmeasured. Do not assume every chained fan is -independently observable just because a downstream socket exists; tach forwarding needs manufacturer -authority or an executed electrical observation. - -## The GPU-bearing node — how a card changes the cassette's structure - -A node carrying a full-height dual-slot card is a **variant of the node contract**, not a second -product. What it changes is not "make it bigger": it converts two of the cassette's founding -assumptions. - -**1. Distributed load becomes a concentrated cantilever.** The board is ~1 kg spread over standoffs; -a dual-slot card is ~1.5-2.5 kg hanging off one slot at one end. The existing standoff model cannot -carry it, and neither can the PCIe connector. The node needs a **second load termination** — a card -support spanning to the cassette frame, with the printed part as an alignment pad rather than the -retention. This is the same law the GPU augmentation program states, arriving inside the chassis: -*printed parts locate, metal retains.* - -**2. The stack load path is sized by the heaviest node, not the average one.** "Remove a node from -the middle without lifting the others" means every shell carries the stack above it through its own -sidewalls. A GPU node is the heaviest member, so it sets the sidewall section for **every** node in a -mixed stack — or the model must carry a per-node mass and refuse a stacking order whose lower -members cannot carry what is above them. The second is the honest version and it is a real -refusal the current model cannot express. - -**3. One airflow domain becomes two, and the second one exhausts sideways.** The cassette channels -80 mm fan air across the DIMMs. A blower card pulls its own air and exhausts out the bracket — which -in an OPEN, stacked chassis discharges into whatever is adjacent, including the intake of the -neighbouring node. **Inter-node recirculation is a failure mode the single-node airflow model does -not have.** It forces a declared exhaust destination per node and the rule that one node's exhaust is -never another's intake — which in practice means a consistent front-intake/rear-exhaust convention -and a duct from the blower to the rear face, even though the chassis is otherwise open. - -**4. Grounding gets materially harder, not incrementally.** The standing requirement is a ground for -each board. A card bracket normally grounds through a metal chassis; in a printed, insulating -cassette there is **no such path** unless one is modeled. The GPU does not create this gap, it -exposes it — the bracket is simply the first component that assumes a conductive chassis and finds -none. - -**5. Service becomes node-scoped.** A mid-stack node must extract with its card, cables and service -loops intact, so disconnect order and bend volumes become properties of the node, not the rack. - -### The modeling consequence, and it is the operator's ruling applied - -The shared authority is the **logical** structure — where load goes, where air goes, where fasteners -and interfaces sit. The **process** is an input to realization, not a fork of the design: PLA wants -thicker walls and rewards ribs; sheet metal is one folded surface that forbids enclosed ribs and -bosses and requires a bend radius and relief at every fold; molding wants uniform walls, draft and no -undercuts. So the process-specific part is **more than thickness**, and the difference must live in a -realization bound to a `FabricationProcess`, never in a hand-edited second model. - -That is the same shape `gunbc.product.printed_chassis.realization_contract` already has for CadQuery, -and it says the emitter must not be forked per process: one logical model, N bound handlers, STL for -the printer and DXF for the laser shop derived from **one** source. Forking the emitter per process -is §3's fused-transport tell exactly. - -### Open facts this cannot proceed without - -Exact card envelope, mass, slot width and bracket geometry · blower exhaust direction and volume · -node pitch (which decides upright versus flat-on-riser, and the riser makes it a cable problem) · -aux power connector type and sustained draw against the node supply and stack distribution · whether -a mixed stack is allowed at all, since a uniform GPU stack and a mixed stack have different sidewall -answers. diff --git a/docs/plans/qualified-subject-identity.md b/docs/plans/qualified-subject-identity.md deleted file mode 100644 index 3aefce5d84a..00000000000 --- a/docs/plans/qualified-subject-identity.md +++ /dev/null @@ -1,122 +0,0 @@ -# Qualified subject identity (owner direction 2026-09-25) - -Sits under `one-namespace-hierarchy.md`: that ruling says there is one -hierarchy; this one says every object in every context names itself with one -construction. Together: one algebra, one graph, three+ projections. - -## The algebra - -```text -QualifiedSubjectName - = authority - + namespace path - + local identity - -SubjectIncarnation - = QualifiedSubjectName - + revision / generation / invocation -``` - -This is NOT one flat namespace for every object. Every object uses the same -construction while preserving its owning authority: - -```text -buganizer:gunbc/issue/ -github:gunb-ai/gunbc/pr/12210 -github:gunb-ai/gunbc/pr/12210@52c56a8 -dag:gunb-ai/gunbc/test/:: -dag:gunb-ai/gunbc/test/::@52c56a8 -run:srv1/ -compiler:gunbc-v2/diagnostic/match_arm_navigation_refused -``` - -At the .dag level: reuse the existing qualified-name/namespace carrier. Do NOT -mint stringly per-kind identities (`BuganizerNodeName`, `PrNumber`, -`TestLabel`, `RunName`). Kind-specific types may wrap one qualified identity; -they must not reinvent its namespace semantics. - -The `SubjectIncarnation` half is load-bearing: it prevents the UI from -attaching a result from main, an older PR head, or a stale run to the stable -subject as though the subject were timeless. Every displayed runtime claim is -bound to an exact incarnation. - -## The relation vocabulary (two issue-to-issue dimensions, never collapsed) - -| Relation | Meaning | Affects scheduling? | -| ----------------------------- | ------------------------------ | ------------------: | -| `UpstreamOf` / `DownstreamOf` | Issue decomposition or nesting | Not inherently | -| `Blocks` / `BlockedBy` | Must complete first | Yes | -| `Implements` | Issue → PR | No issue dependency | -| `ValidatedBy` | Issue → test/claim | No | -| `ObservedBy` | Test/claim → exact run | No | -| `ReportedDiagnostic` | Exact run → diagnostic | No | -| `BasedOn` | PR head → another PR head | Not automatically | - -Laws: - -- A stacked PR is `BasedOn`, not automatically `Blocks`. It becomes `Blocks` - only when the associated issue genuinely cannot complete independently. -- A test is not automatically a child issue; a run is not a blocker node; a - diagnostic is not an issue dependency; a merge-queue state is not an issue - status; a numbered landing-order list is not a dependency graph. -- Never use PR numbers as issue node IDs. Each pairing is an explicit - `BuganizerIssue --Implements--> GitHubPullRequest` link. -- Common causes collapse: N affected tests with one shared refusal render as - ONE blocker plus an N-row impact population, never N independent blockers. - -## Execution-milestone state machine (acceptance tracking) - -```text -ContextRefused → ContextResolved → BodyReached → BodyExecuted - → AssertionVerdictProduced → verdict -``` - -A population that has not reached its bodies is `BLOCKED`, not `FAIL`: - -```text -Passed / Failed in body / Blocked before body / Not yet adjudicated -``` - -Milestones (e.g. `BodyReached`) are issue-local acceptance milestones, not -synthetic dependency nodes and not new issues. Do not pre-create "unknown -blocker" issues for failures not yet observed. - -## Presentation rules (enforced on the tracker surfaces) - -1. Never display a result without its incarnation (head sha / invocation id / - exact run). -2. Distinct states: `PASS / FAIL / BLOCKED / NOT RUN / RUNNING / UNREADABLE` — - no red "failed" styling for pre-body refusal. -3. Render a DAG, not an ordered prose list (independent branches proceed - independently). -4. `Upstream / Downstream` and `Blocked by / Blocks` render in separate - sections even when one issue pair carries both. -5. PRs, tests, runs, diagnostics attach to issue nodes through typed - relationships as evidence (Implementation / Verification sections); they - never masquerade as issue nodes. -6. Collapse common causes (above). -7. Never infer issue completion from PR state — "approved" / "merge queue" / - "checks running" are properties of the implementing PR incarnation; the - issue closes only by its own completion rule. Verification with unresolved - attribution reads `pending attribution`, never a blamed subject. - -## The invariant - -> Every displayed object has one authority-owned qualified subject name; every -> runtime claim is bound to an exact incarnation; upstream/downstream expresses -> issue nesting; blockers express must-complete-first ordering; PRs, tests, -> runs, and diagnostics attach as evidence rather than silently becoming issue -> nodes. - -## Where this binds current lanes - -- Tracker frontend (agent-48 + follow-ups): assignee/issue semantics, the - detail page's sections (Upstream/Downstream vs Blocked by/Blocks, - Implementation, Verification), state vocabulary, DAG rendering. -- Issue-tracker extdeps model: the relation vocabulary above becomes the - typed relation set; the roster gains incarnation fields on any rendered - result. -- Alignment consolidation: alignment walks the namespace path of the - QualifiedSubjectName; ambiguity/refusal uses the same carrier. -- SCM/belt surfaces: captures, receipts, and verdicts already carry exact - heads — the display rule (1) makes that binding uniform on every surface. diff --git a/docs/plans/receipts/allocation-state-custody-2026-09-29/README.md b/docs/plans/receipts/allocation-state-custody-2026-09-29/README.md deleted file mode 100644 index 767c8ad35ed..00000000000 --- a/docs/plans/receipts/allocation-state-custody-2026-09-29/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Protected-state credential custody prerequisite - -This cut extracts only the storage credential row from allocation PR #12505 onto -main `889312b928ae19593bb78e918bd2687c86e3db11`. It does not install the protected -storage server, designate a workspace slot, or deploy the allocation stack. - -The existing host custody convergence resolves the pinned Secret Manager version, -checks the administrator SSH subject, writes through the existing typed remote -file authority, and reads back content, permissions, ancestors and staging cleanup. -The state credential uses root:operator 0640 inside root:operator 0750, outside job -and guest trees. Permission-bit readback is not an ACL or effective-open proof. -The cloud accessor remains a row in the existing secret-accessor roster. - -After review and qualification, the bounded custody sequence is the existing -`fleet-converge` mode `host_credential_custody_converge`, `credential=fabric_state_writer_key`, -once for srv1 (storage/controller) and once for srv2 (the website). Use the merged -reviewed source revision and the workflow's own WIF identity. No operator token, -manual SCP, or fallback approval key is required. Read each run's custody receipt -before advancing to storage service commissioning. Those runs have not occurred. - -The branch-local release build passed under 6 GiB/no swap (15m10s). The initial -older-binary test and both branch-local full-root test/emission runs exited 137. -Byte-identical dependency-only runs also exited 137: 1746 modules for custody -controls, 2110 for the standard registry emitter. The latter reached some -transitive typechecking but neither produced a qualification result or workflow -output. No memory limit was increased. - -PR #12580 remains draft and unqualified for installation. The superseded PR run -36510800728 was cancelled and supplies no qualification. The explicitly dispatched -integrated run 36512302394 is pinned to 7768a9597efb03bd6729cda76beb85b8e9485544 -and remains outstanding at this checkpoint. The generated workflow option -must be emitted and checked before landing. These failures are a significant -local qualification blocker, not proof that the custody behavior passed or a -claim that any specific source defect caused the process kills. - -Further bounded checks used the existing typed-module cache cap of one and -`MALLOC_ARENA_MAX=2`, matching the CI allocator setting. Both custody controls -(1746 original modules) and a direct call to the canonical fleet workflow -generator (1822 original modules plus a harness) ended with systemd -`Result=oom-kill`, before results. These were still 6 GiB/no swap; no further -identical local retry is planned. The integrated CI run uses its existing budget. - -CI run 36512302394 passed floor, compiler, clippy and emit-build; generated -projection drift remained. PR run 36513776136 regenerated and verified the -workflow successfully. Its repair artifact intentionally excludes workflow files. -The emitted YAML was therefore read from that exact unchanged CI source checkout -over the modeled srv3 access path, checking HEAD before and after and requiring -an empty dag/src/v2 diff. Only the credential dropdown changes. The emitted bytes -and their provenance are now retained; the updated branch still needs its checks. -No credential installation was performed. diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/README.md b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/README.md deleted file mode 100644 index 2f40d3fcd70..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# IAM bootstrap qualification and partial commissioning - -Source: `a23a0f99cbfe3f39da52cd40638eb44c85e76c6c`, based on main -`2886b9e396d5ca6026c54212f27c52ea4113fba4`. Independent draft: #12565. - -The branch's own release build succeeded. Its binary passed all three focused -controls, printed the exact bootstrap plan, and refused the missing-credential -control with the expected reason before creating a started receipt. All commands -ran under `MemoryMax=6G`, `MemorySwapMax=0`. `receipt.json` records binary and -closure-manifest hashes. The runbook reproduces the byte-identical closure; this -is not a full landing-suite result. - -## Cloud result: stopped before permissions were granted - -Attempt `20260928-a23a0f99c-1` submitted creation of the IAM job's pool, provider, -and observer account, but immediate readback did not establish their readiness. -It refused without reaching role or permission stages. - -Attempt `20260928-a23a0f99c-2` reread the same named resources, created the apply -account and missing bare Namecheap pool/account, and completed the identity -stage. Existing target resources were read, not replaced. It then created all -seven exact role definitions and independently reread them. - -GCP refused the next stage's deny-policy creation with HTTP 403: -`Permission iam.googleapis.com/denypolicies.create denied on resource -cloudresourcemanager.googleapis.com/projects/582015116396`. - -No capability bindings or workflow impersonation bindings were applied by this -bootstrap. No target provider was created. The privilege probes and real -OIDC/approval workflow acceptance have not run. Existing unrelated identities and -access were not removed. Roles are definitions, not grants to the new accounts. - -The operator-supplied token was read through a private temporary file, never a -command argument. The launcher removed that file after each attempt. Receipt -copies redact the temporary pathname and contain no token. The refusal is a -missing permission, not an observed token-expiry error. - -## Resume - -An existing administrator credential or approved administrative workflow must -supply the deny-policy authority on `gunbai-secrets`. The bootstrap never grants -that authority to itself. Run the same modeled entry with a new receipt identity; -completed named subjects and role definitions are reread before further effects. -Later capability and impersonation checks may expose additional missing -permissions; they have not been qualified by this partial run. - -Successful deny readback, capability reconciliation, observer/apply checks, and -final workload trust must precede the real approval-workflow acceptance run. - -## Fresh-token retry - -Attempt `20260928-a23a0f99c-3` used a newly supplied operator token and reread -the existing identities and seven role definitions. GCP again refused deny-policy -creation with HTTP 403 for `iam.denypolicies.create`. Capability and workflow -trust stages were not reached. Temporary credential removal was independently -checked. Refreshing the token did not supply the missing IAM permission. diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/build.log b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/build.log deleted file mode 100644 index 41e6e0db3b0..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/build.log +++ /dev/null @@ -1,114 +0,0 @@ - Compiling proc-macro2 v1.0.106 - Compiling quote v1.0.45 - Compiling unicode-ident v1.0.24 - Compiling syn v2.0.117 - Compiling shlex v1.3.0 - Compiling find-msvc-tools v0.1.9 - Compiling cc v1.2.62 - Compiling synstructure v0.13.2 - Compiling version_check v0.9.5 - Compiling serde_core v1.0.228 - Compiling libc v0.2.186 - Compiling zerofrom-derive v0.1.7 - Compiling typenum v1.20.1 - Compiling cfg-if v1.0.4 - Compiling zerofrom v0.1.8 - Compiling yoke-derive v0.8.2 - Compiling serde v1.0.228 - Compiling stable_deref_trait v1.2.1 - Compiling yoke v0.8.2 - Compiling serde_derive v1.0.228 - Compiling object v0.37.3 - Compiling zerovec-derive v0.11.3 - Compiling memchr v2.8.0 - Compiling zerovec v0.11.6 - Compiling displaydoc v0.2.5 - Compiling ar_archive_writer v0.5.1 - Compiling zmij v1.0.21 - Compiling psm v0.1.31 - Compiling serde_json v1.0.149 - Compiling tinystr v0.8.3 - Compiling bitmaps v2.1.0 - Compiling im v15.1.0 - Compiling stacker v0.1.24 - Compiling rand_core v0.6.4 - Compiling litemap v0.8.2 - Compiling writeable v0.6.3 - Compiling itoa v1.0.18 - Compiling icu_locale_core v2.2.0 - Compiling rand_xoshiro v0.6.0 - Compiling sized-chunks v0.6.5 - Compiling zerotrie v0.2.4 - Compiling potential_utf v0.1.5 - Compiling generic-array v0.14.7 - Compiling icu_properties_data v2.2.0 - Compiling icu_normalizer_data v2.2.0 - Compiling utf8_iter v1.0.4 - Compiling icu_collections v2.2.0 - Compiling icu_provider v2.2.0 - Compiling ring v0.17.14 - Compiling unicode-properties v0.1.4 - Compiling zeroize v1.8.2 - Compiling v1-stage0-runtime v0.1.0 (/home/briansrls/scratch-wt/gcp-iam-bootstrap/src/v1/stage0_runtime) - Compiling rustls-pki-types v1.14.1 - Compiling subtle v2.6.1 - Compiling smallvec v1.15.1 - Compiling icu_normalizer v2.2.0 - Compiling icu_properties v2.2.0 - Compiling v1-stage0-std-core v0.1.0 (/home/briansrls/scratch-wt/gcp-iam-bootstrap/src/v1/stage0_std_core) - Compiling getrandom v0.2.17 - Compiling untrusted v0.9.0 - Compiling utf8parse v0.2.2 - Compiling crc32fast v1.5.0 - Compiling anstyle-parse v1.0.0 - Compiling v1-stage0-std-surface v0.1.0 (/home/briansrls/scratch-wt/gcp-iam-bootstrap/src/v1/stage0_std_surface) - Compiling idna_adapter v1.2.2 - Compiling crypto-common v0.1.7 - Compiling block-buffer v0.10.4 - Compiling adler2 v2.0.1 - Compiling colorchoice v1.0.5 - Compiling equivalent v1.0.2 - Compiling percent-encoding v2.3.2 - Compiling simd-adler32 v0.3.9 - Compiling rustls v0.23.40 - Compiling anstyle-query v1.1.5 - Compiling hashbrown v0.17.1 - Compiling is_terminal_polyfill v1.70.2 - Compiling anstyle v1.0.14 - Compiling anstream v1.0.0 - Compiling indexmap v2.14.0 - Compiling miniz_oxide v0.8.9 - Compiling form_urlencoded v1.2.2 - Compiling digest v0.10.7 - Compiling idna v1.1.0 - Compiling v1-stage0-extdeps-languages v0.1.0 (/home/briansrls/scratch-wt/gcp-iam-bootstrap/src/v1/stage0_extdeps_languages) - Compiling rustls-webpki v0.103.13 - Compiling webpki-roots v1.0.7 - Compiling toml_datetime v0.6.11 - Compiling serde_spanned v0.6.9 - Compiling strsim v0.11.1 - Compiling winnow v0.7.15 - Compiling heck v0.5.0 - Compiling log v0.4.29 - Compiling clap_lex v1.1.0 - Compiling toml_write v0.1.2 - Compiling once_cell v1.21.4 - Compiling toml_edit v0.22.27 - Compiling clap_builder v4.6.0 - Compiling clap_derive v4.6.1 - Compiling webpki-roots v0.26.11 - Compiling flate2 v1.1.9 - Compiling v1-stage0-v1-artifact v0.1.0 (/home/briansrls/scratch-wt/gcp-iam-bootstrap/src/v1/stage0_v1_artifact) - Compiling url v2.5.8 - Compiling cpufeatures v0.2.17 - Compiling base64 v0.22.1 - Compiling v1-compiler v0.1.0 (/home/briansrls/scratch-wt/gcp-iam-bootstrap/src/v1/stage0) - Compiling ureq v2.12.1 - Compiling sha2 v0.10.9 - Compiling v1-stage0-v1-infer v0.1.0 (/home/briansrls/scratch-wt/gcp-iam-bootstrap/src/v1/stage0_v1_infer) - Compiling clap v4.6.1 - Compiling toml v0.8.23 - Compiling hmac v0.12.1 - Compiling lazy_static v1.5.0 - Compiling hex v0.4.3 - Finished `release` profile [optimized] target(s) in 15m 29s diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/cloud-attempt-1.log b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/cloud-attempt-1.log deleted file mode 100644 index 820189fcf42..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/cloud-attempt-1.log +++ /dev/null @@ -1,17 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 20 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -[file] read -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-1-started.txt (54 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-1-identities.txt (114 bytes) -gcp iam converge observe federation: named creation is not yet established by readback; no rights granted diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/cloud-attempt-2.log b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/cloud-attempt-2.log deleted file mode 100644 index dc9ecc69f38..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/cloud-attempt-2.log +++ /dev/null @@ -1,91 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -[file] read -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-2-started.txt (54 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-2-identities.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeCreate -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/roles -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeCreate -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeProvider -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/roles -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeProvider -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/roles -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/roles -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObservePool -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/roles -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObservePool -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveAccount -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/roles -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveAccount -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveSecret -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/roles -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveSecret -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-2-roles.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[rest] POST https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-2-deny.txt (879 bytes) -status 403: { - "error": { - "code": 403, - "message": "Permission iam.googleapis.com/denypolicies.create denied on resource cloudresourcemanager.googleapis.com/projects/582015116396", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "IAM_PERMISSION_DENIED", - "domain": "iam.googleapis.com", - "metadata": { - "permission": "iam.googleapis.com/denypolicies.create", - "resource": "projects/582015116396", - "error_info_id": "CiQwMWEwZWEwOS1lY2U1LTdmYTgtOGUyNi03NGQyNmM0MjRkODQSFXByb2plY3RzLzU4MjAxNTExNjM5Ng==", - "troubleshooter_url": "https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWEwOS1lY2U1LTdmYTgtOGUyNi03NGQyNmM0MjRkODQSFXByb2plY3RzLzU4MjAxNTExNjM5Ng%3D%3D" - } - } - ] - } -} diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/cloud-attempt-3.log b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/cloud-attempt-3.log deleted file mode 100644 index 0d7ec5449d0..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/cloud-attempt-3.log +++ /dev/null @@ -1,74 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 20 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -[file] read -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-3-started.txt (54 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-3-identities.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeCreate -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeProvider -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObservePool -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveAccount -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveSecret -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-3-roles.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[rest] POST https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies -[file] write_create_new target/gcp-iam-bootstrap-20260928-a23a0f99c-3-deny.txt (879 bytes) -status 403: { - "error": { - "code": 403, - "message": "Permission iam.googleapis.com/denypolicies.create denied on resource cloudresourcemanager.googleapis.com/projects/582015116396", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "IAM_PERMISSION_DENIED", - "domain": "iam.googleapis.com", - "metadata": { - "permission": "iam.googleapis.com/denypolicies.create", - "error_info_id": "CiQwMWEwZWExMi1mMzBkLTdkMjUtOWYwZC1mMjNhNGJmYmFhNmQSFXByb2plY3RzLzU4MjAxNTExNjM5Ng==", - "resource": "projects/582015116396", - "troubleshooter_url": "https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWExMi1mMzBkLTdkMjUtOWYwZC1mMjNhNGJmYmFhNmQSFXByb2plY3RzLzU4MjAxNTExNjM5Ng%3D%3D" - } - } - ] - } -} diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/controls.log b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/controls.log deleted file mode 100644 index 7bff137e61b..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/controls.log +++ /dev/null @@ -1,7 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 20 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -PASS bootstrap_role_drift_never_authorizes_adoption -PASS deny_exceptions_and_incomplete_readback_withhold_trust -PASS workflow_trust_requires_completed_deny_and_privilege_readback diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-1-identities.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-1-identities.txt deleted file mode 100644 index 2522b49ad07..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-1-identities.txt +++ /dev/null @@ -1 +0,0 @@ -refused: gcp iam converge observe federation: named creation is not yet established by readback; no rights granted \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-1-started.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-1-started.txt deleted file mode 100644 index b286fc75b3e..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-1-started.txt +++ /dev/null @@ -1 +0,0 @@ -operator bootstrap started; not a convergence receipt diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-deny.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-deny.txt deleted file mode 100644 index bb00c5c4118..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-deny.txt +++ /dev/null @@ -1,20 +0,0 @@ -refused: status 403: { - "error": { - "code": 403, - "message": "Permission iam.googleapis.com/denypolicies.create denied on resource cloudresourcemanager.googleapis.com/projects/582015116396", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "IAM_PERMISSION_DENIED", - "domain": "iam.googleapis.com", - "metadata": { - "permission": "iam.googleapis.com/denypolicies.create", - "resource": "projects/582015116396", - "error_info_id": "CiQwMWEwZWEwOS1lY2U1LTdmYTgtOGUyNi03NGQyNmM0MjRkODQSFXByb2plY3RzLzU4MjAxNTExNjM5Ng==", - "troubleshooter_url": "https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWEwOS1lY2U1LTdmYTgtOGUyNi03NGQyNmM0MjRkODQSFXByb2plY3RzLzU4MjAxNTExNjM5Ng%3D%3D" - } - } - ] - } -} diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-identities.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-identities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-identities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-roles.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-roles.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-roles.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-started.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-started.txt deleted file mode 100644 index b286fc75b3e..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-2-started.txt +++ /dev/null @@ -1 +0,0 @@ -operator bootstrap started; not a convergence receipt diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-deny.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-deny.txt deleted file mode 100644 index 02019ed4fe0..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-deny.txt +++ /dev/null @@ -1,20 +0,0 @@ -refused: status 403: { - "error": { - "code": 403, - "message": "Permission iam.googleapis.com/denypolicies.create denied on resource cloudresourcemanager.googleapis.com/projects/582015116396", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "IAM_PERMISSION_DENIED", - "domain": "iam.googleapis.com", - "metadata": { - "permission": "iam.googleapis.com/denypolicies.create", - "error_info_id": "CiQwMWEwZWExMi1mMzBkLTdkMjUtOWYwZC1mMjNhNGJmYmFhNmQSFXByb2plY3RzLzU4MjAxNTExNjM5Ng==", - "resource": "projects/582015116396", - "troubleshooter_url": "https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWExMi1mMzBkLTdkMjUtOWYwZC1mMjNhNGJmYmFhNmQSFXByb2plY3RzLzU4MjAxNTExNjM5Ng%3D%3D" - } - } - ] - } -} diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-identities.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-identities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-identities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-roles.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-roles.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-roles.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-started.txt b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-started.txt deleted file mode 100644 index b286fc75b3e..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/gcp-iam-bootstrap-20260928-a23a0f99c-3-started.txt +++ /dev/null @@ -1 +0,0 @@ -operator bootstrap started; not a convergence receipt diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/no-credential.log b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/no-credential.log deleted file mode 100644 index 9a75d5f411d..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/no-credential.log +++ /dev/null @@ -1,5 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 24 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -no GUNBC_GCP_ACCESS_TOKEN_FILE in the environment: this entry needs a path to a file holding an access token diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/plan.log b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/plan.log deleted file mode 100644 index 35135a13db7..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/plan.log +++ /dev/null @@ -1,52 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -Bootstrap stages: identities -> roles -> deny -> capabilities -> privilege-readback -> workflow-trust -project gunbai-secrets -workflow provider github-gcp-iam-converge-oidc -workflow condition assertion.repository_id == '1143718943' && assertion.repository_owner_id == '244123794' && assertion.workflow_ref == 'gunb-ai/gunbc/.github/workflows/fleet-converge.yml@refs/heads/main' && assertion.ref == 'refs/heads/main' && assertion.event_name == 'workflow_dispatch' && assertion.environment == 'gcp-iam-converge' -workflow principal set principalSet://iam.googleapis.com/projects/582015116396/locations/global/workloadIdentityPools/github-gcp-iam-converge/* -deny policy gunbc-iam-converge-boundary -deny principal serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com -deny permissions secretmanager.googleapis.com/versions.access,secretmanager.googleapis.com/versions.add,iam.googleapis.com/serviceAccounts.actAs,iam.googleapis.com/serviceAccounts.getAccessToken,iam.googleapis.com/serviceAccounts.getOpenIdToken,iam.googleapis.com/serviceAccounts.implicitDelegation,iam.googleapis.com/serviceAccounts.signBlob,iam.googleapis.com/serviceAccounts.signJwt -Target bootstrap creates only bare pools/accounts; target providers and workload trust remain approval-gated. -role projects/gunbai-secrets/roles/gunbcIamConvergeCreate permissions=iam.workloadIdentityPools.create,iam.workloadIdentityPools.get,iam.serviceAccounts.create,iam.serviceAccounts.get -role projects/gunbai-secrets/roles/gunbcIamConvergeProvider permissions=iam.workloadIdentityPoolProviders.create,iam.workloadIdentityPoolProviders.get,iam.workloadIdentityPoolProviders.list -role projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy permissions=iam.serviceAccounts.get,iam.serviceAccounts.getIamPolicy,iam.serviceAccounts.setIamPolicy -role projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy permissions=secretmanager.secrets.get,secretmanager.secrets.getIamPolicy,secretmanager.secrets.setIamPolicy -role projects/gunbai-secrets/roles/gunbcIamObservePool permissions=iam.workloadIdentityPools.get,iam.workloadIdentityPoolProviders.get,iam.workloadIdentityPoolProviders.list -role projects/gunbai-secrets/roles/gunbcIamObserveAccount permissions=iam.serviceAccounts.get,iam.serviceAccounts.getIamPolicy -role projects/gunbai-secrets/roles/gunbcIamObserveSecret permissions=secretmanager.secrets.get,secretmanager.secrets.getIamPolicy -binding projects/gunbai-secrets role:52:projects/gunbai-secrets/roles/gunbcIamConvergeCreate;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/locations/global/workloadIdentityPools/github-heal-publisher role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com role:59:projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/agent-github-app-private-key role:58:projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/locations/global/workloadIdentityPools/github-heal-publisher-private role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com role:59:projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/locations/global/workloadIdentityPools/github-mtcollins1-boot role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com role:59:projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/bmc-mtcollins1-gunbc role:58:projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/fleet-automation-ssh-key role:58:projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/locations/global/workloadIdentityPools/github-namecheap-dns role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com role:59:projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/namecheap-api-key role:58:projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/locations/global/workloadIdentityPools/github-heal-publisher role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com role:52:projects/gunbai-secrets/roles/gunbcIamObserveAccount;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/agent-github-app-private-key role:51:projects/gunbai-secrets/roles/gunbcIamObserveSecret;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/locations/global/workloadIdentityPools/github-heal-publisher-private role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com role:52:projects/gunbai-secrets/roles/gunbcIamObserveAccount;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/locations/global/workloadIdentityPools/github-mtcollins1-boot role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com role:52:projects/gunbai-secrets/roles/gunbcIamObserveAccount;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/bmc-mtcollins1-gunbc role:51:projects/gunbai-secrets/roles/gunbcIamObserveSecret;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/fleet-automation-ssh-key role:51:projects/gunbai-secrets/roles/gunbcIamObserveSecret;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/locations/global/workloadIdentityPools/github-namecheap-dns role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com role:52:projects/gunbai-secrets/roles/gunbcIamObserveAccount;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/namecheap-api-key role:51:projects/gunbai-secrets/roles/gunbcIamObserveSecret;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/locations/global/workloadIdentityPools/github-gcp-iam-converge role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com role:52:projects/gunbai-secrets/roles/gunbcIamObserveAccount;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/approval-submission-mac-key role:51:projects/gunbai-secrets/roles/gunbcIamObserveSecret;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com role:52:projects/gunbai-secrets/roles/gunbcIamObserveAccount;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -binding projects/582015116396/secrets/approval-submission-mac-key role:34:roles/secretmanager.secretAccessor;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition-title:54:gcp-iam-converge pinned approval-submission-mac-key v1;condition-expression:87:resource.name == "projects/582015116396/secrets/approval-submission-mac-key/versions/1";condition-description:96:gunbc.auth.gcp_iam_converge: the one version the gcp iam converge job reads, to sign its request; -binding projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com role:30:roles/iam.workloadIdentityUser;member:120:principalSet://iam.googleapis.com/projects/582015116396/locations/global/workloadIdentityPools/github-gcp-iam-converge/*;condition:4:none; -binding projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com role:30:roles/iam.workloadIdentityUser;member:120:principalSet://iam.googleapis.com/projects/582015116396/locations/global/workloadIdentityPools/github-gcp-iam-converge/*;condition:4:none; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/receipt.json b/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/receipt.json deleted file mode 100644 index 192c58d5a66..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-2026-09-28/receipt.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "source_revision": "a23a0f99cbfe3f39da52cd40638eb44c85e76c6c", - "base_revision": "2886b9e396d5ca6026c54212f27c52ea4113fba4", - "binary_sha256": "179882133a4c03958a1bed8a8b444ea06e48c63a6c42263ff83694508825b415", - "closure_modules": 837, - "closure_manifest_sha256": "8b62fc4c64f5d76a879ae2c2fb7802e3a2aa5d53684431d7fa3276cd70d8d298", - "memory_max": "6G", - "memory_swap_max": 0, - "release_build_exit": 0, - "focused_controls": { - "passed": 3, - "failed": 0 - }, - "plan_exit": 0, - "missing_credential_exit": 1, - "missing_credential_exact_reason_verified": true, - "missing_credential_created_started_receipt": false, - "cloud_bootstrap": { - "status": "refused", - "attempts": 3, - "completed_stages": [ - "identities", - "roles" - ], - "refused_stage": "deny", - "http_status": 403, - "missing_permission": "iam.denypolicies.create", - "capability_bindings_applied": false, - "workflow_trust_applied": false, - "temporary_credentials_removed": true, - "fresh_token_retry": "same HTTP 403 at deny-policy creation" - }, - "full_landing_suite": "not run", - "oidc_approval_workflow_acceptance": "not run" -} diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/README.md b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/README.md deleted file mode 100644 index 10cf532a6c9..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Temporary bootstrap authority convergence receipt - -Source: `70009ad9d2c8f184153976576f5a1ec2211da1b3` on draft PR #12565. -The branch binary passed 12 focused controls over 843 byte-identical modules, -within 6 GiB with swap disabled. The full landing suite was not run. - -The organization-scoped run verified the operator subject and primary IAM email, -verified project ancestry, granted the time-bounded Deny Admin cell, installed -and read back the project deny policy, then removed the exact temporary grant -and verified its absence. The retirement journal and sanitized execution log -are included. Existing unrelated grants were outside the owned cell. - -The capabilities stage subsequently applied the project create-role binding, -then stopped on a successful pool getIamPolicy response containing `{}` with -neither a body etag nor an HTTP ETag. No pool policy write followed. Account -privilege probes and workflow trust were not reached. The bootstrap as a whole -is incomplete; no VM or fleet deployment acceptance is claimed. - -The earlier project-scoped attempt was rejected by GCP with HTTP 400: Deny Admin -is grantable at organization scope. Its log and initial seven-control receipt -are historical, not qualification of the final source. The new run also binds -the verified stable subject to the actual primary email `brian@gunb.ai`. - -## Remaining dependency - -The pool's initial IAM policy needs an established safe publication contract. -A successful empty read does not supply an optimistic-concurrency token. Do not -replace this with an unconditional setIamPolicy or fabricate an etag. Google's -[Policy contract](https://docs.cloud.google.com/iam/docs/reference/rest/v1/Policy) -describes etag as the concurrency protection; the current required-etag wire -shape reports this boundary as a decode refusal. A follow-up should model the -missing publication prerequisite explicitly, including provider-supported -initialization or established exclusive ownership, before enabling the write. - -The temporary credential file was removed by the launcher. No token is included. diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/controls.log b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/controls.log deleted file mode 100644 index afefd6cdcf9..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/controls.log +++ /dev/null @@ -1,11 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -PASS bootstrap_role_drift_never_authorizes_adoption -PASS deny_exceptions_and_incomplete_readback_withhold_trust -PASS workflow_trust_requires_completed_deny_and_privilege_readback -PASS authority_deadline_is_bounded_and_retry_does_not_renew_it -PASS uncertain_publication_cannot_be_settled_from_an_absent_cell -PASS unknown_policy_and_unowned_grants_never_authorize_changes -PASS authority_retirement_preserves_existing_human_and_foreign_access diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt deleted file mode 100644 index 96c40eedfff..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt +++ /dev/null @@ -1 +0,0 @@ -target-project:gunbai-secrets;authority-resource:organizations/266638272282;role:19:roles/iam.denyAdmin;member:18:user:brian@gunb.ai;condition-title:50:gunbc-bootstrap-deny-deny-bootstrap-org-20260928-1;condition-expression:48:request.time < timestamp('2026-09-28T23:15:00Z');condition-description:95:Temporary dependency for gunbc IAM deny-policy installation; cleanup remains owed after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt deleted file mode 100644 index 96c40eedfff..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt +++ /dev/null @@ -1 +0,0 @@ -target-project:gunbai-secrets;authority-resource:organizations/266638272282;role:19:roles/iam.denyAdmin;member:18:user:brian@gunb.ai;condition-title:50:gunbc-bootstrap-deny-deny-bootstrap-org-20260928-1;condition-expression:48:request.time < timestamp('2026-09-28T23:15:00Z');condition-description:95:Temporary dependency for gunbc IAM deny-policy installation; cleanup remains owed after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt deleted file mode 100644 index 96c40eedfff..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt +++ /dev/null @@ -1 +0,0 @@ -target-project:gunbai-secrets;authority-resource:organizations/266638272282;role:19:roles/iam.denyAdmin;member:18:user:brian@gunb.ai;condition-title:50:gunbc-bootstrap-deny-deny-bootstrap-org-20260928-1;condition-expression:48:request.time < timestamp('2026-09-28T23:15:00Z');condition-description:95:Temporary dependency for gunbc IAM deny-policy installation; cleanup remains owed after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt deleted file mode 100644 index 96c40eedfff..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt +++ /dev/null @@ -1 +0,0 @@ -target-project:gunbai-secrets;authority-resource:organizations/266638272282;role:19:roles/iam.denyAdmin;member:18:user:brian@gunb.ai;condition-title:50:gunbc-bootstrap-deny-deny-bootstrap-org-20260928-1;condition-expression:48:request.time < timestamp('2026-09-28T23:15:00Z');condition-description:95:Temporary dependency for gunbc IAM deny-policy installation; cleanup remains owed after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-authority-cleanup.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-authority-cleanup.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-authority-cleanup.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-capabilities.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-capabilities.txt deleted file mode 100644 index c897ef1e0af..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-capabilities.txt +++ /dev/null @@ -1 +0,0 @@ -refused: status 200 undecodable: HTTP 200 body did not inhabit the declared output (null at etag) \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-deny.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-deny.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-deny.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-identities.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-identities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-identities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-roles.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-roles.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-roles.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-started.txt b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-started.txt deleted file mode 100644 index b286fc75b3e..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/gcp-iam-bootstrap-20260928-organization-authority-1-started.txt +++ /dev/null @@ -1 +0,0 @@ -operator bootstrap started; not a convergence receipt diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/operator-userinfo-readback.json b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/operator-userinfo-readback.json deleted file mode 100644 index 7d0a1f9a653..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/operator-userinfo-readback.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "endpoint": "https://openidconnect.googleapis.com/v1/userinfo", - "observation": "read-only authenticated API research; native convergence verifies before publication", - "sub": "116084671989231734979", - "email": "brian@gunb.ai", - "email_verified": true -} diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/organization-apply.log b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/organization-apply.log deleted file mode 100644 index 6b28875b530..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/organization-apply.log +++ /dev/null @@ -1,117 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -[file] read -[file] write_create_new target/gcp-iam-bootstrap-20260928-organization-authority-1-started.txt (54 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[file] write_create_new target/gcp-iam-bootstrap-20260928-organization-authority-1-identities.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeCreate -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeProvider -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObservePool -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveAccount -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveSecret -[file] write_create_new target/gcp-iam-bootstrap-20260928-organization-authority-1-roles.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[rest] GET https://openidconnect.googleapis.com/v1/userinfo -[rest] GET https://cloudresourcemanager.googleapis.com/v3/projects/gunbai-secrets -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt (397 bytes) -◐ started Clock.Now -✓ Clock.Now done in 1ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt (397 bytes) -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:setIamPolicy -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt (397 bytes) -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -◐ started Clock.Now -✓ Clock.Now done in 2ms -[rest] POST https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[file] write_create_new target/gcp-iam-bootstrap-20260928-organization-authority-1-deny.txt (78 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:setIamPolicy -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] write_create_new target/gcp-iam-bootstrap-20260928-organization-authority-1-authority-cleanup.txt (78 bytes) -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:getIamPolicy -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:setIamPolicy -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[file] write_create_new target/gcp-iam-bootstrap-20260928-organization-authority-1-capabilities.txt (97 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -status 200 undecodable: HTTP 200 body did not inhabit the declared output (null at etag) diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/organization-identity-controls.log b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/organization-identity-controls.log deleted file mode 100644 index 29fdcef5d65..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/organization-identity-controls.log +++ /dev/null @@ -1,16 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -PASS bootstrap_role_drift_never_authorizes_adoption -PASS deny_exceptions_and_incomplete_readback_withhold_trust -PASS workflow_trust_requires_completed_deny_and_privilege_readback -PASS authority_deadline_is_bounded_and_retry_does_not_renew_it -PASS uncertain_publication_cannot_be_settled_from_an_absent_cell -PASS unknown_policy_and_unowned_grants_never_authorize_changes -PASS authority_retirement_preserves_existing_human_and_foreign_access -PASS bootstrap_authority_requires_an_organization_resource -PASS definite_publication_rejection_is_distinct_from_unknown_commit -PASS deny_propagation_retry_requires_live_authority_and_a_remaining_budget -PASS cleanup_dependency_does_not_wait_for_successful_deny_installation -PASS authority_beneficiary_requires_the_verified_stable_google_subject diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/organization-receipt.json b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/organization-receipt.json deleted file mode 100644 index 68ca89d3178..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/organization-receipt.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "source_revision": "70009ad9d2c8f184153976576f5a1ec2211da1b3", - "binary_sha256": "179882133a4c03958a1bed8a8b444ea06e48c63a6c42263ff83694508825b415", - "closure_modules": 843, - "closure_manifest_sha256": "73b2373809d4b41d22cf8d00db62dc996afd06042a28b676409e735b6b3653ca", - "memory_max": "6G", - "memory_swap_max": 0, - "focused_controls_passed": 12, - "authority_operation": "deny-bootstrap-org-20260928-1", - "authority_expires_at": "2026-09-28T23:15:00Z", - "organization": "organizations/266638272282", - "operator_subject": "116084671989231734979", - "operator_member": "user:brian@gunb.ai", - "cloud_status": "temporary authority granted and read back; deny policy installed and read back; temporary authority removed and absence verified; capabilities stopped at missing pool policy etag", - "full_landing_suite": "not run", - "workflow_trust": "not enabled", - "credential_file_removed": true, - "pool_policy_readback": { - "http": 200, - "etag_header": null, - "policy": {} - }, - "remaining_dependency": "safe initial pool IAM policy publication with concurrency protection" -} diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/project-ancestry-readback.json b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/project-ancestry-readback.json deleted file mode 100644 index a8c927d63d3..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/project-ancestry-readback.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "endpoint": "https://cloudresourcemanager.googleapis.com/v3/projects/gunbai-secrets", - "observation": "read-only authenticated API research; native convergence rereads this before publication", - "name": "projects/582015116396", - "projectId": "gunbai-secrets", - "parent": "organizations/266638272282", - "state": "ACTIVE" -} diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/project-scope-rejected.log b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/project-scope-rejected.log deleted file mode 100644 index 68eee46624a..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/project-scope-rejected.log +++ /dev/null @@ -1,86 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -[file] read -[file] write_create_new target/gcp-iam-bootstrap-20260928-authority-1-started.txt (54 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[file] write_create_new target/gcp-iam-bootstrap-20260928-authority-1-identities.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeCreate -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeProvider -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObservePool -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveAccount -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveSecret -[file] write_create_new target/gcp-iam-bootstrap-20260928-authority-1-roles.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:getIamPolicy -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-20260928-1-intent.txt (344 bytes) -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-20260928-1-publication-elected.txt (344 bytes) -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:setIamPolicy -[file] write_create_new target/gcp-iam-bootstrap-20260928-authority-1-deny.txt (230 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -authority mutation not established; recovery remains owed: status 400: { - "error": { - "code": 400, - "message": "Role roles/iam.denyAdmin is not supported for this resource.", - "status": "INVALID_ARGUMENT" - } -} -; authority cleanup outstanding: authority dependency unresolved: expired, unowned, or publication may still commit; no grant retry or renewal diff --git a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/receipt.json b/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/receipt.json deleted file mode 100644 index ae232277a9b..00000000000 --- a/docs/plans/receipts/gcp-iam-bootstrap-authority-2026-09-28/receipt.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "source_revision": "e413906c2a71e3268ddf4b93d0327f3d1becf2d4", - "binary_sha256": "179882133a4c03958a1bed8a8b444ea06e48c63a6c42263ff83694508825b415", - "closure_modules": 842, - "closure_manifest_sha256": "6befe7f9b00c41861cdccb76e1a7c25868bde8a876ccbd083b52c31ea905a73d", - "memory_max": "6G", - "memory_swap_max": 0, - "focused_controls_passed": 7, - "authority_operation": "deny-bootstrap-20260928-1", - "authority_expires_at": "2026-09-28T23:00:00Z", - "cloud_status": "HTTP 400: Deny Admin is not grantable on a project; no grant applied", - "full_landing_suite": "not run", - "temporary_credentials_removed": true -} diff --git a/docs/plans/receipts/gcp-iam-pool-initialization-2026-09-29/README.md b/docs/plans/receipts/gcp-iam-pool-initialization-2026-09-29/README.md deleted file mode 100644 index 1e751548a20..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-initialization-2026-09-29/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Exclusive pool initialization source qualification - -The operator confirmed on 2026-09-29 that no other administrator will update the -pool policies during this bootstrap. The change models a one-off initial policy -publication under that exclusivity assumption, then requires an observed etag for -all normal updates. This is not provider-enforced CAS during initialization. - -All 15 focused controls passed under 6 GiB with no swap, using the branch release -binary and 843 byte-identical dependency modules. Controls distinguish empty -wire policies from update authority and reject nonempty unfenced policies, -unsupported policy versions, existing audit configuration, undeclared pools, -and incomplete or revisionless initialization readback. - -The complete bootstrap module typechecked in this closure. The new cloud-write -path and interrupted-publication recovery have not been exercised against GCP. -No live pool initialization or workflow credential-minting acceptance is claimed. -The create-only publication journal stays in the same recovery workspace; an -uncertain publication cannot be retried from a fresh workspace. - -An existing administrator credential is still needed for initial bootstrap. The -normal approval workflow first authenticates as iam-observe through its dedicated -pool, then requests approval and obtains iam-converge credentials. Its initial -trust is not yet installed, so it cannot create that trust for itself. There is -no usable gcloud installation, ADC file, or configured token file in this shell. diff --git a/docs/plans/receipts/gcp-iam-pool-initialization-2026-09-29/controls.log b/docs/plans/receipts/gcp-iam-pool-initialization-2026-09-29/controls.log deleted file mode 100644 index c0cc6807ad4..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-initialization-2026-09-29/controls.log +++ /dev/null @@ -1,19 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 20 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -PASS bootstrap_role_drift_never_authorizes_adoption -PASS deny_exceptions_and_incomplete_readback_withhold_trust -PASS workflow_trust_requires_completed_deny_and_privilege_readback -PASS authority_deadline_is_bounded_and_retry_does_not_renew_it -PASS uncertain_publication_cannot_be_settled_from_an_absent_cell -PASS unknown_policy_and_unowned_grants_never_authorize_changes -PASS authority_retirement_preserves_existing_human_and_foreign_access -PASS bootstrap_authority_requires_an_organization_resource -PASS definite_publication_rejection_is_distinct_from_unknown_commit -PASS deny_propagation_retry_requires_live_authority_and_a_remaining_budget -PASS cleanup_dependency_does_not_wait_for_successful_deny_installation -PASS authority_beneficiary_requires_the_verified_stable_google_subject -PASS empty_pool_policy_does_not_authorize_unconditional_publication -PASS pool_initialization_rejects_existing_unfenced_bindings -PASS pool_initialization_readback_requires_exact_cells_and_a_revision diff --git a/docs/plans/receipts/gcp-iam-pool-initialization-2026-09-29/receipt.json b/docs/plans/receipts/gcp-iam-pool-initialization-2026-09-29/receipt.json deleted file mode 100644 index a4206b1529a..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-initialization-2026-09-29/receipt.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "source_revision": "07c17deb9eba5a1b545d68e56fddbfacac1c94e4", - "binary_sha256": "179882133a4c03958a1bed8a8b444ea06e48c63a6c42263ff83694508825b415", - "closure_modules": 843, - "memory_max": "6G", - "memory_swap_max": 0, - "focused_controls_passed": 15, - "live_pool_initialization": "not executed", - "full_landing_suite": "not run", - "credential_dependency": "existing administrator credential; normal workflow initial federation still awaits bootstrap" -} diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/apply.log b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/apply.log deleted file mode 100644 index 320d988a0d7..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/apply.log +++ /dev/null @@ -1,244 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -[file] read -[file] write_create_new target/gcp-iam-bootstrap-20260929-pool-initialization-1-started.txt (54 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[file] write_create_new target/gcp-iam-bootstrap-20260929-pool-initialization-1-identities.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeCreate -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeProvider -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObservePool -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveAccount -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveSecret -[file] write_create_new target/gcp-iam-bootstrap-20260929-pool-initialization-1-roles.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[file] write_create_new target/gcp-iam-bootstrap-20260929-pool-initialization-1-deny.txt (78 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[file] write_create_new target/gcp-iam-bootstrap-20260929-pool-initialization-1-authority-cleanup.txt (78 bytes) -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] GET https://openidconnect.googleapis.com/v1/userinfo -[file] write_create_new target/gcp-iam-pool-initialization-github-heal-publisher-elected.txt (438 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[file] write_create_new target/gcp-iam-pool-initialization-github-heal-publisher-closed.txt (438 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] GET https://openidconnect.googleapis.com/v1/userinfo -[file] write_create_new target/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt (439 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[file] write_create_new target/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt (439 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] GET https://openidconnect.googleapis.com/v1/userinfo -[file] write_create_new target/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt (437 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[file] write_create_new target/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt (437 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-gcp-iam-converge-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-gcp-iam-converge-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:getIamPolicy -[rest] GET https://openidconnect.googleapis.com/v1/userinfo -[file] write_create_new target/gcp-iam-pool-initialization-github-gcp-iam-converge-elected.txt (282 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:getIamPolicy -[file] write_create_new target/gcp-iam-pool-initialization-github-gcp-iam-converge-closed.txt (282 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:setIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[file] write_create_new target/gcp-iam-bootstrap-20260929-pool-initialization-1-capabilities.txt (78 bytes) -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -[file] write_create_new target/gcp-iam-bootstrap-20260929-pool-initialization-1-privilege-readback.txt (1028 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -apply-account readback credential unavailable; workflow trust withheld: status 403: { - "error": { - "code": 403, - "message": "Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist). Remediate access with this Troubleshooter URL or share it with your administrator - https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWE5My04Mzg0LTcwNzItYmI4OC04MjJiYWNlNzVkMGISAA%3D%3D .", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "IAM_PERMISSION_DENIED", - "domain": "iam.googleapis.com", - "metadata": { - "error_info_id": "CiQwMWEwZWE5My04Mzg0LTcwNzItYmI4OC04MjJiYWNlNzVkMGISAA==", - "permission": "iam.serviceAccounts.getAccessToken", - "troubleshooter_url": "https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWE5My04Mzg0LTcwNzItYmI4OC04MjJiYWNlNzVkMGISAA%3D%3D" - } - } - ] - } -} - diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-authority-cleanup.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-authority-cleanup.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-authority-cleanup.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-capabilities.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-capabilities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-capabilities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-deny.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-deny.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-deny.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-identities.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-identities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-identities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-privilege-readback.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-privilege-readback.txt deleted file mode 100644 index 7b6068988cd..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-privilege-readback.txt +++ /dev/null @@ -1,19 +0,0 @@ -refused: apply-account readback credential unavailable; workflow trust withheld: status 403: { - "error": { - "code": 403, - "message": "Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist). Remediate access with this Troubleshooter URL or share it with your administrator - https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWE5My04Mzg0LTcwNzItYmI4OC04MjJiYWNlNzVkMGISAA%3D%3D .", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "IAM_PERMISSION_DENIED", - "domain": "iam.googleapis.com", - "metadata": { - "error_info_id": "CiQwMWEwZWE5My04Mzg0LTcwNzItYmI4OC04MjJiYWNlNzVkMGISAA==", - "permission": "iam.serviceAccounts.getAccessToken", - "troubleshooter_url": "https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWE5My04Mzg0LTcwNzItYmI4OC04MjJiYWNlNzVkMGISAA%3D%3D" - } - } - ] - } -} diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-roles.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-roles.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-roles.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-started.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-started.txt deleted file mode 100644 index b286fc75b3e..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-bootstrap-20260929-pool-initialization-1-started.txt +++ /dev/null @@ -1 +0,0 @@ -operator bootstrap started; not a convergence receipt diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-gcp-iam-converge-closed.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-gcp-iam-converge-closed.txt deleted file mode 100644 index d87ad974c40..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-gcp-iam-converge-closed.txt +++ /dev/null @@ -1,3 +0,0 @@ -exclusive-pool-policy-initialization-20260929 -projects/582015116396/locations/global/workloadIdentityPools/github-gcp-iam-converge -role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-gcp-iam-converge-elected.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-gcp-iam-converge-elected.txt deleted file mode 100644 index d87ad974c40..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-gcp-iam-converge-elected.txt +++ /dev/null @@ -1,3 +0,0 @@ -exclusive-pool-policy-initialization-20260929 -projects/582015116396/locations/global/workloadIdentityPools/github-gcp-iam-converge -role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-heal-publisher-closed.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-heal-publisher-closed.txt deleted file mode 100644 index b195bdceb73..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-heal-publisher-closed.txt +++ /dev/null @@ -1,4 +0,0 @@ -exclusive-pool-policy-initialization-20260929 -projects/582015116396/locations/global/workloadIdentityPools/github-heal-publisher -role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-heal-publisher-elected.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-heal-publisher-elected.txt deleted file mode 100644 index b195bdceb73..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-heal-publisher-elected.txt +++ /dev/null @@ -1,4 +0,0 @@ -exclusive-pool-policy-initialization-20260929 -projects/582015116396/locations/global/workloadIdentityPools/github-heal-publisher -role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt deleted file mode 100644 index f4d0845f19d..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt +++ /dev/null @@ -1,4 +0,0 @@ -exclusive-pool-policy-initialization-20260929 -projects/582015116396/locations/global/workloadIdentityPools/github-mtcollins1-boot -role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt deleted file mode 100644 index f4d0845f19d..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt +++ /dev/null @@ -1,4 +0,0 @@ -exclusive-pool-policy-initialization-20260929 -projects/582015116396/locations/global/workloadIdentityPools/github-mtcollins1-boot -role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt deleted file mode 100644 index ece3b479b8e..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt +++ /dev/null @@ -1,4 +0,0 @@ -exclusive-pool-policy-initialization-20260929 -projects/582015116396/locations/global/workloadIdentityPools/github-namecheap-dns -role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt deleted file mode 100644 index ece3b479b8e..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt +++ /dev/null @@ -1,4 +0,0 @@ -exclusive-pool-policy-initialization-20260929 -projects/582015116396/locations/global/workloadIdentityPools/github-namecheap-dns -role:54:projects/gunbai-secrets/roles/gunbcIamConvergeProvider;member:66:serviceAccount:iam-converge@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; -role:49:projects/gunbai-secrets/roles/gunbcIamObservePool;member:65:serviceAccount:iam-observe@gunbai-secrets.iam.gserviceaccount.com;condition:4:none; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/receipt.json b/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/receipt.json deleted file mode 100644 index b2159f7436f..00000000000 --- a/docs/plans/receipts/gcp-iam-pool-live-2026-09-29/receipt.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "source_revision": "be068166e93def4ab273b3d4cd11c5476a182e57", - "pool_initialization": "completed with readbacks and revision tokens", - "capabilities": "completed", - "privilege_readback": "refused: operator lacks iam.serviceAccounts.getAccessToken on iam-converge", - "workflow_trust": "not granted", - "temporary_deny_authority": "absence reverified", - "temporary_token_file": "removed", - "memory_max": "6G", - "memory_swap_max": 0 -} diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/apply.log b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/apply.log deleted file mode 100644 index dcf9fd23b54..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/apply.log +++ /dev/null @@ -1,404 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 20 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -[file] read -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-1-started.txt (54 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-1-identities.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeCreate -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeProvider -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObservePool -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveAccount -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveSecret -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/roles -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-1-roles.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-1-deny.txt (78 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-1-authority-cleanup.txt (78 bytes) -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-gcp-iam-converge-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-gcp-iam-converge-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-1-capabilities.txt (78 bytes) -[rest] GET https://openidconnect.googleapis.com/v1/userinfo -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt (427 bytes) -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt (427 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (427 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt (426 bytes) -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt (426 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (426 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (426 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-1-probe-authority.txt (78 bytes) -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 1ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 1ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 1ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 2 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-1-privilege-readback.txt (1028 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (426 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt (426 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -apply-account readback credential unavailable; workflow trust withheld: status 403: { - "error": { - "code": 403, - "message": "Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist). Remediate access with this Troubleshooter URL or share it with your administrator - https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWE5ZS05ODlkLTcwZjYtOTRiZS1hOTZmNWI2NjYwNjMSAA%3D%3D .", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "IAM_PERMISSION_DENIED", - "domain": "iam.googleapis.com", - "metadata": { - "troubleshooter_url": "https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWE5ZS05ODlkLTcwZjYtOTRiZS1hOTZmNWI2NjYwNjMSAA%3D%3D", - "permission": "iam.serviceAccounts.getAccessToken", - "error_info_id": "CiQwMWEwZWE5ZS05ODlkLTcwZjYtOTRiZS1hOTZmNWI2NjYwNjMSAA==" - } - } - ] - } -} - diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/controls.log b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/controls.log deleted file mode 100644 index 6fdb2b3d97e..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/controls.log +++ /dev/null @@ -1,21 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -PASS bootstrap_role_drift_never_authorizes_adoption -PASS deny_exceptions_and_incomplete_readback_withhold_trust -PASS workflow_trust_requires_completed_deny_and_privilege_readback -PASS authority_deadline_is_bounded_and_retry_does_not_renew_it -PASS uncertain_publication_cannot_be_settled_from_an_absent_cell -PASS unknown_policy_and_unowned_grants_never_authorize_changes -PASS authority_retirement_preserves_existing_human_and_foreign_access -PASS bootstrap_authority_requires_an_organization_resource -PASS definite_publication_rejection_is_distinct_from_unknown_commit -PASS deny_propagation_retry_requires_live_authority_and_a_remaining_budget -PASS cleanup_dependency_does_not_wait_for_successful_deny_installation -PASS authority_beneficiary_requires_the_verified_stable_google_subject -PASS empty_pool_policy_does_not_authorize_unconditional_publication -PASS pool_initialization_rejects_existing_unfenced_bindings -PASS pool_initialization_readback_requires_exact_cells_and_a_revision -PASS probe_authority_has_no_signing_or_policy_permission -PASS probe_cleanup_is_required_for_trust_but_not_successful_probes diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-authority-cleanup.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-authority-cleanup.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-authority-cleanup.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-capabilities.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-capabilities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-capabilities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-deny.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-deny.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-deny.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-identities.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-identities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-identities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-privilege-readback.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-privilege-readback.txt deleted file mode 100644 index 46c242cbff9..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-privilege-readback.txt +++ /dev/null @@ -1,19 +0,0 @@ -refused: apply-account readback credential unavailable; workflow trust withheld: status 403: { - "error": { - "code": 403, - "message": "Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist). Remediate access with this Troubleshooter URL or share it with your administrator - https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWE5ZS05ODlkLTcwZjYtOTRiZS1hOTZmNWI2NjYwNjMSAA%3D%3D .", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "IAM_PERMISSION_DENIED", - "domain": "iam.googleapis.com", - "metadata": { - "troubleshooter_url": "https://console.cloud.google.com/iam-admin/troubleshooter/summary;errorId=CiQwMWEwZWE5ZS05ODlkLTcwZjYtOTRiZS1hOTZmNWI2NjYwNjMSAA%3D%3D", - "permission": "iam.serviceAccounts.getAccessToken", - "error_info_id": "CiQwMWEwZWE5ZS05ODlkLTcwZjYtOTRiZS1hOTZmNWI2NjYwNjMSAA==" - } - } - ] - } -} diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-probe-authority.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-probe-authority.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-probe-authority.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-roles.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-roles.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-roles.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-started.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-started.txt deleted file mode 100644 index b286fc75b3e..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-1-started.txt +++ /dev/null @@ -1 +0,0 @@ -operator bootstrap started; not a convergence receipt diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt deleted file mode 100644 index ed615588743..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-1;condition-expression:48:request.time < timestamp('2026-09-29T01:05:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt deleted file mode 100644 index ed615588743..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-1;condition-expression:48:request.time < timestamp('2026-09-29T01:05:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt deleted file mode 100644 index ed615588743..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-1;condition-expression:48:request.time < timestamp('2026-09-29T01:05:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt deleted file mode 100644 index ed615588743..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-1;condition-expression:48:request.time < timestamp('2026-09-29T01:05:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt deleted file mode 100644 index 10de7ae96f7..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-1;condition-expression:48:request.time < timestamp('2026-09-29T01:05:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt deleted file mode 100644 index 10de7ae96f7..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-1;condition-expression:48:request.time < timestamp('2026-09-29T01:05:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt deleted file mode 100644 index 10de7ae96f7..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-1;condition-expression:48:request.time < timestamp('2026-09-29T01:05:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt deleted file mode 100644 index 10de7ae96f7..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/gcp-iam-probe-probe-20260929-1-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-1;condition-expression:48:request.time < timestamp('2026-09-29T01:05:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/receipt.json b/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/receipt.json deleted file mode 100644 index 5eec823bba3..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-authority-2026-09-29/receipt.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "source_revision": "da6f77d4bd3929d41c6ea4a58093420a6a4cb7d4", - "binary_sha256": "179882133a4c03958a1bed8a8b444ea06e48c63a6c42263ff83694508825b415", - "focused_controls_passed": 17, - "closure_modules": 843, - "memory_max": "6G", - "memory_swap_max": 0, - "probe_operation": "probe-20260929-1", - "probe_deadline": "2026-09-29T01:05:00Z", - "cloud_status": "both temporary probe grants installed and read back; token mint refused through bounded retry; both grants removed and absence verified; workflow trust withheld", - "full_landing_suite": "not run", - "temporary_token_file": "removed", - "mint_attempts": 30 -} diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/apply.log b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/apply.log deleted file mode 100644 index f3988596a10..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/apply.log +++ /dev/null @@ -1,352 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -[file] read -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-2-started.txt (54 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-2-identities.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeCreate -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeProvider -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObservePool -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveAccount -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveSecret -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-2-roles.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-2-deny.txt (78 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 1ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-2-authority-cleanup.txt (78 bytes) -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-gcp-iam-converge-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-gcp-iam-converge-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-2-capabilities.txt (78 bytes) -[rest] GET https://openidconnect.googleapis.com/v1/userinfo -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt (427 bytes) -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt (427 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (427 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt (426 bytes) -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt (426 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (426 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (426 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-2-probe-authority.txt (78 bytes) -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:testIamPermissions -[file] write_create_new target/gcp-iam-bootstrap-20260929-probe-authority-2-privilege-readback.txt (1828 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (426 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt (426 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -status 403: { - "error": { - "code": 403, - "message": "Cloud Resource Manager API has not been used in project 582015116396 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview?project=582015116396 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "SERVICE_DISABLED", - "domain": "googleapis.com", - "metadata": { - "containerInfo": "582015116396", - "service": "cloudresourcemanager.googleapis.com", - "consumer": "projects/582015116396", - "serviceTitle": "Cloud Resource Manager API", - "activationUrl": "https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview?project=582015116396" - } - }, - { - "@type": "type.googleapis.com/google.rpc.LocalizedMessage", - "locale": "en-US", - "message": "Cloud Resource Manager API has not been used in project 582015116396 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview?project=582015116396 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry." - }, - { - "@type": "type.googleapis.com/google.rpc.Help", - "links": [ - { - "description": "Google developers console API activation", - "url": "https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview?project=582015116396" - } - ] - } - ] - } -} - diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-authority-cleanup.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-authority-cleanup.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-authority-cleanup.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-capabilities.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-capabilities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-capabilities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-deny.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-deny.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-deny.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-identities.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-identities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-identities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-privilege-readback.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-privilege-readback.txt deleted file mode 100644 index ebc3b097d9f..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-privilege-readback.txt +++ /dev/null @@ -1,35 +0,0 @@ -refused: status 403: { - "error": { - "code": 403, - "message": "Cloud Resource Manager API has not been used in project 582015116396 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview?project=582015116396 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.", - "status": "PERMISSION_DENIED", - "details": [ - { - "@type": "type.googleapis.com/google.rpc.ErrorInfo", - "reason": "SERVICE_DISABLED", - "domain": "googleapis.com", - "metadata": { - "containerInfo": "582015116396", - "service": "cloudresourcemanager.googleapis.com", - "consumer": "projects/582015116396", - "serviceTitle": "Cloud Resource Manager API", - "activationUrl": "https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview?project=582015116396" - } - }, - { - "@type": "type.googleapis.com/google.rpc.LocalizedMessage", - "locale": "en-US", - "message": "Cloud Resource Manager API has not been used in project 582015116396 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview?project=582015116396 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry." - }, - { - "@type": "type.googleapis.com/google.rpc.Help", - "links": [ - { - "description": "Google developers console API activation", - "url": "https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview?project=582015116396" - } - ] - } - ] - } -} diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-probe-authority.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-probe-authority.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-probe-authority.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-roles.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-roles.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-roles.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-started.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-started.txt deleted file mode 100644 index b286fc75b3e..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-bootstrap-20260929-probe-authority-2-started.txt +++ /dev/null @@ -1 +0,0 @@ -operator bootstrap started; not a convergence receipt diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt deleted file mode 100644 index 8157e104a58..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-2;condition-expression:48:request.time < timestamp('2026-09-29T01:15:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt deleted file mode 100644 index 8157e104a58..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-2;condition-expression:48:request.time < timestamp('2026-09-29T01:15:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt deleted file mode 100644 index 8157e104a58..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-2;condition-expression:48:request.time < timestamp('2026-09-29T01:15:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt deleted file mode 100644 index 8157e104a58..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-2;condition-expression:48:request.time < timestamp('2026-09-29T01:15:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt deleted file mode 100644 index d91803eb262..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-2;condition-expression:48:request.time < timestamp('2026-09-29T01:15:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt deleted file mode 100644 index d91803eb262..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-2;condition-expression:48:request.time < timestamp('2026-09-29T01:15:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt deleted file mode 100644 index d91803eb262..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-2;condition-expression:48:request.time < timestamp('2026-09-29T01:15:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt deleted file mode 100644 index d91803eb262..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/gcp-iam-probe-probe-20260929-2-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-2;condition-expression:48:request.time < timestamp('2026-09-29T01:15:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/receipt.json b/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/receipt.json deleted file mode 100644 index 3583953c60c..00000000000 --- a/docs/plans/receipts/gcp-iam-probe-propagation-2026-09-29/receipt.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "source_revision": "1630eddc7", - "mint_attempts": 17, - "token_mint": "succeeded", - "positive_probe": "refused SERVICE_DISABLED cloudresourcemanager.googleapis.com for project 582015116396", - "temporary_grants": "both removed and absence read back", - "workflow_trust": "withheld", - "credential_file": "removed" -} diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/README.md b/docs/plans/receipts/gcp-iam-services-2026-09-29/README.md deleted file mode 100644 index 4bcaa3f7825..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Successful GCP IAM bootstrap, 2026-09-29 - -At source `85bdb4e19`, all ten modeled bootstrap stages completed with exit 0. -Cloud Resource Manager was enabled and independently read back on the exact -project; the other required APIs were already enabled. Resource-specific grants, -positive and negative permission probes, and the observer estate read succeeded. -The observer could read the pinned approval-submission credential; the writer -received exact HTTP 403 reading that same known-readable credential. - -Both temporary exact-account probe grants were removed and absence verified -before the final observer/apply workload-trust bindings were installed/read back. -The earlier temporary organization Deny Admin grant was also verified absent. -Issued probe tokens retain their original 600-second lifetime; grant removal is -not revocation of already issued tokens. The operator credential file was removed. - -18 focused controls passed over 844 byte-identical modules under 6 GiB/no swap. -The full landing suite was not run. This is bootstrap commissioning, not a VM -allocation or a completed approval-workflow acceptance run. - -Workflow acceptance was separately dispatched on main as run `36505895733`, -source `15b977d417c1a7de52eae96f48bcd3955fb1f94c`, mode `gcp_iam_converge`, host -`srv1`. That run uses its own GitHub OIDC credentials, not the supplied operator -token. Its success and approval delivery must be established independently. - -The acceptance run subsequently completed its release build and its observer -GitHub OIDC authentication step successfully. The plan/file/wait step remains -in progress; approval delivery, apply authentication, and apply/readback have -not yet been established. - -Attempt 1 subsequently passed request approval and writer GitHub OIDC -impersonation. Apply accepted a Namecheap provider creation, then refused because -the created subject was not yet visible on immediate readback. No later effects -were applied. This is a partial cloud change, not a successful estate convergence. - -Attempt 2 reruns the failed job on the same source, with a fresh run-attempt -identity and a freshly observed plan. It has passed approval and writer OIDC -impersonation and is executing apply/readback. No administrator token is involved. - -Attempt 2 completed successfully. The existing approval app admitted the fresh -request; the apply identity was minted only afterward, and the existing workflow -completed its approved effects and readback. Run: https://github.com/gunb-ai/gunbc/actions/runs/36505895733/attempts/2 . -This closes the ordinary GCP IAM workflow acceptance case, not host credential -custody, protected storage commissioning, or VM acceptance. diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/apply.log b/docs/plans/receipts/gcp-iam-services-2026-09-29/apply.log deleted file mode 100644 index 2bd54d5da0f..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/apply.log +++ /dev/null @@ -1,410 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -[file] read -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-started.txt (54 bytes) -[rest] GET https://serviceusage.googleapis.com/v1/projects/gunbai-secrets/services/iam.googleapis.com -[rest] GET https://serviceusage.googleapis.com/v1/projects/gunbai-secrets/services/iamcredentials.googleapis.com -[rest] GET https://serviceusage.googleapis.com/v1/projects/gunbai-secrets/services/cloudresourcemanager.googleapis.com -[rest] POST https://serviceusage.googleapis.com/v1/projects/gunbai-secrets/services:batchEnable -[rest] GET https://serviceusage.googleapis.com/v1/projects/gunbai-secrets/services/cloudresourcemanager.googleapis.com -[rest] GET https://serviceusage.googleapis.com/v1/projects/gunbai-secrets/services/secretmanager.googleapis.com -[rest] GET https://serviceusage.googleapis.com/v1/projects/gunbai-secrets/services/sts.googleapis.com -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-services.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-identities.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeCreate -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeProvider -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeAccountPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamConvergeSecretPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObservePool -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveAccount -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamObserveSecret -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-roles.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-deny.txt (78 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-authority-cleanup.txt (78 bytes) -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-heal-publisher-private-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-mtcollins1-boot-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-namecheap-dns-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[file] list target -[file] read target/gcp-iam-pool-initialization-github-gcp-iam-converge-elected.txt -[file] list target -[file] read target/gcp-iam-pool-initialization-github-gcp-iam-converge-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key:getIamPolicy -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-capabilities.txt (78 bytes) -[rest] GET https://openidconnect.googleapis.com/v1/userinfo -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt (427 bytes) -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt (427 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (427 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt (426 bytes) -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt (426 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (426 bytes) -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (426 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-probe-authority.txt (78 bytes) -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 1ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 1ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 1ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -◐ started Clock.Now -✓ Clock.Now done in 2ms -◐ started sleep.Delay.Seconds -✓ sleep.Delay.Seconds done in 5 seconds -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -[rest] POST https://cloudresourcemanager.googleapis.com/v1/projects/gunbai-secrets:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:testIamPermissions -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:testIamPermissions -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:testIamPermissions -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:testIamPermissions -[rest] POST https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:testIamPermissions -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-gcp-iam-converge:testIamPermissions -[rest] POST https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:generateAccessToken -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-heal-publisher-private/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/heal-publisher-private@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/agent-github-app-private-key:getIamPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-mtcollins1-boot/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/mtcollins1-boot@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/bmc-mtcollins1-gunbc:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/fleet-automation-ssh-key:getIamPolicy -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/locations/global/workloadIdentityPools/github-namecheap-dns/providers -[rest] GET https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/namecheap-dns@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/namecheap-api-key:getIamPolicy -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key/versions/1:access -[rest] GET https://secretmanager.googleapis.com/v1/projects/gunbai-secrets/secrets/approval-submission-mac-key/versions/1:access -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-privilege-readback.txt (78 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt (426 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt (426 bytes) -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-probe-cleanup.txt (78 bytes) -[rest] GET https://iam.googleapis.com/v2/policies/cloudresourcemanager.googleapis.com%2Fprojects%2F582015116396/denypolicies/gunbc-iam-converge-boundary -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:setIamPolicy -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -[file] write_create_new target/gcp-iam-bootstrap-20260929-services-and-probes-1-workflow-trust.txt (78 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt (427 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[rest] POST https://iam.googleapis.com/v1/projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt (426 bytes) -[file] list target -[file] read target/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-intent.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-elected.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-publication-closed.txt -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt -[rest] POST https://cloudresourcemanager.googleapis.com/v3/organizations/266638272282:getIamPolicy -◐ started Clock.Now -✓ Clock.Now done in 2ms -[file] write_create_new target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt (397 bytes) -[file] list target -[file] read target/gcp-iam-authority-deny-bootstrap-org-20260928-1-retired.txt diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/controls.log b/docs/plans/receipts/gcp-iam-services-2026-09-29/controls.log deleted file mode 100644 index bcdab55c990..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/controls.log +++ /dev/null @@ -1,22 +0,0 @@ -[workspace-root] discovered /home/briansrls/scratch-wt/gcp-iam-bootstrap -✓ typecheck v2.std.compilers.target_model done in 23 seconds -✓ typecheck v2.compiler.target_serialize done in 2 seconds -✓ typecheck v2.compiler.translate done in 3 seconds -PASS bootstrap_role_drift_never_authorizes_adoption -PASS deny_exceptions_and_incomplete_readback_withhold_trust -PASS workflow_trust_requires_completed_deny_and_privilege_readback -PASS authority_deadline_is_bounded_and_retry_does_not_renew_it -PASS uncertain_publication_cannot_be_settled_from_an_absent_cell -PASS unknown_policy_and_unowned_grants_never_authorize_changes -PASS authority_retirement_preserves_existing_human_and_foreign_access -PASS bootstrap_authority_requires_an_organization_resource -PASS definite_publication_rejection_is_distinct_from_unknown_commit -PASS deny_propagation_retry_requires_live_authority_and_a_remaining_budget -PASS cleanup_dependency_does_not_wait_for_successful_deny_installation -PASS authority_beneficiary_requires_the_verified_stable_google_subject -PASS empty_pool_policy_does_not_authorize_unconditional_publication -PASS pool_initialization_rejects_existing_unfenced_bindings -PASS pool_initialization_readback_requires_exact_cells_and_a_revision -PASS probe_authority_has_no_signing_or_policy_permission -PASS probe_cleanup_is_required_for_trust_but_not_successful_probes -PASS service_enablement_requires_enabled_state_on_the_exact_project diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-authority-cleanup.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-authority-cleanup.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-authority-cleanup.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-capabilities.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-capabilities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-capabilities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-deny.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-deny.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-deny.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-identities.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-identities.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-identities.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-privilege-readback.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-privilege-readback.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-privilege-readback.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-probe-authority.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-probe-authority.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-probe-authority.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-probe-cleanup.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-probe-cleanup.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-probe-cleanup.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-roles.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-roles.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-roles.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-services.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-services.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-services.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-started.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-started.txt deleted file mode 100644 index b286fc75b3e..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-started.txt +++ /dev/null @@ -1 +0,0 @@ -operator bootstrap started; not a convergence receipt diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-workflow-trust.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-workflow-trust.txt deleted file mode 100644 index 480da8f3ea6..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-bootstrap-20260929-services-and-probes-1-workflow-trust.txt +++ /dev/null @@ -1 +0,0 @@ -stage checks completed; see the stage authority for configuration/probe scope diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt deleted file mode 100644 index dc3646c20d9..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-intent.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-3;condition-expression:48:request.time < timestamp('2026-09-29T01:20:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt deleted file mode 100644 index dc3646c20d9..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-3;condition-expression:48:request.time < timestamp('2026-09-29T01:20:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt deleted file mode 100644 index dc3646c20d9..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-3;condition-expression:48:request.time < timestamp('2026-09-29T01:20:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt deleted file mode 100644 index dc3646c20d9..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-converge@gunbai-secrets.iam.gserviceaccount.com-retired.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-converge@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-3;condition-expression:48:request.time < timestamp('2026-09-29T01:20:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt deleted file mode 100644 index 384a398af82..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-intent.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-3;condition-expression:48:request.time < timestamp('2026-09-29T01:20:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt deleted file mode 100644 index 384a398af82..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-closed.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-3;condition-expression:48:request.time < timestamp('2026-09-29T01:20:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt deleted file mode 100644 index 384a398af82..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-publication-elected.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-3;condition-expression:48:request.time < timestamp('2026-09-29T01:20:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt b/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt deleted file mode 100644 index 384a398af82..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/gcp-iam-probe-probe-20260929-3-iam-observe@gunbai-secrets.iam.gserviceaccount.com-retired.txt +++ /dev/null @@ -1 +0,0 @@ -projects/gunbai-secrets/serviceAccounts/iam-observe@gunbai-secrets.iam.gserviceaccount.com;role:57:projects/gunbai-secrets/roles/gunbcIamBootstrapProbeToken;member:18:user:brian@gunb.ai;condition-title:38:gunbc-bootstrap-probe-probe-20260929-3;condition-expression:48:request.time < timestamp('2026-09-29T01:20:00Z');condition-description:83:Temporary exact-account bootstrap readback; physical cleanup required after expiry.; \ No newline at end of file diff --git a/docs/plans/receipts/gcp-iam-services-2026-09-29/receipt.json b/docs/plans/receipts/gcp-iam-services-2026-09-29/receipt.json deleted file mode 100644 index 6e985b5b2a8..00000000000 --- a/docs/plans/receipts/gcp-iam-services-2026-09-29/receipt.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "source_revision": "85bdb4e19", - "binary_sha256": "179882133a4c03958a1bed8a8b444ea06e48c63a6c42263ff83694508825b415", - "focused_controls_passed": 18, - "closure_modules": 844, - "memory_max": "6G", - "memory_swap_max": 0, - "cloud_status": "all ten bootstrap stages completed; exit 0", - "probe_operation": "probe-20260929-3", - "probe_deadline": "2026-09-29T01:20:00Z", - "full_landing_suite": "not run", - "temporary_grants": "probe grants and prior deny grant absent on final independent readback", - "credential_file": "removed", - "newly_enabled_api": "cloudresourcemanager.googleapis.com", - "workflow_acceptance": { - "run_id": 36505895733, - "source_revision": "15b977d417c1a7de52eae96f48bcd3955fb1f94c", - "status": "dispatched; no success claimed" - } -} diff --git a/docs/plans/receipts/google-workspace-admin-api-2026-09-28.md b/docs/plans/receipts/google-workspace-admin-api-2026-09-28.md deleted file mode 100644 index 089aa5a569c..00000000000 --- a/docs/plans/receipts/google-workspace-admin-api-2026-09-28.md +++ /dev/null @@ -1,41 +0,0 @@ -# Workspace administration API qualification — 2026-09-28 - -This is documentation research, not a live organization readback. No credentials were -provisioned and no Google-side setting was changed. - -| Control | Supported observation found | Mutation | Evidence | -|---|---|---|---| -| Auth Platform web client | Console readback | Manual console action | [Create clients](https://support.google.com/cloud/answer/15549257) | -| Internal audience | Console readback | Manual console action | [Configure consent](https://developers.google.com/workspace/guides/configure-oauth-consent) | -| This client's Specific Google data access | Console readback | Manual console action | [Control app access](https://support.google.com/a/answer/7281227) | -| External directory sharing | Cloud Identity Policy API, `directory.external_directory_sharing` | API mutation unsupported; manual console action | [Supported settings](https://docs.cloud.google.com/identity/docs/concepts/supported-policy-api-settings), [admin control](https://knowledge.workspace.google.com/admin/users/let-third-party-apps-access-directory-data) | -| Profile photo editing policy | Console readback | Manual console action | [Profile editing policy](https://knowledge.workspace.google.com/admin/users/allow-directory-users-to-change-their-profile-and-photo) | - -“No supported read found” is a dated qualification of these documented surfaces, not a claim -that Google can never add an API. The Policy API inventory lists several global API-controls -settings, but those are not the specific-client access control in row 3. The Directory API's -user/photo operations are not an administration API for row 5. IAM `oauthClients` applies to -[Workforce Identity Federation](https://docs.cloud.google.com/iam/docs/workforce-oauth-app), -not the Auth Platform client in row 1. - -Row 4 values are `REQUESTER_BASIC_PROFILE_ONLY` and `ORGANIZATION_DIRECTORY_DATA`. -Unspecified/unknown values are not evidence of either. The minimum desired value is the first; -organization discovery requires an explicit rationale. - -A future supported observer uses [policies.list/get](https://docs.cloud.google.com/identity/docs/how-to/list-get-policies) -with the separately approved `cloud-identity.policies.readonly` scope. Complete pagination and -[effective-policy reduction](https://docs.cloud.google.com/identity/docs/concepts/policy-api-concepts) -are required; a single raw policy is not necessarily the customer's effective policy. Unread -OU/group overrides remain a refusal. Honor the documented quota and do not derive identity -from the older SYSTEM-policy name/sort-order convention. - -The implementation's receipts bind customer/project/client/OU, typed setting, source, -evidence reference, observation time and expiry. A receipt producer remains responsible for -authenticating and durably retaining its evidence. The assessor is pure and cannot independently -verify a screenshot or API response referenced by a caller. Fixture receipts are not deployment -receipts. Matching scope/redirect lists are compared as sets, not presentation order. - -Validation: the focused Workspace claim suite uses a byte-identical import closure under the -existing 6 GiB limit. It covers missing observations, mismatch, tenant isolation, reader admission, -expiry/future timestamps, supported API setting identity, unresolved effective coverage and -scope-order equivalence versus extra privilege. See the branch's validation receipt for results. diff --git a/docs/plans/receipts/issue-cut1-command-boundary-2026-09-28.md b/docs/plans/receipts/issue-cut1-command-boundary-2026-09-28.md deleted file mode 100644 index 5fc6c7b9b3e..00000000000 --- a/docs/plans/receipts/issue-cut1-command-boundary-2026-09-28.md +++ /dev/null @@ -1,47 +0,0 @@ -# Issue command boundary — 2026-09-28 - -Base: `e286b29962c3b89904277c64d0e0c7cc25ddb2d0`, isolated branch -`codex/issue-cut1-toposort`. The live checkout and authentication handlers are unchanged. - -The Git carrier now owns an isolated snapshot for each operation and publishes against the -exact fetched remote ref. It checks the supplied issue-parent expectation in that snapshot, -then publishes event and operation receipt in one commit. A remote movement refuses; it never -silently rebases a stale command. This is a Git realization of revision CAS, not a migration -to the held fabric-storage implementation. - -Operation identity binds node, authenticated principal, payload and original precondition. -The committed receipt is checked before the current issue revision, so retry after another -comment returns the original event/ordinal. Clock time is committed result data. Another -operation with identical comment text is an intentional second comment. Native forms have -server-minted keys and explicit empty-revision fields. Enhanced assignment preserves those -keys; an inline lost-response retry retains the original command. - -The shared URL-encoded decoder handles native CRLF, Unicode, plus/percent encoding and -field-looking multiline text without ambiguous newline framing. Duplicate decoded names, -malformed percent escapes and invalid UTF-8 refuse. Missing expected revision is distinct -from explicitly empty expected revision. - -Detail reads refuse unreadable/forked/incomplete issue history and derive standing, assignment -and form revision from one event snapshot. The attempt observation is still not connected: -the page explicitly reports that gap instead of claiming no attempts. Recents are suggestions -and may degrade independently. This cut does not install a durable consumer for Fabric launch -obligations; the existing first-time inline dispatch path remains. Retrying a committed command -does not dispatch another worker. - -## Evidence and limits - -- Actual `.dag` command -> disposable Git remote -> readback: PASS, including same-key retry - after an intervening event, identical-body different command, stale revision and payload reuse. -- Forced concurrent publication: PASS, exactly one event and one operation receipt committed. - Fixture receipt: `/tmp/gunbc-issue-command-yzokh_mx/receipt.json`. -- Native Chromium POST fed into actual `.dag` decoder: PASS. -- Emitted assignment client in Chromium with a lost response: PASS for native-form and inline - retry identity, including preventing a different inline payload from reusing a pending key. -- Focused codec claims: 2 PASS. Focused carrier claims: 3 PASS. -- Workspace administration claims are independent: 5 PASS. -- Full serve witness closure: FAILED (exit 137 at the existing 6 GiB limit after serve typecheck). - This is not a passing integrated floor, merge approval or deployment receipt. - -All DAG executions use `MemoryMax=6G`, `MemorySwapMax=0`. Focused source closures are byte-identical -copies with source hashes, not edited test substitutes. The checked-in wet/browser controls -operate on disposable local fixtures, never production state. No memory ceiling was raised. diff --git a/docs/plans/repo-stability-2026-08.md b/docs/plans/repo-stability-2026-08.md deleted file mode 100644 index 048925cc365..00000000000 --- a/docs/plans/repo-stability-2026-08.md +++ /dev/null @@ -1,306 +0,0 @@ -# Repo stability plan — 2026-08-28 (living document) - -Working plan iterated with the operator. Subject: get the repository back to a -stable place and keep it there. Maintained on branch -`claude/repo-stability-priorities-frtsy4` until adopted. - -## Operator intent (recorded 2026-08-28) - -- CI required checks should take **~minutes at most**, not 40, not 100. -- The desired mechanism is **minimal invocation** — affected-set / build-system-style - caching of test results between runs ("only run what changed"). -- **Witness rule:** every witness is a short, one-pass transformation contract. - Target budget per witness: **<5ms, eventually <1ms guaranteed**. Long - corpus-walk / integration / performance witnesses are POLICY VIOLATIONS — - the current 44 BUDGET-REFUSED rows are considered erroring/failing now, - not "budget too small". -- The pattern to break: things reach "good enough" (e.g. 40-min CI) then spiral - (100 min) because nothing prices the derivative. v1 / reconcile / CI root work - keeps being avoided; it is admitted under the 2026-08-20 purpose ruling (in - support of v2 self-host; fixing issues is fine). - -## Measured state (2026-08-28, main @ e910a39) - -- `witnesses` required check: **red on every recent main run** with an - IDENTICAL set of 47 unexpected witness failures across runs (zero flake). - Latest measured: `planned=12949 executed=12949 passed=12615 known_red_held=33 - failed=47 stale_quarantine=1 interrupted_before_verdict=44 - completed_over_cost_requirement=2` (run 33145062452). -- Wall: ~85–100 min/run (was ~30 min on 2026-08-22 at routed=10439). - `compile.reconcile` in strict-preparation: 22 min over 4187 modules - (15 min over 3892 on 2026-08-24 → superlinear; ~92% attributed to - `typecheck_with_census_extra`). -- Roster growth: routed 10439 → 12949 in 6 days (+24%). -- Queue: 26 main-push runs queued at once (main pushes never coalesce); - operator: not a priority right now. -- srv1 belt/daily workspace: DOWN since 2026-08-20. `/opt/gunbc/gunbc` is - ~664 commits behind the admitted fleet revision; belt ticks exit 0 and - refuse to spawn (diagnosed in #9559). Convergence chain landed - (#9506 → #9555 → #9563 → #9576) but `fleet-converge.yml` is - workflow_dispatch-only and the apply has not been run. - -## Priorities - -### P1 — CI to ~minutes: minimal invocation + short witnesses - -The two whole-corpus terms (fold ~30 min, reconcile 22 min) reach minutes only -by not redoing unchanged work, never by optimization. Plan: - -1. **Hoist corpus walks out of witnesses into run-once indexes** - (`declaration_index` pattern: the required parse sweep derives the facts - once; witnesses read the index in ms). Dominant violator families among - the 44 BUDGET-REFUSED rows: `grammar_coverage_*` (65–69s), - `enforcement_live_*` (52–60s), `cost_coverage_*` (54–62s), - `vocabulary_containment` (~30s), plus assorted 5s rows. - This fixes ~40 of 44 policy violations AND removes minutes of cold cost. -2. **Closure-digest verdict store (the affected-set mechanism, done the - cache way).** Key: (witness identity, closure content digest, binary - identity). Hit ⇒ stored verdict replayed as this run's verdict; miss ⇒ - execute, fail-closed. No git-diff observation exists in the mechanism — - this is identity-derived, not diff-derived, which is what distinguishes - it from the deleted affected-set machinery and its absorbing failure - arms. Hosted in the v1 seed now (admitted: it gates all v2 work); the - durable artifacts (digest definition via `std.content_hash`, store - schema) survive the v2 cutover. Same treatment per-module for reconcile. -3. **Cold backstop cadence:** scheduled whole-corpus uncached run; - cached-vs-cold divergence is a typed line-stop (the doc's own purity - oracle). The cache is never trusted, only checked. -4. **Witness budget wall:** once violators are hoisted, enforce the short- - witness rule as a hard refusal (existing budget machinery already - refuses; today its refusals are ignored). Ratchet the ceiling down - toward the 5ms/1ms target as the population complies. -5. **Derivative gate:** required run holds its own cost against a declared - policy budget (a §5-legal oracle) so regrowth stops the line the day it - lands, not six weeks later. -6. stage0 cargo build: sccache / runner image — ordinary work, separate. - -First artifact (measurement, before building the store): compute closure -digests over the roster on the current tree, replay the last ~10 main -commits, report would-have-been hit rate + residual per-push cost. - -### P2 — Main back to green: the 47 standing reds - -Identical across runs; grouped by module (run 33145062452): -doc_reachability (x6, v1+v2 copies), sole_constructor audit probes (x6), -lens_module_gate (x3), quarantine_probe_disposition (x3), ci_budget_tree -(x3), cost_coverage (x3), compiler_closure ingest/emit (x4), guarantee -probes (x5), and singletons. Plus 1 STALE-QUARANTINE -(`duplicate_definition_binding_probe_test` passes; remove from -`v2.workflow.floor_expected_red`). Triage by shared root, not row-by-row; several sit in P1.1's corpus-scan -families and get cheap after the hoist. Dispositions must be honest: fix, or -expected-red with typed reason + trigger — never quarantine-to-green. - -### P3 — srv1 / roadmap daily workspace - -Run the fleet-converge plan → apply with the landed convergence chain; -verify by the belt's own evidence (next tick spawns; dispatch_preflight -refused_axis_count 5 → 0; fresh provider-events capture). Then consider a -scheduled convergence so a 664-commit drift can never silently accumulate again -(it was invisible for 8+ days because the belt exits 0). - -### P4 — Repo cleanup pass ("pristine") - -Top-level census (2026-08-28) and dispositions to decide per item: - -| Path | Size | Standing | Proposed disposition | -|---|---|---|---| -| `dag/` (2962 .dag) | 40M | live corpus | keep; audit `dag/examples/` (6 demo dirs — likely stale/scaffold) and `dag/config/` (1 file) | -| `src/v1` | 17M | frozen seed + 13M `stage0/` mirror | keep (seed); no growth | -| `src/v2` | 14M (1346 .dag) | active | keep; `experimental/` dir needs a disposition | -| `docs/plans/` | **20M, 250 files** | mostly stale/pre-cut | classify each: live / completed / superseded / bankrupt; delete-first for the dead (bulk of the 20M) | -| `dag/gunbc/plans/` | 66 files | dual home with docs/plans | decide the ONE home for plans; migrate or delete | -| `docs/briefs/` | 8 files, all 2026-08-24 | one-day lane briefs | likely bankrupt with the measurement corpus; delete or fold into carriers | -| `docs/probes/` | 1 file | recreated after the 2026-08-24 bankruptcy deleted the dir whole | violates the bankruptcy ruling's spirit; model or delete | -| `docs/runbooks/` | 5 files | hand-authored ops docs | §6 out-of-band-actuation tells; keep only with dissolution obligations | -| `docs/extdeps/lotes_azifa072/` | 428K | vendor pad-array data (CSV/SVG) | verify cited by a `.dag` authority; else relocate/delete | -| `tools/*.txt,*.tsv` (5 files, ~1.1M) | receipts/manifests at repo root of `tools/` | transcribed measurement output — the class the bankruptcy deleted; delete or model (`m1c_bulk0_residual.tsv` modified TODAY — find the writer first) | -| `fixtures/`, `test/fixture_roots` | 196K/20K | fixture carriers | keep; confirm all reachable from witnesses | -| `artifacts/bmc/` | 24K | committed artifacts? | verify generated-artifact registry covers or delete | -| `provider-runtime/codex/` | 24K | ? | classify: live realization or residue | -| `DESIGN.md` / `ROADMAP.md` | 160K/58K | generated projections | verified by `generated-artifact` phase; keep | - -Rule for the pass: every deletion goes through the delete-first census -(what refuses tells us what was load-bearing); anything kept must name its -consumer; transcribed measurement outputs are debt regardless of accuracy. - -### P5 — Plans content refresh - -After P4 shrinks the population: rewrite the surviving live plans (floor-cut, -namespace-cut, guarantee-recovery gap analysis, replacement-migration doctrine) -against the current tree, so plan prose stops seeding sessions with dead premises. - -## Open operator decisions - -1. Ruling wanted: identity-derived verdict caching (P1.2) is NOT the - deleted diff-derived affected-set machinery — confirm it may be built, - with the cold cadence as its honesty backstop. -2. Where do plans live: `docs/plans/` vs `dag/gunbc/plans/` (one home). -3. P3 apply touches a live host — dispatch when ready. -4. Main-push queue coalescing: deferred by operator (not a priority). - -## Log - -- 2026-08-28: created; measured state recorded; priorities agreed in session. -- 2026-08-28: P4 first cut — deleted 21 orphan docs (15 docs/plans, 5 docs/briefs, - 1 more) after a full reference census. Remaining candidate classes recorded in - session discussion: 27 docs referenced only by other docs (islands to cut whole), - ~200 docs/plans referenced from dag/src carriers, tools/ receipts (consumer: - srv1_residue_rehearsal path strings only), docs/probes single file (referenced by - lotes_azifa072.dag), runbooks (referenced by roadmap/actuate carriers). -- 2026-08-28: P4 second cut — deleted tools/ receipt dumps (5 files, operator: - derived data is never committed; only .dag derivation code), 33 more stale - off-topic plan docs (closure-safe: refs only from snapshot strings/each other), - and src/v2/experimental/ (empty quarantine, failed concept per operator). - DEFERRED: 112 old off-topic plans cited by live carriers (roadmap_authority - ~25, dag plan carriers, cli_run.rs, lens modules) — need carrier-row edits; - dag/examples/ (witness-consumed: declared_type_inhabitance, cost_estimate_float, - bootstrap_witness.rs); artifacts/bmc (bmc_fan_program carriers consume); - provider-runtime/codex (codex witnesses consume). -- 2026-08-28: P4 third cut — deleted docs/plans/receipts (13M, 73 files) and - docs/plans/measurements (456K) whole: persisted measurement dumps, operator-ruled - garbage (derived data is never committed; only the derivation code is). Every - in-tree mention verified to be prose (comments, note strings, srv1 snapshot rows, - a roadmap handback sentence) — no executing consumer reads any of these paths. - Those prose pointers now reference history, same status as any receipt citation. -- 2026-08-28: P4 fourth cut — deleted the 112 deferred stale plan docs; flipped - 48 plan carriers to PlanIsAuthorityOnly (registry row is what made each deleted - .md expected on disk — the a295e17415 precedent); stripped 45 comment-channel - pointers to deleted docs (13 files). DEFERRED to P5: string-field citations in - roadmap_authority.dag (~46 lines) and stale plan-carrier bodies — editing those - requires regenerating ROADMAP.md alongside. FOUND & FIXED IN PASSING: main @ - e910a39 (#9612) does not build — duplicate shared-artifact wall-fill definitions - re-added over #9609 (E0428 x3, cli_run.rs); deduped on this branch; main needs - the same fix (its CI runs were still queued when found). Parse sweep advisory - count unchanged vs main (164). NEXT CANDIDATE ROUND: delete the 48 stale - authority-only plan carriers themselves (imports/rosters/doc_graph edits), and - the docs/plans doc-to-doc islands. - -## Finding — per-invocation whole-tree pool tax (2026-08-28, CORRECTED same day) - -AN EARLIER REVISION OF THIS SECTION CLAIMED A CWD-GATED SWITCH (identical trees -<0.5s outside a checkout) AND IS REPLACED WHOLE: those fast controls were -PANICS — `repo_relative_path_normalized` aborts on roots outside the process -workspace root, and the discarded stderr was misread as fast success — the -fabricated-fast twin of execution-provenance loss. `git rev-parse` is workspace -discovery only, not a heavy-work toggle. - -ESTABLISHED by succeeding runs (entry compiled, NoSuchFunction reached) over -subset roots inside the workspace, entry `std/abi.dag` (imports nothing): -140 modules 3.5s · 1637 39s · 2962 (dag) 63s · 4308 (dag+src/v2) 81–99s. -LINEAR at ~20–25ms per POOL module, paid per process invocation, independent of -the entry (widest closure 110s vs smallest 81s on the same roots). - -MECHANISM (gdb stacks + code read; all hand-Rust in src/v1/stage0/src/cli_run.rs): -`resolve_entry_graph` routes every run through the process shared index -(`try_process_shared_index_for_pool`); building it (`new_multi_entry_index_shell`) -EAGERLY constructs `build_module_graph_facts_live` — a tolerant parse of EVERY -pool module (`reference_resolution_facts`, `module_declaration_facts`, import + -strict-reference adjacency at two tiers) — plus `build_module_path_index`, -another whole-pool parse. Hot leaves: `v1_std_core::build_newline_index` -(im::Vector push_back per element — plain-Vec shape) and `v1_std_core::intern` -String clones in `pre_intern_tokens`. - -WHY THE FACTS EXIST (per the in-code comment block): loader-tier adjacency and -selection-tier adjacency (affected-set selection). A single-entry run needs at -most one entry's loader closure but pays both tiers over the whole pool. The -facts ARE memoized (thread-local MODULE_GRAPH_FACTS_CACHE / PROCESS_RESOLVE_INDEX), -so one process pays once — but every PROCESS pays cold, and CI phases, -emit-compile entries, and belt ticks are one process per invocation. - -RELATION TO .dag: acknowledged hand-Rust area — 🟡 dissolve-on markers cite the -cli-run hollowing plan (`dag/gunbc/plans/cli_run_hollowing_plan.dag`, -`gunbc.cli_run_hand_rust_area_ledger`); `import_closure_live_paths` labels -itself "Host realization of v2.lens.module_graph.import_closure_live"; path -admission modeled by `gunbc.cli_run_repo_grant`. The MODEL is .dag; the -execution that costs the time is hand-Rust. - -NOT ESTABLISHED: the floor's 22-min `compile.reconcile` -(`typecheck_with_census_extra`, ~300ms/module) is a separate larger cost, not -attributed here. REPRO: in-workspace subset roots (untracked dirs), success -judged by the NoSuchFunction cause line, never by wall time with output -discarded; gdb -p -batch -ex "thread apply all bt" during the slow window. - -## cli_run.rs decomposition (started 2026-08-28, branch claude/repo-stability-priorities-frtsy4) - -50,536 → 46,443 lines; 13 clusters moved to src/v1/stage0/src/cli_run/ submodules -(pure code motion, build-verified, smoke-tested): test_migration, compile_clean, -non_fold_residue, class_b_census, languages_census, external_authority, -inert_carrier, witness_gates, emit_host, complexity_gates, doc_graph, -declared_refs, census_heads. Mechanics: submodule carries `use super::*` + the -parent's use-header; parent re-exports `pub(crate) use ::*` (paths stable); -bin-consumed fns get explicit `pub use`; private moved items widen to pub(crate); -eight report/plan types widened for the private-interface lint (CI: -D warnings). -lib.rs is generated and untouched — submodule declarations live in cli_run.rs, -matching the existing pattern (declaration_index, phase_profile, ...). - -DELIBERATELY NOT MOVED YET, to avoid conflicting with in-flight operator work: -the resolver/index core (resolve_entry_*, build_module_*, import_closure_*, -whole_tree_*, the shared-index thread_locals — the pool-tax subject) and the -floor runner (floor_*, run_required_*, budget/accounting). Move each in a quiet -window; the extraction recipe is in the split commits' messages. -- 2026-08-28 (later): the two deferred cores are moved with operator approval — - entry_resolve (75 items, ~2.5k lines: shared index, resolve store, module - graph facts, path index, typed-module cache) and required_floor_runner - (49 items, ~4.8k lines: run_required_floor, claim eval/measurement, - discovery, diff observation, resource sampling). cli_run.rs 50,536 → 38,636 - lines, 26 submodules. Remainder: ~450 fns of mixed verbs/helpers plus ~350 - embedded test fns; further carving is ordinary follow-up, no core left. - -## The dependency map (2026-08-29, standing — replaces re-deriving this in chat) - -The interpreter has four roles: R1 witness executor · R2 orchestration executor -· R3 v2's executor (v2 stages are .dag, so v2 compiles by being interpreted) · -R4 the host-effect seam (builtin handlers). Emitting .dag→Rust does NOT need -the interpreter (the v1 passes are native); interpretation is evaluation without -emission. Three separable decisions: - -1. REALIZATION STORE (backport of v2.std.materialize's model into a v1-host - persistent content-keyed store; digest authority already shared via - std.content_hash/fnv1a64). No dependency on the interpreter either way — - the interpreter becomes a CLIENT. Gives fast interpreted execution, CI in - minutes, fast v2 compile loop. v2 SELF-HOST DEPENDS ON THIS AND NOT ON - DECISIONS 2-3. -2. NATIVE PRODUCERS PER FAMILY (emit witnesses etc. into content-addressed - native bundles; store's producer flips per family; interpreter drains). - Optional per family; needs emitter perf + diagnostics work. -3. DELETE THE EVAL LOOP (only after 2 completes everywhere; R4 survives as the - runtime library with a new caller; may never be worth taking to zero — a - bounded evaluator for bootstrap/dev is near-free). - -digest authority ──► store ──► fast interpreted execution ◄── v2 self-host - ├──► native producers per family - └──────────┴──► interpreter drains ──► (optional) delete - eval loop; keep effect runtime - -## The one execution test (anti-planning: build this, then judge the plan) - -SUBJECT: persist the process-shared index's derived facts across processes, -keyed on content digest. Concretely: module path index + module graph facts -(and optionally the typed-module cache) in cli_run/entry_resolve.rs get a -disk-backed store under target/ keyed on (source file digests, binary identity). -ACCEPTANCE, binary and pre-registered: on an unchanged tree, the SECOND -`gunbc run --source-root dag --source-root src/v2 --entry dag/std/abi.dag` -completes in <10s (cold today: ~81–99s), AND the warm facts are byte-identical -to cold-derived facts (the §5 purity oracle, asserted by execution). A miss -(any file changed) re-derives fail-closed. If the warm run is not <10s or the -oracle disagrees, the store thesis as specified is refuted and the plan is -rewritten before anything else is built on it. -WHY THIS ONE: smallest end-to-end existence proof of the store (one producer, -one consumer, one oracle); kills the measured entry-independent pool tax; the -same mechanism CI-minutes and the v2 loop need; ~a day; pass/fail visible in -one command run twice. -- 2026-08-29: REGRESSION PINNED BY BISECT — main's converge actuator dies with - `trim expects a string argument, got Null`. First bad commit: 655f82a74 - (#9344, "Carry occurrence identity on every semantic v1 Node"). Mechanics at - the crash site (dag/gunbc/repo/repo_local_git_config.dag observe_binding_at_repo): - the effect result of git.Core.ConfigLocalGetInRepo reads `.success` fine - (the branch is taken) but `.value` comes back Null — a record-field read on - an effect result resolving to Null post-#9344, so the suspect is field - lookup / effect-result decode interacting with the new occurrence identity. - Repro: build any commit >= 655f82a74, run - `gunbc run --source-root dag --source-root src/v2 --entry - dag/gunbc/repo/repo_local_git_config.dag --function converge`. - Likely blast radius: any interpreted entry reading effect-result fields - (belt, deploys, gates). Not fixed here — #9344 is the operator's own thread. - Also fixed forward on this branch: #9656 omitted TypeEnv.unit_variant_index - in six bin/infer_semantics_witness.rs literals (main's full build is red). diff --git a/docs/plans/required-floor-in-flight-shared-fill-design.md b/docs/plans/required-floor-in-flight-shared-fill-design.md deleted file mode 100644 index b348bc8b631..00000000000 --- a/docs/plans/required-floor-in-flight-shared-fill-design.md +++ /dev/null @@ -1,35 +0,0 @@ -# Required-floor in-flight shared-fill refusal design - -Status: implementation design, following -`required-floor-in-flight-shared-fill-findings.md`. - -## Decision - -Keep the required floor's existing 500ms attempt-safety ceiling. When its CPU poll fires during -an admitted cross-claim fill, classify the interruption as `FillBudgetExceeded` only when the -claim's marginal CPU without that still-active fill remains within the ceiling. The refusal -carries the claim entry, prospective producer, fill CPU lower bound, marginal CPU lower bound, -and unchanged limit. - -This is not pre-publication fill credit. The clock still charges all uncommitted work and stops at -the same point. The change repairs the subject of the refusal: first-touch order no longer appears -as intrinsic claim cost when the active prospective fill is what crossed the bound. - -## Boundary and nesting - -`CrossClaimFillGuard` remains the one lifetime bracket. Its stack frame additionally retains the -producer and CPU start. At a poll, the outermost active frame identifies the prospective artifact; -its CPU is inclusive elapsed CPU less already-stored descendants. Those descendants remain netted -by the existing committed-fill accumulator, exactly as before. - -If marginal CPU alone already exceeds the ceiling, the ordinary evaluation-budget refusal wins. -If no admitted fill is active, behavior is unchanged. Store, abandon, fill receipts, completion -accounting, wall accounting, memo admission, and memo serving are unchanged. - -## Safety and evidence - -The refusal does not make an over-budget fill pass, raise a ceiling, retry a row, or create an -artifact receipt. Its stable cause token makes the population countable. A real evaluator-path -test must force the poll while an admitted fill is active and assert the producer is carried; a -matched unadmitted control must retain the ordinary evaluation-budget refusal. This instrument -can provide evidence for a separately modeled fill envelope later, but creates no such policy. diff --git a/docs/plans/restore-src-v1-required-floor-stall-finding.md b/docs/plans/restore-src-v1-required-floor-stall-finding.md deleted file mode 100644 index cec22b631da..00000000000 --- a/docs/plans/restore-src-v1-required-floor-stall-finding.md +++ /dev/null @@ -1,40 +0,0 @@ -# RESTORE-stall finding: widening the required floor's source roots to admit `src/v1` - -Standalone finding, split out of triage prose per parent-session request (dashboard node `adhoc-9b80ec49-d63`, session `zesty-newt-828`, 2026-08-19). It is the receipt for the RESTORE disposition ruled out repo-wide across `v1_dead_witness_tree_triage_receipt`, `v1_dead_witness_tree_triage_receipt_remainder`, and `v1_dead_witness_tree_triage_receipt_emitter_ambiguous_variant_owner` (`dag/gunbc/ci/ci_layer_roots.dag`) — those receipts cite this finding rather than re-deriving it. - -## The question - -Could the dead-witness-tree population under `src/v1/tests/claim` be restored to execution by the cheap route — adding `--source-root src/v1` to the required floor's invocation (`claim_executor --required-floor --source-root dag --source-root src/v2`) — instead of migrating or retiring each file individually? - -## Local measurement (this session, prior window) - -- **Invocation:** `claim_executor --required-floor --source-root dag --source-root src/v2 --source-root src/v1` (the one-token addition of `--source-root src/v1` to the standing invocation). -- **Head SHA at time of run:** not recorded in this session's surviving notes; the run was taken against this branch (`session/zesty-newt-828`) during the window that produced the 17-file triage, before either the emitter_ambiguous_variant_owner receipt or the remainder receipt existed. This is a named provenance gap. The qualitative result (stall, not slow-but-progressing) is not sensitive to the exact commit, since strict-preparation's cost is dominated by corpus size and import-graph shape, not by this triage's one-line prose edits — but no exact SHA is citable. -- **Local vs. remote:** run locally in this session's container (not via `ctrl-build --remote`), predating the guidance that heavy whole-tree operations prefer remote dispatch. It is a single-shot diagnostic, not a build, and its result (stall before any phase progress) makes host contention an unlikely confound: a resource-starved run would still log *some* forward progress at a slower rate, not zero. -- **Contention caveat:** the container's RAM cap is shared across `sessions.slice` with other sessions on the same host; a co-located heavy build could inflate wall-clock or add memory pressure. This cannot be fully ruled out for this run. It is addressed, not eliminated, by the corroboration below, measured on dedicated CI hardware with no co-tenant. -- **Result:** the process stalled 8+ minutes inside `strict-preparation` (the whole-corpus typecheck phase preceding any witness execution), RSS plateaued at approximately 7.2GB with no further growth, and no phase-progress log line advanced past the point already reached without `src/v1`. The process was killed: a plateaued RSS with no progress is the signature of a stall (thrashing, or an unreachable fixed-point), not of a slow-but-advancing computation. - -## Independent corroboration (relayed by parent session smart-ram-730, CI run 32196317824) - -A CI run on a dedicated `srv4` runner (no co-tenant contention) measured the **ordinary floor, without `src/v1` added at all** — the ceiling this triage's population currently sits below, not the widened case: - -- `rss_kb=7078696` (~7.0GB) -- `wall_s=540` (9 minutes) in `strict-preparation`, logged as `compile.reconcile done in 9 minutes` -- heavy paging during the run: `pswpin=231511094`, `pgmajfault=169789103` - -This is the cost of `strict-preparation` **today, over `dag` + `src/v2` alone** — the baseline the required floor already pays before any witness runs. Both observations land independently in the same ~7GB / high-single-digit-minutes regime on the same phase, so the local stall was not primarily an artifact of `src/v1`'s weight pushing an otherwise-cheap phase over a threshold: `strict-preparation` is already expensive at the two-root baseline. Adding `src/v1` (52 non-test `.dag` modules plus the 15-17 test files, per `witness_fold_src_v1_coverage_gap_note`'s count) pushes an already near-ceiling phase further. - -**Second data point (relayed by parent session smart-ram-730, CI run 32204338777, dedicated runner, same unwidened floor):** `rss_kb` peaked at 9,816,820 (~9.8GB) in the same `strict-preparation` phase this triage's own PR (#8486) exercised — against the ~7.0GB from run 32196317824. Both runs measure the identical unwidened invocation (`dag` + `src/v2`, no `src/v1`) on dedicated hardware, so the ~2.8GB spread is not host contention or a `src/v1` effect — it is `strict-preparation`'s own run-to-run variance at the two-root baseline. The honest ceiling claim is therefore a **range** (~7.0–9.8GB observed so far), not a single figure; the headroom between that range and this host class's actual OOM/thrash threshold is narrower than either measurement alone suggests, which strengthens the conclusion that widening to `src/v1` is not the cheap route. - -## Stall vs. slow classification - -Both signatures point to **stall**, not **slow**: - -1. The local run showed RSS plateau with zero phase-progress advancement over 8+ minutes — a computation still progressing would show continuing RSS growth or advancing log lines, not both flat. -2. The CI corroboration establishes that even the *unwidened* floor is already within single-digit minutes and ~7GB of whatever ceiling this host class enforces; paging activity (`pswpin`/`pgmajfault` in the hundreds of millions) is itself evidence of memory pressure at the baseline, before `src/v1`'s modules are added. - -Together, `--source-root src/v1` does not merely make `strict-preparation` *slower*; it very plausibly pushes an already-memory-pressured phase past a practical ceiling into OOM or genuine thrashing — the local kill preempted observing which. Re-running to distinguish "would eventually finish, slowly" from "would OOM or thrash indefinitely" was not attempted, since the practical consequence (RESTORE is not the cheap route the one-token diff suggested) is decided either way. - -## Conclusion - -RESTORE is ruled out for the whole `src/v1/tests/claim` population, repo-wide, on this evidence: a local stall (contention-caveated, but with a stall rather than slow-progress signature) independently corroborated by dedicated-hardware CI numbers showing the *unwidened* floor already runs close to whatever ceiling caused the local stall. This finding is the citation target for the RESTORE disposition in all three `v1_dead_witness_tree_triage_receipt*` rows; MIGRATE and RETIRE are the two remaining dispositions, applied per-file in those receipts. diff --git a/docs/plans/review-instrument-observations.md b/docs/plans/review-instrument-observations.md deleted file mode 100644 index b71839e3ced..00000000000 --- a/docs/plans/review-instrument-observations.md +++ /dev/null @@ -1,87 +0,0 @@ -# Review-instrument observations (not a census of this repository) - -Two findings about the **review and merge-readiness instrument**, recorded because they were each -found by accident while doing other work and are the kind of thing that is otherwise rediscovered -expensively. They are facts about the tooling, not about the seed or the `.dag` corpus, which is why -they are here rather than in [seed-string-decode-census.md](seed-string-decode-census.md). - -Both were observed on `gunb-ai/gunbc#10180` and independently reproduced by a second session. - -## 1. `stale_provider_count` does not fire, even where both operands are local - -A single `dashboard-ops reviews 10180` payload carried **three different shas at once**: - -| field | value | -|---|---| -| `reviews[0].sha` (the approving review) | `60aefd9d3acb684e` | -| `merge_criteria.head_sha` (the dashboard's own head) | `aff564ca8a7d2fc7` | -| `gh pr view --json headRefOid` (actual) | `2c9c08a439b5b0c2` | - -and, in that same object: `stale_provider_count: 0`, `stale_providers: []`, `approvals: 1`, -`meets_approval_rule: true`. - -**What is observed, stated before what explains it.** The approval sha differed from the dashboard's -own `head_sha`, both fields present in one object, and `stale_provider_count` still read 0. A later -payload on the same PR DID report `stale_provider_count: 1`, so the field is not simply inert. - -**Correction to an earlier reading.** This document first concluded "the comparison does not fire even -when it has everything it needs". Two observations now constrain it better, and the second refutes -that sentence as written: - -| observation | `head_sha` | provider's latest review sha | `stale_provider_count` | -|---|---|---|---| -| A | `aff564ca8a7` | claude @ `60aefd9d3ac` | **0** | -| B | `dee613521e4` | claude @ `dee613521e4`, codex @ `c29a33ce28e` | **1** (codex) | - -In B the field fires correctly. **Hypothesis that fits both, and it is a hypothesis, not a -measurement:** staleness is evaluated when a review is INGESTED, against the head known at that -moment, and stored — while `head_sha` is read live at query time. Under that reading A is a stored -verdict that was true when written and went false underneath, not a comparison that failed to run. -Distinguishing it would take a payload sampled at a known ingest boundary, which has not been done. - -**The operative rule is unchanged either way**, which is why the correction does not disturb it: a -stored-and-gone-stale verdict and a non-firing comparison are indistinguishable to a reader, and both -report 0 on an approval that is not on the current head. - -**Consequence for any merge decision.** Comparing the dashboard's `head_sha` to `gh`'s `headRefOid` is -NECESSARY BUT NOT SUFFICIENT — it catches the lag case and would have passed observation A the moment -the dashboard caught up, with an approval three heads old. The sufficient check is computed by the -reader: - -``` -gh pr view --repo --json headRefOid --jq .headRefOid -dashboard-ops reviews # compare each reviews[].sha to that value yourself -``` - -`stale_provider_count` is not evidence of anything on its own — it is right in B and wrong in A, and -nothing in the payload distinguishes the two cases for you. - -**The same applies to `request_changes_count` reaching 0.** On this PR a codex REQUEST_CHANGES on -`c29a33ce28e` disappeared from the counter after the next push. The findings WERE addressed in that -push — but the counter would read 0 either way, because a REQUEST_CHANGES is superseded by any push -regardless of whether anything was fixed. The commit and the reply are the evidence that the findings -were addressed; the zero is not. - -**A second, softer form.** An approval can also be superseded in PREMISE rather than in sha. #10180's -approving review reasoned explicitly from *"census-only … no code, no `.dag`, no seed changes"*; a -later commit added seed code. Even had the shas matched, the verdict's stated grounds no longer -described the diff. Read what a review says it approved, not only what it approved. - -## 2. A refused review burns its sha slot, invisibly to the counters - -In the same payload, review `59066` carried `status: "failed"` with: - -``` -worktree freshness check failed: HEAD is 95ef0941… but PR #10180 head is 44efc6eb… -— refusing to review a stale/wrong checkout -``` - -That refusal is correct behaviour: reviewing a checkout that is not the PR head would produce a verdict -about a different object. But the slot is consumed — `44efc6eb` is never reviewed, and no retry occurs. -A PR can therefore accumulate refused reviews that leave no trace in `approvals`, -`request_changes`, or `stale_provider_count`; only the `reviews[].status` / `error` fields carry it. - -**The pairing is the finding.** One half of this system refuses to review unless its checkout matches -the PR head EXACTLY; the other half reports staleness as 0 across a three-sha spread. One half is -strict about precisely the question the other half does not ask. Classify a review by its `status` and -`error` fields before reading any counter. diff --git a/docs/plans/review-sheet-and-census-lane-handoff.md b/docs/plans/review-sheet-and-census-lane-handoff.md deleted file mode 100644 index 8f346f3624e..00000000000 --- a/docs/plans/review-sheet-and-census-lane-handoff.md +++ /dev/null @@ -1,252 +0,0 @@ -# Review-sheet and census lane — state at wind-down - -Written 2026-09-20 by zesty-crane-846 on an operator instruction to wind down and record -remaining items, so the system can prioritise v1 performance and v2 migration. Nothing here is -in progress. - -**Read the census section before acting on any number in this lane's output.** The prospect rows -that exist are historical prototype output and are NOT authority for runner occupancy, spend, or -opportunity. That is the single most important thing this page carries. - -## What landed - -| PR | what | -|---|---| -| #11552 | the App-manifest acquisition MODEL and its route — **the flow is not executable end to end**, see below | -| #11581 | three format-converge defects a live spreadsheet found that no witness could | -| #11610 | the actuator consumes the plan's custody name instead of re-deriving it | -| #11564 | GitHub observation record and bounded scan producer — incomplete; completed by #11656 | -| #11669 | `gunbc.review_sheet_identity` — the review-sheet identity semantic kernel | -| #11656 | the three repairs #11564 merged without, plus a sealed scan outcome and an admitted page size | - -**No external prerequisite of this lane is still open.** #11552, #11564, #11656, #11669, #11671, -#11677 and #11679 are all merged. Earlier revisions of this page described #11656 as open and the -census token as queued behind #11671 → #11677 → #11679; that is stale and the blockers are gone. - -### #11552 acquisition is modeled, not achieved - -`gunbc.github_app_acquisition` instructed the operator to "paste the manifest below into the -form's manifest field, and submit". **That instruction was not executable.** GitHub's ordinary -App-creation page carries no manifest field; the manifest protocol requires a form POST carrying -a `manifest` parameter, and a GET renders the blank create page. This was observed live: the -operator followed the step and reported a form with no such field. - -**The instruction is now an artifact.** `census_app_registration_instruction` renders an HTML -document whose form POSTs to the registration endpoint, carries the declared manifest in a hidden -field under the parameter name upstream reads it from -(`extdeps.github.app app_manifest_form_parameter_name`), and carries the run's state nonce in its -action URL; `census_app_registration_instruction_receipt` writes that document to -`.gunbc/github-app--register.html` and refuses to print the instruction if the write failed. -The human act left is pressing its button under an owner session, which is the consent GitHub -requires and exposes no API for. - -**Four facts, kept apart.** REGISTRATION, INSTALLATION, CALLBACK CAPTURE and KEY CUSTODY are -separately established and joined by `census_app_acquisition_standing`, which refuses at the -earliest missing fact and names it. No later fact mints an earlier one: an installation reading -and a private key on disk do not establish a registration, and an installation naming another -App's id does not install this one. `census_app_acquisition_receipt` is the executing route — -three independent readings (the public app record, `GET /app/installations`, the handoff file), -exit zero only when all three hold. #11677's JWS/token route still consumes an App and an -installation that have already been admitted; it creates neither. - -**The two browser acts are owed, and they are NOT sufficient on their own.** No receipt in this -corpus establishes registration, installation or custody for the census App today, and the -acquisition receipt says so in those words rather than reading as done. But an earlier wording of -this paragraph said the browser acts were all that remained "in the model", and the diff it -described contradicts it: `live_installation_population` returns `PopulationUnreadable` on *every* -arm, including a successful decode, because this repository's REST transport models request headers -only and GitHub states whether another page exists solely in the RFC 8288 `Link` header. A short or -empty page is not closure. So after an operator submits the manifest, grants consent and places the -pem, `census_app_acquisition_receipt` still exits non-zero with `InstallationReadingUnavailable`. - -**What is also owed is that transport capability**, and it is declared rather than described: -`census_app_installation_population_frontier_rows` names it — a REST operation result carrying the -response's `Link` header value, sufficient for admitting an installation over a population the -authority itself closed, and explicitly NOT satisfied by a larger `per_page`, by stopping at a short -or empty page, or by routing the read through a CLI whose credential a PAT could satisfy. That -refusal is the honest state and should not be relaxed to make the runbook end green; the operator -should know before they start that the browser acts leave the receipt refusing for a reason that is -not theirs to fix. - -**Custody's second hop is pending a sibling lane.** The declared destination for the App private -key is `census_app_private_key_custody` in Secret Manager. This process cannot write there, so -`CustodyStanding` has exactly one established arm — `CustodyAtHandoffOnly` — and no constructor -for "the key is at its destination". The key belongs as a ROW in the closed custody roster the -`ntfy-publisher-token-onto-gcp-custody` node consolidates; minting a second delivery path here to -close the fact sooner would fork that authority. - -**Authorization pattern.** The registration effect is rostered in -`gunbc.auth.privileged_effect_census` and the selection lands on `HumanOnlyStep`: the surface is -human-only (no API creates an App), so every API pattern fails the surface gate, and the effect is -irreversible (the private key is returned once, and the App name is spent) so a witness is -required — which the human step is. - -## The one fact everything else rests on - -An empty `appProperties` search establishes **"this OAuth client received no matching rows"**. It -never establishes **"no legacy-marked file exists"**. `appProperties` are private to the -*requesting application* — the OAuth client id the token was obtained with — and nothing in this -corpus observes the application identity of its own token. A service-account principal is a -different subject, so comparing service-account emails does not establish visibility. - -That distinction is the whole content of the identity kernel, and it is why the kernel has no -"compatible empty search" arm at all. - -## Remaining: the review-sheet migration cuts - -The kernel landed in #11669. It does **not** repair the production converge; it provides the -authority a future repair is written against. The remaining cuts were designed but not built, and -they replace the approach in the closed #11596 rather than salvaging it — that PR decided the -meaning of each reading inside effectful routing, which is why it took ten review rounds. - -- **Cut 2 (A)** — LANDED. The shared marker-query constructor is - `gunbc.review_sheet_drive_converge` `review_sheet_marker_query`, one shape over a field - parameter; the Drive superseded-marker producer and the subject-bound operator-declaration - reader are `gunbc.review_sheet_legacy_extent_producer`; the decision over a standing is - `gunbc.review_sheet_identity_converge` `ensure_review_spreadsheet_on_identity`, which REPLACED - the observation-only `ensure_review_spreadsheet` rather than landing beside it. The production - caller is `gunbc.review_sheet_spreadsheet_actuator` `converge_review_spreadsheet`, reached from - the argv-free `converge_drive_half_cli`. Two of the kernel's three frontier rows retired by - execution; `LegacyFileSelection` remains. - **The behaviour change worth knowing before the next live run:** a current-marked file beside an - unsettled superseded extent now REFUSES instead of adopting, and the discharge is an operator - declaration at `review_sheet_legacy_declaration_path` carrying the subject in its content. -- **Cut 3 (C1)** — LANDED, as `gunbc.review_sheet_declared_id_admission`. `declared_id_candidacy` - reconciles a `LegacyFileSelection` against the intended subject, the standing and a contradicting - observation, in that order and before any request; `preflight_file_by_id` is a new - `drive.Files.GetFile` whose field mask names every required field of `DriveFile`, and - `admit_preflighted_file` binds the readback to the requested id rather than to the status. - `converge_review_spreadsheet` asks the admission first and reports - `SpreadsheetAdoptedByDeclaredId` for an admitted one. **The refusal that must not be widened:** a - selection beside an unresolved extent stays unresolved — the operator has said which file they - chose, not that no other lineage exists. The discharge is an `"absent"` declaration, which is a - statement about the world and has a kernel arm. - The operator declaration file now admits two `legacy_extent` values, `"absent"` and `"selected"`; - a `"selected"` one also carries `file_id`. Both carry all five subject members. -- **Cut 4 (C2)** — `UpdateFileProperties` PATCH and the readback that asserts the patched file id - rather than a count. The field mask must name every required field of its declared response - type; `fields=id` against a `DriveFile` decodes short and reports a landed write as a transport - failure. -- **Cut 5 (E)** — legacy-generation retirement. - -**Three standing constraints for whoever resumes this.** The closed #11596's combined migration -route must not be executed. The operator's live sheet was repaired by hand, and the ordinary -steady-state adoption path was exercised live — two GETs, zero writes, existing sheet adopted. -And the marker must not be relocated again until the producer and admission cuts are resumed, -because a second relocation with no producer is how the first duplicate was minted. - -## Remaining: the census API budget - -The census needs a GitHub App installation token to lift its rate limit. This is **not** a -build — it is a reuse, settled across three lanes. - -- `#11677` already signs an App JWT: `extdeps.auth.jws` for the RFC 7515 signing input, - `extdeps.tools.openssl` as an enrolled host CLI, and `github.AppInstallationAccessTokens.Create` - for the exchange. The key is read from a file, the signing input enters on stdin, the signature - leaves on stdout; no key material reaches the evaluator or argv. -- **PRIMITIVE-EGRESS-0 admits that route conditionally.** It bans *ambient* primitives — registry - names and interpreter arms realising pure computation the `.dag` could own. It does not ban a - modeled external tool whose subject is genuinely external. The condition: the subject must be - the **key custody boundary**, a signer interface over a key reference with openssl as one - handler. Modeled as "RSA is realized by openssl" it fails, as an ambient primitive wearing a CLI. -- **Do not add an `rs256_sign` host primitive.** CRYPTO-0 is actively deleting host crypto - primitives; a new one would be a second authority landing against that program. RSA is not in - CRYPTO-0 wave 1 and a future modeled RSA is a different subject anyway — key inside the - evaluator — so a keystore-shaped interface survives it. -- **The shape, from #11677's owner:** a second composed performer in `gunbc.github_effect_perform`, - not a second module. Factor the `clock → claims → jws → openssl → Create` prefix into one - function returning a `sole_constructor` installation-token carrier. Each performer consumes the - carrier inside its own call. Never return a bare `Secret`, and never introduce a second - environment-variable token a PAT could silently satisfy. -- **The live control** should run with the PAT route unavailable, so a successful code-search page - is attributable only to the token producer rather than proving a token could be rendered. - -Not blocked. #11671, #11677 and #11679 are all merged, so the route this describes is available -to build against today. - -## Remaining: ntfy publisher token onto GCP custody - -`gunbc.auth.approval_ntfy_deployment` `approval_ntfy_publisher_token_intervention` is a -`HumanIntervention` — an operator creates the ntfy user and records its token where the unit can -read it. The read side is already modeled. Delivery is not. - -The design is agreed across three lanes and is **a consolidation of #11679 after it lands**, not a -sibling module: move it to a custody authority and make the controller key its first caller row. - -- One fleet-converge mode, with the credential a dispatch choice over a **closed roster of custody - rows**. One mode per credential grows a hand-enumerated roster — a known stall. A free-text - credential input would be a stringly selector. -- Each row: `SecretRef`, path, owner principal, group principal, and a **mode arm**. `root:root - 0400` becomes the controller key's row rather than a constant. -- **The directory's owner/group/mode must be a row field too.** A `0640` token for a service group - needs a directory that group can traverse (`0750 root:`); today's check refuses any group - bit. -- Mode stays a **closed arm**, never a caller octal. Derive the exact-mode leg and the - forbidden-bits leg from the arm (`077` for 0400, `037` for 0640); the owner leg takes - `-user`/`-group` from the row. - -**A caveat that must not be lost.** A find-metadata readback establishes which principals *hold -permission bits*. It does **not** establish that a non-writer cannot open the store — that needs an -execution-as-another-user leg. `gunbc.rung_drop.approval_store_single_writer_by_agreement`'s -restoration trigger requires the stronger observation, and its row has since been amended to name a -permission-bits readback among the things that do *not* retire it. The custody work is -necessary-but-not-sufficient for that trigger. - -Not blocked. #11679 is merged, so the custody authority this consolidates into exists today. - -**Landed as a consolidation** into `gunbc.host_credential_custody_converge` (the #11679 module, -renamed because it no longer holds one key): fleet-converge mode `host_credential_custody_converge` -with a `credential` choice over `host_custody_credential_roster` — `ControllerAppKey` first, then -`ApprovalNtfyPublisherToken`. The delivery HumanIntervention is deleted; what stays human is the -**mint** (`approval_ntfy_publisher_token_mint_intervention`): creating the ntfy user and adding its -token to Secret Manager. Two operator acts remain before the first live receipt for the ntfy row: -the mint, and ensuring `gunbc.auth.fleet_secret_accessor_roster` -`approval_ntfy_publisher_token_accessor_row`. - -## Small, unblocked - -Seven unused imports across `gunbc.ci_workflow_scan_producer` (`CodeSearchPage`, -`code_search_hits_read`, `GithubObservationRequest`, `ObservationBudgetStanding`) and its witness -(`CodeSearchPageComplete`, `CodeSearchPageTruncated`, `ObservationCompleteness`). All predate -#11656 and were deliberately left out of it to avoid widening a head-pinned repair. One small PR. - -## The census rows are historical prototype output, not an authority - -`gunbc-private:sheets-census` at `94fa118` holds `strategy.ci_prospect_census` — 57 rows carrying a -17-column set matching the formatted sheet's schema, transcribed rather than derived. - -**Label them: historical prototype output; NOT current runner-occupancy, spend, or opportunity -authority.** The economic readings attached to those rows were shown not to have the meanings -assigned to them, and the specific errors are worth carrying because each is easy to repeat: - -- workflow **wall duration is not summed runner occupancy** — a run lasting an hour may occupy one - runner for a minute, or twenty runners concurrently; -- workflow-run **admission delay is not runner queue delay** — they are different waits with - different causes; -- a **provider declaration can outlive actual provider execution**, so a declared runner label does - not establish that provider ran the job; -- **provider adoption does not establish positive spend** — a self-hosted or free-tier runner is - adoption with no bill. - -So the derived totals that prototype displayed — a runner-minutes-per-day figure and an ARM-tier -economic projection — do not follow from what was observed, and the "five carry `CostOpportunity`" -classification must not be quoted as a finding. Treat the 57 rows as a shape demonstration. - -**The bounded scan producer is necessary and NOT sufficient.** It yields a declared candidate -population; it cannot reproduce these rows or establish any economic reading, because the facts -those readings need are job-level and the scan is workflow-level. The minimal rebuilding route is -job-level observation: - -``` -jobs?filter=all - → all pages and attempts - → actual runner / provider identity - → job occupancy - → pre-start observation - → cancelled occupancy - → provider execution standing - → private commercial projection - → operator-safe sheet rows -``` - -That arc is carried as its own roadmap item rather than left in this prose. diff --git a/docs/plans/route-gap-dormant-observation.md b/docs/plans/route-gap-dormant-observation.md deleted file mode 100644 index 9ce1fe57b84..00000000000 --- a/docs/plans/route-gap-dormant-observation.md +++ /dev/null @@ -1,199 +0,0 @@ -# Where the route-gap population drops out of the required run — finding, then proposed repair - -Lane: calm-koi-257 (claim-execution-route). Status: finding complete; repair BUILT and under review on -PR #13376 (coordinator ack covered all four conditions; reviews 38602/76419/76431 addressed). - -## The population and the run - -`v2.workflow.floor_route_gap.floor_route_gap_roster` (authority: `src/v2/workflow/floor_route_gap.dag`) -enrolls identities the floor executes and that reach a host effect the hermetic route has no arm for -(`NoMockResponse` 420, `FilesystemRemoval` 10, `UnpublishedMockCase` 8 among the 425 typed rows; 100 more -as bare identity strings in chunks 00–05). 525 distinct identities across 146 modules; every one currently -resolves against the tree (verified per identity: module present, `test fn` tail present — zero stale rows -on current main; an earlier count of 2 stale rows was an artifact of my worktree lagging main and was -withdrawn). - -On required run 37236808750 (merge_group, pr-13305, floor job 111537440982): - - [floor-cost-debt] floor_route_gap: 5 enrolled identity(ies) suppressed (cost-debt roster withholds them) - [floor-required-gate] floor_route_gap: 536 enrolled identity(ies) suppressed (module outside the required gate, never loaded) - [floor-route-gap] roster carries 5 enrolled identity(ies) - [floor-route-gap] 5 enrolled identity(ies) held as route-gapped; 0 unenrolled route gap(s) reported - -Arithmetic: 546 decoded = 541 route-gap-roster rows + 5 gate-inside rows enrolled in the floor's -grandfathered roster (`v2.workflow.floor_grandfathered_roster`). The route-gap source on the run's merge -commit carried 421 typed rows + 120 legacy strings (= 541; current main carries 425 + 120); the 5 carried -rows — `parse_test.parse_witness_floor_holds`, `parse_test.parse_witness_perf_holds`, -`namespace_import_closure_witness.namespace_import_closure_receipt_holds`, -`namespace_structural_root_exposure_generated_witness_test.namespace_structural_root_exposure_generated_witness_holds`, -`v1_dag_parse_witness.v1_dag_parse_witnesses` — are not in the route-gap chunks at all; the floor's -grandfathered roster declares them as gate-inside enrollments, and the runner decodes both sources into -one 546-row roster before the gate split. - -## The chain, and the three drop points - -For an enrolled identity the required run promises (route-gap .dag header): execute → gap → held; or execute -→ pass → stale-row red; or enrolled → did not execute → red. What actually happens for 541 of 546: - -1. **Discovery/import** — preparation ranges over the gate closure. The gate (`required_gate_prefixes`, 11 - prefixes + seed modules, cut 2026-08-29, rung drop "Required gate reduced to the compiler floor") admits - only 5 of the 146 modules carrying enrollments. 536 rows sit in modules never loaded: no plan, no - execution, no receipt. *Declared* — the bankruptcy names the population per run in - `required_floor_disposition.tsv` (`declined_outside_gate_closure=26844`, `declined_outside_required_gate=696` - on this run; summary line `declared=28274 offered=1430 routed=704`). -2. **Selection/admission** — the only mechanism asserting "enrolled ⇒ executed" is the route-gap join - (`required_floor_runner.rs`). `suppress_withheld` removes the 541 from the roster *before* the join, so the - join's universe is the 5 gate-inside rows. The suppressed list is produced **per identity with grounds** — - and then discarded: route-gap publishes only the two aggregate count lines above. Expected-red got the - per-identity treatment on 2026-09-01 (`v1_compiler_expected_red_roster_join.rs`: "THE DENOMINATOR IS THE - ENROLLED ROSTER, NOT THE SURVIVORS", suppressed rows recorded with their ground); route-gap never did. -3. **Decision** — the run greens with "5 held; 0 unenrolled gaps". 541 enrollments are in no ledger the run - publishes: the claim-cost TSV carries only executed identities (704 rows, 5 `host_effect_refused` — the - carried ones: `test.claim.parse_test.parse_witness_floor_holds`, `...parse_witness_perf_holds`, - `test.claim.namespace_import_closure_witness.namespace_import_closure_receipt_holds`, - `test.claim.namespace_structural_root_exposure_generated_witness_test.namespace_structural_root_exposure_generated_witness_holds`, - `test.claim.v1_dag_parse_witness.v1_dag_parse_witnesses`); the measurement receipt carries only - `standing` + `blockers`; the disposition/join TSVs are written by the floor job but **not uploaded**. - -## The (a)/(b)/(c) split (sampled, identity grain) - -- **(a) Deliberately outside the supported guarantee** — the wet/service families: machine intake - (`test.claim.machine_intake.mtcollins1_kvm_observer_protocol_wet_witness.*`, 44 rows), fabric (31), spark - serving (74), runner (13), codex supervised turn (21), workspace storage — claims whose execution needs - service fabric the hermetic required run does not attach by design. Their route-gap rows were enrolled from - the pre-bankruptcy era; the bankruptcy (declared 2026-08-29) is the standing disposition for the family, - and `DeclinedNoCiWetLane`/`DeclinedOutsideRequiredGate` type the planning-level fact per identity. For - these, a per-identity suppressed record on a required run is **measurement, not closure** — closure for - them is the gate/wet-lane decision already declared in the rung drop. -- **(b) Meant to support, unobserved** — the hermetic-adjacent families whose route gap is exactly the - missing mock arm the register exists to demand: filesystem/store mock arms (`artifact_store_fs_witness`, - `durable_exclusive_hold_file_store`, `materialization_store_local`, `effect_plan_bash`: operations Dir 127, - DirWithTemplate 130, DigestStdin 42, Run 46, Check 26) and the withdrawn `v2.test.execution.emit_on_demand_*` - family (probed 2026-10-01, amendment names the restoration trigger: "the restoration trigger above stands - whole"). The register's demand semantics — "a row leaves when a route is supplied" — require observation; - with no observing run, no arm gets built and the trigger cannot fire. The suppression comment's promise - ("observable again ... in the whole-corpus receipts run") is **a promise with no executor**: no workflow in - `.github/workflows/` (8 files, none scheduled for a corpus pass) runs it, and the rung-drop authority - itself rules the escape hatch out ("a receipt-only run outside the required path does NOT retire the row"). - So for (b) the absence of that run is not merely a gap — it is the defect: the demand register's only - claimed observation point does not exist. -- **(c) Defect (structural, live today at zero count)** — the class "enrollment the tree no longer declares". - The mechanism cannot see it: suppression counts it with the (a)/(b) rows, no arm refuses it, and the - roster header itself warns "an enrollment nothing observes is a row that can never ask to be removed". - Zero live rows today (audited); the class is one rename away, and nothing would notice. Also (c): the - route-gap contract's four-arm sentence still claims whole-roster observability and was never amended when - gate-bounded suppression landed (2026-08-29) — two authorities now disagree silently. - -## Proposed repair (one partition in the existing admission path) - -In `suppress_withheld` + the route-gap join in `required_floor_runner.rs`, partition the roster at identity -grain — the cost-debt partition precedent (gunbc#9684) and the expected-red join-report precedent -(2026-09-01), no new system, no new CI job: - -1. **Declared + gate-inside** → observed today, join as now (held / stale-red). -2. **Declared + outside-gate (or cost-debt withheld)** → typed, located, per-identity suppressed row carried - into the outcome (like the expected-red join report) and named per identity on the existing stderr - channel (pattern: the cost-debt "kept as record" line), each with its ground - (`OutsideRequiredGate` / `CostDebtWithheld`). Label: for (a) this is measurement; for (b) it is the - explicit disposition that replaces the green absence. -3. **Undeclared** (module absent from the discovery roots / identity tail absent) → **typed refusal**, red, - naming identity and roster. Fires on zero rows today; keeps it that way. -4. **Text amendments**: the route-gap header's four-arm sentence and the suppression comment's - "whole-corpus receipts run" promise are amended to state the real standing (dormant until the gate - widens; no receipts run exists and none retires rows). - -Acceptance path: author a fresh claim in an out-of-gate module, enroll it in `floor_route_gap`, and the -required run records it per identity with ground `OutsideRequiredGate` — or, if misnamed, refuses. Heavy -runs remote/CI only. - -## Repair as approved and built (coordinator ack, conditions + single-implementation round) - -The partition decision is now **modeled on the .dag authority** — -`v2.workflow.floor_route_gap.floor_route_gap_admission_partition` is the single implementation of the -route-gap admission decision, membership judgment included; the Rust call site marshals the run's real -values (suppressed identities with their declared ground arms, and the discovery walk's declared-identity -index WHOLE as a keyed map) into it through `run_in_context_with_args`, and the relation performs the -membership judgment itself with `map_contains_key` per row — the runner does no membership test, so -refuse-if-undeclared is decided on the authority. An earlier Rust classifier -(`route_gap_suppressed_undeclared`) was **deleted** rather than kept beside the call — one implementation, -a net reduction of seed decision surface; the seed receipt counts **+4 hand items**, all in the runner: the -shared entry name, the suppressed-row marshal, the declared-index marshal, and the shared production -decode+enforce (`route_gap_admission_decode_and_enforce`) — extracted from `run_required_floor` so the -pairing test drives the same function the run site executes, and the misnamed fixture produces the actual -`REQUIRED-FLOOR REFUSAL cause=RouteGapEnrollmentUndeclared`, not just the relation's refusal arm. The declared index arrives as a keyed map so the judgment -stays in .dag at one O(1) lookup per suppressed row: an earlier shape flattened the declared index for -the .dag to linearly re-scan per suppressed row (541 x 28,274 ≈ 15M interpreted string comparisons per -required run) — a cost-shape defect, fixed per DESIGN §6 rather than retired on "n is small here"; a -per-row Boolean read in Rust was the intermediate shape, retired because a Rust-side `contains_key` IS -the refuse-if-undeclared judgment. What would move even the marshal into the .dag is the self-emitted -claim executor owning the discovery walk and feeding the relation directly; deliberately not built. The -judgment could not have been expressed in .dag on its own earlier because the declared universe is -discovery-walk knowledge (the roster decodes in a hermetic frame whose subject is the gate closure); -modeling the RELATION and marshaling the index whole as a keyed map is the resolution of that. - -1. **The wall (closure)**: a refused row names an enrollment the tree does not declare (module or tail - absent from the discovery roots; the disposition index covers every declared witness identity, - gunbc#9684) and the run refuses with `cause=RouteGapEnrollmentUndeclared`, naming the identities and - the modeled entry that decided. -2. **Single authority**: the route-gap .dag header contract states the gate-bounded amendment (the two - further arms) beside the original four; the `suppress_withheld` stderr line no longer promises a - whole-corpus receipts run — it states the true standing; and the membership test has exactly one - implementation, on the authority. -3. **Measurement (labeled)**: every suppressed identity is named with the ground the modeled partition - returned for it, headed MEASUREMENT — for the (a) families this records declared dormancy and closes - nothing. The ground itself is a **declared coproduct** (round 3, per review 76626): `ground` carries - `v2.workflow.floor_route_gap`'s `FloorRouteGapSuppressionGround` (`OutsideRequiredGate | - WithheldCostDebt | DeclinedNoCiWetLane`, spelled arm-for-arm after the floor's suppression enum as - the seed realizes it — `v1.expected_red_roster_join`'s suppression ground, generated into the runner - as a Rust enum) instead of a free label; the marshal decodes the enum into the declared arm and the - run site refuses an arm the enum does not declare, so a fabricated ground can never pass as a label. - The arms cannot be shared by import because the eval universe's discovery roots are `dag` + `src/v2` - (no `src/v1`), so name-alignment is what keeps the two spellings one vocabulary. -4. **Not closed here — class (b)**: unchanged from the section above; the named follow-ups are gate - admission of those modules, or a dispatch instrument row running a probe roster with the join armed. - -**Evidence, pinned on both sides of the boundary:** -- **Floor-side route witness** — `test.claim.route_gap_partition_witness`, gate-admitted at exact module - grain in `required_gate_authored_modules` (the same mechanism and operator ruling as - `test.claim.discovery_census_witness` and `test.claim.seed_growth_admission_witness`): drives the - modeled relation over the shared fixture `src/v2/test/fixture/route_gap_admission_partition.dag` and - asserts both arms on the floor — the route, executed by a required run. Substrate inputs only - (constructed lists; no host effect, no service). -- **Seed-side pairing witness** — `route_gap_admission_partition_tests` in `required_floor_runner.rs` - drives the SAME entry by the SAME constant through `run_in_context_with_args` (the real call path the - run site uses) and asserts both arms at identity grain; a second test pins the marshal shape (identity - and declared ground arm, nothing else). Runs via the required `rust-unit-tests` lane (see the wall's - standing below — `DESIGN.md` "Building & checks"; the lane returned required on 2026-09-30). -- **One implementation**: with the Rust classifier deleted, the run site's route to the answer is the - modeled entry by construction; a run-path refusal names the entry that decided it. - -**The wall's real standing (corrected per review 76431 — an earlier revision of this paragraph claimed a -shipped fixture; the fixture is gone, per review 38602, and this is what actually holds):** - -- **Green by execution on the required path:** the partition IS the run's own admission step — on every - required run it executes over the real roster, names every suppressed enrollment per identity with its - ground (the `[floor-route-gap]` suppressed lines), and the reverse join decides over the observable - remainder. The consumer is the fold itself; nothing about the green arm is test-only. A freshly authored - claim shaped like the dropped population (declared, out-of-gate, enrolled) gets the same typed, located - disposition on that run as the 536 do. -- **Discriminating red: authored at unit grain AND enrolled on a required lane.** The misnamed-enrollment - control runs both ways this PR ships: - - **On the floor (REQUIRED, blocks merge):** the claim-home row in `v2.workflow.required_floor` - `required_gate_authored_modules` gate-admits `test.claim.route_gap_partition_witness`, whose third test - (`dag/test/claim/route_gap_partition_witness_test.dag`) drives the modeled partition over the shared - fixture and asserts the refused arm at identity grain — the wall's red executes on the `witnesses` - lane, which is a required merge gate, with substrate inputs only (constructed rows and the fixture's - keyed declared map; nothing shipped on the production roster, per review 38602's rejection). - - **At unit grain (REQUIRED):** the pairing witness in `route_gap_admission_partition_tests` drives - `run_in_context_with_args` — the real call path the run site takes — over the same fixture rows and - asserts both arms; it runs via `cargo test --release -p v1-compiler --lib`, which per `DESIGN.md` - "Building & checks" runs as the `rust_unit_tests` step of the required `rust-unit-tests` job, so a - unit-test red blocks a merge. (The doc previously cited a stale generated projection, - `docs/onboarding.md` line 175, saying the lane ran on no required step; the authority is `DESIGN.md`, - and `DESIGN.md` records the lane's 2026-09-30 return and the retirement of the - `rust_unit_tests_off_the_merge_path` drop.) - The standing caveat that remains is narrower: a *live roster* misname — a misnamed enrollment shipped - onto main's production roster — is not how the red is authored (rejected by review 38602: it would red - main forever and fabricate a gap the §5 oracle forbids); the red is authored from fixtures the claim - supplies. The (b) follow-up above — a dispatch instrument row that runs a probe roster with the join - armed — remains the path to authoring that red over the real production roster rather than a fixture. diff --git a/docs/plans/runner-service-capacity-convergence.md b/docs/plans/runner-service-capacity-convergence.md deleted file mode 100644 index 2bc7701cd9d..00000000000 --- a/docs/plans/runner-service-capacity-convergence.md +++ /dev/null @@ -1,213 +0,0 @@ -# Runner service capacity convergence - -**Status:** design, not implemented. Written 2026-08-25 from a live incident on srv1/srv3. - -## Why this exists - -Fleet convergence reported hosts converged while they served no traffic. srv1 held **50 slot -directories and 5 running services**; srv3 went from 8 directories to 21 while its running count -stayed at 5. Nothing in the pipeline could notice, because the convergence object is filesystem -membership and the subject that matters is **service capacity**. - -Filesystem convergence is a legitimate *subordinate* family; the defect is that it was read as -implying serving capacity. - -## The incident chain, in order, each blocker masking the next - -``` -1. ghrunner held no sudo grants -> apply could not reach installation -2. grants installed -> 13-16 runner trees could be materialized -3. install path does not activate -> directories existed, capacity unchanged -4. units enabled/started by hand -> pinned runner v2.334.0 reached GitHub -5. GitHub refused fresh 2.334.0 -> systemd restarted unusable processes forever -6. release replaced with v2.336.0 -> serving capacity finally rose -``` - -The privilege work was necessary and could never be sufficient: it proved the host could -execute the installer, never that the provider would accept what was installed. - -## Three defects, all still open - -### 1. Membership is not incarnation-sensitive - -`runner_slot_member_value_eq` compares only `slot_dir`. A 2.334.0 tree and a 2.336.0 tree at the same -path are the same member, so a release bump repairs nothing that already exists. - -**The receipt:** 29 slot directories were deleted by hand across srv1 and srv3 to force -re-provisioning — replacement could only be induced by turning an existing member into an absence. - -`LocalRunnerSlotIncarnation` carries slot, unit, unit_active and registration_name -- not the -installed artifact -- so it cannot express *same slot, same unit, same registration, different -release*, which is exactly the state that hurt. - -### 2. The install path cannot activate — INTENT LANDED, NOTHING ACTIVATES YET - -The emitted `apply.sh` carried `daemon-reload` and `disable`. Enable count 0, start count 0. - -The two are separate transitions that must not be re-fused: `enable` is durable boot intent, -`start` is immediate activity. Both consume the activation admission, because enabling authorizes a -future reboot to start the broker under readiness conditions that may no longer hold. - -**What landed.** `gunbc.runner_service_activation` models the two transitions, admits each one -separately against `admit_runner_activation`, and derives the emitted lines from -`gunbc.executor_privileged_operation`'s new `SystemdEnableUnit` / `SystemdStartUnit` — so the -sudoers grant and the executed command are one argv, and the fused `enable --now` the hand loop ran -has no grant on any executor host. `fleet_converge_plan` carries an activation family beside the -slot family, with its own line in `plan.txt` and its own refusal count. - -**What did NOT change — read this before quoting a green apply as converged capacity.** The -admission is unreachable in production; two blockers sit **in series**: no host observation -transaction exists, so every production caller supplies `ActivationReadinessUnobserved` and the -admission is never asked; behind that, `admit_runner_activation` requires a `HostCompilePoolReady` -whose producer answers `PoolNotDeclaredInTopology` fleet-wide under `CompilePoolInRunnerSlots`. -Flipping the placement changes nothing observable while the first stands — the pool receipt is a -field *inside* a readiness value that is never `Observed`. So both transitions refuse on every host -today, `apply.sh` prints a typed located refusal for each, and serving capacity does not rise from -apply alone. - -That state is a typed `GuaranteeStall` row — `runner_activation_reachable_green_stall` — not -prose, per §4b: a gate whose only reachable state is refusal must say so, or a reader cannot tell a -permanent structural refusal from a transient one. The row names the observation transaction as -the immediate blocker and the placement behind it, in that order, because naming only the second -yields a plan that does fleet-wide topology work and measures no change. - -**Unreachable in production is not inert.** §4b judges reachability against what a *fixture* may -author, and `test.claim.runner_service_activation` authors `ActivationReadinessObserved` and drives -the admitted enable/start path with it — so the emission has an authorable, exercised RED at the -fixture boundary. - -**What the item bought,** without inflation: an invisible hand loop became a named, -counted, located refusal. `enable count 0` was indistinguishable from a host with no activation -work; `activation-refusals=2` with its cause is not. - -**The host boundary is mitigated, not structural, and the safety row says so.** The grants install -the raw `systemctl enable ` / `systemctl start ` verbs for `ghrunner`, so the grantee can -run them directly, outside the admission. Three claims must be kept apart: *enable and start -are separately authorized* is structural (no derived sudoers line matches `enable --now`); *this -emitter cannot activate without readiness* is structural inside the modeled call graph; *the host -cannot activate without readiness* is **false**. `runner_activation_safety_guarantee` records -`Mitigatable` with the next-rung trigger — the job user stops holding the verbs, via a root-owned -helper that validates a receipt or a root-owned convergence service. A narrower sudoers match cannot -climb it: any grant that lets the sanctioned path run the command lets the grantee run it too. - -**Next.** The host observation transaction — the shared blocker named below — and only then the -placement flip, with a width decision re-derived per host from live `nproc`/`MemTotal` rather than -from `gunbc_jobserver_token_override_note`, which is stale for srv1. Until both land, capacity -increases stay manual, and the refusal in `apply.sh` says so. - -### 3. Provider admissibility is unmodeled - -Three different guarantees were collapsed into one pin: - -``` -ArtifactIntegrity these are the exact bytes and digest we chose HELD -ArtifactAvailability the vendor still publishes those bytes HELD -ProviderAdmissibility the vendor's control plane still permits them MISSING -``` - -An exact digest proved we faithfully installed the wrong operational version. - -`Listening for Jobs` is **not** acceptance -- the rejection arrives after that line. - -## Serving vs restartable capacity - -The distinction the incident exposed, worth carrying as a number rather than prose: - -``` -serving_count members providing capacity now -restartable_count members whose fresh incarnation the provider still admits -``` - -Before the replacement srv3 was `ServingButNotRestartable`: five runners on pre-deprecation -registrations, alive only until something restarted them. A fleet that meets load only while nothing -restarts is not converged, and today no carrier can say so. - -## The layered object - -Independently observed families composing into one verdict -- not a single `runner healthy` Bool: - -``` -RunnerMaterializationStanding = Absent | CorrectIncarnation | WrongIncarnation | Unobserved -RunnerEnablementStanding = Enabled | Disabled | Unobserved -RunnerActivityStanding = Active | Inactive | Activating | Failed | Unobserved -RunnerProviderStanding = ExistingSessionServing | FreshMessageChannelAccepted - | FreshJobAccepted | RejectedDeprecated | Unobserved -``` - -The host verdict is over the **exact desired GitHub identities**, never raw committed width -- srv3 is -precisely where those differ, and a fabric identity must never inhabit the GitHub service family. - -## The shared blocker: there is no host observation transaction - -Both remaining repairs wait on the same missing seam. `fleet_converge_plan` observes directory -membership and nothing else -- zero matches for `BuildCacheInstanceReady`, `HostCompilePoolReady` or -`admit_runner_activation`. It cannot answer: - -``` -which release is installed in this slot? -is the unit enabled? active? failed? -does this exact cache / compile-pool receipt exist? -does GitHub accept a fresh incarnation of this release? -``` - -The repair is a real observation transaction feeding several reconcilers -- **not** manufacturing the -receipts in the planner, and not an activation action that permanently refuses. - -**Every sub-observation needs its own unavailable arm.** One failed `systemctl` read must not erase a -successful installed-version observation, and one unavailable version read must not become -`WrongIncarnation`. - -## The compatibility probe, and why it need not run a job - -Two questions hide inside "will GitHub hand work to it": - -1. will the broker accept this fresh runner version on its message channel? -2. will the scheduler match and deliver an actual job? - -**Only the deprecation class is question 1**, and a no-job probe settles it. The runner opens a broker -session then polls a message endpoint, sending `runnerVersion` among other fields; the broker maps -`RunnerVersionTooOld` to exactly the access-denied failure observed on srv3. So a successful -message-poll from a *fresh incarnation* is the discriminator. A canary job is only needed for -question 2, which is not what deprecated us. - -Scope it per `release x architecture x scope` with a bounded validity age -- not per slot. - -## Do not make "latest" the authority - -That trades reproducibility for churn and is wrong during a progressive rollout or a bad release. -Policy: a reviewed pinned candidate plus a compatibility receipt plus a declared maximum -observation age. A new upstream release creates an *obligation* -- test, review the pin change, plan a -rolling replacement -- never an automatic fleet mutation. - -## Rolling replacement, not restart - -When the desired artifact changes, per slot: stage the correct artifact, drain the old broker, replace -the incarnation, start the new broker, prove fresh provider acceptance, then continue. - -**Do not restart the old runners first.** In this incident restart was exactly what converted latent -old capacity into visible failure. - -## Sequencing - -``` -materialization plan/apply -> independent materialization readback -fresh cache/pool observations -> activation admission -> enable -> start -> service readback -provider compatibility readback -> serving/restartable capacity receipt -``` - -An installation plan minted before readiness must never later authorize starting a runner: activation -needs a fresh subject and fresh observations. - -## Landed vs open - -**Landed** (PR #9152): one argv grounds both the executed command and its sudoers grant; run-as -narrowed `ALL` -> `root`; wildcards eliminated; the bootstrap principal recorded as a typed row with a -witness (`ubuntu`, not the deploy row's `ssh_target`); release bumped to 2.336.0. - -**Built, not landed** (`session/warm-tern-755-runner-activation`): the three-axis service standing with -its 13-row truth table and mutation receipt. Split out for having no production consumer -- which is -this document's shared blocker. - -**Open:** incarnation-sensitive membership; the host observation transaction; provider admissibility; -serving/restartable counts; the compile-pool placement flip that gives the activation admission a -reachable green. diff --git a/docs/plans/scm-corpus-manifest-design.md b/docs/plans/scm-corpus-manifest-design.md deleted file mode 100644 index 226f147d99a..00000000000 --- a/docs/plans/scm-corpus-manifest-design.md +++ /dev/null @@ -1,256 +0,0 @@ -# SCM `add`/`commit` — a commit freezes an authored corpus; a program is a projection of it - -Subject of gunbc#9891 finding 6, settled with the designated SCM reviewer before implementation. -The governing sentence: - -> **A repository commit freezes an authored corpus manifest. A semantic program is a separately -> bound ingestion projection of that corpus, not an alternative kind of commit subject.** - -## 1. The gap - -`gunbc.scm.cli` exposes `scm_log`, `scm_status`, `scm_init` — no `add`, no `commit`. `cli.dag` -documents the consequence about itself: it passes `empty_proposal()` because "the entry does not -carry what is staged", "a REAL limitation rather than a placeholder". So the kernel has no producer, -which is the §5 specification-without-execution gap the SCM design note also names. - -## 2. Two corrections to this note's own earlier drafts - -**Ingestion is modeled, not absent.** An earlier draft asserted otherwise. It searched -`dag/gunbc/scm`, found nothing, and let an empty subtree speak for the repository — *a partial -observer returning the negative value of a total observer*, the class whose ledger was -`gunbc.namespace_wave_admission`'s at this writing (that module was deleted with the -wave-admission wall, operator ruling 2026-09-19). `src/v2/compiler` models `01_tokenize`, `02_parse`, -`03_ingest`, `00_compile`. - -**Most of the proposed vocabulary exists.** `v2.compiler.source_authority` declares -`SourceRef { path, source_root, content_hash }` and `SourceRootIngest`, with -`source_root_ingest_build_from_source_refs`. Minting a manifest of `path -> content identity` would -be a second name for it. Finding 6's *substance* stands; its proposed vocabulary does not. - -## 3. `semantic_root` does not belong in the manifest - -Finding 6 phrases the manifest as `path -> semantic_root -> authored_source_identity`. That is -wrong for a deeper reason than present-day constructibility: **`semantic_root` is a derived -ingestion result**, depending on the source object, corpus context and imports, the ingestion rule, -language and rule versions, and possibly source-root and target configuration. Putting it in the -manifest fuses an input with a realization result, and makes the manifest unauthorable for -malformed or not-yet-ingested source — precisely the corpora a source-control system must still be -able to freeze. - -The authored manifest is therefore only: - - canonical corpus path -> exact authored-source target - -and the semantic relation lives in a separate carrier, whose load-bearing properties are that it -names the manifest it is about, names the ingestion rule/version, keeps each per-file semantic -result bound to the exact source identity, and derives the global program root rather than -accepting one supplied beside the corpus: - - type CorpusIngestionProjection { corpus, ingestion_rule, program_root, files } - type FileIngestionProjection { path, source, semantic_root } - -This preserves the case that proves the split: a comment-only edit changes authored bytes, so -manifest A ≠ manifest B, while the semantic root is unchanged. - -## 4. Object-store membership and edge admissibility are separate axes - -The manifest joins `ScmObject` **without** becoming a legal `StoredEdge` target: - - type ScmObject = SemanticNodeObject(ObjectRecord) - | AuthoredSourceObject(AuthoredSourceRecord) - | CorpusManifestObject(CorpusManifestRecord) - -`StoredEdge.target` remains exactly `SemanticNodeTarget`, so #9891's distinction is untouched. Each -kind gets its own target and its own three-way find outcome, so a walk cannot read one kind's -absence as another's: the manifest walk follows only authored-source targets, a semantic node only -semantic targets, an authored source nothing. - -`CommitClosure` is **not** silently widened — its subject is a semantic root. Either a separate -corpus closure, or a parameterized mechanism whose exported outcomes still distinguish -semantic-node requirements from authored-source requirements. - -## 5. `commit` must not refuse for want of ingestion - -This corrects an earlier proposal in this note that `commit` be withheld until ingestion is green. - -Freezing what was authored is **fully answerable today**: paths are known, bytes are known, source -identities and the manifest identity derive from them, and the exact authored state is -reconstructible. The missing fact appears only when a *semantic* question is asked — reconstruct the -node, integrate role requirements, validate, produce semantic closure. Those answer -`SemanticProjectionUnavailable { corpus }` or an ingestion refusal carrying real diagnostics. - -Refusing `commit` would fuse **recording state** with **establishing semantic validity**, which the -SCM design keeps apart. An invalid corpus, or one this compiler cannot ingest, must still be -committable if `commit` is to remain distinct from certification. - -So the canonical record becomes approximately: - - type RepositoryCommit sole_constructor { - reference: RepositoryCommitRef - corpus: CorpusManifestObjectRef - message: String - ancestry: CommitAncestry - } - -Two repositories may differ in *projection availability* while agreeing entirely about the commit. -Disagreement about which manifest a commit names stays forbidden; two projections under one -`(manifest, ingestion rule)` key yielding different roots must refuse as an authority collision. - -Compatibility with the existing semantic-root format belongs in a **versioned decoder**, not in the -canonical writer — and absent a measured durable population, root-migrate rather than keep a -permanent legacy arm. - -## 6. A naming fork to close before both sides gain consumers - -`gunbc.scm.checkout` declares a bare `Commit`, which role-requirement integration returns; the -repository record is `RepositoryCommit`. They currently meet on a semantic root. Once repository -history is corpus-rooted they stop meaning the same thing, so the bare one becomes -`SemanticCommit` / `ProgramRoot` before both gain production consumers — the same §3 fork this -lane's previous PR closed for `merge`. - -## 7. Scope, stated no larger than it is - -`add`/`commit` makes load-bearing: the authored-source store, the manifest, repository history, -persistence, `log`, `status`, and the ancestry spine. - -It does **not** make role-requirement integration load-bearing. That operation returns the separate -semantic commit whose only content is a node root; it does not produce a `RepositoryCommit`. -Joining those two worlds still needs ingestion, and eventually semantic-to-authored emission. - -## 8. Sequence - -1. **Model**: the manifest object, its ref and target, the three-way find outcome, the corpus - closure, `RepositoryCommit.corpus`, the `Commit` rename, the versioned decoder. -2. **Verbs**: `add` (stage), `status` (report staged), `commit` (freeze the manifest). - -Pending state is not option B: a *committed* manifest must be reachable from the commit, while a -*pending* one is intentionally not — `RepositoryEnvelope.checked_out` is already local workspace -state rather than authoritative history. - -## 9. Where a manifest is wrong, and where it is merely present - -Adding a third arm to `ScmObject` forces every exhaustive match over it to answer for a manifest, -including the commit-closure census and the v3 JSON encoder. Two of the three available answers are -wrong for reasons worth recording, because each is a failure class this lane has already committed -once. - -**Refusing when a manifest exists anywhere in the store** makes "serialize the semantic closure -rooted at P" fail on unrelated repository inventory. A manifest is wrong **at a -`SemanticNodeTarget`**; it is not wrong **in an `ObjectStore`**. That is the same -membership-versus-admissibility split that keeps `StoredEdge.target` exactly `SemanticNodeTarget`, -applied one layer up — and collapsing it is the absorbing fallback of §5, refusing a superset -because the precise subject was not derived. - -**Bumping the closure format tag** was rejected here on the grounds that the wire schema was -unchanged — and that reasoning was superseded by what the cut turned out to require. The v4 bump -that landed is the OPPOSITE claim: the writer can no longer emit `{source}` and the reader refuses -one, so the language got **narrower**, and holding a tag while the language shrinks is the same -false claim as holding it while it grows. `gunbc-scm-commit-closure-v4`, `gunbc-scm-repository-v3` -and `scm-object-identity-v2` are what shipped. - -> **Superseded in part.** The repository tag is now `gunbc-scm-repository-v4`: gunbc#10560 moved -> `RepositoryCommit.root` from a semantic node to a corpus manifest, so the member spelled `root` -> designates a different population while keeping its name — which is precisely why the tag had to -> move. `gunbc-scm-repository-v3` is refused BY NAME as retired rather than folded into unrecognized, -> and there is deliberately no v3 reader. The other two tags are unchanged, and that is the point of -> versioning them on separate axes: the closure format has its own subject, and the identity rule did -> not move because hashing did not. Read the tags from `gunbc.scm.repository_envelope`, not from this -> sentence — a transcribed tag rots without anyone touching either end. - -The cut is neither of the two arms above. `commit_closure_json_v2` already declared the obligation -this change discharges: every record the store holds is serialized, which it states is correct -*while nothing produces unreachable objects*. `CorpusManifestObject` is exactly the event that makes -that false, so the deferral's own dissolution trigger has fired. - -So the admitted carrier stops carrying the raw store and carries the **root-reachable semantic -closure**. The design draft above proposed a two-armed `ClosureObject` with a -`ClosureAuthoredSource` arm; that arm was found to be **uninhabited** during implementation, because -no typed path reaches an authored source from a semantic root, and the reviewer's ruling changed to -nodes only. What shipped is: - -- `WellKindedClosure { root: SemanticNodeTarget, nodes: List }` — one kind, not a - coproduct, because there is nothing else to be reached; -- the admission walks from the root; at each `SemanticNodeTarget` a node is included and recursed - through, an authored source and a manifest are each their own named wrong-kind population; -- the encoder's parameter therefore cannot name a manifest at all: unconstructible rather than - validated — §4b rung 4 rather than a commented-over rung 3. - -Two consequences bind the implementation, and one more was found by review. First, **one derivation -authority**: both `admit_well_kinded_closure` and the partial route must consume the same -`derive_semantic_closure(store, root)` result. Second, `well_kinded_store` **loses its name**: -returning a node list under a store-shaped name would say the carrier still holds a store. - -Third, and this is the one the draft missed: `sole_constructor` seals the record LITERAL, and `.dag` -has no module privacy, so a public mint beside it re-opens everything. The module shipped -`well_kinded_image(closure)`, which took an unchecked closure, discarded the derivation's census and -returned encoder input anyway. **The mint is deleted**; the literal appears once, inside the -else-branch of the census that authorizes it, and the admitted arm carries the unresolved population -so nothing walks the closure twice to re-derive it. - -Unrelated authored sources leaving the document is a consequence, not a regression. "It happened to -be in the repository store" was never semantic reachability. - -### The witness bar for this cut - -1. **Unrelated manifest control** — semantic root, valid node closure, an unrelated manifest in the - store: admission succeeds and the manifest is absent from the admitted closure. -2. **Manifest-at-node RED** — a `SemanticNodeTarget` whose locator holds a `CorpusManifestObject`: - a named wrong-kind refusal, never `absent`. -3. **Unrelated source control** — an authored source not reachable from the root is absent from the - emitted population. -4. **Partial path parity** — the ordinary and partial routes derive the same population; mutating - either back to the raw store takes a manifest fixture red. - -## 10. What the corpus closure does *not* get yet - -`CorpusClosure` is **not** modelled. An earlier revision of this section said it was — naming its -complete/partial/wrong-kind outcomes, its identity behaviour and its witnesses as landed — and no -such carrier exists. What exists is `ManifestSourceCensus`, which answers the manifest-source -reachability question over a finished store in three populations (absent, node-occupied, -manifest-occupied) and is consumed by both the checked repository writer and the repository reader. -That is the minimum the write-safety question needed; it is not a closure carrier and does not -pretend to be one. - -It gets no JSON codec here either. A named codec earns an actual boundary, and nothing yet persists -or transports a corpus closure; building one would add a representation with no consumer immediately -before a lane whose stated purpose is to stop doing that. - -The deferral does not extend to the repository codec. The moment `add`/`commit` persists a -repository holding manifest objects, commits naming them, or pending corpus state, the repository -format necessarily advances and must encode manifests — and that lands atomically with the first -path that saves such a repository, not before and not after. If the repository envelope turns out to -be the only representation `add`/`commit` needs, a standalone corpus document should never be built -at all. - -## 11. Corrections this document owes to its own earlier revisions - -Recorded here rather than silently rewritten, because a plan that quietly agrees with whatever -shipped is not evidence of anything. - -- **The `SourceRef` duplication warning (§2) was about a different construction.** The manifest that - shipped holds `(path, AuthoredSourceTarget)` and mints no second source identity, so the warning - stands as a rule and does not describe this head. -- **The manifest was specified as an ordered list and is now a function.** Ordered hashing made a - host directory traversal order part of the manifest identity, and admitted one path naming two - sources. Entries canonicalize by path and a duplicate path refuses. -- **`ClosureAuthoredSource` was an uninhabited arm**, as above. -- **The witness bar in §9 is met, with its second item widened.** A manifest at a semantic - requirement is a named wrong-kind refusal in the closure, the checked writer and the reader — the - draft only anticipated the closure. -- **That last sentence overclaimed the reader for one revision, and this is where it did.** When it - was first written the reader adjudicated one subject: a MANIFEST ENTRY's authored source. A - SEMANTIC CHILD naming a manifest or a file by digest still reached `RepositoryDecoded`, so the - sentence was true of the writer and only half true of the reader. Both subjects are adjudicated - now, against the finished decoded table, and the discriminators for each occupying kind execute in - `test.claim.scm_repository_envelope_witness` — but the claim preceded its second half, which is - the failure this document is a ledger of. -- **A wrong-kind requirement reached two different standings depending on how its reference was - spelled.** `uncontained` classified as `LoadRequirementAtWrongKind`; the same predicament reached - through a contained position classified as `LoadDocumentMalformed`, which is the one standing that - permits the supersession step. The predicament decides the standing now, and the paired claims run - the decoder, the classifier and the permission table rather than constructing standings by hand. -- **A "the occupant arrives after the referrer" witness never presented that order.** It changed - store insertion order, but `emit_in_dependency_order` follows each reference through the - kind-agnostic `find_object` and emits the occupant first regardless, so both fixtures produced the - same bytes. The controlled version rewrites the encoded `objects` array and asserts the order took - effect before asserting the refusal. diff --git a/docs/plans/scm-demo-cli-rebuild.md b/docs/plans/scm-demo-cli-rebuild.md deleted file mode 100644 index a82ebb689d7..00000000000 --- a/docs/plans/scm-demo-cli-rebuild.md +++ /dev/null @@ -1,322 +0,0 @@ -# SCM demo CLI — rebuild plan and API inventory - -A working `gunbc scm` CLI existed and was lost with an uncommitted `/tmp` worktree. This doc is -the durable record so the rebuild does not start from a survey again. - -It is a PLAN AND A HISTORICAL LEDGER, and the two are separated by section. Everything above -`## LANDED` is the plan and the baseline survey, written BEFORE this work and describing main as -it stood then; it claims nothing is built. `## LANDED` onward is the ledger, and it carries -execution receipts for what has actually shipped. Read a present-tense statement in the plan half -as a statement about the BASELINE, not about the tree today. - -## What already exists on main (verified by reading the modules) - -The read side is modelled and already reaches `CliWireResponse`: - -| module | symbol | shape | -| --- | --- | --- | -| `gunbc.scm.read_command` | `ScmReadResult` | `ScmReadAnswered { path, answer }` \| `ScmReadRepositoryUnavailable { cause }` | -| `gunbc.scm.render` | `scm_log_cli_response(result, target, capability)` | `-> CliWireResponse` | -| `gunbc.scm.render` | `scm_status_cli_response(result, target, capability)` | `-> CliWireResponse` | -| `gunbc.scm.render` | `scm_read_exit(result)` | `-> ProcessExit` | -| `gunbc.scm.log` | `repository_log(envelope)` | `-> CommitLog` | -| `gunbc.scm.status` | `repository_status(envelope, pending)` | `-> RepositoryStatus` | -| `gunbc.scm.repository_load` | `load_repository(path)` | `-> RepositoryLoad` | -| `gunbc.scm.repository_load` | `repository_load_refusal_lines(cause)` | `-> List` (takes `cause` ONLY, not `path`) | -| `gunbc.scm.repository_save` | `save_repository(path, repo)` | `-> RepositorySave` | -| `gunbc.scm.repository_envelope` | `empty_repository()` | `-> RepositoryEnvelope` | -| `gunbc.scm.repository_envelope` | `mint_repository_commit(...)` | `-> RepositoryCommitMint` | -| `gunbc.scm.repository_envelope` | `commit_at_reference(commits, reference)` | `-> RepositoryCommit?` | -| `gunbc.scm.checkout` | `checkout_repository_at(repo, reference)` | `-> RepositoryCheckout` | -| `gunbc.scm.authoring` | `empty_proposal()`, `author_requirement(proposal, requirement)` | `-> Proposal` | -| `gunbc.scm.object_store` | `empty_store()`, `store_contains`, `find_object`, `store_object_count` | — | - -## The gap, stated precisely - -**This was the baseline gap, and this change closes it.** As of the survey above, -`scm_log_cli_response` and `scm_status_cli_response` had NO consumer outside -`dag/test/claim/scm/scm_render_witness_test.dag` — the unwired-renderer state -`gunbc.cli_dispatch_surface` recorded: the answer computed and discarded because no host bound it. -`gunbc.scm.cli` is that consumer, and the `LANDED` section below carries its receipts. - -`dag/gunbc/cli_dispatch_surface.dag` declares an `scm` verb with an operand and `--path`, marked -`AbsentFromEmitMainRs`. - -## Two host shapes, and why the idiomatic one is not sufficient - -The corpus's established instrument shape is `fn check(subject) -> ProcessExit`, returning -`exit_failure(reason: join(lines, "\n"))` — see `tools.dag_compile_clean_gate` and its siblings under `dag/gunbc/instruments/`. `gunbc run` prints -that reason. **It can only emit text on FAILURE.** `log` and `status` must print on SUCCESS, so -that shape cannot express them, and returning `CliWireResponse` from `gunbc run` refuses (its -not-`ProcessExit` refusal prints the value, which is not a CLI). - -So the host binding is REQUIRED, and it is the piece to rebuild: - -1. DONE for read verbs. Still to do for WRITE verbs (`init`, `add`, `commit`, `checkout`): - they need `Document` builders, which only log and status have today, and `save_repository` - goes through `Filesystem.Write`, so they are host-effect entries rather than pure reads. - Original note: `dag/gunbc/scm/cli.dag` — one entry per verb returning `CliWireResponse`, composing - `load_repository` -> verb -> render, with a `RenderCapability { color: false, tier: Ascii, - cursor_addressable: false }` for a plain terminal. Write verbs (`init`, `add`, `commit`, - `checkout`) need their own `Document` builders; only log/status have them today. -2. Host side. **NOT the shape that landed, and deliberately so — this bullet is a FUTURE - ERGONOMIC SURFACE, not unfinished work required by the binding that shipped.** The original - plan was a `Commands::Scm` variant in `src/v1/stage0/src/main.rs` plus a `scm_verb` evaluating - the entry and writing `CliWirePrintable { bytes, exit }` to stdout. What landed instead binds - `CliWireResponse` ONCE in the generic `run_verb` outcome seam, so EVERY wire-returning entry - becomes reachable rather than only the scm family — one binding instead of one per verb. A - dedicated `gunbc scm` subcommand would be a nicer surface over the same answer; it is not - needed for the answer to reach an operator, and it is not part of this change. - -## LANDED — the read side reaches an operator - -`dag/gunbc/scm/cli.dag` (`scm_log`, `scm_status`) plus the `run_verb` outcome-seam binding. -Receipts, by execution against `dag/test/fixture/scm_repository_load/empty_repository.json`: - - $ gunbc run --entry dag/gunbc/scm/cli.dag --function scm_log --arg path= - no commits yet - exit 0 - - $ gunbc run --entry dag/gunbc/scm/cli.dag --function scm_status --arg path= - nothing checked out - 0 commits - nothing staged - - $ gunbc run --entry dag/gunbc/scm/cli.dag --function scm_log --arg path=/tmp/no-such-repo - cannot read repository at /tmp/no-such-repo - No such file or directory (os error 2) - repository unavailable - exit 1 - -The third is the load-bearing one: bytes printed AND a nonzero exit, the case an absorbing -implementation turns into silence or a spurious 0. - -### `init` — LANDED separately, via the create-only write (#10026) - -**init was CUT from the log/status change and landed on its own, as gunbc#10026.** The receipts -below were taken against the ORIGINAL prototype and are kept as its historical record; the shape -that actually landed differs, and the differences are the point. - -Why it could not ride along: the prototype observed an absence and then called `save_repository`, -which writes UNCONDITIONALLY. A racing actor between the observation and the write makes it -truncate the very bytes it claims to refuse — a TOCTOU the four-arm fold does not close, because -the fold decides on a fact that can stop being true before the write happens. Closing it needed an -atomic create-only write (`O_CREAT|O_EXCL`) as a new modeled file-transport verb, which is why it -became its own change with its own review. - -What #10026 landed, and what review then found still open in it: `Filesystem.WriteCreateNew` makes -the existence test and the creation one syscall, and `create_repository` routes through it. Review -`5089156132` then found six further defects in the init MODEL — among them that `ScmInitialized` -could contain a FAILED save, and that the `FilesystemEstablishedAbsence` authorizing the write was -matched as `_` and discarded while the actuation used an independently supplied path. Those are -being repaired in the follow-up on the same branch; do not read this subsection as describing a -finished init. - -`scm_init` takes a DIRECTORY and a NAME rather than a path, because presence is a fact about a -directory listing and recovering the directory by splitting a string would be a second, positional -naming scheme for something the caller already knows. It routes through -`extdeps.filesystem.filesystem_io` `filesystem_file_observation`, so absence is established from a -listing that succeeded and did not name the entry — never from a failed read, whose success channel -cannot separate absent from unreadable. Only the established-absence arm reaches `save_repository`. - -Two earlier revisions were fail-open and both were caught by external review before merge: the first -called `save_repository` unconditionally and NAMED the overwrite in a comment; the second proceeded -on `RepositoryFileUnreadable`, which is a guess about the host's permission model — a file can be -unreadable and perfectly truncatable — and again named the residual instead of refusing it -(review 58060 on gunbc#9864). - -Receipts, one per arm, `gunbc run --entry dag/gunbc/scm/cli.dag --function scm_init --arg -directory= --arg name=repo.json`: - - fresh directory [file] list … / [file] read … / [file] write …/repo.json (166 bytes) - initialized repository at /tmp/scminit/fresh/repo.json - - repository present refusing to initialize /tmp/scminit/existing/repo.json - a repository is already there, and init would replace its whole history - - foreign file present refusing to initialize /tmp/scminit/foreign/repo.json - a file is already there that is not a repository, and init would destroy it - and the file read back afterwards still holds its original bytes - - directory unlistable refusing to initialize /tmp/scminit/nodir/repo.json - the path could not be observed, so nothing about it is established -- - the directory /tmp/scminit/nodir could not be listed, so the absence of - repo.json is not established -- Permission denied (os error 13) - -The fourth is the load-bearing one: an observation that could not be made refuses rather than -widening to "nothing is there", which is the absorbing fallback DESIGN §5 names. - -Those init claims were hermetically enrolled in `test.claim.scm.scm_cli_witness` at the time — no -init refusal rendering as a success exit, the three refusals rendering differently, the unobserved -arm carrying the host's cause through. **They left with init and are NOT in this PR's witness**; -they travel with `scm-init-create-only`. Verified: `scm_cli_witness` on this branch contains no -init claim at all. - -### Evidence boundary, stated rather than implied - -Enrolled and executing in CI (`rust-unit-tests`, `cargo test -p v1-compiler --lib`): the five -`cli_wire_outcome_tests` covering the total map — bytes with the response's own exit, a rendered -answer that still fails, a renderer refusal not reading as empty success, the non-wire -fall-through, and the inherited `ExitFailure { code: 0 }` refusal. - -NOT enrolled: the end-to-end runs above are a MANUAL receipt. An integration test that executes -the binary would live in `src/v1/tests/`, which `clippy --all-targets` compiles and no CI step -runs (DESIGN: no CI step executes test targets outside `--lib`). A `.dag` witness cannot stand in -either — `scm_log` reads a file, and the SCM witnesses are `SubstrateInputsOnly`, refusing at the -hermetic boundary. So the class is `mitigatable` on the e2e path and the next-rung trigger is a -CI step that runs an integration target, not another test file. - -## Next increment: `add` and `commit`, and the one design step they need - -Still settled, because it is a fact about the HOST rather than about any object model: - -- **A host WRITE is permitted from `gunbc run`** — established by the init prototype above, which - created a file, and since confirmed by #10026 landing the create-only write. It is not a claim - that init's model is finished. The remaining write verbs are a modeling question, not a - permissions one. - -### SUPERSEDED BY #9891 — the recipe below is historical reasoning, not the current model - -**This section previously listed the following as "settled by execution". #9891 replaced the -identity model underneath it, so the recipe is now WRONG at exactly the boundary add/commit needs, -and it is kept only as the reasoning that led to the replacement.** It survived on main because a -plan that says "settled" is read as an instruction; that is the failure this heading exists to -stop. - -The superseded recipe said: content goes in via `store_node(store, n: Node) -> StoreOutcome`; a -file is built as a synthetic semantic `Node` via `node_synthetic`; a runtime name becomes a -`Symbol` with `symbol_intern_lexeme`; and -`mint_repository_commit(repository, root: ObjectId, ...)` requires `store_contains(root)`, so `add` -precedes `commit` and `commit` re-derives an added object's `ObjectId` by rebuilding the same node. - -**What #9891 changed, and why the recipe cannot survive it:** - -- Authored source is its OWN object arm, not a semantic node wearing a node costume. A file has - somewhere to live that is not a synthetic `Node`, which is the whole point of that change. -- A semantic-node reference and an authored-source reference are DISTINCT, so "rebuild the same - node and re-derive its `ObjectId`" no longer identifies one thing. -- `mint_repository_commit` takes a `SemanticNodeTarget`, not a bare `ObjectId`. Handing it an - authored-source identity is not a runtime refusal to be checked for — it does not typecheck. - -**SUPERSEDED — this paragraph became the dead recipe it warned about, and is corrected in place.** -It said `add` and `commit` are blocked on building a `CorpusManifestObject` shaped -`path → semantic_root → authored_source_identity`. Both halves are now false: - -- **The object exists.** `gunbc.scm.object_store` declares `CorpusManifestObject`, - `CorpusManifestRecord`, and `CorpusManifestEntry`, and `gunbc.scm.object_table_json` codes them. -- **Its shape is `{ path, source }` — path to AUTHORED SOURCE, and deliberately not to a semantic - root.** `object_store` states why: a semantic root is a derived ingestion result, and fusing it - into the entry would make a manifest unauthorable for malformed or not-yet-ingested source, which - is precisely the corpus a source-control system must still freeze. #10522 §5 ruled the same - separation — the authored snapshot is authoritative, structural interpretation is a derived view. - -**The actual boundary** is a *pending authored-source snapshot* authority shared by three consumers: -`add` updates it, `status` reads it, and `commit` consumes it. It is workspace-scoped and must -survive between invocations; it need not live in the repository document. - -Two constraints that paragraph got right and one it got wrong. Right: immediate object-store -insertion is not by itself the `add` model, and `ScmWriteOutcome` is still the right shape for the -verb's result. Wrong: `add` is not blocked ON THE MANIFEST OBJECT, which is the specific dependency this -paragraph asserted and which no longer exists. It remains blocked on real work — host acquisition of -file bytes, path admission, a durable selection to persist into, and conditional update of that -selection — and `status.dag`'s `StagedRole` is role-grain, so it is not that place. A removed -dependency is not a finished verb. - -**`status` moves with this cut rather than being an unchanged downstream reader.** Today -`scm_status` passes `empty_proposal()` and renders "nothing staged". Once source staging exists, -leaving that path in place would report "nothing staged" while authored-source changes are staged — -an observation correct about its input and wrong about the subject the user asked about. Paths must -not be translated into `StagedRole` to keep the existing renderer. - -**Staging carries the same conditional-write obligation as publication.** One `add` can race -another, and a `commit` can race a newer stage. Updates are conditional on the observed stage; a -commit consumes the exact staged snapshot rather than re-reading working files; and clearing after -commit consumes only the stage generation actually committed, so newer work is never erased. Under -the no-rebase workflow a moved target is a named stale-base condition, never permission to apply an -old stage to a different base. `save_repository` does not supply this — it checks encoding and then -performs an unconditional `Filesystem.Write`. - -Two further distinctions the verbs owe: an established unchanged candidate is not the same as an -unavailable or unreadable stage; and staged-versus-unstaged reporting needs a separate working-tree -observation, because comparing the stage to its base says nothing about whether the working files -changed again. - -### The design step — SUPERSEDED as a recipe; one principle survives - -**The composition below is no longer the instruction for the next increment, and the sketch under it -is unsound. Both are kept visible rather than deleted, because the sketch is the more instructive -half.** - -It said `add` composes `store_node` with `save_repository`, and `commit` composes -`mint_repository_commit` with the same save, over one outcome coproduct: - - type ScmWriteOutcome - = ScmWriteRepositoryUnavailable { cause: RepositoryLoadRefusal } - | ScmWriteRefusedByStore { collision: StoreOutcome } - | ScmWriteRefusedByMint { refusal: RepositoryCommitMint } - | ScmWritePersisted { save: RepositorySave } - -**Why the sketch is wrong: the outer classification and the inner payload can disagree.** -`RepositorySave = RepositorySaved | RepositorySaveRefusedByCodec | RepositoryFileUnwritable | -RepositoryWriteByteCountUnrepresentable`, so `ScmWritePersisted { save: RepositoryFileUnwritable { -path: "repo.json", error: "permission denied" } }` is constructible — a renderer selecting on the -outer `Persisted` arm announces persistence while its own payload says the file was unwritable. The -inverse is equally expressible: `ScmWriteRefusedByMint` accepts the successful `RepositoryCommitMinted` -arm. This is a fabricated-plausible-output shape, which makes it validation-where-construction-was- -available rather than a modelling nicety. - -**The governing requirement instead:** - -> A success outcome carries established success evidence for that exact operation. A refusal carries -> an actual refusal cause. Where publication cannot be established, that uncertainty remains an -> explicit outcome rather than being resolved into either. - -**What survives** is only the original motivation: compose operation outcomes without positional -ambiguity, so a renderer never encodes "which one failed" by argument position. **What does not -survive** is this coproduct, and also the composition itself — `store_node` does not preserve -authored source as authored source, and `save_repository`'s write is unconditional, so neither -supplies the staging contract stated above. Narrowing the payload to a successful save would not fix -that: an ordinary save and a *conditional* stage update are different guarantees. - -The shared result type is left undesigned here deliberately. A write-verb outcome invented at a call -site is the anemic modelling this repository keeps paying for, and so is one certified in a plan -before its operations have contracts. - -### `add` has no staging authority — SUPERSEDED, and its conclusion was the wrong branch - -This section's PREMISE stands and its CONCLUSION is withdrawn. The premise: `repository_status` takes -`pending: Proposal` as a parameter because what is staged is not a fact the repository document -carries, so a literal stage-now-commit-later has nowhere to persist to. That remains true, and it is -why `status` moves as part of this cut. - -The conclusion — that `add` therefore writes an object into the store immediately, with no staging, -and that this is "the honest reading of `add` for this substrate" — is **withdrawn**. It picked the -branch that was cheapest to build rather than the one that answers the question, and immediate -insertion is not staging: it cannot say which paths are selected for the next commit, and it cannot -be revised or cleared. The other branch was the right one — a staging authority has to exist first — -and it is workspace-scoped, not necessarily a repository-document member, so the premise never -implied its absence. - -## Constraints learned the hard way - -- A runtime name becomes a `Symbol` via `symbol_intern_lexeme(lexeme: name)` - (`v2.std.compilers.lexing`). `name as Symbol` REFUSES; an earlier conclusion that runtime names - were inexpressible was wrong. -- `RepositoryCommitRef` has no `ordinal` field. Read it with `minted_id_ordinal(id: ref.identity)` - and construct via `RepositoryCommitRef { identity: MintedId { ordinal } }`. -- Annotations inside a declaration body are parse errors; hoist them above the declaration. - An indented `//` is also a parse error — annotations are module-item grain only. -- There is no two-commit merge in the model. `gunbc.scm.role_requirement_integration` - (formerly `gunbc.scm.merge`, renamed because one spelling was carrying two materially different - contracts) integrates authored role requirements into ONE target commit; its vocabulary lives in - `gunbc.scm.proposal`. The operator's ask for practising merges/conflict resolution needs - commit-merge DESIGN first; it is not a wiring job. - -## Not in scope here - -Mirroring this repository's history through `gunbc scm` onto srv2 stays blocked on model work: -`ScmObject = SemanticNodeObject | AuthoredSourceObject | CorpusManifestObject` with one content -identity — **which now exists**; the manifest carries `{ path, source }` to authored source, NOT -path + semantic_root + authored_source_identity as this sentence originally said (see the corrected -boundary above) — and git correspondence confined to a mirror-layer -`GitMirrorCursor { source_commit, native_commit }`. diff --git a/docs/plans/scm-envelope-read-path-bounded-migration.md b/docs/plans/scm-envelope-read-path-bounded-migration.md deleted file mode 100644 index f4838e64bd9..00000000000 --- a/docs/plans/scm-envelope-read-path-bounded-migration.md +++ /dev/null @@ -1,53 +0,0 @@ -# The envelope read path: bounded, and the one-time rebuild to v7 - -Status: declared 2026-09-23. Authority: the production incident — -`belt_integrate_one_cli` over the `shell-typed-invocation` project envelope -(`attempt-state/projects/shell-typed-invocation.gunbc-repository.json`, a 104 MB -`gunbc-scm-repository-v6` monolith) exceeded ninety minutes at ~100% CPU and was -killed while reading; a rehearsal probe of the same read ran past two hours -before it too was killed. - -## What was unbounded - -The monolith decode admitted the object table entry by entry through the -per-object store verbs. `find_object` recomputes a content identity for every -already-held object on every question, and the insert copies the object spine -per append — so decoding an n-object table pays the square of n in interpreted -identity compares. At the 7,051-object project envelope that is ~25M identity -computations and the same again in spine copies; the read never completed. The -2026-09-21 intake measurement pinned the same shape (~50M compares holding one -belt tick inside a single seed commit for over an hour), and the JSON unescaper -additionally paid the square of each large source token (a 15 KB source cost -225 MB of accumulator copies) until the same day's span fix. - -## The fix - -One admission rule, two carriers. `gunbc.scm.object_store` now carries a -fold-shaped `StoreAdmission` accumulator (digest-keyed map, prepend-and-reverse- -once) beside `insert_admission`; the object-table decoder admits through the -accumulator, so the table decode is O(n log n) with the same idempotence and the -same collision arms. Object construction for sources, derived nodes, and -manifests is split into constructors behind the sole_constructor wall, and the -single-insert verbs are re-derived on them — one construction authority, one -admission rule. The sharded (v7) read keeps its own two linear passes (per-shard -decode, then `store_assemble_decoded`), now on the same accumulator. - -## The declared migration - -The production envelope is converted to the v7 sharded layout by the next -successful integrate over it — the integrate's save is already sharded, so the -conversion is a side effect of the first bounded run, not a separate tool, and -no monolith writer remains to recreate it. After that run: - -- every load is the sharded read: the index plus ~110 shard reads at the current production - scale (7,051 objects at a batch of 64), each decoded and admitted in linear passes; -- the v6 monolith reader stays in the tree for old documents and fixtures, but - it is no longer on any production path, so its remaining costs are bounded by - history, not by the read path this declaration governs. - -Rollback: the conversion writes the v7 index and shard files beside the -monolith's path; the pre-conversion monolith is not deleted by the save (the -sharded writer writes `.object..json` siblings and replaces the -index document's contents), so the v6 bytes remain recoverable from backup -state until the envelope's next writes settle. The load path dispatches on the -`format` tag, so a restored v6 document reads exactly as today. diff --git a/docs/plans/scm-merge-design.md b/docs/plans/scm-merge-design.md deleted file mode 100644 index c3f004388f1..00000000000 --- a/docs/plans/scm-merge-design.md +++ /dev/null @@ -1,571 +0,0 @@ -# SCM merge: the design ruling, recorded before implementation - -This document settles what merge IS in `gunbc.scm` before any merge code exists. It is written -first because a merge and publication consumer, once cut, fixes the vocabulary every nearby question is -then answered in — and a vocabulary cut against an unproven correspondence contract is the §3 -replacement-migration trap. - -**An earlier revision of this document opened by saying §5 decides the shape of -`CorpusManifestObject`. That was false and is corrected in §5:** that object already exists in -`gunbc.scm.object_store`, its shape is already ruled there, and it is not gated by this document. The -pending boundary is a consumer / commit-root cut, which is materially smaller. - -Nothing here is implemented. Where a question is open, it is marked open rather than resolved by -plausible reasoning. - -## 0. The workflow being served, stated exactly - -The operator's goal is **not** "resemble git". Git terms are familiar vocabulary; the interface is a -projection over the existing realizations (`gunbc.scm_compatibility.git` / `.mercurial` / `.pijul`). -The workflow is: - -- **squash-merge into main**, so an integration is ONE commit; -- **kill dev history after the merge** — the branch line is discarded, deliberately; -- **never rebase**; -- optimize for *simpler and faster merges*. - -Every ruling below is derived from that, and several of them differ from what a general two-parent -merge would need. Where a general merge would want a capability this workflow never exercises, the -capability is refused as authored redundancy (§2). - -## 1. What already exists, and must be consumed rather than duplicated - -**`gunbc.scm.ancestry`** supplies `ancestry_walk(start, history) -> AncestryWalk` with four arms: -`AncestryTraced`, `AncestryBrokenAt`, `AncestryStartNotInHistory`, `AncestryRevisitsACommit`. That -module deleted its own boolean ancestor predicate on the ground that `false` was answering for four -different facts, and it names this document's subject as the intended consumer: - -> When a real consumer arrives — merge-base, ahead/behind, a publication decision — it derives its -> answer from `ancestry_walk` and decides for itself what a broken chain means to IT. - -**So merge inherits an obligation, not just a function.** It must decide, per arm, what that arm -means to a merge, and it may not collapse them into a Bool or into one refusal. §4 does that. - -**`gunbc.scm.supersession`** supplies `HeadSlot { slot: String, generation: ObjectId }`, -`observe_generation`, and `supersede_head_slot` — a compare-and-swap whose evidence is minted from -the slot and cannot be supplied alongside it. Its subject is the **repository document generation**, -not a branch head. It is therefore a *precedent for the shape* of a named, CAS-guarded slot and -**not a reusable authority for commit refs**. Saying otherwise would be a nickname (§3). - -**`gunbc.scm.checkout`** supplies `checkout(store, commit) -> CheckoutOutcome`, already modeled with -its refusal population intact. - -## 2. RULING: no two-parent ancestry arm. A squash absorption is a receipt, not a parent. - -`CommitAncestry = RootCommit | DescendsFrom { parent }` is **already the correct shape** and must not -gain a `MergedFrom { left, right }` arm. - -The derivation is mechanical rather than a preference. A squash merge produces one commit whose -parent is the target's tip; the absorbed line is discarded by construction. So no commit this -workflow can produce has two parents, and an arm with no producer is what §4b names *worse than -absent* — it gets cited as a capability the model does not have. - -The stronger reason is that a second parent would be **actively wrong**, not merely unused. -`ancestry_walk` follows parents. A `MergedFrom` arm would make the walk traverse into the very line -the workflow exists to discard, so "kill dev history" would be false at exactly the layer that -answers questions about history. The feature would reintroduce what the workflow deletes. - -**But discarding the line must not silently discard the FACT.** If the squash commit records nothing -about what it absorbed, then "was this work merged?" is unanswerable, and an unanswerable question -that gets guessed at is the fabricated-plausible-output failure. The resolution is a distinction the -model can hold precisely: - -> A **parent** is a structural fact the ancestry walk follows. -> An **absorption** is a historical receipt the walk must NOT follow. - -So a merge commit carries a `MergeAbsorption` receipt naming the absorbed tip (and the derived base, -§4) as *data*, while its `CommitAncestry` stays `DescendsFrom { parent: }`. History -stays linear and traversal stays honest; the question "what did this absorb" becomes answerable -without reviving the line. - -### D1. The receipt is only real if a QUERY consumes it - -A receipt nobody reads is data, not evidence. This document therefore fixes the consumer before the -producer, because the producer's shape is determined by what the query must answer. - -**The query.** Given a source tip S and a target ref T: *does T's history contain a commit whose -absorption receipt names S?* It is scoped two ways and both are load-bearing: - -- **exact source.** The receipt names a specific absorbed tip. "Some commit from that line" is a - different, weaker question, and answering it while claiming the strong one is fabrication. -- **target lineage.** The answer is relative to T. A receipt sitting in a line T cannot reach does - not mean the work is in T. So the query walks T's ancestry (§4's arms apply, with the same per-arm - refusals) and inspects receipts along that chain — it does not scan the store. - -**Three facts stay distinct, and collapsing any two is the defect this section exists to prevent:** - -1. **Ancestry membership** — S is literally an ancestor of T. Under squash this is normally FALSE for - absorbed work, which is exactly why (2) exists. -2. **A committed absorption receipt** — some commit reachable from T recorded absorbing S. This is a - historical claim about an act, and it is the only one the receipt itself supports. -3. **Newly derived content comparison** — the content of S is present in T's current tree. This is a - fresh derivation over current state, not a receipt read. - -**(2) does not imply (3).** Work absorbed and later reverted still carries its receipt. So the query -answers *"was this integrated?"* and never *"is this effective now?"*; a consumer needing the latter -must derive it, and the outcome vocabulary must name which question it answered. Historical -integration and current effect are separately reported, never one boolean. - -**Survival of collection.** The query must remain answerable after the absorbed objects are collected -(the open question below). That is a constraint on the receipt's contents: it holds the absorbed tip's -identity and the derived base's identity as VALUES, so answering never requires dereferencing the -absorbed line. A query that needs the collected objects would make garbage collection silently -destroy history's answers. - -**Refusals.** Not-found is `NoAbsorptionRecorded`, distinct from `TargetHistoryUnwalkable { cause }` -carrying the §4 walk arm, distinct from `SourceNotInRepository`. "No" and "cannot tell" are different -answers. - -**Open:** whether the absorbed commits' objects are retained in the store or become unreachable. This -is a garbage-collection question with its own ruling, and it is deliberately not decided here. What -IS decided: the receipt names the absorbed tip whether or not that tip's objects survive, because a -receipt that silently becomes dangling is a worse artifact than one that names something collected. - -## 3. The minimum ref model, and why it is smaller than branches - -To merge, two lines must be designatable at once. Today `RepositoryEnvelope.checked_out` is a single -optional `RepositoryCommitRef`, so checking out the target loses the pointer to the work. - -The workflow bounds how much is needed, but two of the bounds this section first drew were deductions -the workflow does not license, and they are withdrawn rather than left to decide the model quietly: - -- **"Discarded at merge" does not mean "does not need to persist."** Retirement on success says nothing - about lifetime before success. A multi-command or resumable merge has a lifetime to preserve exactly - while it is unfinished, which is when the ref matters. -- **"Exactly one checked out" erases the empty and unborn repository.** `checked_out` is - `RepositoryCommitRef?` today for a reason, and a ref model that cannot express a repository with no - commits has dropped a state the current model holds. - -What the workflow does bound: no remote tracking, no upstream, no reflog, no branch-of-branch, no -rename. - -**Proposal:** the envelope holds a set of named commit refs, at most one of which is checked out — -"at most", because the withdrawal above governs and an unborn repository has none — -the minimum that lets two lines coexist. Naming is required (not merely a second anonymous slot) -because the merge outcome, the refusal diagnostics and the CLI surface all need to say *which* line, -and an anonymous "other slot" would force position to carry identity, which is the positional-naming -defect §3 rules against. - -**Open, and genuinely undecided:** whether merge should take its two refs as *arguments* — leaving -the ref set purely a CLI convenience — or whether merge should read them from the envelope. The -argument form is smaller and testable without a ref model at all; the envelope form is what makes -`merge ` expressible. My inclination is arguments for the operation and a ref set for the -surface, so the merge model does not depend on the ref model. Flagged for ruling. - -## 4. Base derivation: four walk arms, two sides, and the outcomes they force - -**The absorption query runs FIRST, and it does not walk the source.** An earlier revision of this -section put base derivation at the front, which left the §D1 counterexample alive at the consumer even -though the query beside it was correct. After M is published with parent T and a receipt naming S, S is -not on M's parent chain — so a re-request for S fell straight through to base derivation, and once S's -objects were collected the mandatory source walk refused before the surviving receipt was ever read. -**A correct query repairs nothing until the decision consumes it.** - -The ordering is therefore fixed: - -1. **Establish the target lineage and consult its committed exact-source absorption evidence, without - requiring the source objects.** This step needs the target's chain and the identity value carried in - each receipt; it dereferences nothing on the source side. -2. **Only if fresh reconciliation is actually needed** does the operation require the live source and - derive a base — and only then do the walk arms below apply to the source side. - -Two refusals get their place from that ordering. `SourceNotInRepository` refuses a NEW operation that -needs an unavailable source; it may never veto a historical receipt lookup merely because the source -was collected. And `NoAbsorptionRecorded` is supported only by a COMPLETE target walk that found no -matching receipt — an unwalkable target yields `TargetHistoryUnwalkable`, which is a different answer. - -**Identity continuity is a requirement, not a consequence of the bytes surviving.** Carrying the source -identity as a value fixes dereferencing; it does not by itself establish that the value still denotes -the same source. The receipt's subject is a source identity that is never reused for different content -within the scope the query answers over. Physical collection scheduling may stay open; identity -continuity may not be left to it. - -Step (2) is what the rest of this section specifies. `ancestry_walk` can answer four ways per side, and -this module owes a decision for each rather than one collapsed refusal. - -| what the walk says | what it means to a merge | -|---|---| -| `AncestryStartNotInHistory` | **the caller's fact.** A ref that is not in this repository. Refuse, naming which side. | -| `AncestryBrokenAt` | **damage.** The history is truncated; a base derived across a hole would be a guess. Refuse, naming the commit and its missing parent. | -| `AncestryRevisitsACommit` | **a cycle.** Refuse; the substrate is bounded and forward. | -| `AncestryTraced` (both sides) | a base can be derived from the two chains. | - -With both chains traced, the outcomes are: - -- **`SourceOnTargetParentChain`** — S is literally an ancestor of T. This is **ancestry membership, - and it is not receipt-backed absorption**; it is reported as its own fact. (`AlreadyAbsorbed` was - the earlier name and it was doing the work of two facts. Receipt-backed historical integration is - answered in step (1) above and never reaches this partition, so the four-outcome count here must - not be preserved by folding the receipt answer back in.) Both are also distinct from a - reconciliation that happens to compute an unchanged result, which is a **content** fact about a - fresh derivation and is evidence of no earlier integration whatever. - Neither answer silently suppresses an explicitly requested reapplication: a caller who asks to - reapply S after a revert is asking for step (2), and the historical receipt is not a refusal of it. -- **`TargetIsAncestorOfSource`** — the target's tip is in the source's chain. The target has not - moved since the branch started, so no content reconciliation is required. **This is now RULED - rather than open, because leaving it open left a loophole that defeats §2.** If publication is - allowed to advance T onto S's tip, then S's development commits become part of T's history — the - dev history the workflow exists to kill — and, worse, no absorption receipt is written, so §D1's - query returns `NoAbsorptionRecorded` for work that WAS integrated. Cheap reconciliation was - silently converted into a different publication. - The ruling separates the two: **reconciliation may be trivial; publication is uniform.** Merge - reports that no reconciliation was needed, and publication still produces a new commit M whose - `CommitAncestry` is `DescendsFrom { parent: A }` — the exact observed target tip — carrying an - absorption receipt naming S. M's root may be identical in content to S's root; that is a content - fact and does not license an ancestry shortcut. Fast-forward as a HISTORY operation is therefore - not available in this workflow, and the outcome name says "no reconciliation needed", not - "fast-forward". -- **`BaseDerived { base }`** — the lines diverged from a common ancestor. This is the real merge. -- **`NoCommonAncestor`** — two unrelated histories. Refuse; this is not a conflict, it is the absence - of a base, and reporting it as a conflict would misname it. - -A boolean or a single `MergeFailed` here would rebuild exactly the collapse `ancestry.dag` deleted. - -**One consequence for `gunbc.scm.ancestry`.** That module currently names its next-rung trigger as the -arrival of a consumer that merges two commits, on the expectation that such a consumer would force a -several-parents arm. §2 rules that expectation false: the consumer arrived and it does NOT want the -arm. That trigger is therefore superseded rather than satisfied, and it must be replaced rather than -deleted — a trigger removed with nothing in its place is an untracked stall (§4b(2)). The replacement -trigger this document proposes is the arrival of a workflow that must PRESERVE both integrated lines -as traversable history; until then `DescendsFrom` is the ceiling, not a stall. - -## 5. CORRECTED: authored source stays authoritative. Structural interpretation is a derived view. - -**The first cut of this section was wrong, and it was wrong in the way this document exists to -prevent.** It ruled that the merge subject is the semantic node graph and demoted -`CorpusManifestObject` to "a projection for emission, not the authority a merge reads". That -conclusion forked an authority this repository already holds, and it did so in a document whose -stated purpose is avoiding exactly that. The correction is recorded in place rather than quietly -rewritten, because a design note that hides its own reversal teaches the next reader nothing. - -**Two factual corrections come first, because the original section was reasoning from a false census.** - -1. `CorpusManifestObject` and `CorpusManifestEntry { path: NonEmptyStr, source: AuthoredSourceTarget }` - **already exist** in `gunbc.scm.object_store`, and `gunbc.scm.object_table_json` already encodes and - decodes them. This document's earlier framing of the manifest as unbuilt was wrong. -2. What has NOT changed is `RepositoryCommit.root`, which is still `SemanticNodeObjectRef`. **So the - pending boundary is a consumer / commit-root cut, not the creation of a missing object kind** — - materially smaller than "build the keystone", and it means `add`/`commit` are nearer than this lane - previously reported. - -**And the model had already ruled on the question §5 tried to settle.** `object_store.dag` states that -a manifest entry is path-to-authored-source *and deliberately nothing else*, because a semantic root is -a DERIVED INGESTION RESULT whose value depends on the source object, the corpus and its imports, the -ingestion rule, and language and rule versions. Carrying it in the entry would fuse an input with a -realization result and would make a manifest **unauthorable for malformed or not-yet-ingested source -— "precisely the corpora a source-control system must still be able to freeze."** That module names -the same comment-only-edit case used against §5 below. - -### The counterexample is information loss, not algorithmic difficulty - -Two authored snapshots differ only in a comment. §4c erases annotations from the semantic projection, -so **their semantic graphs are identical**. A merge handed only those graphs cannot determine which -authored change occurred. No choice of three-way graph algorithm recovers the distinction, because the -distinction was destroyed before the algorithm was reached. - -Authored placement adds a second obligation the graph cannot answer: where does a moved declaration -belong in the output, and what happens when independent additions select the same destination. Source -that cannot be ingested at all adds a third — it still needs an honest source-control disposition -rather than vanishing because the structural view is unavailable. - -### The separation this document now rules - -> **The authored snapshot is authoritative for what was authored. Structural interpretation and -> correspondence are DERIVED VIEWS used to reconcile changes. A merge produces a new authored -> snapshot, and must not silently discard distinctions its semantic view cannot represent.** - -That is not two authorities for one fact. *What was authored* and *an interpretation of what was -authored* are different questions, and §3's replacement-migration doctrine does not reach them: it -applies when X and Y answer the SAME semantic question and X is intended to disappear. Neither holds -here, and applying it anyway is what produced the error. - -Two consequences follow, and they are why the original binary was false: - -- **How a complete source snapshot is represented and frozen**, and **at what granularity changes are - compared and reconciled**, are INDEPENDENT choices. A path-to-source manifest can support structural - reconciliation; adopting a more conservative merge algorithm later does not inherently change the - manifest's shape. -- **So the manifest is not blocked behind merge.** The ordering constraint is narrower than this - document first claimed: *do not make the permanent merge and publication consumer depend on an - unproven correspondence or source-reconstruction contract.* Preserve the complete authored subject, - establish a bounded structural capability over it, and cut the consumer against the demonstrated - contract. - -If a lossless authored representation derived from a semantic graph is ever proposed, it is a -**materially different subject with a replacement proof to supply** — not something established by the -phrase "semantic node graph". - -### One correction to this document's own git comparison - -The original section claimed per-path merging means "the same path changed on both sides". That is not -git's general conflict predicate — git performs three-way merging of file *contents* and admits custom -merge drivers. So "two edits to different functions in one file" is not by itself a differentiating -demonstration, and it is withdrawn as one. The differentiator, if it exists, must be stated in terms of -correspondence guarantees (§5a), not in terms of a strawman. - -## 5a. The tractability boundary is CORRESPONDENCE and COMPOSITION - -An earlier revision of this section argued that `EdgeLabel = Named { name } | Positional` partitions -the merge problem, with named children alignable and positional children refused. That partition is -real and is retained below, **but it is not sufficient, and presenting it as the tractability answer -overstated it.** Alignment by label is one form of correspondence EVIDENCE; it is not correspondence. - -**`ObjectId` recognizes unchanged content. It does not identify one logical declaration across an -edit.** Three counterexamples bound the claim: - -- **Changed ancestors.** One side edits `f`, the other edits `g`. Both change their function's digest - and every enclosing digest up to the root. At root grain *both sides diverged*, so digest comparison - alone reports a conflict for two edits that never touched each other. Descent requires a justified - correspondence between components first. -- **Content identity is not occurrence identity.** Two logical occurrences may reference the same - stored subtree. Editing one must not edit the other, and a replacement keyed only on the old - subtree's content identity would hit both. -- **Renames and moves.** A name is evidence and a position is evidence, but neither is a stable - identity across precisely the change that alters it. - -And structural disjointness does not establish independence: one side may delete a declaration while -the other adds a caller, and two independently added declarations may collide on one name. The combined -graph still needs binding and admission judgments. **Behavioral compatibility is an additional claim, -never a corollary of touching different nodes.** - -### What the first capability must promise - -Deliberately narrower than arbitrary structural merge. **Once base-relative correspondence is -established**, the ordinary cases are straightforward: equal target and source agree; a side unchanged -from base yields to the changed side; otherwise descend only into components with an established -composition rule, or return an explicit contention or unsupported-correspondence outcome. **Absence -participates explicitly**, so deletion-versus-modification and competing additions cannot disappear -into a default. - -Three interface obligations, which this document fixes and the algorithm design later discharges: - -1. **What evidence establishes that these are the corresponding logical components?** -2. **What does the automatic arm guarantee about the combination it produces?** -3. **What does it return when either answer is unavailable?** - -The answer to (3) is a **named inability to decide** — never a guessed rename, never an automatic side -selection, and never a silent fallback to path-based merging. The `Positional` case above is one -instance of (3), not a separate mechanism: position is the only identity such a child has, occurrence -identity is deliberately outside content identity, and a sequence-alignment heuristic is what §4 rules -out in a closed system. - -### The performance claim is withdrawn until it has a denominator - -This document earlier implied structural merge serves the "simpler and faster merges" goal. That is -not established. The denominator is the whole operation — interpretation, correspondence and indexing, -the merge, source reconstruction, and admission — and `object_store` records its own list-backed -lookup and traversal as quadratic. **Content addressing does not by itself establish faster merging**, -and no speed claim should be made until measured against that full path. - -Also recorded: reducing the number of reported conflicts is not evidence of improvement, because a more -aggressive reconciliation can miss real conflicts. Any future claim here needs a discriminating -oracle, not a lower count. - -## 6. What merge produces, and what it must refuse - -A merge is a **candidate then a commit**, not one step: the candidate carries the derived base, the -absorbed tip, and — per §5's corrected output authority — the resulting authored snapshot, of which a -resolved graph is a derived view rather than the product. D5 below states the full binding. Committing -it is a separate act that advances the head. This -mirrors the shape `supersession` already establishes — observe, then compare-and-swap — and keeps a -merge from being authorized by anything other than the observation it rests on. - -Merge must refuse, never widen: an underivable base, a damaged history, an unrelated history, an -unresolved conflict. In particular there is **no "take ours" or "take theirs" fallback** available to -the merge itself. That is an escape hatch in §5's sense — a toggle whose only effect is to proceed as -if the refusal had not fired — and a resolution supplied by an author is a different, named input, -not a mode the merge can select on its own. - -### D5. The candidate is bound to the generation it observed - -"Observe, then compare-and-swap" is not enough on its own, because it does not say WHAT the swap -compares. A candidate is derived against a specific target tip; if the target moves between derivation -and publication, publishing the candidate silently discards whatever moved it — a lost update wearing -the shape of a successful merge. - -**An earlier revision routed publication through `observe_generation` and `supersede_head_slot`. That -was wrong at the level of the contract, not the field list, and it contradicted this document's own §1.** -`gunbc.scm.supersession` is a **malformed-document replacement policy**: its `standing_permission_refusal` -answers `none` for `LoadDocumentMalformed` **alone**, and returns `StandingDoesNotPermit` for every other -standing including `LoadComplete`. So an ordinary merge into a healthy repository would have reached -`StandingDoesNotPermit { standing: LoadComplete }` on that route — refused precisely in the normal case, -regardless of whether the generation matched. `SupersessionApplied` also yields a modeled slot and a -warrant; it does not install a commit or a receipt. - -**The policy must not be weakened to admit merge.** Its narrowness is the point. What `supersession` -supplies here is the SHAPE — observe, bind the observation, conditionally write — which §1 already -records as precedent, and merge publication owes its own admission contract over its own subject. Nearby -primitives exist and none is promoted by name alone: `std.durable_compare_and_set` declares its own -observation-to-slot binding boundary, and `save_repository` does checked encoding followed by an -**unconditional** write. Citing either does not establish this contract; this document fixes the -contract and does not implement the actuator. - -**The candidate binds the whole derivation, not a generation value.** A generation alone is not the -relation. The candidate carries: the repository and target slot; the exact observed target commit and -state; the source identity absorbed; the base evidence; the interpretation and rule inputs and any -author-supplied resolution; and — per the corrected §5 — the resulting **authored snapshot**, which is -the output authority, not a resolved graph alone. - -**Publication is conditional on that exact observed target**, and a mismatch refuses rather than -publishing; the candidate is then not publishable and must be re-derived. A candidate is not a value -that stays valid; it is a value indexed by the state it was computed from. - -**Successful publication binds the installed facts together.** The new commit M, its absorption receipt, -and the target advance become coherently available or none of them does — an uninstalled candidate or -receipt must never answer §D1's historical-integration query. Publication returns named success evidence, -and that evidence is what retirement consumes. - -These outcomes stay separately named, because each demands a different action. **The obligations -below govern, not their count** — `PublicationUnestablished` above is one of them, and publication- -success and retirement carry their own: - -- **`PublicationFailed { cause }`** — publication is ESTABLISHED not to have applied. Only an outcome - that establishes that may carry this meaning; a realization that installs the state and then loses - its acknowledgement must return `PublicationUnestablished` instead, and be reconciled rather than - replayed. Either the realization guarantees the ambiguity cannot arise, or the unestablished outcome - is preserved. A further attempt stays conditional on the exact observed target — "retry" never means - blind replay. -- **`TargetGenerationStale { observed, current }`** — the target moved. Re-derive; do NOT retry. -- **`WrongTargetSlot { expected, actual }`** — publication was attempted against a slot other than - the one observed. This is a caller error and must not be absorbed into staleness. -- **`SourceMovedSinceDerivation { observed_tip, current_tip }`** — the source line advanced after the - candidate was built. The candidate absorbs less than the caller now means by "the source". - -**Retirement checks the source AT RETIREMENT TIME.** Naming the absorbed source generation is necessary -and not sufficient, because this ordering is possible: derive a candidate for S, publish M successfully, -the source ref advances S → S₂, then retirement runs. Deletion is therefore conditional on that same -source slot still naming S. Otherwise the operation discards S₂ after honestly publishing only S. - -The outcome when it does not hold is **`IntegrationCompletedRetirementWithheld { published, source_moved_to }`** -— integration succeeded and the source moved — which is neither a deletion of newer work nor a claim -that integration failed. Retirement also consumes the successful-publication evidence above and never a -derived candidate: retiring on a candidate that then fails to publish destroys the line and integrates -nothing. And later collection respects other live references — absorbing one line does not authorize -deleting content something else still holds. - -## 7. The projection obligation - -`gunbc.scm_compatibility_shape` already carries what the operator asked for: opaque realization and -target handles, honest capture fidelity, projection plus independent read-back, and — its own words — -*"dispatch is a generic handler value, never `ScmKind` or a vendor switch. A fourth realization -supplies one handler and does not edit this module."* - -So merge does not get to invent a second presentation path. Whatever merge exposes must project -through that shape, or it becomes a parallel authority for "how this repository is presented to a -foreign realization" — the §3 fork, in the one place the operator explicitly asked for a projection. - -## 8. Author and timestamp: a bounded metadata contract, neither absent nor nearly finished - -This section has now been wrong in **both** directions, and both errors are recorded because the pair -is more instructive than either. - -**First error — claimed absence.** It said this repository has no modelled clock and no modelled -process identity, and pointed the lane at acquiring them. False: `extdeps.clock` declares `Clock.Now`, -`gunbc.clock_read` `clock_now_probed_at` consumes it, and `gunbc.worker_lifecycle` -`ProcessIdentityEvidence = ProcessIdentityObserved { pid, started_at, owner } | ProcessIdentityUnobserved -{ reason }` carries the other half. Directing work at re-inventing an available capability is the §2 -re-invention this project exists to prevent. - -**Second error — claimed the observation side was solved.** That was the same defect one level up. -Finding an evidence type is not a solved observation capability, and the specific trap is visible in -the helper: `probed_at_word` returns a `NonEmptyStr` on **both** arms, rendering absence as the word -`clock-unreadable`. So **accepting a rendered nonempty value would not establish that an instant was -observed.** That is not a defect in the helper — it is a display renderer doing its job — but it means -display text is not admission input, and a clock read is not automatically a timestamp bound to this -commit. - -### The bounded statement this section actually supports - -> Existing clock operations and adapters are reusable, and a process-identity evidence vocabulary -> already exists. Commit metadata still requires **an applicable observation producer**, **binding to -> the commit and its declared attribution role**, **a defined timestamp event**, and **an admission -> policy for unavailable evidence**. This document does not establish that the complete -> commit-attribution observation path exists, and does not direct the creation of another generic -> clock or identity schema. - -`gunbc.scm.ancestry` carries the first error in its own note; this document relayed it rather than -checking it, and both are corrected together. That annotation's retained conclusion — that modelling -these fields here would mean *inventing two facts to fill fields* — is also corrected: an unavailable -or unverified binding is a reason not to fabricate a value, and is not proof that modelling the fields -over existing observation carriers inherently fabricates anything. - -### What the four open pieces mean, and their order - -Each is a real question, and **they are ordered**: the admission policy comes last because it cannot -be decided before the field's subject and applicable evidence are. - -1. **Producer** — which operation observes the fact this commit's field claims, invoked where. -2. **Binding** — that the observation attaches to *this* commit and to a declared attribution role. - Reusing an observation means retaining the operation's failure contract, not its rendered output. -3. **Timestamp subject** — *which event* the field describes. Authoring, integration and publication - are different instants, and a commit that does not say which one it names is not carrying a fact. -4. **Admission** — whether a commit is publishable with the evidence unavailable. - -On (4), this document withdraws its earlier lean. "A merge receipt with no author is weak" does not -determine the policy: the existing source / base / target / result relationship already expresses a -meaningful integration fact, and attribution and time support *additional* claims. A contract requiring -an identified integrating actor should refuse when that actor cannot be established; a different -contract could admit an explicitly absent attribution. **Neither may render absence as observed -evidence** — that is the constraint, and it is the one `probed_at_word`'s two arms illustrate. - -One composition obligation with §D5: a missing-metadata check must run where it can refuse *before* a -write, because a missing observation discovered after a write does not turn an applied publication into -one established never to have applied. - -## 9. The evidence this design owes when it is built - -Stated now so the implementation cannot be declared done by typecheck (§5's -specification-without-execution): - -- one control per `AncestryWalk` REFUSAL arm, per side — `AncestryStartNotInHistory`, - `AncestryBrokenAt`, `AncestryRevisitsACommit` — each reaching a distinct, named merge refusal; - `AncestryTraced` is the success arm, and the controls it owes are the four traced/traced outcomes - below rather than a refusal; -- the traced/traced partition discriminated four ways: `SourceOnTargetParentChain`, - `TargetIsAncestorOfSource`, `BaseDerived`, `NoCommonAncestor` — each from a real merge and from each - other, and `SourceOnTargetParentChain` additionally discriminated from the step-(1) receipt answer, - since those are two facts and the earlier `AlreadyAbsorbed` name held both; -- a merge whose result is asserted by CONTENT, not by "it succeeded" — the mutation that returns an - empty or unchanged graph must go red; -- a refusal that stays a refusal: a conflict specimen with no author-supplied resolution must not - produce a commit; -- the absorption receipt asserted to be present AND the ancestry asserted to remain single-parent, - because the whole ruling of §2 is that those two facts coexist; -- **D1** — the receipt query answered on a target whose chain CONTAINS the absorbing commit and on one - whose chain does not, plus `NoAbsorptionRecorded` discriminated from `TargetHistoryUnwalkable` and - from `SourceNotInRepository`; and the absorbed-then-reverted specimen, which must report integrated - historically and not-present currently, since collapsing those two is the defect §D1 names; -- **D2** — the no-reconciliation-needed case asserted to produce a commit with `DescendsFrom { parent: - A }` and an absorption receipt naming S; the mutation that advances the head onto S's tip must go - red, because that mutation is precisely the loophole; -- **D4** — a comment-only-edit specimen whose semantic roots are equal and whose authored snapshots - differ, asserted to preserve the authored distinction; a changed-ancestors specimen (`f` and `g` - edited on opposite sides) asserted NOT to report a root-grain conflict; and a shared-subtree - specimen where editing one occurrence leaves the other unchanged. Each is a discriminating red for - a correspondence claim, not a demonstration that a merge algorithm exists; -- **D1 at the DECISION, not only the query** — a repeated exact-source request after M is published, - asserted to answer from the receipt rather than proceeding to another base-derived merge; the same - with the source objects COLLECTED, which must still answer (the control that would have gone red on - the earlier ordering); the same source requested against a DIFFERENT target lineage; a never-absorbed - source whose fresh reconciliation happens to compute an unchanged result, asserted NOT to report - historical integration; and a later target commit asserted not to disturb either answer; -- **D5** — a candidate published against a moved target asserted to refuse, discriminated from - `PublicationFailed`, `WrongTargetSlot` and `SourceMovedSinceDerivation`; **ordinary publication into a - HEALTHY repository asserted to succeed**, which is the control the superseded `supersede_head_slot` - route would have failed with `StandingDoesNotPermit { standing: LoadComplete }`; commit, receipt and - target advance asserted coherently available together, with an uninstalled receipt asserted not to - answer the D1 query; source movement AFTER publication but BEFORE retirement asserted to yield - `IntegrationCompletedRetirementWithheld` and to leave S₂ intact; and a failed publication asserted to - leave the source ref unretired. - -## 10. What this document does not claim - -It does not claim a structural merge algorithm is BUILT, and §5a's tractability finding is derived from the node model rather than demonstrated by a running merge. It does not settle the ref model (§3 open) or object retention for absorbed lines (§2 open), or author and -timestamp (§8). It does not claim a bounded structural correspondence capability is feasible — §5a records that as an -obligation to demonstrate, not a result. Its performance claim is withdrawn (§5a) rather than weakened. - -It does not authorize a merge implementation to be built. What it settles is the vocabulary those -consumers must preserve. **It does not gate `CorpusManifestObject`**: that object exists today in -`gunbc.scm.object_store`, this document's earlier claim to the contrary was false, and the ordering -constraint §5 actually establishes reaches only the permanent merge and publication consumer — not the -authored-source primitive, and not `add`/`commit`. diff --git a/docs/plans/scope-rank-view-design.md b/docs/plans/scope-rank-view-design.md deleted file mode 100644 index d4f8b79734a..00000000000 --- a/docs/plans/scope-rank-view-design.md +++ /dev/null @@ -1,248 +0,0 @@ -# The scope rank-view — resolve a scope instead of materializing one - -The seed builds a fresh set of name-keyed maps for every claim scope it enters. `[floor-scope-cost]` -already names the correction in the tree, beside the number it prices: *"a scope that is a view -rather than a rebuild costs nothing to enter."* This is the design for that view. - -It is a **replacement migration** (DESIGN §3): the per-scope materialized maps and the rank-view -answer the same semantic question — *which declaration does this name resolve to, in this scope* — -and one of them is intended to disappear. The model lands first, alone; the reader cutover follows -in its own PR, at the root, with no interval in which both representations are live authorities. - -## 1. What is materialized today, and what of it is scope-dependent - -`cli_run` `claim_scope_for` computes the scope's module `order` (own module, then the compiler's -precedence-ordered import closure, then the reference closure) and then builds two things over it: - -- the scope `item_registry` — a union of each scoped module's own registry, folded in `order`, - first write wins, with the authored-region rule deciding which collisions count as ambiguous; -- `v1.interpreter` `build_scope_indexes_with_module_order` — `fn_nodes` (a bare slot per name and a - qualified slot per declaration), `ambiguous_bare_function_names`, `file_module_paths`, - `file_import_bindings`, `service_ops`. - -gunbc#9809 removed the module-local half of that derivation: `ModuleScopeFragment` derives each -module's contribution once per prepared subject rather than once per scope containing it. What -survives is exactly the **fold** — the part that is not module-local, because it applies the scope's -precedence to fragments the subject already holds. - -The observation this lane rests on is that the surviving fold stores a *decision* that is cheaper to -compute than to store. Each entry of each per-scope map is the answer to "of the modules declaring -this key, which one does this scope rank first". That answer is a function of two things the process -already has: the corpus-wide set of declarers, and the scope's own ordering of modules. Materializing -it once per scope is DESIGN §2 redundancy in its plainest form — 660 copies of a table derived from -one corpus and 660 short lists — and DESIGN §3's second-authority problem, because from that point on -the map, not the order, is what a reader consults. - -## 2. The view - -Two carriers replace the maps. - -**Corpus-wide, one per prepared subject.** A `name -> declarers` index, each declarer carrying the -declaring module's identity alongside the declaration node, in a stable corpus order. This is the -fragment population of #9809 inverted from per-module lists into per-name lists; it introduces no -new derivation and no new authority, only a different join order over facts the subject already -carries. - -**Per scope.** A `module -> rank` map, where rank is the module's position in `order`, plus the -`authored_region` boundary already computed there. Its size is the scope's module count (mean 427, -max 878 at the measured run), not its item count. - -**Lookup.** Resolution intersects the two: take the key's declarers, keep those whose module has a -rank in this scope, and select by the slot's polarity. Nothing is materialized at scope entry beyond -the rank map, and every answer is derived at the ask. - -## 3. The polarity finding — the slots do not agree, and a uniform cutover is silently wrong - -The brief names the rule as "minimum rank". That is correct for the slot the prize is denominated in -and **not uniform across the maps being replaced**, which is the first thing this model exists to -record: - -| slot | seed rule | rank rule | -|---|---|---| -| `fn_nodes` bare | `or_insert` under `module_order` | lowest rank wins | -| scope `item_registry` | first-write-wins over `order` | lowest rank wins | -| `file_import_bindings` | `or_insert` over the fold | lowest rank wins (key is module-local, so no scope can discriminate) | -| `fn_nodes` qualified | unconditional `insert` | exactly one declarer; **admission-gated** (§4) | -| `file_module_paths` | unconditional `insert` | highest rank wins | -| `service_ops` | unconditional `insert` | **highest rank wins** | - -`service_ops` is the inversion that matters: a service operation key claimed by two scoped modules is -today the *last* module in precedence order, and a rank-view that resolved it by minimum rank would -silently redispatch it. The tree already carries the receipt for why this key is dangerous — the -`std.resources` `Filesystem` / `extdeps.filesystem` `Filesystem` pair, whose registry-merge collision -once dropped a service's operations into "unknown service operation" at runtime. - -`file_module_paths` is last-write-wins over a key that is module-local in practice (one module per -file), so no current input discriminates the polarity. The model files it at the seed's polarity -rather than at the one a reader would guess, and names what would discriminate it, because "no input -reaches it" is a ceiling to record, not a licence to pick either arm. - -## 4. The hard constraint, and the rung it puts at risk - -On record for this lane: *a scope's qualified `fn_nodes` must never resolve a name outside the -scope's admitting closure — a claim must not call a module its closure never admitted.* - -Today that holds **by construction and incidentally**: the qualified slot exists only because a -scoped module wrote it, so an out-of-scope declaration has no key in the map. The rank-view removes -that construction. The corpus-wide index holds every declarer in the subject, so an unguarded -qualified lookup would answer from a module the scope never admitted — a strictly new failure, and -one that fails in the direction that looks like success. - -So the migration's obligation is not to add a filter but to keep the guarantee structural: resolution -must have no arm that yields a declaration without having consulted the rank map, so an unadmitted -answer has no constructor. Concretely, the resolver returns only a declarer obtained *through* rank -membership; there is no accessor that reaches the corpus index and returns a node. DESIGN §3's rule -for a replacement migration is the same point stated generally: the minimum Y is not the smallest -thing that executes the happy path, it must preserve every required refusal. - -The model records this as a §4b subject with its previous rung, its target rung and the evidence that -would establish either — not as prose, so that a cutover which reaches the prize while lowering the -rung is a red row rather than an unnoticed regression. - -## 5. What the view does not make free — the enumerating readers - -Point lookups and membership tests become O(declarers of the key). Four readers instead **enumerate** -the scope registry, and for them the rank-view trades a cheap iteration over a scope-sized map for an -iteration over a corpus-sized one: - -- `v1.interpreter` `build_initial_env` — per claim, over data items; -- `v1.interpreter` `eval_data_initializer_values` — over data items; -- `v1.interpreter` `resolve_published_mock_keys` — over data items; -- `cli_run` `roster_entry_registry_cache` — over every key. - -The first three filter to `DataItem` immediately, so the view serves them from a kind-partitioned -corpus name list and iterates a fraction of the corpus rather than all of a scope. The fourth wants -every in-scope name and is the one row where the view is not obviously cheaper; it is named here so -the cutover measures it rather than discovering it, and its call frequency is the fact the migration -PR must carry. - -This is the design's honest boundary: the prize is scope *entry*, and a reader that enumerates a -scope pays at the ask what it no longer pays at entry. A cutover that quotes the entry saving without -this column would be reporting half a ledger. - -## 5b. The migration census — every site, its demand, and its cut order - -The brief sizes the reader migration at "~126 `item_registry` sites". That is the grep count over the -token across `src/`, and it is not the migration population: **most of those occurrences are a -different carrier's field of the same name.** Filing them separately is the first thing the census -does, because a cutover scoped to the grep would edit the typecheck layer and the emitter for no -reason, and a cutover that ignored the difference would edit the wrong authority. - -### What is not in the population (112 of 126) - -| kind | where | count | why it is not the subject | -|---|---|---|---| -| `TypedModule.item_registry` | emitters, `coproduct_reflection`, `owned_data`, `data_initializer_identity`, `v1_compiler_compile` | ~18 | the per-module registry — the authority the corpus index is *derived from*, and the one the fragment population of #9809 already reads. Untouched. | -| typecheck-layer `scope`/`fn_scope`/`lam_scope`/`local`/`state`/`dep_state`/`ctx` registries | `v1_compiler_infer` | ~30 | `v1_compiler_infer`'s own threaded registry at compile time. A different carrier with the same field name; it never sees a claim scope. | -| field declarations | `v1_compiler_infer`, `v1_compiler_infer_items`, `v1_interpreter` | 8 | type declarations, not reads. | -| empty-map struct initializers | test fixtures and empty contexts across 9 files | ~12 | `Rc::new(HashMap::new())`; nothing is read. | -| `item_registry_keys` | `v1_compiler_artifact`, `v1_compiler_compile` | 5 | a different field: the serialized key list of an artifact. | -| doc-comment mentions | `cli_run`, `v1_interpreter`, `coproduct_reflection` | ~9 | prose. | -| `build_qualified_item_registry` and its duplicate marker | `v1_compiler_emit_rust` | ~10 | the emitter's own qualified overlay, with its own fail-closed refusal. A separate authority on a separate key. | - -### The population (14 readers, 4 construction sites) - -Demands, and the arm each falls in. The fourth arm is one the reader census did not originally -carry and this sweep found: - -**Provenance test** — the reader looks the name up *in order to read `info.module_name`*, and decides -a builtin dispatch from it. These are the sites where a polarity error does not surface as a missing -name but as a **different function executing**, so they are the ones the qualified-admission RED and -the ambiguity line must both be exercised against: - -| site | decides | -|---|---| -| `v1_compiler.v1_interpreter` `try_witness_evaluation_dispatch` | whether a call is the witness-evaluation authority's own | -| `v1_compiler.v1_interpreter` `is_v4_bridge_family` | whether a name is one bridge family's, by declaring module | -| `v1_compiler.v1_interpreter` `is_v2_std_collection_map_grounded_fn` | whether a call takes the primitive map grounding | -| `v1_compiler.cli_run` `definer_module_for_name` | the declaring module of a name — on a `ResolvedGraph` argument, so whether it is in the population depends on which graph its callers pass, and that is the one row this census leaves open for the cutover to close | - -**Point lookup** — `v1_compiler.v1_interpreter` `eval_var` (the registry slow path behind the env -lookup) and `eval_data_item_value`. Both already re-resolve through `lookup_fn_from` immediately -after, so under the view they collapse into one resolution rather than two. - -**Membership test** — five sites, all `contains_key` guarding a call into a modeled function: -`call_test_claim_fn_bool`, the three `call_floor_kernel_would_skip` / `call_floor_row_would_skip` / -`call_floor_row_precompute_would_skip` guards, and `rerun_frontier_nodes_for_entry`. Each -becomes `resolve(name).is_some()`. - -**Enumeration** — the four readers of §5. - -**Construction** — `v1_compiler.cli_run` `claim_scope_for`'s registry fold (with its `winner_of` / -`ambiguous` bookkeeping) and `v1_compiler.v1_interpreter` `build_scope_indexes_with_module_order`. -These are the root: they are deleted, not migrated. - -Plus one site that looked like the hardest and is not. `v1_compiler.v1_interpreter` -`InterpContext::resolved_graph` hands out a whole `ResolvedGraph` built from the scope map, which -would force materialization to survive the cut for any consumer of it. **It has no consumer** — no -call site anywhere in `src/`. It is deleted with the maps rather than being the reason to keep them. - -### Cut order - -1. The resolver and the rank map land, and the **provenance tests and point lookups** move first — - they are the sites that read the winner rather than merely its presence, so they are where a - polarity or admission error is loudest, and moving them first means the discriminating REDs run - against the arm most likely to be wrong. -2. The **membership tests**, which are the same resolution asked for less. -3. The **kind-filtered enumerators**, which need the corpus kind partition to exist. -4. `roster_entry_registry_cache`, last, and only after its call frequency is measured — it is the one - reader the view does not obviously make cheaper. -5. The **construction sites and `resolved_graph`** are deleted in the same motion, which is what - makes this a root cut rather than a leaf-first refinement. Nothing in steps 1–4 may be merged with - the fold still standing: a surviving map is the attractor, and readers understanding both - representations is the parallel-authority state the migration exists to avoid. - -## 6. The scope population — 660 and 1,155 come from one instrument at two revisions - -The brief asks that the delta between 1,155 and 660 be reconciled rather than assumed. Both are -readings of the **same instrument**: the required floor's -`floor: N scope construction(s) for M distinct scope(s)` line, whose `M` is the distinct-scope count. - -- **660** is a current reading, from a named required-floor job on the #9809 branch, where - `[floor-scope-split] constructions=660` and `M=660` agree — constructions equal distinct scopes, - so the per-claim rebuild is already gone. -- **1,155** is a *transcription* of an earlier reading of that same line, sitting in the - `PreparedScopeIndexes` doc comment in `v1.interpreter` inside the sentence that records the - per-claim-rebuild finding ("9,573 reconstructions of maps that only 1,155 distinct scopes can - differ in"). Its companion figure, 9,573 constructions, is the one that has since been repaired to - equal the distinct count. - -So the delta is not two instruments disagreeing and not a scope-universe difference: it is one -instrument read at two revisions, with the older reading copied into prose. Whether `M` fell from -1,155 to 660 because the corpus moved, because the claim manifest moved, or because an intervening -lane narrowed the closure is **not decidable from either figure**, and this design does not guess — -the question is settled by re-deriving `M` on the current tree, which is precisely the "before" half -of §7's paired run. Naming that as the answer rather than picking one of the two numbers is DESIGN -§6's rule applied to the reconciliation itself. - -The transcription is the mechanism that made a stale reading look like a rival present-tense -measurement. **This PR deletes it**, replacing the literal pair in the `PreparedScopeIndexes` doc -comment with the name of the line that produces it. The number is not updated — updating it -re-commits the defect at a fresher value. The model keeps the row, because the reconciliation is a -finding about how the two figures were produced and survives the comment that carried one of them. - -## 7. Acceptance - -Paired required-floor runs, before and after, same instruments: - -- identical claim population, results, verdict and digests; -- identical scope identities (the `hash_combine` fold over `order` — the rank map is derived from the - same `order`, so a moved identity means the closure moved, not the view); -- `[floor-bare-name-ambiguity]` **byte-identical between the pair**. The line moves if and only if - bare-name precedence forks, which is the exact failure a rank-view can introduce. -- `[floor-scope-split]` as the prize measurement: the `indexes` and `registry` terms go to - approximately the rank-map build, and `order` is untouched. - -The oracle is the paired run, never a literal (DESIGN §5): the relayed figures for the ambiguity line -were measured on a pre-#9809 corpus and are re-baselined by the "before" half of the pair, not -asserted. - -## 8. Order of work - -1. **This PR — the model only.** `gunbc.scope_rank_view` and its executing claim. No reader moves. -2. The rank-view carriers land beside the fold, with the equivalence law executing over a fixture - corpus whose two entries rank one colliding bare name oppositely — the discriminating RED that - `scope_fragment_memo_equivalence` established the shape of for #9809. -3. The root cut: the materialized maps are deleted and every reader is fixed forward onto the view in - one motion. Not leaf-first — a surviving map is DESIGN §3's attractor, and readers that understand - both representations are the parallel-authority state the brief forbids. diff --git a/docs/plans/seed-string-decode-census.md b/docs/plans/seed-string-decode-census.md deleted file mode 100644 index acf26ecd6cf..00000000000 --- a/docs/plans/seed-string-decode-census.md +++ /dev/null @@ -1,608 +0,0 @@ -# Seed string-decode census — the authority/realization coupling is spelling-keyed - -**Subject.** The Rust seed reads values produced by `.dag` authorities through the interpreter and -addresses them BY SPELLING: type name, variant name, field name and entry-function name are string -literals in Rust. The seed and the authority share no Rust type, so a rename on the `.dag` side has -NO COMPILE-TIME LINK to the decoder that depends on it. gunbc#9975 discovered this by accident when a -migration renamed what `local_repo_wet_schedule` reads. This document reports the population, the -mechanism, and the §4b ceiling. It repairs nothing and enrols nothing. - -> **SCOPE, BEFORE ANY NUMBER BELOW.** Every count in this document was discovered by MATCHING A -> SPELLING, and the population it ranges over is defined by an ABSENCE — decode sites with no -> compile-time link. A search cannot enumerate an absence: a site written in a spelling the search did -> not anticipate is indistinguishable from a site that does not exist. **Every number here is an -> ESTIMATE WITH NO KNOWN SIGN.** Re-derivation with a parser moved P4 by +68 (42 → 110) and moved P7 -> in BOTH directions at once (`--function` 35 → 30, `--entry` 6 → 8): a line-keyed instrument misses -> what formatting separates and over-claims what proximity suggests. Per-primitive status — measured, -> or unmeasured — is in the table's own column. - -**What this document turned out to be.** It began as a census of a seed defect. The P6 incident below -— the class reproducing in a different language, against a different file, within the hour, on the -author repairing it — shows that it is not seam-specific: the class needs only a substitution whose -failure arm widens, and not the seed/authority seam at all. So what is measured here is ONE SEAM of a -primitive that is not seam-specific, and the population below is a FLOOR rather than a ceiling. - -Per DESIGN §7 the finding is not "the seed is wrong" — a realization lagging its authority is the -expected state. The finding is that the COUPLING is spelling-keyed, which is a safety property -independent of how much seed remains. - -## Deriving the census - -Subject population: the 90 hand-written `.rs` files under `src/v1/stage0/src` — every file whose -first line is not `// Generated by v1 compiler`. Generated mirrors are excluded on purpose: they are -re-emitted from `.dag`, so a rename propagates into them and this class cannot arise there. - -Right-hand side of the join: declarations scanned out of the 4,617 `.dag` files under `dag/`, -`src/v1`, `src/v2`, `tools/`, `test/`, `fixtures/`. **This is a declaration scanner, not the -compiler's own namespace authority**, and it is keyed on the BARE name — so the authority attribution -below is an upper bound wherever a name has several declarers (8 of 79 do: `Refused` ×7, `Failed` ×6, -`Present` ×4, `Done` ×3, `ExitSuccess`/`None`/`Suppressed`/`Empty` ×2). 70 of 79 names have exactly -one declaring module; those attributions are exact. - -## The decode primitives — seven, and each pass that widened the search found more - -The set of PRIMITIVES is much narrower than the set of call sites, and it is what makes the census -closeable. **P1–P5 bottom out in four `InterpContext` methods** (`sym`, `sym_eq`, `field`, `resolve`), -declared together in `v1_interpreter`. **P6 and P7 do not touch `InterpContext` at all** — P6 is a -`str::replace` over file text and P7 is a subprocess argument vector — and that is precisely why they -were missed: every earlier sweep was keyed on the interpreter surface, so a name crossing into `.dag` -by any other route was outside the search by construction. The primitives are grouped by the ROUTE a -name takes, not by a shared implementation. - -| # | primitive | shape | sites (hand `.rs`) | how counted | -|---|---|---|---|---| -| P1 | `ctx.sym_eq(sym, "Name")` | type/variant name test | 87 (79 distinct names) | **argument parser — gap measured, 0** | -| P2 | `ctx.field(fields, "name")` | field lookup by literal | 110 (62 distinct names) | **argument parser — gap measured, +7 over the line regex** | -| P3 | `ctx.resolve(sym).as_str()` matched against string-literal arms | rendered-name dispatch | 61 | line regex — LOWER BOUND, gap unmeasured (match arms rarely wrap, so the gap is likely small; that is a guess, not a measurement) | -| P4 | `run_in_context(ctx, "entry_fn", …)` | call-by-name into the authority | **110 literal, of 135 call sites**; the other 27 are enumerated one-by-one below | **argument parser — gap measured, +68** | -| P5 | file-local wrappers (`field_str`, `field_value`, `field_list`, `variant_field`) over P1–P3 | indirection layer | 29 over those four names | argument parser for those four names; the WRAPPER SET itself is a lower bound — wrappers were found by naming convention, so a wrapper named otherwise is invisible | -| P6 | `str::replace` rewriting a live-HEAD `.dag` file's own text | spelling-keyed EDIT, not lookup | 5 (3 + 2, in 2 producer fns reached from 6 call sites) | read exhaustively in 1 file — complete FOR THAT FILE; `str::replace` over `.dag` text elsewhere is unswept | -| P7 | subprocess ARGV: `--entry ` / `--function ` | call-by-name through a second door | **30 spelling-keyed `--function` (12 distinct), 8 spelling-keyed `--entry` (7 distinct)** — see the re-derivation below | **token scanner — gap measured, and it ran in BOTH directions**; the assembled-argv blind spot is closed by mechanism, not by a zero | - -**The set is SEVEN KNOWN, NOT PROVABLY CLOSED**, and the method matters more than the number. - -**P7 is the find that matters most.** Entry and function names cross into `.dag` through SUBPROCESS -ARGV — 35 `--function` literals across `bin/interp_recorded_fixture_witness.rs`, `bin/claim_batch.rs` -and `pre_push.rs`, 6 literal `--entry` `.dag` paths, 11 distinct names. It is the same coupling through -a second door, it is larger than the primitive this census started from, and it is invisible to every -`run_in_context`-keyed search. A reader would least expect this door, which is exactly why it went -unmeasured. - -**Method, stated because it is the transferable part.** Each pass of this census was keyed on a -SYNTAX, and each re-keying found more: a `sym_eq`-keyed sweep missed P3 and P5; a `run_in_context`-keyed -sweep missed P6 and P7. Both misses were committed by this document's author AFTER being warned that a -grep written from the known specimen returns only the specimen's shape. The pass that found the -subprocess door was keyed differently — on **any route by which a name crosses from Rust into the -`.dag` world**, not on a spelling of a call. That is the formulation any future sweep should use, and -it is why the count above is reported as known-not-closed. The same error recurred inside the -instrument at smaller scale: locating P7's names needed `test fn`, not `fn` — a declaration scanner -keyed on one keyword misses a whole declaration form. - -**P6 was the only primitive in the set that could fail OPEN, and this PR repairs it.** -`str::replace` returns its input unchanged when the pattern is absent, so an ordinary edit to the -`.dag` literal made the scratch copy silently unrewritten. The two arms differed and only one was -loud: a missed module-path rewrite produced a same-name duplicate that the module-path collision wall -refuses, while a missed `/tmp` rewrite made the witness write to the SHARED path instead of its -per-run scratch directory — unreported, with the damage landing as cross-run interference in some -other session's witness. Being caught by a neighbouring wall is a property of that wall, not of this -rewrite, so **all five substitutions now refuse** — three in `unique_fs_witness_entry` and two in -`closure_scale_witness_entry`, the two producer functions reached from six call sites — not just the -silent one. The -refusal names the PATTERN and the SOURCE FILE, because whoever trips it will be editing the `.dag` -with no reason to know a Rust harness depends on its literal text. Evidence: a discriminating RED -(pattern edited out → refuses, asserting the refusal names both), a positive control (pattern present -→ substitutes, so the refusal is not a function that refuses everything), and a **counterfactual** -that runs bare `str::replace` on the identical input and asserts it answers with the source unchanged -and no error — because `substituted` did not exist before the repair, the RED alone would show only -that a function which refuses, refuses, and not that the mechanism it replaced failed open. All three -in `substituted_refuses_on_absent_pattern`. Honest placement: these execute under -`cargo test --workspace`, not under the required lane, which runs `--lib` only and merely compiles bin -targets. The incident that produced the counterfactual is recorded in its own section below. - -**Mint side** — the same coupling in the opposite direction, where the seed CONSTRUCTS a value the -`.dag` side then destructures: 262 `type_name:`/`variant_name: ctx.sym("Lit")` sites and 198 -`ctx.sym("field")` field-name sites, over 170 distinct minted type/variant names. - -### What my search covered, and what a decoder using some other primitive would look like - -The instrument is keyed on P1–P5, which are name-keyed. A decoder that avoided names entirely would -have to be **positional** (`fields[0]`, `fields.get(0)`) or **shape-only** (matching `Value::Record` -without inspecting `type_name`), or **textual** (`format_value`/`type_label_public` rendered and then -substring-matched). I looked for all three: - -- positional: **2 sites**, both in one `#[cfg(test)]` block in `v1_interpreter.rs`, and both still - assert the resolved NAME beside the index. Not a live decode mechanism. -- rendered-text: **0 sites** in hand `.rs` — `format_value` appears 38 times and `type_label_public` - 46 times, all inside refusal messages, never as the discriminant. -- shape-only: present, but it is a distinct class (order/arity coupling, not spelling) and is not - what this census is about. - -So the name-keyed primitives are the whole live population, and P3/P5 are exactly what a grep written -from the #9975 specimen (`sym_eq`) would have missed: 97 further sites in 9 files. - -## The finding: a safety claim asserted BY SUBTRACTION over a population nobody enumerated - -The count correction below is the receipt. **This is the finding.** - -The first version of this census's P4 row read: - -> 42 literal (of 142 total; **the other 100 take the name from a `.dag`-supplied roster and are NOT -> exposed**) - -That bolded clause is the sentence that says most of the surface is fine, and it was reached by -arithmetic: 142 minus 42. **The 100 was never a set.** Nobody enumerated it, nobody inspected a -member, and no property of any member was ever checked — yet it was handed an exculpatory property -and it carried the weight of the whole primitive's safety. - -Both of its operands were also wrong. 142 was a raw textual grep that counted the `pub fn -run_in_context` definitions and doc-comment mentions; the call-site count is 135. And 42 came from a -regex requiring the literal on the same LINE as the callee, which missed 68 wrapped calls. So the -subtraction produced a phantom population of 100 where the real complement is 27. - -**Why it passed.** A subtraction over an unenumerated complement *reads as arithmetic rather than as a -claim.* It has no verb like "I checked" to interrogate, it inherits the authority of the two counts -beside it, and both of those counts looked like measurements. It passed the author, it passed review, -and it passed the manager who commissioned the census. Nothing in the sentence's shape invites the -question "which 100?". - -**The rule.** A complement is not evidence. If a population is small enough to matter to a safety -claim, it is small enough to enumerate — and if it cannot be enumerated, the honest disposition is -*undetermined*, never *not exposed*. - -## The 27 non-literal P4 sites, enumerated - -Enumerated rather than subtracted, one row each, because replacing a subtraction over 100 with a -subtraction over 27 would rebuild the same defect at a fifth of the scale. - -| site | second argument | disposition | -|---|---|---| -| `cli_run.rs:31408` | `CI_FLOOR_COMMIT_WITNESS_SCHEDULE_FN` | **RustLiteral** — a Rust `const` | -| `cli_run.rs:21376` | `basis_constructor` | **RustLiteral one hop** — `let basis_constructor = match kind { … }` selecting between Rust literals | -| `cli_run/witness_gates.rs:494` | `basis_constructor` | **RustLiteral one hop** — same value, passed in | -| `cli_run.rs:21405` | `constructor` | **RustLiteral one hop** — `match` arms including `"witness_cost_seed_timed_out_event"` | -| `cli_run.rs:4249` | `fn_name` | HelperParameter | -| `cli_run.rs:17354` | `function` | HelperParameter (`run_witness_verdict_diagnostic`) | -| `cli_run.rs:17460` | `fn_name` | HelperParameter (`eval_census_string_fn`) | -| `cli_run.rs:18514` | `function` | HelperParameter (`run_value`) | -| `cli_run.rs:23786` | `fn_name` | HelperParameter (`call_test_claim_fn_bool`) | -| `cli_run/required_floor_runner.rs:345` | `function` | HelperParameter (`run_claim_failure_receipt`) | -| `cli_run/required_floor_runner.rs:376` | `function` | HelperParameter | -| `cli_run/required_floor_runner.rs:3331` | `qualified_name` | HelperParameter | -| `pre_push.rs:110` | `function` | **HelperParameter, traced** — `eval_fn(plan, "pre_push_zero_sha_authority")`, `eval_fn(plan, "pre_push_source_roots")`: Rust literals | -| `pre_push.rs:282` | `function` | HelperParameter (`eval_fmt_recipe`) | -| `required_regen_host.rs:4027` | `function` | HelperParameter — a local closure `let call = \|function: &str\|` | -| `required_regen_host.rs:7217` | `function` | HelperParameter — same shape | -| `v1_interpreter.rs:2549`, `:2577`, `:2610` | `name` | HelperParameter, inside `#[cfg(test)]` | -| `cli_run/required_floor_runner.rs:3013`, `:3043` | `&qualified` | **Computed** — `format!("v2.workflow.required_floor.{func}")`; the PREFIX is a Rust literal and is itself exposed | -| `cli_run.rs:19378` | `&qualified_declaration` | **Computed** — `format!("{declaration_module}.{declaration_name}")` from `.dag`-supplied parts | -| `cli_run.rs:19082` | `&function` | Undetermined | -| `cli_run.rs:18659` | `projection` | Undetermined | -| `cli_run/shared_fill.rs:542` | `&format!("fixture.tmshare.{entry}")` | **Computed over a synthetic fixture** — no live authority | -| `v1_interpreter.rs:21175` | `&format!("fixture.{module}.total")` | **Computed over a synthetic fixture** — no live authority | -| `v1_interpreter.rs:4864` | `entry_fn` | Not a call site — the internal delegation inside `run_in_context_with_args` | - -**Reading this table honestly.** *HelperParameter is not a safe disposition — it moves the question one -stack frame up, it does not answer it.* Two were traced to Rust literals at their callers, and nothing -suggests the others differ; but they were not traced, and a reader must not convert them to "safe". -**Not one row in this table says "reads its name from a `.dag` roster and is therefore not exposed"** — -the property the deleted sentence claimed for a hundred sites is claimed here for none, because it was -never established for any. - -## What the spelling-to-parsing gap cost, measured - -Re-derived with a brace-aware parser that reads each call's actual argument list instead of matching a -line: - -| primitive | line-keyed regex | argument parser | miss | -|---|---|---|---| -| P1 `sym_eq` | 87 | 87 | 0 | -| P2 `ctx.field` | 103 | 110 | 7 | -| **P4 `run_in_context`** | **42 (reported "of 142")** | **110, of 135 call sites** | **68** | - -**The blind spot is a function of FORMATTING, not of meaning**, which is why the miss rate is not -uniform: a line-keyed pattern sees a call only when the literal lands on the same line as the callee, -so the gap rises with argument count. `sym_eq(sym, "Name")` never wraps; `run_in_context_with_args(ctx, -"name", &args, false)` is long enough that `rustfmt` breaks it at 68 sites. Those 68 are ordinary -literal entries — `"generated_artifact_body_for_path"`, `"committed_generated_artifact_paths"`, -`"live_read_selection_manifest_live"`, `"resolve_channel_policy"`, `"ci_batch_summary_text"` — 21 in -`required_regen_host.rs`, 18 in `cli_run.rs`, 9 in `cli_run/required_floor_runner.rs`, the rest across -six more files. - -P3, P5, P6 and P7 have NOT been re-derived this way; their status is in the primitive table's own -column, and where the gap is unmeasured it says so rather than implying zero. - -## The class reproduced on the author, inside the repair, within the hour - -This is recorded as evidence rather than as an anecdote: it is the only observation in this document -that shows the class arising rather than being inventoried, and it happened to the person writing the -inventory. - -While adding the counterfactual test to the P6 repair — the repair whose entire subject is -`str::replace` failing open on a missed pattern — the editing script used a Python `str.replace()` -whose pattern did not match, because the escaping of `\n` and `\"` differed between the script and the -file. Every signal available agreed with success: - -| signal | said | actual | -|---|---|---| -| `str.replace()` return | a string | the file, unchanged | -| `cargo fmt` | "modified 1 file" | an unrelated reformat | -| command exit code | `0` | the edit never applied | -| remote `cargo test` | **green** | ran the two tests that already existed | -| `test result: ok. 2 passed` | ok | the third test was never written | - -**The count was the only tell, and the colour was not.** `2 passed` where `3` was expected is the -entire discriminating signal; it was caught by reading the run's test NAMES out of the log rather than -its exit code, which was luck of habit and not of design. - -Three things follow, and none of them are about Python. - -1. **The primitive is not `.dag`- or seed-specific.** A silent no-op on a missed pattern reproduced in - a different language, against a different file, one hour after the paragraph explaining why it is - dangerous was written. That is the strongest available evidence that the population estimated above - is a floor rather than a ceiling: the class does not require the seed/authority seam to occur, only - a substitution whose failure arm widens. -2. **Assert the pattern is PRESENT before replacing.** The edits in this PR that worked did exactly - that; the one that vanished did not. That is the whole difference between them. -3. **A green run is not evidence that a test EXISTS.** A test run reports the tests that are there, - never the ones you meant to add — so a vanished edit and a passing suite are indistinguishable by - colour, exit code, or any single-number summary. Read names and counts. The same shape appears as a - vacuous `0 passed; 0 failed; N filtered out`, and both are caught by ARITHMETIC rather than by - suspicion. - -## Which authorities the seed decodes - -25 unambiguously-attributed `.dag` modules, by decoding site: - -| authority | names decoded | decoder | -|---|---|---| -| `v2.std.live_read` | 10 | `cli_run/live_read_decode.rs` | -| `v2.workflow.floor_diff_observe` | 9 | `cli_run/required_floor_runner.rs` | -| `gunbc.output_policy` | 6 | `cli_run.rs` | -| `std.observation` | 5 | `cli_run.rs`, `cli_run/witness_gates.rs`, `cli_run/test_module_hygiene_bridge.rs` | -| `gunbc.observation_seed_render` | 4 | `cli_run.rs` | -| `tools.dag_compile_clean_scope` | 4 | `cli_run/compile_clean.rs` | -| `gunbc.generated_artifact_emit` | 3 | `generated_artifact_boundary_host.rs`, `behavioral_receipt_host.rs` | -| `gunbc.cli_wire`, `gunbc.test_module_hygiene`, `extdeps.render.terminal`, `std.process`, `v2.std.live_tree`, `v2.workflow.floor_discovery_producer` | 3 each | `cli_run.rs` (+ bridge) | -| `gunbc.githooks_pre_push_plan` | 2 | `pre_push.rs` | -| `v2.workflow.floor_terminal_ledger_wire` | 2 | `cli_run/terminal_ledger_publish.rs` | -| `extdeps.transports.rest`, `std.access` | 2 each | `v1_interpreter.rs` | -| `v2.workflow.wet_evidence`, `v2.workflow.floor2_prepared_subject`, `v2.workflow.floor_route_gap` | 1 each | `cli_run/required_floor_runner.rs` (the #9975 site) | -| 9 further modules | 1 each | `cli_run.rs`, `v1_interpreter.rs` | - -**Load-bearing:** `required_floor_runner.rs`, `compile_clean.rs`, `witness_gates.rs`, -`live_read_decode.rs`, `terminal_ledger_publish.rs`, `generated_artifact_boundary_host.rs` and -`cli_run.rs` are all in the library the required lanes reach through `claim_executor --required-ci`. -`pre_push.rs` is the local hook. I did NOT measure per-site execution coverage; reachability of the -FILE is not execution of the SITE, and that gap is the census's main residue. - -## Instances: one confirmed, one retracted - -**1 — #9975 (`v2.workflow.wet_evidence`), known and standing.** Four spellings, all refused on the -required path, no Rust compile failure. This remains the only confirmed instance. - -**2 — `roadmap_acceptance_event_history`: RETRACTED. Not a defect, and the retraction is the more -useful finding.** `cli_run.rs` calls `run_in_context(&ctx, "roadmap_acceptance_event_history", true)` -and no declaration of that name exists **at HEAD** — `gunbc.roadmap_authority` carries -`roadmap_acceptance_event_history_load` instead. I reported that as a live break. It is not, and the -reason is a defect in my own census METHOD rather than in the seed. - -That decoder's subject is **revision-addressed**, not HEAD. It is the one-time carrier-introduction -bootstrap in `gunbc.roadmap_acceptance_history_observation`: it fires only when -`git_show_path_absent_at_ref` reports the JSONL carrier ABSENT at the merge-base, and the text it -decodes is `git.Core.Show(ref: baseline_sha, path: dag/gunbc/roadmap/roadmap_authority.dag)` — the -merge-base revision's authority, not the worktree's. - -The two facts are complementary, and they were bound in a single commit. At `bfaaf3e4ee7^` (#7791) -`fn roadmap_acceptance_event_history()` exists and the carrier is absent; at `bfaaf3e4ee7` the -carrier exists and the function is gone. That commit both introduced -`dag/gunbc/roadmap_acceptance_event_history.jsonl` and removed the function. So the arm's own guard — -carrier absent at merge-base — implies the old spelling is present in the text it is about to read. -The seed's spelling is correct for exactly the revision set in which the decoder can fire, and -unreachable everywhere else. - -**The probe question, answered.** The `#[ignore]`d test pins -`git show 9ce6526c528:dag/gunbc/roadmap_authority.dag`. `9ce6526c528` is an ancestor of -`bfaaf3e4ee7^`, carries the function, and has no carrier — it is a faithful representative of the -revision class this decoder serves. **The pin is legitimate and must stay.** A "live probe that goes -red on a rename", which is the reflex repair, would be WRONG here: live resolution is not this -decoder's subject, so such a probe would assert a property the code never claimed and would have to -be deleted or exempted the moment it went red. The `#[ignore]` is separately declared (live-corpus, -minutes per test) and is not what hid anything. - -**The class this actually names, and it is a census-method class:** -*a spelling-keyed decode whose subject is a REVISION-ADDRESSED text must be joined against the -revision it reads, not against HEAD.* Joining against HEAD reports every historically-correct -spelling as a break. My scanner had no notion of the decoder's subject revision, so it could not -distinguish "the seed lags the authority" from "the seed reads an older authority on purpose". Any -future instrument over this class inherits that requirement: the join key is (name, subject -revision), not name. - -**Consequence for the population above.** P4 is the only primitive whose sites can be -revision-addressed in this way, and the attribution below shows exactly one such site — this one. The -P1/P2/P3/P5 sites all decode values produced by the CURRENT resolved graph, so the HEAD join is the -right join for them. The retraction therefore narrows to P4 and does not disturb the rest — but the -census reports it because a reader who copies the method would repeat the error. - -**Mint-side residue:** 6 of 170 host-minted type/variant names have no `.dag` declaration at all — -`EmitHostTransportResult`, `FilesystemReadResult`, `TargetModelish`, `PortLocus`, `OtherDiagnostics`, -`UnrelatedDecision`. These are not rename victims; they are shapes the host owns with no corpus -authority, which is the §3 half of the same coupling: nothing on either side can be renamed -*together* because there is no other side. - -## P4 subject-revision attribution (second pass) - -The retraction above established that the join key is (name, subject revision). - -> **THE 68 ARE NOW ATTRIBUTED — see "Closing the 68" below.** The table in this section covers only -> the 42 sites the line-keyed regex found, and its conclusion stays struck, because a conclusion is -> not rescaled onto a population it was not derived over. The 68 were answered separately and by a -> different question. - -The 42 sites the first pass found, attributed by reading each caller's context construction: - -| subject | sites | how established | files | -|---|---|---|---| -| **HEAD / live worktree** (ctx built from `default_source_roots()` / `workspace_root()`) | 17 | context construction reads the live source roots | `required_regen_host.rs` 4, `cli_run/required_floor_runner.rs` 4, `cli_run/materialization_provider_consumer.rs` 4, `cli_run.rs` 2, `cli_run/terminal_ledger_publish.rs` 1, `cli_run/test_module_hygiene_bridge.rs` 1, `derived_realization_schedule.rs` 1 | -| **In-file synthetic source** (subject is the `r#"…"#` literal beside the call; no tree at all) | 24 | the module text and the entry name are authored together in the same expression | `bin/auth_declared_but_unwired_witness.rs` 10, `bin/interp_recorded_fixture_witness.rs` 4, `cli_run/shared_fill.rs` 4, `v1_interpreter.rs` 4, `compiler_tests.rs` 2 | -| **Revision-addressed** (`git.Core.Show` of a merge-base ref) | 1 | `cli_run.rs:680`, the carrier-introduction bootstrap — the retracted candidate | `cli_run.rs` | - -**~~The residue is closed.~~ STRUCK.** Of the 42 attributed sites, exactly one is revision-addressed -and it is the one already adjudicated; the 24 synthetic sites cannot drift against any tree, since -their authority and their consumer are the same expression; and the 17 HEAD-subject names all resolve -today. **All three statements are about 42 of 110 sites.** The remaining 68 have not been attributed, -so the residue is OPEN: it is not known whether any of them is revision-addressed, and no claim is -made that they are not. - -P7's 35 `--function` names are all HEAD-subject and all 11 distinct names resolve today -(`filesystem_write_keystone_holds`, `witness_write_then_read_roundtrip`, -`clock_freshness_keystone_holds`, `env_freshness_keystone_holds`, `diagnostic_redfish_keystone_holds`, -`http_pilot_rest_keystone_holds`, `w_site_label_is_the_module_path_as_package`, and four others), each -in a `dag/test/claim/*.dag` entry named as a literal `--entry` path beside it. - -So of the P4/P7 call-by-name axis, this section attributes 52 sites (HEAD-subject, 0 stale); the -remaining 68 are answered in the next section by a different question, and none is revision-addressed. - -## Closing the 68: the question was re-shaped twice, and the first re-shaping was still a spelling - -The 68 unattributed P4 sites are closed. The method took two attempts, and the failed first attempt is -kept because it is the same defect this document is about, committed for the third time, in the -paragraph that claimed to be immune to it. - -**First re-shaping — right idea, wrong population.** A call site has no subject revision; the -`InterpContext` it runs in does, and many sites share one context. So the question became *which -context constructions are built from revision-addressed source text?* — and the population was given -as "**86** `InterpContext::new` / `make_eval_context` constructions". **That population was itself -discovered by grepping two constructor NAMES.** It is a spelling wearing a mechanism's clothes, and -review 59276 refuted it with a positive hit: contexts are also constructed through -`InterpContext::with_runtime_options` and `InterpContext::over_scope_indexes`. The 86 was a lower -bound presented as a complete population — the exact move retracted twice above. - -**The remedy the review proposed is the weaker of the two available**, and this is the one place this -correction does not simply take the reviewer's instruction. "Expand the census to all constructors" -re-keys on a longer list of names, so a constructor added tomorrow escapes it again. The stable fix is -to anchor on the CONSTRUCTION ITSELF. - -**Second re-shaping — anchor on the struct literals, of which there are three.** - -| carrier | struct-literal construction sites | consequence | -|---|---|---| -| `InterpContext` | **1** — inside `over_scope_indexes`, *derived by reading the constructor chain, not by grepping the brace: `InterpContext *{` returns 20 hits of which 19 are `fn … -> InterpContext {`, since a return type followed by its opening brace is textually identical to a construction* | `new`, `with_fixture_store` and `with_runtime_options` all funnel into it; so does every future constructor, because there is nowhere else to build one | -| `PreparedScopeIndexes` (its only content-bearing input) | **1** — inside `build_scope_indexes_with_module_order`, which takes `graph: &ResolvedGraph` | every context's content therefore comes from a `ResolvedGraph` | -| `ResolvedGraph` | 21, of which 13 are empty test graphs, 4 are the compile path, and **4 are the resolved-graph cache** | the cache is the only one whose bytes are read from disk rather than freshly compiled | - -**The cache is not a third route, and it is refused into that shape rather than assumed into it.** Its -key is `subject_digest_for_closure(sources: &[Rc])` — derived from the sources themselves — -and a header whose stored subject differs from `expected_subject` is REJECTED -(`CacheRejectReason::BackendKeyMalformed`), not served. So a cache hit is content-identical to the -sources the caller already holds; it is a memo keyed on the subject, not an independent origin. - -**The closure, now anchored on constructions rather than on names.** Every context's decodable content -reaches it through one struct literal, from one `PreparedScopeIndexes` literal, from a `ResolvedGraph` -that is either freshly compiled from `SourceFile`s or restored from a cache keyed on those same -`SourceFile`s. And `SourceFile`s arrive by exactly two routes: - -| route | how source text reaches a context | revision-addressed instances | -|---|---|---| -| a disk path under a `--source-root` | including `fs::write` of externally-obtained text into such a directory | **1** — the roadmap carrier-introduction overlay, already adjudicated | -| in-memory `SourceFile { content: … }` / `single_source(…)` | | **0** | - -That one context's call is `cli_run.rs:680`, a single-line site counted in the original 42. **It is not -among the 68, so none of the 68 is revision-addressed.** - -**ORIGIN IS IRRELEVANT, and that is the strength of the argument.** Git, a network fetch, an archive, -synthesis — to be decoded, text must become a context's source, and every origin enters by one of the -two routes above. A fetch composed with a write is not a third route; it is the first route with a -different upstream. - -*An earlier revision bounded this with an observation instead: "no network fetch or archive extraction -exists in this tree". That was FALSE — `v1_interpreter` carries a live `ureq` REST transport, -dispatched by method — and it is the kind of false a reader trusts precisely because it was labelled an -observation, and observations read as checked. It was deleted rather than repaired: it was a sentence -that can rot, guarding a claim that cannot. Recorded rather than silently removed, because the weaker -sentence was written to bound the stronger one and ended up being the only part that could be wrong.* - -**What remains genuinely unknown**, stated as unknown: whether any `.dag` program composes a fetch with -a write into a directory later passed as a `--source-root`. Not found, not exhaustively searched. It -does not affect the closure — that composition is the first route — but it matters to anyone reasoning -about where a context's bytes originate. - -**Why the HEAD-vs-synthetic split within the 68 was NOT enumerated.** It changes no answer: both are -HEAD-time, both are typed by the trigger identically, neither can drift against a revision. It would be -the same completeness-priced-as-completeness declined for the 14 `HelperParameter` rows. - -## P7 re-derived: the line-keyed instrument's error was not one-signed - -P4's re-derivation found 68 sites a line-keyed regex had missed, and the obvious generalisation is -"line regexes undercount." **That generalisation is wrong, and P7 is the counterexample.** Re-derived -with a token scanner that reads the next token after each flag literal — skipping whitespace, commas -and line comments, so wrapping is irrelevant: - -| flag | occurrences | literal next token | literal inside the adjacent expression | computed | spelling-keyed total | census had said | -|---|---|---|---|---|---|---| -| `--function` | 35 | 29 | 1 | 5 | **30** (12 distinct) | 35 (11 distinct) — **over** by 5 | -| `--entry` | 41 | 3 | 5 | 33 | **8** (7 distinct) | 6 — **under** by 2 | - -**Over-counted on one flag, under-counted on the other, from the same instrument in the same pass.** -`--function` was reported as 35 because 35 is the count of the FLAG, and every flag occurrence was -assumed to carry a name; 5 of them take their name from a variable. `--entry` was reported as 6 -because the old query asked whether a `.dag` literal appeared on the following line, which conflates -*adjacency* with *containment*: `ws.join("dag/test/claim/x.dag")` is not a literal next token but it -does carry a spelling, and 5 such sites were missed while 3 truly-adjacent ones were counted. - -So the correct lesson from P4 is narrower than it looked: **a line-keyed instrument's error has no -sign.** It misses what formatting separates and over-claims what proximity suggests, and which one -dominates depends on the syntax at hand, not on the instrument. - -**The assembled-argv blind spot is closed by MECHANISM, not by a zero — and the closure is stated as -ENTRY, not as origin.** The previous entry said argv built as a `Vec` elsewhere would be -invisible "in principle". That is true of the scanner, so the question was re-shaped the way the 68 -were. - -*A first attempt at that re-shaping was incomplete and is recorded rather than quietly replaced.* It -argued that the flag string must be either a LITERAL (all 35 occurrences scanned above) or -SYNTHESIZED (and there is no synthesis — the only `format!("--…` in the hand `.rs` set builds an error -message). **That disjunction has a third arm: a string can be READ rather than written or built** — -from an environment variable, a config file, a JSON or TOML payload, a recorded fixture, or argv -forwarded into the process. Reading is neither a literal nor a synthesis and it leaves no `format!` to -find, so the two-armed version was a closure with a hole in it. - -**The correct form is the one this document already uses one section earlier: ENTRY, NOT ORIGIN.** -Every argv that reaches a subprocess is CONSTRUCTED at one of exactly two sites, and both are wholly -inside the scanned population: - -| spawn site | argv construction | forwarded input | -|---|---|---| -| `pre_push.rs` `run_claim_batch` | `.arg()` chain: `--entry --function `, then `for arg in suffix_args` | ONE caller, passing the literal array `&["--claim-run"]`; `entry` and `function` come from `ActiveGate::DocWitness` read out of the `.dag` plan | -| `bin/interp_recorded_fixture_witness.rs` `run_claim_batch(args: &[&str])` | the caller's array, verbatim | 30 call sites, every one a literal `&[…]` array | - -So a value's ORIGIN — literal, synthesized, or read — does not matter. To become an argument it must be -interpolated at one of those two constructions, where it appears as a non-literal and is classified as -*computed* above. A zero from a grep would have established nothing; this establishes it, and it -establishes it without needing to enumerate the ways a string can come into existence. - -**Two facts of different kinds hold this up, and they must not be read as one.** The ENTRY argument is -STRUCTURAL: a value becomes an argument only by being interpolated at a construction, so it is visible -there as a non-literal whatever anyone does tomorrow. The POPULATION reading is CONTINGENT: that -`suffix_args` has one caller passing `&["--claim-run"]`, and that all 30 call sites of the other -`run_claim_batch` pass literal arrays, is true of this tree TODAY. **A second caller forwarding a read -string breaks the second half silently** — no test fails, no gate fires, and the sentence above stays -green while it is false. Nothing enforces it. Stating the two in one breath would make a rung-1 fact -wear a rung-4 argument's clothes, which is this document's own recurring defect in another costume; -the typed-decoder trigger is what eventually makes the contingent half unnecessary. - -**The computed sites are not hidden spellings, and their origins are named rather than assumed.** The -33 computed `--entry` values and 3 of the 5 computed `--function` values are the P6 scratch-copy paths -built by `unique_fs_witness_entry` / `closure_scale_witness_entry`, whose `.dag` spellings are already -counted under P6. `pre_push.rs`'s `entry` and `function` come from the `.dag` plan itself -(`ActiveGate::DocWitness { entry, function }`, read out of `pre_push_active_kinds`) — **authority-supplied, -so not a Rust spelling and not exposure.** That is the disposition the retracted P4 sentence claimed -for a hundred sites without checking; here it is claimed for four, by reading them. - -## The identifier-preserving shape axis — a real, unmeasured sibling - -Ruled by the design authority and recorded here rather than discovered later. The class is not "every -`.dag` rename silently breaks the seed"; it is **any change to a `.dag` schema element consumed -reflectively by host code is a host↔dag ABI change with no compiler-enforced edge**. A rename is one -trigger. The class also includes changing a qualified function name, changing a record tag, renaming -or NESTING or ADDING or REMOVING or RETYPING a field read by name, adding an arm to a host-side closed -match, changing a serialized wire spelling shared by `.dag` and Rust, and — decisively for this -document — **changing optionality, cardinality or shape while preserving every identifier**. - -**Every primitive P1–P7 is NAME-keyed, so the identifier-preserving shape axis passes all seven and -still breaks the decode. The population measured here is the NAME-KEYED SUBSET and nothing more.** -This is not hypothetical: the one case examined closely in this census (§ instance 2) was half a shape -change — the `.dag` side changed the result from `List` to a `Load` coproduct -alongside the rename, and the seed's `Ok(Value::List(events))` arm would have been wrong even had the -name matched. - -The ceiling below is unaffected, and that is the point: a generated typed decoder makes a shape change -a type error exactly as it makes a rename one. **The ceiling is right; only the census is narrow.** - -## Rung and ceiling - -**Rung found at: 1 (mitigatable), and the honest reason is better than expected.** Every P1/P2 decode -site I read fails CLOSED — the catch-all arm is a typed `Err` naming the expected type, not a -default (`require_permitted_transport`, `discover_floor_corpus_rows_from_host_facts`, -`expand_explicit_pairs_or_refuse`, the `live_read_decode` chain). So the class is NOT -silent-wrong-answer. It is **silent at compile time and loud only where executed**, and #9975 is the -evidence: four spellings refused on the required path with nothing failing to compile. - -That refinement changes what the class COSTS and therefore how it ranks. A silent-wrong-answer class -is priced by the harm a fabricated output causes downstream; this one is priced by the delay between -a rename landing and the first execution of the affected path — the cost is deferred detection, not -corrupted output. It ranks above a merely cosmetic class and below a fail-open one. - -**Compiler-silent, not necessarily execution-silent.** It is loud and correct when the decoder -actually runs. It becomes operationally silent by four routes: the path does not run; a fixture -bypasses it; a default absorbs the mismatch; or a stale artifact answers instead. (Adopted from the -design authority's ruling, which reached this independently of the code reading above.) - -A caution that falls out of the retraction above: because the class is loud-when-executed, the -temptation is to hunt it by static join, and a static join over the WRONG subject manufactures -findings. That happened once in this census, to me, in the space of one pass. - -**Attainable ceiling: 3, structurally guaranteed, and possibly 4.** The class is decidable and the -authority already exists: the seed EMITS Rust mirrors from `.dag` today. If the decode consumed a -generated mirror type instead of a spelling, a rename would be `rustc` E0433/E0599 — construction, -not a check, and it climbs the mint side at the same time (a generated constructor cannot name a -field the type does not have). - -**Next-rung trigger — the CAPABILITY, not an artifact:** *emission of a typed decoder -(`Value` → mirror type) and a typed constructor for every `.dag` type the seed decodes or mints, -sufficient to replace every P1–P7 and mint site above, and to make an identifier-preserving shape -change a type error as well as a rename, such that no hand-written spelling of a `.dag` -type, variant or field name survives in `src/v1/stage0/src`.* Partial emission does not retire the -class: any site left on a spelling keeps its own exposure, so the trigger is stated over the -population, not over one module. - -**The trigger needs an explicit version-boundary arm, and without it it is UNSATISFIABLE.** A -generated decoder binds the type as it exists at HEAD. A site that decodes REVISION-ADDRESSED source -text must read the shape that revision had, so typing it against HEAD is not safer — it is wrong, and -it is wrong in the direction that produces a confident answer about the wrong schema. One such site -exists (the roadmap carrier-introduction bootstrap) and the closure above establishes it is the only -one, but one is enough: as originally worded, "no hand-written spelling survives" cannot be satisfied -there, because there is no HEAD type to bind to. - -So the trigger carries a second arm: *for every revision-addressed decode, either a decoder bound to -the revision's own schema, or a TYPED REFUSAL at the version boundary — never a HEAD-typed decoder -applied to older text.* This matters more than its one-site population suggests, because §4b(3) makes -the trigger the whole check: **an unsatisfiable trigger is worse than a weak one.** It can never be -retired, so the class parks below its ceiling permanently behind a row that still reads live — which -is rung inflation arriving by a different door than the usual one. - -**What is explicitly NOT the answer.** A lens or test that greps the seed for spellings and joins -them to `.dag` declarations — i.e. an executable version of this document — is validation standing -where construction is available, and it would additionally inherit the bare-name ambiguity noted -above (`Refused` ×7). It belongs only as the interim mitigation if the emission capability is -deferred, and then as a declared §4b(3) row, not as the climb. - -## Residue this census did not close - -- Per-site execution coverage. File reachability is not site execution; the P4 entry names refuse - loudly *when run*, and I did not establish which run. This is the residue that matters most: it is - what separates a spelling that is stale from a spelling that is merely unexercised. -- **The identifier-preserving shape axis, in full.** Named above, deliberately not measured in this - pass. It is the largest single gap between this document and a closed answer. -- **The transitive origin of the 16 `HelperParameter` rows** in the P4 enumeration. Two were traced; - the rest move the question one frame up and are not answered. DELIBERATELY not closed: the typed - decoder types literal-origin and `.dag`-origin names identically, so the distinction dissolves when - the trigger lands, and completing the inventory of a population scheduled to stop existing is - completeness priced as completeness. -- **The HEAD-vs-synthetic split within the 68**, for the same reason — it changes no answer. -- Whether any `.dag` program composes a fetch (the live `ureq` REST transport in `v1_interpreter`) - with a write into a directory later passed as a `--source-root`. Not found, not exhaustively - searched, and stated as unknown. It does not affect the closure — that composition is route A with a - different upstream — but it matters to anyone reasoning about where a context's bytes originate. -- **P3 and P5 re-derivation.** P1, P2, P4 and P7 are done; P3 and P5 carry line-keyed counts and are - floors with an unmeasured gap — and after P7, "floor" is the wrong word: the gap has no known sign. -- P6's fail-open arm is described but not sized beyond its 5 substitutions in 2 producers; - `str::replace` against `.dag` text elsewhere in the tree was not swept. Given the reframing above, the wider sweep is the - substitution-whose-failure-arm-widens class, not the `.dag`-text subset. -- Two findings about the REVIEW instrument surfaced while this census was in flight and are recorded - separately, in [review-instrument-observations.md](review-instrument-observations.md), because they - are facts about the tooling rather than about the seed. -- The `.dag` FILE-PATH axis: 420 distinct `"*.dag"` string literals in hand `.rs`, most synthetic - fixture paths. A path rename is the same spelling-keyed coupling in a different alphabet; I did not - separate the live paths from the synthetic ones. -- The mirror-emitted files were excluded by construction. That exclusion is sound for THIS class and - is not evidence about any other. diff --git a/docs/plans/service-interface-and-uses-carriers.md b/docs/plans/service-interface-and-uses-carriers.md deleted file mode 100644 index 4c24cfe1588..00000000000 --- a/docs/plans/service-interface-and-uses-carriers.md +++ /dev/null @@ -1,150 +0,0 @@ -# XL-2: service interface, realization binding, and `uses` — carrier design - -Status: design only, no behaviour change. Owner: XL-2 (quiet-seal-543). Decisions recorded below were ruled by the XL-2 manager on 2026-10-01. -Governs: `gunbc.recurring_failure_mode` `service_interface_member_has_no_carrier`, `gunbc.recurring_failure_mode` `uses_clause_has_no_carrier`, and the two `v2.workflow.compile_door_cause_ownership` rows for `body_lowering_reason_service_realization_unreachable` and `body_lowering_reason_uses_clause_unmodeled`. -History: #12277 (declaration-grade service lowering), #12816 (`uses` parses and refuses at the clause). - -## The question - -Two refusals in `v2.compiler.body_lowering_fold` keep whole modules out of the XL-2 reference census, which reads the **full** normalize route, not census grade: - -- `body_lower_service_decl` refuses every service carrying a set-aside member as `body_lowering_reason_service_realization_unreachable`. -- `body_lower_fn_uses_refusal_optional` refuses every fn carrying a `uses` clause as `body_lowering_reason_uses_clause_unmodeled`. - -For each member, this document asks what fact it is, who consumes it, and which existing authority already owns it. It then gives the smallest lowering change after which every member either lowers into its carrier or refuses at a located diagnostic. No member is dropped. - -## Population (measurement, to be re-derived) - -Measured by a source grep at the base of this change. This is a sizing, **not an oracle** (DESIGN §5). The authoritative population is the set of modules the CI census run refuses under each reason, re-measured after each PR below lands. - -| Subject | Modules | Note | -| --- | --- | --- | -| declares a `service` | 114 | all refuse whole on the full route today | -| — with a realization member (`transport` / `config` / `exit` / `response` / `mock_response`) | 111 | | -| — with only interface members (modifiers, io tails) | 3 | | -| fn or pattern header carrying `uses alias: Type` | 30 (97 rows) | no overlap with the service modules; no row reads its alias in the body | - -**Consequence:** an operation-modifier carrier alone, the trigger the failure-mode row named, unblocks 3 modules. The service frontier is the realization members. The `compile_door_cause_ownership` flip trigger already said so, and this design adopts that sentence as the scope. - -## Inventory: what each member means - -### Service members - -| Member | Fact | Layer (DESIGN §3) | v1 semantics | Downstream consumer | -| --- | --- | --- | --- | --- | -| `operation Op { input {…} output {…} }` | an arrow of the interface | interface | operation signature | resolve (call targets), infer (call typing). **Lowered today.** | -| io field `= default` | the value an omitted input takes | interface | `v1.compiler` `make_field_node` `default_value` | infer (call-site arity: a defaulted field may be omitted), eval | -| `readonly` | a declared claim that the operation's effect shape is `ReadEffect` | interface (a claim about the arrow's effect) | `OperationModifier.Readonly` (`v1` `00_core` `field_init_operation_modifier`) | `std.effects` `check_modifier_vs_derivation`; effect_demand (a read never needs a write grant) | -| `idempotent` | a declared claim that the effect shape is idempotent | interface | `OperationModifier.Idempotent` | `std.effects` `check_modifier_vs_derivation` / `is_idempotent_effect`; retry admission (DESIGN §4b names a non-idempotent effect under retry) | -| `hermetic` | a declared claim that the operation is admissible on the hermetic execution mode | interface (execution-mode axis, not an effect shape) | `OperationModifier.Hermetic` | `std.execution_mode` `Hermetic`; effect_demand keys mode as part of the demand | -| io field `from "key"` | which wire field fills a declared output | **realization** — a decode projection of one transport's observation | `field_node_from_key` | the transport's output projection (target_model / eval). It is not interface: another transport of the same shape need not have a wire key | -| `transport shell {…}` / `rest {…}` / `file` / local | which handler realizes the operation | realization | `classify_transport` | `std.effect_grant` `HandlerBinding` (`RealTransport`) via `extdeps.transports.shell` `ShellTransportConfig` / `extdeps.transports.rest` `RestTransportConfig` | -| `config {…}` | handler configuration at service grain | realization | service-level transport defaults | the same transport config, applied to each operation | -| `exit {…}` / `response {…}` | how an observation (exit status, streams, body) projects onto declared outputs | realization | output projection of the observation | the interpreter's declared-output projection (`v2.std.operation_realization` feeds it a `TransportObservation`) | -| `mock_response {…}` | one published response arm of one operation | realization (a hermetic-replay handler) | published mock | `std.hermetic_replay` `PublishedMockCase`, selected through `HandlerBinding.Replay` | - -### `uses alias: Type` - -| Fact | v1 semantics | Owning authority today | -| --- | --- | --- | -| the resources a fn's body demands | `v1` `04_items` `ResourceRequirement` (`binding_name`, `resource`), consumed by the Rust emitter's call-site binding | **D13** (`gunbc.plans.demand_engine_program`): for a transparent body, demand is DERIVED (`DependencyDemand`, produced once by resolution). Authored restatement rows are deleted (USES-0, `docs/plans/uses-occurrence-census.md`). Binder, opaque-contract and policy rows move to their own carriers. Derivation does not yet name Network (`gunbc.rung_drop` `network_requirement_unrepresented_after_uses_cut`) | - -## One carrier per fact - -### Interface: carried on the operation's Arrow and on its input binders (ruled 2026-10-01) - -The first draft of this section put modifiers as edges on the operation node and defaults on the payload field. Neither is admitted by the substrate: `v2.std.node` `arrow_signature_edges_conform` counts every Arrow named edge other than the body and the declared order as the one type-binder edge, and a payload field is a bare name-to-type edge with no slot for a default. The side-chat ruling (option A) places them: - -1. **Effect and execution-mode claims are Arrow contract edges, kept apart.** `readonly` and `idempotent` are effect claims, checked against the derived effect shape by `std.effects` `check_modifier_vs_derivation`. They lower onto `^arrow_effect_claims_edge`. `hermetic` is a `std.execution_mode` claim and lowers onto `^arrow_execution_mode_claim_edge`. The surface `OperationModifier` stays one syntax vocabulary, and lowering projects each arm to its semantic home. Arrow conformance enumerates the legal labels and target shapes. Duplicates and unknown labels refuse. Contract edges are not binders, and canonical identity is order-independent. -2. **A default belongs to the binder, not to its type.** One binder representation (a required type, an optional single default, closed labels, one reader and builder) is reused by fn parameters, service io fields and record fields. Because that changes the existing name-to-type binder shape, it is a replacement migration (DESIGN §3): every producer and reader moves, and the two shapes never coexist. - -Once these carriers exist, `InterfaceMemberUnmodeled` has no producer for modifiers or defaults. Wire keys move to the realization carrier (below), so that set-aside kind is deleted, not left empty. - -### Realization: a sibling node, never inside the interface - -Per DESIGN §3, transport is one of N handlers bound to the interface shape, and "the dispatch that selects a realization is itself realization". So the realization members lower into a **sibling** of the service, never into its interface. - -**Correction (PR3a, 2026-10-02).** The first draft said `transport` / `config` lower "onto the transport's existing config record" (`ShellTransportConfig`, `RestTransportConfig`, `FileTransportConfig`). They cannot. Those records are the host's runtime configuration (working directory and environment; base URL, credentials and TLS posture; base path). No operation authors them, and none carries what an operation does author: shell `argv` / `stdin`, rest `method` / `path` / `query` / `headers` / `body`, file `path` / `verb`. Lowering onto them would be a meaning fork. `PublishedMockCase` is a key with no body. The rulings (XL-2 manager, 2026-10-02): - -1. PR3 splits into 3a (model), 3b (lowering) and 3c (cut). -2. Declared bindings live per kind in `extdeps.transports.`, and the runtime configs are not forked. -3. Infer typing of exit, response and mock arms against the declared outputs comes later. In PR3, infer refuses at the sibling under its own owned cause. -4. The sibling attaches by **MIRROR**. - -**MIRROR.** Each service emits, beside its interface: - -``` - -> { shell -> { Find -> LEAF } } -LEAF = Conj { Run -> ENTRY, ..., -> CONFIG? } -ENTRY = Conj { -> TRANSPORT, -> ARMS?, -> ARMS?, -> ROWS? } -``` - -- **The mirror keys each entry by the operation's path, structurally.** That is the same path `v2.std.operation_argv` `OperationRef` names at run time, so no second identity is minted. -- **Prefix segments reuse the spine's marked namespace bodies.** Services sharing a prefix therefore merge through the spine's one merge rule, with no new graft rule. -- **The root is unspellable, so no source can name an entry.** `v2.compiler.symbol_index_fill` indexes nothing beneath it, so an entry is not a declaration and can neither collide with nor shadow its operation (ruling condition 1). -- **Each operation has at most one entry, and an entry names a declared operation** (ruling condition 2, enforced by the wall). That an operation authoring realization facts has an entry is the lowering's obligation (3b). - -**3a grounding: what each vocabulary reuses rather than mints.** - -| Fact | Home | Reused or added | -| --- | --- | --- | -| transport kind | `std.fidelity` `TransportClass` (`ShellLocal`, `RestNetwork`, `FileBoundary`, `LocalDirect`) | reused; `local` has no declared binding and refuses as unmodeled | -| response status code | `std.types` `HttpStatus` (100–599) | reused | -| response status class (`5xx`) | `extdeps.ietf.http_semantics` `HttpStatusClass`, RFC 9110 §15 | added beside `HttpMethod`; the IETF fact, not the transport's | -| exit status | an integer (`extdeps.process.posix_exit` rows); `extdeps.transports.shell_declared` `ShellExitPattern` = `ShellExitCode { code }` \| `ShellExitNonzero` | an exit status is never passed through an HTTP type | -| shell `from` key | `extdeps.transports.shell_declared` `ShellOutputChannel` | completed: it had no consumer and lacked `exit_code` (135 rows); transcribed from the seed's `ShellResultChannel` / `shell_result_channel_of_key` | -| file `from` key | `extdeps.transports.file_declared` `FileOutputChannel` | added; transcribed from the seed's `FileResultChannel` / `file_result_channel_of_key` | -| rest `from` key | open (RFC 8259 member names) | no closed vocabulary | -| declared binding fields | `ShellBindingField`, `RestBindingField`, `FileBindingField` in each kind's module | added; closed by the corpus, and an unknown field refuses | -| HTTP method | `extdeps.ietf.http_semantics` `HttpMethod` | reused | - -**What the full route does with the sibling.** -- **Resolution** resolves its references, which is what puts them in the census. -- **Each stage that cannot yet consume the sibling** refuses at it under its own cause. The old blanket `service_realization_unreachable` therefore becomes per-stage located causes, and once no member is set aside, `ServiceSetAside` and both set-aside reasons are deleted (3c). - -**3b depends on an unrecorded language gap**, now `gunbc.recurring_failure_mode` `string_interpolation_read_as_literal_text_by_v2`. Most transport strings interpolate an input (`"{repository_path}"`), and v2 reads that as literal text. Until v2 lowers interpolation, 3b refuses each interpolating transport string, located. - -### `uses`: no carrier (ruled: follow D13), with a prerequisite - -A v2 carrier for the authored row would be a second authority for `DependencyDemand`, the fact D13 rules DERIVED for every transparent body. **But D13's derivation is not yet complete for Network.** `gunbc.rung_drop` `network_requirement_unrepresented_after_uses_cut` records that derived demand (`ItemInfo.service_names`) names the services a Network effect reaches and never Network itself, and that earlier deletions of `uses net: Network` rows were a silent rung loss. The D13 follow-up audit found that nearly every live row binds Network. So a Network row is **not yet a restatement**, and deleting it now would repeat that loss. - -Resolution of this half: - -- **The refusal stays.** `body_lower_fn_uses_refusal_optional` keeps refusing at the clause. Until the prerequisite lands, the authored clause is the only carrier of a Network requirement, so a fn carrying one stays out of the census rather than lowering without its requirement. -- **Prerequisite (the rung drop's restoration trigger):** D13's `DependencyDemand` carrier derives each resource requirement keyed on the resource declaration's identity (`std.resources.Network`), so that the derived demand of every function authoring `uses net: Network` names Network. -- **Then** the restatement rows are retired under D13's criterion, measured by resolution, not grep. -- What could survive is a named dependency binder (a distinct subject), an opaque contract (no body) or a policy envelope. Each moves to its own carrier when one first appears. The live corpus has none, so none is designed here. - -## The smallest lowering change, member by member - -| Member | Lowers into | Refuses (located) when | -| --- | --- | --- | -| `readonly` / `idempotent` | `^arrow_effect_claims_edge` on the operation's Arrow | repeated on one operation | -| `hermetic` | `^arrow_execution_mode_claim_edge` on the operation's Arrow | repeated on one operation | -| io `= default` | the input field's binder default (2b) | the value reader refuses the expression | -| io `from "key"` | projection row in `^service_realization_binding` | the key is not a string literal | -| `transport` / `config` | transport config record in the sibling | the transport kind has no config record | -| `exit` / `response` | output projection in the sibling | the value reader refuses the expression | -| `mock_response` | `PublishedMockCase` row in the sibling | the case has no operation it can key on | -| any other member | — | `body_lowering_reason_service_member_unread` (unchanged) | -| `uses` entry | — (no carrier) | always: `body_lowering_reason_uses_clause_unmodeled` (unchanged) | - -## Rollout - -| PR | Content | Unblocks | Behaviour change | -| --- | --- | --- | --- | -| 1 (this) | this document; `uses_clause_has_no_carrier` trigger amended to D13; DESIGN CLI-section sentence amended (separate hunk) | — | none | -| 2a | Arrow contract edges (`^arrow_effect_claims_edge`, `^arrow_execution_mode_claim_edge`) and their conformance in `v2.std.node`. **DESIGN §3c declared frontier** (see below) | — | substrate only | -| 2b | the one binder representation with an optional default: a replacement migration of every binder producer and reader, measured first | — | yes | -| 2c | lowering of modifiers and io defaults onto 2a and 2b; discriminating red per refusal row above | the 3 interface-only services | yes | -| 3a | the realization sibling's model: per-kind declared-binding vocabularies, `HttpStatusClass`, the MIRROR shape and its conformance wall (`v2.compiler.service_realization`), and the symbol-index arm. **DESIGN §3c declared frontier** (trigger: 3b) | — | substrate only | -| 3b | lowering of transport / config / exit / response / mock_response / `from` into the sibling; the wall runs on every lowered service | the realization-bearing services whose strings do not interpolate, up to the next stage's refusal | yes | -| 3c | delete `ServiceSetAside` and both set-aside reasons; per-stage located causes for any stage that cannot yet consume the sibling | — | yes | -| 4 | flip `service_interface_member_has_no_carrier` and the cause-ownership rows; re-measure the census on the CI population | — | ledger only | -| `uses` (separate lane) | first the resource-keyed `DependencyDemand` carrier (the rung drop's restoration trigger); only then retire restatement rows by D13's criterion, measured by resolution | up to 30 modules, and none before the carrier lands | carrier, then source rows deleted | - -No model lands in PR 1. - -**2a is a DESIGN §3c declared frontier, not dangling.** In 2a, its consumers by execution are the conformance wall itself (`v2.std.node` `arrow_signature_edges_conform`, reached by every `well_formed` check of an Arrow) and the discriminating test that exercises it. Its *production* consumer is 2c: the operation-modifier lowering in `v2.compiler.body_lowering_fold` `body_lower_operation` emits the two edges, and the resolve, infer and translate readers gain their metadata arms in the same change. **Transition trigger:** 2c lands. If 2c is abandoned, the two edges and their conformance are deleted, not left standing. 2a lands ahead of 2c because the ruling asked for the substrate change to be reviewable on its own. That separation is the stated reason for the frontier. A model with no consumer in its own change is dangling by DESIGN §3c. - -**Caveat, stated so it is not read as a promise.** No service has ever reached resolve or infer on the full route. PR 3 will surface new downstream refusals, located at the sibling or the operation under the consuming stage's cause. The count of services that reach the census is therefore re-measured on the CI population after PR 3, not inferred from the grep above. diff --git a/docs/plans/serving-front-door.md b/docs/plans/serving-front-door.md deleted file mode 100644 index fe93742f634..00000000000 --- a/docs/plans/serving-front-door.md +++ /dev/null @@ -1,93 +0,0 @@ -# Serving front door: closing the second door onto the serving groups - -Item 3 of [dogfood-route-manual-interventions](dogfood-route-manual-interventions.md). The decision home is `gunbc.serving.serving_front_door`. The redemption chain's home is `product.capacity.redemption`, realized in `gunbc.fabric_event_log`. - -## What landed in this change - -- **The permit** (`ServingSeatPermitClaims`). It holds the claims a successful `gunbc.harness.harness_seat` `harness_bind_seat` already has: - - the group, the exact seat partition and the co-tenancy class; - - the structured seat reference (`HarnessSeatReference`: attempt = work identity, offer key = launch, stamp, round, head); - - the `product.capacity.lease` grant identity and fence generation; - - the caller principal and the lease term. -- **The signed representation is injective.** `serving_seat_permit_signing_input` length-prefixes every field, including the protocol, which is taken from the claim itself. As a result: - - No content in one field can move a boundary into its neighbour. - - A protocol rewritten after signing fails the MAC join. - - Issuance refuses an unexpected protocol. - - The permit header's decoder (owed under Proposal A) must consume exactly this encoding. -- **The ledger join is on the exact seat pool.** The door is configured with `HarnessSeatPoolIdentity` values minted by the seat authority (`harness_seat_pool_identity`: group, class, partition, ceiling, root). It then checks, against the ledger: - 1. The permit's partition equals the pool partition for its class. A prefix match is not accepted, so `group-b-not-a-seat` refuses. The selected pool must belong to the door's own group, and two configured pools for one class refuse rather than one being chosen. - 2. Exactly one acquisition of the reference exists, for exactly one seat. - 3. That acquisition was made by the grant event the permit names. - 4. The fence generation equals the acquisition's position in the chain. - 5. The partition folds under the pool's own root and ceiling. - 6. The seat has not been released. - 7. The recorded actor equals the authenticated caller. - 8. The recorded acquisition time plus the recorded term is still in the future. -- **Admission is a transition, not a read** (`product.capacity.redemption`): - - **Redeem.** A seat moves `Unredeemed → InFlight { request, holder } → UseEnded`. The front door appends the redemption to the seat's redemption chain, beside its pool partition on the same fabric event log, by compare-and-set (`gunbc.fabric_event_log` `fabric_grant_redeem`). Only an admitted redemption forwards. - - **Concurrent and replayed admissions.** A second admission of the same permit, concurrent or replayed, re-reads `InFlight` and refuses (`permit-already-redeemed`). - - **Release while in flight.** Every seat release passes the redemption gate in `release_retry`, so a sender's release while a request is in flight refuses. - - **Stream end.** The door ends the use (`fabric_grant_use_end`, exactly once per request) and then releases. - - **Recovery.** A door that dies after redeeming leaves `InFlight` standing. The seat stays held and is charged to the redeemed request and proxy. It is ended only on *observed* quiescence (`front_door_recover`), never by a timer. Recovery releases only `UseEnded`; a seat that was never redeemed (`Unredeemed`) is left with its holder. -- **Minting.** `gunbc.harness.harness_cli` `harness_place_for` mints the permit after the seat is bound and records the principal it acts for as the acquisition actor; it no longer takes a free-text actor. If no permit can be minted, the seat is released and placement fails. -- **Counting.** `front_door_tally` keeps one count per refusal cause, plus the number of forwarded requests. -- **Router contract.** `gunbc.serving.router_realization` gains the forbidden arm `ForwardWithoutPermit`. -- **Witnesses** are in `test.claim.serving.serving_front_door_witness_test` (35 claims). They include: - - the RED-first case; - - each pool-join red (non-seat partition, another group's partition, class/partition mismatch, unserved class, zero amount, duplicate acquisition, wrong fence generation); - - a protocol rewrite; - - the ruling's outer-field collision; - - all five redemption controls: two concurrent admissions give exactly one forward; a replay refuses; a sender release while the stream is in flight does not free the seat; stream end permits exactly one release; controller death leaves recoverable, charged state. - - Mutation runs disabled each new wall in turn, and its witnesses went red: the separator-joined signing input, the unsigned protocol, partition equality, the amount check, redemption ignoring state, and in-flight release. - -## Proposal A: the forwarding realization (NOT applied; needs bold-bee-114's go) - -**Constraint.** The interpreter is never on the token stream. Admission, which includes the redemption, runs **once per request**, and the body then streams without interpretation. - -**Shape.** Use an auth-subrequest. A stock reverse proxy listens on the public `:30000`. For each request it makes one subrequest to a decision endpoint, which evaluates `front_door_admit_on_store` with the request's identity and the proxy's identity. Only a 2xx response streams to vLLM on loopback. The stream's end, whether complete, client-aborted or upstream-failed, must reach `front_door_stream_end` exactly once for that request. If the proxy cannot guarantee that hook, the seat stays `InFlight` and is recovered only on observed quiescence. - -**Owed before Proposal A can be applied:** -1. **A measured admission latency budget.** Measure p50 and p99 of the decision endpoint, which now includes one compare-and-set, against group-b's time-to-first-token. -2. **The permit header wire form and its decoder.** It must decode exactly the length-prefixed signing encoding. -3. **The stream-end hook and the quiescence observation** that `front_door_recover` consumes. One candidate is vLLM's per-request state, observed through `gunbc.serving.engine_progress`. -4. **Tally persistence.** - -**Consumption.** Until Proposal A lands, `front_door_admit_on_store`, `front_door_stream_end`, `front_door_recover` and `front_door_access_decision` have no production caller. This is a declared frontier, and its trigger is Proposal A's go. The redemption gate in `release_retry` *is* consumed today, by every harness release. - -## Proposal B: the host change (NOT applied; needs bold-bee-114's go, because it takes group-b's traffic) - -On each serving group's head (group-b: 192.168.1.236): -- vLLM binds to `127.0.0.1`. -- The front door takes `0.0.0.0:30000`. -- The permit key is materialized at `serving_seat_permit_key_path`, readable only by the door and the placement authority. - -This is converged through the existing `gunbc.spark` serving deployment authority. The door and the loopback rebind move in one step. - -**Owed population before Proposal B (rollout correction from the ruling).** Only `harness_place_cli` mints a permit today. The probe, worker, reviewer, auditor and supervisor paths in `gunbc.harness.harness_cli` acquire a seat and then call `harness_run_turn` with the URL, ignoring the seat record. Their acquisition actors are free-text role identities (`worker:`), not the authenticated principal the door requires. Moving vLLM to loopback before they change would refuse the factory's own harness traffic. Each of these paths must do one of two things: -- **(a)** Mint and present a permit under a principal the door authenticates, record that principal as the actor, and end its use at the door. -- **(b)** Use an explicitly modeled internal route that a bypasser cannot reach. - -Choosing between them is part of Proposal B's go. - -## Credential: the permit key - -- **Key id:** `serving-seat-permit-2026-10` (HMAC-SHA-256). With HMAC, the verifying key is also the issuing key, so custody is the boundary. -- **The ledger and the redemption chain are the authority; the MAC only protects the claims.** A holder of the real key cannot forward by minting: - - A permit for a seat that does not exist, sits in another pool, belongs to another caller or another launch, claims another fence generation or stretches its own expiry is refused on what placement recorded. - - A permit for a seat it does hold forwards at most one request at a time. -- **What the caller check can tell apart today.** There is one service principal (`principal:gunbc/workflows/fabric`). So the check separates humans from the service, but not one service sender from another. Per-sender principals are credential issuance, which belongs to the managed-identity row (#13068), and this change does not fork it. -- **Issuance, rotation and revocation drill** are owned by `credential-lifecycle-revocation` (gunb-ai/gunbc#13068). This key is registered there as a member that row must cover. - -## Migration note: what the existing senders must change (stays manual) - -- **ctrl dashboard router.** - - Delete its load score and its host selection. - - Per request, call `harness_place_cli` and present the permit, in the header form fixed under Proposal A item 2. - - Do **not** release a seat that the door has redeemed. The door releases at stream end, and a sender release refuses while the request is in flight. A seat that was placed but never forwarded is still released with `harness_release_cli`. - - Authenticate as Fabric service evidence. -- **ac-dialogue batch.** - - Stop POSTing raw requests. - - Take one seat per in-flight request and present that seat's permit. - - A batch wanting N concurrent requests holds N seats. A permit is redeemable once, so one permit cannot carry two requests. -- **Which identities may request grants.** This stays an operator decision at placement. diff --git a/docs/plans/shared-pool-byte-correction.md b/docs/plans/shared-pool-byte-correction.md deleted file mode 100644 index 50acad00c97..00000000000 --- a/docs/plans/shared-pool-byte-correction.md +++ /dev/null @@ -1,151 +0,0 @@ -# The shared-pool byte correction (owner directive 2026-09-26) - -Amends docs/plans/moa-memory-modeling.md. The 738/1035 block result is -coherent; the 125,010,432-byte result beside it was dimensionally wrong by -~106× and is withdrawn (it becomes a RED control). - -## The defect - -The allocation-plan fold computed: - -```text -per-seat bytes = Σ group.blocks_at_requested_length × group.page_size_bytes -``` - -But `spec.page_size_bytes` is the page represented by that cache-group -specification — NOT the physical byte cost of taking one block ID from the -shared pool. At the pinned revision vLLM derives a separate physical pool -quantity (`_get_kv_cache_bytes_per_block`): for GLM-5.3-Flash the shared pool -block size comes from the packed MLA and index pages; `num_blocks = -available_memory // bytes_per_block`; the worker allocates one backing tensor -of `bytes_per_block × num_blocks`, and cache groups OVERLAY that allocation — -a block ID is owned by one group at a time. The check: 738/1035 blocks is -71.30% of the pool; against TP3's 17.29 GiB arena that is ~12.33 GiB of -block-equivalent capacity, not 0.116 GiB. The fold implied ~169 KB/block vs -the ~17.1 MiB/block the arena and inventory imply. - -## The corrected model - -```text -KvAllocatorCarve — physical resident pool allocated at startup -KvAdmissionDemand — logical blocks required by the promised population -``` - -(Calling both "KV bytes" is what enabled the mistake.) - -The allocation plan must carry: - -```text -allocator_blocks -physical_pool_bytes_per_block -allocated_pool_bytes -``` - -with the old per-group field renamed `group_spec_page_size_bytes` (and -`group_blocks_per_max_request`) so it cannot be read as the shared pool block -size. Conservation controls: - -```text -allocated_pool_bytes == allocator_blocks × physical_pool_bytes_per_block -allocated_pool_bytes <= every rank's admitted KV arena -arena − allocated_pool_bytes < one physical pool block - (modulo block override, null-block reservation, profile rounding) - -request_blocks = Σ exact group blocks at requested length -request_block_equivalent_bytes = request_blocks × physical_pool_bytes_per_block -``` - -The 125,010,432 result is a RED control: 738/1035 blocks cannot occupy <1% -of the same pool. - -## The verdict structure - -The block wall is authoritative; the byte wall is a conservation CHECK on the -pool's construction, not an independent capacity answer: - -```text -PoolConstructionProved { physical_pool_block_bytes, allocator_blocks, - allocated_pool_bytes, per-rank supply } -RequestBlockCostProved { exact group roster, blocks_per_request } -CacheAdmissionProved { request_blocks <= usable_allocator_blocks } -``` - -`CacheFitProved` carries: limiting_rank, seats, length, blocks_per_seat, -blocks_used, blocks_reserved, hard_ceiling. If bytes appear, they are named -`block_equivalent_bytes` — never ordinary memory demand or physical headroom. - -## The cache frontier (block wall) - -| Seats | Blocks used | Blocks left | Reserve | -|---:|---:|---:|---:| -| 6 | 738 | 297 | 28.70% | -| 7 | 861 | 174 | 16.81% | -| 8 | 984 | 51 | 4.93% | -| 9 | 1,107 | −72 | REFUSED | - -These are shares of the already-allocated block inventory, NOT additional -process-memory allocations (vLLM allocates the whole pool at startup). - -## Reserve policy — hard ceiling ≠ guaranteed service - -```text -CacheReservePolicy - = NoOperationalReserve - | ReserveBlocks { blocks } - | ReserveFullLengthSeats { seats } - | ReserveBasisPoints { basis_points } -``` - -Standings: hard cache ceiling 8; normal guaranteed seats selected by policy; -burst/cache-only ceiling 8. First production policy candidate -`ReserveFullLengthSeats { seats: 1 }` → **7 normal 262K seats**. Six remains -the conservative restoration point (28.7% reserve). Eight is not advertised -as normal guaranteed service until transient and SLO proofs support it. - -## Mixed-length service (beyond 8 sessions) - -The uniform "every seat may reach 262K" promise caps at 8. More ACTIVE -sessions are possible when not every session reserves 262K: - -```text -SeatClass { max_context_tokens, guaranteed_count } -ServingCapacityIntent { guaranteed_classes, max_active_sequences, - max_batched_tokens, cache_reserve_policy } -``` - -Admission prices the actual reservation population through the same -allocation plan (fixed KDA per-sequence, length-growing MLA, pooled KPool, -fixed tail, grouping/padding) — NO second seat calculator. Until a real -request-admission boundary enforces the mixed budget, `max_num_seqs = N` -exposes N slots that may ALL submit maximum-length requests, and the safe -proof prices that maximum behavior. - -## Cache fit is still not the whole arm - -The bounded run established initialization + per-rank profiles + the plan; it -did NOT execute six simultaneous 262K requests. Before promoting seven or -eight: (1) pinned-source prefill derivation at the deployed revision; -(2) profile-shape binding (a 6-seat activation/graph receipt does not prove -an 8-seat profile); (3) a bounded falsification run at the derived worst -legal prefill/decode shape; (4) service capacity/SLO evidence (throughput, -latency, preemption, failure behavior). Final result shape: - -```text -ServingCapacityPoint { workload_intent, cache_standing, prefill_standing, - decode_standing, static_residency_standing, - reserve_policy, slo_standing } -``` - -— a Pareto frontier, not one magic tuple. - -## Operational sequence (owner-set) - -1. Interrupt the byte-wall completion (done). -2. Land the shared-pool byte correction + RED control. -3. Recompute the exact cache frontier (6/7/8 by one fold). -4. Close the 262K prefill obligation for six seats. -5. Restore persistent serving at 262K × 6 with the planted-residue - convergence proof and exact readback. -6. Evaluate 262K × 7 under one-full-seat reserve. -7. Retain 262K × 8 as hard/burst ceiling until phase + SLO evidence. -8. Add mixed-length token-weighted admission for stranded block capacity. diff --git a/docs/plans/shell-dag-census-0a-brief.md b/docs/plans/shell-dag-census-0a-brief.md deleted file mode 100644 index 52185585c32..00000000000 --- a/docs/plans/shell-dag-census-0a-brief.md +++ /dev/null @@ -1,694 +0,0 @@ -# SHELL-DAG-CENSUS-0A — dispatch brief (derived shell execution census) - -Status: dispatch brief for the next shell→dag cut. Authored 2026-08-20 against `4cec10f6`, corrected twice before dispatch (see "Corrections that produced this brief"). Not a design authority — the carriers it describes become the authority when they land. - -# Revised call - -The 0A contract changes in four material ways: - -1. **`36/1 at 4cec10f…` is a historical calibration receipt, not an oracle and not a permanent expected count.** -2. **Every fact states its evidence grain.** Declared type, static reachability, interpreter semantics, and runtime observation are separate claims. -3. **The argv representation-ambiguity repair is removed from scope.** #8549 already closed it at the seam. -4. **Instrument validity includes formatting, subject identity, exact-token discrimination, and message integrity—not merely detector-logic mutations.** - -The dispatch precondition remains unmet. `main` is still `4cec10f66a3d84cbde631e20ca9feaa31457d88c`; #8652 remains open, #8653 is closed unmerged. Since your message, #8652’s body claims a clean two-generation fixed-point protocol was run, but its CI checkbox remains open and the repair is not on `main`. The prerequisite is unchanged: the merged `main` SHA itself must pass required regen, fixed point, and the full witnesses workflow. fileciteturn121file0L1-L7 fileciteturn114file0L4-L13 fileciteturn115file0L4-L13 - -# The corrected historical calibration - -At the exact historical subject: - -```text -subject SHA: -4cec10f66a3d84cbde631e20ca9feaa31457d88c - -EmitArtifactThenThinRun occurrences: - 1 variant declaration - 36 production match arms across 14 modules - 4 dag/test/claim occurrences ----- - 41 total occurrences - -Production-arm body partition: - 1 static shell-realizing route - 35 no static shell route - 0 unresolved/unknown -``` - -The sole static realizer is: - -```text -gunbc.host_effect_realize.realize_converge_on_host - -> EmitArtifactThenThinRun - -> ExistingHostQuiescentReload - -> realize_converge_in_process - -> retained script execution -``` - -The number `36` must **not** be compiled into the detector as its expected current population. 0A must run the completed detector against a worktree pinned to that exact SHA and produce a dated calibration receipt, and separately run against its own branch head and publish the current population. - -A disagreement has this disposition: - -```text -HistoricalCalibrationDisagrees { - historical_receipt - detector_result -} -``` - -It is neither “the detector is wrong” nor “update the expected count”; it stops acceptance until the discrepancy is explained. - -The omitted Codex arm is now an explicit control. Its `EmitArtifactThenThinRun` branch returns `CodexSupervisedSessionApplyRefused`, but only a body read establishes that it performs no execution; the return variant’s name is not the evidence. fileciteturn120file0L2-L7 - -# Evidence grain is part of every fact - -The prior brief used “reach” too loosely. Given the current refinement and coproduct construction gaps, 0A must not imply that a statically visible route is a successfully executable runtime route. Rename the central result from: - -```text -MayReachShell -``` - -to: - -```text -StaticShellRoute -``` - -and make the basis explicit. - -## Required grain vocabulary - -```dag -type EvidenceGrain - = AuthoredSyntax - | DeclaredType - | StaticControlFlow - | StaticValueFlow - | InterpreterSemantics - | RuntimeObservation - | ConstructionGuarantee -``` - -A fact may cite more than one grain, but it may claim only what those grains establish. - -| Fact class | Evidence grain | What it may claim | What it must not claim | -|---|---|---|---| -| File/declaration/body inventory | `AuthoredSyntax` | This body or arm exists at this SHA | It executes in production | -| Declared argument or return type | `DeclaredType` | The author declared `List`, `String`, etc. | The runtime `Value` has that representation | -| Call edge | `StaticControlFlow` | The body contains or transitively selects this call | The call is dynamically reached | -| Value origin/path | `StaticValueFlow` | This authored expression can feed this parameter or channel | The runtime value satisfies its refinement or coproduct shape | -| Shell-sink meaning | `InterpreterSemantics` | This operation/channel interprets bytes as a shell program when evaluated successfully | A valid carrier necessarily reaches the interpreter | -| Shell route | `StaticControlFlow + StaticValueFlow + InterpreterSemantics` | An authored path can attempt shell interpretation | A shell process was spawned | -| Runtime carrier shape | `RuntimeObservation` or `ConstructionGuarantee` | The observed/guaranteed runtime representation is this shape | Anything inferred solely from the declared type | -| Runtime process execution | `RuntimeObservation` | This process actually executed with these channels | Whole-corpus coverage | -| Final migration disposition | Reviewed classification in 0B | This static route belongs to runtime, foreign, bootstrap, or pending work | Completeness of the derived population | - -## Consequence for the detector - -The detector must not prune a route because: - -```text -the declaration says List -the declaration says NonEmptyStr -the declaration says a particular coproduct -a refinement should hold -an arm appears unconstructible from the declared type -``` - -At 0A grain, every syntactically authored branch remains part of the control-flow population unless a stronger construction wall is separately cited. - -The detector may report both: - -```dag -type DeclaredCarrierShapeFact { - occurrence: OccurrenceId - declared_type: String - basis: DeclaredType -} - -type RuntimeCarrierShapeReading - = RuntimeShapeUnobserved - | RuntimeShapeObserved { - shape: RuntimeValueShape - receipt: DeclarationRef - } - | RuntimeShapeGuaranteed { - shape: RuntimeValueShape - wall: DeclarationRef - } -``` - -Most 0A rows should carry: - -```text -runtime_shape = RuntimeShapeUnobserved -``` - -That is not a defect in 0A; it is an honest statement of its static grain. - -The sharp-ant `258 operations / five declared argv shapes` measurement may be retained as a **declared-type side receipt**, but it is not an 0A acceptance condition and cannot establish that a runtime `Value` is a list, string, or `ProcessArgvExpansion`. - -# Class 1 is struck completely - -The reissued brief must contain no work item to repair `push_shell_argv_tokens`’ former list-versus-concatenated-string ambiguity. - -#8549 already made that position refuse with a typed, located diagnostic when the same free monoid admits both readings, preserving native list expansion, codepoint-monoid decoding, and the explicit `ProcessArgvExpansion` route. A completed prerequisite, not remaining SHELL-DAG work. fileciteturn119file0L4-L14 - -For 0A, direct argv execution is simply a negative discrimination control: - -```text -shell.Exec.RunArgv - -> direct process carrier - -> NOT a POSIX/Bash program sink -``` - -A malformed or unexpected runtime representation may cause a refusal; it does not turn direct process execution into shell interpretation. - -# Instrument robustness contract - -The five failures you identified become five independent controls — not one generic “bad grep” class, because each has a different remedy. - -| Failure observed | Required 0A control | -|---|---| -| Too narrow to contain the answer | Recursive full-body traversal fixture with the real sink more than ten nested nodes below the arm root | -| Pointed at the wrong subject | Subject identity carrier: exact commit, roots, inventory digest, and an anchor file that must occur in the selected corpus | -| Format-induced false zero | Surface-format invariance pair plus a nonzero control partition in the same run | -| Over-broad token false positive | Exact parsed identity control: `words` must not match `keywords` | -| Messenger altered the result | Structured receipt file with content digest and read-back; no count transmitted through shell interpolation or hand-transcribed prose | - -## 1. No source-text grep as the authority - -The production detector must consume parsed bodies or a typed body-fact projection. - -It must not classify routes using: - -```text -grep -regular expressions over source text -line windows -substring search -file-name patterns -variant-name patterns -comment or prose matching -``` - -A text search may locate candidates during development; it may not produce the committed census. - -If current body projections cannot preserve callee identity, branch identity, and argument edges, the worker must stop and open a separate substrate projection increment, never fill the gap with a source-text approximation. - -## 2. Format-equivalence fixture - -Two fixture files should express the same semantic route with deliberately different formatting: - -```text -fixture A: - ordinary one-line call and string layout - -fixture B: - calls split over lines - nested indentation - comments between syntax nodes - long strings using continuation/layout forms - arguments placed many lines below the callee -``` - -After normalizing fixture-specific path and occurrence identity, their derived facts must be equal: - -```text -normalize(census(fixture_a)) -== -normalize(census(fixture_b)) -``` - -This is the direct control against the wrapped diagnostic-string false zero. - -## 3. Same-run dirty control - -Every production census invocation must also scan an isolated control partition containing exactly: - -```text -1 known shell route -1 direct-argv no-shell control -1 substring near miss -1 deeply nested route -``` - -The output remains partitioned: - -```text -production_result -control_result -``` - -A production result of zero is admitted only when the control partition reports its exact expected population in the **same process, with the same detector implementation and settings**. - -This encodes: - -> When a scan returns zero, the first hypothesis is the scan. - -## 4. Orthogonal observation - -At least one positive observation must not flow through the census fold. - -A suitable control is: - -```text -directly parse one planted fixture --> locate its exact callee node --> locate the exact program-channel argument edge --> assert both exist -``` - -This control may reuse the parser, but it must not call: - -```text -shell_route_facts() -shell_sink_facts() -shell_producer_facts() -``` - -or their shared selection predicate. - -The historical `36/1` audit is a second, human-produced orthogonal calibration. Its earlier `24` error shows why it cannot be the authority; the corrected measurement remains useful as an independent comparison. - -## 5. Exact identity, never token substrings - -All operation, function, variant, and field matching must use parsed declaration or symbol identity. - -The fixture matrix must include: - -```text -words -> intended exact symbol hit -keywords -> no hit -swordsmith -> no hit -``` - -Likewise, `Run` must not match `RunArgv`, and `ShellCommand` in prose must not match a `ShellCommand` constructor. - -## 6. Receipt integrity - -The detector should emit a structured artifact, for example: - -```text -target/shell-dag-census//facts.tsv -target/shell-dag-census//summary.json -``` - -The summary must carry: - -```text -subject commit -source roots -source inventory digest -detector declaration or binary identity -schema version -files attempted -files parsed -parse refusals -declarations -bodies -match arms -sink occurrences -static routes -unknown routes -facts artifact digest -``` - -A witness must read the artifact back and prove: - -```text -reported row count == parsed artifact row count -reported digest == content digest of artifact -reported subject == requested subject -``` - -The PR body may quote that artifact but must not be the result's only home. - -No command may place report text containing backticks, dollar signs, braces, or shell metacharacters inside an interpolated shell string. The artifact path—not its content—is the handoff. - -# Revised result types - -```dag -type StaticShellReachability - = NoStaticShellRoute - | StaticShellRoute { - route: ShellRouteFact - } - | StaticShellRouteUnknown { - cause: ShellCensusRefusal - } - -type ShellRouteFact { - root_consumer: DeclarationRef - owner: DeclarationRef - arm: BodyArmIdentity? - sink: ShellSinkFact - call_path: List - branch_path: List - source: ShellProgramSourceReading - declared_carrier: DeclaredCarrierShapeFact? - runtime_carrier: RuntimeCarrierShapeReading - basis: List -} - -type ShellSinkFact { - owner: DeclarationRef - occurrence: OccurrenceId - language: ShellLanguage - channel: ShellProgramChannel - interpreter_path: List - basis: List -} - -type ShellProgramSourceReading - = RawLiteralSource - | StaticStringCompositionSource { - producers: List - } - | GrammarEmittedSource { - emitter: DeclarationRef - } - | MaterializedFileSource { - writers: List - path_identity: FilePathIdentity - } - | ExternalSource - | MixedSource { - parts: List - } - | SourceUnknown { - cause: ShellCensusRefusal - } -``` - -The names make the ceiling visible: - -```text -StaticShellRoute -``` - -does not claim: - -```text -RuntimeShellExecuted -``` - -A later wet receipt may add: - -```dag -type RuntimeShellExecutionReading - = RuntimeShellExecutionUnobserved - | RuntimeShellExecutionObserved { - route_key: ShellRouteKey - receipt: DeclarationRef - } -``` - -That is not required to complete 0A. - -# Reissued dispatch brief - -> ## SHELL-DAG-CENSUS-0A — derive static shell-program routes from parsed bodies -> -> **Precondition** -> -> Do not branch until #8652 is merged and the resulting exact `main` SHA passes: -> -> ```text -> claim_executor --required-regen -> claim_executor --required-regen-fixed-point -> full witnesses workflow -> ``` -> -> A branch-local claim or first-generation result is not sufficient. Record the exact first green `main` SHA as the census subject. -> -> **Goal** -> -> Derive, from parsed `.dag` bodies, every authored path that can attempt POSIX/Bash program interpretation. The population must be derived from the corpus and interpreter semantics, never from remembered bridge names, filenames, imports, variant names, refusal types, line windows, or source-text grep. -> -> This is a **static may-route census**, not a runtime-execution guarantee. -> -> **Required evidence grain** -> -> Every fact carries one or more of: -> -> ```text -> AuthoredSyntax -> DeclaredType -> StaticControlFlow -> StaticValueFlow -> InterpreterSemantics -> RuntimeObservation -> ConstructionGuarantee -> ``` -> -> A declared type may be recorded, but no runtime representation may be inferred from it. Do not prune routes using refinements, declared coproduct shape, or expected interpreter representation. -> -> **Required corpus partitions** -> -> Scan all authored `.dag` files under the declared production roots. Scan tests and fixtures too, but report them in separate partitions: -> -> ```text -> Production -> Test -> Fixture -> ``` -> -> No file may disappear through exclusion. Each attempted file becomes either: -> -> ```text -> Parsed -> ParseRefused { path, cause } -> ``` -> -> **Required body population** -> -> Enumerate: -> -> ```text -> fn bodies -> func bodies -> service operation bodies -> data initializers that feed executable plans or function values -> every nested if/match/loop/closure body -> every match arm at occurrence identity -> every direct call and service invocation -> ``` -> -> A later realizing sub-arm makes the containing arm `StaticShellRoute` even when an earlier sibling or first branch refuses. -> -> **Required shell channels** -> -> Recognize shell interpretation through: -> -> ```text -> TransportScript/direct script arguments -> sh or bash -c command arguments -> sh or bash -s standard-input programs -> wrapped forms such as sudo -S sh -s -> remote command strings with modeled shell interpretation -> materialized script files later executed -> GitHub Actions run bodies -> cron command bodies -> git-hook programs -> grammar-emitted Bash artifacts -> ``` -> -> The semantic seed may identify what interprets bytes as shell. It may not enumerate current call sites. -> -> **Required value flow** -> -> Trace program values backward through: -> -> ```text -> bindings -> parameters and returns -> record fields and variant payloads -> list/fold/map constructions -> concat/join/interpolation -> renderer calls -> file write/read joins where identity is structural -> transitive helper calls -> recursive call-graph fixed points -> ``` -> -> An unresolved relevant call or unjoinable file flow becomes `StaticShellRouteUnknown`, never `NoStaticShellRoute`. -> -> **Required body law** -> -> Return shape, variant name, reason-field name, comments, and prose are irrelevant. -> -> The result is decided only from transitive body behavior: -> -> ```text -> NoStaticShellRoute -> StaticShellRoute -> StaticShellRouteUnknown -> ``` -> -> Explicit controls must cover: -> -> - a refusal represented as a success-valued record; -> - a refusal represented as `HostnameSetCasApplyFailed { stderr: ... }`; -> - `CodexSupervisedSessionApplyRefused` under `EmitArtifactThenThinRun`; -> - a realizer behind an earlier refusing branch; -> - a classifier returning `Bool` or a variant label with no execution. -> -> **Historical calibration—not authority** -> -> Run the finished detector against: -> -> ```text -> 4cec10f66a3d84cbde631e20ca9feaa31457d88c -> ``` -> -> and publish a dated receipt reconciling: -> -> ```text -> 41 total EmitArtifactThenThinRun occurrences -> 1 declaration -> 36 production match arms across 14 modules -> 4 test occurrences -> -> 36 production arms: -> 1 StaticShellRoute -> 35 NoStaticShellRoute -> 0 StaticShellRouteUnknown -> ``` -> -> This measurement must not become a permanent hard-coded count. A disagreement blocks acceptance for investigation; it does not automatically indict the detector. -> -> **Required live discoveries** -> -> Without using their names as seeds, the detector must find: -> -> - the one historical converge realizer above; -> - all four `spark_managed_access_apply` script producers flowing through `stdin_payload` to `sudo -S sh -s`; -> - every remaining `retained_*` route; -> - direct `RunArgv` routes as no-shell controls; -> - the Codex and hostname refusal disguises as no-shell arms. -> -> **Instrument controls** -> -> The same production implementation must pass: -> -> 1. **Deep-body control:** the sink sits more than ten nested nodes below the matched arm. -> 2. **Subject control:** output carries exact SHA, root set, inventory digest, and reaches a planted anchor under every intended root. -> 3. **Format-invariance control:** semantically identical, differently formatted fixtures produce equivalent facts. -> 4. **Exact-identity control:** `words` matches; `keywords` and other substring near misses do not. -> 5. **Messenger-integrity control:** structured output round-trips with row count and digest unchanged. -> 6. **Orthogonal anchor:** a direct parsed-node assertion sees one planted sink without invoking the census fold. -> 7. **Same-run dirty control:** the control partition reports its exact nonzero population in every production census invocation. -> -> A zero production count is inadmissible unless all seven hold in the same subject run. -> -> **Class 1 status** -> -> Do not modify `push_shell_argv_tokens` for the former free-monoid list/string ambiguity. #8549 already made that ambiguity a typed located refusal. Preserve it and use direct argv as a negative shell control. -> -> **Deliverables** -> -> ```text -> typed sink, route, producer, arm, evidence-grain, and refusal carriers -> corpus-derived producer -> deterministic structured facts artifact -> deterministic summary artifact -> historical 4cec10f calibration receipt -> current-head receipt -> fixture and mutation-control suite -> reconciliation predicates -> ``` -> -> **Non-goals** -> -> ```text -> no shell migration -> no bridge deletion -> no route disposition table -> no runtime-value guarantee from declared types -> no CLI-AUTHORITY tool/argv census -> no srv* survival decision -> no build-cache wiring decision -> no stage0 mirror edit -> no grep-generated production population -> ``` -> -> **Stop condition** -> -> If the available parsed-body facts cannot preserve exact callee identity, argument edges, branch identity, or interprocedural value flow, stop. File the missing typed projection as a separate substrate increment. Do not substitute text scanning. -> -> **Merge bar** -> -> ```text -> exact-base main green before branch -> zero production parse refusals -> zero unexplained StaticShellRouteUnknown rows -> all corpus/count reconciliation laws hold -> all instrument controls discriminate under mutation -> historical 36/1 calibration reconciled or discrepancy explained -> current-head receipt published -> facts artifact deterministic on repeat -> receipt row count and digest round-trip -> full CI green -> ``` - -# Trust predicate - -The 0A result should expose a single predicate with no runtime overclaim: - -```text -static_shell_census_trusted = - subject_identity_holds - && corpus_denominator_nonvacuous - && production_parse_refusals == 0 - && unexplained_static_route_unknowns == 0 - && control_partition_matches - && deep_body_control_holds - && format_invariance_holds - && exact_identity_control_holds - && orthogonal_anchor_holds - && messenger_integrity_holds - && route_sink_producer_arm_reconciliation_holds - && repeated_run_digest_equal -``` - -It should **not** be named: - -```text -shell_execution_guaranteed -shell_runtime_population_complete -shell_migration_complete -``` - -The strongest honest conclusion from 0A is: - -> At this exact source subject, the parsed authored corpus contains this complete, non-vacuously derived population of static paths capable of attempting shell interpretation; every row states whether its carrier shape is merely declared, statically flowed, construction-guaranteed, or runtime-observed. - ---- - -## Corrections that produced this brief - -Recorded because each falsified an input the brief had already been written against, and a reader who finds only the final text would re-derive them. - -1. **The acceptance oracle was wrong.** An earlier revision required the detector to reconcile against a manual 24-arm `EmitArtifactThenThinRun` audit. Measuring that audit in order to pin it falsified it: at `4cec10f66a3d84cbde631e20ca9feaa31457d88c` the tree carries 41 occurrences — 1 variant declaration, **36 production match arms across 14 modules**, 4 under `dag/test/claim/`. Neither manual audit reached 36, and neither named `gunbc.host_effect_codex_supervised_turn`. The conclusion was unaffected — exactly one arm realizes — but with 24 pinned as the oracle, the first *correct* detector would have reported 36 and the reconciliation step would have read as a detector bug. Hence `HistoricalCalibrationDisagrees` stops acceptance for investigation rather than indicting either side. - -2. **Declared types constrain less than the brief assumed.** The argv reachability probe found 258 shell-transport operations whose argv elements resolve to five shapes, none typed `Map`, `Set`, `Record` or nullable. That is a *declared-type* result, and declared types are not construction-enforced here: 3939 `WhereRefinementUnenforced` across 612 files, and separately any coproduct in this tree can be constructed by naming the type with no variant tag, refused only at runtime by the interpreter. Hence the `EvidenceGrain` vocabulary and the rule that the detector may not prune a route because a declaration says `List`. - -3. **A scoped repair was already closed.** The brief's original Class 1 item — the argv representation ambiguity — was closed on main before dispatch, by the lane about to be assigned it. It survives only as a negative control. - -4. **Prior art exists with the exact blind spot this census is built to avoid.** `v2.lens.effect_reach` classifies host-effect sinks and carries a `ShellExecRunSink` variant, but `sink_kind_for_callee` selects by name equality against a remembered callee-text list, everything else falling to `UnknownHostEffectSink`. It therefore cannot see `sh -c`, `sh -s` on stdin, or a `sudo -S sh -s` wrapper — the hole the hand census carried before `gunbc.spark.managed_access_apply` was found. Disposition: **supersede, not extend** — extending inherits its selection principle. - -## Where the instrument controls came from - -The seven controls are not theorized: each is a failure that occurred while producing this brief, and they are **not one class**: - -| control | the failure it encodes | -| --- | --- | -| deep-body | an instrument too narrow to contain the answer — a realizing sub-arm sat ten lines below anything the command could print | -| subject | an instrument pointed at the wrong subject — a stale head, a mirror rather than its authority | -| format-invariance | a **confident false zero**: a grep for a diagnostic string returned 0 on both refs while the author was looking at that sentence, because it wraps across a backslash continuation | -| exact-identity | a **confident false positive**: a pattern containing `words` matched inside `keywords` and returned seven irrelevant maps | -| messenger-integrity | the report of the previous item reached its recipient with the words deleted, because backticks inside a shell command were substituted away | -| orthogonal anchor | every one of the above was caught only because an observation existed that did not come through the instrument | -| same-run dirty partition | a zero is admissible only when something non-zero is proven visible in the same run | - -The generalization, and why a zero production count is inadmissible unless all seven hold: **when a scan returns zero, the first hypothesis is the scan.** diff --git a/docs/plans/shell-dag-census-0a-projection-blocker.md b/docs/plans/shell-dag-census-0a-projection-blocker.md deleted file mode 100644 index af5e5718136..00000000000 --- a/docs/plans/shell-dag-census-0a-projection-blocker.md +++ /dev/null @@ -1,369 +0,0 @@ -# SHELL-DAG-CENSUS-0A — projection blocker and derived shell-interpretation seed - -Status: finding. Produced under the stop condition in -[shell-dag-census-0a-brief.md](shell-dag-census-0a-brief.md), which reads: - -> If the available parsed-body facts cannot preserve exact callee identity, argument edges, -> branch identity, or interprocedural value flow, stop. File the missing typed projection as a -> separate substrate increment. Do not substitute text scanning. - -Subject: `a6ca6882d18114b52532c0804dc89f97b441f493` (first `main` SHA satisfying the brief's -precondition — #8652 merged, `witnesses` workflow SUCCESS on that exact SHA, run 32370279129). - -No detector was built; no text-scanning census was substituted. Below: (1) why the available -fact surface cannot carry 0A, axis by axis, each with its remedy, and (2) the shell-interpretation -seed derived from what actually interprets bytes as shell, needed under whichever remedy lands. - ---- - -## 1. The whole fact surface `.dag` can read - -`src/v1/stage0/src/coproduct_reflection.rs` exposes exactly three live-fact accessors: - -| accessor | item filter | `.dag` surface | payload | -| --- | --- | --- | --- | -| `eval_concept_decl_facts_live` | `ItemKind::TypeItem` | `v2.std.concept_index` `ConceptDecl` | `qualified_name`, `name`, `node` | -| `eval_fn_arrow_decl_facts_live` | `ItemKind::FnItem \| ItemKind::FuncItem` | `v2.std.fn_index` `FnArrowDecl` | `qualified_name`, `name`, `output`, `params` | -| `eval_data_init_decl_facts_live` | `ItemKind::DataItem` | `v2.std.data_index` `DataInitDecl` | `qualified_name`, `module`, `name`, `literal_fp` | - -That is the entire typed body-fact projection. `ItemKind::ServiceItem` exists in the seed's item -vocabulary and has **no accessor**. - -The one prior art, `v2.lens.effect_reach`, reads that surface and carries a `ShellExecRunSink` -variant — but `sink_kind_for_callee` decides by `string_eq` against a remembered callee-text list -(`"Run"`, `"shell.Exec.Run"`, `"Exec.Run"`), everything else falling to `UnknownHostEffectSink`. -The brief's disposition on it is *supersede, not extend*; the reason is in the table above, not -in that function: the surface it reads cannot express the alternative. - -## 2. Six axes, six remedies - -### Axis 1 — service operations and transports are absent entirely - -The fact that decides what interprets bytes as shell lives in transport declarations: - -```dag -service shell.Exec { - operation Run { - input { script: TransportScript } - transport shell { argv: ["bash", "-s"] stdin: script.body } - } -} -``` - -`ItemKind::ServiceItem` has no accessor, so no `.dag` consumer can read the operation, the argv, -or the stdin channel. The brief permits exactly one seed ("what INTERPRETS bytes as shell") and -forbids the alternatives (a file list, a call-site roster, a variant-name list). In this tree the -permitted seed is obtainable *only* from these declarations, so a census over the available -surface cannot have a permitted seed at all. - -**Remedy:** a typed service/operation/transport declaration projection. - -### Axis 2 — `FnArrowDecl.output` is a wiring-liveness skeleton, not a body - -`marshal_stmt_sequence` folds statements right-to-left carrying a live-reference set back from -the return, and grafts a `let b = rhs` **only** when `b` is referenced downstream by code that -itself reaches the return. Its own comment states the intent: a parameter used only inside a dead -`let` must be absent so it flags as a dead wire. Correct for wiring liveness; fatal here. A shell -call whose result is bound and not consumed is **absent from the input**, so `NoStaticShellRoute` -is returned for a body that contains a route. - -**Proven by execution, not by reading the marshal.** A fixture with two functions of identical -shape — one binding the call and not using it, one returning it — folded through -`fn_arrow_decl_facts_live`, atom identities collected from each `output`: - -```text -fn fixture_dead_let_shell() -> String { - let unused_result = fixture_sink(program: "echo DEADLETMARKER") - "returned-without-using-unused_result" -} -fn fixture_live_named_args() -> String { - fixture_sink2(program: "echo LIVEMARKER", args: "echo ARGSMARKER") -} - -DECL probe.fixture.fixture_dead_let_shell ATOMS: (empty) -DECL probe.fixture.fixture_live_named_args ATOMS: fixture_sink2 | echo LIVEMARKER | echo ARGSMARKER -``` - -The dead-let arm yields **nothing** — callee identity and program literal both absent; the live -arm yields both. Same construct, opposite verdict: the loss is the projection's, not the probe's. - -This is a structural false zero upstream of the detector. None of the brief's seven instrument -controls can catch it: each is a control over the detector, and the loss has already happened in -the detector's input. - -**Remedy:** a body projection whose fold is total over statements — effect positions retained -independently of return reachability. - -### Axis 3 — no named-argument edges - -`marshal_generic` emits positional child edges plus string-literal atoms, and -`hoist_call_arg_string_literal_edges` lifts literals from arbitrary depth up to the call node, -discarding which argument they came from. Argument labels never appear. The brief requires "the -exact program-channel argument edge", and §4 below shows why: in -`["sh", "-c", "command -v \"$1\"", "sh", "{command}"]` the program is the *third* element and -`{command}` is data, while in `["sh", "-c", "{command}"]` the same-named value *is* the program. -Positionally-erased arguments cannot distinguish those. - -The same executed run shows it directly: `fixture_sink2(program: "echo LIVEMARKER", args: "echo -ARGSMARKER")` projects to `fixture_sink2 | echo LIVEMARKER | echo ARGSMARKER` — three atoms in -authored order with **no labels**. Nothing says which literal was `program:` and which `args:`; -the ordering is an accident of authoring, not a carried fact. - -**Remedy:** argument edges labelled with the authored argument name, and positional index -preserved, at each call. - -### Axis 4 — no arm or occurrence identity - -Match arms are undifferentiated positional children. Nothing in the surface can construct the -brief's `BodyArmIdentity` or `OccurrenceId`. Beyond the missing field: the historical calibration -is stated at arm grain (36 production arms across 14 modules, 1 realizing), so it is not -reproducible against this surface at all — the detector could neither agree nor disagree with it. - -**Remedy:** stable occurrence identity on body nodes, and arm identity on match arms. - -### Axis 5 — callee is an authored lexeme, not a resolved identity - -Callee atoms, string literals, record-construction spellings and variant names all arrive as the -same `atom_identity_node` kind. The brief's exact-identity control has two halves. The easy half -passes: `words` and `keywords` are different atoms. The hard half fails: `"ShellCommand"` as a -string literal and `ShellCommand` as a constructor are the same atom, so a prose or literal -mention is indistinguishable from a use — the false-positive direction of the same control. - -**Remedy:** resolved declaration identity on callees, and a node-kind discriminator separating -literal, callee, constructor and variant occurrences. - -### Axis 6 — the registry is the entry's import closure, not the corpus - -Measured, not read: a probe folding `fn_arrow_decl_facts_live()` under -`--source-root dag --source-root src/v2` reported **1,698** fn/func declarations. The corpus -declares **41,965**. The surface is the *entry's loaded-module closure* — about 4% of the tree, -and its content is whatever that entry happened to import. - -This is a declared frontier, not a discovery. `v2.lens.affected_set.corpus_dependency_view` -already guards it: `corpus_dependency_view_per_pr_substrate_ready` calls -`fn_arrow_decl_substrate_is_whole_tree`, and when it is false -`ensure_corpus_dependency_view_per_pr_substrate` routes to a host refusal reading -`corpus_dependency_view per-PR execution refused ... (blocked-on-#6239)`. Fail-closed, correctly. -A census over this surface inherits that refusal. - -It is fatal for 0A specifically because the brief's corpus requirement is a denominator claim — -"Scan all authored `.dag` files under the declared production roots ... No file may disappear -through exclusion" — and here files disappear through *non-import*, silently, with no per-file -`ParseRefused` row to count them. That is DESIGN's empty-observation narrow: the population never -loaded is indistinguishable from the population carrying no route. - -**Remedy:** a corpus-grain producer whose denominator is an enumerated file set, not an import -closure. - -## 3. The worked proof — `gunbc.spark.managed_access_apply` - -The brief names four script producers reaching a host through `stdin_payload` behind -`sudo -S sh -s`, and requires that a correct detector find them **without being told their -names**. That chain proves the axes above are a capability gap, not an API preference: each hop -is one of them. - -In `privileged_leg`: - -1. `portable_remote_words(raws: ["sudo", "-S", "sh", "-s"])` — the interpreter words are a list - literal in a **named argument** (axis 3). -2. `match … { Present { value: words } => … }` — the words are bound by a **match-arm binder** - (axis 4; and the binder is not a fn parameter, so it is not even emitted as a - parameter-reference atom). -3. `shape_fleet_ssh_exec(…, words: words)` then `ssh_session_exec_portable_words_with_stdin(client_args: …, stdin_payload: …)` - — interprocedural flow through **named arguments** into a **service operation** (axes 1, 3, 5). -4. `ssh.Session.ExecPortableWordsWithStdin` declares `transport shell { argv: ["ssh", client_args] stdin: stdin_payload }` - — the shell program travels on the **transport stdin channel** (axis 1). - -No hop in that chain is visible to the available surface. A detector over it finds these four -producers only by being told their names — the defect this cut exists to end. - -## 4. The derived shell-interpretation seed - -Whichever remedy lands, the census needs a vocabulary for what interprets bytes as shell, derived -rather than remembered. The finding: the discriminator is **positional, not a program-name -list**, and the shell-interpretation locus is **not only the `extdeps` declaration layer**. - -Corpus-wide (`dag/`, `src/v2/`) there are **262** `transport shell` declarations. The -overwhelming majority are **direct execs** whose transport is merely *named* shell — `find`, -`test`, `mv`, `chmod`, `uname`, `mktemp`, `systemctl`, `tmux`, `xorriso`, `jq`, `wc`, `whoami`, -`true`. No byte of their argv is parsed as a shell program. - -**17** sites carry an argv literal whose word list names a shell interpreter, partitioned by -*locus* — the load-bearing part: - -| locus | count | files | -| --- | --- | --- | -| `extdeps` `transport shell` declarations | 6 | `extdeps/shell/exec.dag` (2), `extdeps/shell.dag`, `extdeps/ssh/session.dag`, `extdeps/entropy/entropy.dag` (2) | -| product **fn-body `ArgvCommand` record constructions** | 10 | `gunbc/fleet_probe_identity_observe.dag` (6), `gunbc/fleet_host_key_enrollment.dag` (4) | -| test | 1 | `test/manual/command_runner_local_argv_receipt_test.dag` | - -The 10 product-body sites matter more than the 6 declarations: they are `ArgvCommand { argv: -["sh", "-c", …] }` records built **inside fn bodies**, one with a *computed* program -(`argv: ["sh", "-c", pin_cmd]` in `fleet_host_key_enrollment`). A projection covering only -service declarations — axis 1 alone — would find the 6 and miss the 10, including the only -computed-program site in the tree. Both the declaration seed and real bodies are required. - -Four structural rules fall out, none a name list: - -1. **The interpreter is at the program position, and some wrappers re-root that position — but - `--` does not tell you which.** 46 argv literals contain a `"--"` element. 41 are - `ssh`-prefixed, where `--` genuinely re-roots: the words after it are a fresh argv executed on - the far host, which is why `["ssh", "{host}", "bash", "-s"]` is a shell route and - `["sshpass", …, "--", "test", "-w", "{path}"]` is not. The other 5 are not one class: - `["systemd-run", "--unit={unit}", "--collect", "--", command_argv]` re-roots into a new program - (not a shell); `["cargo", "run", "-p", package, "--bin", bin, "--", args]` passes *arguments* to - an already-named program; `["git", "-C", "{repo}", "ls-files", "-u", "--", "{path}"]` is plain - POSIX end-of-options and re-roots nothing. - - So `--` is overloaded three ways and no syntactic rule separates them. Which reading holds is a - fact about the *host program's* CLI contract — `ssh(1)`, `systemd-run(1)`, `cargo`, `git(1)` — - an `extdeps` citation duty (DESIGN §3), not something the census may infer from the token. The - census must consult a per-program re-root contract, and where none is modeled the honest answer - is `StaticShellRouteUnknown`. Treating `--` uniformly as "re-root" would read - `git ls-files -- {path}` as executing `{path}`; uniformly as "end of options" would miss every - remote route. This is the single place in the seed where a *new modeled authority* is owed - rather than a derivation. - -2. **The flag selects the channel.** `-c`/`-lc` puts the program in the *next argv element*; `-s` - puts it on **stdin**. These are different edges, and a census reading only argv misses every - `-s` route — including `shell.Exec.Run`, the tree's main script route. -3. **A program-position value is not necessarily a program.** `shell.PosixCommand.Check` declares - `argv: ["sh", "-c", "command -v \"$1\"", "sh", "{command}"]`: the script is a *fixed literal* - owned by the declaration and the caller's value is bound to the shell's positional `$1`, so it - never enters the text parsed as shell. Reading `{command}` as a program source is a false - positive. Two of the `entropy.dag` routes are the same shape — fixed literal scripts with an - interpolated `head -c {count}` length. The discriminator is which argv slot a value occupies, - which is why axis 3 (argument edges) is not a nicety. -4. **Some operations are undecidable at their declaration.** `ssh.Session.ExecPortableWords` and - `ExecPortableWordsWithStdin` declare `argv: ["ssh", client_args]` — the program position is - *inside* an opaque expansion. Whether they interpret shell depends entirely on what a caller - flows into `client_args`; the spark caller flows `["sudo", "-S", "sh", "-s"]` and makes it a - shell route. At the declaration these are `StaticShellRouteUnknown`, never - `NoStaticShellRoute`. - -Rule 4 is load-bearing for the increment: the seed alone is insufficient, and the census is -necessarily *declaration seed joined with interprocedural value flow*. Neither half is optional, -and the value-flow half is exactly what axes 2–5 remove. - -A fourth locus exists, named so it is not mistaken for absence: three test fixtures carry -`service … transport shell { argv: [\"sh\", \"-lc\", \"{script}\"] }` inside **string -literals** of `.dag` source (`pipeline_transport_emit_rest_shell_witness_test.dag`, -`transport_script_wall_compile_red_test.dag`). Those are program text *about* shell transports, -not declarations; they belong in the brief's Test partition, and are the exact false-positive -shape axis 5 cannot discriminate. - -## 5. The full-fidelity route: measured, and it is a third outcome - -The full-fidelity route does exist in `.dag`: `v2.compiler.source_authority` -`parse_dag_source_ast` takes a `Medium` to `Outcome` where -`ParseTree = Node`, with `normalize`/`resolve` above it giving `ResolvedTree = Node`; and -`Filesystem.List`/`Filesystem.Read` are callable from `.dag` -(`v2.workflow.floor_discovery_producer` already walks scan dirs with both). - -But `parse_dag_source_ast` has **no consumer**. Its only two call sites are inside -`canonical_dag_source_parse_print_law`, whose name occurs exactly once in the tree — its own -definition. A parse/print law nothing executes: DESIGN §5's specification-without-execution -class, in the compiler's own source authority. So "the route exists in `.dag`" was not evidence -that it works, and it had to be measured. That specimen is worth a row wherever the -enforcement-intent registry ends up, independent of this census. - -### Correctness: one named grammar class refuses a large fraction of the corpus - -Three arms in one run, the first a positive control so a refusal cannot be blamed on the harness: - -```text -CONTROL_3LINE (the tree's own "module m / fn add" fixture) => ACCEPTED -REAL_SMALL (dag/gunbc/bash_materialized_transport.dag) => ACCEPTED -REAL (dag/extdeps/shell/exec.dag) => REJECTED - reason = parse_grammar_choice_overlap_residue -``` - -The route is real — it parses authored corpus source, not just fixtures — and its failure is a -**located, typed refusal naming a specific grammar deficiency**, the fail-closed -`ParseRefused { path, cause }` shape the brief anticipates. - -`extdeps/shell/exec.dag` is not a corner case. On a random 10-file sample of the real corpus: - -```text -A src/v2/std/constraint_satisfaction_predicate.dag -R dag/test/claim/filesystem_read_hermetic_witness.dag parse_grammar_choice_overlap_residue -R dag/test/claim/wet_hermetic_equivalence_witness_test.dag parse_grammar_choice_overlap_residue -R src/v2/test/claim/.../cargo_fmt_dead_param_test.dag parse_grammar_choice_overlap_residue -A dag/test/fixture/sole_constructor_sealed/admitted_caller.dag -A dag/extdeps/transports/file.dag -A src/v2/workflow/host_discovered_owned_data_manifest.dag -A src/v2/lens/structural_similarity.dag -R src/v2/test/claim/round_trip/source_authority_contract_test.dag parse_grammar_choice_overlap_residue -A dag/extdeps/cache/catalog_placement.dag - -6 accepted, 4 refused -``` - -The grouping is the finding, not the rate: **all four refusals, and the `shell/exec.dag` refusal, -carry the same reason** — `parse_grammar_choice_overlap_residue`. One grammar deficiency with many -victims, not scattered file-specific problems. That cuts both ways: the route is a single repair -away from a much larger accepted population, and no census can run on it until that repair lands, -because the brief's merge bar is *zero* production parse refusals and the refusal set includes the -census's own seed file. - -### Affordability: measured, and it is the third outcome - -Correctness alone would send out a detector that cannot run, so accepts-or-refuses was the wrong -frame. Three outcomes: refuses (substrate cut); accepts and is affordable at corpus grain -(projection lands first, census rebases); and **accepts but is unaffordable at corpus grain**, -which is neither. Measured on a random 40-file sample, ascending by size, one process per run: - -| files | source bytes | wall | exit | -| --- | --- | --- | --- | -| 1 | 372 | 63.1 s | returned `A` | -| 10 | 5,332 | 67.5 s | returned 6 A / 4 R | -| 40 | 388,527 | 187.3 s | **`EXIT=137` — OOM-killed after 34 files** | - -Two numbers fall out, pointing at different walls: - -- **Time is not the wall.** 63.1 s → 67.5 s for nine more files is ≈ **0.49 s marginal per file** - against ≈ **62.6 s fixed overhead** — paid to stand the whole corpus up before any question is - asked, which is itself the shape under discussion. -- **Memory is the wall, and it is not about big files.** The kill came at file ~34 of 40 with only - **94 KB of source consumed**, on files of ~7.8 KB each — the 212 KB outlier sorts last and was - never reached. So ~94 KB of parsed source exhausted the runner's ≈1.6 GB budget. Whether that is - parse-tree retention, interpreter heap growth, or both is **not** established by this probe and - not claimed; what is established is that the process cannot hold the result of parsing 34 small - files, against a census subject of 3,733 files and 31.3 MiB. - -The census fold accumulated only a short result string, so the retention is not the probe's -accumulator. A corpus-grain fold over this route does not fit in a process today. - -### The verdict: outcome 3, and DESIGN has already rejected this shape once - -Recorded because it converts a performance observation into a repository ruling. DESIGN §6's -lens bullet records the deletion of the corpus-wide censuses in #8140 (2026-08-11) and states the -defect in general terms: *"the unit of computation was the world, the unit of fact was one -module's authorship, and the price was paid by every consumer that wanted a witness roster and -nothing else."* Its declared next-rung trigger is the seed-side shape: an authorship fact -*"belongs on the module's own declaration, checked at ingestion where the module is parsed -anyway — one module's facts from one module's source — rather than reconstructed corpus-wide by a -consumer that wanted something else."* - -That is axis 1's remedy with a precedent and a cost argument attached. A `ServiceItem` accessor -plus a non-lossy body projection pays per module at the point the module is *already* being -parsed; a corpus-wide parse fold re-parses the world on every run to answer a question about -shell routes — 62.6 s of fixed world-acquisition before the first question, and an OOM before the -34th file. - -The measurement lands on outcome 3: the corpus-parse route is the exact cost-shape defect this -repository deleted machinery over, and it additionally fails the correctness axis today. **The -recommended increment is therefore the ingestion-side projection, not a census-side fold**, and -0A rebases onto it: - -1. a `ServiceItem` accessor carrying operations, transports, argv and stdin channels (axis 1); -2. a non-lossy body projection — total over statements (axis 2), labelled argument edges (axis 3), - arm and occurrence identity (axis 4), resolved callee identity with a node-kind discriminator - (axis 5); -3. a corpus-grain denominator that is an enumerated file set rather than an import closure - (axis 6). - -`parse_grammar_choice_overlap_residue` is worth filing separately whichever route wins: a single -named grammar deficiency refusing a large fraction of authored source in the compiler's own -parser, currently invisible because the only code path that would surface it — -`canonical_dag_source_parse_print_law` — has no callers. diff --git a/docs/plans/spark-fleet-hand-edit-gap-analysis.md b/docs/plans/spark-fleet-hand-edit-gap-analysis.md deleted file mode 100644 index 3a9dea2a470..00000000000 --- a/docs/plans/spark-fleet-hand-edit-gap-analysis.md +++ /dev/null @@ -1,180 +0,0 @@ -# Spark fleet: hand-edited state with no modeled authority - -**Status 2026-09-02, corrected.** Every row below was established by reading the live -hosts and comparing against the module named as its authority. The instrument for the -desired side is `gunbc.spark.serving_unit_render spark_serving_desired_user_unit_text`; -for the live side, the unit file and `/api/ps` on each host. - -## Correction, and it is the most important thing in this document - -**The first version of this analysis was measured against the wrong tree, and its two -headline findings were false.** It was derived by running the renderer from a session -branch based on `373b8d11`, and `main` had since advanced. Re-derived against -`origin/main`: - -- **Context is NOT diverged.** `spark_serving_desired_context_length` is `1048576` on - main, matching the live hosts. The claimed `131072` was this branch's stale value; main's - own annotation records that exact number as the thing it replaced. -- **`OLLAMA_NUM_PARALLEL` is NOT missing.** `extdeps.ollama.server_env` carries it as a - typed axis, `serving_desired` carries a `PositiveSlotCount` of 4, the renderer emits - `ollama_num_parallel_env_assignment`, and - `test.claim.spark.spark_serving_unit_render_witness_test` holds the renderer to it. - -So **the freeze recommendation that followed from those two rows is withdrawn.** On those -axes a convergence run would not degrade the fleet; it would agree with it. - -The failure was mine and it is worth naming as its own class, because it is the exact -defect this repository spends its review budget on: I ran a real instrument, got a real -number, and read it as a fact about the fleet when it was a fact about **my branch**. A -measurement is only as current as the tree it was taken from, and a session branch is not -the authority. Re-derive against `origin/main` before reporting a divergence. - -## Why this document exists - -The fleet is being assembled by hand while the model that is supposed to own it is -elsewhere. That is a legitimate way to move, but it has a specific failure mode worth -stating up front, because it is already loaded and armed: - -**A convergence run today would degrade the fleet, silently and without failing.** The -modeled desired unit differs from the live unit on both serving hosts in ways that all -point the same direction — narrower context, no concurrency, a different model. Nothing -in that path refuses; the unit rewrites, the service restarts, and it serves. The numbers -every serving decision is currently reasoned from would be invalidated without a single -error. - -So this is not a tidiness ledger. It is the list of things that must land before -`gunbc.spark` convergence may be actuated against `srv5`/`srv6` again. - -## The divergences - -### 1. Context window — WITHDRAWN, see the correction above - -Main desires `1048576`, which is what the hosts serve. No divergence. - -### 2. Concurrency — WITHDRAWN, see the correction above - -Main models the slot count as a typed `PositiveSlotCount` of 4, renders it, and witnesses -the rendering. No divergence. - -### 3. Served model — CLOSED - -`gunbc.spark.serving_desired` selected a gpt-oss build while both serving hosts ran -DeepSeek-V4, and the two are not variants of one choice — different publisher, family, -artifact and runtime footprint — so no evidence gathered against one answered for the -other. - -`spark_serving_desired_manifest` now names the DeepSeek manifest, resolved through -`gunbc.ollama_model_resolution` with the provenance arm that records it as locally -materialized rather than upstream-published, and `gunbc.spark.serving_model_admission` -requires an exact runtime version AND a manifest-identity match before a local artifact is -admitted. The divergence INSIDE the model closed with it: the rendered `Description=` was a -second, stale spelling of the model name and is now derived from -`spark_serving_desired_model_ref` rather than authored beside it. - -What this did NOT close, and the reason the ordering below still starts here: the desired -model is now correct, and reproducing that artifact from desired state remains impossible, -which is carried as a declared §4b(3) rung drop rather than as silence. - -### 4. Residency: modeled and live both silent on `OLLAMA_KEEP_ALIVE` - -Neither the desired unit nor the live unit sets it, so a 94 GB model is evicted after the -default idle period and the next request pays a full reload. Measured today: both `srv5` -and `srv6` reported `none resident` minutes after serving a request. Any latency -measurement that does not declare residency state is measuring two different things -depending on when it ran. - -### 5. The second pair is entirely outside the model — AND IS NOT TWO SERVING HOSTS - -This section originally described the pair as two independent ollama serving hosts. That -was the state when it was written and it is not the state now, and the difference is -load-bearing rather than a refresh: `192.168.1.232` runs a **llama-server front door** on -`:30000` whose weights live on `192.168.1.233` behind an `ggml-rpc-server` reached over the -RoCE fabric. They are ONE serving unit split across two machines, not two of anything, and -a roster that admits them as two serving cells would plan an 86.7 GB load onto the weights -peer — which has roughly 16 GiB free and swap disabled, so the load is an OOM kill that -takes the front door down with it. - -`gunbc.spark.serving_unit_observed` now carries that subject with the split as a -constructor condition. The membership gap below is unchanged and is what remains open; -what changed is what membership would have to mean. - -`spark-c2b1` (192.168.1.232) and `spark-ac79` (192.168.1.233) are provisioned, serving, -and unknown to every authority that should own them: - -- no `SparkCellRoleAssignment` — `gunbc.spark.cell_role` knows only `srv5` and `srv6` -- no fleet slot, so `gunbc.network_identity_subsumption` cannot bind them -- their static DHCP reservations are refused by design by that same module, so they have - no modeled network identity even in principle -- different service user (`briansrls`, not `gunbc-automation`) -- different install root (`/usr/local/ollama`, not the install-paths authority's tree) -- different unit name, and on `.232` not an ollama unit at all -- `OLLAMA_KEEP_ALIVE=-1` on the ollama that `.233` still runs alongside its weights role, - which the modeled unit has no field for — and that ollama is itself the hazard in the - paragraph above, because it WILL accept a load the weights peer cannot survive - -The runtime itself is the one thing that IS aligned: the pinned `v0.32.9` arm64 asset was -installed after verifying its SHA-256 against `extdeps.ollama.binary_release`, so the -runtime materialization identity matches the first pair rather than drifting to `latest`. - -### 6. Credentials are hand-carried - -`gunbc.spark.credential_workflow` models a `spark-administrator-password` `SecretRef` -resolving through `gunbc.auth.secret_ref_credential`, and its own note says wet entry -points name attempt identity and host on argv and never plaintext passwords. The password -in use today was passed in chat and now exists in at least two session transcripts. SSH -keys were installed by hand on top of it. - -The modeled path exists and was bypassed. That is the §6 out-of-band actuation tell in its -plainest form, and the credential should be rotated once the fleet is stable. - -### 7. ~~Runner configuration lives in the environment, and the identity model reads argv~~ - -**CLOSED.** `gunbc.model.ollama_choice` no longer keys a realization on argv. The carrier -is `ObservedOllamaLaunchConfiguration`, a closed projection of the two environment-backed -causal axes — `server_default_context` and `serving_slots` — and -`ExactRuntimeConfigurationIdentity` hashes the canonical wire those values render to -through `ollama_context_length_env_assignment` and `ollama_num_parallel_env_assignment`. -So the two runners this section said would collide now carry distinct identities. - -Three details of the closure are worth carrying forward, because they are not what this -section asked for: - -- It was **replaced, not widened**. Argv was never the right subject; the authority for - what configures an Ollama server is `extdeps.ollama.server_env`, which already holds the - variables as typed axes. -- The partition is **owned by the module, not supplied by the caller**. The predecessor - took a `varied_flags` list as an argument, which let any caller erase a causal axis from - the fixed mode and thereby authorize evidence transport across it. -- Identity runs **typed values first, wire second** — the module never parses an observed - string, because a string-to-integer admission here would be a second numeric authority. - -The ruling this section asked for was given and is recorded at item 4 below. - -## What has to happen, in order - -1. ~~**Decide the served model in the model.**~~ CLOSED — see §3. The desired row names the - DeepSeek manifest and admission requires an identity match, not a name match. -2. **Model residency.** `OLLAMA_KEEP_ALIVE` appears nowhere on main, so a 94 GB model - evicts on the default idle timer and every measurement silently depends on when it ran. -3. **Admit the second pair** — cell roles, host identities, and whatever network identity - can be bound given that static reservations are refused by design. Admission must admit - ONE serving unit, not two serving cells; see §5. -4. ~~**Widen the realization carrier to environment.**~~ CLOSED — see §7. The ruling was: - consume a runtime-owned closed projection of causal configuration, not the whole - process environment. `OLLAMA_CONTEXT_LENGTH` and `OLLAMA_NUM_PARALLEL` are - configuration; `OLLAMA_HOST` is deployment identity; `OLLAMA_MODELS` is - artifact-resolution provenance already subsumed once the artifact digest is joined. - It landed in #9897, and `ObservedOllamaLaunchConfiguration` carries exactly that - partition. -5. **Route credentials through the modeled `SecretRef`** and rotate the current one. - -## The standing risk this document is really about - -After the correction, and after the served-model divergence closed, the remaining -divergences are narrower still but they are the same class: residency, the second pair, -and credentials are facts about the fleet that no authority owns. The danger is not that the model is wrong on -those axes — it is that it is SILENT on them, so nothing refuses when they drift. - -The generalization worth keeping is the one the correction taught: a divergence report is -a measurement, and a measurement is only as current as the tree it was taken from. This -document was itself an instance of the failure it exists to catalogue. diff --git a/docs/plans/srv1-sudoers-drift-observation.md b/docs/plans/srv1-sudoers-drift-observation.md deleted file mode 100644 index 335c7ee0821..00000000000 --- a/docs/plans/srv1-sudoers-drift-observation.md +++ /dev/null @@ -1,86 +0,0 @@ -# srv1 sudoers drift — observation receipt (2026-09-07) - -Authority for the class: `gunbc.recurring_failure_mode.capability_arrives_outside_the_converged_path`, -Specimen C. This file is the detail behind that row's specimen entry. - -**Why this file exists.** The observation was made on a live host while wiring the KVM applier that -row calls for. If the host is converged, the observed state is gone — so the record has to outlive -it. A chat message is not a record. - -**Handling constraint (operator ruling).** Grants are described by SHAPE — binary, run-as, whether an -argument restriction is present — and never transcribed as copyable authorization lines. The point of -this document is the *shape of the drift*, and a verbatim line would add nothing to that while making -the file itself a liability. - -## What was observed - -On `srv1`, at the time of observation: - -- The **modeled** grant artifact for this host, `provisioning/srv1/gunbc-ghrunner.sudoers`, is - **absent from the host's sudoers directory**. It is not installed under any name. -- An **untracked, hand-maintained file** stands in its place. It is not in this repository: no - authority row, no generator, no ignore rule, and no path that any modeled emission writes. -- Its mtime was **the same day it was found**, so it is maintained, not vestigial. A backup file - beside it, dated some weeks earlier, lacks three of the grants present in the live file — so those - three were **added** in the interval. - -### The shape of the divergence - -| | modeled artifact | installed file | -|---|---|---| -| run-as | `root` | `ALL` | -| argument restriction | **exact argv**, one line per permitted invocation | **none** — bare binary path | -| population | per-path, per-unit, enumerated | seven binaries, unrestricted | - -Several of the seven binaries are ordinary root escalations when granted without argument -restriction — this follows from each tool's own documented behaviour (editing the privileged -configuration, altering account group membership, creating accounts, writing arbitrary -root-owned files, running arbitrary units). **No escalation was attempted or tested.** The property -is inherent to unrestricted-argument grants on those binaries and does not need demonstrating. - -The grantee is the account that **executes CI jobs** on this host. That is what makes this a live -privilege boundary rather than only a provenance gap. - -## Why this blocked the work in flight - -The applier being wired runs one modeled argv — a supplementary-group grant for the KVM device. -While an unrestricted grant for that binary stands, **that argv is admitted by the hole, not by the -modeled line.** A success would therefore prove the over-broad grant works, not that convergence -does: the instrument and the subject share a path. That is a corrupted instrument, not a deferred -cleanup, which is why the work stopped here rather than proceeding and filing the finding. - -## The discriminator, fixed in advance - -**Stated in the authority row, not here.** The discriminating negative control, its positive -control, and why neither alone is sufficient are carried by -`gunbc.recurring_failure_mode.capability_arrives_outside_the_converged_path` — the row whose specimen -this document details. It is written there because that is the carrier the class lives on, and -restating it here would give one fact two independently editable homes. - -What this document adds is only the *occasion*: the discriminator was fixed **before** the evidence -existed, because a post-convergence green is uninformative unless what would falsify it was named -first. - -## A second, independent fact recorded at the same time - -Applying the group grant does **not** make the device usable by anything already running. The live -runner listener process holds a fixed supplementary-group set that does not include the device group. -`usermod -aG` edits the account database; it cannot alter the credentials of a running process. The -executor acquires the group only in a **new session** — i.e. after the service restarts. - -This is why the reconciler models `applied, pending new session` as an outcome distinct from both -`applied and now writable` and from failure. Collapsing them would report either a false success or a -false failure; which one occurs is determined by re-observation, never assumed. - -## How to re-derive - -Read-only, from a shell on the host: - -- list the sudoers drop-in directory with a long listing (names, sizes, mtimes) -- grep that directory for `NOPASSWD` to see each grant's run-as and whether an argument follows the - binary path -- search the same directory for the modeled artifact's filename to confirm presence or absence -- read the runner listener's `/proc//status` `Groups:` line, and compare against the device - group's gid from `getent group` - -Nothing above modifies the host. diff --git a/docs/plans/t5b-closure-bearing-serde-debug-decision-2026-08-21.md b/docs/plans/t5b-closure-bearing-serde-debug-decision-2026-08-21.md deleted file mode 100644 index 22574f76d97..00000000000 --- a/docs/plans/t5b-closure-bearing-serde-debug-decision-2026-08-21.md +++ /dev/null @@ -1,163 +0,0 @@ -# T5b decided: closure-bearing declarations split into two dispositions, not one repair - -**Session `tidy-dove-648`. Work item:** `node://adhoc-c3017faf-ad6` — "E0277 root T5b (35 sites, -largest): serde/Debug is demanded over closure-bearing values and NO derive can be added — decide -the modeling question, do not repair." This document is that decision. It does not touch -`v1.trait_derive_emit`, `v1.04_emit_info`, `v1.05_emit_rust`, or `v2.std.compilers.target_model` — -the repairs it hands off are named at the end, scoped but not implemented here. - -**Inputs.** Three prior receipts, re-verified against the live `.dag` declarations rather than -trusted as transcribed numbers (§3's citation rule — symbols below are grep-checked by name, not -carried as positions): - -- `docs/probes/e0277_partition_2026-08-21.md` (PR #8712, on `main`) — first framed root 1 ("serde/Debug - derive on a function-valued carrier") as impossible-to-derive and structural. -- `docs/plans/self-host-cargo-refusal-root-partition.md` §11.19 and §11.23 (on `main`) — named the - same root T5b, attributed 44 sites to ~12 enclosing declarations, and posed the open question - verbatim: *"should a declaration whose value contains a function be serializable at all?"* -- `docs/probes/e0277_root_partition_2026-08-21.md` (session `bright-moth-92`, PR #8731, **not yet - merged**) — re-measured T5b at E0277-only, site grain: **35 sites**, concentrated in - `v2_std_runtime.rs` (13), `v2_std_compilers_target_model.rs` (11), and three compiler-stage files - (11). Cited here as evidence, not as merged fact — its branch is `origin/session/bright-moth-92`. - -## The decision - -**T5b is not one modeling question with one answer. It is two populations that look identical at -the trait-bound grain and require opposite treatment.** "Drop serde/Debug wherever a closure -appears" is wrong for the second; "keep serde/Debug and find a wrapper" is wrong for the first. -Distinguishing them is the modeling work this root needed. - -### Population 1 — process-local realization values: drop serde/Debug/PartialEq, keep Clone only - -`v2.std.runtime` `ValueInterpreter`, `TransformInterpreter`, `BranchInterpreter`, `LoopInterpreter`, -`BindInterpreter`, `MatchInterpreter`; the `v2.std.runtime` `RuntimeBehaviorInterpreter` coproduct -wrapping them; `v2.std.runtime` `InterpretationAlgebra`; `v2.compiler.01_tokenize` -`CompiledLexRule`/`LexWalkAcc`; `std.algebra` `PartialFunction`; and `v2.compiler.05_eval` -`EffectIoEvalBundle` / `EffectIoEvalContext` / `EffectIoYieldOutcome` are, by construction, -**evaluator plumbing that exists only inside one compile process.** None is written to disk, sent -across a process boundary, or logged as a debug artifact — each is built from a live -`EvaluationAlgebra`/handler table at run start and discarded at the end. `Serialize`/`Deserialize`/ -`Debug` on them was never a consumer need; it was the derive roster applied uniformly regardless of -what the declaration is. - -**This is not a new call.** `v1.trait_derive_emit` `v1_emit_struct_derives` already special-cases -`has_fn_fields` to emit `std.trait_derive_shape` `fn_field_derive_traits()` — Clone, nothing else — -and `docs/plans/self-host-cargo-refusal-root-partition.md` §11.19 states it: *"`CompiledLexRule` -emits `#[derive(Clone)]` and nothing else, because `fn_field_derive_traits()` is Clone-only — -correctly, since `Rc` is not serializable and not `Debug`."* Population 1's decision: -**apply that existing, correct rule consistently.** Per DESIGN §5, whether a declared type -transitively reaches a function value is decidable and structural, so this is a construction wall, -not per-declaration validation — one authority (reachability), derived once. - -**Why 32 of these 35 sites exist despite the correct rule: it is wired for structs but not -coproducts, and the transitive walk cannot see through either.** Confirmed by reading -`v1.04_emit_info`, not inferred from the site count: - -- `v1.05_emit_rust` `enum_derives` calls `v1.trait_derive_emit` `v1_emit_enum_derives` with **no - `has_fn_fields` parameter at all** — `v1_emit_struct_derives`'s `has_fn_fields` branch has no - enum counterpart, so a coproduct is never routed to `fn_field_derive_traits()` whatever its - variants carry. Hence `RuntimeBehaviorInterpreter`, a coproduct whose every variant wraps a - closure-bearing struct, still derives the full `payload_coproduct_derive_traits()` roster - (Debug/Clone/PartialEq/Serialize/Deserialize) — 18 of the 44 occurrences / a majority of the 35 - E0277 sites by itself. -- `v1.04_emit_info` `build_type_summary`'s enum branch hardcodes `field_type_map: empty_map()` for - every enum, where its struct branch populates `field_type_map` from the real fields. - `v1.04_emit_info` `type_summary_reaches_fn` walks exactly that map for transitive - closure-reachability, so enum variant payload types are **structurally invisible** to the - existing fixpoint (`v1.04_emit_info` `close_fn_fields`). `v2.std.runtime` - `InterpretationStructureWitness` (6 sites, holding only `Symbol` fields) is not a second modeling - case but this blind spot's collateral, most likely misattributed by the census's 200-line - proximity heuristic to a neighboring declaration that legitimately fails. It is expected to - dissolve once the two gaps above close — not a third disposition. - -**Handoff (repair, not modeling — do not do this in this PR):** thread `has_fn_fields` through -`v1_emit_enum_derives` as `v1_emit_struct_derives` already consumes it, and populate -`field_type_map` for enum variant payload fields so `type_summary_reaches_fn` sees through a -coproduct as it does a struct. Both are decidable, mechanical, and covered by the existing fixpoint -infrastructure — completing an existing wall, not authoring a new one. - -### Population 2 — dispatch fused into an interface record: split it out, don't strip the record - -`v2.std.compilers.target_model` `ProducedDeclSupport` is different in kind, not degree: - -``` -type ProducedDeclSupport - = ProducedDeclUnwired - | ProducedDeclWired { - render: fn(Node) -> Outcome - scaffold_relation_rule_name: Symbol - scaffold_base_row: Node - } -``` - -`ProducedDeclSupport` is a field of `v2.std.compilers.target_model` `TargetModel`, which is not -process-local plumbing but the **per-target-language configuration record** built once per -language (`rust_target_model()`, `python_target_model()`, and eight more call sites across -`src/v2/extdeps/languages/*.dag`, `src/v2/extdeps/{github,bmc}/*`, `src/v2/extdeps/formats/*`) -and consumed pervasively through the emit pipeline. Stripping `Debug`/`Serialize`/`Deserialize` -from `TargetModel` would be a real loss — comparing, inspecting, and (per the `06_translate` -consumers) potentially persisting target-model facts is what this record exists for, unlike a -`ValueInterpreter` closure table nobody prints. Population 1's answer does not transfer; this is -where §11.23's "operator question" lives. - -**The field is redundant with a fact already on the same variant.** `ProducedDeclWired` already -carries `scaffold_relation_rule_name: Symbol` — a named, resolvable identity for *which* rule -renders the scaffold. Its `render` field is that rule's own dispatch, embedded a second time as a -live closure beside the name that identifies it — the shape DESIGN §3 rules out: *"the dispatch -that selects a realization is itself realization... A pure spec is dispatch-free; a `std` -projection that matched over its realizations would have to name them, fusing dispatch back in."* -`ProducedDeclWired` should be the *interface* fact (which rule, over which base row) that -`TargetModel` carries as configuration; embedding the closure makes it carry the *realization* -too, which is why it alone among its siblings cannot be part of a serializable record. - -**Decision: remove `render` from `ProducedDeclWired`.** The variant keeps -`scaffold_relation_rule_name: Symbol` and `scaffold_base_row: Node` — plain data, derivable under -the standard record roster. The `fn(Node) -> Outcome` behavior -moves to a peripheral, non-serialized dispatch table (a `Map Outcome<...>>` or -equivalent registry) keyed by `scaffold_relation_rule_name`, resolved only where a scaffold is -rendered — never carried inside `TargetModel`. This is the named-resolvable-reference answer from -§11.23's three options, chosen over "drop serde/Debug" (wrong here — throws away a real -requirement) and over "split into description + realization as two fields on the same record" -(redundant — the description, the rule name, already exists; a second field beside it would -nickname one identity twice, which DESIGN §3 forbids). With `render` gone, `ProducedDeclSupport`, -`TargetModel`, and everything containing them keep full Debug/Clone/PartialEq/Serialize/Deserialize -— the fix removes a misplaced fact, not the record's strength. - -**SUPERSEDED, AND IN THE SAME DIRECTION.** The `render` field is gone, but not into a peripheral -registry keyed by `scaffold_relation_rule_name`: rendering is now DECLARED ROWS on the variant -(`v2.std.compilers.target_model` `ProducedDeclRenderRows`, read by the single shared fold -`produced_decl_render_from_rows`), and each target declares its own signature order as data -(`v2.extdeps.languages.rust` `rust_produced_decl_render_rows`, `v2.extdeps.languages.c` -`c_produced_decl_render_rows`). A registry would have kept the fn alive one indirection away — still -origin-bound, which is exactly what blocked the required floor from sharing one evaluation of a -target model across claim frames (`v2.workflow.floor_pure_producer_share`). Rows delete the fn -outright, so `TargetModel` is a pure content value; the interface/realization split is preserved, -the target still owns its order, and the compiler holds no per-target arm. - -**The other four `v2_std_compilers_target_model.rs` sites are very likely the same collateral -pattern as `InterpretationStructureWitness`, not independent decisions.** -`v2.std.compilers.target_model` `TargetDeriveSupplementalGenericBoundContractAuthority`, -`TargetDeriveSupplementalGenericBoundContract`, `TargetCollectionRealization`, and -`TargetRepresentationParameterSlot` — confirmed by source read to be **an empty struct**, -`TargetRepresentationParameterSlot {}` — hold no function field directly or (by inspection of their -declared fields) transitively. Their likeliest cause is proximity to `ProducedDeclSupport` within -the same module/derive-refusal cluster — the 200-line-window attribution effect the prior receipt -flagged for `InterpretationStructureWitness`. **Do not author a fix for these four independently.** -Re-measure this file after the `render` removal lands; whatever remains is a real, distinct defect -and gets its own row. - -## What this decision resolves and what it hands off - -| population | declarations | disposition | this document | handoff | -|---|---|---|---|---| -| 1 — realization values | `ValueInterpreter`+5 siblings, `RuntimeBehaviorInterpreter`, `InterpretationAlgebra`, `CompiledLexRule`, `LexWalkAcc`, `PartialFunction`, `EffectIoEvalBundle`/`EvalContext`/`YieldOutcome` | Clone-only; serde/Debug never legitimate | decides + grounds the rule already implicit in `fn_field_derive_traits()` | wire `has_fn_fields` through `v1_emit_enum_derives`; populate enum `field_type_map` in `v1.04_emit_info` | -| 1 (collateral) | `InterpretationStructureWitness` | expected to dissolve with population 1's repair | names it, does not fix it | re-measure after the repair, do not pre-emptively touch | -| 2 — dispatch-in-interface | `ProducedDeclSupport`/`ProducedDeclWired` | drop the embedded `render`; dispatch by the existing `scaffold_relation_rule_name` in a peripheral registry | decides the split and which existing fact survives as the identity | remove the field, add the registry, rewire the ~10 `TargetModel` construction sites that build a `ProducedDeclWired` | -| 2 (collateral, unconfirmed) | `TargetDeriveSupplementalGenericBoundContractAuthority`, `TargetDeriveSupplementalGenericBoundContract`, `TargetCollectionRealization`, `TargetRepresentationParameterSlot` | presumed collateral of population 2; not independently decided | names the four, declines to assume | re-measure after population 2's repair; triage what remains then | - -**What is not claimed.** This document does not re-run the probe, so it confirms neither that the -35-site count drops to zero nor that population 2's four collateral declarations resolve on their -own — both are expectations grounded in the reachability mechanism being fixed, not measured -outcomes. Per DESIGN §16 of the shared partition doc, a site count measures where the compiler -pointed; closing this decision is a repair PR's job, verified by re-running -`docs/probes/curated_cargo_probe_one.sh` against the same entry set after both handoffs land. diff --git a/docs/plans/the-emit-near-ceiling-family-is-not-a-defect.md b/docs/plans/the-emit-near-ceiling-family-is-not-a-defect.md deleted file mode 100644 index 4bc28ea5b6d..00000000000 --- a/docs/plans/the-emit-near-ceiling-family-is-not-a-defect.md +++ /dev/null @@ -1,116 +0,0 @@ -# The emit near-ceiling family is not a defect - -*A negative result, filed so the next lane skips four plays that do not work. Produced by -jolly-ferret-412 while owning the charge subject behind the required floor's 500ms per-claim CPU -ceiling. Every figure below names the producer that re-derives it; where a number and a producer -disagree, the producer is right.* - -## What this closes - -Three `v2.test.emit` identities refused the required floor on run `33695697323`. The question -routed to this lane was whether the emit family carries a repairable cost defect. **It does not.** -Four rows, two clusters, 292–350ms off the floor, none individually remarkable; what put two of -them over 500ms is the run they landed in. - -That answer is only useful with the plays that were tried attached, because each looks obviously -correct from the outside: - -| play | outcome | where it is recorded | -| --- | --- | --- | -| serve the shared producer across claim frames | **measured and refused**, three candidate rows, every one made its consumers worse | `v2.workflow.floor_pure_producer_share` `floor_cross_claim_refused_candidates` | -| find the one dominant shared producer, as gunbc#10133 did | **refuted twice**, independently | `eval_steps` fingerprint in `docs/plans/floor-cost-distribution-near-the-ceiling.md`; and the present-vs-absent runs behind the refused rows above | -| repair the worst row per-producer | **8ms**, against a question 87 rows deep | the top-down table in the same memo | -| read the interrupted rows' printed cost as a margin | **not a cost at all** — an interrupted row reports the ceiling that interrupted it | `v2.workflow.required_floor`, and the in-band sentence the floor prints beside it | - -## The four rows, measured off the floor - -Two of the three refusing identities were `budget_interrupted`, so their cost was **unbounded -above** — `required_floor_claim_cost.tsv` records them at 505/503 and 508/504, which is the -interrupt point, not a measurement. A row with no upper bound cannot be ranked, cannot be compared, -and cannot be shown to have improved by any repair, so the first step was to bound them. - -Instrument: `claim_batch --source-root dag --source-root src/v2 --entry --functions -`, one entry per module, no floor and no budget. - -| identity | floor | off-floor | -| --- | --- | --- | -| `produced_decl_two_targets_render_own_order` | ≥503 interrupted | 346ms | -| `rust_produced_decl_emits_named_add` | ≥504 interrupted | 294ms | -| `rust_produced_decl_emits_named_add2` | 489 passed | 292ms | -| `rust_produced_decl_name_discriminates` | 527 over-budget | 350ms | - -**No absolute figure crosses between those columns.** The off-floor arm is a different -architecture and a different execution context; every conclusion here uses within-column ordering -only, four rows measured under identical conditions in each column. - -## Two rows, two mechanisms - -`named_add` off the floor is 294ms against its sibling `named_add2` at 292ms — the same row within -1% — while on the floor the sibling completed at 489 and it was interrupted. **Contention**, and -three instruments that share no mechanism agree: - -1. µs/step from the floor artifact: 3.238 against the sibling's 2.990, 8.3% slower per unit of - work — derived from `cpu_ms` and `eval_steps`, which are sampled in consecutive statements after - `run_claim` returns and netted by the same shared-fill rule, so the ratio survives reordering. -2. off-floor sibling parity, above. -3. required multiple: both siblings needed ~1.70x to cross, above the measured p90, and exactly one - of them did. - -`two_targets` is the opposite. Its 188,416 steps at interrupt exceed every sibling's *completed* -total, so it does more work — but off the floor it lands at 346ms beside `name_discriminates` at -350ms. **Biggest in work, ordinary in time**, and time is what the ceiling adjudicates. A work -bound is not a cost property; inferring one from the other is the error this row exists to -document. - -## What the ceiling is actually adjudicating - -Eleven floor runs joined on claim identity: `33661252708`, `33664119594`, `33667640710`, -`33671317370`, `33688140761`, `33695697323`, `33699325123`, `33699540412`, `33700216949`, -`33700681714`, `33701938720`. - -**No identity crossed twice.** Four runs had crossings; not one identity recurs. Producer: -`gunbc.floor_cost_distribution` `crossing_recurrence` and `recurring_crossers`, whose empty answer -over single-run crossings is the finding rather than the absence of one — and whose positive -control in `test.claim.floor_cost_distribution_witness` is what keeps the empty answer from being -vacuous. - -**Crossings cluster by family within a run.** Run `33688140761`: ten crossers, all ten in -`test.claim.self_host_compile_phase_live_gate_witness`. Run `33695697323`: three, all in the emit -produced-decl family. A run tips a family; which identity inside it tips is arbitrary. - -**The run factor is large.** Over 296 identities present in all eleven runs with median cpu ≥100ms, -taking each row's cross-run median as its own baseline, the per-run median factor runs -0.858 … 1.154 — 1.35x best to worst on the *median* row, not a tail. The near-ceiling population -tracks it: rows ≥350ms per run come out 3 on the 0.959 run and 36 on the 1.154 run. Producer: -`identity_inflation_permille` composed with `permille_percentile`. - -So the run factor decides **whether** anything crosses; a row's baseline cost decides **which -family** is exposed when it does. The line adjudicates a product of two things and attributes it to -one. - -## The instrument that needs no control population - -`eval_steps` is host-independent and byte-identical for the large majority of identities across a -run pair. For those rows the only thing that could have changed did not, so any cpu movement is -inflation **by construction** — no normalisation, no control population to choose and defend. -Producer: `identity_inflation_permille_at_equal_work`, which drops any row whose step count moved -rather than smoothing it. - -Its reach must be reported beside it — `count` it against `identity_inflation_permille` — because a -clean instrument over three rows is not a measurement of a corpus. - -## What is NOT claimed - -- **The vintage confound is load-bearing.** The eleven runs span different tree vintages, so "no - identity crossed twice" is confounded by repair: the ten `live_gate` crossers stopped recurring - partly because that family was fixed. This is not a pure contention result. -- **Equal steps is equal EVALUATOR work, not equal host work.** A row whose time sits inside one - opaque host call reports few steps in both runs; `v2.workflow.required_floor` carries a declared - rung drop for that region. -- **No safe-cost figure is derived here, and one was withdrawn.** A `500 / p90` derivation was - circulated by this lane and retracted: a p90 answers a per-row question while the ceiling refuses - the whole *run*, and the stratum quoted stopped one bucket short of the population that can - actually reach 500 — where inflation rises again rather than continuing to fall. Both errors ran - in the unsafe direction. The stratified table with n per bucket belongs with the decision-maker, - and the quantile choice is theirs. -- **Nothing here is a recommendation about where the line should sit.** diff --git a/docs/plans/throughput-qualified-convergence-design.md b/docs/plans/throughput-qualified-convergence-design.md deleted file mode 100644 index f78f2e4fc44..00000000000 --- a/docs/plans/throughput-qualified-convergence-design.md +++ /dev/null @@ -1,255 +0,0 @@ -# Throughput-qualified convergence — one interface, one bound subject, one frontier - -Standing: a design under revision, not a landed capability and not yet enrolled in DESIGN. It has been -through three review rounds (side-chat, 2026-09-17); the corrections are recorded in place rather than -absorbed, because several of them are the kind of error this document exists to make harder. - -## The defect this exists to close - -**A convergence today verifies its CONFIGURATION and stops. Neither subject verifies the RATE the -configuration was chosen to deliver.** The configuration is a proxy for the goal, and reading the goal -off the proxy is the gap. - -Two live instances, each found by looking: - -- **Serving, Group B, 2026-09-17.** A converge reported success and was independently verified on four - hosts: image derived, pin correct, checkpoint loading, fp8 admission holding, KV law single-sourced. - Every one of those is true and none is about collective transport. The arm was running every NCCL - channel over `NET/Socket/0` with no IB device present, which the converge could report full - agreement through because transport was not in its comparison. -- **CI.** `docs/plans/ci-humming.md` derives a runner-slot count from a memory budget across T0-T6 and - never measures jobs per hour afterwards. T1 carries the right instinct one level down — a cap is not - effective until the live cgroup is read back — and stops at configuration. - -## Convergence and qualification are two facts, not one (corrected) - -An earlier draft said "converged" should mean configuration agreed AND the rate was met. That is -wrong, and the error is worth keeping: a realization can match its desired image, checkpoint, argv, -transport plan, capabilities, devices and rank population exactly while missing a throughput floor. -That is not configuration drift, and redefining convergence to cover it destroys a distinction the -operator needs — *state agreement* and *service qualification* have different remedies. - -`std.goal_assessment` already owns the remedy-free relation between a caller-supplied goal and an -independent observation, and deliberately keeps observation, assessment and remedy apart. So the -shape is a composition, not a redefinition: - - ConfigurationStanding = ConfigurationConverged | ConfigurationDiverged | ConfigurationIndeterminate - ThroughputAssessment = GoalSatisfied | GoalDiverged | GoalIndeterminate | GoalAssessmentRefused - ConfigurationConvergedReceipt -- mintable ONLY by matching ConfigurationConverged - ThroughputGoalSatisfiedReceipt -- mintable ONLY by matching GoalSatisfied - - type OperationalQualification sole_constructor - = RealizationQualified { - configuration: ConfigurationConvergedReceipt - throughput: ThroughputGoalSatisfiedReceipt - } - | RealizationNotQualified { - configuration: ConfigurationStanding - throughput: ThroughputAssessment - } - -**Two revisions of this carrier were wrong, in opposite directions, and both are worth keeping.** The -first offered `ConfigurationNotConverged | ThroughputNotQualified` as alternative negative arms, which -forces a reader to pick one when a realization can be configuration-diverged AND throughput-diverged -at once -- so the negative arm now carries both complete standings, and no priority fold can lose -evidence when both are non-positive. - -The second declared `RealizationQualified { configuration_receipt, throughput_assessment }` with the -assessment typed as the whole coproduct. That field proves only that SOME assessment is present, so -`RealizationQualified { throughput_assessment: GoalDiverged { .. } }` is constructible and the carrier -certifies nothing. `sole_constructor` confines construction to one module; it does not refine which arm -of a supplied coproduct is present. The refinement has to be in the TYPE: a receipt mintable only by -matching the satisfied arm. That error is precisely the one §4b names -- *a brand, wrapper or -`Validated` is cosmetic until construction and acceptance enforce the distinction* -- committed in -the paragraph that cites it. - -The structural rung is earned only when this implication holds by construction, and not before: - - RealizationQualified => ConfigurationConverged AND GoalSatisfied - -This also repairs the rung argument. A configuration convergence with no throughput result is a -legitimate value, not a defect. The invalid state to remove is a `RealizationQualified` that does not -imply both positives — and THAT is structurally impossible once the two receipts are refinement types, -because no unsatisfied assessment can be carried into the positive arm. Enrolling every production -route remains mechanically preventable. Two different rungs for two different claims, which is what -§4b(1) asks for. - -## Why this is one interface (DESIGN §2, horizontal) - -"16 decode streams against a serving arm" and "N concurrent builds against a runner pool" share an -interface: within a declared window, apply a declared workload to a subject, derive a rate from a -counter the system itself keeps, and assess it against a floor that is not a copy of the last reading. - -The partition matters more than the sharing, and DESIGN §3b's fabric row already states it for -capacity: **selection is shared; occupancy is not.** - - shared: observation and assessment interface, rate derivation, goal assessment - subject-specific: isolation acquisition, workload realization, counter producer, - subject identity, occupancy and release mechanism - -So serving's termed `product.capacity.lease::LeaseGrant` is NOT the CI window's carrier. Compute -occupancy is a timeless `LeaseIdentity` settled by `file_compare_and_set`. An earlier draft of this -document cited `std.temporal_effect::HeldLease` for both, which carries an epoch and an observed state -and **no deadline at all**; a later draft then cited `LeaseGrant` for both, which crosses the very -boundary the roster draws. Each subject brings its own occupancy carrier. - -## The parts, each with the failure it prevents - -### 1. A quiet read is not a reserved window -A CAS stops two probes believing they hold one claim. It does not stop a production request arriving -after `num_requests_running == 0`, GitHub dispatching ordinary work after a queue check, or an -already-admitted operation becoming active mid-measurement. Isolation is therefore a standing the -subject-specific handler PRODUCES, not something the generic fold assumes: - - ProbeWindowStanding - = WindowIsolated { grant, deadline, drain_receipt, exclusion_receipt } - | WindowSharedWithDeclaredTraffic { traffic_receipt } - | WindowIsolationUnread { cause } - -Serving realizes it by route withdrawal or a dedicated endpoint, a drain, and a fence against -non-probe requests; CI by a dedicated runner population withdrawn from ordinary labels, drained, with -probe-only dispatch. Without an exclusion receipt the reading is still useful production telemetry — -it is simply not a controlled qualification, and must not be recorded as one. - -### 2. The offered load is a policy, and the receipt constrains it rather than manufacturing it -Declaring the load is necessary; grounding the FIGURE is the rest. But an earlier correction here -introduced circularity — "concurrency derived from the admitted policy, and the policy owes a capacity -receipt" — which has the receipt manufacture the workload that then verifies it. The clean relation: - - QualificationProtocol declares the offered load (e.g. 16 concurrent) - ObservedCapacityReceipt may admit or refuse that policy as safe or plausible - ThroughputObservation establishes the delivered rate AT that load - -A contract may legitimately require 16 concurrent before anyone has found maximum capacity. And -`max_num_seqs` is a **scheduler ceiling, not a workload authority**: a launch configured for 16 may be -qualified at 8, 16 or another declared load depending on the contract being tested. - -### 3. Backlog is what is observed; saturation is an inference, and capacity a further one -Two drafts got this wrong in opposite directions — first "queue depth > 0 means saturated", then "a -sweep establishes it". vLLM's own metric definitions treat running as current execution and waiting as -backpressure; neither defines a physical-saturation verdict. `16 running / 0 waiting` can be fully -occupied, and `16 running / 5 waiting` can be waiting on `max_num_seqs` or KV admission with no -hardware bottleneck identified. So: - - queue > 0 does NOT establish saturation - queue == 0 does NOT establish unsaturation - -The reading names only what was observed — `BacklogObserved` | `NoBacklogObserved` | `BacklogUnread` — -and a capacity inference is a separate operation with its own declared load progression and plateau -criterion. **For an operating floor, capacity need not be inferred at all:** *under protocol P at -offered load L, did the subject deliver at least floor F?* is answerable whether or not L saturated -anything. - -*Correcting this document's own figures:* the 2026-09-17 socket readings retained no queue trace, so -they are `BacklogUnread` and `CapacityUnestablished`. The 8-to-16 scaling argument (aggregate 27.31 to -51.19 while per-stream moved 3.41 to 3.20) is useful causal reasoning and is **not** a queue -observation; assigning `UnsaturatedAtOfferedLoad` from it, as an earlier draft did, was an inference -reported as a reading. - -### 4. Subject equality alone does not make two rates comparable -`serving_performance_subject` is a configuration fence, and its own source says launch identity is a -separate boundary — several launches may share one subject. The observation therefore carries more -than the subject: - - ThroughputObservation { - subject the effective configuration - protocol concurrency, work identity, sizes, stopping rule, warmup and cache rules - realization the exact serving incarnation or runner population - window_standing isolated, shared under declared traffic, or unread - counter_producer who kept the count - counter_span before, after, and continuity across the interval - operation_ledger every offered operation reached an admissible terminal outcome - raw_carrier the bytes the reading was taken from - observed_at - } - -**Counter continuity** is its own field because a counter that reset, rebound or switched incarnation -mid-window yields a delta that is arithmetic rather than a measurement. - -**Operations and rate units are different grains**, which an earlier draft conflated by counting -"attempted, completed and refused tokens". For serving the operation is a request and the unit is a -generated token — and a request can emit tokens and *then* fail, so the ledger cannot be denominated -in tokens. For CI the operation is an exact Work/job and the unit a completed work occurrence; "jobs -per hour" means something only over homogeneous work or a declared normalization, because a one-minute -no-op and a fifteen-minute clean build are not interchangeable units. - -### 5. The rate is derived, never stored -`gunbc.harness.harness_throughput` already states this law for its own stream — counts and interval -are carried, the rate is a function of them, and storing it alongside would be a second representation -free to disagree with its inputs. Cited, not re-coined. - -### 6. The floor is not a copy of the last reading -DESIGN §5: a merge-blocking literal needs a controlled fixture, an external or versioned authority, an -explicit policy budget, or a monotone debt contract. A floor pasted from this morning's run collapses -to `measure() == measure()`. So the floor is a declared operating budget someone owns, or a monotone -contract raised deliberately, with provenance either way. The verdict is `std.goal_assessment`. - -## Conformance (DESIGN §3b) - -Rows touched, with module names as the modules declare them (an earlier draft prefixed two of these -with `gunbc.`, which is the file path and not the module): - -- **fabric / compute** — `product.capacity.lease::LeaseGrant`, `product.fabric.selection::select_supply`. - The row already states the partial sharing this design inherits: shared selection, separate - occupancy producers and linearizations. -- **leasing / locking / grants** — the serving window as a termed grant; the CI window through - compute's own timeless identity. `std.durable_compare_and_set::CasExpectation` is the linearizer and - is not by itself an exclusion receipt. -- **process observability / reporting** — `std.observation::ObservationEvent`, - `std.observation::RecordedObservation`. -- **decision / selection** — deliberately NOT touched while the probe only assesses. The moment it - ranks configurations, `std.decision` is the home and §3d binds. - -## Evidence standing for readings already taken - -An earlier draft argued the socket readings qualify as a receipt BECAUSE the arm is gone and the -question cannot be put again. That is wrong: irreproducibility establishes urgency to preserve -evidence, never provenance for it. The honest standing follows what was retained: - -| retained | standing | -|---|---| -| exact subject, protocol, incarnation, raw counter and request traces, interval | subject-bound historical observation | -| summarized numbers with no raw producer carrier | historical reported reading | -| no queue trace | `BacklogUnread` | -| successor moved transport AND other axes | no transport-only attribution | -| no exact workload identity | protocol-unbound historical claim | - -The 2026-09-17 socket figures are a **historical reported reading**: summarized aggregate and -per-stream rates, no retained raw carrier, no queue trace. They are worth recording as that, and they -are not a `RecordedObservation`. - -The arm they were taken against was converged by a lane outside the modelled transaction, so a floor -set from them grounds a claim about the SUBJECT and never about a transaction that did not run. -`AdoptedOutOfBandRealization` is this document's PROPOSED name for that standing and has no home in -the tree — it is used here in the indicative and is not yet vocabulary (review 67372). Its nearest -neighbour is `docs/plans/scaffold-admission-doctrine.md`, which owns out-of-band ACTUATION, and the -two are adjacent rather than the same question: that doctrine governs whether a hand-authored actuator -may merge, while this standing labels how an OBSERVATION OF ITS RESULT may be cited. Either it is -given a home when the design settles, or it is dropped in favour of whatever already answers that — -and until one of those happens, naming it in the indicative here is a §3 nickname waiting to happen. - -## Staging - -1. **Record the socket figures at their honest standing** — historical reported reading, `BacklogUnread`, - `CapacityUnestablished`, `AdoptedOutOfBandRealization`. -2. **The serving probe** — extdeps rows for the completions route and the metrics counters; window, - protocol, ledger, counter-span and rate folds; per-layer witnesses on supplied inputs with one - inhabitance claim that runs the real endpoint. -3. **The CI subject is a FRONTIER, not a bound subject** — which is why this document's title says one - bound subject and one frontier. `ci-humming.md` is the June slot/cgroup program and carries the - older control/apply architecture; current main has a Firecracker/JIT runner-attempt planner working - through fabric identities and execution grants. Stage 3 therefore begins with a producer-and-consumer - census of the CURRENT tree, not the historical plan, covering at least: exact Work and source-tree - identity, runner image/kernel/rootfs, microVM shape, host class, slot count and memory envelope, - jobserver and resource controls, runner/JIT revision, toolchain revision, cache topology and cache - state, and dispatch policy. Which of those invalidate a prior rate is decided by that census. -4. **The CI probe**, binding the same interface to dispatched builds. -5. **Qualification** — `OperationalQualification` composed from the two standings, with - `RealizationQualified` unmintable without its assessment. - -## Not yet done, stated plainly - -This plan is **not enrolled in DESIGN**. DESIGN.md is generated from `gunbc.design_document` and is -never hand-edited; a plan is linked from the section that governs it. A file existing under -`docs/plans/` is not that linkage. Enrolment lands when the design settles, and until then this -document governs nothing. diff --git a/docs/plans/typed-module-cross-run-materialization.md b/docs/plans/typed-module-cross-run-materialization.md deleted file mode 100644 index b00ca3a7baf..00000000000 --- a/docs/plans/typed-module-cross-run-materialization.md +++ /dev/null @@ -1,109 +0,0 @@ -# Typed-module materialization across runs - -> **Status:** DELETED (2026-10-03), after measurement. The measurement this file was written to -> run was taken on BuildBuddy (a 65 GB runner, one `claim_batch --claim-run` process over 5 entries, -> re-derived by arming `GUNBC_TYPED_STORE_PERSIST` against the same invocation unarmed): armed, the -> cold run exceeded a 720 s cap against ~150 s unarmed without evaluating an entry, and wrote -> ~7.3 GB in TWO entries; a second armed run also exceeded the cap and wrote nothing new. So each -> entry was a multi-gigabyte snapshot rather than one module, the store was a large net loss, and -> nothing bounded its root or cleaned it. The realization (`PersistentTypedStore`), its arming -> switch, its `cli_run` call sites and the `gunbc.floor_materialization` provider row were deleted -> in one change; the cross-process demands that row discharged are still declared there and now -> refuse with no provider. Sections 1-4 below describe the deleted design and are kept only as the -> record the open compiler-identity roadmap node still cites; nothing below is current behaviour. -> The replacement is a `TypecheckModuleRequest` store realized through -> `extdeps.realization.materialization_store_local`, encoded per module, bounded by a byte ceiling -> with oldest-generation eviction, under a scoped root that cleans itself. -> **Authority it answers to:** `DESIGN.md`; the ladder's admission rules in -> `std.materialization_ladder`; the retention vocabulary in `std.cache_interface`. -> **Framing:** relief during the v1 → v2 migration. Not a parallelism project. -> **Supersedes the design landed in #11779**, which this file replaced in place, as that design's -> own status line prescribed for acceptance. Three of its claims did not survive implementation and -> are withdrawn rather than quietly dropped: the `SharedStateFrame` and `CasTier` choices (§5), the -> statement that no content key existed, and the cross-push relief its key could not deliver (§2). -> Its projected figures — 8.3 min → ~1 min, 32 min → ~20 min per push — were never measurements and -> are not carried forward; §3 is what would establish them. - ---- - -## 1. What this is - -The typed-module snapshots the in-process tiers already produce now have a host-local, -content-addressed backing, so the value survives the process that computed it. It is one ladder -rung — a second provider over the identity `union-typecheck-store(dag,src/v2)` that the in-process -provider already covers — and not a new cache: the byte transport, the content key and the seam -are the ones that were already there, and only the backing changed. - -## 2. What it delivers, and what it does not - -**Delivered: same-build, cross-process reuse.** Two processes running ONE built binary over one -corpus — the witness fold and the generated-artifact drift gate — each pay typecheck preparation -in full today. The second one now reads the first one's results. - -**Not delivered: cross-push reuse.** The typed-module content key's compiler-identity term is -`resolved_graph_cache::transform_content_digest()`, the content hash of the running binary, and -`src/v1/stage0/build.rs` embeds `GUNBC_BUILD_IDENTITY` — the checkout's commit — into that binary -through `cargo:rustc-env`. A rebuild at any new commit, INCLUDING a corpus-only `.dag` commit, -therefore re-keys every entry. Any claim that an unchanged module survives a push is unsupported -until compiler identity is derived from something invariant under a corpus-only commit, which is a -change to that derivation and not to this store. **The design this file replaces asserted that -relief; that assertion does not follow from the key it proposed, and is withdrawn.** - -The bound is also narrower than "preparation" within one run: a hit skips `collect_parent_envs` -and the typecheck compute, which is where the preparation wall is spent, but the module is still -parsed and resolved, because that is what derives the key. Parse is not relieved. - -## 3. The measurement that decides whether this was worth it - -Not yet run. It is three demonstrations, and the first two are what "working" means here: - -1. **Cold → warm, separate processes.** Run the fold and then the drift gate over one corpus under - one built binary with the store armed, against the same pair with it unarmed. Compare end-to-end - wall, peak RSS, and the fold's subject digest — which must be EQUAL, because a store that - changes a verdict is a defect and not a saving. Read the `[typed-store-persist]` line on the - floor receipt for hits, misses, rejections, refusals, throughput and LIVE OCCUPANCY. -2. **An invalidation control.** Edit one module in the corpus and confirm that module and its - dependents miss while the rest hit, and that no obsolete result is served. -3. **A failure control.** Point the store at an unwritable directory, truncate an entry, and fill - it past its ceiling. Each must produce a counted cold or refused path. A verification mismatch - must be reported as `persist_rejected`, never folded into an ordinary miss. - -The Rust-level evidence for (3) and for the store's own properties is -`persistent_typed_store_tests` in `v1_compiler.shared_typecheck_store`, including the ceiling -refusal, the truncation rejection, the key-mismatch rejection and the residency-versus-throughput -distinction. Those are unit-level; (1) and (2) are the production-path receipt and are outstanding. - -## 4. How to arm it - -Off by default. Three environment variables, read once per process: - -- `GUNBC_TYPED_STORE_PERSIST=

` — the host-local directory. Arming is naming it. -- `GUNBC_TYPED_STORE_PERSIST_MAX_BYTES=` — overrides the modeled ceiling - (`floor_typecheck_store_persist_cap_bytes`). - -The SOURCE-ROOT SET is not an environment variable: it is taken from the index that asks, because -resolution is root-relative and the roots are a fact about the computation rather than an -operator's assertion about it. A second root set inside one process refuses. - -## 5. The four operational choices, and why - -| Choice | Settled as | Why | -|---|---|---| -| Location and ownership | One host-local directory named by the operator, outside the checkout. No daemon, no network service. | The tier is `ArtifactTier`/`LocalFilesystem` by `tier_axes`; anything more is a different provider with a different correctness surface. | -| Capacity and replacement | `ByteCapacity` with an exact limit, `RefuseNewStore`. | The ladder refuses `ReleasedNever` without a ceiling AND without observed occupancy, so both are obligations. `RefuseNewStore` over LRU because replacement is a second mechanism this bridge does not need: a full store declines, counts, and the run is merely cold. An entry-count bound over a variable-size payload establishes no byte bound. | -| Warm-seeding | Ordinary runs populate it. No seeding workflow. | A seeding job is a standing cost defended by a benefit nobody has measured yet. | -| Verification | The cold/warm procedure in §3, operator-invoked. | A scheduled job costs a full preparation by definition and is not a prerequisite for the first relief. | - -## 6. Deletion trigger - -This is a bridge over the v1 seed, and it ends with its consumer, not with a date: **when the -authoritative native route replaces the v1 preparation this store sits behind, its persistence -realization, its arming switch and its provider row are removed together.** v1 deletion does not -wait for this optimization to become a general platform. The rows are all named -`floor_typecheck_store_persist_*` in one module and the realization is one section of one file, so -the removal is a deletion rather than an untangling. - -## 7. Non-goals - -Threading the resolver, re-sharding the fold, and moving the fold to a hosted runner are all out -of scope. This change removes repeated work rather than distributing it. diff --git a/docs/plans/unconsumed-module-census.md b/docs/plans/unconsumed-module-census.md deleted file mode 100644 index 374f99356bb..00000000000 --- a/docs/plans/unconsumed-module-census.md +++ /dev/null @@ -1,1098 +0,0 @@ -# Corpus-wide unconsumed-module census - -**Execution record:** the disposition of this population -- what was deleted, and the typed -reason every held row survived -- is -[`unconsumed-module-residue-disposition.md`](unconsumed-module-residue-disposition.md). -This document stays the authority on how the population is derived. - -**Status: census only. Nothing is deleted by this document.** It exists to be reviewed -before any uprooting, per the dispatch: *"337 is too many to delete on one session's -judgment."* - -Operator directive it serves (2026-08-21, verbatim): *"yes please make sure to clean up -anything without consumers that we don't need, or get them actually consumed."* Both arms -are live — a module that **should** be consumed and is not is a missing-consumer defect, -not residue. - -## 1. The defensible number - -| quantity | value | unit | -| --- | --- | --- | -| `.dag` modules under `dag/` + `src/v2` | 3816 | DistinctModuleCount | -| consumed by discovery, not by import (`/test/`, `*_test.dag`, `/lens/`, `/manual/`, `/fixture*`) | 1928 | DistinctModuleCount | -| additional roots: named by an entry row (argv `--entry` or an `*entry*:` path field) | 48 | DistinctModuleCount | -| additional roots: carrying a v1 seed mirror in `src/v1/stage0/src/.rs` | 79 | DistinctModuleCount | -| reachable from those roots through imports **and qualified calls** | 3518 | DistinctModuleCount | -| **unreachable on imports, qualified calls, entry rows and seed mirrors** | **298** | DistinctModuleCount | -| — of those, **consumed by bare-symbol whole-pool reference** (§2 defects 6 and 7, re-scored 2026-08-22) | **≥ 92** | DistinctModuleCount, `LowerBoundOnly` | -| **unreachable after the re-score — the population as it now stands** | **≤ 206** | DistinctModuleCount, `LowerBoundOnly` | -| — of those, **residue on every decoded surface** — what a deletion lane consumes (§4h) | **≤ 112** | DistinctModuleCount, `LowerBoundOnly` | - -**Every number in this table is a BOUND, not a measurement, and the inequality signs are -load-bearing.** They sit in the cells because *a bounded number quoted without its boundary -becomes an exact one in the next reader's hands* — which is how **298** left this document and -became a deletion list. 206 and 112 are `LowerBoundOnly` for the same reason 298 was, and the -named blind surfaces (§2: dynamically composed argv, host-side invocation naming neither path -nor module, an entry row declared inside an unreachable module, and the service-namespace -credit of §2 defect 7(e)) can only move modules **out** of both. A reader who decodes an -eighth surface should expect both to fall again; that is the standing working, not failing. - -**The 298 is an over-count by at least 92 modules — roughly 31% — concentrated in the batch -this document proposed to delete first.** It is in the headline table because the headline -number is the one anyone acts on. **The two numbers answer different questions and must not be -substituted:** ≤ 206 is the *unresolved population*; ≤ 112 is what a deletion lane may -actually consume — the 94-module difference is `AMBIGUOUS-SHARED-ONLY` plus `MISSING-FILE`, -rows unresolved at identity grain rather than proven residue. Method and buckets: §2 defect 6; -residue list: §4h. - -Every number here carries its unit, and two are never interchangeable: a **DistinctFileCount** -(how many files name a thing) and a **SourceOccurrenceCount** (how many times it is named). -Reporting one as the other produced the false escalation in §4b; the discipline is cheap: name -the unit. - -**298, not 337.** The inherited figure was an upper bound over a different question (*zero -importers*), correctly labelled as such. Two independent corrections move it in opposite -directions and do not cancel: - -- *Zero-importer is too narrow.* It cannot see an **island** — a cluster whose members import - each other and nothing outside imports any of them. `extdeps/colo/` is one: 18 of its 19 - modules have zero importers, and the nineteenth (`extdeps.colo.types`) has eighteen, all - from inside the island. Reachability from roots catches all 19. -- *Zero-importer is also too wide.* See the instrument defect in §2. - -Re-derived from scratch; the inherited number was not reused. - -## 2. Instrument: the universe it decodes, and the defects found in building it - -**One cause produced most of what follows.** Every defect below except 1 and 4 has the same -shape: *a conclusion drawn without reading the surface that would settle it.* Defect 3 scored -the accelerator family dead without reading qualified calls; defect 5 froze two `tools/` rows -against a real re-add anchor without reading the `entry:` field surface, and ten more of that -class turn out to be invoked (see the re-score below); defect 6 scored `module_refs` dead -without reading bare-symbol resolution; defect 7 scored `pr_digests` dead without reading -variant constructors, in an instrument built to catch exactly that. The sequencing inversion -in §5 is the same failure applied to a decision. **In every instance the reasoning was locally -valid and the missing surface was the whole error**, so the review question is not "was this -argument sound" but "which surface settles this, and did you read it". - -**The rule that catches all of it, stronger than any defect below:** *a control derived from -the measurement it controls does not discriminate that measurement's blind spot.* Defect 7 was -invisible to every counter this census produces — the reachable count and the -CONSUMED-DECISIVE count both come from the defective instrument, so they could not register -the case it could not see, and read as reassurance *precisely because they stayed consistent*. -What caught it was the **required floor**, an instrument sharing no method: it refused a -deletion with `unresolved type MergeReadinessVerdict`. A census's own numbers are never its -control; only an independent mechanism is. - -**This rule is self-demonstrating twice in its own filing.** Defect 7 was checked against this -census *only* because an independent instrument refused on a different branch — nothing here -prompted it, and the counters had been stable across every revision. And the three further -defects inside its fix (generic headers, multi-line variant records, `operation` declarations) -were found by **hand-reading individual rows before publishing a number**; no counter surfaced -any of them, and the counters stayed consistent while all three were live. For anyone -extending this document: **before publishing a count, read enough rows behind it to be wrong -out loud** — and treat a number that has never disagreed with you as untested, not confirmed. - -A corollary governing how this document is read: **where a confidence label and an explicit -standing disagree, the standing governs.** `RESIDUE-UNMENTIONED` reads as strong evidence of -deadness; §6's `LowerBoundOnly` says no class in this population is blind-spot-free. The label -sat on the thing being sequenced and the standing three sections away, so the label won and 13 -live-called modules went into the lead deletion slot. A sequencing decision citing a class -name rather than the standing is unevidenced however reasonable the name sounds. - -The same disease one level down, in the mechanism layer, is what made finding **f** plausible: -**a name that implies enrolment while the mechanism keys on something else.** Floor discovery -keys on the **file suffix**; 200 modules are *named* `v2.test.*` without it, and 198 declare -no test at all (§4h). The name asserts a test-hood nothing grants. Both corollaries are one -rule — a word doing work only a mechanism can do — and the second is self-demonstrating: this -census read those names as enrolment until the suffix was checked. - -**That rule has three independent sightings, which makes it a pattern**, found by three lanes -on one night: - -1. **200 modules named `v2.test.*`** whose file is not `_test.dag`, of which 2 declare a - test (§4h). Named as tests, discovered as nothing. -2. **`commit_closure_round_trip_probe`** (§4g): a carrier claiming its persistence is - *"verified by direct execution"* while the probe it names **is enrolled nowhere and nothing - executes it** — a §4b rung-honesty defect, volunteered by the module's own author, not - found by any census. -3. **Vacuous witnesses** in the `spark` lane, reported separately. - -The variation says where to look next: the unread surface is **discovery** in (1), -**enrolment** in (2), and **assertion content** in (3). Same shape, three mechanisms, and -nothing could refuse any of them — which is why each survived. **A claim that no mechanism can -refuse is not evidence, whatever it asserts about itself.** - -Attribution, because a lesson read as one session's mistakes gets discounted: **defects 1 and -2 were the dispatching lane's, self-reported** in the brief that commissioned this census. -**Defect 3 was in both instruments** — theirs and this one's first pass. **Defects 4 and 5 -were this census's own**, neither caught by its author: 4 by review of the first revision, -after it had produced a false escalation (§4b); 5 by an architecture ruling asking which call -surfaces the number covered. Two of five found by the measurer; three by someone reading the -measurement. - -1. **Module name is not derivable from the path.** Names come from the declared `module` - line. All 3816 files carry one (checked; zero missing). -2. **Test modules are consumed by discovery, not by imports.** Floor discovery is by - **file suffix**, not module prefix — `cli_run.rs` `floor_discovery` selects - `rel.ends_with("_test.dag")`. So the exclusion is by path/filename, and a module named - `v2.workflow.ci_materialization_emit_test` living in `ci_materialization_emit_test.dag` - *is* discovered even though its name is not under a `test.` namespace. -3. **NEW — qualified calls are consumption without an import.** `.dag` admits a - fully-qualified call with no import statement: - `dag/test/claim/accelerator_demo_gpu_witness_test.dag` calls - `gunbc.accelerator_demo_gpu.witness_m5_gpu_execution_lane_count_grounded()` and never - imports the module. An import-line census scores that module dead. Adding - qualified-reference edges (over string- and annotation-stripped source, so prose mentions - create no false edges) moved the population 333 → 303 and returned the whole - `gunbc.accelerator_demo_*` family to *reachable*. **Any earlier orphan number in this repo - built from import lines alone over-reports by roughly 10%.** - -**No instrument is committed with this document, deliberately.** A hand-authored census script -beside a substrate that already reads the module graph is the §6 manual-application tell — the -durable form is a lens over the same `Node` tree, a separate, larger piece of work. The method -is stated at the grain needed to re-derive the population independently, and §2's three -defects are what a re-derivation must reproduce to agree. - -4. **NEW — a bare substring is not a reference, and one module name is a suffix of another.** - `v2.std.verification` *contains* `std.verification`, so a substring scan reported - `std.verification` as cited by 128 files; boundary-anchored (not preceded by - `[A-Za-z0-9_.]`, not followed by an identifier character) the true count is **6 - occurrences, one of which is the module's own `module` line**. Same class as defect 3 from - the other side — an instrument reading text where it should read structure — and caught by - review, not by me. Every mention count in §3 and §6 is boundary-anchored; the census - document is excluded from its own scan, which is not pedantry: including it silently - reclassified 40 rows out of RESIDUE-UNMENTIONED by naming them. **Rule for whoever measures - next: never a bare substring for a module name.** Re-measuring the whole population this - way moved only 4 rows — a narrow class, but fatal on exactly the row the census had - escalated. - -5. **NEW — an entry row is not always spelled `--entry`.** The first revision decoded argv - `--entry` and a narrow `entry_file` form, not the `entry: "dag/…"` / `_entry_file: - String = "…"` field rows that `gunbc.ci_spec` and the emitted workflows actually use — 764 - SourceOccurrenceCount of that shape against 116 of the argv shape, so the undecoded surface - was the *larger* one. Decoding it moved the population 303 → 298 and returned three modules - to consumed, two of which this document had classified FROZEN-PENDING-RE-ADD on the - strength of a re-add anchor: `tools.floor_effect_gate_witness` and `tools.ci_heal_dispatch` - are not frozen, they are **invoked**. A carve-out argued from a named anchor was still - wrong, because the instrument had not read the settling surface. - -6. **NEW — whole-pool unqualified resolution is consumption with neither an import nor a - qualified name.** v2 resolves a bare symbol against the **whole module pool**, not a - containment scope, so a consumer may use a declaration while naming neither the declaring - module nor its file. `src/v2/lens/extdeps_shape_transport_policy/` `module_refs.dag` is the - worked case: **26 consumer files, all floor-discovered `*_test.dag`, and all 26 name - neither `v2.lens.extdeps_shape_transport_policy.module_refs` nor its path** — they write - `extdeps_cargo_build_module` bare and the resolver finds it. All 9 of the module's - declarations are consumed this way. **Scope of that 26, so a narrower probe's smaller - number is not reconciled by splitting the difference:** it counts consumers of *any* of the - module's 9 declarations. A probe of the single symbol `extdeps_cargo_build_module` returns - **13 consumer files, none naming the module or the path**. Both figures are correct at - their own scope; the class is identical. *One caveat, because it recurred:* run that probe - after this document names the module and it returns 14 with 1 naming it — the fourteenth is - **this file**. Defect 4's rule (the census is excluded from its own scan) is repeated at - the receipt because contamination arrives the moment the finding is written down. This - class is invisible to a module-name surface and a file-path surface **by construction**: - neither string occurs in any consumer. Defect 3 is its near neighbour and does not cover it - — a qualified call at least spells the module name, which is what makes defect 3 detectable - by a name scan and this one not. - - *Direction, and what is NOT claimed.* Like every §2 surface this one can only move modules - **out** of the population. It was measured on one module, reached by re-deriving this - census independently; **the whole 298 has not been re-scored against it**, so how many rows - it moves is unmeasured and no estimate is offered — §6's `LowerBoundOnly` standing already - covers this and needs no revision here. **Rule for whoever measures next, beside the - defect-4 rule:** a bare declaration name is a reference too, and in `src/v2/lens/` it is - the *normal* one. Restrict such a scan to declaration names **unique corpus-wide**, or it - reports collisions on common words (`Stage`, `Review`, `Permission`) as consumption — - measured, and the reason this receipt counts unique-owner declarations only. - -### The defect-6 re-score of the whole population (2026-08-22) - -Defect 6 was found on one module, so the population was re-scored against it. **Method**, so -it can be re-derived and disagreed with: for each of the 298, take its *declared symbols* — -not its module name or path — and ask whether any other `.dag` file names one of them -**bare**. Three restrictions, each removing a false-positive class that was **observed, not -anticipated**: - -- **`.dag` consumers only.** Whole-pool resolution is a resolver behaviour; a mention in - `.md` or `.rs` is prose, not consumption. -- **Comments and string literals stripped with a character scanner, not a regex.** The first - pass used `"(?:[^"\\]|\\.)*"` and *failed on real `.dag` strings* — - `dag/gunbc/plans/axiom_syllogism_lens.dag` embeds prose containing `\{` interpolation - escapes, the regex terminated the literal early, and the exposed prose scored - `std.syllogism` as consumed. Caught by hand-reading a sample, not by the instrument. -- **Identifiers preceded by `.` or followed by `:` are not references.** The first excludes - a qualified call's tail (`extdeps.ebay.ebay.create_offer` must not score `create_offer` - as bare) and field projection; the second excludes record fields and parameter labels. - -**Each filter is reported with its cost, because the intermediate values let a reader judge -them:** the raw pass scored **98**; excluding qualified-tail and field/label identifiers took -it to **93**; the character-scanner string strip to **91**; defect 7's variant extraction and -its three corrections to **93**; the (e)/(f) corrections below to **92**. A bare final number -would hide that no single filter dominates — itself evidence that none is a fudge factor tuned -to a target. - -**Attribution is by unique ownership, which makes a hit decisive.** A bare symbol declared by -exactly one module attributes to that module alone. A symbol declared by several does not, and -is never counted as consumption here. - -| bucket | count | meaning | -| --- | --- | --- | -| **CONSUMED-DECISIVE** | **92** | a uniquely-owned symbol named bare by a **reachable** `.dag` file. Consumed. | -| DEAD-CONSUMER-ONLY | 34 | named bare only by other modules *inside* the 298 — the island shape of §4a, still residue. **Deletes as a group or not at all**, see below | -| AMBIGUOUS-SHARED-ONLY | 93 | named bare only via symbols several modules declare; no attribution possible | -| STILL-UNCONSUMED | 78 | no bare reference on any surface | -| MISSING-FILE | 1 | appendix row whose path no longer exists | - -Verified by hand-reading the reference site, not by trusting the count: -`extdeps.filesystem.posix` ← `posix_entry_kind(t: S_IFSOCK)`; -`gunbc.host_runner_memory_cap_plan_emit` ← `expected_runner_memory_cap_apply_sheet(host:)`; -`tools.infer_semantics_witness_transport` ← `run_infer_semantics_witness()`; -`gunbc.install_media`, `gunbc.hostname_allocation`, `v2.extdeps.languages.wasm`. **Zero of -the six import the module or qualify the name.** - -**Where the correction lands, which matters more than the total:** - -| disposition (§3) | population | consumed via defect 6 | -| --- | --- | --- | -| RESIDUE-EMPTY | 8 | **0** | -| RESIDUE-UNMENTIONED | 67 | **12** | -| RESIDUE-DOC-ONLY | 28 | 12 | -| FROZEN-PENDING-RE-ADD | 13 | 10 | -| PROSE-NAMED | 79 | 36 | -| CITED-AUTHORITY | 103 | 22 | - -**RESIDUE-UNMENTIONED is the row to read.** §3 calls it *"Delete. Highest-confidence residue"* -and §5 sequences it as batch B2 because it is *"least exposed to the blind spots, because a -module named nowhere in any surface is not waiting on a surface to be decoded."* Sound -reasoning, false premise: the surface it was waiting on had not been decoded, and **12 of -those 67 are consumed by live callers**. The batch selected *because* it was safest carries -the most concentrated risk of deleting working code. RESIDUE-EMPTY at 0 of 8 is the control -showing the instrument is not finding consumption everywhere — a module declaring no symbols -cannot be bare-referenced, and it scores zero. - -**FROZEN-PENDING-RE-ADD, 10 of 13, is the second correction and repeats §2 defect 5 exactly.** -Those rows were frozen against a named re-add anchor; ten are *invoked now*, by live witnesses -— `tools.infer_semantics_witness_transport` is called bare by -`src/v2/test/claim/infer_semantics_witness_test.dag`. As with -`tools.floor_effect_gate_witness` before them, the anchor was real and the conclusion still -wrong, because the instrument had not read the settling surface. - -**The ambiguity is a finding, not only instrument noise.** 93 modules are unresolvable because -their symbols are not uniquely owned, and the distribution is not uniform: -`extdeps_external_authority_anchor` is declared by **102 modules** (the boilerplate citation -anchor of §3's extdeps duty), `extdeps_model_scope` by 27, and `main` by 11. Under whole-pool -resolution a bare `main` has 11 candidate declarers. **The repository already measures this -class every run** — the floor prints `[floor-bare-name-ambiguity] scopes_affected=961 of 1339 -names_total=87040 worst_scope=125`, so 72% of scopes carry at least one ambiguous bare name. -That line is the corroborating instrument for this bucket, independent of this census. For -*this* document the meaning is narrow: those 93 rows are **unresolved, not consumed** — they -stay in the population, and no deletion should read their ambiguity as evidence either way. - -**What this re-score does NOT claim.** Not that 206 is the true count: `DEAD-CONSUMER-ONLY` -and `AMBIGUOUS-SHARED-ONLY` are both unresolved, the appendix rows were not individually -re-read, and §6's `LowerBoundOnly` standing is unchanged and now applies to 206. It claims one -thing, enough to block a deletion: **at least 92 of the 298 have a live caller.** -206. It claims one thing, and it is enough to block a deletion: **at least 92 of the 298 -have a live caller.** - -7. **NEW — coproduct VARIANT CONSTRUCTORS are declared symbols, and declaration extraction - read only `fn`/`func`/`data`/`type`/`const`.** Defect 6 established that a bare symbol is a - reference; this is the same surface missed on its constructor half. `gunbc.pr_digests` owns - the type name `MergeReadinessVerdict` but its variants are `Ready` and `NotReady`, and - `gunbc.code_change_workflow` names `Ready` **bare, with no import**. Extraction that stops - at the `type` line does not own the variants, so `pr_digests` scored residue while - consumed. **Found by an independent instrument — the required floor, refusing a deletion - with `unresolved type MergeReadinessVerdict` and `8x undefined variable Ready` — not by - reasoning about the census.** Re-scoring with variants moves the population: - CONSUMED-DECISIVE 91 → **92**, STILL-UNCONSUMED 96 → **78**, and the §4h residue list 131 → - **112**. - - *Three further defects were found inside the fix, all by hand-verifying rows before - publishing the number, each producing a **false** consumption claim:* (a) a **generic** - type header (`type UpsertDecision

`) defeated variant extraction, so - `std.upsert_decision` did not own `Apply` and `gunbc.apply` looked uniquely to own it; (b) - a **multi-line variant record** truncated the scan region, so `llvm_ir` did not own - `Select` and `extdeps.transports.sql` looked to own it; (c) `operation Parse {` and - `operation Delete {` are **declarations in the flat service namespace**, and were read as - references. Each created *false uniqueness* — the mechanism that turns a collision into a - confident wrong attribution. Filter costs across the whole re-score: **98 → 93 → 91** - (defect 6 filters), then **→ 93** with variants and the three corrections. - - **A fourth extraction bug exists and this document's numbers postdate its fix; a fifth is a - real gap with measured zero exposure.** Both were found by the deletion lane running the - four discriminating cases published with the extractor — the argument for shipping - discriminators rather than bug descriptions, since the check found a bug its author did not - have. (d) **Same-line coproducts**: `type PrerequisiteKind = Capability | Credential | …` - puts every variant on the type line, so a scan anchored to lines *starting* with `=` or `|` - never sees them. A region-based extractor handles it, and all four cases pass on the - extractor that produced the counts above, so **every count in this document postdates (d)** - — re-verified, not assumed. (e) **`operation` / `service` / `resource` rows are - declarations** in the flat service namespace (338 / 8 / 5 in the corpus). This instrument - subtracts them from the *consuming* side but does not credit them to the *declaring* side, - so a module whose declarations were **only** operation rows would own nothing and could - score residue while consumed — the delete-a-live-module direction. **Measured exposure on - this population: zero.** No row of the 298 declares nothing-but-operations, and **not one - of the 20 operation-bearing rows sits in STILL-UNCONSUMED** (13 AMBIGUOUS, 5 CONSUMED, 2 - DEAD-CONSUMER-ONLY) — the bucket where a false residue would be dangerous contains none. - Mechanism real, no victim here; a lane extending this instrument to another population must - credit them. - - **(f) A generic type PARAMETER is a binder, not a reference — one false consumption - claim, found while implementing (e).** `type GraphInvariant { … }` binds - `Projection`; it does not reference `v2.std.projection`, which was that module's *only* - link and had scored it CONSUMED-DECISIVE. Excluding the parameter list of a generic - declaration moves it to AMBIGUOUS and takes CONSUMED-DECISIVE 93 → **92**. - - ***(f) has two halves, and the obvious fix reaches only one.*** (f1) is the parameter list - in the header; **(f2) is every use of the bound name inside that declaration's body** — - `type GraphInvariant { projection: Projection … }`, where the field type on the - next line is a *second, independent* false reference that survives stripping the header. - Reported by the deletion lane, whose extractor still failed the discriminator after - implementing (f1) exactly as first described here. - - **The tempting fix for (f2) is the delete-a-live-module direction — this document shipped - it.** The first implementation here shadowed the bound name **file-wide**, which is wrong: - a name bound as a parameter in one declaration may be **genuinely referenced in another**, - and removing those references moves a live module *into* residue. The shadow must be scoped - to the binding declaration's region — the region walk the variant extractor already - performs, run over the reference side. Correcting it **restored 5 real references and moved - 5 modules out of STILL-UNCONSUMED**, taking the §4h residue list 117 → **112**. Each of - those five was a deletion candidate solely because of an over-broad fix to a false-positive - defect. - - **The symmetry is three-way, not two.** One construct — a generic header — *defeats* - declaration extraction (bug (a)), *inflates* reference extraction in the header (f1), and - *inflates it again* in the body (f2); the body half survives the obvious fix for the header - half. A binder read as a reference is the representation-vs-subject error this document is - about, at the smallest grain the language has. - - **(e) was implemented, and implementing it confirmed the zero.** Crediting - `operation`/`resource` names moved **zero** rows — the count-based prediction held. A first - cut also credited the service's own short name (`service gcp.Metadata` → `Metadata`), was - blamed for a **false** consumption claim on `extdeps.cloud.gcp.sts`, and was withdrawn. - **Both halves of that were wrong, and the correction is recorded rather than quietly - reverted.** The false claim was caused by **(f)** — the consumer named `Metadata` as a - generic type parameter — and the service credit merely stood next to it; with (f2) scoped, - restoring the credit moves **zero rows** and `gcp.sts` stays non-consumed. And the stated - reason ("no evidence any consumer names a service bare") was refuted by the deletion lane's - seventh discriminator: **`dag/extdeps/realization/artifact_store_fs.dag` writes - `Filesystem.Write`, `Filesystem.Read` and `Filesystem.Delete` with no import naming - `Filesystem`** — a consumer bare-resolving an undotted service through the whole-pool - namespace. Worse, withdrawing the credit is the *own-nothing* direction: `Filesystem` is - declared **twice** — `service Filesystem` in `extdeps/filesystem/filesystem_io.dag` and - `resource Filesystem` in `std/resources.dag` — so dropping the service credit hands - `std.resources` **false sole ownership** of a symbol another module visibly declares. The - credit is restored, justified by correctness of ownership since the measured effect is nil. - *Both wrong answers in this paragraph came from the fix, not the defect* — now the pattern - rather than the exception, and the reason every row that moves is read before a number is - published. - - **Two non-equivalent fixes for (c), distinguished rather than reconciled.** Subtracting - declaration-keyword names from the *consumer's* reference set (this document) is more - precise; adding them to the *declaring* module's owned set (the deletion lane) is more - conservative, because it pushes a row to AMBIGUOUS where subtraction correctly leaves it in - STILL-UNCONSUMED. They disagree in a known direction, and **for a deletion lane the - conservative error is the right one** — so the implementations are deliberately not - unified: a residue *count* comes from the precise one, a deletion *set* from the - conservative one. - - **Direction, and one row that went the other way.** At population level this defect can - only move modules *out* of residue. At *row* level it is not one-directional: - `extdeps.access.zanzibar` moved CONSUMED-DECISIVE → AMBIGUOUS, because counting variants - made a symbol it appeared to own uniquely into a shared one. Losing an attribution is a - correction too. - -**The universe the 298 is over.** Call surfaces decoded: `import` lines; fully-qualified -`module.symbol` references (string- and annotation-stripped); argv `--entry` path literals; -`*entry*:` path-field rows in `.dag`, `.yml`, `.rs`, `.md`, and shell; v1 seed mirrors by -filename. Surfaces **not** decoded, each of which can only make the population smaller: - -- **Dynamically composed argv.** `gunbc.roadmap_serve` is invoked with - `--function ", svc.serve_function` — the function is a field read, not a literal. Any - entry whose *path* is likewise composed is invisible to this instrument. -- **Host-side invocation in Rust that names neither the path nor the module.** -- **An entry row declared inside a module that is itself unreachable** — admitted here as a - root, which over-admits consumption. `v2.workflow.product_receipt_stage` is rooted this - way by a dead declarer. - -**Controls, run on every pass** (a zero is readable only beside a nonzero): -`v2.compiler.compile`, `gunbc.spark.serving_desired`, `gunbc.clock_read`, `v2.std.node` -all score *reachable*; `gunbc.accelerator_demo_gpu` scores *reachable* only after defect 3 -was fixed, and it is retained as the standing discriminating control for that arm. - -**Known limits of this instrument, stated rather than left to be found.** It cannot see a -module invoked by a path assembled at runtime, and it treats an `--entry` argv literal -anywhere in the tree — including in a doc — as a root, deliberately generous: the census -over-admits consumption, so the population is a floor, and every row still needs the §3 -mention check before deletion. - -## 3. Dispositions - -Assigned mechanically from evidence, then read. The rule for each class is stated so the -row can be re-derived and disagreed with. - -| disposition | count | rule | what it means | -| --- | --- | --- | --- | -| RESIDUE-EMPTY | 8 | ≤5 lines — a `module` line and nothing else | Delete. No content to strand. | -| CITED-AUTHORITY | 103 | declares an `ExternalAuthority` anchor | **Do not sweep.** The value may be the citation (DESIGN §3 extdeps duty), not a call. Needs a per-island decision, §4. | -| PROSE-NAMED | 79 | named by live (reachable) `.dag`, `.rs`, or `.yml` source, boundary-anchored | Deleting strands a citation. Each needs the mention read before it moves: superseded, missing-consumer, or delete-with-citation-repair. | -| FROZEN-PENDING-RE-ADD | 13 | the `.dag` side of a capability whose invoker a cut removed, where the re-add is named | **Not residue.** DESIGN §3 frozen-X: deleting these deletes what the re-add queue exists to re-attach. Each row names its anchor, §4d. | -| RESIDUE-DOC-ONLY | 28 | named only in `.md`, receipts/TSVs, or other dead modules | Delete; repair the doc citation in the same diff. | -| RESIDUE-UNMENTIONED | 67 | not named anywhere in the tree outside itself | Delete. Highest-confidence residue. | - -Per-module rows: appendix, §6. - -**ENTRY-INVOKED is zero here by construction.** The brief's first job was to build the entry -index and subtract; it is built (48 entry-row roots + 79 seed mirrors) and subtracted *before* -the population is formed, so every entry-invoked module is already outside the 298. The first -revision's index was smaller (34 roots) because it decoded only the argv spelling; §2 defect 5 -is what that cost, and why this section reports the index by *surface* rather than as one -number. - -## 4. Named findings - -These are the results worth a decision, as opposed to a row. - -**a. `extdeps/colo/` — a 19-module island, zero consumers.** Real colocation vendors (Equinix, -CoreSite, Iron Mountain, QTS, …) with cited authority anchors, importing `extdeps.colo.types` -and each other, consumed by nothing. One decision, not nineteen: does a siting consumer exist -or is it planned? If not, the island is the largest single deletion in the census. Same shape, -smaller: `extdeps/formats/elf/` (7), `extdeps/container/oci/` (5), `extdeps/boot/` (5), -`extdeps/ebay/` (6), `extdeps/tcgplayer/` (5), `extdeps/llm/` (7). - -**b. `std.verification` — delete it; the escalation this census originally raised was an -instrument artifact.** The first revision reported 120 citing files and asked for an operator -decision. That count was a substring match against `v2.std.verification` (§2, defect 4). -Boundary-anchored and verified independently of the review that caught it, the real citation -surface is small and the deletion ordinary: - -- **5 genuine name references, in 2 files**, both plan carriers: - `gunbc.plans.resolver_type_name_collision_wall` and - `gunbc.plans.realization_measurement_loop`. -- **1 path reference the name-based scan could not see** — `src/v2/test/fixture/` - `frontier_probe_elision_boundary_overlay.dag` pins `"dag/std/verification.dag"` by path - **with a content hash**. A fixture pinned by content hash is a consumer: the deletion must - move it, not just the prose. This site was not in the review's count either; a name-only - census would have found it during the deletion instead of before it. - -The second-authority half is measured, not inferred: `dag/std/verification.dag` is 604 bytes -with 0 importers (`AssertKind`, `AssertionClaim`, `TestCase`); `src/v2/std/verification.dag` -is 13,813 bytes with 118 importers (`TestgenTier`, `TestClassification`, …). Same name, same -subject space, and the small one has no consumers — the §3 second-authority shape. Delete the -604-byte module and repair all three sites in one PR: a plan claim still true points at -`v2.std.verification`; one no longer true retires with the module. A pointer to a deleted -authority is not an acceptable landing state. - -**c. `src/v2/extdeps/formatters/` (9) and `typecheckers/` (2) — cited config models, no -consumer, no mention anywhere.** rustfmt, prettier, gofmt, black, ktfmt, clang-format, -swift-format, google-java-format, lean4-format, mypy, pyright. Modeled upstream config -surfaces with zero readers. Standing irony: DESIGN's fixed-point rule for the emitted mirror -is *about* rustfmt, and `v2.extdeps.formatters.rustfmt` is not what implements it. -Missing-consumer or residue — not a mechanical call. - -**d. `dag/gunbc/instruments/` — 13 FROZEN, 5 residue, 2 that turned out to be invoked, and the -split is per-module.** These are the `.dag` sides of capabilities whose invokers the floor cut -(2026-08-15) and the regen root cut (2026-08-18) removed. DESIGN's CI paragraph names a -**re-add queue** with a restoration trigger, and a module the queue exists to re-attach is §3 -frozen-X, not residue — deleting it means re-authoring it worse from memory later. So the -carve-out is granted **per module against a named anchor**, never to the group: - -- **Eight witness transports** — `parse`, `bootstrap`, `dag_collect_fingerprint`, - `infer_semantics`, `interp_recorded_fixture`, `effects_rest_transport`, - `auth_declared_but_unwired`, `v1_dag_parse`. Anchor: each wraps a Rust binary that - **`gunbc.ci_release_bins` (live, reachable) still declares as a CI release artifact**. - The binary is retained and the `.dag` invoker is the unattached half — the definition of - frozen. -- **`tools.merge_admission_capture_transport`** and **`tools.merge_admission_current_context`** - (merge-admission stamping). Anchor: the gate is named on DESIGN's own unguarded list. -- **NOT frozen after all: `tools.floor_effect_gate_witness` and `tools.ci_heal_dispatch`.** - The first revision froze them on the unguarded-list anchor. Decoding the `entry:` field - surface (§2, defect 5) shows both are named by live entry rows — **invoked**, never in the - population. Recorded rather than quietly dropped: an anchor can be real and the conclusion - still wrong when the instrument has not read the settling surface. -- **`tools.dag_compile_clean_seam`, `_seam_transport`, `_shard_transport`.** Anchor: DESIGN's - compile-clean entry-point trigger — **the weakest of the three anchors, flagged as such**: - prose, no binary, no queue line. If that trigger is judged not a queue entry, these three - separate out as residue. - -**Not on any queue, therefore residue:** `tools.build`, `tools.readme`, -`tools.roadmap_dispatch`, `tools.codegen` (empty), and `tools.gunbc_ci` — the last is -**superseded**, not merely unreferenced: it shells out to the old `gunbc ci` generate-and-run -script, and `gunbc.witness_floor_workflow` → `.github/workflows/witnesses.yml` is what runs CI -now. That is the §3 second-authority find in this cluster. - -Related, and why this cluster is worth naming: **12 of the 298 declare `main()`** — an entry -shape with no argv anywhere naming it. Four are in `tools/`; the rest are `examples/` and -three `extdeps` witnesses. An entry that lost its invoker is exactly the residue a -delete-first cut should surface loudly, and it did not, because nothing downstream could -refuse. - -**e. Eight empty modules.** `std.list`, `std.containers`, `std.import`, `std.rational`, -`tools.codegen`, `v2.bin.main`, `v2.std.inhabitant_bridge`, -`v2.std.type_expr_projection_row_schema` — a `module` line and whitespace. `v2.bin.main` -is the one to look at twice: an empty `main` is a name reserving a seat. - -**f. `src/v2/extdeps/language_model/` — 15 unmentioned rung modules** (`*_r2a`, `*_r2b`, -`*_r3_external` across Go/Python/Rust/TypeScript) beside `*_r1_test.dag` siblings that -*are* discovered. The `_test`-suffixed rungs run; their non-suffixed peers do not, and -nothing names them. Likely a ladder that stopped being climbed — but "the rung above the -one we execute" is a claim about intent, so it is flagged, not classified. - -**g. Three modules that landed AFTER this census and are unconsumed — none residue.** Surfaced -by a second census run independently on 2026-08-22 (population: every `.dag` module scoring -zero on module name *and* file path; the 14 it returned were 11 instrument artifacts of -defects 3 and 6 plus these three). All three postdate #8803, which is why §6's appendix does -not carry them. Dispositions, one per module: - -*Why a naive re-run disagrees with the 298, recorded so the next reader does not re-derive -it.* That second run scored 14 modules unreachable; **none are in this document's 298**, and -there is no contradiction. Eleven were reachable all along through surfaces this document -already names: ten by fully-qualified reference (§2, defect 3 — the run required an *exact* -match against the module name, so `extdeps.bmc.access.redfish_rbac_policy` matched nothing -because the reference string is *longer* than the module name), and one, `module_refs`, by -whole-pool resolution (§2, defect 6 — the surface that run contributed). The -`gunbc.accelerator_demo_*` four-module cluster it returned is the family §2 defect 3 records -as moved back to *reachable*, and `gunbc.accelerator_demo_gpu` is retained in §2's controls as -the standing discriminator for that arm. The remaining three are the rows below. **The general -shape, the part worth keeping:** an import-line or exact-name census over this corpus does not -merely under-count, it reports a **structural zero indistinguishable from a true zero** — -deleting `module_refs` on such a reading would have taken 26 live floor-discovered witnesses -with it while the census showed nothing. Before building a census instrument, search -`docs/plans` for the census. - -- **`gunbc.empty_decl_file_checkpoint_bypass`** (`183e50a3469`) and - **`gunbc.generic_binder_field_projection_deficit`** (`aecb1fed927`) — **KEEP; being - unconsumed is their correct state, not a defect.** Both are DESIGN §4b error-class filings: - declared rung found-at, ceiling with reason, next-rung trigger, dissolution condition. - §4b(2) *requires* a class below its ceiling to carry that row, and nothing in §4b makes a - code consumer part of the requirement — the row is the filing. Deleting them deletes the - safety ledger, and a future census scoring them residue is re-deriving a question this row - answers. **The ending event is each carrier's own stated dissolution condition, both bounded - events rather than an unbounded "later":** for the first, `lookup_checkpoint` refusing on an - empty `decl_file` with every production call site threading identity; for the second, v2 - inference resolving a generic coproduct's type argument into the arm binder. For a reader - checking whether these should carry witnesses: `generic_binder_field_projection_deficit` - states in its own header why it does not — §5 construction-over-validation: its - discriminating RED was *refused at resolve* because the invalid state has no constructor, so - the predicate and its witness were deleted rather than kept as a green that cannot go red. - -- **`gunbc.scm.commit_closure_store`** (#8807) — **DELETE. Cause: staged orphan at the wrong - grain.** Answered by the `#8820` author (SCM lane, `gentle-eagle-360`), who disposed of it - *and* refuted both arms this row originally offered — recorded because a false cause - attached to a true deletion is exactly the class this document exists to catch, and this row - had asserted both arms as the live possibilities. - - **Arm A ("the envelope grew its own save/load and this deletes with the grain it served") - was false.** `gunbc.scm.repository_envelope` contains **zero `Filesystem` operations and not - one `func`** (verified independently here) — a pure codec, `RepositoryEnvelope ↔ JsonValue`. - Nothing superseded this module, so "replaced by the envelope" would have been a false cause. - - **Arm B ("the envelope should consume it") was false, and is the load-bearing half.** The - grains differ: this module persists **one root and its closure**, while a repository has an - empty initialized state with *no root*, several commits over one shared node population, and - a checked-out selection. Wiring the envelope to a carrier that demands a root would force - `init` to invent a phantom commit — a grain mismatch dressed as a fix. - - So it was never wired, and the layer that will do this job is repository-grain and gets - written that way regardless. Deleted rather than frozen because **a surviving X is an - attractor** (DESIGN §3): while it stands, nearby persistence questions keep being answered - in a vocabulary already scheduled to die. *Citation for this row is `gentle-eagle-360`'s - disposition message, deliberately **not** a pointer into the module — the module is going - away, and a citation into it dies with it.* - - **One defect surfaced with the disposition, volunteered by its author, not found by this - census.** The module's header claims its persistence is *"verified by direct execution in - Wet mode"* and names `commit_closure_round_trip_probe` as the executable subject. True when - it was run **by hand**; not true now. **The probe is enrolled nowhere and nothing executes - it** — verified here: no reference to it exists anywhere outside its own module, and the - file is not `_test.dag`, so floor discovery never sees it. A carrier asserting executed - evidence for a probe that does not run is a §4b **rung-honesty** defect — the reported rung - exceeds what executed evidence establishes — and that is why the claim survived - unchallenged: nothing could refuse it. The class is in §2's common cause, one sighting of - three. - -**h. The list the cleanup directive actually points at is 112, not 298 — and two rows in it -are not what their names say.** Combining the re-score's buckets: **78 STILL-UNCONSUMED (no -bare reference on any surface) + 34 DEAD-CONSUMER-ONLY (named bare only from *inside* the -population — §4a's island shape, deleting as a group or not at all) = 112 modules that are -residue on all three decoded surfaces.** That is the defensible starting point for the -operator's *"clean up anything without consumers"* arm. The other 186 are not: 92 have live -callers, and 93 are unresolvable at identity grain and need a per-row read first. **The list -was 131 before defect 7, and 117 before (f2) below** — of the 19 that left it, only 2 became -consumed; the rest moved to AMBIGUOUS, meaning no longer provably unconsumed rather than -proven consumed. That distinction is why the ambiguous bucket exists. - -Two observations from reading that population, neither captured by the disposition classes: - -- **`v2.test.*` is a name, not an enrolment, and 22 of the 112 are the gap.** Floor discovery - is by **file suffix** (§2, defect 2), so a module *named* `v2.test.…` is discovered only if - its file ends `_test.dag`. Corpus-wide, **200 modules are named `v2.test.*` whose file does - not end `_test.dag`, and exactly 2 of those declare a `test fn`.** Most are ordinary support - modules consumed by real witnesses; the 22 here are consumed by nothing, and their names and - paths disagree — `v2.test.language_model.go_r1` lives at - `src/v2/extdeps/language_model/go_r1.dag`, `v2.test.algebra_laws.zip_eq_list_equality` at - `src/v2/std/algebra_laws/`. This subsumes and sharpens finding **f**: those language-model - rungs are not merely unclimbed; they are named as tests, declare no test, sit outside any - test path, and execute nowhere. **A name that implies enrolment while the mechanism keys on - something else is the more useful form of that finding** — the ladder metaphor invites - "climb it"; the measurement says "nothing here was ever wired to run." -**The 35 island rows carry a constraint that is not a preference, and it belongs on the row -rather than in a report.** A per-module verdict over a mutually-referencing island is -incoherent: **each member looks consumed until its neighbours go**, so scoring them one at a -time returns "consumed" for every one and the island never becomes eligible. They delete as a -group or not at all, and the group is the connected component, not the directory. Otherwise -the deletion lane meets this as a surprising refusal partway through a batch — the census -working, but expensively. - -- **`gunbc.spark.provisioning`'s appendix row no longer resolves** — the path it names does - not exist. It was one of the two rows §5 excluded from every batch as `fierce-lynx-647`'s - area. The deletion was deliberate — ruled by the lane that commissioned this census, on the - grounds that the model predated knowledge of the real procedure — so the row, not the - deletion, is the defect. Recorded because an appendix row pointing at nothing is the - staleness class this document polices elsewhere, and a receipt that the population has a - clock on it. - -**A hazard for whoever measures next, cheap to hit and silent.** A fresh worktree can be -**shallow-grafted** — the 2026-08-22 run found its clone rooted at a single 4608-file import -commit dated six days earlier, so `git log ` reported that graft commit as the first -commit of every older module. Any census arm asking *was this consumed until a recent cut* -(the question separating a severed consumer from §6 experimental residue) silently answers -from a truncated history. `git rev-parse --is-shallow-repository` before trusting a history -claim; `git fetch --unshallow` fixes it. - -## 5. Proposed sequencing (for approval, not execution) - -One red in a 298-file deletion blocks everything, so: small coherent batches, each its own -PR, each verified by `claim_executor --required-ci --source-root dag --source-root src/v2` -with `failed=` read and PASS counted against the roster. - -1. **B1 — RESIDUE-EMPTY (8).** Nothing to strand; a pure control batch that proves the - deletion pipeline and the floor both behave. -2. **B2 — RESIDUE-UNMENTIONED, non-extdeps (53 of 67). SUPERSEDED BY THE DEFECT-6 RE-SCORE — - do not run this batch as written.** The rationale was that an unmentioned module has no - citation to repair and is least exposed to a blind spot; the re-score (§2) found **12 of - the 67 consumed by live bare-symbol callers**, so this batch would delete working code, and - the ordering argument that put it first is what made that risk invisible. It becomes - eligible again only over the rows surviving the re-score, and B1 replaces it as the batch - to establish the mechanics on (RESIDUE-EMPTY scored 0 of 8 consumed — a module declaring no - symbols cannot be bare-referenced). -3. **B3 — RESIDUE-DOC-ONLY (28).** Deletion plus the doc/receipt citation repair in the - same diff. -4. **B4 — the five `tools/` rows NOT on the re-add queue (finding d)**, including the - superseded `tools.gunbc_ci`. The 13 FROZEN-PENDING-RE-ADD rows are in no batch: they stay - until their queued gate is re-derived, and their disposition is recorded here so a future - census does not re-derive the question and answer "residue". -5. **B5 — `std.verification` (finding b)** alone: delete the module, repoint the two plan - carriers, and move the content-hash fixture pin, all in one PR. -6. **B6+ — the extdeps islands (finding a, c)**, one island per PR, each gated on whether - the citation is the deliverable. - -PROSE-NAMED (79) gets no batch until each row's mention has been read; several will resolve to -*missing consumer* and be wired up rather than deleted — the directive's second arm. - -**Excluded from every batch:** `dag/gunbc/spark/` — `fierce-lynx-647` owns that area and is -mid-census there. Two of the 298 (`gunbc.spark.provisioning`, -`gunbc.spark.managed_access_apply`) fall in it and are reported here for their benefit -only. `gunbc.spark.provisioning` is a live instance of the dangling-annotation hazard: -`extdeps/systems/nvidia_dgx_spark_setup.dag` names it as a fact's home. - - -## 6. Completeness standing - -**`LowerBoundOnly`.** 298 resolved-unconsumed modules over the universe declared in §2. Not -`CompleteForDeclaredUniverse`, and the difference is not modesty: three call surfaces are -named undecoded in §2, and each can only *remove* modules from the population, never add. Two -prior revisions each lost modules to a newly decoded surface (333 → 303 → 298) — the empirical -case for the standing. - -What the instrument can support: *298 resolved consumers-of-none over universe U; standing -LowerBoundOnly; blind spots as named.* What it cannot, and this document nowhere claims: -*there are exactly 298 unconsumed modules.* The second sentence dies the moment someone -decodes a fourth surface; the first survives it and says what would change it. - -This standing is why §5's batches are ordered as they are — and the 2026-08-22 re-score is the -case study for why the standing exists. The original ordering argued RESIDUE-UNMENTIONED first -because *"a module named nowhere in any surface is not waiting on a surface to be decoded."* -Valid argument, false premise: a surface remained undecoded (§2, defect 6) and 12 of that -batch had live callers. **A `LowerBoundOnly` standing is not compatible with treating any -batch as blind-spot-free**, and the deletion order must follow from the standing rather than a -class name: B1 (RESIDUE-EMPTY) leads because a module declaring no symbols is unreachable by -*construction* on the bare-symbol surface, not because nobody has mentioned it. - -### Two broken entry strings, confirmed - -Decoding the entry-row surface also surfaced consumption that is *declared and broken* — -neither consumed nor residue, invisible to the import graph. Confirmed by reading both sites, -not by the count: - -- **`dag/gunbc/spark/grant_install.dag`** — `gunbc.ci_spec` `gunbc_ci_spark_grant_install_invoke` - names it as an entry with function `spark_grant_install_ci_wet`. **The file does not - exist.** -- **`dag/gunbc/spark/serving_durability.dag`** with function - `"spark_serving_durability_ci_wet"` — named by `gunbc.ci_spec` *and* by - `.github/workflows/fleet-converge.yml`. The file exists; **that function is not in it** - (its `fn`s are `spark_serving_boot_id_probe_argv`, `..._from_probe`, - `spark_serving_reboot_transition`, `spark_serving_durability_verdict`, `..._is_proven`, - `..._wire`). - -Both are in `dag/gunbc/spark/`, `fierce-lynx-647`'s area — reported, not touched. A sibling -lane found the same two independently: a second instrument agreeing, not a second report of -one measurement. - -**Not defects, named so a future sweep does not "fix" them:** the raw scan also flags -`dag/a.dag`, `dag/mini.dag`, `dag/gunbc/live_deploy/WRONG_ENTRY.dag`, -`dag/test/claim/__no_such_entry_zzz.dag` and similar. Those are *deliberate negative controls* -inside witness tests — an entry that must fail to resolve. An instrument reporting them is -producing false positives, which is what §7's last case exists to catch. - -## 7. Calibration benchmark for the next audit instrument - -Today's failures, written as cases rather than prose, so a future instrument calibrates -against a suite instead of rediscovering all five. Each case names a real subject in this -tree, the wrong answer, and the honest answer. - -| # | case | subject | wrong answer | required answer | -| --- | --- | --- | --- | --- | -| 1 | **cardinality** | `std.verification` | one number for "how cited" | **both** units: SourceOccurrenceCount 6 **and** DistinctFileCount 3 — they are different questions | -| 2 | **exact identity** | `std.verification` vs `v2.std.verification` | substring match folds them | boundary-anchored: the two stay separate, and the suffix relation never merges them | -| 3 | **representation vs subject** | any modeled capability | "spelling absent ⟹ capability absent" | a typed model present with zero occurrences of its shell spelling means capability PRESENT, spelling ABSENT — **no absence conclusion** | -| 4 | **qualified use** | `gunbc.accelerator_demo_gpu` ← `dag/test/claim/accelerator_demo_gpu_witness_test.dag` | orphan (no import line) | caller edge PRESENT; an import-line census must declare itself incomplete | -| 5 | **string-bound entry** | `dag/gunbc/spark/serving_durability.dag` + `spark_serving_durability_ci_wet` | consumed (path resolves) | **resolution REFUSED** — path present, function absent; neither consumed nor residue | -| 6 | **positive no-finding** | `dag/gunbc/live_deploy/WRONG_ENTRY.dag`, `dag/a.dag`, `__no_such_entry_zzz.dag`; and any healthy imported module | flagged as broken/orphaned | **no finding** — deliberate negative controls and ordinary live modules must come back clean | - -Case 6 is the one most likely to be skipped and the one that catches a broken audit: a suite -built only from defects is passed by an instrument reporting *everything* as suspicious. Cases -1, 2, 4, 5 each have a live specimen in this tree, so the suite is executable against the real -corpus rather than a fixture someone must maintain. - -## 8. Appendix — the 298 rows - -### RESIDUE-EMPTY — 8 modules - -| module | path | lines | live-source mentions | -| --- | --- | --- | --- | -| `std.containers` | `dag/std/containers.dag` | 2 | — | -| `std.import` | `dag/std/import.dag` | 2 | {'dag': 1} `dag/gunbc/commit_workflow.dag` | -| `std.list` | `dag/std/list.dag` | 3 | — | -| `std.rational` | `dag/std/rational.dag` | 5 | {'dag': 2} `dag/gunbc/doc_graph_roots.dag` `dag/gunbc/econ/acquisition.dag` | -| `tools.codegen` | `dag/gunbc/instruments/codegen.dag` | 5 | — | -| `v2.bin.main` | `src/v2/bin/main.dag` | 4 | — | -| `v2.std.inhabitant_bridge` | `src/v2/std/inhabitant_bridge.dag` | 4 | — | -| `v2.std.type_expr_projection_row_schema` | `src/v2/std/type_expr_projection_row_schema.dag` | 4 | — | - -### RESIDUE-UNMENTIONED — 67 modules - -| module | path | lines | live-source mentions | -| --- | --- | --- | --- | -| `config.codegen_paths` | `dag/config/codegen_paths.dag` | 20 | — | -| `examples.html_markup_smoke` | `dag/examples/html_markup_smoke/html_markup_smoke.dag` | 66 | — | -| `examples.js_site` | `dag/examples/js_site/js_site.dag` | 185 | — | -| `examples.js_site_emit` | `dag/examples/js_site/js_site_emit.dag` | 76 | — | -| `examples.nominal_distinctness_twin` | `dag/examples/nominal_distinctness_witness/twin.dag` | 15 | — | -| `examples.nominal_distinctness_witness` | `dag/examples/nominal_distinctness_witness/witness.dag` | 19 | — | -| `gunbc.assimilate.bmc_wif_canary_bootstrap` | `dag/gunbc/assimilate/bmc_wif_canary_bootstrap.dag` | 126 | — | -| `gunbc.ci_oom_reclassify` | `dag/gunbc/ci/ci_oom_reclassify.dag` | 87 | — | -| `gunbc.cursor_sdk_secure_api_key` | `dag/gunbc/cursor_sdk_secure_api_key.dag` | 73 | — | -| `gunbc.devboot.vertical_receipt` | `dag/gunbc/devboot/vertical_receipt.dag` | 68 | — | -| `gunbc.host_converge_delta` | `dag/gunbc/host/host_converge_delta.dag` | 119 | — | -| `gunbc.install_media` | `dag/gunbc/install_media.dag` | 43 | — | -| `gunbc.parse_allowlist` | `dag/gunbc/parse_allowlist.dag` | 20 | — | -| `gunbc.plans.wave2_prep_design` | `dag/gunbc/plans/wave2_prep_design.dag` | 205 | — | -| `gunbc.provider_standing_live_probes` | `dag/gunbc/provider_standing_live_probes.dag` | 194 | — | -| `gunbc.srv4_seeded_install_media_artifact` | `dag/gunbc/srv4_seeded_install_media_artifact.dag` | 47 | — | -| `gunbc.tools.bmc_onboard_validate` | `dag/gunbc/tools/bmc_onboard_validate.dag` | 22 | — | -| `gunbc.tools.ebay_listing` | `dag/gunbc/tools/ebay_listing.dag` | 143 | — | -| `gunbc.tools.roadmap_spawn_request` | `dag/gunbc/tools/roadmap_spawn_request.dag` | 36 | — | -| `std.binding` | `dag/std/binding.dag` | 7 | — | -| `std.syllogism` | `dag/std/syllogism.dag` | 82 | — | -| `tools.build` | `dag/gunbc/instruments/build.dag` | 34 | — | -| `tools.readme` | `dag/gunbc/instruments/readme.dag` | 70 | — | -| `tools.roadmap_dispatch` | `dag/gunbc/instruments/roadmap_dispatch.dag` | 17 | — | -| `v2.extdeps.formats.csv` | `src/v2/extdeps/formats/csv.dag` | 136 | — | -| `v2.extdeps.formats.openapi` | `src/v2/extdeps/formats/openapi.dag` | 459 | — | -| `v2.extdeps.formats.toml` | `src/v2/extdeps/formats/toml.dag` | 132 | — | -| `v2.extdeps.formatters.black` | `src/v2/extdeps/formatters/black.dag` | 87 | — | -| `v2.extdeps.formatters.clang_format` | `src/v2/extdeps/formatters/clang_format.dag` | 893 | — | -| `v2.extdeps.formatters.gofmt` | `src/v2/extdeps/formatters/gofmt.dag` | 14 | — | -| `v2.extdeps.formatters.google_java_format` | `src/v2/extdeps/formatters/google_java_format.dag` | 22 | — | -| `v2.extdeps.formatters.ktfmt` | `src/v2/extdeps/formatters/ktfmt.dag` | 81 | — | -| `v2.extdeps.formatters.lean4_format` | `src/v2/extdeps/formatters/lean4_format.dag` | 189 | — | -| `v2.extdeps.formatters.prettier` | `src/v2/extdeps/formatters/prettier.dag` | 191 | — | -| `v2.extdeps.formatters.rustfmt` | `src/v2/extdeps/formatters/rustfmt.dag` | 286 | — | -| `v2.extdeps.formatters.swift_format` | `src/v2/extdeps/formatters/swift_format.dag` | 110 | — | -| `v2.extdeps.typecheckers.mypy` | `src/v2/extdeps/typecheckers/mypy.dag` | 29 | — | -| `v2.extdeps.typecheckers.pyright` | `src/v2/extdeps/typecheckers/pyright.dag` | 47 | — | -| `v2.std.generic_instantiation` | `src/v2/std/generic_instantiation.dag` | 36 | — | -| `v2.std.projection` | `src/v2/std/projection.dag` | 19 | — | -| `v2.test.algebra_laws.is_prefix_of_prefix_check` | `src/v2/std/algebra_laws/is_prefix_of_prefix_check.dag` | 90 | — | -| `v2.test.algebra_laws.zip_eq_list_equality` | `src/v2/std/algebra_laws/zip_eq_list_equality.dag` | 93 | — | -| `v2.test.language_model.go_r1` | `src/v2/extdeps/language_model/go_r1.dag` | 59 | — | -| `v2.test.language_model.go_r2a` | `src/v2/extdeps/language_model/go_r2a.dag` | 59 | — | -| `v2.test.language_model.go_r2b` | `src/v2/extdeps/language_model/go_r2b.dag` | 59 | — | -| `v2.test.language_model.go_r3_external` | `src/v2/extdeps/language_model/go_r3_external.dag` | 59 | — | -| `v2.test.language_model.python_cross_runtime_drift` | `src/v2/extdeps/language_model/python_cross_runtime_drift.dag` | 49 | — | -| `v2.test.language_model.python_l2_cross_target_parity` | `src/v2/extdeps/language_model/python_l2_cross_target_parity.dag` | 81 | — | -| `v2.test.language_model.python_r2a` | `src/v2/extdeps/language_model/python_r2a.dag` | 58 | — | -| `v2.test.language_model.python_r2b` | `src/v2/extdeps/language_model/python_r2b.dag` | 53 | — | -| `v2.test.language_model.python_r3_external` | `src/v2/extdeps/language_model/python_r3_external.dag` | 58 | — | -| `v2.test.language_model.rust` | `src/v2/extdeps/language_model/rust.dag` | 256 | — | -| `v2.test.language_model.rust_r2a` | `src/v2/extdeps/language_model/rust_r2a.dag` | 57 | — | -| `v2.test.language_model.rust_r2b` | `src/v2/extdeps/language_model/rust_r2b.dag` | 75 | — | -| `v2.test.language_model.rust_r3_external` | `src/v2/extdeps/language_model/rust_r3_external.dag` | 57 | — | -| `v2.test.language_model.typescript_r2a` | `src/v2/extdeps/language_model/typescript_r2a.dag` | 58 | — | -| `v2.test.language_model.typescript_r2b` | `src/v2/extdeps/language_model/typescript_r2b.dag` | 54 | — | -| `v2.test.language_model.typescript_r3_external` | `src/v2/extdeps/language_model/typescript_r3_external.dag` | 58 | — | -| `v2.test.nat_semiring.rung_0_to_2_three_targets` | `src/v2/std/nat_semiring/rung_0_to_2_three_targets.dag` | 95 | — | -| `v2.test.nat_semiring.rung_l1_go_compiler_slice` | `src/v2/std/nat_semiring/rung_l1_go_compiler_slice.dag` | 58 | — | -| `v2.test.nat_semiring.rung_l1_python_runtime` | `src/v2/std/nat_semiring/rung_l1_python_runtime.dag` | 96 | — | -| `v2.test.qualified_name.from_node` | `src/v2/std/qualified_name/from_node.dag` | 222 | — | -| `v2.workflow.bmc_lifecycle_roundtrip` | `src/v2/workflow/bmc_lifecycle_roundtrip.dag` | 38 | — | -| `v2.workflow.ci_v1_compiler_test_targets_compile_gate_emit` | `src/v2/workflow/ci_v1_compiler_test_targets_compile_gate_emit.dag` | 110 | — | -| `v2.workflow.floor2_prepared_subject` | `src/v2/workflow/floor2_prepared_subject.dag` | 182 | — | -| `v2.workflow.gha_expression_fidelity` | `src/v2/workflow/gha_expression_fidelity.dag` | 22 | — | -| `v2.workflow.probe_selector_host_health` | `src/v2/workflow/probe_selector_host_health.dag` | 43 | — | - -### RESIDUE-DOC-ONLY — 28 modules - -| module | path | lines | live-source mentions | -| --- | --- | --- | --- | -| `examples.cost_estimate` | `dag/examples/cost_estimate/cost_estimate.dag` | 29 | — | -| `examples.gunbhub_serve_program` | `dag/examples/gunbhub_serve_program/gunbhub_serve_program.dag` | 60 | — | -| `examples.interp_test` | `dag/examples/interp_test/interp_example.dag` | 40 | — | -| `gunbc.code_change_workflow` | `dag/gunbc/code_change_workflow.dag` | 371 | — | -| `gunbc.floor_resolve_realization` | `dag/gunbc/floor_resolve_realization.dag` | 28 | — | -| `gunbc.hand_lens_host_bridge_scaffold_watchdog` | `dag/gunbc/hand_lens_host_bridge_scaffold_watchdog.dag` | 46 | — | -| `gunbc.host_runner_memory_cap_plan_emit` | `dag/gunbc/host/host_runner_memory_cap_plan_emit.dag` | 110 | — | -| `gunbc.hostname_allocation` | `dag/gunbc/hostname_allocation.dag` | 148 | — | -| `gunbc.language_subject_scope_scaffold` | `dag/gunbc/language_subject_scope_scaffold.dag` | 10 | — | -| `gunbc.p3a1_self_fork_homonym_disposition` | `dag/gunbc/p3a1_self_fork_homonym_disposition.dag` | 10 | — | -| `gunbc.pr_digests` | `dag/gunbc/pr_digests.dag` | 72 | — | -| `gunbc.site.register_principles` | `dag/gunbc/site/register_principles.dag` | 12 | — | -| `gunbc.spark.managed_access_apply` | `dag/gunbc/spark/managed_access_apply.dag` | 453 | — | -| `gunbc.tools.card_intake` | `dag/gunbc/tools/card_intake.dag` | 216 | — | -| `gunbc.tools.cron_tag` | `dag/gunbc/tools/cron_tag.dag` | 67 | — | -| `gunbc.tools.grounding_confirm` | `dag/gunbc/tools/grounding_confirm.dag` | 115 | — | -| `gunbc.witness_family_fanout` | `dag/gunbc/witness_family_fanout.dag` | 65 | — | -| `shared.dag_util` | `dag/shared/dag_util.dag` | 44 | — | -| `std.exec_format` | `dag/std/exec_format.dag` | 37 | — | -| `std.patterns` | `dag/std/patterns.dag` | 24 | — | -| `v2.extdeps.bmc.lifecycle_fidelity` | `src/v2/extdeps/bmc/lifecycle_fidelity.dag` | 146 | — | -| `v2.extdeps.formats.json` | `src/v2/extdeps/formats/json.dag` | 52 | — | -| `v2.extdeps.formats.json_schema` | `src/v2/extdeps/formats/json_schema.dag` | 103 | — | -| `v2.extdeps.formats.yaml` | `src/v2/extdeps/formats/yaml.dag` | 85 | — | -| `v2.extdeps.github.expression_fidelity` | `src/v2/extdeps/github/expression_fidelity.dag` | 54 | — | -| `v2.std.rust_leaf_model_claim` | `src/v2/std/rust_leaf_model_claim.dag` | 61 | — | -| `v2.test.workflow.host_discovered_owned_data_manifest` | `src/v2/workflow/host_discovered_owned_data_manifest.dag` | 19 | — | -| `v2.workflow.ci_stage0_partition_compile_gate_emit` | `src/v2/workflow/ci_stage0_partition_compile_gate_emit.dag` | 103 | — | - -### FROZEN-PENDING-RE-ADD — 13 modules - -| module | path | lines | re-add anchor | -| --- | --- | --- | --- | -| `tools.auth_declared_but_unwired_witness_transport` | `dag/gunbc/instruments/auth_declared_but_unwired_witness_transport.dag` | 12 | `auth_declared_but_unwired_witness` bin, still declared in `gunbc.ci_release_bins` | -| `tools.bootstrap_witness_transport` | `dag/gunbc/instruments/bootstrap_witness_transport.dag` | 12 | `bootstrap_witness` bin, still declared in `gunbc.ci_release_bins` | -| `tools.dag_collect_fingerprint_witness_transport` | `dag/gunbc/instruments/dag_collect_fingerprint_witness_transport.dag` | 12 | `dag_collect_fingerprint_witness` bin, still declared in `gunbc.ci_release_bins` | -| `tools.dag_compile_clean_seam` | `dag/gunbc/instruments/dag_compile_clean_seam.dag` | 110 | compile-clean entry point — WEAKER ANCHOR: a prose restoration trigger in DESIGN, no bin and no queue line | -| `tools.dag_compile_clean_seam_transport` | `dag/gunbc/instruments/dag_compile_clean_seam_transport.dag` | 124 | compile-clean entry point — WEAKER ANCHOR, as above | -| `tools.dag_compile_clean_shard_transport` | `dag/gunbc/instruments/dag_compile_clean_shard_transport.dag` | 43 | compile-clean entry point — WEAKER ANCHOR, as above | -| `tools.effects_rest_transport_witness_transport` | `dag/gunbc/instruments/effects_rest_transport_witness_transport.dag` | 12 | `effects_rest_transport_witness` bin, still declared in `gunbc.ci_release_bins` | -| `tools.infer_semantics_witness_transport` | `dag/gunbc/instruments/infer_semantics_witness_transport.dag` | 12 | `infer_semantics_witness` bin, still declared in `gunbc.ci_release_bins` | -| `tools.interp_recorded_fixture_witness_transport` | `dag/gunbc/instruments/interp_recorded_fixture_witness_transport.dag` | 13 | `interp_recorded_fixture_witness` bin, still declared in `gunbc.ci_release_bins` | -| `tools.merge_admission_capture_transport` | `dag/gunbc/instruments/merge_admission_capture_transport.dag` | 32 | merge-admission stamping — named on DESIGN's unguarded list | -| `tools.merge_admission_current_context` | `dag/gunbc/instruments/merge_admission_current_context.dag` | 134 | merge-admission stamping — named on DESIGN's unguarded list | -| `tools.parse_witness_transport` | `dag/gunbc/instruments/parse_witness_transport.dag` | 20 | `parse_witness` bin, still declared in `gunbc.ci_release_bins` | -| `tools.v1_dag_parse_transport` | `dag/gunbc/instruments/v1_dag_parse_transport.dag` | 12 | `v1_src_dag_parse` bin, still declared in `gunbc.ci_release_bins` | - -### PROSE-NAMED — 79 modules - -| module | path | lines | live-source mentions | -| --- | --- | --- | --- | -| `direct_rust_door_ingest_fixture` | `src/v2/compiler/self_host/direct_rust_door_ingest_fixture.dag` | 6 | {'dag': 1} `src/v2/compiler/self_host/direct_rust_door_fixture.dag` | -| `examples.weather` | `dag/examples/weather/weather.dag` | 48 | {'rs': 1} `src/v1/stage0/src/bin/bootstrap_witness.rs` | -| `extdeps.bmc.mock_corpus` | `dag/extdeps/bmc/mock_corpus.dag` | 71 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.cloud.gcp.mock_corpus` | `dag/extdeps/cloud/gcp/mock_corpus.dag` | 110 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.cron.mock_corpus` | `dag/extdeps/cron/mock_corpus.dag` | 22 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.diagnostic.mock_corpus` | `dag/extdeps/diagnostic_mock_corpus.dag` | 39 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.ebay.mock_corpus` | `dag/extdeps/ebay/mock_corpus.dag` | 79 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.filesystem.mock_corpus` | `dag/extdeps/filesystem/mock_corpus.dag` | 22 | {'dag': 2} `dag/gunbc/extdeps_scope_frontier.dag` `dag/gunbc/plans/m4_universal_hermetic_corpus.dag` | -| `extdeps.git.mock_corpus` | `dag/extdeps/git/mock_corpus.dag` | 167 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.github.mock_corpus` | `dag/extdeps/github/mock_corpus.dag` | 71 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.linux.mock_corpus` | `dag/extdeps/linux/mock_corpus.dag` | 15 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.llm.mock_corpus` | `dag/extdeps/llm/mock_corpus.dag` | 55 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.sec.mock_corpus` | `dag/extdeps/sec/mock_corpus.dag` | 23 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.shell.mock_corpus` | `dag/extdeps/shell_mock_corpus.dag` | 31 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.tcgplayer.mock_corpus` | `dag/extdeps/tcgplayer/mock_corpus.dag` | 63 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `gunbc.apply` | `dag/gunbc/apply.dag` | 118 | {'dag': 2} `dag/gunbc/runner/runner_lifecycle.dag` `dag/gunbc/runner/runner_capacity_realize.dag` | -| `gunbc.auth.credentials` | `dag/gunbc/auth/credentials.dag` | 89 | {'dag': 1, 'rs': 1} `dag/gunbc/tailscale_acl_phase2_credential.dag` `src/v1/stage0/src/bin/parse_witness.rs` | -| `gunbc.auth.optional_impersonation` | `dag/gunbc/auth/optional_impersonation.dag` | 20 | {'dag': 2} `dag/test/claim/tailscale_acl_phase2_design_witness_test.dag` `dag/gunbc/tailscale_acl_phase2_credential.dag` | -| `gunbc.auth.patterns` | `dag/gunbc/auth/patterns.dag` | 113 | {'rs': 1} `src/v1/stage0/src/cli_run.rs` | -| `gunbc.bootstrap` | `dag/gunbc/bootstrap.dag` | 126 | {'dag': 2} `dag/gunbc/doc_graph_roots.dag` `src/v2/compiler/self_host/frontier_probe_types.dag` | -| `gunbc.char_at_scaling_probe_support` | `dag/gunbc/char_at_scaling_probe_support.dag` | 58 | {'rs': 1} `src/v1/stage0/src/bin/char_at_scaling_probe.rs` | -| `gunbc.ci_build_job_v1_compiler_unit_receipt` | `dag/gunbc/ci/ci_build_job_v1_compiler_unit_receipt.dag` | 21 | {'dag': 1} `dag/gunbc/ci/ci_spec.dag` | -| `gunbc.ci_input_envelope` | `dag/gunbc/ci/ci_input_envelope.dag` | 86 | {'dag': 3} `dag/gunbc/doc_graph_roots.dag` `dag/gunbc/plans/bounded_input_cost_envelope_scheduling.dag` | -| `gunbc.compile_source_model` | `dag/gunbc/compile_source_model.dag` | 65 | {'dag': 1} `dag/gunbc/plans/seed_debt_bundle_item_2.dag` | -| `gunbc.deployed_intent_v0` | `dag/gunbc/deployed_intent_v0.dag` | 61 | {'dag': 2} `dag/gunbc/host/host_standup.dag` `dag/gunbc/host/host_identity_adopt.dag` | -| `gunbc.deployed_intent_v1` | `dag/gunbc/deployed_intent_v1.dag` | 69 | {'dag': 1} `dag/gunbc/host/host_standup.dag` | -| `gunbc.design_argument` | `dag/gunbc/design_argument.dag` | 93 | {'dag': 1} `dag/gunbc/plans/axiom_syllogism_lens.dag` | -| `gunbc.githooks_pre_push_cli` | `dag/gunbc/githooks/githooks_pre_push_cli.dag` | 10 | {'dag': 2, 'rs': 2} `dag/std/emit_on_demand.dag` `dag/gunbc/githooks/githooks_pre_push_fmt_transport_scaffold.dag` | -| `gunbc.host_authorized_keys_reconcile` | `dag/gunbc/host/host_authorized_keys_reconcile.dag` | 104 | {'dag': 1} `dag/gunbc/build_cache/build_cache_instance.dag` | -| `gunbc.host_build_cache_provision` | `dag/gunbc/host/host_build_cache_provision.dag` | 335 | {'dag': 4} `dag/gunbc/build_cache/build_cache_instance.dag` `dag/gunbc/fleet/fleet_host_budget.dag` | -| `gunbc.host_identity_assimilation` | `dag/gunbc/host/host_identity_assimilation.dag` | 266 | {'dag': 3} `dag/gunbc/host/host_standup.dag` `dag/gunbc/host/host_identity_adopt.dag` | -| `gunbc.host_identity_converge` | `dag/gunbc/host/host_identity_converge.dag` | 250 | {'dag': 1} `dag/gunbc/host/host_standup.dag` | -| `gunbc.host_identity_knob` | `dag/gunbc/host/host_identity_knob.dag` | 55 | {'dag': 1} `dag/gunbc/host/host_standup.dag` | -| `gunbc.host_identity_observation` | `dag/gunbc/host/host_identity_observation.dag` | 89 | {'dag': 1} `dag/gunbc/host/host_standup.dag` | -| `gunbc.host_network_diagnosis` | `dag/gunbc/host/host_network_diagnosis.dag` | 213 | {'dag': 1} `dag/gunbc/prose_row_frontier.dag` | -| `gunbc.host_toolchain_components` | `dag/gunbc/host/host_toolchain_components.dag` | 195 | {'dag': 1} `dag/gunbc/prose_row_frontier.dag` | -| `gunbc.interpreter_kernel_model` | `dag/gunbc/interpreter_kernel_model.dag` | 82 | {'dag': 1} `dag/gunbc/plans/interpreter_kernel_d.dag` | -| `gunbc.namespace_census_receipt` | `dag/gunbc/namespace/namespace_census_receipt.dag` | 74 | {'dag': 1} `dag/gunbc/doc_graph_roots.dag` | -| `gunbc.network_identity_subsumption` | `dag/gunbc/network_identity_subsumption.dag` | 134 | {'dag': 5} `dag/test/claim/dgx_spark_witness_test.dag` `dag/test/claim/host_phase_status_witness_test.dag` | -| `gunbc.p1_retention_cohort_receipt` | `dag/gunbc/p1_retention_cohort_receipt.dag` | 8 | {'dag': 1} `dag/gunbc/doc_graph_roots.dag` | -| `gunbc.plans.affected_set_self_confirmation` | `dag/gunbc/plans/affected_set_self_confirmation.dag` | 29 | {'rs': 1} `src/v1/stage0/src/cli_run.rs` | -| `gunbc.plans.branch_merge_admission_model` | `dag/gunbc/plans/branch_merge_admission_model.dag` | 172 | {'dag': 2} `dag/test/claim/merge_lifecycle_interleaving_witness_test.dag` `dag/gunbc/merge_lifecycle.dag` | -| `gunbc.plans.fleet_subsumption_manual_gaps` | `dag/gunbc/plans/fleet_subsumption_manual_gaps.dag` | 196 | {'dag': 12} `dag/test/claim/retained_shell_script_witness_test.dag` `dag/gunbc/build_cache/build_cache_instance.dag` | -| `gunbc.plans.host_convergence_circuit_residue` | `dag/gunbc/plans/host_convergence_circuit_residue.dag` | 75 | {'dag': 1} `dag/gunbc/host/host_converge.dag` | -| `gunbc.plans.merge_admission_gate_shape_proposal` | `dag/gunbc/plans/merge_admission_gate_shape_proposal.dag` | 78 | {'dag': 1} `dag/gunbc/merge_admission.dag` | -| `gunbc.plans.transport_argv_anemia_dissolution` | `dag/gunbc/plans/transport_argv_anemia_dissolution.dag` | 89 | {'dag': 2} `dag/extdeps/git/git.dag` `dag/extdeps/exec/command.dag` | -| `gunbc.process_algebra` | `dag/gunbc/process_algebra.dag` | 147 | {'dag': 3} `dag/gunbc/doc_graph_roots.dag` `dag/gunbc/plans/invert_hand_maintained.dag` | -| `gunbc.runner_slot_enforcement` | `dag/gunbc/runner/runner_slot_enforcement.dag` | 129 | {'dag': 3} `dag/gunbc/host/host_standup.dag` `dag/gunbc/runner/runner_slot_allocation.dag` | -| `gunbc.seed_closed_vocabulary_wildcard_census` | `dag/gunbc/seed_closed_vocabulary_wildcard_census.dag` | 175 | {'rs': 1} `src/v1/stage0/src/cli_run.rs` | -| `gunbc.site.interaction` | `dag/gunbc/site/interaction.dag` | 14 | {'dag': 1} `dag/gunbc/design/interaction.dag` | -| `gunbc.spark.provisioning` | `dag/gunbc/spark/provisioning.dag` | 543 | {'dag': 2} `dag/extdeps/systems/nvidia_dgx_spark_setup.dag` `dag/test/claim/spark_provisioning_witness_test.dag` | -| `gunbc.srv3_os_install_diagnostic` | `dag/gunbc/srv3/srv3_os_install_diagnostic.dag` | 1410 | {'dag': 1} `dag/gunbc/non_fold_residue.dag` | -| `gunbc.tailscale_acl_emit` | `dag/gunbc/tailscale_acl_emit.dag` | 52 | {'dag': 1} `dag/gunbc/host/host_standup.dag` | -| `gunbc.test_node_wall_clock_ratchet` | `dag/gunbc/test_node_wall_clock_ratchet.dag` | 99 | {'dag': 1} `dag/gunbc/plans/structural_quadratic_wall_coverage_audit.dag` | -| `gunbc.tools.review` | `dag/gunbc/tools/review.dag` | 187 | {'dag': 4} `dag/test/claim/workflow_default_field_projection_fold_witness_test.dag` `src/v2/lens/meta_exec_confinement.dag` | -| `gunbc.tools.review_codex` | `dag/gunbc/tools/review_codex.dag` | 205 | {'dag': 2, 'rs': 1} `dag/test/claim/workflow_default_field_projection_fold_witness_test.dag` `dag/gunbc/roadmap/roadmap_belt_actuate.dag` | -| `gunbc.v1_maintenance_standing` | `dag/gunbc/v1/v1_maintenance_standing.dag` | 83 | {'dag': 6, 'rs': 1} `dag/test/claim/match_arm_pattern_identity_emission_witness_test.dag` `dag/test/claim/documentary_refs_witness_test.dag` | -| `gunbc.workflow.types` | `dag/gunbc/workflow/types.dag` | 311 | {'dag': 1} `dag/gunbc/plans/host_effect_orchestration.dag` | -| `std.behavioral` | `dag/std/behavioral.dag` | 51 | {'rs': 1} `src/v1/stage0/src/bin/parse_witness.rs` | -| `std.durable_compare_and_set` | `dag/std/durable_compare_and_set.dag` | 292 | {'dag': 1} `dag/test/claim/durable_compare_and_set_witness_test.dag` | -| `std.methods` | `dag/std/methods.dag` | 67 | {'dag': 1, 'rs': 2} `src/v1/compiler_tests_rust.dag` `src/v1/stage0/src/v1_compiler_compiler_tests_rust.rs` | -| `std.stack` | `dag/std/stack.dag` | 56 | {'dag': 1, 'rs': 1} `dag/gunbc/witness/witness_floor_workflow.dag` `src/v1/stage0/src/bin/parse_witness.rs` | -| `std.verification` | `dag/std/verification.dag` | 34 | {'dag': 3} `dag/gunbc/plans/resolver_type_name_collision_wall.dag` `dag/gunbc/plans/realization_measurement_loop.dag` | -| `tools.gunbc_ci` | `dag/gunbc/instruments/gunbc_ci.dag` | 25 | {'dag': 2, 'rs': 1} `dag/std/emit_on_demand.dag` `src/v2/test/claim/host_language_transport_script/corpus/wall_residue_live_test.dag` | -| `v2.extdeps.languages.ecmascript` | `src/v2/extdeps/languages/ecmascript.dag` | 1340 | {'dag': 1} `dag/gunbc/language_target_registry.dag` | -| `v2.extdeps.languages.machine_code` | `src/v2/extdeps/languages/machine_code.dag` | 559 | {'dag': 4} `dag/extdeps/languages/riscv/subject.dag` `dag/test/claim/language_target_registry_totality_test.dag` | -| `v2.extdeps.languages.ptx` | `src/v2/extdeps/languages/ptx.dag` | 223 | {'dag': 1} `dag/gunbc/language_target_registry.dag` | -| `v2.extdeps.languages.swift` | `src/v2/extdeps/languages/swift.dag` | 2366 | {'dag': 2} `dag/gunbc/language_target_registry.dag` `src/v2/test/claim/complexity/accumulator_copy_roster_gate_swift_test.dag` | -| `v2.extdeps.languages.wasm` | `src/v2/extdeps/languages/wasm.dag` | 2019 | {'dag': 2} `dag/gunbc/language_target_registry.dag` `dag/gunbc/plans/language_target_self_host_frontier.dag` | -| `v2.std.datetime` | `src/v2/std/datetime.dag` | 658 | {'dag': 2, 'rs': 1} `dag/extdeps/pin.dag` `src/v2/test/manual/parse_forensics_scaling_witness.dag` | -| `v2.std.float` | `src/v2/std/float.dag` | 174 | {'dag': 1} `dag/gunbc/non_fold_residue.dag` | -| `v2.std.probe_selector` | `src/v2/std/probe_selector.dag` | 674 | {'dag': 2} `dag/gunbc/non_fold_residue.dag` `dag/gunbc/plans/dag_v2_defork_audit.dag` | -| `v2.test.workflow.glob_discovery_law` | `src/v2/workflow/glob_discovery_law.dag` | 113 | {'dag': 1} `src/v2/test/claim/complexity/accumulator_copy_roster_gate_test.dag` | -| `v2.workflow.class_b_import_closure_transport` | `src/v2/workflow/class_b_import_closure_transport.dag` | 118 | {'dag': 2, 'rs': 1} `dag/test/claim/long/rust_test_fixtures_import_closure_witness_test.dag` `src/v2/workflow/class_b_import_closure_probe.dag` | -| `v2.workflow.compile_door_ledger` | `src/v2/workflow/compile_door_ledger.dag` | 341 | {'dag': 1} `src/v2/test/claim/long/door_real_module_probe_test.dag` | -| `v2.workflow.compiler_closure_ingest_transport` | `src/v2/workflow/compiler_closure_ingest_transport.dag` | 150 | {'dag': 4} `dag/gunbc/instruments/ci_gates.dag` `dag/gunbc/ci/ci_layer_roots.dag` | -| `v2.workflow.phase_profile_proof_plan` | `src/v2/workflow/phase_profile_proof_plan.dag` | 22 | {'rs': 1} `src/v1/stage0/tests/phase_profile_claim_executor.rs` | -| `v2.workflow.source_root_ingest_gate` | `src/v2/workflow/source_root_ingest_gate.dag` | 18 | {'dag': 3} `dag/test/claim/guarantee_stall_witness_test.dag` `dag/gunbc/instruments/ci_gates.dag` | -| `v2.workflow.source_root_ingest_transport` | `src/v2/workflow/source_root_ingest_transport.dag` | 90 | {'dag': 2} `dag/gunbc/instruments/ci_gates.dag` `src/v2/test/claim/host_language_transport_script/corpus/migrated_transports_clean_test.dag` | - -### CITED-AUTHORITY — 103 modules - -| module | path | lines | live-source mentions | -| --- | --- | --- | --- | -| `extdeps.access.aws_iam` | `dag/extdeps/access/aws_iam.dag` | 45 | {'dag': 1} `dag/gunbc/principal_projection.dag` | -| `extdeps.access.zanzibar` | `dag/extdeps/access/zanzibar.dag` | 62 | {'dag': 1} `dag/gunbc/principal_projection.dag` | -| `extdeps.audit.cloudevents` | `dag/extdeps/audit/cloudevents.dag` | 58 | — | -| `extdeps.bmc.ipmi` | `dag/extdeps/bmc/ipmi.dag` | 48 | — | -| `extdeps.boot.emit` | `dag/extdeps/boot/emit.dag` | 127 | — | -| `extdeps.boot.framebuffer` | `dag/extdeps/boot/framebuffer.dag` | 15 | — | -| `extdeps.boot.freestanding_payload` | `dag/extdeps/boot/freestanding_payload.dag` | 19 | — | -| `extdeps.boot.freestanding_witness` | `dag/extdeps/boot/freestanding_witness.dag` | 198 | — | -| `extdeps.boot.linux_x86_boot` | `dag/extdeps/boot/linux_x86_boot.dag` | 37 | — | -| `extdeps.cloud.gcp.iam_admin` | `dag/extdeps/cloud/gcp/iam_admin.dag` | 114 | — | -| `extdeps.cloud.gcp.serviceusage` | `dag/extdeps/cloud/gcp/serviceusage.dag` | 51 | — | -| `extdeps.cloud.gcp.sts` | `dag/extdeps/cloud/gcp/sts.dag` | 111 | {'rs': 2} `src/v1/stage0/src/cli_run.rs` `src/v1/stage0/src/bin/effects_rest_transport_witness.rs` | -| `extdeps.cloud_init.cloud_init` | `dag/extdeps/cloud_init/cloud_init.dag` | 58 | {'dag': 1} `dag/gunbc/prose_row_frontier.dag` | -| `extdeps.colo.centersquare` | `dag/extdeps/colo/centersquare.dag` | 47 | — | -| `extdeps.colo.colocation_america` | `dag/extdeps/colo/colocation_america.dag` | 45 | — | -| `extdeps.colo.coresite` | `dag/extdeps/colo/coresite.dag` | 58 | — | -| `extdeps.colo.dataverge` | `dag/extdeps/colo/dataverge.dag` | 47 | — | -| `extdeps.colo.digital_realty` | `dag/extdeps/colo/digital_realty.dag` | 59 | — | -| `extdeps.colo.equinix` | `dag/extdeps/colo/equinix.dag` | 71 | — | -| `extdeps.colo.evocative` | `dag/extdeps/colo/evocative.dag` | 49 | — | -| `extdeps.colo.h5` | `dag/extdeps/colo/h5.dag` | 59 | — | -| `extdeps.colo.halsey_165` | `dag/extdeps/colo/halsey_165.dag` | 73 | — | -| `extdeps.colo.hivelocity` | `dag/extdeps/colo/hivelocity.dag` | 45 | — | -| `extdeps.colo.interserver` | `dag/extdeps/colo/interserver.dag` | 100 | — | -| `extdeps.colo.iron_mountain` | `dag/extdeps/colo/iron_mountain.dag` | 44 | — | -| `extdeps.colo.natcoweb` | `dag/extdeps/colo/natcoweb.dag` | 123 | — | -| `extdeps.colo.netrality` | `dag/extdeps/colo/netrality.dag` | 45 | — | -| `extdeps.colo.qts` | `dag/extdeps/colo/qts.dag` | 45 | — | -| `extdeps.colo.summit` | `dag/extdeps/colo/summit.dag` | 49 | — | -| `extdeps.colo.three_sixty_five` | `dag/extdeps/colo/three_sixty_five.dag` | 89 | — | -| `extdeps.colo.tierpoint` | `dag/extdeps/colo/tierpoint.dag` | 86 | — | -| `extdeps.colo.types` | `dag/extdeps/colo/types.dag` | 110 | — | -| `extdeps.container.oci.ctrl_session_witness` | `dag/extdeps/container/oci/ctrl_session_witness.dag` | 361 | — | -| `extdeps.container.oci.image_config` | `dag/extdeps/container/oci/image_config.dag` | 100 | — | -| `extdeps.container.oci.linux` | `dag/extdeps/container/oci/linux.dag` | 156 | — | -| `extdeps.container.oci.manifest` | `dag/extdeps/container/oci/manifest.dag` | 98 | — | -| `extdeps.container.oci.runtime_config` | `dag/extdeps/container/oci/runtime_config.dag` | 97 | — | -| `extdeps.currency.currency` | `dag/extdeps/currency/currency.dag` | 24 | {'dag': 1} `dag/gunbc/prose_row_frontier.dag` | -| `extdeps.darwin.rusage` | `dag/extdeps/darwin/rusage.dag` | 31 | {'dag': 2, 'rs': 1} `dag/test/claim/peak_resident_measured_witness_test.dag` `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.darwin.sysctl` | `dag/extdeps/darwin/sysctl.dag` | 64 | {'dag': 3, 'rs': 1} `dag/gunbc/extdeps_scope_frontier.dag` `dag/gunbc/prose_row_frontier.dag` | -| `extdeps.ebay.ebay` | `dag/extdeps/ebay/ebay.dag` | 94 | — | -| `extdeps.ebay.ebay_contracts` | `dag/extdeps/ebay/ebay_contracts.dag` | 34 | — | -| `extdeps.ebay.errors` | `dag/extdeps/ebay/errors.dag` | 61 | — | -| `extdeps.ebay.inventory` | `dag/extdeps/ebay/inventory.dag` | 468 | — | -| `extdeps.ebay.oauth` | `dag/extdeps/ebay/oauth.dag` | 151 | — | -| `extdeps.energy.nj_electricity` | `dag/extdeps/energy/nj_electricity.dag` | 61 | — | -| `extdeps.exec.xargs` | `dag/extdeps/exec/xargs.dag` | 31 | {'dag': 1} `dag/gunbc/prose_row_frontier.dag` | -| `extdeps.filesystem.ntfs` | `dag/extdeps/filesystem/ntfs.dag` | 28 | — | -| `extdeps.filesystem.posix` | `dag/extdeps/filesystem/posix.dag` | 34 | — | -| `extdeps.formats.elf.encode` | `dag/extdeps/formats/elf/encode.dag` | 201 | — | -| `extdeps.formats.elf.hello_static_witness` | `dag/extdeps/formats/elf/hello_static_witness.dag` | 259 | — | -| `extdeps.formats.elf.primitives` | `dag/extdeps/formats/elf/primitives.dag` | 61 | — | -| `extdeps.formats.elf.relocation` | `dag/extdeps/formats/elf/relocation.dag` | 26 | — | -| `extdeps.formats.elf.sections` | `dag/extdeps/formats/elf/sections.dag` | 31 | — | -| `extdeps.formats.elf.segments` | `dag/extdeps/formats/elf/segments.dag` | 79 | — | -| `extdeps.formats.elf.types` | `dag/extdeps/formats/elf/types.dag` | 190 | — | -| `extdeps.git.versioning` | `dag/extdeps/git/versioning.dag` | 73 | — | -| `extdeps.github.auth` | `dag/extdeps/github/auth.dag` | 61 | {'rs': 1} `src/v1/stage0/src/bin/parse_witness.rs` | -| `extdeps.github.ci` | `dag/extdeps/github/ci.dag` | 30 | {'dag': 1} `dag/gunbc/plans/realization_measurement_loop.dag` | -| `extdeps.github.gists` | `dag/extdeps/github/gists.dag` | 76 | {'rs': 2} `src/v1/stage0/src/bin/parse_witness.rs` `src/v1/stage0/src/bin/effects_rest_transport_witness.rs` | -| `extdeps.github.github_contracts` | `dag/extdeps/github/github_contracts.dag` | 15 | — | -| `extdeps.github.issues` | `dag/extdeps/github/issues.dag` | 202 | {'dag': 1, 'rs': 1} `dag/gunbc/extdeps_scope_frontier.dag` `src/v1/stage0/src/bin/effects_rest_transport_witness.rs` | -| `extdeps.github.mergeable_state` | `dag/extdeps/github/mergeable_state.dag` | 39 | — | -| `extdeps.github.mergeable_state_contracts` | `dag/extdeps/github/mergeable_state_contracts.dag` | 18 | — | -| `extdeps.gitignore` | `dag/extdeps/git/gitignore.dag` | 36 | — | -| `extdeps.languages.go.module` | `dag/extdeps/languages/go/module.dag` | 144 | {'dag': 1} `dag/gunbc/commit_workflow.dag` | -| `extdeps.languages.go.primitives` | `dag/extdeps/languages/go/primitives.dag` | 211 | {'dag': 1, 'rs': 1} `dag/std/checked_arithmetic.dag` `src/v1/stage0/src/std_checked_arithmetic.rs` | -| `extdeps.languages.python.primitives` | `dag/extdeps/languages/python/primitives.dag` | 86 | — | -| `extdeps.languages.rust.primitives` | `dag/extdeps/languages/rust/primitives.dag` | 112 | {'dag': 2, 'rs': 2} `dag/extdeps/languages/rust/types.dag` `dag/std/checked_arithmetic.dag` | -| `extdeps.languages.typescript.primitives` | `dag/extdeps/languages/typescript/primitives.dag` | 103 | — | -| `extdeps.linux.edac` | `dag/extdeps/linux/edac.dag` | 26 | — | -| `extdeps.linux.proc_meminfo` | `dag/extdeps/linux/proc_meminfo.dag` | 41 | {'dag': 1} `dag/extdeps/linux/procfs.dag` | -| `extdeps.linux.rusage` | `dag/extdeps/linux/rusage.dag` | 31 | {'dag': 2, 'rs': 1} `dag/test/claim/peak_resident_measured_witness_test.dag` `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.llm.anthropic_errors` | `dag/extdeps/llm/anthropic_errors.dag` | 80 | — | -| `extdeps.llm.anthropic_rest` | `dag/extdeps/llm/anthropic_rest.dag` | 96 | {'rs': 1} `src/v1/stage0/src/bin/effects_rest_transport_witness.rs` | -| `extdeps.llm.llm` | `dag/extdeps/llm/llm.dag` | 12 | — | -| `extdeps.llm.openai_contracts` | `dag/extdeps/llm/openai_contracts.dag` | 32 | — | -| `extdeps.llm.openai_errors` | `dag/extdeps/llm/openai_errors.dag` | 66 | — | -| `extdeps.llm.openai_rest` | `dag/extdeps/llm/openai_rest.dag` | 92 | {'rs': 1} `src/v1/stage0/src/bin/effects_rest_transport_witness.rs` | -| `extdeps.netplan.netplan` | `dag/extdeps/netplan/netplan.dag` | 69 | {'dag': 1} `dag/gunbc/prose_row_frontier.dag` | -| `extdeps.observability` | `dag/extdeps/observability.dag` | 49 | — | -| `extdeps.posix.rusage` | `dag/extdeps/posix/rusage.dag` | 66 | {'dag': 3, 'rs': 2} `dag/test/claim/peak_resident_measured_witness_test.dag` `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.provisioning.ubuntu_seeded_install_media_toolchain` | `dag/extdeps/provisioning/ubuntu_seeded_install_media_toolchain.dag` | 39 | — | -| `extdeps.realestate.nj_industrial` | `dag/extdeps/realestate/nj_industrial.dag` | 70 | — | -| `extdeps.render.surface` | `dag/extdeps/render/surface.dag` | 79 | {'rs': 2} `src/v1/stage0/src/cli_run.rs` `src/v1/stage0/src/v1_interpreter.rs` | -| `extdeps.render.terminal_capability` | `dag/extdeps/render/terminal_capability.dag` | 60 | {'rs': 1} `src/v1/stage0/src/v1_interpreter.rs` | -| `extdeps.runtime.api.darwin` | `dag/extdeps/runtime/api/darwin.dag` | 59 | — | -| `extdeps.runtime.api.windows` | `dag/extdeps/runtime/api/windows.dag` | 114 | — | -| `extdeps.runtime.local` | `dag/extdeps/runtime/local.dag` | 22 | — | -| `extdeps.sec.edgar_rest` | `dag/extdeps/sec/edgar_rest.dag` | 71 | {'dag': 2} `dag/extdeps/sec/edgar.dag` `dag/gunbc/prose_row_frontier.dag` | -| `extdeps.shell.credentials` | `dag/extdeps/shell/credentials.dag` | 19 | — | -| `extdeps.tailscale.acl` | `dag/extdeps/tailscale/acl.dag` | 312 | {'dag': 2} `dag/test/claim/host_standup_spine_witness_test.dag` `dag/gunbc/host/host_standup.dag` | -| `extdeps.tailscale.acl_api` | `dag/extdeps/tailscale/acl_api.dag` | 70 | — | -| `extdeps.tcgplayer.catalog` | `dag/extdeps/tcgplayer/catalog.dag` | 180 | — | -| `extdeps.tcgplayer.pricing` | `dag/extdeps/tcgplayer/pricing.dag` | 78 | — | -| `extdeps.tcgplayer.store` | `dag/extdeps/tcgplayer/store.dag` | 97 | — | -| `extdeps.tcgplayer.tcgplayer` | `dag/extdeps/tcgplayer/tcgplayer.dag` | 82 | — | -| `extdeps.tools.diffutils` | `dag/extdeps/tools/diffutils.dag` | 29 | {'dag': 1} `dag/extdeps/tools/gnu_coreutils.dag` | -| `extdeps.transports.sql` | `dag/extdeps/transports/sql.dag` | 41 | {'dag': 1} `src/v2/test/fixture/frontier_probe_elision_boundary_overlay.dag` | -| `extdeps.vendor.arm` | `dag/extdeps/vendor/arm.dag` | 31 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.vendor.qualcomm` | `dag/extdeps/vendor/qualcomm.dag` | 31 | {'dag': 1} `dag/gunbc/extdeps_scope_frontier.dag` | -| `extdeps.version.pep440` | `dag/extdeps/version/pep440.dag` | 310 | — | -| `gunbc.hand_lens_host_bridge_scaffold_index` | `dag/gunbc/hand_lens_host_bridge_scaffold_index.dag` | 23 | — | \ No newline at end of file diff --git a/docs/plans/unconsumed-module-residue-disposition.md b/docs/plans/unconsumed-module-residue-disposition.md deleted file mode 100644 index 7dcb8c6000e..00000000000 --- a/docs/plans/unconsumed-module-residue-disposition.md +++ /dev/null @@ -1,805 +0,0 @@ -# Disposition of the unconsumed-module residue - -**This is the execution record for the population -[`unconsumed-module-census.md`](unconsumed-module-census.md) measured. The census stays the -authority on how the population is derived; this records what was done to it and, for every -row not deleted, the typed reason it survived.** Separate facts on separate clocks: the census -answers *what is unconsumed*, this answers *what was dispositioned, and what refused*. - -Operator directive both serve (2026-08-21, verbatim): *"yes please make sure to clean up -anything without consumers that we don't need, or get them actually consumed."* - -## 1. What was deleted, and what the number is - -The census's reconciled cleanup list is **131 modules** -- 96 STILL-UNCONSUMED plus 35 -DEAD-CONSUMER-ONLY, the two buckets that are residue on all three decoded surfaces. Measured -2026-08-22 against a tree that has since moved. **Re-deriving the instrument on this branch's -base (`90986d19469`) reproduces it within the drift:** - -| bucket | census (2026-08-22) | re-derived here | delta | -| --- | --- | --- | --- | -| population (unreachable) | 298 | 302 | +4 | -| CONSUMED-DECISIVE | 91 | 89 | -2 | -| DEAD-CONSUMER-ONLY | 35 | 32 | -3 | -| AMBIGUOUS-SHARED-ONLY | 75 | 74 | -1 | -| STILL-UNCONSUMED | 96 | 107 | +11 | -| **residue (the cleanup list)** | **131** | **139** | **+8** | - -The instrument is re-derived, not inherited, on purpose: the census records that the population -*has a clock on it* -- one of its own appendix rows already pointed at a deleted file -- so a -lane trusting a four-day-old row list deletes against a tree that no longer exists. Re-deriving -also re-runs the census's controls: `v2.compiler.compile`, `gunbc.spark.serving_desired`, -`gunbc.clock_read`, `v2.std.node` and `gunbc.accelerator_demo_gpu` all score reachable here, -and RESIDUE-EMPTY again scores 0 consumed, so the instrument finds consumption neither -everywhere nor nowhere. - -**Of the corrected 114-row residue, this change deletes 55.** The deleted rows carry no -obligation to anything outside themselves, on every surface the instrument decodes *and* every -surface it does not: - -- unreachable from every root -- discovery path, entry row, or v1 seed mirror; -- no uniquely-owned symbol -- **including coproduct variant constructors and flat-namespace - `operation`/`service`/`resource` declarations**, see 4d -- named bare by any `.dag` file; -- **no mention of the module name or its path in any `.dag`, `.rs`, `.yml`, `.yaml`, - `.toml`, `.sh` or `.txt` file** in the tree; -- no `test fn` declared, so no assertion stops executing; -- no `src/v1/stage0/src` mirror, so the seed population is unchanged; -- **and its whole island is in the set** -- eligibility is computed as a fixed point over the - deletion set, not per module (4g). - - - -## 2. The deletion as the census - -DESIGN §3 says the deletion *is* the census. Measured against the corrected instrument (§4d), -before and after the cut: - -| | before | after | delta | -| --- | --- | --- | --- | -| modules | 3851 | 3796 | -55 | -| **reachable** | **3549** | **3549** | **0** | -| population | 302 | 247 | -55 | -| **CONSUMED-DECISIVE** | **91** | **91** | **0** | -| AMBIGUOUS-SHARED-ONLY | 97 | 97 | 0 | -| STILL-UNCONSUMED | 80 | 29 | -51 | -| DEAD-CONSUMER-ONLY | 34 | 29 | -5 | - -Reachable, CONSUMED-DECISIVE and AMBIGUOUS are unchanged; the whole movement is inside the two -residue buckets and equals the file count exactly. - -**These counters are necessary and NOT sufficient, and §4d is why.** An earlier revision offered -this table as proof the cut was safe. It was computed by an instrument carrying an extraction -defect, so it could not surface the case that defect was blind to -- and read as reassurance -precisely because it was consistent. The table is retained because it still falsifies a whole -class of error (a cut catching a *reachable* module would move the top row), and demoted because -it cannot falsify the class that actually occurred. The independent instrument is the floor, §8. - -## 3. The one row the census deferred, now answered by its author -- and the cause it gave is not the cause the census guessed - -`gunbc.scm.commit_closure_store` was deleted by the batch rule, restored while the question -was open, and is **deleted again** on the answer from `gentle-eagle-360`, who authored #8820. -The verdict is recorded here rather than in the module, because a citation into a deleted -module dies with it. - -**Both arms the census offered rest on a false premise, and the correction is load-bearing.** -The census framed this as *the envelope grew save/load and superseded it* versus *the envelope -should consume it as its persistence layer*. - -- *Not superseded.* `gunbc.scm.repository_envelope` contains **zero `Filesystem` operations - and not one `func`** -- a pure codec, `RepositoryEnvelope` to `JsonValue` and back. Nothing - took over this module's job; recording the deletion as "replaced by the envelope" would be - false. -- *And the envelope should NOT consume it.* `commit_closure_store` persists **one root and - its closure**. A repository is a different subject: an empty initialized state with **no - root at all**, several commits over one shared node population, a checked-out selection. - Wiring the envelope to a carrier that demands a root would force `init` to invent a phantom - commit -- a grain mismatch dressed as a fix. - -**The correct disposition row: DELETE, cause = STAGED ORPHAN AT THE WRONG GRAIN.** Not -superseded, not replaced, not merely unconsumed. It was never wired, and the layer that will -do this job is repository-grain and will be written that way whether or not this module stands. - -**Why delete rather than hold it for that layer** -- the author's reasoning and DESIGN §3's: a -surviving X is an **attractor**; while it stands, every nearby persistence question is answered -in commit-closure vocabulary already scheduled to die. They declined to hold a lane open for a -speculative "export one commit" operation with no consumer -- §6 experimental residue, applied -by an author to their own module. - -**A rung-honesty defect in its own carrier, volunteered by that author, strengthens the -warrant.** The module's header claims its persistence is *"verified by direct execution in Wet -mode"* and points at `commit_closure_round_trip_probe`. True when run by hand, not now: the -probe is **enrolled nowhere and nothing executes it**. The module carries an overclaim about -its own evidence -- a stronger reason to delete than unreachability, and exactly the -specification-without-execution shape DESIGN §5 names. - -**What must survive into the replacement, recorded because this deletion removes the prompt -for it** (not this lane's to carry): the host's success/content/error triple folded into a -coproduct **at** the boundary, so no downstream consumer picks which member to believe or reads -content from a failed read; and encode adjudicated **before** the write, so a successful write -cannot upgrade an encoding failure into `Saved`. - -## 4. One finding the deletion surfaced, and it is not a deletion - -**`gunbc.v1_maintenance_standing` is unreachable from every root and named bare by nothing, -and DESIGN.md names it as an authority.** DESIGN's 3 standing rule on the v1 seed states: -*"The authority is `gunbc.v1_maintenance_standing` `v1_seed_standing`"* -- the carrier for the -semantics-frozen / maintenance-active reclassification, its admission test, and its five-class -refusal vocabulary. - -It is held, not proposed for deletion. The finding runs the other way: **the carrier DESIGN -designates as the authority for the v1 freeze standing executes nowhere and is reached by -nothing.** Its four in-tree `.dag` mentions (`ci_layer_roots`, `documentary_refs`, -`roadmap_serve`, `whole_corpus_compile_admission`, plus two witnesses) are string citations, -not calls. Consistent with the standing's own declared rung -- *"is consumed by review -diligence, not by any gate, so it sits at mitigatable"* -- and this measurement is its -independent corroboration: not merely ungated, unreachable. Recorded as a rung-honesty datum -for whoever climbs it, not as a disposition. - -## 4b. A strand that appeared, then dissolved when its module came back - -A record of the surface, not of a repair. Widening the mention scan past source extensions to -`.txt` found `docs/probes/census_extra_excludes.txt` and its seeds file naming -`dag/examples/gunbhub_serve_program/gunbhub_serve_program.dag`, which the first cut deleted. -`v1_compiler.census_exclude_derive` loads both -- the seeds drive the derived exclude closure -and the pinned oracle is its drift witness -- so a row naming a deleted path skews the -symmetric diff toward *drift* rather than *staleness*. - -**Both the row removal and the two count literals it forced are reverted**, because -`gunbhub_serve_program` left the deletion set in §4d's re-derivation and the row is correct -again. **This PR touches no file under `src/v1`.** - -The reusable finding: **a source-extension mention scan is not a complete consumption -surface.** Authored data files carry references, and this one was load-bearing. The census's -decoded-surface list should include them. The check is now part of the §1 precondition and -stays there whether or not any row trips it. - -## 4c. One deleted row was a committed copy of a DO-NOT-COMMIT artifact - -Raised by review as two surviving references to a deleted filename -(`gunbc.ci_layer_roots`'s `WitnessExclusionRow` pattern and `discover_owned_data`'s -`exclude_subpaths` default), cleared there as harmless string patterns. Correct -- and checking -*why* turned up the sharper fact and reversed the tidy-up it invites. - -`v1_compiler.cli_run` `discover_owned_data_decls` **generates** a module named -`v2.test.claim.workflow.host_discovered_owned_data_manifest` under -`src/v2/test/claim/workflow/`, stamped `GENERATED by discover_owned_data -- ephemeral -host transport. DO NOT COMMIT.` What this change deleted is -`v2.test.workflow.host_discovered_owned_data_manifest` at `src/v2/workflow/` -- a -**committed instance of that ephemeral artifact under a second module path, carrying all-zero -counts**. A stale committed copy of a file the generator says must not be committed is a -better reason to delete than unreachability. - -**And the two exclusion rows must stay.** `path_excluded` is a substring match over the full -path, so `host_discovered_owned_data_manifest.dag` still matches the *generated* file whenever -`discover_owned_data` produces it. They are live exclusions of an ephemeral artifact, not dead -rows. Removing them as tidy-up -- what "a pattern that no longer matches anything" invites -- -would have re-admitted a generated transport into discovery. - -Recorded because the wrong reading is the natural one for the next person to grep these names. - -## 4e. One row restored on disposition, not on measurement - -`gunbc.generic_binder_field_projection_deficit` scores STILL-UNCONSUMED correctly and is -restored anyway: dispositioned KEEP-WITH-REASON in #8851 as a DESIGN 4b **deficit filing** -- -a declared rung, a ceiling, a next-rung trigger. - -**An unconsumed deficit filing is 4b(2) working, not residue.** A class below its ceiling is -*required* to name its trigger, and nothing consumes that filing by design; unreferenced is its -normal state. Deleting it would remove a safety-ledger row and lower a rung with none of the -reason, bounded population and restoration trigger 4b(3) demands. Its sibling -`gunbc.empty_decl_file_checkpoint_bypass` carries the same disposition and was never in this -batch. - -The general point, stated by the census and learned here twice: **unreachability is the wrong -predicate for a row whose purpose is to be read by a human ledger.** This row and section 3's -`commit_closure_store` are that shape; neither was caught by a mechanical rule -- both came -from a per-row disposition someone had already written down. - -## 4d. Four extraction defects, one mechanism: under-extraction creates false uniqueness - -**The floor refused the first cut**, with `unresolved type 'MergeReadinessVerdict'` and eight -`undefined variable 'Ready'` in `gunbc.code_change_workflow`. `gunbc.pr_digests` had been -deleted and should not have been. - -The defect-6 re-score asks whether another `.dag` file names a candidate's declared symbol -*bare*. That needs what a module **declares** -- and four ways of getting it wrong were found, -all under-extraction, all with the same consequence: - -| # | defect | effect | -| --- | --- | --- | -| 7 | coproduct **variant constructors** not extracted (`= Ready \| NotReady`) | found here, by the floor | -| a | a **generic header** `type X

= A \| B` skips the block | relayed by `silent-deer-368` | -| b | a **multi-line variant record** truncates a start-of-line scan | relayed by `silent-deer-368` | -| c | `operation`/`service`/`resource` are **declarations**, read as references | relayed by `silent-deer-368` | -| d | a **same-line coproduct** `type X = A \| B` | found by their discriminator, in *my* extractor | - -**Why this direction is the dangerous one.** Under-extraction makes a module own fewer symbols -than it does, so a *shared* name looks **uniquely owned** -- false uniqueness. A module whose -declarations all sit behind an unparsed header owns nothing, so a live consumer naming its -symbols bare is invisible and the module scores residue and **gets deleted**. The opposite -direction from the `pr_digests` case, which merely kept a dead-looking module alive. - -**Fixed at the root, not per bug.** Adopted `silent-deer-368`'s region-based extractor: a -type's region runs to the next top-level declaration (so (b) cannot truncate it), a leading -`<...>` is stripped before testing for `=` (so (a) parses), and `operation`/`service`/ -`resource` names are declarations (so (c) does not misattribute). Verified against their four -discriminators -- `Apply` in `std.upsert_decision`, `Select` in `v2.extdeps.languages.llvm_ir`, -`Ready` in `gunbc.pr_digests`, `Capability` in `std.behavioral` -- **my extractor passed three -and failed the fourth**, which is how (d) was found. Their (c) tell also passes: -`extdeps.transports.sql` scores AMBIGUOUS rather than consumed-by-`filesystem_io`, which linked -to it only through `operation Delete {`. - -**What re-deriving cost.** **13 modules left the deletion set** -- nine formatters, the -`language_model` rust root, `generic_instantiation`, `roadmap_dispatch`, -`gunbhub_serve_program` -- and **every one landed in AMBIGUOUS-SHARED-ONLY, not CONSUMED.** -That distinction is why the bucket exists: they are *no longer provably unconsumed* and *not -proven consumed*, so none may be deleted on this evidence, and reading the shrinking residue as -"more live modules found" collapses exactly the gap the bucket holds. - -**The general lesson, which outlives every row above:** - -> **A control derived from the measurement it controls does not discriminate that -> measurement's blind spot.** - -Both counters §2 offers came from the instrument carrying defect 7. The floor caught it because -it is an independent instrument sharing none of the method. That is delete-first's actual -mechanism (DESIGN §3): not that the rule is trustworthy, but that the substrate refuses on an -authority the rule had no part in building. Defects (a)-(d) were found by **hand-reading rows** -while counters stayed consistent throughout -- the same lesson from the other direction. - -## 4h. Six discriminators, and the count/set rule that resolves two instruments disagreeing - -The extraction defects were settled by **discriminating cases, not by descriptions**, and that -is the reusable result. Bug *descriptions* relayed between lanes found one bug in this lane's -extractor; the *test cases* that came with them found two more nobody had named -- and one of -those still failed here after its own description had been implemented faithfully, which is -(f2) below. **A discriminator travels; a description does not.** - -The roster, each failing on exactly the defect it names: - -| case | specimen | catches | -| --- | --- | --- | -| `Apply` | `std.upsert_decision` | (a) generic header | -| `Select` | `v2.extdeps.languages.llvm_ir` | (b) multi-line variant record | -| `Ready` | `gunbc.pr_digests` | defect 7, variant constructors | -| `Capability` | `std.behavioral` | (d) same-line coproduct | -| `Run` | `gunbc.cli_services` | (e) service-namespace-only module | -| `v2.std.projection` NOT consumed by `v2.lens.common.graph_invariant` | (f) generic parameter counted as a reference | - -**(f) is the mirror of (a), with a second half the obvious fix does not reach.** A generic -parameter is a **binder, not a reference**: `type GraphInvariant` binds -`Projection`, and `v2.lens.common.graph_invariant` imports no projection module at all. One -construct yields two distinct false references: - -- **(f1)** the parameter list in the declaration header; -- **(f2)** *uses of the bound name inside that declaration's body* -- `projection: Projection` - on the very next line. - -Implementing (f1) alone left `v2.std.projection` scoring CONSUMED-DECISIVE; only region-scoped -shadowing of the bound name moved it to AMBIGUOUS. So the construct touches the instrument -three ways in two directions: a generic header **defeats declaration extraction** in (a) and -**inflates reference extraction** in (f1) and (f2). - -**The scoping is the safety-critical part.** Dropping the bound name file-wide is the obvious -implementation and the *delete-a-live-module* direction: removing references moves a live -module into the residue, so a name bound as a parameter in one declaration and genuinely -referenced in another would silently lose its real reference. The shadow is scoped to the -binding declaration's region. - -**The `service` short-name credit: dropped, then restored, and the second reason is the one -that holds.** First dropped citing a false-consumption claim measured on the *other* lane's -instrument -- never true here, since their specimen `service gcp.Metadata` is **dotted** and -this lane's pattern never matched it. Then justified as *"no measured effect on this -population"*, also wrong as a basis: a nil effect argues for either choice and settles nothing. - -**The basis that holds is correctness of ownership.** `extdeps.filesystem.filesystem_io` -declares `service Filesystem`; an instrument saying otherwise is wrong whether or not a row -moves today. And withholding the credit is not merely a missing fact -- `Filesystem` is declared -**twice**, as a `service` there and as a `resource` in `std.resources`, so dropping the service -side hands `std.resources` **false sole ownership** of a name two modules declare. That is -*manufactured uniqueness*: the mechanism behind every confident wrong attribution in this -chain, created by the fix meant to avoid it. - -The evidence is the seventh discriminator: `v2.extdeps.realization.artifact_store_fs` uses -`Filesystem.Write`, `.Read` and `.Delete` as a qualifier with **no import of `Filesystem` in -its import block**. A consumer does bare-resolve an undotted service short name through -whole-pool resolution. Credited, dotted forms included by last segment (`service gcp.Metadata` -declares `Metadata`); zero modules in this corpus own nothing without it, but the same basis -applies, since under-extraction is the delete-a-live-module direction. - -**How the real cause was located, the transferable part.** This lane could not reproduce the -other's false positive *at all* -- the patterns differ on dotted names. That **inability to -reproduce, with a stated reason**, identified (f) as the actual cause and the service credit as -merely adjacent. A cross-check that agrees tells you less than one that fails to reproduce and -can say why. - -**Direction check, which is what makes (f) safe to have found late:** (f) and the service -short-name credit both *inflate* consumption, so neither can cause a wrong deletion -- they can -only hold a dead module. Both were fixed anyway; the deletion set did not move. That is also -the count/set rule predicting which lane had to care about which defect before either -measured it: an inflated-consumption defect moves a residue *count* and leaves a conservative -deletion *set* alone. - -**(e) is contributed back and is the sharpest of the five**: its specimen's declared set is -*empty* under an unfixed extractor rather than merely incomplete -- `gunbc.cli_services` is a -`service` block with three `operation`s and **zero** `fn`/`data`/`type`/`const`. Owning nothing -is the exact precondition for the delete-a-live-module failure. Measured exposure today: none --- reported as that lane reported theirs, **in the bucket where it would be dangerous** rather -than over the population: of the operation-bearing rows, none sits in STILL-UNCONSUMED. - -**The two lanes' (c) fixes are not equivalent, and reconciling them would be wrong.** -`silent-deer-368` subtracts declaration-keyword names from the *consuming* file's reference -set; this lane adds them to the *declaring* module's owned set, making the symbol -non-uniquely-owned and parking the row in AMBIGUOUS instead of removing the reference. -Subtraction is more precise; addition is more conservative. Their rule, verbatim, because it -settles what looked like a discrepancy: - -> **A residue COUNT should come from the precise implementation, a deletion SET from the -> conservative one. Our numbers disagreeing in a known direction is a property to state, not -> a discrepancy to reconcile.** - -**Two independent populations, same skew -- the finding, not the bookkeeping.** 13 of this -lane's 69 left the residue on re-derivation and 12 of 14 left theirs, in both cases **almost -entirely to AMBIGUOUS and near-zero to CONSUMED**. The residue shrank far more than the -consumption claim grew. That gap is the whole reason the ambiguous bucket exists, and the -natural summary -- *"the residue shrank, so more modules are live"* -- is the reading that -collapses it into unevidenced deletions. - -## 4g. Eligibility is a fixed point over the set, not a property of a module - -31 residue rows are named bare only from *inside* the population. A per-module verdict over a -mutually-referencing island returns "consumed" for every member and the island never becomes -eligible; splitting one across batches reds the first batch. - -So the deletion set is computed as a **fixed point**: repeatedly drop any module with a -surviving in-population consumer until nothing drops. This ran twice for real, in both -directions: - -- **A deletion created a violation.** The first cut deleted `gunbc.pr_digests` while keeping - `gunbc.code_change_workflow` -- one island, split. That is the row the floor refused on. -- **A restore created one.** Putting the 13 AMBIGUOUS rows back re-created an in-population - consumer for `v2.std.rust_leaf_model_claim` (via `v2.test.language_model.rust`), eligible - until its neighbour returned. - -**This is why the change is one PR** and not six cluster PRs: the property is over the set, so -six PRs merging in arbitrary order re-open this class unless each is re-derived against the -others' merged state -- strictly more work and risk than keeping the set closed. The reviewer's -seams are the commits. - -## 4f. A finding walked past while editing the pin, filed rather than fixed - -Repairing `census_extra_excludes.txt` meant moving two count literals in -`v1_compiler.census_exclude_derive` (83 to 82, 27 to 26). **Those literals are a change -detector, not a check**, and this lane can say so with a receipt rather than a style objection. - -A hand-maintained count beside a hand-maintained list measures whether someone remembered to -update both. It has no independent referent: not a controlled fixture, not an external or -versioned authority, not a policy budget, not a monotone debt contract over a closed universe --- the four grounds DESIGN §5 admits for a numeric literal in a merge-blocking test. The -section's review tell applies exactly: automating the literal's update collapses the assertion -to `measure() == measure()`, so the manual update was the assertion's entire content. - -The receipt is this change: one row moved for an unrelated reason, both literals had to be -hand-followed, and nothing about the list's *correctness* was checked by either. Had the row -moved without them, the failure would have reported as a count mismatch rather than as what it -was. - -**Filed, not fixed.** The pin belongs to the exclude-closure lane -- `census_exclude_derive`'s -own header names its dissolution (the pin retires when strict whole-tree resolve greens without -host fixed-point closure) -- and repairing an oracle inside a deletion PR would be this -document's own §6 objection turned on itself. The person who paid the cost is the right person -to report it and the wrong person to fix it. - -**Postscript: the edit that prompted this is reverted.** `gunbhub_serve_program` left the -deletion set in §4d, so the pin's row is correct again and the literals return to 83 and 27. -The finding stands on its own merits -- a property of the pin, not of this deletion. - -## 4i. The floor's second refusal, and it is the same module telling the truth again - -The required floor refused a second time, on one witness: -`v2.test.lens_inert_carrier.inert_carrier_test.inert_carrier_no_unrostered_or_stale`. The cause -is real, caused by this change, and is the census working rather than a defect. - -Deleting `v2.test.workflow.host_discovered_owned_data_manifest` (§4c -- the committed copy of a -`DO NOT COMMIT` ephemeral artifact) removed the **only non-test reference** to -`OwnedDataDiscoveryReceipt`, declared in `v2.compiler.discovery_enumeration`. Verified directly, -not through any re-implementation: before the cut that carrier had two non-test `.dag` -references, its own declaration and that manifest's construction of it; after, only its own -declaration. It is self-tested by `discovery_enumeration_test`. Declared once, self-tested, -zero consumers outside its own block is exactly the inert-carrier definition, so the lens -refused. - -**Fixed by rostering it, not by restoring the module.** The roster is the declared frontier for -precisely this state: the carrier's real consumer is *generated at runtime and deliberately -never committed*, so in the committed corpus it is inert **by construction** and no corpus scan -can see otherwise. Restoring the deleted module would re-commit a file its own generator stamps -`DO NOT COMMIT` to satisfy a hygiene lens -- the tail wagging the dog. - -**How this was diagnosed, because the first attempt was the error this document keeps -recording.** I re-implemented the inert-carrier rule from the Rust to locate the moved name. -That re-implementation was **not faithful** -- its absolute output shared *zero* names with the -live roster, twice, under two different test-predicate readings. Its *delta* was stable across -both, which is suggestive and not evidence. The claim above rests on a direct, checkable -observation about one symbol's references before and after. An unfaithful instrument does not -become trustworthy because its delta looks stable. - -## 4j. A finding this change is a specimen of, and it is not about this change - -**A defect keyed on a specific `OccurrenceId` value stops reproducing under any change that -perturbs occurrence allocation.** Stated as a property of the class, because the next such -defect will have it and nobody will be watching. - -The specimen is this branch, usable *precisely because it contains no fix*: - -| | tree | failed | planned | -| --- | --- | --- | --- | -| base `90986d19469` | as-is | **16** | 10425 | -| this branch | base minus 55 deleted modules | **1** (unrelated) | **10425** | - -The 16 are the `fold_lowering` / `body_lowering.statement_let_bind` family, every one -reporting `non-exhaustive pattern match on: OccurrenceId { value: 79 }`. All 16 identities are -absent -- passing -- on a tree that differs from their red base *only* by deleted modules. No -test file was deleted, no lowering code touched, and both runs reported the same discovery -figures (`offered=11798 routed=10425`). - -**That last control is weaker than an earlier revision of this section claimed.** It read -*"`planned` is identical at 10425, which rules out roster change"*. Identical counts are -consistent with an unchanged roster and do not establish one. Not a theoretical caveat: on the -very next run, `offered` moved **11798 → 11812** and `routed` **10425 → 10439** across two -commits that changed **only a markdown file and a single `inert_row` data row**, declines -unchanged. Fourteen witnesses entered *discovery* with no corpus change, cause **not yet -attributed**. So the discovered roster is not a pure function of the committed tree, and a -matching count is weaker evidence than it looks. - -The comparison still stands as a controlled one -- base and post-deletion runs agree on every -reported discovery figure, and the tree difference is exactly the 55 deletions -- but it is -corroboration, not proof, and the isolated-variable claim is downgraded accordingly. The same -failure this document records elsewhere, committed in its own write-up: **a count treated as a -check.** - -**Consequence for method:** a bisect, or any before/after comparison, across trees of different -module counts measures allocation luck rather than the defect. A green obtained that way is not -evidence of a repair. - -**What this is NOT a claim about.** Not that this change fixed anything -- it plainly did not, -and saying so would be the rung inflation DESIGN §4b names. Nor about the original defect's -status: that was root-caused separately at the declaration (`v2.extdeps.languages.dag`'s -int-literal constructors declared `OccurrenceId` while every caller passed -`SyntheticOccurrence`, repaired with a 17-site caller census), and main is green on that -repair. Two greens, two causes; this branch has the luck, main has the fix. - -## 4k. What the static instrument was actually worth, measured - -Across **seven instrument defects**, four extractor corrections, and a population that moved -298 → 131 → 117 → 114, **the static census never once identified a live module.** It -identified *unknowns*, correctly, and stopped this lane from acting on them. The one true -positive -- `gunbc.pr_digests` -- was found by **one execution**, the required floor; no static -bucket caught it. - -DESIGN §3 as a measurement rather than a doctrine: *in a fail-closed substrate the deletion is -the census*, demonstrated against a static alternative that got four rounds of sharpening and -still could not do the job. - -**The "zero near-misses" reading, stated so a later reader does not relax at it.** Of the 14 -rows that left the deletion set, **none became CONSUMED**. That reads as reassurance and is -not. Thirteen went to AMBIGUOUS -- **unresolved**, neither proven consumed nor unconsumed -- -and **a live module among them would present exactly as they do; the bucket cannot distinguish -it.** The honest statement is *thirteen rows of unknown status that I would have deleted*, and -the near-miss count is **1, found by execution**. - -**The same standard applied to the 55 being deleted.** They are STILL-UNCONSUMED or -island-closed DEAD-CONSUMER-ONLY: zero on three *decoded* surfaces whose declared standing is -`LowerBoundOnly`. The best-evidenced set this instrument can produce. **It is not proof** -- -which is the argument *for* landing it and reading the refusals, not a risk run in spite of -them. - -## 4l. Editing this set means re-running the fixed point, not adjusting the count - -`69 − 13 + 1 = 56` reconciled (a fourth term lands in §4m, making it 55) arithmetically **by -the wrong route**: the 13 left on *evidential* grounds (the corrected extractor moved them to -AMBIGUOUS) and the 14th, `v2.std.rust_leaf_model_claim`, on a *structural* one -- restoring -`v2.test.language_model.rust` re-created its in-population consumer, so deleting it would have -split an island the restore had just re-formed. It is still residue. Two unrelated mechanisms -that happen to sum correctly: coincidence in the totals wearing the appearance of -reconciliation -- a control derived from the measurement it controls, one level up. - -**So: anyone who later adds or removes a row from this set must RE-RUN THE FIXED POINT, not -adjust the count.** Eligibility is a property of the set (§4g), a restore creates a violation -as readily as a deletion, and the failure is silent -- the arithmetic keeps agreeing. - -## 4m. The merge was the third census, and it refused one module - -**`v2.workflow.floor2_prepared_subject` was deleted by this branch and is restored here.** -Not because the measurement was wrong -- it was STILL-UNCONSUMED at base `90986d19469` on every -decoded surface, and still is at that commit. It acquired a consumer *while this branch was -open*: #8896 landed `src/v2/workflow/floor_terminal_ledger.dag`, which imports -`WitnessIdentity` and `witness_identity_qualified_name` from it by name, as does its test. - -**Worth a section for the shape of the failure, which no measurement in this document could -have had.** The two branches touch disjoint files: #8896 adds two files this branch never saw, -this branch deletes one file #8896 never saw. **git merged them clean.** No textual conflict, -no marker, no driver refusal -- and the merged tree does not compile. A three-way textual merge -answers *did the same bytes change twice*; the deciding question is *does the merged tree still -resolve*, which is not about bytes at all. **A merge preserves bytes, not measurements**: every -count in this document is a statement about a tree, and the merge produced a tree none of them -were measured against. - -The correction is the doctrine's own mechanism arriving one stage later than expected. §3 says -the deletion is the census and the refusals are the instrument; here the refusal is a -*merge-time* one, and the instrument that would have reported it is the floor on the merged -tree -- which did not exist until the merge created it. **The re-score against the merged tree -found exactly one**, and the §4l rule was followed rather than the count adjusted: the -restore's own imports were checked for in-population consumers, the fixed point closed at one, -no cascade followed. - -**The near-miss ledger now reads 2, both found by execution and neither by the census.** -`gunbc.pr_digests` (defect 7, the floor refusing on an unread declaration surface) and this one -(the merge refusing on a tree that did not exist when the surface was read). The same finding -from two directions: **a static census is a statement about the tree it read, and both of its -blind spots are trees -- the part it decoded wrongly, and the part not yet written.** The -second is not fixable by improving the instrument. - -**And the arithmetic breaks the way §4l says it does, for a third unrelated reason.** -`69 - 13 + 1 - 1 = 55` now reconciles, and the fourth term shares no mechanism with the other -three: 13 evidential, 1 structural, 1 temporal. Anyone tempted to read that chain as a -derivation should read §4l instead. - - -### 4m.1 The rule this produced was wrong on its dangerous half, and the correction is unilateral - -**First statement of the consequence: "a PR that deletes a module and merges main cleanly is not -verified by that clean merge, so deleters owe a resolve pass."** True, and it told a sibling -lane it was clear when it was not. `bright-ferret-335` checked its subtree on that rule, found -no deletion lanes -- #8877, #8909 and #8882 are purely additive -- and concluded it had nothing -to check. #8909 adds three files that all import `v2.workflow.floor2_prepared_subject`, the -module §4m restores. - -**The hazard is symmetric and the ADDER is the worse side.** A deleter that merges main -discovers an added consumer in its own resolve. An adder that merges main is *already green* -when the deletion lands later and breaks it -- and an additive PR feels safe by construction, -so its author has no reason to ask whether anything it imports is scheduled for deletion in -someone else's open branch. The party in danger is the party without a prompt. - -**The proposed repair was an intersection requiring publication** -- the deleter cannot see -unmerged additions, the adder cannot see unmerged deletions, so one side publishes a list. -**The first clause is false**, falsified by running it: open PR heads are on the remote, -`gh pr list` enumerates them, `git fetch origin ` retrieves them, and the intersection was -run against all three sibling PRs from this side with no list. Zero real exposure: no qualified -import of any of the 55, and of the 664 symbols declared solely by the 55 with no surviving -declarer, one token hit -- `render_footer` in #8909, which declares its own and never depended -on the deleted `tools.readme`. Deleting it *removes* a two-declarer whole-pool ambiguity rather -than creating a break. - -**One result from that intersection has the opposite sign from a normal token hit, and is -worth its own class.** `render_footer` read as a hazard -- a name declared by a module being -deleted and used in an open PR -- and inverted on inspection: the consumer declares its own, -and while `tools.readme` stands the name has TWO whole-pool declarers. It is AMBIGUOUS, and the -deletion *removes* the ambiguity. **So a deletion can improve resolution safety, and the same -intersection that finds breaks also finds these.** A reviewer reading every token hit as danger -misclassifies this direction. - -It matters beyond one row because it is the ambient-pool defect in miniature: a name resolving -by whole-pool uniqueness is **one deletion away from binding differently**, so its meaning is a -function of the corpus denominator rather than of anything written at the declaration or the -use. This deletion happens to move that name from two declarers to one. **Nothing structural -guarantees the next one moves that way** -- the same mechanism can, elsewhere, silently rebind -a use from one surviving declarer to another. That class is neither created nor closed by this -change; it is visible from here because an intersection over deletions is one of the few -operations that looks directly at it. - -**So the obligation is unilateral, which is the point of restating it.** A rule requiring -publication requires coordination, which fails silently when a lane is busy, blocked or -archived; a rule one party can execute alone has no such failure mode. **The deleter owes the -intersection against every open PR head at merge time. The adder owes nothing but pushing.** -That places the duty on the party that knows it is dangerous -- the sibling lane's own argument -for why the adder cannot be trusted to look, one step further: an adder who cannot be expected -to look cannot be expected to publish either. - -The residue is real but strictly smaller than the version needing coordination: this reaches -*pushed* work only. Work in a never-pushed worktree is unreachable by any mechanism, and that -alone is what publication would buy. - -## 5. The 55 deleted - -| module | path | bucket | -| --- | --- | --- | -| `gunbc.assimilate.bmc_wif_canary_bootstrap` | `dag/gunbc/assimilate/bmc_wif_canary_bootstrap.dag` | STILL-UNCONSUMED | -| `gunbc.cursor_sdk_secure_api_key` | `dag/gunbc/cursor_sdk_secure_api_key.dag` | STILL-UNCONSUMED | -| `gunbc.devboot.vertical_receipt` | `dag/gunbc/devboot/vertical_receipt.dag` | STILL-UNCONSUMED | -| `gunbc.floor_resolve_realization` | `dag/gunbc/floor_resolve_realization.dag` | STILL-UNCONSUMED | -| `gunbc.language_subject_scope_scaffold` | `dag/gunbc/language_subject_scope_scaffold.dag` | STILL-UNCONSUMED | -| `gunbc.p3a1_self_fork_homonym_disposition` | `dag/gunbc/p3a1_self_fork_homonym_disposition.dag` | STILL-UNCONSUMED | -| `gunbc.parse_allowlist` | `dag/gunbc/parse_allowlist.dag` | STILL-UNCONSUMED | -| `gunbc.provider_standing_live_probes` | `dag/gunbc/provider_standing_live_probes.dag` | STILL-UNCONSUMED | -| `gunbc.scm.commit_closure_store` | `dag/gunbc/scm/commit_closure_store.dag` | STILL-UNCONSUMED | -| `gunbc.site.register_principles` | `dag/gunbc/site/register_principles.dag` | STILL-UNCONSUMED | -| `gunbc.srv4_seeded_install_media_artifact` | `dag/gunbc/srv4_seeded_install_media_artifact.dag` | STILL-UNCONSUMED | -| `gunbc.tools.bmc_onboard_validate` | `dag/gunbc/tools/bmc_onboard_validate.dag` | STILL-UNCONSUMED | -| `gunbc.tools.grounding_confirm` | `dag/gunbc/tools/grounding_confirm.dag` | STILL-UNCONSUMED | -| `gunbc.tools.roadmap_spawn_request` | `dag/gunbc/tools/roadmap_spawn_request.dag` | STILL-UNCONSUMED | -| `gunbc.witness_family_fanout` | `dag/gunbc/witness_family_fanout.dag` | STILL-UNCONSUMED | -| `shared.dag_util` | `dag/shared/dag_util.dag` | DEAD-CONSUMER-ONLY | -| `std.binding` | `dag/std/binding.dag` | STILL-UNCONSUMED | -| `std.containers` | `dag/std/containers.dag` | STILL-UNCONSUMED | -| `std.list` | `dag/std/list.dag` | STILL-UNCONSUMED | -| `tools.build` | `dag/gunbc/instruments/build.dag` | STILL-UNCONSUMED | -| `tools.codegen` | `dag/gunbc/instruments/codegen.dag` | STILL-UNCONSUMED | -| `tools.readme` | `dag/gunbc/instruments/readme.dag` | STILL-UNCONSUMED | -| `v2.bin.main` | `src/v2/bin/main.dag` | STILL-UNCONSUMED | -| `v2.extdeps.formats.csv` | `src/v2/extdeps/formats/csv.dag` | STILL-UNCONSUMED | -| `v2.extdeps.formats.json_schema` | `src/v2/extdeps/formats/json_schema.dag` | DEAD-CONSUMER-ONLY | -| `v2.extdeps.formats.openapi` | `src/v2/extdeps/formats/openapi.dag` | STILL-UNCONSUMED | -| `v2.extdeps.formats.toml` | `src/v2/extdeps/formats/toml.dag` | STILL-UNCONSUMED | -| `v2.extdeps.formats.yaml` | `src/v2/extdeps/formats/yaml.dag` | DEAD-CONSUMER-ONLY | -| `v2.test.language_model.go_r1` | `src/v2/extdeps/language_model/go_r1.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.go_r2a` | `src/v2/extdeps/language_model/go_r2a.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.go_r2b` | `src/v2/extdeps/language_model/go_r2b.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.go_r3_external` | `src/v2/extdeps/language_model/go_r3_external.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.python_cross_runtime_drift` | `src/v2/extdeps/language_model/python_cross_runtime_drift.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.python_l2_cross_target_parity` | `src/v2/extdeps/language_model/python_l2_cross_target_parity.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.python_r2a` | `src/v2/extdeps/language_model/python_r2a.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.python_r2b` | `src/v2/extdeps/language_model/python_r2b.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.python_r3_external` | `src/v2/extdeps/language_model/python_r3_external.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.rust_r2a` | `src/v2/extdeps/language_model/rust_r2a.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.rust_r2b` | `src/v2/extdeps/language_model/rust_r2b.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.rust_r3_external` | `src/v2/extdeps/language_model/rust_r3_external.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.typescript_r2a` | `src/v2/extdeps/language_model/typescript_r2a.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.typescript_r2b` | `src/v2/extdeps/language_model/typescript_r2b.dag` | STILL-UNCONSUMED | -| `v2.test.language_model.typescript_r3_external` | `src/v2/extdeps/language_model/typescript_r3_external.dag` | STILL-UNCONSUMED | -| `v2.extdeps.typecheckers.mypy` | `src/v2/extdeps/typecheckers/mypy.dag` | STILL-UNCONSUMED | -| `v2.extdeps.typecheckers.pyright` | `src/v2/extdeps/typecheckers/pyright.dag` | STILL-UNCONSUMED | -| `v2.test.algebra_laws.zip_eq_list_equality` | `src/v2/std/algebra_laws/zip_eq_list_equality.dag` | STILL-UNCONSUMED | -| `v2.std.inhabitant_bridge` | `src/v2/std/inhabitant_bridge.dag` | STILL-UNCONSUMED | -| `v2.test.nat_semiring.rung_l1_go_compiler_slice` | `src/v2/std/nat_semiring/rung_l1_go_compiler_slice.dag` | STILL-UNCONSUMED | -| `v2.test.nat_semiring.rung_l1_python_runtime` | `src/v2/std/nat_semiring/rung_l1_python_runtime.dag` | STILL-UNCONSUMED | -| `v2.test.qualified_name.from_node` | `src/v2/std/qualified_name/from_node.dag` | STILL-UNCONSUMED | -| `v2.std.type_expr_projection_row_schema` | `src/v2/std/type_expr_projection_row_schema.dag` | STILL-UNCONSUMED | -| `v2.workflow.ci_stage0_partition_compile_gate_emit` | `src/v2/workflow/ci_stage0_partition_compile_gate_emit.dag` | STILL-UNCONSUMED | -| `v2.workflow.ci_v1_compiler_test_targets_compile_gate_emit` | `src/v2/workflow/ci_v1_compiler_test_targets_compile_gate_emit.dag` | STILL-UNCONSUMED | -| `v2.test.workflow.host_discovered_owned_data_manifest` | `src/v2/workflow/host_discovered_owned_data_manifest.dag` | STILL-UNCONSUMED | -| `v2.workflow.probe_selector_host_health` | `src/v2/workflow/probe_selector_host_health.dag` | STILL-UNCONSUMED | - -## 6. The 58 held, with the reason each survived - -**Held is not keep.** Every row carries a typed reason and an owed next step; none is -dispositioned as "consumed". The census's instruction is that a refusal is data, and 58 -refusals over a 114-row residue is the measurement this lane produces. - -Separately, **97 rows sit in AMBIGUOUS-SHARED-ONLY**, outside the residue entirely: not -provably unconsumed, not proven consumed, each needing a per-row read. 13 arrived there from -this lane's own re-derivation (§4d). - -### Residue, but named somewhere this cut would strand (35) - -Named by a live `.dag`, `.rs` or data file, or otherwise carrying an obligation the deletion precondition refuses to guess at. Weak evidence both ways: several are receipt prose and ordinary residue; at least one (`v2.workflow.product_receipt_stage`) is a live transport named via an `--entry` path held in a *variable*, the dynamically-composed-argv blind spot the census declares. - -| module | path | bucket | -| --- | --- | --- | -| `direct_rust_door_ingest_fixture` | `src/v2/compiler/self_host/direct_rust_door_ingest_fixture.dag` | STILL-UNCONSUMED | -| `extdeps.bmc.mock_corpus` | `dag/extdeps/bmc/mock_corpus.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.ebay.mock_corpus` | `dag/extdeps/ebay/mock_corpus.dag` | STILL-UNCONSUMED | -| `extdeps.linux.mock_corpus` | `dag/extdeps/linux/mock_corpus.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.tcgplayer.mock_corpus` | `dag/extdeps/tcgplayer/mock_corpus.dag` | STILL-UNCONSUMED | -| `gunbc.auth.optional_impersonation` | `dag/gunbc/auth/optional_impersonation.dag` | STILL-UNCONSUMED | -| `gunbc.auth.patterns` | `dag/gunbc/auth/patterns.dag` | STILL-UNCONSUMED | -| `gunbc.char_at_scaling_probe_support` | `dag/gunbc/char_at_scaling_probe_support.dag` | STILL-UNCONSUMED | -| `gunbc.ci_build_job_v1_compiler_unit_receipt` | `dag/gunbc/ci/ci_build_job_v1_compiler_unit_receipt.dag` | STILL-UNCONSUMED | -| `gunbc.generic_binder_field_projection_deficit` | `dag/gunbc/generic_binder_field_projection_deficit.dag` | STILL-UNCONSUMED | -| `gunbc.githooks_pre_push_cli` | `dag/gunbc/githooks/githooks_pre_push_cli.dag` | STILL-UNCONSUMED | -| `gunbc.namespace_census_receipt` | `dag/gunbc/namespace/namespace_census_receipt.dag` | STILL-UNCONSUMED | -| `gunbc.p1_retention_cohort_receipt` | `dag/gunbc/p1_retention_cohort_receipt.dag` | STILL-UNCONSUMED | -| `gunbc.plans.affected_set_self_confirmation` | `dag/gunbc/plans/affected_set_self_confirmation.dag` | STILL-UNCONSUMED | -| `gunbc.plans.fleet_subsumption_manual_gaps` | `dag/gunbc/plans/fleet_subsumption_manual_gaps.dag` | STILL-UNCONSUMED | -| `gunbc.plans.host_convergence_circuit_residue` | `dag/gunbc/plans/host_convergence_circuit_residue.dag` | STILL-UNCONSUMED | -| `gunbc.plans.transport_argv_anemia_dissolution` | `dag/gunbc/plans/transport_argv_anemia_dissolution.dag` | STILL-UNCONSUMED | -| `gunbc.seed_closed_vocabulary_wildcard_census` | `dag/gunbc/seed_closed_vocabulary_wildcard_census.dag` | STILL-UNCONSUMED | -| `gunbc.site.interaction` | `dag/gunbc/site/interaction.dag` | STILL-UNCONSUMED | -| `gunbc.test_node_wall_clock_ratchet` | `dag/gunbc/test_node_wall_clock_ratchet.dag` | STILL-UNCONSUMED | -| `gunbc.v1_maintenance_standing` | `dag/gunbc/v1/v1_maintenance_standing.dag` | STILL-UNCONSUMED | -| `std.exec_format` | `dag/std/exec_format.dag` | DEAD-CONSUMER-ONLY | -| `std.import` | `dag/std/import.dag` | STILL-UNCONSUMED | -| `std.methods` | `dag/std/methods.dag` | STILL-UNCONSUMED | -| `std.verification` | `dag/std/verification.dag` | STILL-UNCONSUMED | -| `v2.std.datetime` | `src/v2/std/datetime.dag` | DEAD-CONSUMER-ONLY | -| `v2.std.probe_selector` | `src/v2/std/probe_selector.dag` | DEAD-CONSUMER-ONLY | -| `v2.std.rust_leaf_model_claim` | `src/v2/std/rust_leaf_model_claim.dag` | DEAD-CONSUMER-ONLY | -| `v2.test.workflow.glob_discovery_law` | `src/v2/workflow/glob_discovery_law.dag` | STILL-UNCONSUMED | -| `v2.workflow.class_b_import_closure_transport` | `src/v2/workflow/class_b_import_closure_transport.dag` | DEAD-CONSUMER-ONLY | -| `v2.workflow.compiler_closure_ingest_transport` | `src/v2/workflow/compiler_closure_ingest_transport.dag` | DEAD-CONSUMER-ONLY | -| `v2.workflow.phase_profile_proof_plan` | `src/v2/workflow/phase_profile_proof_plan.dag` | STILL-UNCONSUMED | -| `v2.workflow.product_receipt_stage` | `src/v2/workflow/product_receipt_stage.dag` | STILL-UNCONSUMED | -| `v2.workflow.source_root_ingest_gate` | `src/v2/workflow/source_root_ingest_gate.dag` | STILL-UNCONSUMED | -| `v2.workflow.source_root_ingest_transport` | `src/v2/workflow/source_root_ingest_transport.dag` | DEAD-CONSUMER-ONLY | - -### Declares an `ExternalAuthority` anchor (21) - -DESIGN §3's extdeps citation duty: the value may be the citation, not a call, so unreachability is not evidence of residue. Mostly `DEAD-CONSUMER-ONLY` — the island shape — so they delete as connected components or not at all (census B6+, one island per PR, each gated on whether the citation is the deliverable). - -| module | path | bucket | -| --- | --- | --- | -| `extdeps.boot.emit` | `dag/extdeps/boot/emit.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.boot.framebuffer` | `dag/extdeps/boot/framebuffer.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.boot.freestanding_payload` | `dag/extdeps/boot/freestanding_payload.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.boot.linux_x86_boot` | `dag/extdeps/boot/linux_x86_boot.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.cloud.gcp.sts` | `dag/extdeps/cloud/gcp/sts.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.colo.types` | `dag/extdeps/colo/types.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.container.oci.image_config` | `dag/extdeps/container/oci/image_config.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.container.oci.linux` | `dag/extdeps/container/oci/linux.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.container.oci.manifest` | `dag/extdeps/container/oci/manifest.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.container.oci.runtime_config` | `dag/extdeps/container/oci/runtime_config.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.ebay.ebay_contracts` | `dag/extdeps/ebay/ebay_contracts.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.formats.elf.encode` | `dag/extdeps/formats/elf/encode.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.formats.elf.hello_static_witness` | `dag/extdeps/formats/elf/hello_static_witness.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.formats.elf.primitives` | `dag/extdeps/formats/elf/primitives.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.formats.elf.segments` | `dag/extdeps/formats/elf/segments.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.formats.elf.types` | `dag/extdeps/formats/elf/types.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.github.mergeable_state_contracts` | `dag/extdeps/github/mergeable_state_contracts.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.posix.rusage` | `dag/extdeps/posix/rusage.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.runtime.local` | `dag/extdeps/runtime/local.dag` | DEAD-CONSUMER-ONLY | -| `extdeps.tcgplayer.tcgplayer` | `dag/extdeps/tcgplayer/tcgplayer.dag` | DEAD-CONSUMER-ONLY | -| `gunbc.hand_lens_host_bridge_scaffold_index` | `dag/gunbc/hand_lens_host_bridge_scaffold_index.dag` | DEAD-CONSUMER-ONLY | - -### Frozen against a named re-add (2) - -DESIGN §3 frozen-X. Deleting these deletes what the re-add queue exists to re-attach. - -| module | path | bucket | -| --- | --- | --- | -| `tools.dag_compile_clean_seam_transport` | `dag/gunbc/instruments/dag_compile_clean_seam_transport.dag` | DEAD-CONSUMER-ONLY | -| `tools.merge_admission_capture_transport` | `dag/gunbc/instruments/merge_admission_capture_transport.dag` | STILL-UNCONSUMED | - -## 7. What this change does NOT claim - -- **Not that 55 is the maximum safe cut.** It is the subset provable clean on every decoded - surface, islands closed. The 35 rows held for a per-row read are held on a *string mention*, - weak evidence in both directions. -- **Not that the residue is now 58.** Five modules moved DEAD-CONSUMER-ONLY to - STILL-UNCONSUMED as their island neighbours went. **The residue is a fixed point reached by - iteration, not a set reached in one pass**, and a follow-on pass is owed. -- **Not that the AMBIGUOUS bucket is cleared.** 97 rows are unresolvable at identity grain. - 13 got there from this lane's own correction; reading that as "13 live modules found" would - be exactly wrong: they are *not proven consumed either*. -- **Not that the extractor is now correct.** Four defects were found in it, three by someone - else hand-reading rows while every counter stayed consistent. The honest claim is that it - passes four discriminators it previously failed one of, not that a fifth defect does not - exist. -- **Not that the directive's second arm was served.** Every row was deleted or held; **none - was wired up.** The candidates are the 21 `CITED-AUTHORITY` islands and the 22 modules the - census found named `v2.test.*` while declaring no test and sitting outside every test path - -- a name asserting enrolment that floor discovery, which keys on the file suffix, never - grants. - -## 8. Evidence - -The deletion's discriminating check is not "the tree still parses" -- 71 modules nothing -imports would parse-clean whether or not they were load-bearing. It is the pair of counters in -section 2 that must not move (`reachable`, `CONSUMED-DECISIVE`) beside the one that must -(`population`, by exactly the number of files removed), plus the required run: - -`claim_executor --required-ci --source-root dag --source-root src/v2` -- the three-phase -required mode (src/v1 `.dag` parse sweep, `--required-regen`, witness floor). Its result on -this branch is reported on the PR. - -**The instrument is not committed, by repository rule:** `.gitignore` excludes `*.py` -tree-wide, so the audit script that produced these numbers has no home here. It is fully -specified instead -- the census's own section-2 method without deviation; the two arms that -matter, exactly: - -- **Roots** are the discovery paths (`/test/`, `*_test.dag`, `/lens/`, `/manual/`, - `/fixture*`), every `--entry` argv literal and `*entry*:` path field found in any `.dag`, - `.rs`, `.md`, `.yml`, `.sh` or `.toml` file, and every module whose name-with-underscores - matches a `src/v1/stage0/src/*.rs` seed mirror. Edges are `import` lines plus - fully-qualified `module.symbol` references resolved by longest-prefix against the module - index. The population is what no root reaches. -- **The defect-6 re-score** takes each population member's declared symbols -- not its name, - not its path -- keeps only those declared by exactly one module corpus-wide, and asks - whether any other `.dag` file names one bare. Comments and string literals are removed - with a character scanner, never a regex, because a regex terminates early on the `\{` - interpolation escapes real `.dag` prose contains. An identifier preceded by `.` or - followed by `:` is not a reference. - -That specification makes the numbers disagreeable: re-implementing it is a half-page, and any -reader who does so and gets a different answer has found a defect in one of us. Beside it, the -two counters in section 2 are checkable without the instrument -- `reachable` and -`CONSUMED-DECISIVE` not moving is a claim about the tree, not about the -script. \ No newline at end of file diff --git a/docs/plans/uses-occurrence-census.md b/docs/plans/uses-occurrence-census.md deleted file mode 100644 index c9b81926ee6..00000000000 --- a/docs/plans/uses-occurrence-census.md +++ /dev/null @@ -1,364 +0,0 @@ -# USES-0 census: authored `uses` joined to body-derived demand - -Authority: operator D13 (2026-09-18), recorded in `gunbc.plans.demand_engine_program`. This analysis is the identity-grain join. It does not delete or add any `uses` row. - -## Method - -Two independently produced populations: - -1. **Authored `uses` rows** — every parsed function/pattern header clause `uses alias: Type` in committed `.dag` source (not AST field `uses:`, not GHA `uses:`, not comments). Identity is `(module file, declaration, alias, type)`. -2. **Body-derived demand (session approximation of DependencyDemand)** — capitalized dotted operation references in the visible body that inhabit the authored resource, plus `file_compare_and_set` (Filesystem) and `clock_now_probed_at` (Clock); plus calls to other declarations that themselves author a `uses` of the same normalized resource (`Network` = `std.resources.Network`). - -A dotted call is **not** Network demand merely because it is a host operation. `Filesystem.*` is Filesystem. `shell.Mktemp`, `shell.Remove`, `shell.Move`, `shell.Env`, `os.Hostname`, and `git.Inspect` / `git.Core` are local and do not establish Network. Classifying `read_docker_hub_image_config_at_digest` as DirectRestatement from `shell.Mktemp` was that error (review 67467): the Network work is `docker_hub_bearer_token` → `http.Client.GetBounded` with no `uses` on those helpers, which is Unresolved. - -This is **not** the compiler `DependencyDemand` carrier. Whole-corpus resolve that would produce that carrier is CI-sized (the seed cannot hold the v2 closure under a session cap). Rows the source join cannot decide are `Unresolved` — that class is the CI-side remainder, not a count to close by hand. No cardinality is an oracle; membership is the table. A helper call that is not itself a `uses` declaration, and is not a known pure combinator, is insufficient call relation: class Unresolved, even if the body also shows a *different* resource. - -## Class dispositions (D13) - -| Class | When | Disposition | -| --- | --- | --- | -| DirectRestatement | body directly references a resolved operation of this resource | delete/derive | -| TransitiveRestatement | body only calls another declaration that requires it | delete/derive over call relations | -| NamedDependencyBinder | a distinct logical provider identity actually used in the body | move to dependency-subject or capability param | -| OpaqueContract | no body; requirement not derivable | keep as contract | -| PolicyEnvelope | constrains what may be used, not actual use | move to policy | -| UnusedRequirement | no body path consumes this authored identity | delete/refuse | -| Unresolved | identity/call relations insufficient (typically a helper without `uses` that still performs host work) | typed refusal until resolution names the relation | - -## Empty classes in the live parsed population - -- **OpaqueContract** — every live `uses` clause is followed by a visible body. The interesting inverse is `gunbc.clock_read` `clock_now_probed_at`, which calls `Clock.Now()` with **no** authored `uses`. -- **PolicyEnvelope** — no header constrains a derived set (no may-not-use / upper-bound form exists in source). -- **NamedDependencyBinder** — every live alias is the generic interface (`net: Network`, `fs: Filesystem`, `clock: Clock`). No row distinguishes `source_tree` from `output_tree`. Fixture strings that bind `net: EaNet` / `GewNet` / `ZpeNet` and read `net.id` are the binder-shaped specimens, listed separately. - -Likely outcome per the ruling: the function-level `uses` clause disappears for transparent bodies; what would survive is opaque/subject/policy, of which the live corpus currently has none. - -## DirectRestatement - -| File | Declaration | Alias | Authored type | Evidence | -| --- | --- | --- | --- | --- | -| `dag/gunbc/assimilate/bmc_bootstrap_provision.dag` | `bmc_bootstrap_provision_srv3` | `net` | `std.resources.Network` | ops shell.GCloud.AuthPrintAccessToken, gcp.ServiceUsage.BatchEnableServices, gcp.IamAdmin.CreateServiceAccount, gcp.SecretManager.GetSecretIamPolicy, gcp.SecretManager.SetSecretIamPolicy | -| `dag/gunbc/auth/access_token_source.dag` | `ensure_access_token` | `net` | `Network` | ops shell.GCloud.AuthPrintAccessToken; local-ops shell.Env.Get | -| `dag/gunbc/auth/credentials.dag` | `gcp_secret_credential` | `net` | `std.resources.Network` | ops shell.GCloud.AuthPrintAccessToken, gcp.SecretManager.AccessVersion | -| `dag/gunbc/auth/credentials.dag` | `gcp_adc_token_from_credentials` | `net` | `std.resources.Network` | ops oauth2.Google.Refresh | -| `dag/gunbc/auth/gcp_secret_access.dag` | `secret_access_ensure_for` | `net` | `Network` | ops gcp.SecretManager.GetSecretIamPolicy, gcp.SecretManager.SetSecretIamPolicy; callees ensure_access_token | -| `dag/gunbc/auth/patterns.dag` | `credential_chain` | `net` | `std.resources.Network` | ops gcp.STS.Exchange, gcp.SecretManager.AccessVersion | -| `dag/gunbc/auth/patterns.dag` | `github_oidc` | `net` | `std.resources.Network` | ops github.OIDC.GetToken; local-ops shell.Env.Get, shell.Env.Get | -| `dag/gunbc/auth/patterns.dag` | `metadata_oidc` | `net` | `std.resources.Network` | ops gcp.Metadata.GetIdentityToken | -| `dag/gunbc/auth/secret_ref_credential.dag` | `fetch_secret_ref_credential_with_token` | `net` | `Network` | ops gcp.SecretManager.AccessVersion | -| `dag/gunbc/command_runner.dag` | `run_shell_command` | `net` | `Network` | ops shell.Exec.RunArgv | -| `dag/gunbc/command_runner.dag` | `run_shell_command_observe` | `net` | `Network` | ops shell.Exec.RunArgv | -| `dag/gunbc/host/host_reset_return_run.dag` | `host_is_reachable` | `net` | `std.resources.Network` | ops ssh.Session.Reachability | -| `dag/gunbc/machine_intake/megarac_media_attach.dag` | `megarac_attach_remote_image` | `net` | `std.resources.Network` | ops megarac.Media.OpenSession; callees megarac_attach_then_release, megarac_release_unreadable_session | -| `dag/gunbc/machine_intake/megarac_media_attach.dag` | `megarac_release_unreadable_session` | `net` | `std.resources.Network` | ops megarac.Media.CloseSession | -| `dag/gunbc/machine_intake/megarac_media_attach.dag` | `megarac_attach_then_release` | `net` | `std.resources.Network` | ops megarac.Media.CloseSession; callees megarac_attach_with_token | -| `dag/gunbc/machine_intake/megarac_media_attach.dag` | `megarac_attach_with_token` | `net` | `std.resources.Network` | ops megarac.Media.GetRemoteConfigurations, megarac.Media.GetRemoteImages; callees megarac_start_and_confirm | -| `dag/gunbc/machine_intake/megarac_media_attach.dag` | `megarac_start_and_confirm` | `net` | `std.resources.Network` | ops megarac.Media.StartMedia, megarac.Media.GetRemoteConfigurations | -| `dag/gunbc/machine_intake/oob_boot_handoff.dag` | `oob_power_cycle_after_confirmed_selection` | `net` | `std.resources.Network` | ops diagnostic.ipmi.Tool.ChassisPowerControl | -| `dag/gunbc/machine_intake/oob_boot_handoff.dag` | `oob_boot_handoff` | `net` | `std.resources.Network` | ops diagnostic.ipmi.Tool.ChassisBootDev, diagnostic.ipmi.Tool.ChassisBootParamGet; callees oob_power_cycle_after_confirmed_selection | -| `dag/gunbc/roadmap/roadmap_launch_deployment_cli.dag` | `rlm_predecessor_run` | `net` | `Network` | ops github.WorkflowRuns.GetRun | -| `dag/gunbc/srv3/srv3_boot_once_cd.dag` | `srv3_boot_once_cd_gated` | `clock` | `std.resources.Clock` | ops clock_now_probed_at | -| `dag/gunbc/srv3/srv3_boot_once_cd.dag` | `srv3_claim_boot_once_cd_authorization` | `fs` | `std.resources.Filesystem` | ops file_compare_and_set | -| `dag/gunbc/srv3/srv3_boot_once_cd.dag` | `srv3_settle_boot_once_cd_claim` | `fs` | `std.resources.Filesystem` | ops file_compare_and_set | -| `dag/gunbc/srv3/srv3_boot_once_cd.dag` | `srv3_boot_once_cd_resolved` | `net` | `std.resources.Network` | ops Filesystem.Write, redfish.Http.SetBootSourceOverride, Filesystem.Write, redfish.Http.ResetSystem; callees materialize_bmc_login_password; other-demand Filesystem | -| `dag/gunbc/tools/bmc_first_contact.dag` | `bmc_first_contact` | `net` | `std.resources.Network` | ops redfish.Http.GetSystem; local-ops shell.Env.Get | -| `dag/gunbc/tools/bmc_onboard.dag` | `bmc_rotate_credential` | `net` | `Network` | ops Filesystem.Write, redfish.Http.SetAccountPassword, redfish.Http.GetSystem; other-demand Filesystem | -| `dag/gunbc/tools/bmc_onboard.dag` | `bmc_store_and_rotate` | `net` | `Network` | ops gcp.SecretManager.AddVersion, gcp.SecretManager.AccessVersion; callees ensure_access_token, bmc_rotate_credential | -| `dag/gunbc/tools/bmc_onboard.dag` | `bmc_assimilate_credential` | `net` | `Network` | ops redfish.Http.GetServiceRoot, redfish.Http.GetSystem; callees bmc_store_and_rotate | -| `dag/gunbc/tools/bmc_onboard.dag` | `bmc_probe_credential_phase` | `net` | `Network` | ops redfish.Http.GetSystem, gcp.SecretManager.AccessVersion, redfish.Http.GetSystem; callees ensure_access_token | -| `dag/gunbc/tools/bmc_onboard.dag` | `bmc_converge_credential` | `net` | `Network` | ops redfish.Http.GetServiceRoot; callees bmc_store_and_rotate, bmc_probe_credential_phase | -| `dag/gunbc/tools/bmc_read_telemetry.dag` | `bmc_read_telemetry` | `net` | `std.resources.Network` | ops redfish.Http.GetSystem, redfish.Http.GetChassisSensors; local-ops shell.Env.Get, shell.Env.Get | -| `dag/test/ctl/pos_emit.dag` | `emits_a_service_call` | `net` | `std.resources.Network` | ops diagnostic.ipmi.Tool.SensorList | -| `dag/test/ctl/pos_emit2.dag` | `emits_a_two_segment_call` | `net` | `std.resources.Network` | ops cargo.Build.Run | -| `dag/test/ctl/pos_service.dag` | `call_three_segment` | `net` | `std.resources.Network` | ops diagnostic.ipmi.Tool.SensorList | - -## TransitiveRestatement - -| File | Declaration | Alias | Authored type | Evidence | -| --- | --- | --- | --- | --- | -| `dag/gunbc/auth/credentials.dag` | `gcp_oauth_access_token_via_adc_refresh` | `net` | `std.resources.Network` | ops Filesystem.Read; callees gcp_adc_token_from_credentials; other-demand Filesystem | -| `dag/gunbc/auth/credentials.dag` | `gcp_oauth_access_token_adc_for_path` | `net` | `std.resources.Network` | callees gcp_oauth_access_token_via_adc_refresh | -| `dag/gunbc/auth/secret_access_admission.dag` | `secret_access_ensure_admitted` | `net` | `Network` | callees secret_access_ensure_for | -| `dag/gunbc/auth/secret_ref_credential.dag` | `fetch_secret_ref_credential` | `net` | `Network` | callees fetch_secret_ref_credential_with_token, ensure_access_token | -| `dag/gunbc/bmc/bmc_fan_converge.dag` | `bmc_fan_run` | `net` | `Network` | callees fetch_secret_ref_credential | -| `dag/gunbc/bmc/bmc_fan_converge.dag` | `srv3_bmc_fan_observe` | `net` | `Network` | callees bmc_fan_run | -| `dag/gunbc/bmc/bmc_fan_converge.dag` | `srv3_bmc_fan_converge` | `net` | `Network` | callees bmc_fan_run | -| `dag/gunbc/bmc/bmc_fan_converge.dag` | `srv4_bmc_fan_observe` | `net` | `Network` | callees bmc_fan_run | -| `dag/gunbc/bmc/bmc_fan_converge.dag` | `srv4_bmc_fan_converge` | `net` | `Network` | callees bmc_fan_run | -| `dag/gunbc/bmc/bmc_fan_converge.dag` | `srv4_bmc_fan_rollback_previous` | `net` | `Network` | callees bmc_fan_run | -| `dag/gunbc/bmc/bmc_netboot_serve.dag` | `bmc_netboot_run_bmc` | `net` | `Network` | callees run_shell_command | -| `dag/gunbc/bmc/bmc_netboot_serve.dag` | `bmc_netboot_put_path` | `net` | `Network` | callees run_shell_command | -| `dag/gunbc/bmc/bmc_netboot_serve.dag` | `bmc_netboot_stage_file` | `net` | `Network` | callees bmc_netboot_stage_inline, bmc_netboot_put_path | -| `dag/gunbc/bmc/bmc_netboot_serve.dag` | `bmc_netboot_stage_inline` | `net` | `Network` | ops Filesystem.Write; callees bmc_netboot_put_path; other-demand Filesystem | -| `dag/gunbc/bmc/bmc_netboot_serve.dag` | `bmc_netboot_provision` | `net` | `Network` | callees bmc_netboot_run_bmc, bmc_netboot_stage_file | -| `dag/gunbc/bmc/bmc_netboot_serve.dag` | `srv4_bmc_netboot_provision` | `net` | `Network` | callees bmc_netboot_provision | -| `dag/gunbc/busybox_bmc_build.dag` | `busybox_bmc_build_run` | `net` | `Network` | callees run_shell_commands | -| `dag/gunbc/command_runner.dag` | `run_shell_command_capture` | `net` | `Network` | callees run_shell_command_observe | -| `dag/gunbc/command_runner.dag` | `run_shell_commands` | `net` | `Network` | callees run_shell_command | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_effective_user_wet` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_host_short_wet` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_timer_members_wet` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_fleet_converge_request_wet` | `net` | `Network` | callees observe_runner_unit_standings_wet, observe_slot_retirement_evidence_wet, observe_runner_slot_members_with_provenance_wet, observe_timer_members_wet, observe_cap_members_wet | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_slot_retirement_evidence_wet` | `net` | `Network` | callees observe_transition_retirement_evidence | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_runner_unit_standings_wet` | `net` | `Network` | callees observe_runner_unit_standing_wet | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `write_fleet_converge_plan_artifact_wet` | `net` | `Network` | ops Filesystem.Write, Filesystem.Write, Filesystem.Write, Filesystem.Write, Filesystem.Write; callees run_shell_commands; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_allocation_store_plan_wet` | `net` | `Network` | callees write_fleet_converge_plan_artifact_wet, observe_generation_store_wet, observe_host_short_wet | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_plan_wet` | `net` | `Network` | callees fleet_converge_scoped_plan_wet | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_launch_environment_plan_wet` | `net` | `Network` | callees fleet_converge_scoped_plan_wet | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_scoped_plan_wet` | `net` | `Network` | callees fleet_converge_plan_scoped_bound_wet | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_plan_scoped_bound_wet` | `net` | `Network` | ops Filesystem.Write, Filesystem.Write, Filesystem.Write, Filesystem.Write, Filesystem.Write; callees run_shell_commands, observe_generation_store_wet, observe_host_short_wet, observe_fleet_converge_request_wet; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_apply_wet` | `net` | `Network` | callees fleet_converge_apply_bound_wet | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_apply_bound_wet` | `net` | `Network` | ops Filesystem.Read, Filesystem.Read, Filesystem.Read, Filesystem.Read, Filesystem.Read; callees run_shell_commands, observe_effective_user_wet, observe_host_short_wet, observe_fleet_converge_request_wet; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_local_plan_wet` | `net` | `Network` | callees observe_host_short_wet, observe_operator_local_run_binding, fleet_converge_plan_scoped_bound_wet | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_operator_cli_entry` | `net` | `Network` | ops Filesystem.Read, Filesystem.Write; callees fleet_converge_operator_cli_dispatch, observe_operator_local_run_binding; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_operator_cli_dispatch` | `net` | `Network` | local-ops git.Core.CurrentBranch; callees fleet_converge_local_apply_wet, fleet_converge_local_plan_wet, observe_host_short_wet | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `fleet_converge_local_apply_wet` | `net` | `Network` | ops Filesystem.Read; callees observe_operator_local_run_binding, fleet_converge_apply_bound_wet; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_runner_slot_provenance_wet` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_runner_slot_members_with_provenance_wet` | `net` | `Network` | callees observe_runner_slot_provenance_wet, observe_runner_slot_members_wet | -| `dag/gunbc/fleet/fleet_host_key_enrollment.dag` | `verify_enrollment_outcome` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/fleet/fleet_host_key_enrollment.dag` | `fleet_host_key_enroll_apply` | `net` | `Network` | ops Filesystem.Write; callees run_shell_commands, run_shell_command_capture; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_host_key_enrollment.dag` | `fleet_host_key_enroll_wet` | `net` | `Network` | ops Filesystem.Write; callees fleet_host_key_enroll_apply; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_host_key_enrollment.dag` | `fleet_authorized_key_self_enroll_wet` | `net` | `Network` | ops Filesystem.Write, Filesystem.Write; callees verify_enrollment_outcome, run_shell_command_capture; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_multi_principal_probe.dag` | `probe_agent_credential_verification` | `net` | `Network` | callees verify_fleet_ssh_agent_credential, materialize_agent_public_selector | -| `dag/gunbc/fleet/fleet_multi_principal_probe.dag` | `fleet_multi_principal_probe_with_attempt` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees materialize_fleet_known_hosts_anchor, run_with_materialized_fleet_ssh_binding; other-demand Clock,Filesystem | -| `dag/gunbc/fleet/fleet_multi_principal_probe.dag` | `spark_exact_grant_probe_wet` | `net` | `Network` | ops Filesystem.Write; callees materialize_fleet_known_hosts_anchor, run_with_materialized_fleet_ssh_binding; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_multi_principal_probe.dag` | `spark_managed_access_standing_for_attempt` | `net` | `Network` | callees spark_managed_access_standing_with_verified_credential, probe_agent_credential_verification | -| `dag/gunbc/fleet/fleet_probe_identity_observe.dag` | `fleet_probe_identity_observe_wet` | `net` | `Network` | ops Filesystem.Write; callees run_shell_command_capture; other-demand Filesystem | -| `dag/gunbc/fleet/fleet_ssh_credential_verify.dag` | `derive_identity_file_public_key_line` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/fleet/fleet_ssh_credential_verify.dag` | `verify_fleet_ssh_credential` | `net` | `Network` | callees derive_identity_file_public_key_line | -| `dag/gunbc/fleet/fleet_ssh_credential_verify.dag` | `list_agent_identities` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/fleet/fleet_ssh_credential_verify.dag` | `verify_fleet_ssh_agent_credential` | `net` | `Network` | callees list_agent_identities | -| `dag/gunbc/fleet/fleet_ssh_locus.dag` | `prepare_fleet_ssh_agent_context` | `net` | `Network` | callees fleet_ssh_context_from_verified_binding, verify_fleet_ssh_agent_credential, materialize_agent_public_selector | -| `dag/gunbc/fleet/fleet_ssh_locus.dag` | `fleet_ssh_context_from_verified_binding` | `net` | `Network` | callees materialize_fleet_known_hosts_anchor | -| `dag/gunbc/fleet/org_actions_converge.dag` | `org_admin_app_key_access_converge_with_supplied_token` | `net` | `Network` | callees secret_access_ensure_for | -| `dag/gunbc/fleet/printer_job_start.dag` | `acquire_pinned_ca_bundle` | `net` | `Network` | local-ops shell.Mktemp.Dir; callees run_shell_command_capture | -| `dag/gunbc/fleet/printer_job_start.dag` | `start_print_on_printer` | `net` | `Network` | callees publish_start_with_credential, fetch_secret_ref_credential | -| `dag/gunbc/fleet/printer_job_start.dag` | `publish_start_with_credential` | `net` | `Network` | local-ops shell.Remove.RecursiveForce, shell.Remove.RecursiveForce, shell.Remove.RecursiveForce; callees acquire_pinned_ca_bundle, run_shell_command_capture | -| `dag/gunbc/fleet/printer_project_delivery.dag` | `deliver_project_to_printer` | `net` | `Network` | local-ops shell.Remove.FileForce, shell.Remove.FileForce; callees fetch_secret_ref_credential, run_shell_command_capture | -| `dag/gunbc/host/host_build_cache_provision.dag` | `provision_build_cache` | `net` | `Network` | callees host_toolchain_ensure | -| `dag/gunbc/host/host_codex_runtime_provision.dag` | `provision_codex_runtime` | `net` | `Network` | callees host_toolchain_ensure | -| `dag/gunbc/host/host_compile_pool_provision.dag` | `provision_compile_pool` | `net` | `Network` | callees host_toolchain_ensure | -| `dag/gunbc/host/host_reset_return_run.dag` | `watch_step` | `net` | `std.resources.Network` | callees host_is_reachable | -| `dag/gunbc/host/host_reset_return_run.dag` | `watch_for` | `net` | `std.resources.Network` | callees watch_step | -| `dag/gunbc/host/host_reset_return_run.dag` | `observe_reset_return` | `net` | `std.resources.Network` | callees watch_for | -| `dag/gunbc/host/host_reset_return_run.dag` | `subject_baseline` | `net` | `std.resources.Network` | callees host_is_reachable | -| `dag/gunbc/host/host_reset_return_run.dag` | `host_reset_return_attempt` | `net` | `std.resources.Network` | callees host_reset_return_issue_and_watch, observer_self_corroboration, subject_baseline | -| `dag/gunbc/host/host_reset_return_run.dag` | `host_reset_return_issue_and_watch` | `net` | `std.resources.Network` | callees oob_boot_handoff, observe_reset_return | -| `dag/gunbc/host/host_reset_return_run.dag` | `host_reset_return_wet` | `net` | `std.resources.Network` | local-ops shell.Env.Get, shell.Env.Get; callees host_reset_return_exit, host_reset_return_attempt, host_reset_return_preflight_exit, host_reset_return_converge_route_read | -| `dag/gunbc/instruments/pair_incumbent_identity_standing.dag` | `group_identity_standing` | `net` | `Network` | callees prepare_fleet_ssh_agent_context | -| `dag/gunbc/instruments/pair_incumbent_identity_standing.dag` | `pair_incumbent_identity_standing` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees group_identity_standing; other-demand Clock,Filesystem | -| `dag/gunbc/live_deploy/deployed_tree_report.dag` | `deployed_tree_report_ci_wet` | `net` | `Network` | callees deployed_tree_report_for_context, prepare_fleet_ssh_agent_context | -| `dag/gunbc/machine_intake/boot_image_fetch.dag` | `fetch_verified_boot_image` | `net` | `std.resources.Network` | callees run_shell_command, verify_and_publish_boot_image | -| `dag/gunbc/machine_intake/boot_image_fetch.dag` | `verify_and_publish_boot_image` | `net` | `std.resources.Network` | callees admit_then_publish_boot_image | -| `dag/gunbc/machine_intake/boot_image_fetch.dag` | `admit_then_publish_boot_image` | `net` | `std.resources.Network` | callees publish_verified_boot_image | -| `dag/gunbc/machine_intake/boot_image_fetch.dag` | `publish_verified_boot_image` | `net` | `std.resources.Network` | local-ops shell.Move.File; callees confirm_published_boot_image | -| `dag/gunbc/machine_intake/mtcollins1_actuate.dag` | `mtcollins1_drive_handoff` | `net` | `std.resources.Network` | callees oob_boot_handoff | -| `dag/gunbc/machine_intake/mtcollins1_actuate.dag` | `mtcollins1_netboot_handoff` | `net` | `std.resources.Network` | callees mtcollins1_drive_handoff | -| `dag/gunbc/machine_intake/mtcollins1_actuate.dag` | `mtcollins1_cdrom_handoff` | `net` | `std.resources.Network` | callees mtcollins1_drive_handoff | -| `dag/gunbc/machine_intake/mtcollins1_actuate.dag` | `mtcollins1_denied_admission_control` | `net` | `std.resources.Network` | callees mtcollins1_drive_handoff | -| `dag/gunbc/machine_intake/mtcollins1_boot_image_fetch.dag` | `mtcollins1_fetch_boot_image` | `net` | `std.resources.Network` | callees fetch_verified_boot_image | -| `dag/gunbc/machine_intake/mtcollins1_boot_image_fetch.dag` | `mtcollins1_fetch_boot_image_wrong_pin_control` | `net` | `std.resources.Network` | callees fetch_verified_boot_image | -| `dag/gunbc/machine_intake/mtcollins1_boot_image_fetch.dag` | `mtcollins1_fetch_boot_image_unowned_export_control` | `net` | `std.resources.Network` | callees fetch_verified_boot_image | -| `dag/gunbc/machine_intake/mtcollins1_media_attach.dag` | `mtcollins1_attach_absent_image_control` | `net` | `std.resources.Network` | callees megarac_attach_remote_image | -| `dag/gunbc/machine_intake/mtcollins1_media_attach.dag` | `mtcollins1_attach_diskless_image` | `net` | `std.resources.Network` | callees megarac_attach_remote_image | -| `dag/gunbc/product/printed_chassis/slicing_toolchain.dag` | `attempt_orca_execution` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/roadmap/roadmap_launch_deployment_cli.dag` | `persist_receipt` | `net` | `Network` | ops Filesystem.Write, Filesystem.Write, Filesystem.WriteOwnerOnly; callees run_shell_commands; other-demand Filesystem | -| `dag/gunbc/roadmap/roadmap_launch_deployment_cli.dag` | `rlm_launch_deployment_receipt_wet` | `net` | `Network` | callees rlm_launch_deployment_receipt_bound, prepare_fleet_ssh_agent_context | -| `dag/gunbc/roadmap/roadmap_launch_deployment_cli.dag` | `rlm_run_provenance_fact` | `net` | `Network` | callees rlm_predecessor_run | -| `dag/gunbc/roadmap/roadmap_launch_deployment_cli.dag` | `rlm_launch_deployment_receipt_bound` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at_or_unknown; callees window_snapshot, observe_host_short_wet, rlm_run_provenance_fact, persist_receipt, unit_standing_fact; other-demand Clock,Filesystem | -| `dag/gunbc/runner/runner_guest_image.dag` | `runner_guest_image_observe_wet` | `net` | `Network` | callees run_shell_commands | -| `dag/gunbc/runner/runner_guest_image.dag` | `runner_guest_image_converge_wet` | `net` | `Network` | callees run_shell_commands | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `observe_runner_host_file` | `net` | `Network` | callees observe_runner_host_file_content, observe_runner_host_paths | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `apply_runner_host_file` | `net` | `Network` | callees observe_runner_host_paths | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `plan_runner_host_file` | `net` | `Network` | callees observe_runner_host_file | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `runner_host_manager_reload_step` | `net` | `Network` | callees reload_runner_host_manager | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `run_runner_host_files` | `net` | `Network` | callees apply_runner_host_file, plan_runner_host_file, observe_runner_slot_population, runner_host_manager_reload_step, observe_runner_host_administrator_privilege | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `runner_host_file_ci_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees prepare_fleet_ssh_agent_context, run_runner_host_files; other-demand Clock,Filesystem | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `runner_host_file_observe_ci_wet` | `net` | `Network` | callees runner_host_file_ci_wet | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `runner_host_file_converge_ci_wet` | `net` | `Network` | callees runner_host_file_ci_wet | -| `dag/gunbc/runner/runner_host_kernel_config.dag` | `observe_built_kernel_config` | `net` | `Network` | callees run_shell_command_observe | -| `dag/gunbc/runner/runner_host_kernel_config.dag` | `verify_built_kernel_config` | `net` | `Network` | callees observe_built_kernel_config | -| `dag/gunbc/runner/runner_host_kernel_config.dag` | `verify_runner_host_kernel_config` | `net` | `Network` | callees verify_built_kernel_config | -| `dag/gunbc/runner/runner_microvm_boot_probe.dag` | `runner_microvm_boot_probe_wet` | `net` | `Network` | ops Filesystem.Write; callees run_shell_commands, run_shell_command_observe, observe_firecracker_host_standing_wet; other-demand Filesystem | -| `dag/gunbc/runner/runner_microvm_host_ready.dag` | `observe_firecracker_host_standing_wet` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/runner/runner_microvm_host_ready.dag` | `runner_microvm_host_observe_wet` | `net` | `Network` | ops Filesystem.Write; callees run_shell_commands, observe_firecracker_host_standing_wet; other-demand Filesystem | -| `dag/gunbc/runner/runner_microvm_host_ready.dag` | `runner_microvm_host_converge_wet` | `net` | `Network` | ops Filesystem.Write; callees run_shell_commands, observe_firecracker_host_standing_wet; other-demand Filesystem | -| `dag/gunbc/runner/runner_observed_version_check.dag` | `observe_captured_console` | `net` | `Network` | callees run_shell_command_observe | -| `dag/gunbc/runner/runner_observed_version_check.dag` | `check_captured_console` | `net` | `Network` | callees observe_captured_console | -| `dag/gunbc/runner/runner_observed_version_check.dag` | `verify_captured_console` | `net` | `Network` | callees check_captured_console | -| `dag/gunbc/runner/runner_observed_version_check.dag` | `verify_bound_attempt_runner_version` | `net` | `Network` | callees verify_captured_console | -| `dag/gunbc/runner/runner_observed_version_check.dag` | `verify_two_attempt_runner_version` | `net` | `Network` | callees verify_captured_console | -| `dag/gunbc/runner/runner_observed_version_check.dag` | `verify_jit_absent_capture_lacks_runner_version` | `net` | `Network` | callees verify_captured_console | -| `dag/gunbc/runner/runner_password_session_tool_converge.dag` | `runner_password_session_tool_converge_ci_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees prepare_fleet_ssh_agent_context; other-demand Clock,Filesystem | -| `dag/gunbc/runner/runner_slot_provision.dag` | `observe_runner_slot_members_wet` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/spark/credential_workflow.dag` | `observe_spark_credential_consumer_evidence` | `net` | `Network` | callees fetch_secret_ref_credential | -| `dag/gunbc/spark/credential_workflow.dag` | `run_with_materialized_fleet_ssh_binding` | `net` | `Network` | callees fetch_secret_ref_credential, verify_fleet_ssh_credential | -| `dag/gunbc/spark/credential_workflow.dag` | `resolve_spark_administrator_credential` | `net` | `Network` | callees fetch_secret_ref_credential | -| `dag/gunbc/spark/fabric_rail_apply.dag` | `fabric_rail_apply_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees materialize_fleet_known_hosts_anchor; other-demand Clock,Filesystem | -| `dag/gunbc/spark/fabric_reach.dag` | `observe_executor_reach` | `net` | `Network` | callees run_shell_command_capture | -| `dag/gunbc/spark/glm_canary_converge.dag` | `glm_canary_cutover_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees materialize_fleet_known_hosts_anchor, observe_executor_reach; other-demand Clock,Filesystem | -| `dag/gunbc/spark/glm_canary_converge.dag` | `glm_canary_converge_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees materialize_fleet_known_hosts_anchor, observe_executor_reach; other-demand Clock,Filesystem | -| `dag/gunbc/spark/glm_group_b_relaunch_cli.dag` | `rank_startup_facet` | `net` | `Network` | callees observe_rank_vllm_revision | -| `dag/gunbc/spark/glm_group_b_relaunch_cli.dag` | `observe_rank_report` | `net` | `Network` | callees observe_rank_container, rank_startup_facet, capture_rank_log | -| `dag/gunbc/spark/glm_group_b_relaunch_cli.dag` | `glm_group_b_observe_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees with_group_b_access, observe_rank_report; other-demand Clock,Filesystem | -| `dag/gunbc/spark/glm_group_b_relaunch_cli.dag` | `glm_group_b_relaunch_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees relaunch_arm, with_group_b_access; other-demand Clock,Filesystem | -| `dag/gunbc/spark/glm_group_b_relaunch_cli.dag` | `with_group_b_access` | `net` | `Network` | callees materialize_fleet_known_hosts_anchor, observe_executor_reach | -| `dag/gunbc/spark/managed_access_apply.dag` | `spark_managed_access_apply_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees resolve_spark_administrator_credential, run_with_materialized_fleet_ssh_binding; other-demand Clock,Filesystem | -| `dag/gunbc/spark/managed_access_apply.dag` | `spark_managed_access_bootstrap_ci_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees prepare_fleet_ssh_agent_context; other-demand Clock,Filesystem | -| `dag/gunbc/spark/managed_grant_install.dag` | `spark_grant_install_execute` | `net` | `Network` | callees run_remote_step_sequence, spark_grant_read_back, run_privileged_step | -| `dag/gunbc/spark/managed_grant_install.dag` | `spark_grant_read_back` | `net` | `Network` | callees spark_grant_probe_outcome | -| `dag/gunbc/spark/managed_grant_install.dag` | `spark_grant_install_host` | `net` | `Network` | callees spark_grant_probe_outcome, spark_grant_install_execute | -| `dag/gunbc/spark/managed_grant_install.dag` | `spark_grant_install_ci_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees prepare_fleet_ssh_agent_context, spark_grant_install_host; other-demand Clock,Filesystem | -| `dag/gunbc/spark/pair_serving_apply.dag` | `spark_pair_serving_apply_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees observe_executor_reach, materialize_fleet_known_hosts_anchor; other-demand Clock,Filesystem | -| `dag/gunbc/spark/pinned_base_env_classification.dag` | `sparkrun_pinned_base_env_read` | `net` | `Network` | callees read_docker_hub_image_config_at_digest | -| `dag/gunbc/spark/pinned_base_env_classification.dag` | `sparkrun_pinned_base_env_classification_wet` | `net` | `Network` | callees sparkrun_pinned_base_env_read | -| `dag/gunbc/spark/remote_step_sequence.dag` | `remote_step_sequence_step` | `net` | `Network` | callees run_remote_operation | -| `dag/gunbc/spark/remote_step_sequence.dag` | `run_remote_step_sequence` | `net` | `Network` | callees remote_step_sequence_step | -| `dag/gunbc/spark/secret_access_ensure.dag` | `spark_secret_access_ensure` | `net` | `Network` | callees secret_access_ensure_for | -| `dag/gunbc/spark/secret_access_ensure.dag` | `spark_secret_access_converge` | `net` | `Network` | callees spark_secret_access_ensure | -| `dag/gunbc/spark/secret_access_ensure.dag` | `spark_secret_access_converge_with_supplied_token` | `net` | `Network` | callees spark_secret_access_ensure | -| `dag/gunbc/spark/serving_rank_observe.dag` | `observe_container_presence` | `net` | `Network` | callees rank_leg | -| `dag/gunbc/spark/serving_rank_observe.dag` | `observe_rank_container` | `net` | `Network` | callees observe_container_presence | -| `dag/gunbc/spark/serving_rank_observe.dag` | `capture_rank_log` | `net` | `Network` | callees rank_leg | -| `dag/gunbc/spark/serving_rank_observe.dag` | `observe_rank_transport` | `net` | `Network` | callees capture_rank_log | -| `dag/gunbc/spark/serving_rank_observe.dag` | `observe_rank_vllm_revision` | `net` | `Network` | callees rank_leg | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `rollback_restart_incumbent` | `net` | `Network` | callees relaunch_leg | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `capture_rank` | `net` | `Network` | callees observe_rank_container, preserved_name_is_free, observe_rank_transport | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `preserved_name_is_free` | `net` | `Network` | callees observe_container_presence | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `capture_arm` | `net` | `Network` | callees capture_rank | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `advance_rank` | `net` | `Network` | callees relaunch_leg | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `stop_and_preserve_arm` | `net` | `Network` | callees advance_rank | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `create_and_start_arm` | `net` | `Network` | callees advance_rank | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `rollback_restore_names` | `net` | `Network` | callees rollback_restore_one | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `rollback_restore_one` | `net` | `Network` | callees rollback_rename_back, rollback_after_observing | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `rollback_after_observing` | `net` | `Network` | callees rollback_rename_back, observe_container_presence | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `rollback_rename_back` | `net` | `Network` | callees relaunch_leg | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `rollback_start_restored` | `net` | `Network` | callees rollback_start_one | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `rollback_start_one` | `net` | `Network` | callees rollback_restart_incumbent | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `rollback_arm` | `net` | `Network` | callees rollback_start_restored, rollback_restore_names | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `await_rank_announcement` | `net` | `Network` | callees observe_rank_container, observe_rank_transport, await_after_interval | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `await_after_interval` | `net` | `Network` | callees await_rank_announcement | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `verify_arm` | `net` | `Network` | callees await_rank_announcement | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `relaunch_arm` | `net` | `Network` | callees capture_arm, rollback_arm, stop_and_preserve_arm, create_and_start_arm, verify_arm | -| `dag/gunbc/spark/v41_runtime_image_probe.dag` | `v41_published_image_probe_ci_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees materialize_fleet_known_hosts_anchor, observe_executor_reach, v41_probe_published_image; other-demand Clock,Filesystem | -| `dag/gunbc/spark/v41_source_patch_converge.dag` | `v41_observe_member` | `net` | `Network` | callees v41_check_applicable, v41_place_declared_patch | -| `dag/gunbc/spark/v41_source_patch_converge.dag` | `v41_observe_members` | `net` | `Network` | callees v41_observe_member | -| `dag/gunbc/spark/v41_source_patch_converge.dag` | `v41_observe_tree` | `net` | `Network` | callees v41_observe_members | -| `dag/gunbc/spark/v41_source_patch_converge.dag` | `converge_vllm_source_with_patches` | `net` | `Network` | callees v41_observe_tree, converge_vllm_source_to_revision, v41_apply_plan | -| `dag/gunbc/spark/v41_source_patch_converge.dag` | `converge_vllm_runtime_image_with_patches` | `net` | `Network` | callees converge_vllm_image_keyed, converge_vllm_source_with_patches | -| `dag/gunbc/spark/v41_source_patch_converge_wet.dag` | `v41_patched_source_acquire_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees converge_vllm_source_with_patches, probe_agent_credential_verification, materialize_fleet_known_hosts_anchor; other-demand Clock,Filesystem | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `vllm_source_after_revision` | `net` | `Network` | callees vllm_source_tree_clean | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `converge_vllm_source_to_revision` | `net` | `Network` | callees vllm_source_after_revision | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `converge_vllm_source` | `net` | `Network` | callees converge_vllm_source_to_revision | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `converge_vllm_image` | `net` | `Network` | callees converge_vllm_image_keyed | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `converge_vllm_image_keyed` | `net` | `Network` | callees vllm_docker_leg | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `converge_vllm_runtime_image` | `net` | `Network` | callees converge_vllm_image, converge_vllm_source | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `vllm_runtime_image_build_ci_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees observe_executor_reach, materialize_fleet_known_hosts_anchor, converge_vllm_runtime_image, probe_agent_credential_verification; other-demand Clock,Filesystem | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `vllm_source_acquire_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees converge_vllm_source, probe_agent_credential_verification, materialize_fleet_known_hosts_anchor; other-demand Clock,Filesystem | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `spark_build_host_reachability_wet` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; callees materialize_fleet_known_hosts_anchor, probe_spark_host_reachability, probe_agent_credential_verification; other-demand Clock,Filesystem | -| `dag/gunbc/srv3/srv3_bmc_credential_resolve.dag` | `materialize_bmc_login_password` | `net` | `Network` | callees fetch_secret_ref_credential | -| `dag/gunbc/srv3/srv3_bmc_credential_resolve.dag` | `observe_and_resolve_srv3_bmc_credential` | `net` | `Network` | callees srv3_probe_credential_phase | -| `dag/gunbc/srv3/srv3_boot_once_cd.dag` | `srv3_boot_once_cd` | `clock` | `std.resources.Clock` | callees srv3_boot_once_cd_gated | -| `dag/gunbc/srv3/srv3_boot_once_cd.dag` | `srv3_boot_once_cd` | `net` | `std.resources.Network` | callees srv3_boot_once_cd_gated | -| `dag/gunbc/srv3/srv3_boot_once_cd.dag` | `srv3_boot_once_cd_operator_approved` | `clock` | `std.resources.Clock` | callees srv3_boot_once_cd_gated | -| `dag/gunbc/srv3/srv3_boot_once_cd.dag` | `srv3_boot_once_cd_operator_approved` | `net` | `std.resources.Network` | callees srv3_boot_once_cd_gated | -| `dag/gunbc/srv3/srv3_boot_once_cd.dag` | `srv3_boot_once_cd_gated` | `net` | `std.resources.Network` | ops clock_now_probed_at; callees srv3_boot_once_cd_resolved, observe_and_resolve_srv3_bmc_credential; other-demand Clock | -| `dag/gunbc/srv3/srv3_os_install_actuate.dag` | `srv3_bmcweb_session_login` | `net` | `std.resources.Network` | callees srv3_bmcweb_session_login_resolved, observe_and_resolve_srv3_bmc_credential | -| `dag/gunbc/srv3/srv3_os_install_actuate.dag` | `srv3_bmcweb_session_login_resolved` | `net` | `std.resources.Network` | ops Filesystem.Write; callees materialize_bmc_login_password; other-demand Filesystem | -| `dag/gunbc/srv3/srv3_os_install_actuator_toolchain_ensure.dag` | `srv3_os_install_actuator_toolchain_ensure` | `net` | `Network` | callees host_toolchain_ensure | -| `dag/gunbc/tailscale_acl_phase2_credential.dag` | `tailscale_acl_operator_ephemeral_credential` | `net` | `std.resources.Network` | callees tailscale_acl_credential_with_operator_token | -| `dag/gunbc/tailscale_acl_phase2_credential.dag` | `tailscale_acl_credential_with_operator_token` | `net` | `std.resources.Network` | callees gcp_secret_credential | -| `dag/gunbc/tools/bmc_onboard.dag` | `srv3_probe_credential_phase` | `net` | `Network` | callees bmc_probe_credential_phase | -| `dag/gunbc/tools/bmc_onboard.dag` | `srv3_converge_credential` | `net` | `Network` | callees bmc_converge_credential | -| `dag/gunbc/tools/bmc_onboard.dag` | `srv4_converge_credential` | `net` | `Network` | callees bmc_converge_credential | -| `dag/test/ctl/pos_emit.dag` | `forwards_to_a_service_call` | `net` | `std.resources.Network` | callees emits_a_service_call | - -## UnusedRequirement - -(empty in the live parsed population) - -## Unresolved - -| File | Declaration | Alias | Authored type | Evidence | -| --- | --- | --- | --- | --- | -| `dag/gunbc/container/registry_image_config.dag` | `read_docker_hub_image_config_at_digest` | `net` | `Network` | ops Filesystem.WriteOwnerOnly; local-ops shell.Mktemp.Dir, shell.Remove.RecursiveForce, shell.Remove.RecursiveForce, shell.Remove.RecursiveForce; other-demand Filesystem; calls check_sha256_hex, container_image_config_env_from_json, container_image_config_from_env, digest_hex_from_sha256_wire, docker_hub_bearer_token, docker_hub_blob_url, docker_hub_manifest_url, manifest_config_digest_wire | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_generation_store_wet` | `net` | `Network` | ops Filesystem.Read; other-demand Filesystem; calls fleet_converge_generation_admission, fleet_converge_generation_observation, fleet_converge_generation_store_path | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_cap_members_wet` | `net` | `Network` | ops Filesystem.List, Filesystem.Read; other-demand Filesystem; calls FilesystemFileAbsent, cap_members_observation, filesystem_file_observation, filesystem_listing_observation, filesystem_read_outcome | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_runner_unit_standing_wet` | `net` | `Network` | calls read_unit_property | -| `dag/gunbc/fleet/fleet_converge_plan_cli.dag` | `observe_operator_local_run_binding` | `net` | `Network` | ops clock_now_probed_at; local-ops git.Inspect.HeadCommit; other-demand Clock; calls clock_now_probed_at, release_revision_text_valid | -| `dag/gunbc/fleet/fleet_host_key_enrollment.dag` | `fleet_host_key_scan_wet` | `net` | `Network` | ops Filesystem.Write; other-demand Filesystem; calls scan_host_key_lines | -| `dag/gunbc/fleet/fleet_known_hosts_anchor.dag` | `materialize_fleet_known_hosts_anchor` | `net` | `Network` | ops Filesystem.Read, Filesystem.Write, Filesystem.Read; other-demand Filesystem; calls compare_content_hash, content_hash_of_value, fleet_known_hosts_anchor_path, fleet_known_hosts_fragment, fleet_known_hosts_registry_identity, fleet_known_hosts_rows, host_key_provenance_label, known_hosts_anchor_seal | -| `dag/gunbc/fleet/fleet_multi_principal_probe.dag` | `spark_managed_access_standing_wet` | `net` | `Network` | calls fleet_ssh_attempt_raw_from_env, spark_managed_access_standing_with_attempt | -| `dag/gunbc/fleet/fleet_multi_principal_probe.dag` | `spark_managed_access_standing_with_verified_credential` | `net` | `Network` | ops Filesystem.Write, clock_now_probed_at; other-demand Clock,Filesystem; calls agent_backed_principal_observation, clock_now_probed_at, derive_spark_managed_access_standing, probed_at_word, serialize_content_hash, spark_authorization_lines, spark_standing_line, spark_standing_unobserved_host | -| `dag/gunbc/fleet/fleet_ssh_credential_verify.dag` | `materialize_agent_public_selector` | `net` | `Network` | ops Filesystem.Read, Filesystem.Write, Filesystem.Read; other-demand Filesystem; calls agent_public_selector_content, agent_public_selector_path, agent_selector_file_seal, compare_content_hash, content_hash_of_value | -| `dag/gunbc/host/host_codex_runtime_provision.dag` | `materialize_codex_runtime_on_local_host` | `net` | `Network` | calls codex_runtime_materialization_refusal_reason, committed_admitted_codex_runtime_source, materialize_codex_runtime_bundle, provider_runtime_placement_for_host | -| `dag/gunbc/host/host_reset_return_run.dag` | `observer_self_corroboration` | `net` | `std.resources.Network` | local-ops os.Hostname.ReadShort; calls canonical_hostname_for_fleet_slot, observer_self_identity | -| `dag/gunbc/host/host_reset_return_run.dag` | `host_reset_return_preflight_exit` | `net` | `std.resources.Network` | ops Filesystem.Write; other-demand Filesystem; calls reset_preflight_refusal_wire | -| `dag/gunbc/host/host_reset_return_run.dag` | `host_reset_return_dispatch_admission_wet` | `net` | `std.resources.Network` | local-ops shell.Env.Get, shell.Env.Get; calls admit_reset_dispatch | -| `dag/gunbc/host/host_reset_return_run.dag` | `host_reset_return_converge_route_read` | `net` | `std.resources.Network` | calls actions_variable_read | -| `dag/gunbc/host/host_reset_return_run.dag` | `host_reset_return_exit` | `net` | `std.resources.Network` | ops Filesystem.Write; other-demand Filesystem; calls host_reset_return_receipt_line, reset_return_refusal_cause_wire | -| `dag/gunbc/host/host_runner_memory_provision.dag` | `provision_runner_memory_caps` | `net` | `Network` | calls fleet_compute_host_for_identity, host_effect_apply_gated, host_toolchain_direct_access, runner_memory_converge_effect | -| `dag/gunbc/host/host_toolchain_ensure.dag` | `host_toolchain_ensure` | `net` | `Network` | calls host_effect_apply_gated, host_toolchain_ensure_effect, host_toolchain_ensure_reach | -| `dag/gunbc/live_deploy/deployed_tree_report.dag` | `deployed_tree_report_for_context` | `net` | `Network` | calls converge_target_revision, deployed_tree_locus, deployed_tree_report_emit, deployed_tree_standing, fleet_locus_ssh_target, observe_deployed_tree, observe_fleet_desired_revision | -| `dag/gunbc/machine_intake/boot_image_fetch.dag` | `confirm_published_boot_image` | `net` | `std.resources.Network` | calls boot_image_digest_agrees, boot_image_observed_hex, sha256sum_file_digest_via_shell | -| `dag/gunbc/roadmap/roadmap_launch_deployment_cli.dag` | `window_snapshot` | `net` | `Network` | calls active_instance_text_routed, deployed_tree_hexes, deployed_tree_locus, fleet_locus_ssh_target, observe_deployed_tree, read_desired_hex, read_remote_main_hex, remote_ref_text | -| `dag/gunbc/roadmap/roadmap_launch_deployment_cli.dag` | `unit_standing_fact` | `net` | `Network` | calls deployment_belt_service_unit_name, deployment_belt_timer_unit_name, deployment_spec_srv1, live_deploy_belt_service_unit_file, live_deploy_belt_timer_unit_file, live_deploy_serve_unit_file, observe_belt_oneshot_standing, observe_belt_timer_standing | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `observe_runner_host_administrator_privilege` | `net` | `Network` | calls elevated_argv, typed_argv_exec_over_fleet_ssh | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `observe_runner_host_paths` | `net` | `Network` | calls classify_root_only_paths, runner_host_root_only_find_argv, typed_argv_exec_over_fleet_ssh | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `observe_runner_host_file_content` | `net` | `Network` | calls classify_host_file_presence, classify_typed_remote_file_comparison, runner_host_file_standing_of_report, runner_host_file_write_admission, typed_argv_exec_over_fleet_ssh, typed_remote_file_compare_argv, typed_remote_file_step | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `observe_runner_slot_population` | `net` | `Network` | calls classify_runner_slot_population, runner_slot_population_units, runner_slot_units_from_list_units, systemctl_list_all_service_units_argv, systemctl_show_properties_argv, typed_argv_exec_over_fleet_ssh | -| `dag/gunbc/runner/runner_host_file_converge.dag` | `reload_runner_host_manager` | `net` | `Network` | calls elevated_argv, systemctl_daemon_reload_argv, typed_argv_exec_over_fleet_ssh | -| `dag/gunbc/runner/runner_slot_population_census.dag` | `observe_slot_population_over_ssh` | `net` | `Network` | calls slot_census_from_command_outcome, systemctl_list_all_service_units_argv, typed_argv_exec_over_fleet_ssh | -| `dag/gunbc/runner/runner_slot_retirement.dag` | `observe_transition_retirement_evidence` | `net` | `Network` | calls observe_retirement_standings, transition_required_retiring | -| `dag/gunbc/spark/glm_canary_converge.dag` | `glm_canary_cutover` | `net` | `Network` | calls glm_canary_replace_container, glm_canary_run_argv | -| `dag/gunbc/spark/grant_privileged_operation.dag` | `run_privileged_step` | `net` | `Network` | calls admitted_root_argv_words, exec_as_fleet_principal_with_stdin, fleet_probe_endpoint_for, portable_remote_words, read_bootstrap_credential, sudo_rejected_the_credential | -| `dag/gunbc/spark/managed_grant_install.dag` | `spark_grant_probe_outcome` | `net` | `Network` | calls run_typed_argv_transport, spark_grant_install_transport | -| `dag/gunbc/spark/remote_step_sequence.dag` | `run_remote_operation` | `net` | `Network` | calls SshShell, converge_typed_remote_file, run_typed_argv_transport | -| `dag/gunbc/spark/serving_rank_observe.dag` | `rank_leg` | `net` | `Network` | calls argv_words, rank_observation_refusal, spark_remote_run | -| `dag/gunbc/spark/serving_relaunch_transaction.dag` | `relaunch_leg` | `net` | `Network` | calls argv_words, relaunch_step_wire, spark_remote_run | -| `dag/gunbc/spark/v41_runtime_image_probe.dag` | `v41_probe_published_image` | `net` | `Network` | calls docker_image_inspect_id_from_stdout, docker_image_inspect_names_no_such_image, image, spark_remote_run, v41_capability_reading, v41_privileged_outcome, v41_privileged_stdout, v41_probe_config_id_argv | -| `dag/gunbc/spark/v41_source_patch_converge.dag` | `v41_place_declared_patch` | `net` | `Network` | ops Filesystem.Read; other-demand Filesystem; calls converge_typed_remote_file, filesystem_read_outcome, typed_remote_file_write_seal, v41_remote_declared_patch_path, vllm_build_root | -| `dag/gunbc/spark/v41_source_patch_converge.dag` | `v41_check_applicable` | `net` | `Network` | calls git_apply_check_command, git_apply_reverse_check_command, v41_remote_declared_patch_path, vllm_image_leg_unchecked, vllm_source_dir | -| `dag/gunbc/spark/v41_source_patch_converge.dag` | `v41_apply_plan` | `net` | `Network` | calls git_apply_command, v41_remote_declared_patch_path, vllm_image_leg, vllm_source_dir | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `vllm_docker_leg` | `net` | `Network` | calls argv_words, spark_remote_run | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `vllm_source_tree_clean` | `net` | `Network` | calls git_status_porcelain_command, vllm_image_leg, vllm_source_dir, vllm_source_is_clean | -| `dag/gunbc/spark/vllm_runtime_image_build.dag` | `probe_spark_host_reachability` | `net` | `Network` | calls classify_reported_hostname, hostname_short_read_command, spark_reach_endpoint, vllm_image_leg | -| `dag/gunbc/srv3/srv3_install_media_fetch.dag` | `srv3_install_media_fetch` | `net` | `Network` | calls srv3_apply_to_process_exit, srv3_install_media_fetch_apply | -| `dag/gunbc/srv3/srv3_os_install_actuate.dag` | `srv3_nbd_proxy_serve` | `net` | `std.resources.Network` | calls srv3_apply_to_process_exit, srv3_nbd_proxy_serve_apply | -| `dag/gunbc/srv3/srv3_seeded_install_media.dag` | `srv3_seeded_install_media_toolchain_ensure` | `net` | `std.resources.Network` | calls srv3_apply_to_process_exit, srv3_seeded_install_media_toolchain_ensure_apply | -| `dag/gunbc/srv3/srv3_seeded_install_media.dag` | `srv3_seeded_install_media_remaster` | `net` | `std.resources.Network` | calls srv3_apply_to_process_exit, srv3_seeded_install_media_remaster_apply, srv3_seeded_install_media_toolchain_ensure_apply | - -## NamedDependencyBinder - -(empty in the live parsed population) - -## OpaqueContract - -(empty in the live parsed population) - -## PolicyEnvelope - -(empty in the live parsed population) - -## Fixture-string population (not live declarations) - -Witness modules embed programs as `data *_source: String`. Those `uses` rows are specimens, not production headers. - -| Host | Embedded decl data | Alias | Type | Class in the specimen | -| --- | --- | --- | --- | --- | -| `dag/test/claim/data_class_refusal_probe_witness_test.dag` | `durable_write_literal_control_source` | `net` | `Network` | `UnusedRequirement` | -| `dag/test/claim/effectful_item_kind_collapse_witness_test.dag` | `efr_control_source` | `net` | `EfrNet` | `NamedDependencyBinder` | -| `dag/test/claim/effectful_item_kind_collapse_witness_test.dag` | `efr_non_tail_source` | `net` | `EfrNet` | `NamedDependencyBinder` | -| `dag/test/claim/effectful_item_kind_collapse_witness_test.dag` | `efr_tail_source` | `net` | `EfrNet` | `NamedDependencyBinder` | -| `dag/test/claim/effectful_item_kind_collapse_witness_test.dag` | `zpe_reads_resource_source` | `net` | `ZpeNet` | `NamedDependencyBinder` | -| `dag/test/claim/entry_authority_witness_test.dag` | `ea_one_entry_source` | `net` | `EaNet` | `NamedDependencyBinder` | -| `dag/test/claim/entry_authority_witness_test.dag` | `ea_uses_library_source` | `net` | `EaNet` | `NamedDependencyBinder` | -| `dag/test/claim/generic_effectful_declaration_wall_witness_test.dag` | `gew_generic_effectful_source` | `net` | `GewNet` | `UnusedRequirement` | -| `dag/test/claim/generic_effectful_declaration_wall_witness_test.dag` | `gew_plain_effectful_source` | `net` | `GewNet2` | `NamedDependencyBinder` | - -## Inverse (derived demand without an authored `uses` on that declaration) - -Not a `uses` occurrence, so not a USES-0 class member. Named because D13 derives demand from operations: `gunbc.clock_read` `clock_now_probed_at` calls `Clock.Now()` with no header contract. Callers then restate `uses clock` transitively. That is the derive-once-carry gap: the Clock demand is produced at `Clock.Now` and should be carried, not re-authored up the chain. - -HOLD: no `uses` row added or deleted by this census. - diff --git a/docs/plans/v2-self-host-direct-path-2026-09-06.md b/docs/plans/v2-self-host-direct-path-2026-09-06.md deleted file mode 100644 index 51e680ba943..00000000000 --- a/docs/plans/v2-self-host-direct-path-2026-09-06.md +++ /dev/null @@ -1,67 +0,0 @@ -# The direct path to a clean v2 self-host — orientation, sequencing, autonomy (2026-09-06) - -**What this file is.** An orientation and sequencing read, written at one point in time, plus the autonomy contract under which an agent executes it. It is **not** a work-item roster and not a second authority: every fact about what remains lives in the carriers named below and is re-derived by running the instruments named below, never by reading a number off this page (DESIGN §6 — name the instrument, never transcribe its output). When this file and a carrier disagree, the carrier is right and this file is stale. - -**The authorities this file defers to:** - -- DESIGN §7 — the goal itself, and the honesty boundary around it. -- [v2-self-hosting.md](v2-self-hosting.md) — the operator-signed (2026-07-11) Weak → Strong Self Host program: four waves, dependency-gated exits, `src/v1` deleted only at the end of Wave 4. That is the plan from here to there; this file does not replace it. -- `dag/gunbc/roadmap/roadmap_authority.dag` — the node chain with per-node boundary, first slice, RED control, exclusions, handback. Projected to `ROADMAP.md`; every node requires explicit manual acceptance. -- The three self-host frontier carriers: `v2.compiler.self_host.seed_retention_frontier`, `v2.compiler.self_host.emit_coverage_frontier`, `v2.compiler.self_host.native_routing_frontier`. -- `dag/gunbc/guarantee_stall/` — the typed stall roster; `self_host_candidate_generation_add_slice_stall` is the grounding-frontier row. -- gunbc#9664 ("XL-N") — the native-closure burn-down program with go/no-go gates. - -## 1. What you actually get at the end - -In daily terms: **you edit `.dag` files, and the compiler that compiles them was itself compiled from `.dag` files.** The hand-written Rust seed is gone except a pinned, content-addressed bootstrap kernel (the wave doc sizes it at roughly 8–15k LOC); everything else the compiler does — reading source, resolving names, deriving types, translating, emitting — is emitted from the graph it compiles. - -The proof shape, per the roadmap nodes' own RED controls: - -- The generator builds itself **twice**; the second build is shown never to have reached the old compiler — run with the old one taken away, or with a receipt proving it was never called. Behaving the same is not accepted as proof of independence. -- Every self-emitted module passes the same behavioral corpus as the seed it replaced, including the deliberately-broken controls that must still fail. Byte-identity with the seed is explicitly **not** the goal (operator, 2026-07-08) — the seed's warts are not reproduced. -- Required CI runs `v2.test.*` through the native emitted crate; the interpreter survives only for `src/v1` regen until that too is gone. - -The payoff is DESIGN §7's: **language design opens up.** A new guarantee is a row, not a compiler fork; and because the substrate reads one grammar in both directions over many media, that row applies on top of an existing language through ingest — no adoption problem. This is the economic point of the whole exercise: today every improvement routes through the generator we are trying to delete. - -What you do **not** get (§7's honesty boundary): the compiler does not invent unstated intent, does not prove unmodeled external reality, and does not lift arbitrary predicates to proof. Runtime mechanisms — typed refusal, totality, rollback, budgets — remain real at honest boundaries. - -## 2. Where "here" is — by instrument - -Each line names the producer that re-derives the state. Run it; do not believe this sentence. - -- **Axis A — the real path, slice by slice.** The add slice (the `add` fn of `std.integer`) is green end-to-end on the dag path as of #10673: infer derives grounding by declared-inhabitant roster membership, and the ingest→compile round trip is byte-faithful. Instrument: `gunbc run --source-root dag --source-root src/v2 --entry src/v2/test/claim/execution/self_host_candidate_generation_stage_verdicts_test.dag --function add_slice_stage_verdicts_entry`. The **direct-rust-door** (roadmap `v2-emitter-direct-rust-door`) is **green by execution as of 2026-09-06**: the production walk reaches `ArtifactProduced`, all five leaf projections establish (source fidelity byte-equal to `direct_rust_door_expected_source`, producer identity, seed-emitter absence, `SourceProduced` qualification, missing-rule refusal), and the known-red admission row deleted per its own dissolution, promoting the group root to the lane's permanent regression wall. Instrument: `claim_batch --source-root dag --source-root src/v2 --entry src/v2/test/claim/long/direct_rust_door_production_group_test.dag --functions direct_rust_door_production_group_closing_expectation_holds` (a `long/` row — it reconstructs the pipeline per evaluation; the cost defect is declared in the test module's header). -- **The grounding frontier.** The door specimen's frontier is closed, and the parse-product family followed on 2026-09-07: the declared-inhabitant roster-membership derivation widened from the dag roster to the closed ingest set (dag, python, typescript), closing the python and typescript fixtures' frontiers and greening the four same-language round-trips. The add-slice stall's trigger fired and it retired per §4b(4). The remaining frontier is counted, not transcribed: `v2.workflow.compile_door_cause_ownership` `node_grounding_frontier_retirement` names its causes, and `gunbc.witness_v2_native_route` `native_route_frontier_retirement_standing` joins them to the route's observed population. -- **Axis B — the native closure (XL-N).** The XL-0 gate (`cargo check` = 0 on the emitted compiler closure) is **not satisfied**; the issue's own receipt table records that the closure reached complete rustc type checking for the first time at XL-0A and carries the honest error population there. gunbc#10266 (fabricated runtime panics on representation-identical casts) is filed as an independent compiler slice on this path. Instrument (the issue's own): `gunbc compile --source-root dag --source-root src/v2 --entry src/v2/compiler/00_compile.dag --target rust --output-dir /tmp/v2emit && cd /tmp/v2emit && cargo check --message-format short`. -- **Axis C — the seed dissolves.** `v2.compiler.self_host.seed_retention_frontier` holds every retained seed file as a declared row; `undeclared_reason_rows()` is the prioritizable debt, and the census refuses unrostered retention in three directions. `v2.compiler.self_host.emit_coverage_frontier`'s `interpreter_retained_rows()` and `v2.compiler.self_host.native_routing_frontier`'s roster cover the witness-execution axes. The first InterpreterRetained → SelfEmittedNative promotion after classical_not landed 2026-09-07: the add family's row flipped when `emit_host_native_only_add_holds` (+ its wrong-octet broken control) landed in the file-grain-enrolled native-only verdict entry — the emitted add crate's stdout pinned to its expected octet with `eval()` never called, program-side discrimination staying with the family's equals_eval primitive-five/six pair. **The emit coverage frontier closed 2026-09-08: all fifteen roster rows are SelfEmittedNative, `interpreter_retained_rows()` is empty**, each row backed by a native-only verdict arm (positive + wrong-octet broken control) in the same enrolled entry, and the `retained_via_eval_agreement` row constructor was deleted with the last flip (DESIGN §3c). This closes axis (a) — witness-body-runs-native — only; it says nothing about which engine compiled the compiler itself (axis (b), the regen-grain flip, remains operator-gated below). Instrument: `claim_batch --source-root dag --source-root src/v2 --entry src/v2/test/claim/execution/long/emit_host_native_only_verdict_test.dag` (offline wet legs — real cargo build and native run per leg). -- **Wave position.** Per the wave doc, Wave 1's four exit items all carry landed receipts; Wave 2 (first real flips — emit-surface tracks self-emit, behaviorally verify, and **replace** their v1 counterparts) is the open wave. The wave doc's header records that its roster carrier was deleted and per-module dispositions must be re-derived from the emitter — which is what the door-first sequence below does. - -## 3. The sequence from here - -Ordered by the roadmap chain; each step names its green condition as the node's own contract states it. - -1. **The door** (`v2-emitter-direct-rust-door`) — **LANDED 2026-09-06.** The loop's actual findings, for the record: not missing `TargetModel` rows. (a) The grounding frontier closed by six real derivation rules in `04_infer.dag` (see §2's frontier line). (b) Emission needed the right composition, not more rules: a resolved module is a module shell over named declarations, so the production path is `generate_rust_module_emission_candidate` — collect the produced-decl-shaped nodes, admit exactly one, emit through `v2.compiler.emit_produced` — while the raw whole-tree `generate_rust_emission_candidate` remains the fixture lane's composition with its own dissolution trigger. (c) One representation decode gap: resolution canonicalizes surface operators into canonical-operation wire nodes, so the translate value-expression projection decodes the operator position with `canonical_operation_from_wire_node` first and falls to the surface-token table only on a wire miss. Green condition met: the production observation reaches `ArtifactProduced`, all five leaf projections establish, the known-red expectation row deleted in the same change, and the group root promotes to permanent regression evidence (`v2_emitter_direct_rust_door_contract_dissolution_note`). -2. **The parse-product grounding family** (the stall's named trigger) — **LANDED 2026-09-07.** The roster-membership derivation generalized from the dag language roster to the closed ingest set (dag, python, typescript): `infer_node_declared_in_language_inhabitants` returns the declaring authority's roster root as evidence, with deep subtree membership so a declared inhabitant's leaf fact atoms derive exactly as the inhabitant node itself does. Measured: the python fixture's 19-node frontier and the typescript fixture's 28-node frontier both close to zero; all four stall-population round-trip witnesses green; the python→typescript cross-language compile accepts, byte-identical to `ts_source_text`. One §4b(4) flip: `cross_language_compile_refuses_canonical_underived_holds` → `cross_language_compile_python_to_typescript_round_trip_holds` (the pipeline-level frontier guard became the permanent regression control for the acceptance). The add-slice stall's trigger fired, so it retired per §4b(4) — removed from `all_guarantee_stalls`, row file deleted, witnesses stay enrolled. -3. **First behavioral module** (`v2-emitter-first-behavioral-module`). One already-tested module regenerated by the new generator; its existing tests — including the deliberately-broken one — pass unchanged, and the run is shown never to have reached the old generator. First-slice evidence landed 2026-09-07 at the coverage-frontier grain: the add family (fewest dependencies — integer literals plus one canonical operation) carries a native-only verdict witness, so its behavior is established by the emitted crate's own stdout with `eval()` unreachable, and its coverage row reads SelfEmittedNative. What remains for the node is the regen-grain flip — a corpus module's *production* switching generators — plus operator acceptance. -4. **The XL-N milestones, in the issue's order** — arms → 0; emitter defects C, B → 0; crate compiles; native = interpreted with every divergence typed; swap. This is Wave 4's "pipeline runs on emitted Rust" executed as a burn-down, and its rules bind: fixes land in `.dag` and reach Rust via regen, and each closed class gets a `test.claim` fixture under the required gate. Its no-go gates are real: M2 stops if a fix needs a fact the inferred tree doesn't carry (an `04_infer` modeling gap — file it, don't grind); M3 re-plans if a class needs a language capability we don't have. -5. **Native bootstrap, then fixed point** (`v2-emitter-native-bootstrap`, `v1-emitter-fixed-point`). The two-round self-build for the generator, then the same two-round test extended stage by stage to the rest of the compiler. Then `dag/gunbc/v1/v1_deletion_plan.dag` executes; the seed-retention roster drains as modules flip, and Wave 3's first obligation — a derived denominator for what the compiler consists of — is named in the wave doc. -6. **Parallel track, not a wave gate:** the import-deletion ladder (wave doc §3) — B3's migration alongside Waves 2–3, B4's grammar deletion at Wave 4. - -## 4. The autonomy contract - -An agent can execute, end to end and without supervision, the per-slice loop: run the instrument → diagnose from the typed diagnostics → land the `.dag` fix → flip each affected witness per §4b(4) with per-witness justification → narrow the roster/stall rows in the same change → commit, push, open the PR, and iterate to green CI. #10673 is the worked example of that loop. Declaring seed-retention reasons, landing derivation rules, and burning XL-N error classes are all inside it. - -The operator gates that remain, each with its repo citation: - -- **Merges.** The agent never merges; every slice pauses at a green PR for human review. -- **Roadmap node acceptance.** ROADMAP.md's header states every active row requires explicit manual acceptance; a green node is handed back, not self-accepted. -- **CI job roster growth.** Closed without operator sign-off (DESIGN *Building & checks*, 2026-09-04 ruling). XL-N's M5 swap changes what required CI runs on — it is an operator decision by construction. -- **Scaffold admission.** §5: approval is external to the diff; an author cannot self-approve a scaffold. -- **Declared rung drops.** §4b(3): the agent can author the full declaration (previous rung, temporary rung, reason, population, restoration trigger); landing it is review-gated. -- **Language-semantics rulings** that the tree cannot decide. The Atom-authority question is the template: surfaced de-jargoned, decided by the operator (2026-09-06: the namespacing rules answer it), recorded in the carrier. These are rare but real, and the agent's obligation is to stop and raise them rather than guess. - -## 5. What would make this plan wrong - -- An XL-N no-go gate firing (M2's modeling gap, M3's missing language capability) — re-plan at that gate, per the issue's own rule. -- ~~The door's emission diagnostics turning out to be an `04_infer` modeling gap rather than `TargetModel` rows~~ — discharged 2026-09-06: the door is green; the gaps were derivation rules, one emission composition, and one operator-representation decode, none of them a modeling wall. -- A Wave-2 flip surfacing behavioral divergence that re-sequences the module order — typed as emitter bug or interpreter bug either way, but the sequence absorbs it. -- This file aging. It is a position read, not a carrier; the instruments in §2 are the standing truth. diff --git a/docs/plans/wet-evidence-convergence-plan.md b/docs/plans/wet-evidence-convergence-plan.md deleted file mode 100644 index c99f9cb2928..00000000000 --- a/docs/plans/wet-evidence-convergence-plan.md +++ /dev/null @@ -1,123 +0,0 @@ -# Wet-evidence convergence: the anti-collapse count, taken before the extraction - -Two authorities answer "did the wet evidence hold" for one fold. `v2.workflow.local_repo_wet_terminal` -(landed via #9903) joins a co-resident execution's terminals against a scheduled roster; -`v2.workflow.floor_wet_route` (proposed in #9725) validates a transported receipt envelope against -a subject digest pair. `v2.workflow.floor_changed_witness` imports one of them and, under the union, -would import both — the meaning fork DESIGN §3 forbids. - -This document exists for one reason: **a careless unification silently merges typed refusals**, and -the tell is that the extracted join grows one generic "binding mismatch" cause where six real -remedies used to live. A detail string is not a typed cause and cannot be joined on. So the union of -both refusal vocabularies is counted HERE, BEFORE the extraction, and the integrated join must be -able to emit at least as many. - -## The count: 18 distinct causes, deduplicated by REMEDY - -Where the two modules already agree on a remedy the row is listed once, naming both spellings. - -| # | cause | local_repo_wet_terminal | floor_wet_route | -|---|---|---|---| -| 1 | scheduled identity has no terminal | `TerminalMissing` | inside `ReceiptRosterInexact { detail }` | -| 2 | two terminals claim one identity | `TerminalDuplicated { occurrences }` | Rust reader only, untyped in `.dag` | -| 3 | terminal for an identity nobody scheduled | `TerminalUnscheduled` | inside `ReceiptRosterInexact { detail }` | -| 4 | executed a different entry than scheduled | `TerminalForeignEntry { observed, required }` | inside `ReceiptContractMismatch { detail }` | -| 5 | executed a different function than scheduled | `TerminalForeignFunction { observed, required }` | inside `ReceiptContractMismatch { detail }` | -| 6 | observed verdict is not the expected one | `TerminalVerdictNotExpected` | `wet_receipt_unexpected_red_identities` | -| 7a | co-resident: ran against a different prepared subject | `TerminalForeignCandidate { observed, required }` | — (meaningless for this route) | -| 7b | transported: semantic subject digest differs | — | `ReceiptSubjectMismatch { axis, receipt, computed }` | -| 7c | transported: executor contract digest differs | — | `ReceiptExecutorSnapshotDifferent { receipt, computed }` | -| 8 | receipt aged past its staleness budget | — (meaningless for this route) | `ReceiptExpired { age_secs }` | -| 9 | schema version is not the one this floor reads | — | inside `ReceiptContractMismatch { detail }` | -| 10 | executed_at exceeds published_at | — | inside `ReceiptContractMismatch { detail }` | -| 11 | published_at exceeds tree commit beyond declared skew | — | inside `ReceiptContractMismatch { detail }` | -| 12 | outcome wire outside the closed vocabulary | — | Rust reader only, untyped in `.dag` | -| 13 | envelope unreadable | — | Rust reader only | -| 14 | projection missing or orphaned from its authority | — | Rust reader only | -| 15 | projection is not the canonical projection (hand edit) | — | Rust reader only | -| 16 | enrolled expected-red observed passing | — | `wet_receipt_now_passing_identities` | -| 17 | no subject verdict reached | — | `wet_receipt_no_verdict_identities` | -| 18 | completed past its line — a COST, not a defect | `LocalRepoWetCompletedOverBudget` | `wet_receipt_cost_debt_identities` | - -**Post-extraction obligation: the integrated join emits ≥ 18, and rows 7a/7b/7c stay three.** -Collapsing them into one "binding mismatch" is the exact failure this count exists to catch. - -## What the count found, which was not the expected direction - -The collapse is in **floor_wet_route**, not in main's module. Rows 1, 3, 4, 5, 9, 10 and 11 are -typed causes on one side and `detail: String` payloads on the other — seven remedies behind two -string-carrying arms. Rows 2, 12, 13, 14 and 15 exist only in the Rust reader and have no `.dag` -spelling at all. - -So the extraction WIDENS the transported route to main's grain. It does not narrow main's join to -meet it, and any version of this work that reduces main's seven causes has failed rather than -converged. - -## The vocabulary is not ours to mint: `floor_terminal_ledger` already owns it - -`v2.workflow.floor_terminal_ledger` already declares the raw attempt terminal -(`ClaimAttemptTerminal`), observed-verdict versus unreadable-observation (`ClaimObservation`), -expectation (`ClaimExpectation`), the row (`ClaimTerminalRow`), a binding (`LedgerBinding`), and -thirteen derived dispositions. Neither wet module imports it. `floor_changed_witness` — the -conflicted consumer — does. - -Its `claim_disposition` already maps a completed-past-limit terminal by expectation: -`ExpectedToHold → PassedOverBudget`, `ExpectedRed → KnownRedNowPassing`. That is row 18 of the count -above, derived independently in `local_repo_wet_terminal`, derived independently again in -`floor_wet_route`, and owned all along by a module none of the three imported. - -**Three independent arrivals at one distinction is the proven coincidence DESIGN §6 asks for before -unifying vocabulary** — a better argument for the shared terminal than any reasoning about elegance. -So the terminal half of this convergence is REUSE, not extraction, and minting a third terminal type -between the two would be the same fork wearing a new name. - -## What is genuinely new work - -`LedgerBinding` carries a repository snapshot and a prepared subject: the co-resident case. The -transported case additionally needs the executor-contract digest, because a receipt produced by a -different executor over the same tree is a different evidential fact. That is one typed widening of -an existing type — two arms feeding one validation — and not a new authority: - -- co-resident prepared-subject binding -- transported receipt-subject binding carrying semantic subject and executor contract - -Freshness and executor age are meaningless for an execution running inside the floor, so they do not -move onto the local route; the local route's prepared-subject equality is meaningless for a receipt -produced by another process, so it does not move onto the transported one. One interface and one -exact join, two execution realizations, two route-owned roster policies. - -## Scope boundary - -This is an extraction. It changes no behavior of `local_repo_wet_terminal`: its join, its seven -refusals, its deliberately single-armed expectation and its invocation wall come out intact and land -back on the same population. Anything found wrong in that module during the lift is NAMED here and -routed separately, never repaired under cover of a mechanical extraction. - -## What the migration step changed, recorded because the paragraph above no longer describes the head - -The scope boundary above is stated for the EXTRACTION, and it was true of it. The migration that -followed is a different step with a different boundary, and leaving only the earlier paragraph would -leave a reader with a sentence that reads as current and is not. - -At this head `local_repo_wet_terminal` no longer owns a join. Its verdict, expectation, scheduled, -terminal and join types and its seven join refusals are DELETED; the module retains its roster, the -requirement it derives, the co-resident executor and the invocation wall, and it reaches -`wet_evidence_validate` for everything else. `floor_changed_witness` carries one route-neutral -standing over `WetEvidenceValidation`. - -**All seven refusals remain emittable as typed causes**, six under their own names and -`LocalRepoWetTerminalForeignCandidate { identity, observed, required }` as -`WetBindingPreparedSubjectDiffers { identity, observed, required }` — the same three fields compared -by the same equality, because the local requirement sets `prepared_subject` to the candidate. None -became a detail string, which is the condition this plan set for the work to have succeeded rather -than reduced. - -**One declared widening.** `wet_disposition_is_agreement` treats `KnownRedHeld` as agreement, which -the deleted single-armed expectation could not express. No roster row constructs it, so no landed -behaviour moves; it is recorded here because a widening discovered later reads as a defect. - -**Two refusals had no asserting witness** when the migration reached them — -`WetTerminalForeignEntry` and `WetTerminalForeignFunction`, emittable by the shared validator and -covered by the local route's old acceptance matrix. A refusal an authority can construct and no test -discriminates can be deleted with nothing going red, so they are now asserted in -`v2.test.wet_evidence` with both cross-negatives. diff --git a/docs/plans/wet-witness-host-premise-census.md b/docs/plans/wet-witness-host-premise-census.md deleted file mode 100644 index ccc4ff8fc30..00000000000 --- a/docs/plans/wet-witness-host-premise-census.md +++ /dev/null @@ -1,90 +0,0 @@ -# Wet-witness host-premise census (2026-09-29) - -Scope: every module on the local-repo wet schedule (`v2.workflow.local_repo_wet_terminal` -`local_repo_wet_schedule`, 51 modules at this revision), classified per claim for **assumed host -state** under `gunbc.recurring_failure_mode` `wet_witness_keyed_to_the_runner_not_its_subject` and -`wet_witness_premise_read_from_the_runner_it_happens_to_land_on`. Hermetic claims reach no host -effect and are out of scope. This is a read of the sources at the revision this file landed with, -not an execution receipt; the dispositions below say which rows were executed by a fix PR. - -The runner pool: CI runners are fleet hosts. srv1 hosts several runner instances sharing one `/tmp` -and one network namespace, and declares a dashboard instance; srv3/srv4 do not. User managers, -memory headroom and installed tools vary. - -Severity: **RUNNER-KEYED** — the verdict flips by which runner dequeues the job. **LATENT** — the -premise holds on every current runner but is neither created nor supplied by the claim. -**CLEAN** — the premise is created by the claim (per-claim `shell.Mktemp`), supplied as data, or -both arms are asserted. - -## Runner-keyed - -| witness | claim(s) | assumed premise | disposition | -|---|---|---|---| -| `test.claim.spark.fabric_capacity_standing_wet_witness`, `test.claim.spark.spark_pair_serving_apply_wet_witness` | all | executor is off-fleet (no dashboard instance) | repaired: #12478 supplied the premise, #12532 removed the live-log reads | -| `test.claim.compute.manager_unaskable_wet_witness` | `a_host_without_a_user_manager_reaches_the_unavailable_arm_instead_of_aborting` | no user manager answers | repaired: #12456 asserts the route relation | -| `test.claim.approval_ntfy_access_readback_wet_witness_test` | store-root claim | approval store root absent on the runner | repaired: #12614 asserts unchanged-by-the-verb | -| `test.claim.mtcollins1_census_image_local_wet` | `a_failed_or_partial_or_disagreeing_workload_emits_no_token_by_real_execution`, `a_workload_larger_than_capacity_refuses_at_preflight_by_real_execution` | fixed `/tmp/gunbc-wl-counter`, `/tmp/gunbc-wl-dd-marker` writable by this runner user (shared `/tmp`, sticky bit) | open: #12467 (per-claim temp dir), covers both paths | -| `test.claim.spark.pair_serving_d0_front_door_real_execution` | `a_vacated_port_reads_as_no_response_by_real_execution`, `a_failed_connect_fences_under_an_active_head_unit_and_suspends_only_under_a_quiet_one_by_real_execution`, `a_listener_behind_a_failed_connect_is_read_on_the_host_and_fences_by_real_execution` | a just-vacated loopback port stays unbound; `/proc/net/tcp` shows no other runner's listener on it | **owned by crisp-lynx-364** — not touched here | -| `test.claim.fabric.fabric_storage_file_store_wet_witness` | `a_put_or_advance_by_a_principal_the_store_areas_do_not_admit_is_refused_by_the_real_file_store` | runner executes as a non-root uid (`shell.Chmod.RecursiveReadOnly` does not bind root) | #12658: uid→outcome relation plus an all-uid refusal claim | -| `test.claim.machine_intake.mtcollins1_kvm_observer_protocol_wet_witness` | `a_held_observer_is_admitted_and_its_triggered_still_is_hash_bound`, `a_busy_viewer_slot_refuses_the_observer_and_the_handoff`, `a_connection_lost_mid_boot_is_reported_and_no_still_is_claimed_after_it`, `an_unreadable_canvas_is_acquisition_failed_with_the_browsers_words`, `a_relative_observer_directory_is_resolved_once` | browser toolchain converged for the job user (`gunbc.runner_browser_toolchain` `runner_browser_toolchain_here_wet` reads Ready; observed false on srv1 where `runner_browser_toolchain_converge` had never run) | typed refusal: declared in `v2.workflow.local_repo_wet_terminal` `local_repo_wet_premise_roster`, read before invocation, refused as `WetTerminalHostPremiseUnmet` carrying the toolchain authority's words rather than `WetTerminalVerdictNotExpected`; claims unchanged and still refuse the lane. Restoration trigger: keen-pike-73's floor slot class makes toolchain convergence a floor-pool readiness property | - -## Latent — fixed - -| witness | claim(s) | assumed premise | disposition | -|---|---|---|---| -| `test.claim.compute.host_capacity_wet_witness` | `competing_reservations_cannot_over_reserve_and_a_release_returns_the_capacity`, `a_reused_reference_is_a_ledger_refusal_and_never_reported_as_a_full_pool`, `releasing_one_seat_twice_is_not_a_ledger_failure_and_charges_nothing` | runner has ≥2 GiB `MemAvailable` at that instant | #12652: reading supplied (`reserve_with_room`); real route kept by `the_real_reservation_route_admits_exactly_what_the_reading_backs` (relation to the reading) | -| `test.claim.fabric.fabric_storage_file_store_wet_witness` | `a_put_or_advance_by_a_principal_the_store_areas_do_not_admit_is_refused_by_the_real_file_store` | runner uid ≠ 0 | #12658: uid→outcome relation; refusal route on every runner via `a_put_or_advance_into_store_areas_that_refuse_every_principal_is_a_typed_fault_by_real_execution` | -| `test.claim.long.fleet_release_bins_key_witness` | positive/key claims reaching `fixture_env` | runner exports no `release_bins_credential_pattern` name; no ancestor of `$TMPDIR` holds `.cargo/config*` | #12703: fixture unsets those names, pins the temp root under `/tmp` | -| `test.claim.machine_intake.sol_hold_stdin_wet_witness` | `the_collector_stdin_supplies_zero_bytes_and_no_eof_when_executed`, `a_launch_that_cannot_publish_its_pid_stops_the_child_it_created` | launch + `timeout 3` probe (or the supervisor's exit record) lands inside a fixed 5 s | #12704: bounded poll for the awaited line (`read_until_contains`) | - -## Latent — recorded, not fixed (operator decision via deep-ferret-305, 2026-09-29) - -Each row holds on every current runner, so no row flips a verdict by assignment today. "Flips when" -names the runner condition that would make the claim red for a reason other than its subject. A row -here is picked up when that condition is observed or planned for a runner class. - -| witness | claim | assumed premise | why latent | flips when | -|---|---|---|---|---| -| `test.claim.commit_writer_heal_admission_real_execution` | `clean_staged_index_admits_by_real_execution`, `unmerged_index_with_no_markers_refuses_by_real_execution`, `unclaimed_text_blob_with_markers_refuses_red_control_by_real_execution`, `space_and_tab_named_paths_admit_by_real_execution` | the runner user's global/system git config and inherited `GIT_*` env do not act on a temp repo (`seed_witness_repository` sets only `user.email`/`user.name` locally) | no runner user carries such config today | a runner user gains `commit.gpgsign=true`, a `core.hooksPath` whose hooks refuse, an `init.templateDir` with hooks, or exports `GIT_DIR`/`GIT_INDEX_FILE`/`GIT_CONFIG_*` | -| `test.claim.contract_identity.required_ci_epoch_real_execution_witness` | `a_run_of_the_contract_at_another_epoch_refuses_naming_both_epochs_by_real_execution`, `a_run_of_the_contract_at_the_required_epoch_admits_by_real_execution`, `a_commit_that_is_not_in_the_repository_is_unreadable_not_absent_by_real_execution`, `a_readable_commit_without_the_workflow_is_path_absent_not_commit_unreadable_by_real_execution` | same git-config premise | same | same | -| `test.claim.devboot_text_blob_real_execution` | `a_lease_claim_is_stored_as_exactly_its_bytes_in_the_bare_store_by_real_execution`, `a_staged_entry_is_stored_as_exactly_its_bytes_in_the_worktree_repository_by_real_execution`, `a_path_routed_hash_under_a_clobbered_path_publishes_the_empty_blob_red_control_by_real_execution` | same git-config premise (no local identity is set either, so `user.*` must resolve from global config or not be needed) | same | same, or a runner user with no resolvable git identity if a path commits | -| `test.claim.generated_artifact_merge_driver_real_execution` | `divergent_generated_artifact_merge_refuses_by_real_execution`, `divergent_generated_artifact_merge_under_true_driver_silently_drops_theirs_red_control_by_real_execution`, `one_sided_generated_artifact_change_merges_clean_by_real_execution` | same git-config premise, plus no global `merge.*` driver or attribute file overriding the locally configured driver | same | same, or a global `core.attributesFile` / `merge..driver` naming the witnessed path | -| `test.claim.effect_plan_bash_materialize_real_execution_witness` | `effect_plan_bash_fail_fast_prevents_later_operation_execution`, `effect_plan_bash_if_branch_fail_fast_stops_the_branch_and_the_plan` | `/gunbc-wave-a-definitely-absent` does not exist | nothing creates it | anything on the runner creates that root-level path | -| `test.claim.effect_plan_bash_materialize_real_execution_witness` | `effect_plan_bash_metachar_and_newline_cannot_open_a_statement` | `/tmp/absent; printf PWN` and `/tmp/absent\nid -u` do not exist in shared `/tmp` | only a deliberate write creates them | another runner instance on a shared-`/tmp` host (srv1) leaves such a path behind | -| `test.claim.fabric.fabric_storage_file_store_wet_witness` | `a_declared_entry_mode_is_the_published_mode_of_objects_and_heads` | the runner's umask leaves the undeclared mode ≠ `0444` | runners use the default umask 022/002 | a runner runs with umask `0333` or stricter | -| `test.claim.approval_device_routes_wet_witness_test` | `a_bound_realization_passes_the_read_past_the_gate` | a read of the host-global approval store root answers (present, absent or unreadable) rather than aborting | the verdict accepts every answering state | a permission error on that root aborts the route instead of producing a typed body | -| `test.claim.self_host_logic_behavioral_witness` | `self_host_logic_behavioral_receipt_holds` | cargo/rustc present, crates registry reachable or warm, memory and disk for a release build | every runner has the toolchain and a warm registry | a runner without network and a cold `~/.cargo`, or below the build's memory | -| `test.claim.self_host_logic_seed_unavailable_witness` | `self_host_logic_seed_unavailable_receipt_holds` | same, plus `git` | same | same | -| `test.claim.runner.runner_browser_toolchain_real_execution` | `the_library_readback_runs_ldd_and_reads_a_resolved_binary_by_real_execution` | `ldd` present and `/bin/sh` dynamically linked | glibc runners | a musl/static-shell runner, or one without `ldd` | -| `test.claim.runner.runner_browser_toolchain_real_execution` | `a_verified_archive_installs_and_its_retained_bytes_read_back_verified_by_real_execution`, `a_sha256_mismatch_refuses_and_runs_no_install_by_real_execution`, `an_npm_integrity_mismatch_refuses_and_runs_no_install_by_real_execution`, `a_retained_archive_altered_after_install_reads_back_mismatched_by_real_execution` | `curl` on PATH | present everywhere | a runner without `curl` | -| `test.claim.spark.pair_serving_authority_log_real_execution` | `a_release_needs_the_host_observed_quiet_by_real_execution` | `python3` and `pgrep` on PATH | present everywhere | a runner without either | -| `test.claim.spark.spark_pair_head_unit_digest_witness`, `test.claim.spark.spark_serving_offer_route_witness_test` (the claims reaching `spark_pair_declared_head_digest`), `test.claim.spark.v41_row_store_encode_witness` `the_receipt_was_produced_by_the_rendered_program_the_mode_ships`, `test.claim.srv3_install_media_fetch_real_execution`, `test.claim.srv3_seeded_install_media_real_execution` `install_media_remaster_ensure_grub_cmdline_inserts_by_real_execution` | digest claims | `sha256sum` on PATH | coreutils everywhere | a runner without coreutils `sha256sum` | -| `test.claim.spark.v41_engram_runtime_witness` (`the_live_storage_backed_patches_are_digestfile_of_committed_bytes`, `overlay_magic_cites_the_row_store_authority`), `test.claim.spark.v41_source_patch_converge_witness` `digestfile_inhabits_the_committed_patch_population` | committed-patch reads | cwd is the repo root | the wet executor runs from the checkout root | the executor's cwd changes | - -| `test.claim.machine_intake.sol_hold_stdin_wet_witness` | the watch, establishment, notice, release and publication claims #12434 added (`an_end_written_before_exit_is_judged_on_the_final_bytes`, `a_collector_gone_with_the_envelope_open_is_channel_lost_and_frozen`, `an_authentic_client_exit_is_classified_by_its_own_words`, `the_watcher_publishes_a_collector_exit_without_the_boot_watch`, `an_adopted_collector_needs_a_receipt_naming_its_instance`, `the_capture_watch_stops_when_its_observer_dies_and_waits_while_it_lives`, `the_release_signals_only_the_recorded_instance`, `a_publication_failure_stops_even_a_term_ignoring_child`, and the claims reaching `observer_alive` / `owned_release`) | a launched probe publishes its pid or reaches its state within a fixed 1–7 s sleep | runners start a `sh` probe in well under a second | a runner loaded enough that spawn + publication exceeds the fixed sleep; remedy is the bounded `read_until_contains` poll #12704 added for the two stdin claims | -The spark/v41 rows are keyed to their subject (the committed tree) rather than to the runner; they -are listed for completeness. - -## Clean - -`approval_assertion_counter_wet_witness_test`, `approval_device_enrolment_code_wet_witness_test`, -`approval_ntfy_access_readback_wet_witness_test` (decode claim), `compute.attempt_lifecycle_wet_witness`, -`durable_cas_fabric_storage_real_execution`, `durable_cas_file_store_wet_witness`, -`durable_exclusive_hold_file_store_wet_witness`, `eval_model_probe`, -`fabric.fabric_control_plane_wet_witness`, `fabric.fabric_event_log_wet_witness`, -`fabric.fabric_partition_read_wet_witness` (these two leak their temp roots — hygiene, not verdict), -`host_cli_dependency_wet_witness_test` (both arms accepted — vacuous, not keyed), -`live_deploy.approval_broker_helper_readback_real_execution`, `materialization_store_local_wet_witness`, -`megarac_spx_ui_surface_artifact_integrity_witness`, `memory_capture_real_execution_witness`, -`pre_os_capture_replay_witness`, `provenance_calibration_report_real_execution`, -`review_sheet_legacy_declaration_wet_witness`, `self_host_logic_seed_unavailable_check_fixture`, -`serving.serving_availability_bind_wet_witness`, `shell_spawn_refused_real_execution_witness`, -`spark.pair_incumbent_identity_wet_witness`, `spark.pair_serving_d0_authorization_witness`, -`spark.pair_serving_d0_real_execution`, `spark_snapshot_verify_wrapper_local_wet`, -`spark.v41_runtime_candidate_witness`. - -## Absorbing-fallback finding (not a host premise) - -`test.claim.fabric.fabric_storage_file_store_wet_witness` `fresh_store` drops a -`FabricStorageRootRefused` and returns the root anyway. §5 names this an absorbing fallback; queued -with the recorded rows; not scheduled. diff --git a/docs/plans/witness-cost-first-touch-attribution.md b/docs/plans/witness-cost-first-touch-attribution.md deleted file mode 100644 index e3e87c6a6ec..00000000000 --- a/docs/plans/witness-cost-first-touch-attribution.md +++ /dev/null @@ -1,892 +0,0 @@ -# The floor's per-row budget charges shared entry cost to whichever row touches it first - -Finding from the 2026-08-18 runaway census -([measurement](measurements/floor-slow-rows-2026-08-18.md)). It changes what the -six runaways are, and so what may be done about them. - -## The claim - -The budget attributes per row; much of the cost is INCURRED per entry: the first -row to reach an expensive shared computation pays all of it, later rows pay -nothing, and the ceiling refuses the first row for a cost not its own. - -## The receipt — two rows, same computation, 1ms apart - -`dag/test/claim/g2_data_reference_under_selection_witness_test.dag` declares a -deliberate pair: both rows call the same `specimen_projection()`, both are -enrolled, both EXECUTED in run 32172125816, and both are reported because both -are expected-red (failures report regardless of duration): - -``` -18:59:48.653 specimen_classifies_as_runtime_read FAILED in 12 seconds -18:59:48.688 specimen_does_not_classify_as_local_read FAILED in 31ms -``` - -12197ms and 31ms, 393x apart, same call, 35ms apart on the clock. The second row -is not cheaper; it is free because the first already paid. - -Not the only instance: in `test.claim.extdeps_scope_placement_gate_loudness_witness` -exactly one of seven rows is over the warn line at 71244ms; the siblings calling -the same `scope_placement_membership_verdict` on the same fixture are all under -100ms. That row is also the run's worst memory event, `grew rss by 1.00GB` — the -shape of a corpus-scale structure built once, not work repeated per row. - -Precisely: the g2 pair shows directly that a SECOND call is free, which makes the -first row's charge shared rather than its own. The extdeps_scope entry is -consistent with that but does not independently establish it — the floor reports -duration only for rows that fail or cross the warn line, so the six siblings are -known under 100ms but their execution order relative to the runaway is not -recorded, and this document does not assume it. - -## Why this matters for the lane - -The lane's framing is to decompose over-budget rows into small complete -segments. For this class decomposition cannot work, and the repository says so: -`gunbc.witness_row_cost` `witness_decomposition_does_not_reduce_entry_cost_note` -(operator, 2026-08-05) rules that splitting a witness does not divide the entry's -cost but amortizes it across more rows, so the per-row drop is an artifact of the -split. This finding is that prediction observed: splitting one of these rows -moves the charge to whichever fragment runs first and changes no real cost. - -The honest remedies are two, neither a split: - -1. Reduce what the shared computation reaches for — a real cost reduction, - which moves the entry total and not just the attribution. -2. Attribute shared cost to the entry rather than its first row, so the ceiling - stops refusing a row for cost it did not incur — a change to the floor's - measurement, not to any witness. - -## What is NOT available, and why it is worth stating - -The third remedy the refusal text offers — "move it to a designated `long/` -home that CI does not run" — is not available today, and taking it would be a -coverage deletion, not a fix. No cadence executes the long home: the repository -carries exactly two workflows, `witnesses.yml` and `fleet-converge.yml`, and the -falsifier cadence was deleted in the floor cut. The 2026-08-04 operator ruling -(DESIGN, "witness cost derives from purpose") names this exact move as its -originating specimen — a witness relocated under `long/`, removed from discovery, -no cadence row, executed by nothing, admission still green. Relocation would -reproduce it. - -Two files' notes still claim a path-based exclusion that no longer exists. -`required_floor.dag` excludes the long home by AUTHORED MODULE NAME -(`long_home_prefixes`: `test.claim.long.`, `v2.test.long.`, -`v2.test.claim.long.`), deliberately off path because a directory deciding -admission was the defect. `test.claim.rust_test_fixtures_import_closure_witness` -declares no long prefix and no `ReadsLiveTree`, so it is fully enrolled and pays -50.4s per run while its note says it does not run; -`v2.test.manual.grounding_lens_whole_tree` is enrolled the same way, its -`v2.test.manual.` prefix not being excluded. The notes were corrected in -gunbc#8430; the enrollment is real and is counted here. - -## Scope of this document - -A measured finding and its consequences; no mechanism changes. The attribution -repair (remedy 2) belongs to whoever owns the floor's measurement and is not -taken here. - -## Follow-up: the 71-second row is not expensive in isolation - -Measured because the row is 13% of all slow time alone, and "first-toucher or -genuinely expensive?" decides whether paring it is aimed at anything. - -Same head, same two source roots CI uses (`--source-root dag --source-root -src/v2`), `claim_batch` on that entry alone, reading the per-row `[witness]` -CPU line — the quantity the floor's budget compares: - -``` -row run alone red_seed_runner_failure_detail_projects_located_receipt cpu=70ms -cheap sibling alone red_membership_fixture_names_change_and_path cpu=0ms -both, in order red_membership 0ms, then red_seed_runner 85ms -``` - -Against CI's `cost is exactly 71060ms against 1552ms` for the same row. Both are -per-row CPU at the same head, so comparable: **70-85ms isolated, 71060ms on the -floor — about a thousandfold.** - -The difference is evaluation context. The sandbox resolves this entry's own -closure (67 modules, 1425 resolved items); the floor evaluates every row against -one whole-corpus prepared subject (2376 modules). The row's cost is CONTRIBUTED -BY THE CONTEXT, not by what the witness computes — it computes almost nothing, -and the sibling reaching the same `scope_placement_membership_verdict` costs 0ms. - -Consequence for the lane: remedy 1, "reduce what the shared computation reaches -for", has almost nothing to bite on here — paring would aim at 70ms of work to -move a 71-second bill not made of it. - -A candidate mechanism, not asserted: this row reaches its companion by NAME -through the seed runner (`run_claim_failure_receipt` → `run_in_context` → -`ctx.lookup_fn`), and the prepared-floor scope is a flat whole-corpus namespace -in which 378 helper names collide and bind last-write-wins (gunbc#8437). A -name-keyed lookup is the one thing this row does that its cheap siblings do not. -A lead for whoever holds the prepared-floor scope, untested here; the measurement -stands without it. - -Sandbox caveat, so the number is not reused wrongly: single-entry `claim_batch` -is fixed-cost dominated — its whole-tree graph-facts phase alone is 18-21s and -its resolve-split load term ~25s — and the repository already rules that such a -harness characterizes itself rather than the floor. Hence the comparison is on -the per-row `[witness]` CPU line, not process wall. An earlier attempt compared -process wall, made the cheap sibling look like an 82-second row, and established -nothing. - -## The eight-sibling test: identity does not predict cost, position does - -`v2.test.claim.effect_reach_test` carries eight rows landing within a 346ms band -just past the ceiling on the floor (1954-2122ms). Two obvious readings — one -shared fill with seven free riders, or eight genuinely expensive witnesses — -predict opposite things under isolation. Measured, with the order-reversal -control that distinguishes them: - -``` -row alone fwd(pos) rev(pos) -path_data_init_derived_host_reading 246 252(1) 511(8) -path_data_init_red_when_import_severed 217 441(2) 488(7) -path_touch_selects_on_normalize_path 225 497(3) 459(6) -unrelated_path_does_not_select 244 517(4) 518(5) -hermetic_fixture_stays_local 241 589(5) 446(4) -prose_string_path_does_not_classify 251 498(6) 421(3) -concat_built_path_frontier_not_classified 247 496(7) 385(2) -live_03_normalize_witness_derived_host_reading 234 514(8) 231(1) - -by position, mean of both orders: - pos 1: 242 pos 2: 413 pos 3: 459 pos 4: 482 - pos 5: 554 pos 6: 478 pos 7: 492 pos 8: 512 -``` - -**Neither reading is right.** Alone, all eight cost 217-251ms — a 34ms spread -around a 238ms mean, as uniform as this harness measures — so not eight -independently expensive witnesses. But the first row in a batch is the CHEAPEST, -the opposite of a shared fill with free riders. Cost tracks position, not -identity: `path_data_init_derived_host_reading` costs 252ms at position 1 and -511ms at position 8; `live_03_normalize_witness_derived_host_reading` 514ms at -position 8 and 231ms at position 1. Same rows, closure, head. - -### The step is a claim_batch artifact, not a floor cost — corrected - -An earlier revision called that step "a second attribution defect", implying the -production path. It does not belong to the floor; three independent checks say so. - -**Source.** `claim_batch` `run_entry_group` builds ONE ctx per entry group, runs -every function in the group against it, and calls `eval_call_memo_frame_exit` -after each — position 1 meets a virgin context, positions 2..N one that has had -an eviction pass. The floor's claim-evaluation fold in `cli_run.rs` builds -`evaluation_frame` FRESH INSIDE the per-claim loop, stating why in source: claims -sharing one immutable scope must not share a context's mutable evaluation caches. -The floor deliberately lacks the mechanism that produces a once-off step. - -**Cross-entry measurement.** Batching two different entries shows no step — each -row costs its alone figure in either order, since each entry gets its own ctx: - -``` - effect_reach row root_d row -alone 239 1774 -batch [effect_reach, root_d] 229 1802 -batch [root_d, effect_reach] 219 1732 -``` - -Within one entry the step reproduces (235ms then 396ms for two rows of one file), -so the effect is bounded by the entry group — a property of the shared ctx, -exactly where the source says. - -**Floor data.** With the same step, later rows within an entry would cost about -twice the first. Across the 32 modules in this run with five or more uncensored -slow rows, the mean rest-to-first ratio is **1.27**, spanning 0.75 to 3.48 — -several modules have a first row MORE expensive than its siblings, the -first-touch shape and the opposite of the step. A shared mechanism would cluster -near 2.0. It does not reproduce. - -So the position step is a fact about `claim_batch` and says nothing about floor -attribution; the additive-versus-multiplicative question it raises is not about -the production path. - -### What this did establish: the first confirmed paring target - -`root_d_checkpoint_scalar_declared_arity_witness_holds` costs **1774ms of its -own work** alone, in a closure of 5 modules and 160 resolved items — far too -small for the cost to be context; it is the witness's own evaluation. The floor -attributes 2792ms, so unlike every other row examined here the gap is modest — -about 1.6x, not a thousandfold. - -It is the first row in this lane whose expense survives isolation, hence the -first genuine subject for the operator's decomposition brief. It is also the -most stable over-budget row across runs (2792 / 2849 / 2855, a 2.3% spread, -against 22-30% for the large ones) — what a real cost looks like and an -attribution artifact does not. - -## The stability screen: a sound negative filter, not a paring worklist - -`root_d`, the one row whose cost survived isolation, was also the most stable -across runs (2.3% spread where the large rows swung 22-30%). That suggests a -cheap screen over all 720 slow rows — rank by cross-run variance rather than -magnitude — since a witness's own cost should not care what else ran, while -context, fill and position costs vary with everything around them. Magnitude -sorts mostly by who touched a cache first, so a principled screen would be the -first way to build the paring worklist without 720 isolation runs. - -Built over four runs (32172125816, 32177951514, 32185058245, 32187164199), -joined by identity, 650 rows with three or more usable observations. - -**Censoring rule, and it decides the result.** An observation is dropped when -the row was explicitly interrupted (`cost is at least`) or sits in the -poll-pinned band — those values are the deadline, not a cost. Budget-refused -rows that COMPLETED are kept: they carry a real measurement. An earlier cut -dropped every budget-refused row and thereby excluded `root_d`, the 71s row and -all eight `effect_reach` rows — every row worth screening — which is over-broad -censoring when the expensive population is exactly the refused one. - -**Validation.** Three rows with known ground truth from isolation: - -``` -row CV mean percentile known truth -root_d 1.5% 2854ms 18 real own work (1774ms isolated) -71s extdeps_scope row 7.8% 76988ms 86 context (70-85ms isolated) -g2 specimen row 7.4% 13292ms 84 first-touch -``` - -Two of three land correctly: the context-dominated rows in the variable head, -`root_d` in the stable tail. - -**But it fails the fourth case, and the failure is the finding.** The eight -`effect_reach` rows come out STABLE — CV 1.9% to 6.4% around a ~2050ms mean — -though isolation established they do about 238ms of their own work. A row can be -highly stable and still carry an order of magnitude of cost not its own, because -the context it carries is itself reproducible run to run. - -So the screen measures REPRODUCIBILITY, not ownership; only the second is what -paring needs: - -- **Sound as a negative filter.** A high-variance row is certainly not measuring - its own work, so the variable head can be excluded from a paring worklist - without isolation — a real saving over 720 rows. -- **Unsound as a positive test.** A low-variance row may be real work (`root_d`) - or stable context (`effect_reach`). Only isolation separates them; the screen - must not be presented as producing the worklist directly. - -The honest procedure is two-stage: screen out the variable head for free, then -isolate the stable tail. The stable tail above 1000ms is small enough for that -to be affordable — the actual saving on offer. - -## Cluster-tightness: also suggestive, also not decisive - -If N distinct witnesses — different names, assertions, modules — land within a -few percent of each other, the figure looks like it belongs to something shared, -since independent work has no reason to coincide. Over the four-run join, rows -above 800ms fall into 19 clusters at 3% width; one is remarkable: **18 -identities across 4 unrelated modules** (roadmap static site, running-release -identity, deploy readiness, site-surface readiness) inside a 2.3% band at -1213-1242ms. - -It correctly flags the `effect_reach` six at 2016-2067ms, which isolation showed -collapse to ~238ms — one confirmed true positive. - -**But the 18-row cluster does not collapse.** One member isolated from each of -its four modules: - -``` -module floor isolated closure -roadmap_static_site_witness 1213 888ms - -running_release_identity 1216 791ms 630 modules / 15657 items -live_deploy.readiness 1218 819ms 632 modules / 15656 items -roadmap_site_surface_readiness 1218 827ms 632 modules / 15640 items -``` - -Two-thirds of each floor figure survives isolation: not eighteen rows riding one -fill — each still pays most of its cost alone. So cluster-tightness is a -suggestive prior, not a decisive test — the same standing as the variance -screen, for the same reason: both observe the SHAPE of a cost distribution, and -neither can see whose work it is. - -**The run did surface a better discriminator than either.** Cost against closure -size: - -``` -root_d 1774ms over 160 resolved items dense -cluster members ~820ms over 15,650 resolved items sparse -effect_reach 238ms over 2,694 resolved items sparse -``` - -`root_d` does an order of magnitude more work per resolved item than anything -else examined. That density — not magnitude, not stability — marks its cost as -its own evaluation rather than its surroundings. - -**Not settled by this run, stated so it is not assumed:** whether the cluster's -residual ~820ms is assertion work or per-process warm-up of a 15,650-item -closure the single running row pays alone. Distinguishing them needs two rows of -one module on a harness without `claim_batch`'s shared-ctx step, which does not -exist today. Until then the honest reading is that ~820ms is *not shared across -the eighteen* — not that it is assertion work. - -## root_d: the cost is one claim's fixture import, not the witness - -`root_d_checkpoint_scalar_declared_arity_witness_holds` is one `test fn` -conjoining three claims, two calling `compile_dag_rust_emit_check` on a small -virtual module. Measured by splitting them temporarily and running each in -isolation (5 modules, 163 resolved items): - -``` -claim verdict cpu -positive fixture (declared-arity leaf) PASS 36ms -arity-0 scalar still strips phantom arguments PASS 1782ms -authority answers for both fixtures PASS 0ms -``` - -**All three pass**, and one claim carries essentially the entire 1774ms. Two -things follow; the first corrects a pre-measurement assumption. - -**The expensive claim is not the one carrying the defect.** The row is enrolled -in `floor_expected_red`, inviting the reading that its positive fixture is red -and expensive. It is neither: the positive fixture costs 36ms and passes. The -row passes as a whole and is a stale quarantine, consistent with the floor's -`stale_quarantine=5`. - -**The cost is an import in a fixture string, not an assertion.** Both -expensive-looking claims call the same checker on a two-line module. The -difference between 36ms and 1782ms is that the second fixture's source begins -`import std.integer { Int8 }`, so the emit check compiles that closure, while -the first declares its `Witness` inline and imports nothing. The 1774ms is -compiling `std.integer` inside a virtual fixture — not proving anything about -checkpoint-scalar arity. - -**Why no split is proposed here.** Splitting would move the 1782ms onto a new -identity not on the expected-red roster, turning a budget-refused quarantined -row into an ordinary over-budget failure — worse than before. The decomposition -is only correct together with the fixture fix, and that fix is a judgement -about whether the arity-0 wall can be witnessed by a scalar that does not drag -`std.integer` in — Root D's owner's call, not a cost lane's. Recorded with the -measurement so that decision rests on facts, not the row's size. - -So the corpus's one row that survived every screen is also not a -witness-decomposition subject: its expense is a fixture-authoring choice with a -localised cause and a plausibly cheap fix. - -## Reconciled against the floor-path fill ledger (gunbc#8464) - -The shared-fill ledger instruments corpus caches on the floor path and reports, -per fill, cost, payer, and readers. Its run 32192150969 (9425 claims, 19 fills) -settles two of this document's rows at identity grain; one corrects a claim here. - -**First-touch, confirmed on the production path.** `unbound_dissolution_empty_literal_refuses` -— one of the six runaways — paid a 51508ms `module_path_index` fill that **139 -claims across 29 modules** then read: 99.8% of the row's measured cost belongs to -a computation twenty-nine other modules consume, observed on the floor rather -than inferred from a sandbox. The g2 pair's shape at corpus scale, and the run's -only shared fill. - -**But the 71-second row is the opposite, which refines what this document said.** -It paid a 29017ms `module_graph_facts` fill and a 24965ms `reference_edges` -fill, **both exclusive** — no other module reads either. Its cost is genuinely -its own, and the earlier framing, "a cheap witness billed for the context it -runs in", is not quite right. - -The measurements reconcile once the variable is named. Isolated in a 67-module -closure the row costs 70-85ms; on the floor's 2376-module subject, 71s. The -witness did not change — it asks for whole-corpus facts, so its bill is a -function of corpus size. Neither a cheap row wearing someone else's cost nor an -expensive assertion: a witness whose subject IS the corpus, small in isolation -and large on the floor. Isolation missed this because shrinking the corpus is -exactly what makes such a row look cheap. - -That distinction decides the remedy, and it inverts between the two rows: - -- the 51.5s `module_path_index` payer must NOT be quarantined — moving it - recovers ~112ms and hands 51.5s to whichever of the other 29 modules runs - first. The fix is to hoist that fill into preparation, where its consumers - already are in spirit and it stops being any witness's bill. -- the 71s row's fills are wanted by nothing else, so moving or reducing it - recovers the whole amount. - -Per-row wall time does not distinguish these cases; nothing in this document's -census does — the strongest argument that the ledger is the instrument the -population needed, and the honest limit of a census built from timings alone. - -**And a floor of ~108s no paring can reach:** three fills — a 55392ms module -graph, a 35060ms path index, a 17646ms reference-edge scan — are paid before any -claim runs. Quarantining every witness would not move them. - -## Retraction: the variance screen measured four different trees - -External review on the subtree rollup caught a defect in the screen above, -unchecked by author and reviewer, fatal to the statistic as stated. - -**The four runs have four different heads, on four different branches:** - -``` -32172125816 6aa80c79 work/last-two -32177951514 59653f56 main -32185058245 178db9ad session/crisp-wren-479 -32187164199 ccf21913 floor/cost-is-not-a-defect -``` - -No pair is a repeat measurement of one subject. Repeatability requires the same -subject digest, claim code, manifest and order, budget and poll policy, and -source universe; these share none. So every coefficient of variation above is -**agreement across four different worlds, not repeatability under identical -input** — and this lane already established that changing the source population -moves both instrument sensitivity and first-touch ownership, exactly the -variable left uncontrolled. - -`root_d`'s 1.5% figure therefore did not establish what it was used for. -Isolation vindicated the row independently; the statistic did not earn that -agreement and should not be cited as if it had. - -**The negative direction fails too.** The screen was demoted to a "sound -negative filter" after `effect_reach` came out stable. Review rejects that half -as well: an intrinsically expensive claim can swing 20-30% from host scheduling -and frequency variation, page-cache state, allocator behaviour, subprocess -startup, lock contention, input-dependent branch and allocation shape, or a -nested shared fill reached during otherwise genuine work. High variance does not -establish context-only, and the variable head cannot be dropped for free. - -And stable context cost is not the accident this document treated it as. Claim -order is deterministic, so the same identity always first-touches a given cache; -corpus and cache key are deterministic; every process starts cold; the same host -class recurs; a cooperative deadline pins observations near one polling boundary. -Under those conditions a shared cost is *expected* to look stable. -`effect_reach` is the normal case, not a freak counterexample. - -**What survives.** Variance is legitimate PRIORITISATION — high variance: investigate -order and context sensitivity early; low variance: repeatable-cost candidate — -and never evidence of ownership either way. The minimum evidence stack for -ownership is three things together: an uncensored completion rather than an -interrupt-point value, repeated runs on the SAME subject, and an order -perturbation showing whether the charge moves to a different first toucher. The -reversal control earlier in this document is the third; the second this lane has -never performed. - -## The error bar on a single floor observation, measured - -The lane had never measured one subject twice. A GitHub re-run replays the -pinned merge ref, so re-running a completed run yields two observations of one -world — every row measured twice. Run 32182126916, attempts 1 and 2. - -**Same world, verified:** identical subject digest `b0eb3f2be1a200e0` and -identical counters on both attempts (planned 9420, executed 9420, passed 8951, -failed 0, budget_refused 102). *(Capture the log BEFORE triggering the re-run: -GitHub serves only the latest attempt and the earlier archive is unrecoverable. -This pair exists only because attempt 1 had been downloaded hours earlier for an -unrelated check.)* - -**Paired per-identity delta**, on the 673 rows uncensored in both attempts — -not marginal distributions, because opposing moves cancel and two histograms can -look identical while every row moved: - -``` -median |delta| 3.8% -p95 |delta| 12.7% -p99 |delta| 16.4% -largest moves -5% on the 93.8s row, +12% on the 16.6s row -``` - -So a single floor observation carries a real error bar: about 4% typically, over -12% at the tail, on an identical subject. - -**Crossings of the 1552ms ceiling: zero.** Spread and crossings are different -facts; only the second is the enforcement question — a 200ms move on a 400ms row -is irrelevant, at 1500ms it flips a build. Across 673 genuine rows measured -twice, none changed sides. - -**Not because the instrument is precise — because the danger band is nearly -empty.** Only **4** genuine rows sit within the p95 flip-radius (191ms) of the -ceiling; the closest: - -``` -margin 29ms (1.9%) 1523 -> 1517 where_refinement_cast_literal_oci_other_digest_algorithm_accepts -``` - -That margin is **below the median noise** of 3.8%, so it is the corpus's one -materially flip-prone identity. It is also, independently, the exact row -sharp-raven-273 observed at 1505ms then 1274ms on a re-run of one frozen ref in -the 1500ms-ceiling era — a 15.4% move, near this distribution's p99. Their flip -is the predicted behaviour of the closest-to-threshold row, not an anomaly; two -lanes located one identity by different routes. - -**What this settles.** The ceiling is enforceable as a per-run threshold on -today's population: enforcement risk is concentrated in a handful of rows, not -diffuse across the corpus. It is not safe in general — any row resting within -~200ms of the ceiling inherits a pass/fail partly a property of the runner, and -so does a single-run quarantine decision. - -**Caveat that changes the reading, nearly unstated.** The rows *closest* to the -ceiling overall sit at 1553ms and did not move between attempts — poll-pinned -censored values, not measurements, so they cannot cross by construction. -Including them makes the instrument look far steadier than it is. Every figure -above excludes them. - -### Rule, not caveat: exclude censored values before computing any distribution - -Three shapes of one class turned up in one day on this log — `cost is at least` -vs `cost is exactly`, the `BUDGET-REFUSED` third channel, and the poll-pinned -1553ms band inside a paired distribution. Each flatters: the instrument looks -steadier, or the population smaller, than it is. So this is a standing step, not -a thing to remember when suspicious: **before computing any statistic over floor -timings, drop every right-censored value, say how many were dropped, and name -the channel each value came from.** The channel is part of the datum because -the three are not equally trustworthy and no field distinguishes them once the -number is extracted: - -``` -BUDGET-REFUSED an interrupt -> right-censored, no upper bound -cost is at least an interrupt -> right-censored, lower bound only -cost is exactly a completion -> a measurement -[floor-witness-slow] elapsed -> a measurement (fires at the 100ms warn) -a poll-pinned 1553ms with 1ms margin -> neither; it cannot move by construction -``` - -(The first three spellings are the pre-#9599 rendering, kept because this -document's figures were parsed from runs that emitted them. Current spelling: -`cost=UNMEASURED` with a separate `interrupt_point=` field for an interrupt, and -`cost={n}ms EXACT` for a completion — `ClaimOutcome::budget_figure_phrase`. That -is this rule made structural: the bound no longer occupies a measurement's field, -so it cannot be extracted as one.) - -(The channel refinement is sharp-raven-273's, and the right generalization: "how -many were dropped" hides that the dropped rows failed in different ways.) An -interrupt is not a measurement and a pinned value cannot move by construction, -so neither answers a question about movement in either direction — they are -unmeasured, a third state from pass and fail. - -## Is the noise relative or absolute? It is relative — so a flip radius scales - -Decides whether the 1552ms result transfers to a lower ceiling. Mean paired -movement, bucketed by row magnitude over the 672 genuine rows: - -``` -magnitude n mean |rel| mean |abs| -100-250ms 391 4.9% 7.9ms -250-500ms 142 5.4% 19.3ms -500-1000ms 109 4.3% 29.7ms -1000-2000ms 30 2.6% 30.4ms -``` - -Absolute movement grows nearly 4x across the range while relative movement stays -flat. The noise is **proportional**, so a flip radius must be a fraction of the -ceiling, not a fixed millisecond figure carried across. Carrying the 1552ms p95 -radius (210ms) down to a 500ms ceiling would overstate that band by more than 3x. - -## What each candidate ceiling costs, measured - -p95 relative movement is 13.5%, so the flip radius at ceiling C is 0.135*C. -Over the 672 genuine paired rows: - -| ceiling | p95 radius | rows in band | CROSSINGS observed | rows already over | -|---|---|---|---|---| -| 500ms | 67.7ms | 53 | **11** | 139 | -| 750ms | 101.5ms | 40 | **9** | 62 | -| 1000ms | 135.3ms | 24 | **3** | 30 | -| 1552ms | 210.0ms | 4 | **0** | 0 | - -Crossings are observed, not modelled: each is a row that landed on opposite -sides of the ceiling in two runs of one identical subject. - -**Not scattered coin flips — they cluster by module.** Six of the eleven 500ms -crossings are one module (`generic_item_clone_bound_witness`, every row -476-491ms then 502-528ms), two more `where_refinement_enforcement_witness`, two -the python round-trip pair. Rows in a module share a closure and move together: -a ceiling in a crowded band refuses a whole module at a time on a runner's luck. - -**So a 500ms ceiling is not a marginal adjustment.** It refuses 139 rows on the -first run for being over, and puts 53 more in a band where 11 were observed -changing sides between two runs of one tree — a build's outcome partly a -property of which runner picked it up. 1000ms is the first candidate where the -band thins (24 rows, 3 crossings); 1552ms has no crossings at all. - -This does not say the corpus should stay slow — the ceiling cannot lead the cost -work. Rows must leave the band before the ceiling descends onto it, the ordering -#8470 demonstrated: the 45941ms row became 844ms by moving a shared fill into -preparation, with no change to the ceiling. - -## Audit of the two quarantine-decision runs - -Both logs retrieved complete (terminal counter line present in each — not the -CLI-truncation class): - -``` -run 32189985063 planned=8995 executed=8995 passed=8636 failed=1 budget_refused=0 - FAIL emitted_lib_rs_module_declaration_witness_test.small_crate_lib_rs_omits_unemitted_dispatch_module - cost is exactly 42545ms against 1552ms (27.4x the ceiling) - -run 32193032348 planned=8997 executed=8997 passed=8638 failed=1 budget_refused=0 - FAIL emitter_nested_refinement_cast_witness_test.single_refinement_carrier_emits_no_unsupported_cast - cost is exactly 45826ms against 1552ms (29.5x the ceiling) -``` - -Neither decision is marginal: flipping a 42545ms observation needs a 96.4% drop -against a measured p99 of 17.7%. Both quarantines are clean on the evidence, and -both rows are the first-touch fill carriers this note is about — the second is -the row #8470 later brought to 844ms without touching the witness. - -One discrepancy recorded, not resolved: each run's terminal counters report -`failed=1, budget_refused=0`, so these logs contain two measured verdicts, not -the eight-plus-one this audit was scoped to. The remaining identities are not in -these runs' counters; their provenance needs naming before they can be audited -or cleared. - -## The ownership instrument now exists, and this document's whole method is superseded - -Everything above infers ownership from timings — variance screens, cluster -tightness, isolation runs, order reversals — and concludes, correctly, that a -census built from timings alone cannot see whose work a cost is. That limit is -now lifted, and by an instrument the floor already emits on every required run. - -`claim_executor` turns `GUNBC_RECOMPUTE_TRACE` on unconditionally, and the -required floor writes **`required_floor_cross_claim_demand.tsv`** beside -`required_floor_claim_cost.tsv`. Its row is a producer identity with the count of -claims that demanded it, the count of times it was actually evaluated, the -modules it spans, and its declaration site. A producer with claims equal to evals -was re-derived once per claim and shared with nobody — the question every screen -in this document was a proxy for, answered directly and at identity grain rather -than inferred from a distribution's shape. - -So the standing procedure for this class is: read the two artifacts, not the log -and not a sandbox. Both are named in the log's own announcements, with their -caveats in band — `[cross-claim-demand]` states that its cost column is -inclusive of callees and must not be summed, and that its printed head is a -preview ordered by recomputation rather than a candidate roster. - -**Both log renderings are capped at 25 rows and both say so in a trailer.** That -trailer is load-bearing: on run 33668368846 the `[over-cost]` head carries 25 -rows while `required_floor_claim_cost.tsv` carries 295 over the 100ms line. A -census taken from the log is the log's ranking read as the population — -`instrument_output_read_as_subject_content`, observed twice on this lane. - -**And a third truncation lives in the ARTIFACT, not the log, which is why it is -the dangerous one.** `required_floor_cross_claim_demand.tsv`'s `module_sample` -column caps at **8 modules**; on run 33668368846, **1044 of 26317 rows** carry a -`modules` count above that cap, so their consumer list is cut. Unlike the two -print caps this one has no trailer — the row simply lists eight modules beside a -`modules=71`, and a reader joining producers to consumer modules gets a silently -partial answer. - -It bites exactly where the join matters: the producers whose sample is truncated -are the WIDELY SHARED ones, which are precisely the producers most likely to -explain why a whole module's rows cluster. So **a corpus-scale join from module -to shared producer is not performable from this artifact today** — the per-module -reading below is sound only where the join was performed by hand, one producer at -a time, and a 73%-of-over-line-CPU classification reported off the step ratio -alone is a step-cluster inference at that strength rather than an ownership -result. Widening or inverting that column is named here as an obligation and -deliberately not undertaken in this lane. - -Three truncated renderings on one lane is a pattern rather than three accidents, -and the shape they share is the reusable part: **an instrument that caps a -collection reports the cap honestly at the top level (`modules=71`) while the -column a consumer actually reads silently answers for eight.** Check the cap -against the count before joining on any list-valued column. - -### Eval steps, not milliseconds, is the within-module discriminator - -`required_floor_claim_cost.tsv` carries `eval_steps` beside the two clocks. Steps -are deterministic where a millisecond is not, so a step figure cannot be explained -by a quiet runner — which is exactly the confound that demoted the variance screen -and the cluster-tightness prior above. - -**What `eval_steps` measures is WORK, and work is not OWNERSHIP.** Rows landing at -near-identical step counts are not thereby shown to share a producer; that is a -step-cluster inference, and the same total can arise from unrelated derivations of -similar size. Ownership is established by the cross-claim demand artifact, which is -keyed by PRODUCER IDENTITY and says which claims actually reach the same producer. -The shared-derivation conclusion is a JOIN of the two instruments: demand identifies -the shared producer, and `eval_steps` then characterises how much evaluation work -the claims reaching it perform. - -Read that way on run 33668368846's over-line population, the majority of the CPU -sits in modules where the demand artifact names a common producer AND the rows -agree on steps to within a few percent: shared derivation, where -splitting re-attributes the fold and changes no real cost -(`witness_row_cost` `witness_decomposition_does_not_reduce_entry_cost_note`). - -The remedy for that bucket is enrolment on -`v2.workflow.floor_pure_producer_share`, whose admission criterion is measured -serve cost below measured recompute cost — the demand census answers only the -recompute half, and a present-versus-absent join of `required_floor_claim_cost.tsv` -across two runs answers the other. A row that fails the serve half comes back out; -the roster's header already records one such exclusion with the capability that -would retire it. - -### What this screening is worth, measured elsewhere and not by this document - -gunbc#10143 landed on `main` after this document's tested base and measures the population this -screening ranks. Two of its results bound what any screening here can buy, so they belong beside -the method rather than discovered after acting on it. - -**The band is dense and the tail is a plateau — there is no gap below the ceiling.** On its -post-repair run, 5 rows sit at or above 500ms, 27 in 400–500, 55 in 300–400, against 3,174 below -100ms. Repairing modules top-down, the first three buy 43ms and the next seven buy 61ms — roughly -**10ms per module**, about one fiftieth of the ceiling. That is the treadmill, quantified. So a -screening that surfaces the next-most-expensive producer is answering a question whose payoff is -small by construction, and the document's own ordering — safety-relevant rows ahead of -milliseconds — is the more defensible one for reasons that are now measured rather than argued. - -**And the crossers do not generally share a root.** gunbc#10143 records that the modules newly near -the ceiling do NOT share a root the way gunbc#10133's three did. The shared-derivation bucket this -document describes is therefore the exception rather than the common case, which sharpens the -warning already stated above: splitting re-attributes a shared fold and changes no real cost, and -the shared fold is not what most near-ceiling rows have. - -**A worked counterexample was claimed here and then REFUTED by this document's own discriminator, -which is worth more than the example would have been.** The claim was that gunbc#10170 relocated -five canonical fixtures out of a ~5k-line module and took four `rust_body_add_emit` rows from -506/425/416/362ms to 180/174/172/168ms, with the spread collapsing 144ms → 12ms. - -Two floor artifacts refute it. Pre-relocation (run `33707763185`) and post-relocation (run -`33711894017`), same four identities: - -| | cpu_ms | eval_steps | -| --- | --- | --- | -| pre | 314 · 305 · 290 · 318 | 171091 · 166669 · 162507 · 171124 | -| post | 339 · 327 · 317 · 344 | 171052 · 166630 · 162468 · 171085 | - -**`eval_steps` moved 0.02%** — 39 steps out of 171091. The work did not change, so the relocation -removed no evaluation work from these claims. CPU is *higher* after, which is within noise and makes -the honest reading "no measured effect in either direction". The 506 baseline was a contended-run -outlier: those rows were already at 314/305/290/318 with a spread of 28 before anything was touched, -so both the "drop" and the "spread collapse" were artifacts of the baseline. And 180/174/172/168 -came from a targeted `claim_batch` — a fraction of the corpus, a different execution envelope, and -not a floor result. - -**The mechanism that caught it is the one this section argues for.** Steps are deterministic where -milliseconds are not; a cost story that milliseconds support and steps refute is the clock talking. -The same discriminator that says work is not ownership says here that a millisecond drop is not a -work reduction. - -The census does name where the family's cost sits, and it is worth recording which instrument found -it: the cross-claim demand artifact keyed by PRODUCER IDENTITY reports -`target_project_arrow_body_to_value_expression` at 95 claims / 117 evals, 4396ms total with -**4350ms cross-claim** across the emit family including all four rows. That is a producer named by -identity, not a cluster inferred from step totals. - -**It is NOT a candidate, and this document disqualifies it below.** That function carries -`handle_transform: fn(Node, Node, TargetModel) -> …`, so it meets the fn-typed-parameter exclusion: -reification refuses the argument, the key cannot be formed, and the store refuses AT PUBLICATION. -A controlled present-versus-absent pair would measure noise against nothing, because the absent arm -is the only arm — the row never stores. **The demand it names is real; the route to serving it is -closed until the key can be formed.** - -Recorded rather than dropped because the reading is the useful part: the largest cross-claim -producer in this family is unreachable by the serve mechanism, which is a fact about the mechanism's -coverage and not a gap in the census. - -### Screening candidates from the census, by reading a declaration - -The demand census ranks by cost, and cost is the wrong sort key: three large -classes on it cannot be shared at all, and one of them refuses at publication -rather than losing a measurement. Screen with the signature before spending a -controlled pair, because a row that never stores cannot have its serve measured — -the resulting pair is noise, not a negative result. - -**The rule is a ratio, and it screens OUT — it never admits.** Serve cost scales -with the size of the ARGUMENT plus the VALUE; recompute cost scales with the -DERIVATION between them, so a candidate is worth measuring only when the -derivation is large relative to what must be hashed and verified. That is a -necessary condition and emphatically not a sufficient one: gunbc#10094 enrolled -three candidates on exactly such reasoning and withdrew all three on measurement, -and gunbc#10108's note withdraws a fourth — `compile_phase_frontier_standing` -claim-forced at its general declaration — on single-authority grounds despite a -measured win. Nothing below replaces the roster's present-vs-absent receipt. - -Three classes are disqualified without a run: - -- **A fn-typed parameter anywhere in the signature.** Reification refuses a - `Value::Fn` as `OriginBoundNode` and a `Value::Closure` as `Closure`, and - arguments are reified on the same path as values - (`v1_interpreter::portable_args_from_ctx` → `CrossClaimStoreOutcome::RefusedArgsNotPortable`), - so the key cannot be formed and the store refuses at publication. This is the - class that excluded the two rust target models, met on the argument side rather - than the value side. *e.g.* `target_project_arrow_body_to_value_expression` and - `target_project_match`, both carrying - `handle_transform: fn(Node, Node, TargetModel) -> …`. -- **A hot utility — evals far exceeding claims.** A function called many times - per claim is not one derivation shared across claims; per-argument keys with - little reuse are cache population, which is why `formal_production_for_lhs_exact` - was removed from the roster after a run measured 1,635 fills against 113 - consumer claims. *e.g.* `bind_outcome`, `zip_eq`, `fold_grammar_expr`, - `emit_host_list_map`. **claims ≈ evals is the shape that SURVIVES this - exclusion screen** — it is worth measuring, which is not the same as admitted. -- **A constructor.** Its census cost is call volume plus inclusive callees, not a - derivation, so there is nothing to serve. *e.g.* `decl_ref`, - `effect_demand_key`, `formal_productions_catalog_to_node`. - -A fourth shape survives the screen but is usually a loss: a large argument reduced to a -small value with little work between, such as -`target_catalog_contains_node_shape(slot: Node, catalog_targets: List) -> Bool`, -where hashing and verifying the node list per serve costs more than the fold it -replaces. That one is the ratio rule applied, not a separate exclusion. - -Applied to the census, these three classes account for every remaining producer -above a second outside the emit family. The emit family is **not** available, and -the reason is stronger than it was: the roster's re-enrol trigger — a serve that -does not walk the value per consuming frame — HAS FIRED (gunbc#10094 made a serve -O(1) by handing over the reified value), the family was re-measured against that -new serve, and it is still out. All three candidates regressed, showed no effect, -or improved a family total while the rows nearest the ceiling got worse. The -remaining cost is deliberately left unidentified there rather than given a -plausible story. - -Two lessons from that run belong here because they bound this whole method. -**The subject is the per-claim row, not a total** — the ceiling is per claim, so a -family that improves on aggregate while its rows nearest the line get worse is a -loss. And **a discharged capability makes re-enrolment testable, never decided**: -inferring admission from "the serve is cheaper now" is the -specification-without-execution DESIGN §5 names, and it was wrong three times. - -Whether that capability also lifts the fn-in-key limitation is **open and should -not be assumed either way**: one is about the cost of moving a portable value, -the other about whether an origin-bound reference can be represented in one at -all. - -### The rows nearest the deadline are the ones whose refusals are being silenced - -Recorded because it reorders the population by something other than milliseconds. -Observed on main run 33671815204 (head 20caf4eb): the two rows the 500ms CPU -deadline preempted there were not incidental — they were -`self_host_compile_phase_live_gate_witness`'s -discriminating REDs — the planted-identity refusal and the equal-cardinality swap -refusal. A preempted row is not a slow row that still answered; it is a refusal -that did not execute, which is why the floor refuses to call the run green. So -cost work on this population is not traded against safety. Measured: sharing the -fold under those rows (a roster row later withdrawn on gunbc#10108's -single-authority grounds, so the cost figures are not carried here) took the run -to `cpu_deadline=0` and both refusals executed. Whoever makes that fold cheap by -whatever route gets the same effect, because the mechanism is the row reaching a -verdict rather than any particular repair. - -**Prioritise this population ahead of milliseconds — but the safety RANKING it -would need does not exist yet, and this document does not supply one.** A -preempted row is a strong candidate regardless of where it sits in a cost -ranking. What is observable is only the VICTIM IDENTITY: `cpu_deadline` counts -how many rows were cut off, `required_floor_claim_cost.tsv` carries each of them -with `verdict_reached=false`, and both name WHICH claim was preempted. - -Neither names what that claim exists to PROVE. The two rows above are refusal -probes because their source was read and verified one at a time, not because any -instrument said so — and reading names off a diagnostic does not generalise to a -corpus-wide procedure. `std.witness_purpose` is explicit that purpose is -AUTHORED, NOT INFERRED FROM IMPLEMENTATION, and it presently carries no per-row -declarations at all: zero declarers, zero consumers. - -So the honest statement of the method is three separate claims, and only the -first two are discharged here: victim identity is observable; these two victims -were independently verified as refusal probes; ranking the population by safety -relevance REMAINS BLOCKED until an authored purpose can be joined against -`verdict_reached`. Treating the second as if it were the third is how a class -acquires a rung it did not earn. - -**The repair for this class has to travel through the gate the class refuses.** -Recorded because it is a structural property and not a queue accident, and -because it decides what "prioritise the fix" can and cannot buy. A preemption is -runner-dependent — the same rows measure anywhere from ~100ms to over 500ms on -one tree — so any PR can draw a refusal from two rows that are not broken, and -that includes the PR carrying the repair. Observed in situ: a 138-line -documentation-only change with no `.dag`, no Rust and no workflow was refused by -`a_live_tree_that_gained_an_identity_refuses_and_names_it` at -`cpu_at_least=501ms/500ms`, while the consolidation that would make that fold -cheap sat open with its own required floor lane running. - -The tempting exit is a re-run, and it is the one move this document's evidence -forbids most specifically. A re-run does not make the row cheaper; it redraws the -runner. What it buys is a green **over refusals that did not execute** — the -`interrupted_before_verdict` rows are the discriminating REDs themselves, so the -mark asserts a verdict for exactly the claims that were preempted. That is worse -than a slow build, and it is why `cpu_deadline` must be read as a safety counter -rather than a performance one. diff --git a/docs/plans/witness-execution-closure.md b/docs/plans/witness-execution-closure.md deleted file mode 100644 index b2e30331a4b..00000000000 --- a/docs/plans/witness-execution-closure.md +++ /dev/null @@ -1,337 +0,0 @@ -# Witness execution closure: 778 identities discovered, declined, and never run - -**Found and closed 2026-08-20 (`nimble-wolf-645`).** The required floor discovered 778 witness -identities every run, declined all at file grain, and executed none — and since the 2026-08-15 -floor cut left `claim_executor --required-floor` as the tree's only witness-executing consumer, -"declined by the floor" and "not run anywhere" became one fact. - -This document is the finding and the receipt; the change is in `v2.workflow.required_floor`, -`v2.workflow.floor_route_gap`, and the seed's site projection. - ---- - -## The measurement - -Counted with the floor's former Rust scanner (a column-zero -`test fn `, and a column-zero `data … LiveTreeDisposition … ReadsLiveTree`), on `4cec10f66a3`: - -| population | identities | files | -|---|---|---| -| discovered `test fn` sites | 11,115 | — | -| `DeclinedLongModule` (authored `long.` module name) | 538 | 97 | -| `DeclinedLiveTree` | 782 | 112 | -| routed and executed | remainder | — | - -The floor's own run receipt agrees and is the citable number: `gunbc#8638`, run `32337765205` -against `32333231183` at the same base, reported `claims=9782 declined_long=538 declined_live=778` -on the base — the brief's 778 exactly. Quoted in -`dag/test/claim/seed_mirror_constant_lens_witness_test.dag` by a session that had just added four -rows to the declined population and measured the delta. - ---- - -## Three defects, and only the third is about cost - -### 1. The decline rested on a premise that had stopped being true - -`DeclinedLiveTree` excluded any identity whose FILE declared -`data live_tree_disposition: LiveTreeDisposition = ReadsLiveTree`, on the premise that reaching -the live tree implies "cannot run in the hermetic frame this floor folds". - -That premise had been false for some time. Hermetic mode carries the **checkout-input carve-out** -(`v1_interpreter`, the readonly `Filesystem.Read|List` arm guarded by -`hermetic_checkout_input_disposition`): a readonly filesystem operation whose path the disposition -*confirms* sits under the checkout root, with no `.git` or `target` component below it, -**dispatches to real input access** — the commit is the run's deterministic input, so this is -input access, not a host effect. Reading committed `.dag` and `.rs` sources is exactly that case, -and is what most of the 778 do. - -So the floor ran a **file-grain prediction of an answer the interpreter already decides exactly, -per identity, at the effect boundary** — and predicted it wrong, in the direction that silently -removed coverage. - -Independent confirmation: another session reached the opposite conclusion in writing one day -earlier — *"the required floor folds one hermetic prepared subject, so a live-tree reader cannot -participate by construction"* — followed by a §4b rung drop to *mitigatable* whose next-rung -trigger was *"an executing consumer for the declined-live population"*. The trigger needed no new -lane, only the stale prediction deleted. - -### 2. One fact, two computations, in one binary - -`reads_live_tree` was derived twice, by methods that cannot agree except by coincidence: - -- the former `cli_run` floor scanner — a **syntactic text scan** for the declaration line. -- `cli_run` `reads_live_tree_effective` — reads the same declaration, then falls through to - `effect_reach_derived_reads_live_tree_for_entry`, a **semantic effect-reachability derivation** - over the entry's import closure. - -These disagree as a function of the import graph (DESIGN §3). The resolution is not to pick one: -the two consumers asked **different questions**, and only one was entitled to the name. -Affected-set selection asks *does this entry's result depend on live tree state* — -`reads_live_tree_effective` keeps that question, untouched. The floor asked *can this identity -execute*, which no authored file-level boolean can answer. The floor's copy is deleted; nothing -replaces it, because execution answers it. - -### 3. The run's own honesty check could not see what it dropped - -`claims_planned` was the **post-decline** number. The terminal invariant -(`ClaimIdentityCountsDisagree`) compares `planned == executed == receipted`, all measured *after* -the projection dropped whatever it dropped: a projection declining a thousand identities and one -declining none produce identical healthy triples. - -And the per-identity receipt (`RequiredFloorDispositionRow` TSV) was gated on -`GUNBC_REQUIRED_FLOOR_DISPOSITION`, which `witnesses.yml` did not set — so on CI the whole -declined population was two integers in a `[floor-phase]` line. - ---- - -## The measurement, and what it settles - -The decline arm was deleted on a branch and the whole population executed — floor run -**32345970386**, sha `c812b9fb6d0`, the first run in which every discovered identity was routed: - -``` -offered=11103 routed=10565 declined_long=538 (partition exact) -planned=10565 executed=10565 terminal=10565 passed=10116 -known_red_held=207 failed=36 route_gap=157 -interrupted_before_verdict=47 completed_over_cost_requirement=2 -``` - -**Of ~783 identities admitted, 626 pass.** 157 route-gap, naming exactly the operations this -corpus's exclusion notes have named in prose for months — `Mktemp.Dir` 54, `IsExecutable` 26, -`Run` 17, `emit_host_native_cache_evict` 10, `Check` 10, `Write` 8, `git.Inspect.HeadCommit` 5, -then a tail. **Not one is a committed-source read.** The premise was stale for the large majority -of the excluded population, not an interesting minority. - -The cost worry did not materialise: 49 of 783 in the cost tail (~6%), 32 minutes wall against a -180-minute timeout. - -### The finding nobody was looking for: a third of the expected-red roster was never red - -That run carried **308** enrolled expected-red identities and held **207**. The difference is -**101** — and the 101 enrolled identities that route-gapped are *precisely* that set, by -intersection, not by count coincidence. - -Those 101 were already executing on main every run and already reaching a hermetic refusal. Since -that refusal arrived as a `TypeError` carrying prose, `ExpectedRedArm` could only read it as an -ordinary failure and **held it as agreement**. The debt ledger recorded "this witness runs and -fails and someone is fixing it" about 101 rows that never reached their subject; nothing was -fixing them because there was nothing to fix: **they needed a route, not a repair.** - -This is the state-space conflation §5 names, inside the mechanism whose job is making debt -visible. It also means main's `passed=8702 / known_red_held=306` counted something narrower than -executed coverage, by 101 identities, independently of the 778. - -### The cost tail is two mechanisms, not one budget - -The CPU histogram over the 49 is bimodal with a **14.4-second empty gap**: - -| band | rows | -|---|---| -| 5001–5013 ms | 23 | -| 6682 ms | 1 | -| *(nothing from 6.7s to 21.1s)* | 0 | -| 21109–53301 ms | 25 | - -A 12ms spread across 23 rows is **the interrupt firing**, not 23 witnesses each needing five -seconds — and an interrupted claim's cost is a *lower bound*, so those rows' true cost is -**unmeasured**. The low mode cannot argue the budget is too tight. - -24 of the high-mode 25 are *also* interrupted, having accrued 21–53s against a 5000ms deadline — -only possible where the stride poll cannot land: `ClaimPreemptionReachability::OpaqueHostCallUnbounded`, -already modeled in `v2.workflow.required_floor`. **Exactly one of the 25 is the known member** of -`opaque_host_call_grandfather_population()`, which declares itself *exhaustive at one member* and -"grows only when a DIFFERENT operation is found to share this shape, which is a new finding, not -a declaration." This is that finding: the other 24 reach host-fed `*_live` builtins in -`enforcement_live`, `cost_coverage`, `grammar_coverage`, `lifecycle_survivor_corpus_census`, and -`realization_vocabulary_containment`. The "bounded population" was bounded by what anyone had -observed on a floor not executing the population where the others live. - -## What replaced it - -**Delete-first at the root** (DESIGN §3 replacement migration). `DeclinedLiveTree` and the text -scan feeding it are gone; no intermediate representation was built beside them. - -- **One execution route per identity.** Every non-long identity is routed and executed. The route - is `RequiredFloorClaim.execution_mode`, already per-claim — no new vocabulary restates it. -- **The interpreter's refusal is the classifier, and it is typed.** - `InterpError::HermeticHostEffectRefused { operation, ground }` replaces three - `TypeError`-with-prose sites and reaches the floor as `ClaimOutcome::HostEffectRefused` — the - precedent `TimedOut` and `HostToolUnresolved` set against substring-matching prose. A route gap - is **not a verdict**: the witness never reached its subject, so it is neither pass nor failure, - and is reported apart from both because its remedy is a route. -- **`HermeticEffectGround` is closed and names the remedy**: `UnpublishedMockCase`, - `NoMockResponse`, `FilesystemRemoval`. One `String` would have collapsed three fixes into one - sentence. -- **The offered population is checked, not just reported.** `SitePartitionInexact` refuses - unless `offered == routed + declined_long`, stated at the loop that could violate it. -- **The residue is identity-grain debt, not a category.** `v2.workflow.floor_route_gap` reuses - the `floor_expected_red` contract rather than re-coining it: enrolled-and-gapped is agreement, - unenrolled-and-gapped reds, enrolled-and-did-not-gap reds as stale, enrolled-and-did-not-execute - reds. Monotone, identity grain, over an independently discovered closed universe — the four - conditions DESIGN §5's 2026-08-01 oracle ruling requires of a debt contract. No count assertion - anywhere. -- **The receipts are emitted on CI**, unconditionally: the disposition TSV and the long-home - storage agreement TSV are named in `gunbc.witness_floor_workflow`. - -Nothing gets a wet route — no live shell, write, or network. That is a separate lane with its own -admission question, deliberately not opened here. - ---- - -## What landed here, and what is staged - -This change carries the **mechanism** and the **101 reclassification**; it does **not** delete -the decline arm. - -The seam is exact, not chosen: the full-closure run surfaced **55 blockers — 6 witnesses that do -not RESOLVE** (`undefined variable`, `no such function`; never caught because nothing evaluated -them) **and 49 in the cost tail — all 55 newly-admitted, none previously routed.** So the -blocker-free part is a complete change that happens to be smaller, not a partial one. The 101 -route-gap on main today and need nothing deleted to be reclassified. - -Staged behind their owners: the 6 broken artifacts (route to author or delete — enrolling a -non-resolving witness as expected-red would assert it runs and fails, re-minting the conflation -this change removes), and the cost-tail policy call, which the histogram says is two questions. - -## What is NOT claimed - -The `long.` decline is untouched: an operator-ruled cost quarantine on a different axis, with -538 identities and no executing consumer — the *other* half of the hidden population, not closed -here. - -## The adjacent residue, named rather than left to be rediscovered - -`run_required_floor` consults `floor_prepared_subject_exclusions()` **and nothing else** — the -`witness_exclusion_frontier` rosters have no consumer on this path, which that function's own -comment records after a session added rows to them and measured `modules_excluded=2` unchanged. - -Three of its five entries exist for **exactly the route-gap reason** — an operation with no -`mock_response`, so "the hermetic floor refuses it and one refusing member fails the run." The -same defect this change removes, one layer over: a **file-grain, uncounted exclusion** where an -identity-grain typed disposition belongs. Strictly worse than a route gap: an excluded module -leaves the prepared subject entirely (not even typechecked), whereas a route-gapped identity -stays, executes, and reports. - -Converting those entries into `floor_route_gap` rows is the obvious next step and is deliberately -**not** here: it should follow, not precede, the measurement proving the route-gap mechanism -behaves as designed on its own population. - ---- - -## The same failure one level up: the projection's denominator (2026-08-29, `nimble-ibex-902`) - -The population above was lost *inside* the projection — discovered, declined, never run. The -sequel is the population lost *before* it: identities that never reached the projection, so no -partition over "offered" could speak for them. - -### The two mechanisms that removed them - -`assemble_prepared_subject_closure` drops modules two ways, and a witness declared in a dropped -module was neither planned nor declined: - -- **the exclusion substrings**, whose entry-grain receipt (`collect_deferred_discovery_rows`) - existed but was never joined to the disposition population at identity grain; and -- **the gate closure** introduced by the 2026-08-29 gate cut (§4b rung drop *Required gate - reduced to the compiler floor*). Only modules in the transitive closure of the gate seeds are - prepared, so `declined_outside_required_gate` — which reads two figures — can only ever speak - for modules that *reached* the index. - -### The subject universe, measured - -Counted with the floor's own rule (a column-zero `test fn ` / `test data `) over `dag/` and -`src/v2`: **13,975 distinct declared identities** in 1,759 files, **zero** duplicate identities, -**zero** module-name collisions. So the gap between the declared corpus and the floor's `offered` -was never duplication but two silent removals. The live figures are the run's own: -`required-floor: declared=… offered=… declined_outside_gate_closure=…`. - -### What changed - -**Hand-Rust receipt.** The receipt is a roster row, not this paragraph: -`gunbc.floor_population_projection_seed_growth`, enrolled in -`gunbc.seed_growth_admission seed_growth_justification_roster` — item delta, admitted -modifications, owning lane (`v1-hand-queue-drain`), dissolution trigger, and boundary chain live -there, where the census reads them. **Full-index retention (review 57430):** the discovery fold -consumes the prepared full-index views by value inside its own phase (the intermediate -`FloorDiscoverySource` vector is deleted), and the phase's completion line prints -`full_inventory_release_rss_kb_before` / `_trim_reclaimed_kb` / `_rss_kb_after` through the -floor's existing statm/malloc_trim instruments, so the run itself states that outside-closure -bytes end with the phase rather than surviving into claim execution. - -1. **The universe is the declared population, answered by one authority.** The floor folds - `v2.workflow.floor_discovery_producer` (`discover_floor_rows_for_source`) over preparation's - FULL module index rather than over the prepared closure alone, and classifies each returned - identity against the prepared closure and the exclusion map: an identity whose module - preparation dropped carries `DeclinedOutsideGateClosure` or - `DeclinedDiscoveryExcluded { matched_substring }` — two arms on the existing authority, not - a second status vocabulary. No Rust rescan stands beside the producer; gunbc#9685's - single-discovery-authority cut holds for the whole corpus, not only the prepared subject. The - gate-closure population keeps its own arm rather than folding into - `DeclinedOutsideRequiredGate`: the two are removed by different mechanisms, restored by - different triggers, differ by two orders of magnitude, and the first is the rung drop's subject. - - **The consequence, stated now:** the producer's per-file refusals — misplaced `test` decl, - barren sidecar, misplaced wire contract, malformed `live_tree_disposition` row — now stop the - required floor for ANY module under the source roots, not only one the gate closure admitted. - A real widening of what this lane refuses over, survivable today because the corpus carries - none of those violations (measured 2026-08-29: zero `test`-marked decls outside a `*_test.dag` - sidecar, zero barren sidecars); the first violation authored anywhere in the tree will red - this lane rather than the one owning the file. -2. **The partition is an identity join, not a count equality.** The old check was - `offered == routed + declined_long + declined_fixture + declined_outside_gate + - declined_cost_debt`, which DESIGN §5 names by shape: green over a projection that drops one - identity and writes another twice, since every count still agrees. It is now the terminal - ledger's own reconciliation, through the same function (`reconcile_identity_population`), - refusing as `FloorDispositionJoinInexact` with the offending identities named. Its calibration - pair is enrolled beside the terminal-ledger one. -3. **The counters are derived from the rows** instead of accumulated beside them, and duplicate - detection moved from the *planned* subset to the *whole declared* population — a duplicate - whose first site declined used to pass unnoticed. -4. **The artifact carries two axes, never folded.** The disposition TSV gains an `outcome` - column, joined from the terminal ledger through `claim_disposition`; an identity that never - ran reads `not_executed` — a statement, not a blank. - -### Two axes deliberately NOT built, and what each needs first - -Both were in the commissioning brief and both would have meant *authoring* the authority they -claim to join to (§3), so they are named as triggers rather than improvised: - -- **semantic producer / retire-with-producer.** No authority in the tree maps a witness identity - to the producer whose behavior it witnesses. Trigger: a modeled producer binding on the witness - carrier itself, at which point the join is a lookup rather than a classification. -- **the Rust `#[ignore]` roster.** A different universe with no roster authority and no identity - grain shared with `.dag` witnesses. Trigger: an enumerated, typed roster of ignored Rust tests - with a stated reason per row — the same shape `floor_cost_debt` already has for its population. - -### One inert wall found on the way - -`gunbc.discovery_census` claims, twice and in prose, that its wildcard-free matches make a new -`RequiredFloorDisposition` arm *fail to compile*. `DeclinedOutsideRequiredGate` had been added -without either match acquiring an arm, and nothing refused — the module's own witness is outside -the gate closure, so no executing path typechecks it. The arms are added here and the claim -restated at its honest rung, with the trigger recorded in the module. - -### A false specification selected out of the nonexecuting population (2026-09-02, gunbc#10092) - -The changed-witness sublane selected -`test.claim.discovery_census_witness.w_unrostered_sibling_in_the_same_module_is_planned`, an -identity the ordinary required floor did not execute. The claim called -`required_floor_site_disposition` with module path -`dag.test.claim.lifecycle_survivor_corpus_census` and asserted `Planned`, although that path -matches none of `required_gate_prefixes` and the function therefore answers -`DeclinedOutsideRequiredGate`. The specification was false and stayed green because it did not -run — this document's class at one identity, not a new failure class. - -Run `33656005986` is the discriminating receipt. Its identity-grain changed-witness ledger reports -the claim as `planned-without-terminal-verdict`, `outcome=failed`; the aggregate reports -`changed_witnesses=12 changed_witness_blocking=1 -changed_witness_declined_in_declared_nonexecuting_root=0`, so the row executed rather than being -declined again. The same run measured the containing universe as 15,383 declared identities, -3,499 routed and 11,277 declined outside the gate closure, and ended with exactly one semantic -failure and zero interrupted verdicts. Those population figures are bound to that run and tree: -declared, offered and routed populations move with the tree, so figures from another head are -neither approximations of nor superseded by this receipt. The repair renames the claim to -`w_unrostered_sibling_in_the_same_module_reaches_the_gate_decline` and asserts the disposition its -own input entails; direct claim execution then passes. The reusable fact is not the spelling fix: -selection of a normally nonexecuting identity is an executable falsifier for specifications that -ordinary floor green cannot adjudicate. diff --git a/docs/plans/workspace-control-plane.md b/docs/plans/workspace-control-plane.md deleted file mode 100644 index a29b6f8cce1..00000000000 --- a/docs/plans/workspace-control-plane.md +++ /dev/null @@ -1,179 +0,0 @@ -# Workspace control plane: off-rack, serverless, queue-driven - -Design for the off-rack control plane of the recoverable workspace. Status: **design only. Nothing here is built, and no store is minted.** - -The plan rows it serves live in the private `strategy.year_end_plan`: - -- `control-plane-state-store` (its recovery half) -- `volume-durability-contract` (the one global epoch authority) -- `checkpointed-workspace` -- `instance-host-evacuation` -- `instance-reconciliation` -- `instance-endpoint-continuity` - -The demo that exercises it is the private `docs/plans/2026-10-02-recoverable-workspace-demo.md`, stages 2–5. The state machine it realizes is the private `docs/plans/2026-09-25-fabric-architecture.md` §2 `resume(instance, target)`. The storage interface it stands on is [fabric-storage](fabric-storage.md), steps 4–5. - -Operator rulings taken as inputs, not reopened here: - -1. The off-rack control plane is serverless and low-QPS, with a queue. -2. Rack loss is a placement failure: restart elsewhere from the last committed revision. -3. Overflow is ordinary fabric placement by cost. Owned capacity has zero marginal cost and wins; OCI is the probable overflow target; work moves back when owned capacity returns. -4. R2 and B2 are fabric options. -5. CPU and memory are requested independently. -6. The operator is in the US northeast. - -Claims are stated as **requirement and confidence**, never as dates. Every external figure carries its standing in the sense of DESIGN §4d (`extdeps.external_authority` `CitedFigureStanding`). A figure marked *transcribed* was copied from the vendor page named beside it, at authoring time, and owes a re-read before any decision is priced on it. - -## 1. The state the control plane carries, and its existing homes - -The control plane holds five kinds of fact. Every one already has a home in the corpus. The design adds **realizations and placements of those homes, not new vocabulary** (DESIGN §3b conformance: each row below conforms or names its divergence). - -| State | Meaning | Home authority | Conformance | -| --- | --- | --- | --- | -| **Intent** | The desired instance: class, CPU and memory requested independently, volume quota, endpoint name, placement constraints. | Requirements: `product.fabric.work` `ExecutionRequirements`. The desired record is a `std.fabric_storage` object under a per-instance head. | Conforms. Intent is an immutable object; "the current intent" is a head. No second desired-state table. | -| **Writer epoch** | Which realization may advance the workspace head and serve the endpoint. A monotone integer per workspace. | `std.temporal_effect` `LeaseEpoch` (`generation` = epoch, `owner_fingerprint` = realization). **The epoch is not a separate record:** it is carried by the `WorkspaceFence` entries on the workspace head, in `std.checkpointed_workspace` (still-dove-673, branch `workspace-revision-model`). | Conforms. There is no second epoch store. Consumer: `std.checkpointed_workspace` `workspace_commit_classify` (§3). | -| **Committed revision head** | The last committed checkpoint of the workspace, as a chain of `WorkspaceRevision` and `WorkspaceFence` entries. | `std.fabric_storage` `FabricHeadReading` / `FabricHeadAdvance`, as **one head per workspace**, `workspace..committed`. Its R2 realization is fabric-storage step 4B (ETag-conditioned put), stacked on gunbc#13080 `gunbc.cloudflare.r2_staged_transfer`. | Conforms. No per-epoch head namespace: a per-epoch namespace would make a stale commit unreadable but still *acknowledged* (§3). | -| **Host lease** | Which host holds a realization, until when, and as observed. | `std.temporal_effect` `HeldLease` (`HeldLeaseObservedState`: `LeaseAbsent` / `LeaseRunningExpected` / `LeaseRunningStale` / `LeasePortForeign` / `LeaseInaccessible`). The cell hold is `gunbc.fabric_control_plane` `CellReservation` / `reserve_selected_cell` / `end_cell_hold` over `std.durable_exclusive_hold`. | Conforms. The reconciler's observation *is* `HeldLeaseObservedState`, so detection reuses its verdict fold (`held_lease_observed_verdict`, `plan_for_held_lease_observation`). | -| **Endpoint binding** | Which realization the workspace's stable name routes to, and at which epoch. | No consumed home yet: the private rows `fabric-network-contract` and `instance-endpoint-continuity` own it. | **Modeling obligation (§6), not a conformance row.** This design only requires that the binding carry the epoch it was bound at (§3). Consumer: the overlay's route programming, named in `instance-endpoint-continuity`. | -| **Credentials per epoch** | Scoped storage write credential and overlay identity for one realization. | `std.effect_grant` `Grant` / `Envelope` (verb `Write`, namespace position = the epoch's chunk prefix). The minting pattern follows `gunbc.auth.authorization_pattern_selection`. | Conforms. **This is a cost and blast-radius control, not the fence.** Chunk writes go under `workspace//e/`, so a revoked epoch-N grant stops a stale writer from spending money. Correctness never depends on revocation. | -| **Placement choice** | Where a realization goes and why. | `product.fabric.selection` `select_supply` → `std.decision` `SelectionReceipt`. | Conforms (§4). | - -No parallel store: intent, epochs, heads and leases are all **objects and heads in one `std.fabric_storage` realization**. The control-plane service that §5 chooses is that realization's *linearization point for heads*. It is not a second database beside it. - -## 2. The reconciler: level-triggered and idempotent - -The reconciler is one pure fold, `reconcile(desired, observed) -> List`. It is wrapped by one effectful step that reads, decides, applies **at most one CAS-guarded transition**, and returns. - -- **Level-triggered.** Its input is the whole current desired state and the whole current observation for one instance (heads plus `HeldLeaseObservedState` plus endpoint binding). It is never the event that woke it. A queue message carries only an instance id ("look at X"), so losing, duplicating or reordering messages costs latency and never correctness. A periodic sweep enqueues every instance, which bounds detection by the sweep period even if every edge-triggered message is lost. -- **Idempotent.** Each action is guarded by the expectation it was computed against: - - an epoch bump is a CAS from `generation = N` to `N+1`; - - a placement is a `CellReservation` keyed by `(instance, epoch)`, via `fabric_cell_allocation_key`; - - an endpoint rebind names its target epoch. - - A retried or duplicated run observes the transition already made and computes `Noop`. This is `ensure` (DESIGN §3d): Noop, Apply or Refuse over a caller-supplied requirement, with independent readback. Selection happens before it, never inside it. -- **One transition per run.** `resume` from fabric-architecture §2 is driven as a sequence of states, not one long transaction: - - `Placed(e) → Fencing(e→e+1) → Fenced(e+1) → Restoring(e+1, target) → Realized(e+1) → Bound(e+1)` - - The current state is a function of the heads, never a flag. Each step is one queue delivery. A worker that dies mid-step leaves the heads unchanged or advanced, and the next delivery continues from what it reads. The receipt chain is `std.temporal_effect` `EffectStepReceipt` / `plan_next_step_from_prior_receipt_and_lease`. Stall detection is `EffectStallBudget`, which escalates as a typed outcome rather than retrying forever (DESIGN §5: refuse, never widen). -- **Detection.** A host lease reads as `LeaseRunningStale` or `LeaseInaccessible` past the lease term. That reading plus the instance's intent "should be running" yields `Fence`. There is no failure detector beyond lease expiry. A partitioned host that still runs is handled by fencing (§3), not by trying to tell "dead" from "unreachable". That is why the demo's harder case, rack alive but unreachable, needs no special arm. - -## 3. Epoch fencing: a stale writer can neither commit nor receive traffic - -**Requirement:** after the epoch authority records epoch N+1 for a volume, no action by a holder of epoch N reaches durable state or a client. **Confidence:** high for commit; medium for traffic, until the endpoint binding has a home. - -**The fence is the same compare-and-set as the commit.** There is no separate epoch record read before the head advance. With "read record == N, then advance `e/head`", a writer fenced *between* the two steps commits after the bump. If restore had already read that head, the stale commit returns success to the guest, an acknowledged barrier, and is then silently lost. A per-epoch namespace makes such a commit unreadable, not unacknowledged, and that breaks the contract's strongest promise. - -The model, `std.checkpointed_workspace` (still-dove-673): - -1. **One head per workspace:** `workspace..committed`. -2. **A fence is a head entry.** An epoch transition is the reconciler advancing that head to a `WorkspaceFence { epoch: N+1, parent: }`. It does so through the same `FabricHeadAdvance` every writer uses, **before** any restore starts. -3. **Every commit expects the entry it built on.** A writer at epoch N advances with `ExpectHeadAt { object: }`. If a fence landed in between, the advance is `FabricHeadMoved`. `workspace_commit_classify` classifies that as `WorkspaceCommitStaleEpoch` (observed entry epoch > writer epoch), and the guest never sees success. The fence and the commit linearize on one compare-and-set, so the gap no longer exists. -4. **Attach** reads the head and refuses unless the newest fence names this realization's epoch. -5. **Restore** walks the chain from the head past any fences to the last `WorkspaceRevision`, then advances from there under the new epoch. -6. **Coverage invariant.** The contract (private `volume-durability-contract`) sells one arm, the asynchronous checkpointed workspace: guest `fsync` is acknowledged locally, and durability is the manifest commit. `oldest_uncommitted_write_age` stays under the published window. A commit refused for unreachable storage is not retried silently: once the oldest uncommitted write would exceed the window, the instance enters the **declared storage outage** and refuses writes. An idle workspace is never refused. A commit refused as `WorkspaceCommitStaleEpoch` makes the realization quarantine itself. - -Two defense-in-depth lines stand beside the fence; neither is the fence: - -- **Per-epoch credentials** (state table): revocation is best-effort at a provider and bounds cost and blast radius only. -- **Traffic follows the binding.** The endpoint binding names `(instance, epoch)`, and the overlay routes only to the realization carrying the bound epoch. Its per-epoch overlay credential is revoked at the fence. A returning rack (demo stage 5) finds its binding gone, its grants dead, and its next commit `WorkspaceCommitStaleEpoch`; it quarantines, and the instance is destroyed. - -**Can R2 conditional puts alone be the epoch authority? Yes.** The epoch authority *is* the workspace head, so R2 `If-Match` on that one object is the only linearization needed: commits and fences both use it, and creation is `If-None-Match: *`. Multi-provider placement does not break this, provided **B2, or any second backend, holds chunks only**, which are immutable and replicate freely. **The head stays in one place.** The fabric-architecture §2 hazard, a partitioned writer advancing a second provider's own head, arises only if a second provider holds a live head, so that is refused by design rather than fenced. - -The residual requirement is to verify R2's conditional put as a strong read-after-write CAS against the cited doc (https://developers.cloudflare.com/r2/api/s3/extensions/ and the conditional-operations reference). Confidence is medium until verified. - -A Durable Object is not needed for fencing. It stays as the fallback **only** if that verification fails, or if one transition must change the head and the endpoint binding atomically. - -## 4. Cost-based placement and move-back - -Placement is `product.fabric.selection` `select_supply` over a candidate field built from: - -- owned cells (`gunbc.fabric_control_plane` `fabric_cell_roster_candidates`) at zero marginal cost; -- overflow offers (OCI Ampere drafted; AWS Graviton the alternative), as priced `CandidateOffer`s. - -`ExecutionRequirements` carries CPU and memory as **independent** requirements. A target is admissible when both fit, never by a bundled shape. The result is a `SelectionReceipt`. A different field (rack lost, rack back) is a different decision, so staleness is mechanical (DESIGN §3d). - -- **Rack loss** removes every owned candidate from the field, and selection lands on overflow. Same `resume`, a different target adapter. -- **Move-back** is not a special path. The sweep re-runs selection for every running instance. When the receipt's selected candidate differs from the current placement and the cost delta exceeds the move cost (the restore transfer plus the endpoint downtime, as a `delay_valuation`), the reconciler plans a planned `resume` to the cheaper target. -- **Thrash guard:** a move is planned only when the delta covers the move cost over a declared hold horizon (`GrantDuration`). This is a selection policy input, not a timer in the reconciler. -- **Overflow quota** is a candidate's availability observation (`AvailabilityObservation`). An unverified quota is `SelectionNeedsEvidence`, never an assumed admit. This follows the demo's "overflow quota" risk. - -## 5. Realization choice - -The load is tiny: one record per instance, transitions on failure or move, and a sweep. The figures below decide which choices are admissible, not which is cheapest; at this QPS every option costs a few dollars a month. - -### (a) Cloudflare Workers + Durable Objects + Queues, beside R2 - -- **Consistency:** - - A Durable Object is a single-threaded actor with transactional, strongly consistent storage. One DO per instance (or per volume) makes every epoch transition serial by construction, and the CAS is a plain read-compare-write inside the object. - - Location hints (`enam`, eastern North America) place it near the operator. - - Docs: https://developers.cloudflare.com/durable-objects/ ; https://developers.cloudflare.com/durable-objects/reference/data-location/ -- **Queues:** - - Delivery is at-least-once, which is what a level-triggered reconciler wants. - - Consumers are Workers, and there are dead-letter queues. - - Docs: https://developers.cloudflare.com/queues/reference/delivery-guarantees/ -- **R2 is the same vendor and the same account.** The credential scope and the step 4B realization are already modeled in `extdeps.cloudflare.r2` / gunbc#13080. There is no egress charge from R2 (https://developers.cloudflare.com/r2/pricing/). -- **Price** (*transcribed*; owes a re-read of https://developers.cloudflare.com/workers/platform/pricing/, https://developers.cloudflare.com/durable-objects/platform/pricing/, https://developers.cloudflare.com/queues/platform/pricing/): - - Workers Paid is $5/month, which includes Workers, DO and Queues allowances: about 10M Worker requests, 1M DO requests and 1M Queue operations per month. - - This load sits inside the included tier. -- **Weakness:** - - A DO's storage lives in one location. A Cloudflare-wide or DO-wide outage takes the control plane down; see §6 for what survives. - - Single vendor for objects and control. That couples their failures, but either one down already stops commits, because the head lives in R2. - -### (b) AWS Lambda + DynamoDB conditional writes + SQS - -- **Consistency:** - - DynamoDB `PutItem`/`UpdateItem` with a `ConditionExpression` is a linearizable per-item CAS: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ConditionExpressions.html - - Strongly consistent reads within a region. Global tables are multi-region but **last-writer-wins**, so they are *not* a CAS across regions: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/V2globaltables_HowItWorks.html - - The fence therefore lives in one region (us-east-1 or us-east-2, for the operator). -- **SQS** standard queues are at-least-once; there is a Lambda event source and DLQs. Docs: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/standard-queues.html -- **Price** (*transcribed*; owes a re-read of https://aws.amazon.com/dynamodb/pricing/on-demand/, https://aws.amazon.com/lambda/pricing/, https://aws.amazon.com/sqs/pricing/): - - On-demand writes are on the order of a dollar per million (reduced in late 2024; re-read). - - Lambda's free tier is 1M requests per month, and SQS's is 1M requests per month. - - This load is near zero. -- **Weakness:** - - A second vendor, a second credential pattern (`gunbc.auth` would need an AWS WIF row) and a second extdeps surface to model, for no consistency gain over a DO. - - Egress from R2 is free, but every control-plane call crosses vendors. - - Its real advantage, independence from Cloudflare, is worth only what §6 says survives anyway. - -### Recommendation - -**Confidence:** medium. - -- **Phase 1 (minimal):** one head per workspace as an R2 object under step-4B conditional puts, carrying commits and fences alike (§3). One Cloudflare Worker is the reconciler, Cloudflare Queues carries "look at X", and a Cron Trigger runs the sweep. No DO and no new store. -- **Phase 2, only if needed:** a Durable Object per workspace holding the head. It is needed if R2's conditional-put semantics fail verification as a strong CAS, or if transitions need multi-key atomicity (epoch + binding in one step). - -Prefer (a) over (b): it puts the control plane on the same vendor whose object store is already the commit dependency. Its correlated failure adds no new outage mode beyond "R2 down", which already stops commits. - -## 6. When the control plane itself is down - -**Requirement:** a control-plane outage degrades to "no *changes* of placement", never to data loss, split brain or a silent wrong answer. - -| Still works | Stops | -| --- | --- | -| Running instances keep running and serving at their bound endpoint (bindings are programmed into the overlay, not looked up per packet). | Detection and recovery: a host that dies during the outage is not re-placed until the control plane returns. Stated in the guarantee set as a compound outage, never hidden. | -| Manifest commits continue **iff** R2 is up: the commit *is* the head compare-and-set and needs no reconciler. | With R2 unreachable, manifest commits refuse. `oldest_uncommitted_write_age` then grows, and at the window the instance enters the declared storage outage. Commits never proceed unchecked. | -| — | A host lost during that outage loses its pending writes. That is receipted as the contract's typed **compound failure** (sole volatile copy lost while coverage could not be kept), never as an ordinary recovery. | -| Restore by hand is possible from the last committed head, because heads and objects are plain storage. | Move-back and overflow placement. Cost drift accumulates; correctness does not. | -| Nothing can split brain: no actor except the reconciler bumps epochs, and it is down. | New instance creation. | - -The rule is the fabric-storage rule applied one layer up: **an unreachable linearization point refuses, it does not fail over.** There is no "proceed without the fence" arm (DESIGN §5, no escape hatches). - -## 7. Consumers and frontier - -Every model named here is consumed by a named row; none lands before its consumer: - -- **Fence-as-head-entry and stale-commit classification:** `std.checkpointed_workspace` `workspace_commit_classify` (still-dove-673). Rows: `checkpointed-workspace`, `instance-reconciliation`, `volume-durability-contract` (demo stages 1 and 5). -- **Per-epoch chunk credential prefix:** the storage grant minting under `instance-reconciliation`. -- **Reconciler fold over `HeldLeaseObservedState`** → `instance-host-evacuation` (demo stage 2). -- **Epoch-carrying endpoint binding** → `instance-endpoint-continuity` (demo stages 2, 5). -- **Overflow candidates in `select_supply`** → `alternate-target-adapter` (demo stage 4). - -## 8. Open decisions (operator) - -1. **Realization:** (a) Cloudflare Workers + Queues (+ DO later), recommended; or (b) AWS Lambda + DynamoDB + SQS. -2. **Epoch authority.** The recommendation is the workspace head itself, on R2 `If-Match`. This is minimal, but R2's availability bounds every commit. The alternative is a Durable Object holding the head. Gated on verifying R2's conditional put as a strong CAS. -3. **Where the epoch is checked:** settled by the model, not open. Fences and commits are the one head CAS; attach reads the head. Nothing is checked on guest `fsync`. (Kept as a numbered item so the list keeps its numbering.) -4. **Sweep period and lease term.** Together they bound detection time, and the demo receipts it. Drafted as same order as the published 60 s window, pending stage 0. -5. **Move-back hold horizon:** how long a cheaper owned slot must be expected to persist before a planned move is worth its downtime. -6. **Second storage provider (B2) timing.** B2 can join as a chunk replica at any time. It never holds a live head, so promotion means moving the one head, which is a planned operator action and not a failover.