From dcb19ffe5db060b42aed10ebf0ad4cad2ab68ee3 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 03:19:16 +0000 Subject: [PATCH 01/12] roadmap belt: the three silent read collapses become typed refusals (reds first) - belt_occurrence_record_for_attempt: an unreadable launch record returned [] and the occurrence census lost the attempt silently. The per-attempt read is now a typed outcome (AttemptOccurrenceRecordRead: Decoded | Refused with the cause naming the attempt), and one unreadable attempt refuses the whole census (BeltAttemptOccurrenceRecords) instead of shrinking it -- the refusal rides the existing OccurrencePopulationRefused channel with the reason. - belt_workflow_attempt_evidence_for_ref and the publish gate: a failed current-attempt-key pointer read claimed 'this attempt has no modeled state pointer' / 'this node has no current attempt' -- absences the read never established. The pointer read folds through filesystem_read_outcome and the two facts split (WorkflowModeledStatePointerState: Absent | Unreadable{cause}); absent keeps today's text, unreadable says the pointer could not be read and what is therefore unknown. - Excluded as argued_collapse (owner's argument, not converted): attempt_launch_revision_hex in roadmap_served_observation -- stale_attempt_nodes dims nothing for any failure and for empty alike, and the presentation witness pins the empty wire for an unreadable record. --- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 284 ++++++++++++++------- 1 file changed, 194 insertions(+), 90 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index 5a18ede4724..b1fe331f2d0 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -562,6 +562,9 @@ import extdeps.filesystem.filesystem_io { filesystem_listing_observation, filesystem_listing_names_entry, filesystem_entry_presence, + FilesystemReadOutcome, + FilesystemReadRefused, + FilesystemReadSucceeded, filesystem_read_outcome, filesystem_file_observation, } @@ -4951,47 +4954,89 @@ fn belt_occurrence_unit_row(unit: String) -> ObservedContainerRow { // attempt whose identity cannot be read produces no record, so a live container over it joins // nothing and reads Unclassified -- visible, occupying, never reaped -- rather than borrowing // today's selection as its meaning. -fn belt_occurrence_record_for_attempt(instance: HostDashboardInstance, attempt: DispatchAttemptRef) -> List { +// The attempt's durable occurrence is a typed read outcome, never a silently emptied list: before +// the fold conversion, an unreadable launch record returned [] and the occurrence census lost the +// attempt without a word. A refusal here reaches the census as a refusal naming the attempt, the +// same way an unobservable attempt population already does. +type AttemptOccurrenceRecordRead + = AttemptOccurrenceRecordDecoded { record: DurableAttemptRecord } + | AttemptOccurrenceRecordRefused { reason: NonEmptyStr } + +fn belt_occurrence_record_from_launch_read(attempt: DispatchAttemptRef, read: FilesystemReadOutcome) -> AttemptOccurrenceRecordRead { + match read { + FilesystemReadRefused { error } => + AttemptOccurrenceRecordRefused { reason: join(["attempt ", attempt.attempt_key, ": the launch record could not be read, so its durable occurrence is unknown: ", error], "") } + FilesystemReadSucceeded { content } => + match parse_json_document(s: content) { + JsonDocumentUnreadable { gap } => + AttemptOccurrenceRecordRefused { reason: join(["attempt ", attempt.attempt_key, ": the launch record does not decode as JSON: ", gap], "") } + JsonDocumentParsed { value: doc } => + match launch_identity_of_json(doc: doc) { + LaunchIdentityUnreadable { reason } => + AttemptOccurrenceRecordRefused { reason: join(["attempt ", attempt.attempt_key, ": the launch record carries no readable launch identity: ", reason], "") } + LaunchIdentityDecoded { identity } => + AttemptOccurrenceRecordDecoded { + record: DurableAttemptRecord { + node: attempt.node_id as String, + attempt: attempt.attempt_key, + spawn_identity: join([ + "cause=", launch_cause_label(c: identity.cause), + " mode=", spawn_mode_label(m: identity.mode), + " instance=", identity.instance_id as String, + " revision=", git_object_id_wire_hex(oid: identity.revision) as String, + ], ""), + }, + } + } + } + } +} + +fn belt_occurrence_record_for_attempt(instance: HostDashboardInstance, attempt: DispatchAttemptRef) -> AttemptOccurrenceRecordRead { let path = dispatch_attempt_launch_path_for_instance( instance: instance, node_id: attempt.node_id as gunbc.roadmap_model.RoadmapNodeId, attempt_key: attempt.attempt_key, ) let read = Filesystem.Read(path: path) - if !read.success { - [] - } else { - match parse_json_document(s: read.content) { - JsonDocumentUnreadable { gap: _ } => [] - JsonDocumentParsed { value: doc } => - match launch_identity_of_json(doc: doc) { - LaunchIdentityUnreadable { reason: _ } => [] - LaunchIdentityDecoded { identity } => - [DurableAttemptRecord { - node: attempt.node_id as String, - attempt: attempt.attempt_key, - spawn_identity: join([ - "cause=", launch_cause_label(c: identity.cause), - " mode=", spawn_mode_label(m: identity.mode), - " instance=", identity.instance_id as String, - " revision=", git_object_id_wire_hex(oid: identity.revision) as String, - ], ""), - }] - } - } - } + belt_occurrence_record_from_launch_read( + attempt: attempt, + read: filesystem_read_outcome(content: read.content, success: read.success, error: read.error), + ) } -fn belt_occurrence_records_for_instance(instance: HostDashboardInstance) -> List? { +// One unreadable attempt refuses the whole census rather than shrinking it silently: a census with +// a hole is not a census. The refusal names every attempt whose record could not be read. +type BeltAttemptOccurrenceRecords + = BeltAttemptOccurrenceRecordsObserved { records: List } + | BeltAttemptOccurrenceRecordsRefused { reason: NonEmptyStr } + +fn belt_occurrence_records_for_instance(instance: HostDashboardInstance) -> BeltAttemptOccurrenceRecords { match belt_attempts_observe_for_instance(instance: instance) { - AttemptsRefused { reason } => none - AttemptsObserved { attempts } => - Present { - value: attempts |> flat_map(a => belt_occurrence_record_for_attempt(instance: instance, attempt: a)), + AttemptsRefused { reason } => + BeltAttemptOccurrenceRecordsRefused { reason: join(["the attempt population is unobservable: ", reason], "") } + AttemptsObserved { attempts } => { + let outcomes = map(attempts, a => belt_occurrence_record_for_attempt(instance: instance, attempt: a)) + let refusal_reasons = fold(outcomes, init: [], f: (acc, o) => match o { + AttemptOccurrenceRecordRefused { reason } => concat(acc, [reason as String]) + AttemptOccurrenceRecordDecoded { record: _ } => acc + }) + if any(outcomes, o => match o { + AttemptOccurrenceRecordRefused { reason: _ } => true + AttemptOccurrenceRecordDecoded { record: _ } => false + }) { + BeltAttemptOccurrenceRecordsRefused { reason: join(refusal_reasons, "; ") as NonEmptyStr } + } else { + BeltAttemptOccurrenceRecordsObserved { + records: flat_map(outcomes, o => match o { + AttemptOccurrenceRecordDecoded { record: r } => [r] + AttemptOccurrenceRecordRefused { reason: _ } => [] + }), + } } + } } } - fn belt_attempt_occurrences_observe_for_instance(instance: HostDashboardInstance) -> BeltOccurrencePopulation { match belt_sessions_observe_for_instance(instance: instance) { ObserveRefused { reason } => @@ -5002,9 +5047,9 @@ fn belt_attempt_occurrences_observe_for_instance(instance: HostDashboardInstance OccurrencePopulationRefused { reason: "the user manager's gunbc unit population is unobservable; the census over half the population is unknowable" } Present { value: unit_names } => match belt_occurrence_records_for_instance(instance: instance) { - Absent => - OccurrencePopulationRefused { reason: "the durable attempt records are unobservable; occurrence identity cannot be joined" } - Present { value: records } => + BeltAttemptOccurrenceRecordsRefused { reason } => + OccurrencePopulationRefused { reason: join(["the durable attempt records are unobservable; occurrence identity cannot be joined: ", reason], "") } + BeltAttemptOccurrenceRecordsObserved { records } => OccurrencePopulationObserved { occurrences: occurrence_population( containers: concat( @@ -5445,6 +5490,86 @@ fn belt_verification_receipt_source( } } +// The modeled-state pointer read is a typed outcome, and the two failure facts are kept apart: a +// pointer that is established absent means this attempt has no modeled state; a pointer that could +// not be read means WHETHER the attempt has modeled state is unknown. Collapsing them -- the old +// `!read.success || trim(content) == ""` -- made an unreadable pointer claim "has no modeled state +// pointer", an absence the read never established. +type WorkflowModeledStatePointerState + = WorkflowModeledStatePointerAbsent + | WorkflowModeledStatePointerUnreadable { cause: NonEmptyStr } + +fn belt_workflow_attempt_evidence_without_modeled_state( + instance: HostDashboardInstance, + panes: BeltAttemptPanes, + attempt: DispatchAttemptRef, + pointer_state: WorkflowModeledStatePointerState, +) -> WorkflowAttemptEvidence { + let pointer_reason = match pointer_state { + WorkflowModeledStatePointerAbsent => + "this attempt has no modeled state pointer, so neither its attempt key nor its head is established" + WorkflowModeledStatePointerUnreadable { cause } => + join(["this attempt's current-attempt-key pointer could not be read, so neither its attempt key nor its head is established: ", cause], "") + } + let worktree_cause = match pointer_state { + WorkflowModeledStatePointerAbsent => + "this attempt has no modeled state pointer, so no attempt key names its worktree" + WorkflowModeledStatePointerUnreadable { cause } => + join(["this attempt's current-attempt-key pointer could not be read, so whether an attempt key names its worktree is unknown: ", cause], "") + } + let placement_detail = match pointer_state { + WorkflowModeledStatePointerAbsent => + "this attempt has no modeled state pointer, so no attempt key names its session placement" + WorkflowModeledStatePointerUnreadable { cause } => + join(["this attempt's current-attempt-key pointer could not be read, so whether an attempt key names its session placement is unknown: ", cause], "") + } + let provider_events_error = match pointer_state { + WorkflowModeledStatePointerAbsent => "" + WorkflowModeledStatePointerUnreadable { cause } => cause as String + } + WorkflowAttemptEvidence { + node_id: attempt.node_id, + attempt_key: attempt.attempt_key, + branch: attempt.branch, + modeled_state_present: false, + worktree: AttemptWorktreeUnobserved { path: "", cause: worktree_cause }, + admission_receipt_readable: false, + admission_receipt: "", + spawn_failure_present: false, + spawn_failure: "", + provider_events_readable: false, + provider_events: "", + provider_events_error: provider_events_error, + process: attempt_process_for_node( + panes: panes, + node_id: attempt.node_id, + attempt_key: attempt.attempt_key, + ), + validation_summary: belt_validation_summary_for_node( + instance: instance, + node_id: attempt.node_id, + ), + verification_subject: VerificationSubjectUnobserved { + reason: pointer_reason, + }, + verification_selection: SelectionAbsent, + verification_source: VerificationReceiptAbsent, + publication_subject: PublicationSubjectUnobserved { + reason: pointer_reason, + }, + publication_source: ReceiptSourceAbsent, + submission_capture: CaptureUnreadable { + reason: pointer_reason, + }, + integration: IntegrationReceiptUnreadable { + reason: pointer_reason, + }, + review: review_report_absent(reason: pointer_reason), + goal_audit: [], + session_placement: SessionPlacementUnreadable { unit: "", detail: placement_detail }, + } +} + fn belt_workflow_attempt_evidence_for_ref( instance: HostDashboardInstance, panes: BeltAttemptPanes, @@ -5456,57 +5581,28 @@ fn belt_workflow_attempt_evidence_for_ref( node_id: attempt.node_id as gunbc.roadmap_model.RoadmapNodeId, ), ) - if !key_read.success || trim(key_read.content) == "" { - WorkflowAttemptEvidence { - node_id: attempt.node_id, - attempt_key: attempt.attempt_key, - branch: attempt.branch, - modeled_state_present: false, - worktree: AttemptWorktreeUnobserved { path: "", cause: "this attempt has no modeled state pointer, so no attempt key names its worktree" }, - admission_receipt_readable: false, - admission_receipt: "", - spawn_failure_present: false, - spawn_failure: "", - provider_events_readable: false, - provider_events: "", - provider_events_error: key_read.error, - process: attempt_process_for_node( - panes: panes, - node_id: attempt.node_id, - attempt_key: attempt.attempt_key, - ), - validation_summary: belt_validation_summary_for_node( + match filesystem_read_outcome(content: key_read.content, success: key_read.success, error: key_read.error) { + FilesystemReadRefused { error } => + belt_workflow_attempt_evidence_without_modeled_state( instance: instance, - node_id: attempt.node_id, - ), - verification_subject: VerificationSubjectUnobserved { - reason: "this attempt has no modeled state pointer, so neither its attempt key nor its head is established", - }, - verification_selection: SelectionAbsent, - verification_source: VerificationReceiptAbsent, - publication_subject: PublicationSubjectUnobserved { - reason: "this attempt has no modeled state pointer, so neither its attempt key nor its head is established", - }, - publication_source: ReceiptSourceAbsent, - submission_capture: CaptureUnreadable { - reason: "this attempt has no modeled state pointer, so neither its attempt key nor its head is established", - }, - integration: IntegrationReceiptUnreadable { - reason: "this attempt has no modeled state pointer, so neither its attempt key nor its head is established", - }, - review: review_report_absent(reason: "this attempt has no modeled state pointer, so neither its attempt key nor its head is established"), - goal_audit: [], - session_placement: SessionPlacementUnreadable { unit: "", detail: "this attempt has no modeled state pointer, so no attempt key names its session placement" }, - } - } else { - belt_workflow_attempt_evidence_for_key(instance: instance, panes: panes, attempt: attempt, current_key: trim(key_read.content)) + panes: panes, + attempt: attempt, + pointer_state: WorkflowModeledStatePointerUnreadable { cause: error as NonEmptyStr }, + ) + FilesystemReadSucceeded { content } => + if trim(content) == "" { + belt_workflow_attempt_evidence_without_modeled_state( + instance: instance, + panes: panes, + attempt: attempt, + pointer_state: WorkflowModeledStatePointerAbsent, + ) + } else { + belt_workflow_attempt_evidence_for_key(instance: instance, panes: panes, attempt: attempt, current_key: trim(content)) + } } } -// THE ATTEMPT-VARYING RECORDS OF ONE NAMED ATTEMPT, addressed by its key -- the one place the -// evidence builder's admission, provider-event and spawn-failure reads are named, so a witness can -// establish that a historical attempt's reads name that attempt and never the node's current -// projection. type BeltAttemptRecordPaths { admission: String events: String @@ -11522,17 +11618,25 @@ fn belt_publish_node_for_instance( node_id: node_id as gunbc.roadmap_model.RoadmapNodeId, ), ) - if !key_read.success || trim(key_read.content) == "" { - PublishDeferred { - node_id: node_id, - reason: "this node has no current attempt, so there is no branch or head for publication to be about", - } - } else { - belt_publish_attempt_for_instance( - instance: instance, - node_id: node_id, - attempt_key: trim(key_read.content), - ) + match filesystem_read_outcome(content: key_read.content, success: key_read.success, error: key_read.error) { + FilesystemReadRefused { error } => + PublishDeferred { + node_id: node_id, + reason: join(["the node's current-attempt-key pointer could not be read, so whether the node has a current attempt is unknown: ", error], ""), + } + FilesystemReadSucceeded { content } => + if trim(content) == "" { + PublishDeferred { + node_id: node_id, + reason: "this node has no current attempt, so there is no branch or head for publication to be about", + } + } else { + belt_publish_attempt_for_instance( + instance: instance, + node_id: node_id, + attempt_key: trim(content), + ) + } } } } From ff4236f19076b055d78014ca9b897da07fb40708 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 06:16:08 +0000 Subject: [PATCH 02/12] roadmap: the honest-but-unfolded read sites fold through the shared fold Every remaining mechanical site read individually, each feeding an existing typed local outcome, reason texts preserved verbatim except where the host error is quoted (the fold's Refused error replaces read.error): - belt_actuate: submission artifact, capture-roster fold, capture-for-head, spawn origin, worktree-bind fold, integration receipt, validation oracle (classifier re-signature to take FilesystemReadOutcome, shared with closing_contract_authoring), claude preflight (decision re-signature; witness calls construct fold outcomes), belt tick receipt (old classifier TEXT-MATCHED host errors 'No such file'/'not found' -- deleted; the wet path classifies a filesystem_file_observation instead, absence established from the listing; witness drives both facts through the owner's producers). - served_observation: same observation conversion (old classifier text-matched host errors -- deleted; witness drives absence through the producers). - acceptance_history_carrier, publication_helper (both sites), closing_contract_authoring: fold + match. - event_carrier: the operation-receipt flow no longer compares receipt.error_kind to 'not_found' -- the observation authority establishes absence (proceed), listed-but-unreadable refuses (whether the operation ran is unknown), readable decodes; the committed-event read folds too. - Parked for a wire-shape ruling: belt_workflow_attempt_evidence_for_key's admission and spawn_failure reads feed Bool+String evidence fields with no cause slot; converting them honestly needs an evidence-shape change. - Excluded as argued_collapse: belt_read_or_empty (absence row owns it), attempt_launch_revision_hex (presentation witness pins the empty wire). --- .../roadmap_acceptance_history_carrier.dag | 21 +- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 219 +++++++++--------- .../roadmap_closing_contract_authoring.dag | 9 +- dag/gunbc/roadmap/roadmap_event_carrier.dag | 97 ++++++-- .../roadmap/roadmap_publication_helper.dag | 59 +++-- .../roadmap/roadmap_served_observation.dag | 39 +++- .../roadmap/roadmap_validation_oracle.dag | 78 ++++--- .../roadmap_belt_actuate_witness_test.dag | 37 ++- ...oadmap_served_observation_witness_test.dag | 23 +- 9 files changed, 353 insertions(+), 229 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_acceptance_history_carrier.dag b/dag/gunbc/roadmap/roadmap_acceptance_history_carrier.dag index d03766bc8ea..ee8c7671ccf 100644 --- a/dag/gunbc/roadmap/roadmap_acceptance_history_carrier.dag +++ b/dag/gunbc/roadmap/roadmap_acceptance_history_carrier.dag @@ -1,7 +1,13 @@ module gunbc.roadmap_acceptance_history_carrier import std.types { List } -import extdeps.filesystem.filesystem_io { Filesystem } +import extdeps.filesystem.filesystem_io { + Filesystem, + FilesystemReadOutcome, + FilesystemReadRefused, + FilesystemReadSucceeded, + filesystem_read_outcome, +} import gunbc.roadmap_model { RoadmapAcceptanceEvent } import std.decl_ref { DeclarationRef, WholeDeclaration } import std.disposition { Disposition, RealizationDispatch, Scaffold } @@ -33,12 +39,13 @@ fn load_roadmap_acceptance_event_history_from_carrier_text(text: String) -> Road fn load_roadmap_acceptance_event_history(path: String) -> RoadmapAcceptanceEventHistoryLoad { let read = Filesystem.Read(path: path) - if !read.success { - RoadmapAcceptanceEventHistoryLoadRefused { - detail: concat("carrier read refused: ", read.error), - } - } else { - load_roadmap_acceptance_event_history_from_carrier_text(text: read.content) + match filesystem_read_outcome(content: read.content, success: read.success, error: read.error) { + FilesystemReadRefused { error } => + RoadmapAcceptanceEventHistoryLoadRefused { + detail: concat("carrier read refused: ", error), + } + FilesystemReadSucceeded { content } => + load_roadmap_acceptance_event_history_from_carrier_text(text: content) } } diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index b1fe331f2d0..3d4e3a16afe 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -559,6 +559,7 @@ import extdeps.filesystem.filesystem_io { FilesystemFileIndeterminate, FilesystemFileObservationsDisagree, FilesystemFileSubjectRefused, + filesystem_file_observation_of_path, filesystem_listing_observation, filesystem_listing_names_entry, filesystem_entry_presence, @@ -989,9 +990,7 @@ fn belt_oracle_observe_in_worktree( ) validation_oracle_observe_from_source( oracle: oracle, - source_readable: read.success, - source: read.content, - read_error: read.error, + source_read: filesystem_read_outcome(content: read.content, success: read.success, error: read.error), ) } @@ -1594,8 +1593,7 @@ fn belt_claude_preflight_decision( claude_on_path: Bool, home: String, trust_store_path: String, - trust_store_readable: Bool, - trust_store_text: String, + trust_store_read: FilesystemReadOutcome, trust_root: String, ) -> BeltClaudePreflight { if !claude_on_path { @@ -1608,24 +1606,29 @@ fn belt_claude_preflight_decision( step: "claude-trust-home", detail: "HOME unset or empty — cannot locate the claude trust store (~/.claude.json), so the workspace-trust seed is unknowable; fail-closed", } - } else if !trust_store_readable { - ClaudePreflightRefused { - step: "claude-trust-store", - detail: concat( - concat("claude trust store unreadable at ", trust_store_path), - " — claude blocks on its workspace-trust dialog for an unseeded root; seed it by launching claude once under the dispatch worktree root, or converge via the live_deploy membership", - ), - } - } else if !claude_workspace_trust_seeded(config_text: trust_store_text, project_path: trust_root) { - ClaudePreflightRefused { - step: "claude-trust-seed", - detail: concat( - concat(concat("workspace trust not seeded for ", trust_root), " in "), - concat(trust_store_path, " (projects entry with hasTrustDialogAccepted: true) — claude would block interactively on its trust dialog; ancestor-dir trust inheritance means seeding this ONE root covers every dispatch worktree"), - ), - } } else { - ClaudePreflightOk + match trust_store_read { + FilesystemReadRefused { error: _ } => + ClaudePreflightRefused { + step: "claude-trust-store", + detail: concat( + concat("claude trust store unreadable at ", trust_store_path), + " — claude blocks on its workspace-trust dialog for an unseeded root; seed it by launching claude once under the dispatch worktree root, or converge via the live_deploy membership", + ), + } + FilesystemReadSucceeded { content: trust_store_text } => + if !claude_workspace_trust_seeded(config_text: trust_store_text, project_path: trust_root) { + ClaudePreflightRefused { + step: "claude-trust-seed", + detail: concat( + concat(concat("workspace trust not seeded for ", trust_root), " in "), + concat(trust_store_path, " (projects entry with hasTrustDialogAccepted: true) — claude would block interactively on its trust dialog; ancestor-dir trust inheritance means seeding this ONE root covers every dispatch worktree"), + ), + } + } else { + ClaudePreflightOk + } + } } } @@ -1640,8 +1643,7 @@ fn belt_claude_preflight_for_instance(instance: HostDashboardInstance) -> BeltCl claude_on_path: claude_ok, home: home, trust_store_path: "", - trust_store_readable: false, - trust_store_text: "", + trust_store_read: filesystem_read_outcome(content: "", success: false, error: "unused on refusal"), trust_root: dashboard_instance_dispatch_worktree_root(instance: instance) as String, ) } else { @@ -1651,8 +1653,7 @@ fn belt_claude_preflight_for_instance(instance: HostDashboardInstance) -> BeltCl claude_on_path: claude_ok, home: home, trust_store_path: path, - trust_store_readable: r.success, - trust_store_text: r.content, + trust_store_read: filesystem_read_outcome(content: r.content, success: r.success, error: r.error), trust_root: dashboard_instance_dispatch_worktree_root(instance: instance) as String, ) } @@ -3597,37 +3598,41 @@ fn belt_pass_outcome_from_member(doc: JsonValue, key: String) -> BeltPassOutcome } } -fn belt_tick_receipt_read_from_filesystem( - success: Bool, - content: String, - error: String, -) -> BeltTickReceiptRead { - if success { - if trim(content) == "" { - BeltTickReceiptAbsent - } else { - belt_tick_receipt_decode(raw: content) - } - } else if string_contains(s: error, pattern: "No such file") - || string_contains(s: error, pattern: "not found") { - BeltTickReceiptAbsent - } else { - BeltTickReceiptUnreadable { - reason: join(["belt tick receipt could not be read: ", error], ""), - } +// The tick receipt read classifies an OBSERVATION, not a host error string: the old classifier +// text-matched the host's error ("No such file", "not found") to decide absent versus unreadable, +// which breaks on every host phrasing and every error-transport change. The observation authority +// establishes absence from the listing instead, and the host error rides inside the cause text. +fn belt_tick_receipt_read_from_observation(observation: FilesystemFileObservation) -> BeltTickReceiptRead { + match observation { + FilesystemFileAbsent(_) => BeltTickReceiptAbsent + FilesystemFileRead { path: _, content } => + if trim(content) == "" { + BeltTickReceiptAbsent + } else { + belt_tick_receipt_decode(raw: content) + } + FilesystemFileIndeterminate { cause } => + BeltTickReceiptUnreadable { + reason: join(["belt tick receipt could not be read: ", cause], ""), + } + FilesystemFileObservationsDisagree { path: _, cause } => + BeltTickReceiptUnreadable { + reason: join(["belt tick receipt could not be read: ", cause], ""), + } + FilesystemFileSubjectRefused { directory: _, name: _, cause } => + BeltTickReceiptUnreadable { + reason: join(["belt tick receipt could not be read: ", cause], ""), + } } } fn belt_tick_receipt_read_for_instance( instance: HostDashboardInstance, ) -> BeltTickReceiptRead { - let read = Filesystem.Read( - path: belt_tick_receipt_path_for_instance(instance: instance), - ) - belt_tick_receipt_read_from_filesystem( - success: read.success, - content: read.content, - error: read.error, + belt_tick_receipt_read_from_observation( + observation: filesystem_file_observation_of_path( + path: belt_tick_receipt_path_for_instance(instance: instance), + ), ) } @@ -5944,15 +5949,16 @@ fn belt_submission_observe(worktree: String) -> BeltSubmissionRead { BeltSubmissionObserved { observation: SubmissionAbsent } } else { let read = Filesystem.Read(path: join([worktree, "/", submission_artifact_name as String], "")) - if !read.success { - BeltSubmissionUnobservable { - reason: join([ - "the submission artifact was listed and then could not be read, so whether it declares a candidate is unknown: ", - read.error, - ], ""), - } - } else { - match submission_decode(text: read.content) { + match filesystem_read_outcome(content: read.content, success: read.success, error: read.error) { + FilesystemReadRefused { error } => + BeltSubmissionUnobservable { + reason: join([ + "the submission artifact was listed and then could not be read, so whether it declares a candidate is unknown: ", + error, + ], ""), + } + FilesystemReadSucceeded { content } => + match submission_decode(text: content) { SubmissionNotDecodable { reason } => BeltSubmissionObserved { observation: SubmissionUndecodable { reason: reason } } SubmissionDecoded { submission } => @@ -6034,15 +6040,16 @@ fn belt_submission_captures_observe(instance: HostDashboardInstance, worktree: S CaptureRosterUnobservable { reason: _ } => acc CaptureRosterObserved { captures } => { let read = Filesystem.Read(path: join([dir, "/", trim(entry)], "")) - if !read.success { - CaptureRosterUnobservable { - reason: join([ - "a submission capture record was listed and then could not be read: ", - trim(entry), " (", read.error, ")", - ], ""), - } - } else { - match submission_capture_decode(text: read.content) { + match filesystem_read_outcome(content: read.content, success: read.success, error: read.error) { + FilesystemReadRefused { error } => + CaptureRosterUnobservable { + reason: join([ + "a submission capture record was listed and then could not be read: ", + trim(entry), " (", error, ")", + ], ""), + } + FilesystemReadSucceeded { content } => + match submission_capture_decode(text: content) { CaptureNotDecodable { reason } => CaptureRosterUnobservable { reason: join(["the submission capture record ", trim(entry), " does not decode: ", reason], ""), @@ -6088,12 +6095,13 @@ fn belt_submission_capture_for_head(instance: HostDashboardInstance, worktree: S CaptureAbsent } else { let read = Filesystem.Read(path: join([dir, "/", submission_capture_basename(head_sha: head_sha)], "")) - if !read.success { - CaptureUnreadable { - reason: join(["the submission capture for this head was listed and then could not be read: ", read.error], ""), - } - } else { - match submission_capture_decode(text: read.content) { + match filesystem_read_outcome(content: read.content, success: read.success, error: read.error) { + FilesystemReadRefused { error } => + CaptureUnreadable { + reason: join(["the submission capture for this head was listed and then could not be read: ", error], ""), + } + FilesystemReadSucceeded { content } => + match submission_capture_decode(text: content) { CaptureNotDecodable { reason } => CaptureUnreadable { reason: join(["the submission capture at this head's own address does not decode: ", reason], ""), @@ -6303,15 +6311,16 @@ fn belt_scm_base_sha_observe(instance: HostDashboardInstance, node_id: RoadmapNo attempt_key: attempt_key, ) let read = Filesystem.Read(path: path) - if !read.success { - BaseShaUnobserved { - reason: join([ - "the attempt's spawn origin could not be read, so the intake base is unknown: ", - path, " (", read.error, ") — the project base must freeze the recorded dispatch base", - ], ""), - } - } else { - match parse_json_document(s: read.content) { + match filesystem_read_outcome(content: read.content, success: read.success, error: read.error) { + FilesystemReadRefused { error } => + BaseShaUnobserved { + reason: join([ + "the attempt's spawn origin could not be read, so the intake base is unknown: ", + path, " (", error, ") — the project base must freeze the recorded dispatch base", + ], ""), + } + FilesystemReadSucceeded { content } => + match parse_json_document(s: content) { JsonDocumentUnreadable { gap: _ } => BaseShaUnobserved { reason: join(["the spawn origin at ", path, " is not JSON"], "") } JsonDocumentParsed { value: doc } => @@ -6664,20 +6673,21 @@ fn belt_scm_delta_fold_step( } WorktreeBind { path } => { let read = Filesystem.Read(path: join([worktree, "/", path as String], "")) - if !read.success { - BeltDeltaFold { - store: acc.store, - unique: acc.unique, - refused: Present { - value: join([ - "the changed path ", path as String, - " could not be read from the worktree (", read.error, - "), so the tree cannot be captured whole", - ], ""), - }, - } - } else { - match store_authored_source(store: acc.store, text: read.content) { + match filesystem_read_outcome(content: read.content, success: read.success, error: read.error) { + FilesystemReadRefused { error } => + BeltDeltaFold { + store: acc.store, + unique: acc.unique, + refused: Present { + value: join([ + "the changed path ", path as String, + " could not be read from the worktree (", error, + "), so the tree cannot be captured whole", + ], ""), + }, + } + FilesystemReadSucceeded { content } => + match store_authored_source(store: acc.store, text: content) { SourceLocatorCollision { identity: _, existing: _, incoming: _ } => BeltDeltaFold { store: acc.store, @@ -10329,12 +10339,13 @@ fn belt_integration_receipt_observe( IntegrationReceiptAbsent } else { let read = Filesystem.Read(path: join([dir, "/", head_sha, ".json"], "")) - if !read.success { - IntegrationReceiptUnreadable { - reason: join(["the integration receipt for this head was listed and then could not be read: ", read.error], ""), - } - } else { - match integration_receipt_decode(text: read.content) { + match filesystem_read_outcome(content: read.content, success: read.success, error: read.error) { + FilesystemReadRefused { error } => + IntegrationReceiptUnreadable { + reason: join(["the integration receipt for this head was listed and then could not be read: ", error], ""), + } + FilesystemReadSucceeded { content } => + match integration_receipt_decode(text: content) { IntegrationReceiptNotDecodable { reason } => IntegrationReceiptUnreadable { reason: join(["the integration receipt at this head's own address does not decode: ", reason], ""), diff --git a/dag/gunbc/roadmap/roadmap_closing_contract_authoring.dag b/dag/gunbc/roadmap/roadmap_closing_contract_authoring.dag index 11384eb4af9..1738444b8eb 100644 --- a/dag/gunbc/roadmap/roadmap_closing_contract_authoring.dag +++ b/dag/gunbc/roadmap/roadmap_closing_contract_authoring.dag @@ -7,7 +7,10 @@ import extdeps.shell import gunbc.output_policy { OutcomeIsData } import gunbc.roadmap_model { RoadmapNode, RoadmapNodeId } import gunbc.roadmap_authority { declared_roadmap_nodes } -import extdeps.filesystem.filesystem_io { Filesystem } +import extdeps.filesystem.filesystem_io { + Filesystem, + filesystem_read_outcome, +} import gunbc.roadmap_execution_contract { WorkItemExecutionContract, ValidationOperation, @@ -133,9 +136,7 @@ fn closing_contract_oracle_observe(oracle: ValidationOracleRef) -> OracleObserva let read = Filesystem.Read(path: oracle.entry as String) validation_oracle_observe_from_source( oracle: oracle, - source_readable: read.success, - source: read.content, - read_error: read.error, + source_read: filesystem_read_outcome(content: read.content, success: read.success, error: read.error), ) } diff --git a/dag/gunbc/roadmap/roadmap_event_carrier.dag b/dag/gunbc/roadmap/roadmap_event_carrier.dag index c016de9526e..558a3c09e23 100644 --- a/dag/gunbc/roadmap/roadmap_event_carrier.dag +++ b/dag/gunbc/roadmap/roadmap_event_carrier.dag @@ -1,7 +1,20 @@ module gunbc.roadmap.roadmap_event_carrier import std.types { String, Int, Bool, List, NonEmptyStr, FilePath, GitRef } -import extdeps.filesystem.filesystem_io { Filesystem } +import extdeps.filesystem.filesystem_io { + Filesystem, + FilesystemFileObservation, + FilesystemFileRead, + FilesystemFileAbsent, + FilesystemFileIndeterminate, + FilesystemFileObservationsDisagree, + FilesystemFileSubjectRefused, + FilesystemReadOutcome, + FilesystemReadRefused, + FilesystemReadSucceeded, + filesystem_file_observation_of_path, + filesystem_read_outcome, +} import std.algebra { trim } import std.content_hash { content_hash_of_value } import std.durable_compare_and_set { CasGeneration, CasReadableSlot, CasReadableAbsent, CasReadablePresent, CasSlotVersion, ExpectSlotAbsent, ExpectSlotGeneration, cas_expectation_admits } @@ -221,6 +234,27 @@ fn roadmap_event_same_command(a: RoadmapEvent, b: RoadmapEvent) -> Bool { a.node == b.node && a.author == b.author && a.parent == b.parent && a.kind == b.kind } +// The operation receipt is observed, not read-and-kind-matched: the old flow branched on the +// host error's error_kind string ("not_found"), which breaks on every host phrasing. The +// observation authority establishes absence from the listing, so a missing receipt is an +// established fact (proceed with the append), a listed-but-unreadable receipt is a refusal that +// says whether the operation already ran is UNKNOWN, and a readable receipt decodes. +fn roadmap_event_append_after_no_operation_receipt( + layout: RoadmapEventCarrierLayout, + event: RoadmapEvent, + env: RoadmapEventEnvelope, + receipt_path: String, + expected_commit: String, +) -> RoadmapEventAppend { + match roadmap_events_read_synced(layout: layout, node: event.node) { + EventsReadRefused { node: _, step, reason } => EventAppendRefused { id: env.id, step: step, reason: reason } + EventsRead { node: _, envelopes } => if any(envelopes, existing => existing.id == env.id) { EventAlreadyPresent { id: env.id } } + else if !roadmap_event_parent_admitted(event: event, envelopes: envelopes) { + EventAppendRefused { id: env.id, step: "revision-conflict", reason: "expected issue revision does not match the committed issue head" } + } else { roadmap_event_publish_private(layout: layout, env: env, operation: operation, receipt_path: receipt_path, expected_commit: expected_commit) } + } +} + fn roadmap_event_append_private(layout: RoadmapEventCarrierLayout, event: RoadmapEvent, operation: String) -> RoadmapEventAppend { let env = roadmap_event_envelope(e: event) match roadmap_event_carrier_sync(layout: layout) { @@ -229,25 +263,45 @@ fn roadmap_event_append_private(layout: RoadmapEventCarrierLayout, event: Roadma CarrierExecFailed { exit_code: _, stderr } => EventAppendRefused { id: env.id, step: "snapshot-revision", reason: stderr } CarrierExecOk { stdout: revision } => { let receipt_path = roadmap_event_operation_path(event: event, operation: operation) - let receipt = Filesystem.Read(path: concat(layout.worktree as String, concat("/", receipt_path))) - if operation != "" && receipt.success { - match roadmap_event_decode(text: receipt.content) { - RoadmapEventUndecodable { reason } => EventAppendRefused { id: env.id, step: "operation-read", reason: reason } - RoadmapEventDecoded { envelope: previous } => if roadmap_event_same_command(a: previous.event, b: event) { - let committed = Filesystem.Read(path: concat(layout.worktree as String, concat("/", roadmap_event_file_relative(node: previous.event.node, id: previous.id)))) - if committed.success && committed.content == receipt.content { EventAlreadyPresent { id: previous.id } } - else { EventAppendRefused { id: env.id, step: "operation-read", reason: "operation receipt has no matching committed event" } } - } else { EventAppendRefused { id: env.id, step: "operation-conflict", reason: "operation identity already belongs to another issue/principal/payload/precondition" } } + if operation == "" { + roadmap_event_append_after_no_operation_receipt( + layout: layout, + event: event, + env: env, + receipt_path: receipt_path, + expected_commit: trim(revision), + ) + } else { + match filesystem_file_observation_of_path(path: concat(layout.worktree as String, concat("/", receipt_path))) { + FilesystemFileRead { path: _, content } => + match roadmap_event_decode(text: content) { + RoadmapEventUndecodable { reason } => EventAppendRefused { id: env.id, step: "operation-read", reason: reason } + RoadmapEventDecoded { envelope: previous } => if roadmap_event_same_command(a: previous.event, b: event) { + let committed = Filesystem.Read(path: concat(layout.worktree as String, concat("/", roadmap_event_file_relative(node: previous.event.node, id: previous.id)))) + match filesystem_read_outcome(content: committed.content, success: committed.success, error: committed.error) { + FilesystemReadRefused { error } => + EventAppendRefused { id: env.id, step: "operation-read", reason: join(["the committed event at this operation's address could not be read, so whether the operation already ran is unknown: ", error], "") } + FilesystemReadSucceeded { content: committed_text } => if committed_text == content { EventAlreadyPresent { id: previous.id } } + else { EventAppendRefused { id: env.id, step: "operation-read", reason: "operation receipt has no matching committed event" } } + } + } else { EventAppendRefused { id: env.id, step: "operation-conflict", reason: "operation identity already belongs to another issue/principal/payload/precondition" } } + } + FilesystemFileAbsent(_) => + roadmap_event_append_after_no_operation_receipt( + layout: layout, + event: event, + env: env, + receipt_path: receipt_path, + expected_commit: trim(revision), + ) + FilesystemFileIndeterminate { cause } => + EventAppendRefused { id: env.id, step: "operation-read", reason: join(["the operation receipt is listed and could not be read, so whether the operation already ran is unknown: ", cause], "") } + FilesystemFileObservationsDisagree { path: _, cause } => + EventAppendRefused { id: env.id, step: "operation-read", reason: join(["the operation receipt observation is self-contradictory, so whether the operation already ran is unknown: ", cause], "") } + FilesystemFileSubjectRefused { directory: _, name: _, cause } => + EventAppendRefused { id: env.id, step: "operation-read", reason: join(["the operation receipt path was refused before any filesystem call ran: ", cause], "") } } - } else if operation != "" && receipt.error_kind != "not_found" { - EventAppendRefused { id: env.id, step: "operation-read", reason: receipt.error } - } else { match roadmap_events_read_synced(layout: layout, node: event.node) { - EventsReadRefused { node: _, step, reason } => EventAppendRefused { id: env.id, step: step, reason: reason } - EventsRead { node: _, envelopes } => if any(envelopes, existing => existing.id == env.id) { EventAlreadyPresent { id: env.id } } - else if !roadmap_event_parent_admitted(event: event, envelopes: envelopes) { - EventAppendRefused { id: env.id, step: "revision-conflict", reason: "expected issue revision does not match the committed issue head" } - } else { roadmap_event_publish_private(layout: layout, env: env, operation: operation, receipt_path: receipt_path, expected_commit: trim(revision)) } - } } + } } } } @@ -315,7 +369,10 @@ type EventFileRead fn roadmap_events_decode_all(node: RoadmapNodeId, dir: String, names: List) -> RoadmapEventsRead { let reads = map(filter(names, n => ends_with(s: n, suffix: ".json")), n => { let r = Filesystem.Read(path: join([dir, "/", n], "")) - if r.success { EventFileContent { content: r.content } } else { EventFileUnreadable { detail: r.error } } + match filesystem_read_outcome(content: r.content, success: r.success, error: r.error) { + FilesystemReadSucceeded { content } => EventFileContent { content: content } + FilesystemReadRefused { error } => EventFileUnreadable { detail: error } + } }) roadmap_events_decode_reads(node: node, reads: reads) } diff --git a/dag/gunbc/roadmap/roadmap_publication_helper.dag b/dag/gunbc/roadmap/roadmap_publication_helper.dag index 8867eadeaf0..2438bdcf569 100644 --- a/dag/gunbc/roadmap/roadmap_publication_helper.dag +++ b/dag/gunbc/roadmap/roadmap_publication_helper.dag @@ -7,7 +7,14 @@ import std.content_hash { content_hash_atom } import std.process { ProcessExit, ExitSuccess, exit_failure } import gunbc.output_policy { OutcomeIsData } import extdeps.shell -import extdeps.filesystem.filesystem_io { Filesystem, filesystem_listing_names_entry } +import extdeps.filesystem.filesystem_io { + Filesystem, + FilesystemReadOutcome, + FilesystemReadRefused, + FilesystemReadSucceeded, + filesystem_listing_names_entry, + filesystem_read_outcome, +} import extdeps.languages.json.emit { JsonValue, json_string, @@ -259,15 +266,16 @@ type PublicationAnswerRead fn publication_answer_read_at(path: String, expected: RoadmapPublicationSubject) -> PublicationAnswerRead { let read = Filesystem.Read(path: path) - if !read.success { - AnswerUnreadable { - reason: join([ - "a publication answer for this head was listed and then could not be read, so it exists and its content is unavailable: ", - path, " (", read.error, ")", - ], ""), - } - } else { - match publication_receipt_decode(receipt: read.content) { + match filesystem_read_outcome(content: read.content, success: read.success, error: read.error) { + FilesystemReadRefused { error } => + AnswerUnreadable { + reason: join([ + "a publication answer for this head was listed and then could not be read, so it exists and its content is unavailable: ", + path, " (", error, ")", + ], ""), + } + FilesystemReadSucceeded { content } => + match publication_receipt_decode(receipt: content) { ReceiptUndecodable { reason: r } => AnswerUnreadable { reason: join([ @@ -904,21 +912,22 @@ fn publication_helper_entry_outcome( publication_request_dir(instance_root: instance_root), "/", entry, ], "") let read = Filesystem.Read(path: path) - if !read.success { - HelperRequestUnreadable { - entry: entry, - detail: join([ - "a publication request was listed and then could not be read: ", - path, " (", read.error, ")", - ], ""), - } - } else { - publication_helper_answer_request( - instance_root: instance_root, - credential: credential, - entry: entry, - request_text: read.content, - ) + match filesystem_read_outcome(content: read.content, success: read.success, error: read.error) { + FilesystemReadRefused { error } => + HelperRequestUnreadable { + entry: entry, + detail: join([ + "a publication request was listed and then could not be read: ", + path, " (", error, ")", + ], ""), + } + FilesystemReadSucceeded { content } => + publication_helper_answer_request( + instance_root: instance_root, + credential: credential, + entry: entry, + request_text: content, + ) } } } diff --git a/dag/gunbc/roadmap/roadmap_served_observation.dag b/dag/gunbc/roadmap/roadmap_served_observation.dag index 2e6b5965d7a..5386e65742c 100644 --- a/dag/gunbc/roadmap/roadmap_served_observation.dag +++ b/dag/gunbc/roadmap/roadmap_served_observation.dag @@ -4,7 +4,16 @@ import gunbc.site.markup { page_render_refusal_body } import gunbc.roadmap.roadmap_event_carrier { roadmap_event_carrier_layout_for_instance, roadmap_standings_read } import std.types { String, Int, Bool, List, NonEmptyStr } -import extdeps.filesystem.filesystem_io { Filesystem } +import extdeps.filesystem.filesystem_io { + Filesystem, + FilesystemFileObservation, + FilesystemFileRead, + FilesystemFileAbsent, + FilesystemFileIndeterminate, + FilesystemFileObservationsDisagree, + FilesystemFileSubjectRefused, + filesystem_file_observation_of_path, +} import std.algebra { trim } import std.content_hash { content_hash_tagged_structural, content_hash_atom } import extdeps.http.server { MediaType, text_html_utf8, text_plain_utf8, application_json_utf8, ServeHttpResponse } @@ -478,19 +487,29 @@ fn served_observation_decode(raw: String) -> ServedObservationRead { } } -fn served_observation_read_from_filesystem(success: Bool, content: String, error: String) -> ServedObservationRead { - if success { - if trim(content) == "" { ServedObservationAbsent } else { served_observation_decode(raw: content) } - } else if string_contains(s: error, pattern: "No such file") || string_contains(s: error, pattern: "not found") { - ServedObservationAbsent - } else { - ServedObservationUnreadable { reason: join(["served observation index could not be read: ", error], "") } +// The served-observation index is classified from an OBSERVATION, not by text-matching the host's +// error string: absence is established from the listing by the observation authority, and the host +// error rides inside the cause text instead of being pattern-matched. +fn served_observation_read_from_observation(observation: FilesystemFileObservation) -> ServedObservationRead { + match observation { + FilesystemFileAbsent(_) => ServedObservationAbsent + FilesystemFileRead { path: _, content } => + if trim(content) == "" { ServedObservationAbsent } else { served_observation_decode(raw: content) } + FilesystemFileIndeterminate { cause } => + ServedObservationUnreadable { reason: join(["served observation index could not be read: ", cause], "") } + FilesystemFileObservationsDisagree { path: _, cause } => + ServedObservationUnreadable { reason: join(["served observation index could not be read: ", cause], "") } + FilesystemFileSubjectRefused { directory: _, name: _, cause } => + ServedObservationUnreadable { reason: join(["served observation index could not be read: ", cause], "") } } } fn served_observation_read_for_instance(instance: HostDashboardInstance) -> ServedObservationRead { - let read = Filesystem.Read(path: served_observation_path_for_instance(instance: instance)) - served_observation_read_from_filesystem(success: read.success, content: read.content, error: read.error) + served_observation_read_from_observation( + observation: filesystem_file_observation_of_path( + path: served_observation_path_for_instance(instance: instance), + ), + ) } // Bodies first, index last: a reader that races the writer sees either the previous complete diff --git a/dag/gunbc/roadmap/roadmap_validation_oracle.dag b/dag/gunbc/roadmap/roadmap_validation_oracle.dag index fbbd6c42d19..f61ef26658a 100644 --- a/dag/gunbc/roadmap/roadmap_validation_oracle.dag +++ b/dag/gunbc/roadmap/roadmap_validation_oracle.dag @@ -11,8 +11,12 @@ import std.content_hash { fnv1a64_structural_hex_digest, serialize_content_hash, } -import extdeps.pin { - Pin, +import extdeps.filesystem.filesystem_io { + FilesystemReadOutcome, + FilesystemReadRefused, + FilesystemReadSucceeded, +} +import extdeps.pin { Pin, ExactPin, PinAdmission, PinIntegrityAdmitted, @@ -250,51 +254,51 @@ fn validation_oracle_declaration_count( fn validation_oracle_observe_from_source( oracle: ValidationOracleRef, - source_readable: Bool, - source: String, - read_error: String, + source_read: FilesystemReadOutcome, ) -> OracleObservation { - if !source_readable { - OracleMissing { - oracle: oracle, - detail: join([ - "validation oracle source could not be obtained at ", - oracle.entry as String, - ": ", - read_error, - ], ""), - } - } else { - let declarations = validation_oracle_declaration_count( - source: source, - function: oracle.function, - ) - if declarations == 0 { + match source_read { + FilesystemReadRefused { error } => OracleMissing { oracle: oracle, detail: join([ - "validation oracle source at ", + "validation oracle source could not be obtained at ", oracle.entry as String, - " declares no ", - oracle.function as String, + ": ", + error, ], ""), } - } else if declarations > 1 { - OracleAmbiguous { - oracle: oracle, - declaration_count: declarations, - } - } else { - let unpinned = validation_oracle_unpinned_dependencies(source: source) - if count(unpinned) > 0 { - OracleDependsOnUnpinnedOracle { + FilesystemReadSucceeded { content: source } => { + let declarations = validation_oracle_declaration_count( + source: source, + function: oracle.function, + ) + if declarations == 0 { + OracleMissing { oracle: oracle, - imports: unpinned, + detail: join([ + "validation oracle source at ", + oracle.entry as String, + " declares no ", + oracle.function as String, + ], ""), } - } else { - OracleFound { + } else if declarations > 1 { + OracleAmbiguous { oracle: oracle, - identity: validation_oracle_source_identity(source: source), + declaration_count: declarations, + } + } else { + let unpinned = validation_oracle_unpinned_dependencies(source: source) + if count(unpinned) > 0 { + OracleDependsOnUnpinnedOracle { + oracle: oracle, + imports: unpinned, + } + } else { + OracleFound { + oracle: oracle, + identity: validation_oracle_source_identity(source: source), + } } } } diff --git a/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag b/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag index 297b953555a..96b8289559b 100644 --- a/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag @@ -2,6 +2,7 @@ module test.claim.roadmap_belt_actuate_witness import gunbc.roadmap_session_placement { SessionPlacementAbsent } import gunbc.roadmap_nesting { roadmap_memberships } +import extdeps.filesystem.filesystem_io { filesystem_read_outcome, filesystem_file_observation, filesystem_listing_observation, FilesystemReadRefused, FilesystemFileObservation } import gunbc.roadmap_belt_actuate { belt_spawn_outcome_with_release } import gunbc.roadmap.roadmap_submission { CaptureAbsent } @@ -126,7 +127,7 @@ import gunbc.roadmap_belt_actuate { belt_workflow_attempt_presentation_json_with_tick_read, belt_verification_presentation_detail, belt_tick_receipt_decode, - belt_tick_receipt_read_from_filesystem, + belt_tick_receipt_read_from_observation, belt_tick_receipt_wire_json_from_read, BeltTickReceiptRead, BeltTickReceiptAbsent, @@ -1000,8 +1001,7 @@ fn preflight_with_config(config: String) -> BeltClaudePreflight { claude_on_path: true, home: "/home/u", trust_store_path: "/home/u/.claude.json", - trust_store_readable: true, - trust_store_text: config, + trust_store_read: filesystem_read_outcome(content: config, success: true, error: "unused on success"), trust_root: "/opt/x/worktrees", ) } @@ -1011,8 +1011,7 @@ test fn witness_preflight_refuses_missing_claude() -> Bool { claude_on_path: false, home: "/home/u", trust_store_path: "", - trust_store_readable: false, - trust_store_text: "", + trust_store_read: filesystem_read_outcome(content: "", success: false, error: "permission denied"), trust_root: "/opt/x/worktrees", )) == "claude-cli" } @@ -1022,8 +1021,7 @@ test fn witness_preflight_refuses_unknown_home() -> Bool { claude_on_path: true, home: "", trust_store_path: "", - trust_store_readable: false, - trust_store_text: "", + trust_store_read: filesystem_read_outcome(content: "", success: false, error: "permission denied"), trust_root: "/opt/x/worktrees", )) == "claude-trust-home" } @@ -1033,8 +1031,7 @@ test fn witness_preflight_refuses_unreadable_store() -> Bool { claude_on_path: true, home: "/home/u", trust_store_path: "/home/u/.claude.json", - trust_store_readable: false, - trust_store_text: "", + trust_store_read: filesystem_read_outcome(content: "", success: false, error: "permission denied"), trust_root: "/opt/x/worktrees", )) == "claude-trust-store" } @@ -1575,24 +1572,26 @@ test fn malformed_pass_reason_makes_receipt_unreadable() -> Bool { } } +// The absent fact is established through the owner's producers: a succeeded listing that omits +// the receipt plus a refused read yield FilesystemFileAbsent inside filesystem_io (the absence +// carrier is a sole_constructor and is never forged here). test fn belt_tick_receipt_missing_file_read_is_absent() -> Bool { - match belt_tick_receipt_read_from_filesystem( - success: false, - content: "", - error: "No such file or directory (os error 2)", - ) { + let listing = filesystem_listing_observation(directory: "/var/lib/gunbc/belt", success: true, entries: "other-state.json", error: "unused on success") + let observed = filesystem_file_observation(listing: listing, name: "belt-tick.json", path: "/var/lib/gunbc/belt/belt-tick.json", read: FilesystemReadRefused { error: "No such file or directory (os error 2)" }) + match belt_tick_receipt_read_from_observation(observation: observed) { BeltTickReceiptAbsent => true BeltTickReceiptPresent { receipt: _ } => false BeltTickReceiptUnreadable { reason: _ } => false } } +// Listed but refused: the listing NAMES the receipt, so absence is not established, and the read +// refusal makes the fact indeterminate -- present, content unavailable. The host error rides in +// the cause text, so the old "Permission denied" witness assertion still discriminates. test fn belt_tick_receipt_io_failure_read_is_unreadable() -> Bool { - match belt_tick_receipt_read_from_filesystem( - success: false, - content: "", - error: "Permission denied (os error 13)", - ) { + let listing = filesystem_listing_observation(directory: "/var/lib/gunbc/belt", success: true, entries: "belt-tick.json\nother-state.json", error: "unused on success") + let observed = filesystem_file_observation(listing: listing, name: "belt-tick.json", path: "/var/lib/gunbc/belt/belt-tick.json", read: FilesystemReadRefused { error: "Permission denied (os error 13)" }) + match belt_tick_receipt_read_from_observation(observation: observed) { BeltTickReceiptUnreadable { reason: r } => string_contains(s: r, pattern: "Permission denied") BeltTickReceiptPresent { receipt: _ } => false diff --git a/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag b/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag index d51c085ede6..37cadb971ec 100644 --- a/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag @@ -1,5 +1,7 @@ module test.claim.roadmap_served_observation_witness +import extdeps.filesystem.filesystem_io { filesystem_file_observation, filesystem_listing_observation, FilesystemReadRefused, FilesystemFileObservation } + import std.types { String, Int, Bool, List, FilePath } import extdeps.http.server { text_html_utf8, text_plain_utf8, application_json_utf8 } import gunbc.host_layout { instance_receipts_dir } @@ -24,7 +26,7 @@ import gunbc.roadmap_served_observation { served_observation_index_of_snapshot, served_observation_index_wire, served_observation_decode, - served_observation_read_from_filesystem, + served_observation_read_from_observation, served_observation_identity_hex, served_observation_body_digest, served_observation_body_read_from_filesystem, @@ -129,9 +131,24 @@ test fn red_control_unknown_schema_is_unreadable() -> Bool { } } +// Both facts drive through the owner's producers: absence is ESTABLISHED by a succeeded listing +// that omits the index (the sole_constructor carrier is produced inside filesystem_io, never +// forged here), while a listed index whose read is refused stays present-but-unavailable. test fn a_missing_index_reads_as_absent_not_unreadable() -> Bool { - let absent = served_observation_read_from_filesystem(success: false, content: "", error: "No such file or directory") - let broken = served_observation_read_from_filesystem(success: false, content: "", error: "Permission denied") + let absent_obs = filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/var/lib/gunbc/served", success: true, entries: "bodies.json", error: "unused on success"), + name: "index.json", + path: "/var/lib/gunbc/served/index.json", + read: FilesystemReadRefused { error: "No such file or directory" }, + ) + let broken_obs = filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/var/lib/gunbc/served", success: true, entries: "index.json\nbodies.json", error: "unused on success"), + name: "index.json", + path: "/var/lib/gunbc/served/index.json", + read: FilesystemReadRefused { error: "Permission denied" }, + ) + let absent = served_observation_read_from_observation(observation: absent_obs) + let broken = served_observation_read_from_observation(observation: broken_obs) (match absent { ServedObservationAbsent => true _ => false }) && (match broken { ServedObservationUnreadable { reason } => string_contains(s: reason, pattern: "Permission denied") _ => false }) } From 21a469f3a57340be75b6461f07673043b8e3df32 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 08:04:19 +0000 Subject: [PATCH 03/12] roadmap: import filesystem_file_observation_of_path from its owning module gunbc.filesystem_file_observe --- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 2 +- dag/gunbc/roadmap/roadmap_event_carrier.dag | 2 +- dag/gunbc/roadmap/roadmap_served_observation.dag | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index 3d4e3a16afe..fdf237ececa 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -559,7 +559,6 @@ import extdeps.filesystem.filesystem_io { FilesystemFileIndeterminate, FilesystemFileObservationsDisagree, FilesystemFileSubjectRefused, - filesystem_file_observation_of_path, filesystem_listing_observation, filesystem_listing_names_entry, filesystem_entry_presence, @@ -569,6 +568,7 @@ import extdeps.filesystem.filesystem_io { filesystem_read_outcome, filesystem_file_observation, } +import gunbc.filesystem_file_observe { filesystem_file_observation_of_path } import extdeps.llm.cli { shape_codex_login_status_env_args_for_program, shape_codex_version_env_args_for_program, diff --git a/dag/gunbc/roadmap/roadmap_event_carrier.dag b/dag/gunbc/roadmap/roadmap_event_carrier.dag index 558a3c09e23..c45413aefdb 100644 --- a/dag/gunbc/roadmap/roadmap_event_carrier.dag +++ b/dag/gunbc/roadmap/roadmap_event_carrier.dag @@ -12,9 +12,9 @@ import extdeps.filesystem.filesystem_io { FilesystemReadOutcome, FilesystemReadRefused, FilesystemReadSucceeded, - filesystem_file_observation_of_path, filesystem_read_outcome, } +import gunbc.filesystem_file_observe { filesystem_file_observation_of_path } import std.algebra { trim } import std.content_hash { content_hash_of_value } import std.durable_compare_and_set { CasGeneration, CasReadableSlot, CasReadableAbsent, CasReadablePresent, CasSlotVersion, ExpectSlotAbsent, ExpectSlotGeneration, cas_expectation_admits } diff --git a/dag/gunbc/roadmap/roadmap_served_observation.dag b/dag/gunbc/roadmap/roadmap_served_observation.dag index 5386e65742c..bd4c652adee 100644 --- a/dag/gunbc/roadmap/roadmap_served_observation.dag +++ b/dag/gunbc/roadmap/roadmap_served_observation.dag @@ -12,8 +12,8 @@ import extdeps.filesystem.filesystem_io { FilesystemFileIndeterminate, FilesystemFileObservationsDisagree, FilesystemFileSubjectRefused, - filesystem_file_observation_of_path, } +import gunbc.filesystem_file_observe { filesystem_file_observation_of_path } import std.algebra { trim } import std.content_hash { content_hash_tagged_structural, content_hash_atom } import extdeps.http.server { MediaType, text_html_utf8, text_plain_utf8, application_json_utf8, ServeHttpResponse } From 67d8558f95a62c7bc96098f045ac32067b659151 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 08:38:10 +0000 Subject: [PATCH 04/12] roadmap event carrier: thread operation through the extracted no-receipt proceed path --- dag/gunbc/roadmap/roadmap_event_carrier.dag | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dag/gunbc/roadmap/roadmap_event_carrier.dag b/dag/gunbc/roadmap/roadmap_event_carrier.dag index c45413aefdb..663d020ed1b 100644 --- a/dag/gunbc/roadmap/roadmap_event_carrier.dag +++ b/dag/gunbc/roadmap/roadmap_event_carrier.dag @@ -243,6 +243,7 @@ fn roadmap_event_append_after_no_operation_receipt( layout: RoadmapEventCarrierLayout, event: RoadmapEvent, env: RoadmapEventEnvelope, + operation: String, receipt_path: String, expected_commit: String, ) -> RoadmapEventAppend { @@ -268,6 +269,7 @@ fn roadmap_event_append_private(layout: RoadmapEventCarrierLayout, event: Roadma layout: layout, event: event, env: env, + operation: operation, receipt_path: receipt_path, expected_commit: trim(revision), ) @@ -291,6 +293,7 @@ fn roadmap_event_append_private(layout: RoadmapEventCarrierLayout, event: Roadma layout: layout, event: event, env: env, + operation: operation, receipt_path: receipt_path, expected_commit: trim(revision), ) From c92500fc69d7fd26f2f10f49c667a2ba15621329 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 13:11:18 +0000 Subject: [PATCH 05/12] roadmap: convert the attempt-request binding and served-observation body reads to the file-observation fold; result-binding gate threaded through the extracted proceed path --- .../roadmap_attempt_request_record.dag | 31 +++++--- .../roadmap/roadmap_served_observation.dag | 37 ++++++--- .../roadmap_result_returned_witness_test.dag | 47 +++++++++-- ...oadmap_served_observation_witness_test.dag | 77 +++++++++++++++++-- 4 files changed, 156 insertions(+), 36 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_attempt_request_record.dag b/dag/gunbc/roadmap/roadmap_attempt_request_record.dag index f5a48600a70..821c6cebb8d 100644 --- a/dag/gunbc/roadmap/roadmap_attempt_request_record.dag +++ b/dag/gunbc/roadmap/roadmap_attempt_request_record.dag @@ -4,7 +4,10 @@ import std.types { String, Bool, List, NonEmptyStr } import extdeps.filesystem.filesystem_io { Filesystem, FilesystemCreateNew, FilesystemCreated, FilesystemCreateTargetOccupied, FilesystemCreateRefused, FilesystemCreateKindUnrecognized, filesystem_create_new, + FilesystemFileObservation, FilesystemFileRead, FilesystemFileAbsent, FilesystemFileIndeterminate, + FilesystemFileObservationsDisagree, FilesystemFileSubjectRefused, } +import gunbc.filesystem_file_observe { filesystem_file_observation_of_path } import extdeps.languages.json.emit { JsonValue, JsonNull, JsonBool, JsonNumber, JsonString, JsonArray, JsonObject, json_object, json_kv, json_string, serialize_json } import extdeps.languages.json.parse { parse_json_document, JsonDocumentParsed, JsonDocumentUnreadable, json_document_gap_text, @@ -155,20 +158,24 @@ type AttemptRequestBindingRead | RequestBindingAbsent | RequestBindingUnreadable { reason: String } -fn attempt_request_binding_read_of(success: Bool, error_kind: String, error: String, content: String) -> AttemptRequestBindingRead { - if success { - match attempt_request_binding_decode(text: content) { - RequestBindingDecoded { binding } => RequestBindingRead { binding: binding } - RequestBindingUndecodable { reason } => RequestBindingUnreadable { reason: reason } - } - } else if error_kind == "not_found" { - RequestBindingAbsent - } else { - RequestBindingUnreadable { reason: error } +fn attempt_request_binding_read_from_observation(observation: FilesystemFileObservation) -> AttemptRequestBindingRead { + match observation { + FilesystemFileAbsent(_) => RequestBindingAbsent + FilesystemFileRead { path: _, content } => + match attempt_request_binding_decode(text: content) { + RequestBindingDecoded { binding } => RequestBindingRead { binding: binding } + RequestBindingUndecodable { reason } => RequestBindingUnreadable { reason: reason } + } + FilesystemFileIndeterminate { cause } => RequestBindingUnreadable { reason: cause } + FilesystemFileObservationsDisagree { path: _, cause } => RequestBindingUnreadable { reason: cause } + FilesystemFileSubjectRefused { directory: _, name: _, cause } => RequestBindingUnreadable { reason: cause } } } fn attempt_request_binding_read_for_instance(instance: HostDashboardInstance, node_id: RoadmapNodeId, attempt_key: String) -> AttemptRequestBindingRead { - let read = Filesystem.Read(path: attempt_request_binding_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key)) - attempt_request_binding_read_of(success: read.success, error_kind: read.error_kind, error: read.error, content: read.content) + attempt_request_binding_read_from_observation( + observation: filesystem_file_observation_of_path( + path: attempt_request_binding_path_for_instance(instance: instance, node_id: node_id, attempt_key: attempt_key), + ), + ) } diff --git a/dag/gunbc/roadmap/roadmap_served_observation.dag b/dag/gunbc/roadmap/roadmap_served_observation.dag index bd4c652adee..7c5f031e5ce 100644 --- a/dag/gunbc/roadmap/roadmap_served_observation.dag +++ b/dag/gunbc/roadmap/roadmap_served_observation.dag @@ -12,6 +12,7 @@ import extdeps.filesystem.filesystem_io { FilesystemFileIndeterminate, FilesystemFileObservationsDisagree, FilesystemFileSubjectRefused, + filesystem_established_absence_detail, } import gunbc.filesystem_file_observe { filesystem_file_observation_of_path } import std.algebra { trim } @@ -581,24 +582,33 @@ fn served_observation_entry_lookup(index: ServedObservationIndex, kind: ServedOb type ServedObservationBodyRead = ServedObservationBodyPresent { body: String } | ServedObservationBodyMissing { reason: String } + | ServedObservationBodyUnreadable { cause: String } | ServedObservationBodyMismatch { expected: String, actual: String } -fn served_observation_body_read_from_filesystem(entry: ServedObservationEntry, success: Bool, content: String, error: String) -> ServedObservationBodyRead { - if !success { - ServedObservationBodyMissing { reason: error } - } else { - let actual = served_observation_body_digest(body: content) - if actual == entry.digest { - ServedObservationBodyPresent { body: content } - } else { - ServedObservationBodyMismatch { expected: entry.digest, actual: actual } +fn served_observation_body_read_from_observation(entry: ServedObservationEntry, observation: FilesystemFileObservation) -> ServedObservationBodyRead { + match observation { + FilesystemFileAbsent(absence) => ServedObservationBodyMissing { reason: filesystem_established_absence_detail(absence: absence) } + FilesystemFileRead { path: _, content } => { + let actual = served_observation_body_digest(body: content) + if actual == entry.digest { + ServedObservationBodyPresent { body: content } + } else { + ServedObservationBodyMismatch { expected: entry.digest, actual: actual } + } } + FilesystemFileIndeterminate { cause } => ServedObservationBodyUnreadable { cause: cause } + FilesystemFileObservationsDisagree { path: _, cause } => ServedObservationBodyUnreadable { cause: cause } + FilesystemFileSubjectRefused { directory: _, name: _, cause } => ServedObservationBodyUnreadable { cause: cause } } } fn served_observation_body_read_for_instance(instance: HostDashboardInstance, entry: ServedObservationEntry) -> ServedObservationBodyRead { - let read = Filesystem.Read(path: served_observation_body_path_for_instance(instance: instance, kind: entry.kind)) - served_observation_body_read_from_filesystem(entry: entry, success: read.success, content: read.content, error: read.error) + served_observation_body_read_from_observation( + entry: entry, + observation: filesystem_file_observation_of_path( + path: served_observation_body_path_for_instance(instance: instance, kind: entry.kind), + ), + ) } fn served_observation_refusal_body(refusal: String, detail: String) -> String { @@ -625,6 +635,11 @@ fn served_observation_body_response(entry: ServedObservationEntry, body: ServedO refusal: "ServedObservationBodyMissing", detail: join(["the ", served_observation_kind_key(kind: entry.kind), " body named by the index could not be read: ", reason], ""), ) + ServedObservationBodyUnreadable { cause } => + served_observation_refusal_response( + refusal: "ServedObservationBodyUnreadable", + detail: join(["the ", served_observation_kind_key(kind: entry.kind), " body named by the index exists but could not be read: ", cause], ""), + ) ServedObservationBodyMismatch { expected, actual } => served_observation_refusal_response( refusal: "ServedObservationBodyMismatch", diff --git a/dag/test/claim/roadmap/roadmap_result_returned_witness_test.dag b/dag/test/claim/roadmap/roadmap_result_returned_witness_test.dag index 5ffe35cd0ef..e891e077637 100644 --- a/dag/test/claim/roadmap/roadmap_result_returned_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_result_returned_witness_test.dag @@ -27,12 +27,13 @@ import gunbc.roadmap.roadmap_event_carrier { } import extdeps.filesystem.filesystem_io { FilesystemCreateNew, FilesystemCreated, FilesystemCreateTargetOccupied, FilesystemCreateRefused, FilesystemPermissionDenied, + FilesystemReadSucceeded, FilesystemReadRefused, filesystem_file_observation, filesystem_listing_observation, } import gunbc.roadmap.roadmap_attempt_request_record { AttemptRequestBinding, RequestClaimBound, RequestAutonomous, attempt_request_binding_json, attempt_request_binding_decode, RequestBindingDecoded, RequestBindingUndecodable, AttemptRequestBindingCommit, RequestBindingCommitted, RequestBindingCommitRefused, attempt_request_binding_commit_of, - attempt_request_binding_read_of, + attempt_request_binding_read_from_observation, AttemptRequestBindingRead, RequestBindingRead, RequestBindingAbsent, RequestBindingUnreadable, } import gunbc.roadmap.roadmap_attempt_request_binding { @@ -317,9 +318,38 @@ test fn the_binding_record_round_trips_and_a_malformed_one_is_unreadable() -> Bo RequestBindingUndecodable { reason } => string_length(s: reason) > 0 RequestBindingDecoded { binding: _ } => false }) - let file_arms = attempt_request_binding_read_of(success: false, error_kind: "not_found", error: "no such file", content: "") == RequestBindingAbsent - && (match attempt_request_binding_read_of(success: false, error_kind: "permission_denied", error: "denied", content: "") { RequestBindingUnreadable { reason } => reason == "denied" RequestBindingRead { binding: _ } => false RequestBindingAbsent => false }) - && (match attempt_request_binding_read_of(success: true, error_kind: "", error: "", content: "not json") { RequestBindingUnreadable { reason: _ } => true RequestBindingRead { binding: _ } => false RequestBindingAbsent => false }) + // The absent arm is driven through the owner's producers: the listing omits the record, so the + // absence is established, not inferred from a failed read. The unreadable arm is listed-but- + // refused; the undecodable arm is present and read but not a binding. + let absent_observed = filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/requests", success: true, entries: "other-attempt.json", error: "unused on success"), + name: "request.json", + path: "/srv/gunbc/requests/request.json", + read: FilesystemReadRefused { error: "No such file or directory (os error 2)" }, + ) + let refused_observed = filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/requests", success: true, entries: "request.json", error: "unused on success"), + name: "request.json", + path: "/srv/gunbc/requests/request.json", + read: FilesystemReadRefused { error: "Permission denied (os error 13)" }, + ) + let undecodable_observed = filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/requests", success: true, entries: "request.json", error: "unused on success"), + name: "request.json", + path: "/srv/gunbc/requests/request.json", + read: FilesystemReadSucceeded { content: "not json" }, + ) + let file_arms = attempt_request_binding_read_from_observation(observation: absent_observed) == RequestBindingAbsent + && (match attempt_request_binding_read_from_observation(observation: refused_observed) { + RequestBindingUnreadable { reason } => string_contains(s: reason, pattern: "Permission denied") + RequestBindingRead { binding: _ } => false + RequestBindingAbsent => false + }) + && (match attempt_request_binding_read_from_observation(observation: undecodable_observed) { + RequestBindingUnreadable { reason: _ } => true + RequestBindingRead { binding: _ } => false + RequestBindingAbsent => false + }) codec && strict && file_arms } @@ -590,7 +620,14 @@ test fn a_bound_attempts_result_reaches_its_requester_through_the_real_codecs() match attempt_request_binding_commit_of(binding: launch, created: FilesystemCreated { path: "p" }, existing: RequestBindingAbsent) { RequestBindingCommitRefused { step: _, detail: _ } => false RequestBindingCommitted { binding: committed } => { - let recorded = attempt_request_binding_read_of(success: true, error_kind: "", error: "", content: attempt_request_binding_json(binding: committed)) + let recorded = attempt_request_binding_read_from_observation( + observation: filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/requests", success: true, entries: "request.json", error: "unused on success"), + name: "request.json", + path: "/srv/gunbc/requests/request.json", + read: FilesystemReadSucceeded { content: attempt_request_binding_json(binding: committed) }, + ), + ) match result_return_precheck(node_id: "result-returned-node", attempt_key: rr_attempt, binding: recorded, envelopes: [c1, c2]) { ResultPrecheckSettled { outcome: _ } => false ResultPrecheckOwed { binding: bound } => diff --git a/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag b/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag index 37cadb971ec..c7a5e959a17 100644 --- a/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag @@ -1,6 +1,6 @@ module test.claim.roadmap_served_observation_witness -import extdeps.filesystem.filesystem_io { filesystem_file_observation, filesystem_listing_observation, FilesystemReadRefused, FilesystemFileObservation } +import extdeps.filesystem.filesystem_io { filesystem_file_observation, filesystem_listing_observation, FilesystemReadRefused, FilesystemReadSucceeded, FilesystemFileObservation } import std.types { String, Int, Bool, List, FilePath } import extdeps.http.server { text_html_utf8, text_plain_utf8, application_json_utf8 } @@ -18,7 +18,7 @@ import gunbc.roadmap_served_observation { ServedObservationEntry, ServedObservationIndex, ServedObservationRead, ServedObservationPresent, ServedObservationAbsent, ServedObservationUnreadable, - ServedObservationBodyRead, ServedObservationBodyPresent, ServedObservationBodyMissing, ServedObservationBodyMismatch, + ServedObservationBodyRead, ServedObservationBodyPresent, ServedObservationBodyMissing, ServedObservationBodyUnreadable, ServedObservationBodyMismatch, ServedObservationEntryLookup, ServedObservationEntryFound, ServedObservationEntryMissing, served_observation_schema, served_observation_path_for_instance, @@ -29,7 +29,7 @@ import gunbc.roadmap_served_observation { served_observation_read_from_observation, served_observation_identity_hex, served_observation_body_digest, - served_observation_body_read_from_filesystem, + served_observation_body_read_from_observation, served_observation_body_response, served_observation_index_refusal, served_observation_entry_for, @@ -170,11 +170,27 @@ test fn a_body_matching_its_entry_is_served_with_stored_status_and_media_type() let workflow = fixture_entry(kind: WorkflowJson) let daily_r = served_observation_body_response( entry: daily, - body: served_observation_body_read_from_filesystem(entry: daily, success: true, content: fixture_daily_body, error: ""), + body: served_observation_body_read_from_observation( + entry: daily, + observation: filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/served", success: true, entries: "daily.html", error: "unused on success"), + name: "daily.html", + path: "/srv/gunbc/served/daily.html", + read: FilesystemReadSucceeded { content: fixture_daily_body }, + ), + ), ) let workflow_r = served_observation_body_response( entry: workflow, - body: served_observation_body_read_from_filesystem(entry: workflow, success: true, content: "{\"refused\":\"tmux unavailable\"}", error: ""), + body: served_observation_body_read_from_observation( + entry: workflow, + observation: filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/served", success: true, entries: "workflow.json", error: "unused on success"), + name: "workflow.json", + path: "/srv/gunbc/served/workflow.json", + read: FilesystemReadSucceeded { content: "{\\"refused\\":\\"tmux unavailable\\"}" }, + ), + ), ) daily_r.status == 200 && daily_r.content_type == text_html_utf8 && daily_r.body == fixture_daily_body && workflow_r.status == 503 && workflow_r.content_type == application_json_utf8 && string_contains(s: workflow_r.body, pattern: "tmux unavailable") @@ -185,7 +201,15 @@ test fn red_control_a_body_that_does_not_hash_to_its_entry_is_refused() -> Bool let daily = fixture_entry(kind: DailyPage) let r = served_observation_body_response( entry: daily, - body: served_observation_body_read_from_filesystem(entry: daily, success: true, content: "stale", error: ""), + body: served_observation_body_read_from_observation( + entry: daily, + observation: filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/served", success: true, entries: "daily.html", error: "unused on success"), + name: "daily.html", + path: "/srv/gunbc/served/daily.html", + read: FilesystemReadSucceeded { content: "stale" }, + ), + ), ) r.status == 503 && string_contains(s: r.body, pattern: "ServedObservationBodyMismatch") @@ -196,16 +220,53 @@ test fn a_body_the_index_names_but_disk_lacks_is_a_typed_503() -> Bool { let daily = fixture_entry(kind: DailyPage) let r = served_observation_body_response( entry: daily, - body: served_observation_body_read_from_filesystem(entry: daily, success: false, content: "", error: "No such file or directory"), + body: served_observation_body_read_from_observation( + entry: daily, + observation: filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/served", success: true, entries: "workflow.json", error: "unused on success"), + name: "daily.html", + path: "/srv/gunbc/served/daily.html", + read: FilesystemReadRefused { error: "No such file or directory (os error 2)" }, + ), + ), ) r.status == 503 && string_contains(s: r.body, pattern: "ServedObservationBodyMissing") } +// RED control: the body the index names is present but unreadable -- the listing establishes it +// and the read refusal says so -- so the refusal says "unreadable", never "missing": a failed +// read is not evidence of absence. +test fn red_control_a_listed_body_that_cannot_be_read_is_unreadable_not_missing() -> Bool { + let daily = fixture_entry(kind: DailyPage) + let r = served_observation_body_response( + entry: daily, + body: served_observation_body_read_from_observation( + entry: daily, + observation: filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/served", success: true, entries: "daily.html", error: "unused on success"), + name: "daily.html", + path: "/srv/gunbc/served/daily.html", + read: FilesystemReadRefused { error: "Permission denied (os error 13)" }, + ), + ), + ) + r.status == 503 && string_contains(s: r.body, pattern: "ServedObservationBodyUnreadable") + && !string_contains(s: r.body, pattern: "ServedObservationBodyMissing") +} + test fn a_refused_daily_render_is_served_as_plain_text_not_html() -> Bool { let entry = ServedObservationEntry { kind: DailyPage, status: 500, digest: served_observation_body_digest(body: "page render refused") } let r = served_observation_body_response( entry: entry, - body: served_observation_body_read_from_filesystem(entry: entry, success: true, content: "page render refused", error: ""), + body: served_observation_body_read_from_observation( + entry: entry, + observation: filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/served", success: true, entries: "daily.html", error: "unused on success"), + name: "daily.html", + path: "/srv/gunbc/served/daily.html", + read: FilesystemReadSucceeded { content: "page render refused" }, + ), + ), ) r.status == 500 && r.content_type == text_plain_utf8 } From acf387f894f627f78ae717ae82b1a634b2e7fd12 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 13:45:40 +0000 Subject: [PATCH 06/12] witness tests: keep annotations at module grain; fix escaped JSON quotes --- .../claim/roadmap/roadmap_result_returned_witness_test.dag | 3 --- .../claim/roadmap/roadmap_served_observation_witness_test.dag | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/dag/test/claim/roadmap/roadmap_result_returned_witness_test.dag b/dag/test/claim/roadmap/roadmap_result_returned_witness_test.dag index e891e077637..2dc6c7b859e 100644 --- a/dag/test/claim/roadmap/roadmap_result_returned_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_result_returned_witness_test.dag @@ -318,9 +318,6 @@ test fn the_binding_record_round_trips_and_a_malformed_one_is_unreadable() -> Bo RequestBindingUndecodable { reason } => string_length(s: reason) > 0 RequestBindingDecoded { binding: _ } => false }) - // The absent arm is driven through the owner's producers: the listing omits the record, so the - // absence is established, not inferred from a failed read. The unreadable arm is listed-but- - // refused; the undecodable arm is present and read but not a binding. let absent_observed = filesystem_file_observation( listing: filesystem_listing_observation(directory: "/srv/gunbc/requests", success: true, entries: "other-attempt.json", error: "unused on success"), name: "request.json", diff --git a/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag b/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag index c7a5e959a17..106ac70c100 100644 --- a/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag @@ -188,7 +188,7 @@ test fn a_body_matching_its_entry_is_served_with_stored_status_and_media_type() listing: filesystem_listing_observation(directory: "/srv/gunbc/served", success: true, entries: "workflow.json", error: "unused on success"), name: "workflow.json", path: "/srv/gunbc/served/workflow.json", - read: FilesystemReadSucceeded { content: "{\\"refused\\":\\"tmux unavailable\\"}" }, + read: FilesystemReadSucceeded { content: "{\"refused\":\"tmux unavailable\"}" }, ), ), ) From e547945a20f1e506e338de7c6425cfba7e7a3566 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 15:40:16 +0000 Subject: [PATCH 07/12] validation oracle witness: construct the fold outcome for the re-signatured classifier --- .../roadmap/roadmap_validation_oracle_witness_test.dag | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag b/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag index 71e89ec5c2f..7d6d3e95e40 100644 --- a/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag @@ -3,6 +3,7 @@ module test.claim.roadmap_validation_oracle_witness_test import gunbc.roadmap_session_placement { SessionPlacementAbsent } import gunbc.roadmap.roadmap_submission { CaptureAbsent } import gunbc.roadmap.roadmap_fanin { IntegrationReceiptAbsent } +import extdeps.filesystem.filesystem_io { FilesystemReadSucceeded, FilesystemReadRefused } import gunbc.roadmap.roadmap_review_function { review_report_absent } import gunbc.worker_lifecycle { WorkerProcessAbsent } @@ -172,9 +173,11 @@ fn observe_witness(source_readable: Bool, source: String) -> List Date: Sun, 4 Oct 2026 20:00:06 +0000 Subject: [PATCH 08/12] roadmap: carry typed pointer standing into attempt evidence; honest Indeterminate wording Side chat on #13281 (msg_b89ab669): two semantic blockers. 1. An unreadable current-attempt pointer still collapsed downstream into absence-shaped facts and minted legacy, supersedable standing. Introduce WorkflowModeledStateStanding (Observed | Absent | Unreadable { cause }), carry it in WorkflowAttemptEvidence.modeled_state, and match it: workflow_provider_from_evidence refuses supersession on Unreadable with 'attempt standing unknown' (never legacy); WorkspaceWorkflowSegment refuses the segment on Unreadable; is_legacy_attempt matches Absent only. Composed control: workflow_provider_from_evidence driven with Unreadable asserts refusal and never-legacy; absent asserts the legacy arm. 2. FilesystemFileIndeterminate covers two worlds (listing refused -> existence unknown; listed but read refused -> existence established) but both refusal texts claimed facts about one world. Wording is now domain-valid ('could not be observed; existence and content unknown' shape) and the owner's cause distinguishes the worlds. Controls: a composed served-body control through the listing producer, and an observation-boundary control at the receipt site (the append flow itself is wet). --- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 9 ++- dag/gunbc/roadmap/roadmap_event_carrier.dag | 2 +- .../roadmap/roadmap_served_observation.dag | 2 +- .../roadmap/roadmap_workflow_command.dag | 14 +++- .../roadmap/roadmap_workflow_progress.dag | 41 ++++++++-- .../roadmap_belt_actuate_witness_test.dag | 4 +- .../roadmap_event_carrier_witness_test.dag | 19 +++++ ...oadmap_served_observation_witness_test.dag | 23 ++++++ ...roadmap_validation_oracle_witness_test.dag | 2 +- .../roadmap_workflow_command_witness_test.dag | 7 +- ...roadmap_workflow_progress_witness_test.dag | 80 +++++++++++++++++-- ...ation_evidence_addressing_witness_test.dag | 3 +- 12 files changed, 178 insertions(+), 28 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index efac27167ad..a0a0561d944 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -525,6 +525,7 @@ import gunbc.roadmap_session_placement { import gunbc.roadmap_workflow_progress { AttemptWorktreeObservation, AttemptWorktreePresent, AttemptWorktreeAbsent, AttemptWorktreeUnobserved, attempt_worktree_observation_of, WorkflowAttemptEvidence, + WorkflowModeledStateStanding, WorkflowModeledStateObserved, WorkflowModeledStateAbsent, WorkflowModeledStateUnreadable, WorkflowAttemptProgress, workflow_attempt_progress, workflow_attempt_progress_json_members, @@ -5551,6 +5552,10 @@ fn belt_workflow_attempt_evidence_without_modeled_state( WorkflowModeledStatePointerUnreadable { cause } => join(["this attempt's current-attempt-key pointer could not be read, so whether an attempt key names its worktree is unknown: ", cause], "") } + let standing = match pointer_state { + WorkflowModeledStatePointerAbsent => WorkflowModeledStateAbsent + WorkflowModeledStatePointerUnreadable { cause } => WorkflowModeledStateUnreadable { cause: cause } + } let placement_detail = match pointer_state { WorkflowModeledStatePointerAbsent => "this attempt has no modeled state pointer, so no attempt key names its session placement" @@ -5565,7 +5570,7 @@ fn belt_workflow_attempt_evidence_without_modeled_state( node_id: attempt.node_id, attempt_key: attempt.attempt_key, branch: attempt.branch, - modeled_state_present: false, + modeled_state: standing, worktree: AttemptWorktreeUnobserved { path: "", cause: worktree_cause }, admission_receipt_readable: false, admission_receipt: "", @@ -5798,7 +5803,7 @@ fn belt_workflow_attempt_evidence_for_key( node_id: node_id, attempt_key: current_key, ), - modeled_state_present: true, + modeled_state: WorkflowModeledStateObserved, worktree: belt_attempt_worktree_observe(instance: instance, node_id: node_id, attempt_key: current_key), admission_receipt_readable: admission.success, admission_receipt: admission.content, diff --git a/dag/gunbc/roadmap/roadmap_event_carrier.dag b/dag/gunbc/roadmap/roadmap_event_carrier.dag index 3ae49a6ed19..9ba81b5867a 100644 --- a/dag/gunbc/roadmap/roadmap_event_carrier.dag +++ b/dag/gunbc/roadmap/roadmap_event_carrier.dag @@ -309,7 +309,7 @@ fn roadmap_event_append_private(layout: RoadmapEventCarrierLayout, event: Roadma expected_commit: trim(revision), ) FilesystemFileIndeterminate { cause } => - EventAppendRefused { id: env.id, step: "operation-read", reason: join(["the operation receipt is listed and could not be read, so whether the operation already ran is unknown: ", cause], "") } + EventAppendRefused { id: env.id, step: "operation-read", reason: join(["the operation receipt could not be observed, so whether the operation already ran is unknown: ", cause], "") } FilesystemFileObservationsDisagree { path: _, cause } => EventAppendRefused { id: env.id, step: "operation-read", reason: join(["the operation receipt observation is self-contradictory, so whether the operation already ran is unknown: ", cause], "") } FilesystemFileSubjectRefused { directory: _, name: _, cause } => diff --git a/dag/gunbc/roadmap/roadmap_served_observation.dag b/dag/gunbc/roadmap/roadmap_served_observation.dag index 7c5f031e5ce..abedf8b3d4f 100644 --- a/dag/gunbc/roadmap/roadmap_served_observation.dag +++ b/dag/gunbc/roadmap/roadmap_served_observation.dag @@ -638,7 +638,7 @@ fn served_observation_body_response(entry: ServedObservationEntry, body: ServedO ServedObservationBodyUnreadable { cause } => served_observation_refusal_response( refusal: "ServedObservationBodyUnreadable", - detail: join(["the ", served_observation_kind_key(kind: entry.kind), " body named by the index exists but could not be read: ", cause], ""), + detail: join(["the ", served_observation_kind_key(kind: entry.kind), " body named by the index could not be observed: ", cause], ""), ) ServedObservationBodyMismatch { expected, actual } => served_observation_refusal_response( diff --git a/dag/gunbc/roadmap/roadmap_workflow_command.dag b/dag/gunbc/roadmap/roadmap_workflow_command.dag index 6cb8350362f..db1aeb16540 100644 --- a/dag/gunbc/roadmap/roadmap_workflow_command.dag +++ b/dag/gunbc/roadmap/roadmap_workflow_command.dag @@ -3,6 +3,7 @@ module gunbc.roadmap_workflow_command import std.types { String, Bool, Int, List } import std.content_hash { ContentHash } import gunbc.roadmap_verification_receipt { VerificationSubject } +import gunbc.roadmap_workflow_progress { WorkflowModeledStateStanding, WorkflowModeledStateAbsent } type AttemptKey = String @@ -54,8 +55,13 @@ type WorkflowCommandAdmission = WorkflowCommandAdmitted | WorkflowCommandRefused { reason: String } -fn is_legacy_attempt(modeled_state_present: Bool) -> Bool { - !modeled_state_present +// Legacy standing comes only from an ESTABLISHED absence of the modeled-state pointer. An +// unreadable pointer has unknown standing and is never legacy here. +fn is_legacy_attempt(modeled_state: WorkflowModeledStateStanding) -> Bool { + match modeled_state { + WorkflowModeledStateAbsent => true + _ => false + } } fn admit_start_new_attempt( @@ -105,8 +111,8 @@ fn admit_retry_mechanism(mints_attempt: Bool) -> WorkflowCommandAdmission { } } -fn legacy_attempt_cannot_satisfy_obligations(modeled_state_present: Bool) -> Bool { - is_legacy_attempt(modeled_state_present: modeled_state_present) +fn legacy_attempt_cannot_satisfy_obligations(modeled_state: WorkflowModeledStateStanding) -> Bool { + is_legacy_attempt(modeled_state: modeled_state) } fn attempt_key_short_form(key: AttemptKey) -> String { diff --git a/dag/gunbc/roadmap/roadmap_workflow_progress.dag b/dag/gunbc/roadmap/roadmap_workflow_progress.dag index 5bdb8f03609..ef185863389 100644 --- a/dag/gunbc/roadmap/roadmap_workflow_progress.dag +++ b/dag/gunbc/roadmap/roadmap_workflow_progress.dag @@ -207,11 +207,19 @@ fn attempt_worktree_observation_of(path: String, marker: FilesystemFileObservati } } +// The modeled-state pointer's standing, carried as a typed fact: an attempt whose pointer is +// established absent is a legacy attempt; an attempt whose pointer could not be read has +// UNKNOWN standing, and no downstream route may mint legacy standing from that unknown. +type WorkflowModeledStateStanding + = WorkflowModeledStateObserved + | WorkflowModeledStateAbsent + | WorkflowModeledStateUnreadable { cause: NonEmptyStr } + type WorkflowAttemptEvidence { node_id: String attempt_key: String branch: String - modeled_state_present: Bool + modeled_state: WorkflowModeledStateStanding worktree: AttemptWorktreeObservation admission_receipt_readable: Bool admission_receipt: String @@ -392,7 +400,25 @@ fn provider_execution_reconcile_process( fn workflow_provider_from_evidence( evidence: WorkflowAttemptEvidence, ) -> ProviderExecutionState { - if !evidence.modeled_state_present { + match evidence.modeled_state { + WorkflowModeledStateUnreadable { cause } => + ProviderEventsRefused { + activity: "attempt standing unknown", + detail: join(["this attempt's current-attempt-key pointer could not be read, so whether it predates modeled admission is unknown and supersession is refused: ", cause], ""), + } + WorkflowModeledStateAbsent => + ProviderEventsRefused { + activity: legacy_attempt_supersedable_label, + detail: legacy_attempt_supersedable_detail, + } + WorkflowModeledStateObserved => workflow_provider_from_observed(evidence: evidence) + } +} + +fn workflow_provider_from_observed( + evidence: WorkflowAttemptEvidence, +) -> ProviderExecutionState { + if !evidence.admission_receipt_readable { ProviderEventsRefused { activity: legacy_attempt_supersedable_label, detail: legacy_attempt_supersedable_detail, @@ -1087,9 +1113,12 @@ fn workflow_segment_from_evidence( }, } WorkspaceWorkflowSegment => - if !evidence.modeled_state_present { - WorkflowSegment { kind: kind, state: WorkflowSegmentRefused, detail: "legacy branch has no modeled workspace publication receipt" } - } else { + match evidence.modeled_state { + WorkflowModeledStateUnreadable { cause } => + WorkflowSegment { kind: kind, state: WorkflowSegmentRefused, detail: join(["the attempt's standing is unknown because its current-attempt-key pointer could not be read: ", cause], "") } + WorkflowModeledStateAbsent => + WorkflowSegment { kind: kind, state: WorkflowSegmentRefused, detail: "legacy branch has no modeled workspace publication receipt" } + WorkflowModeledStateObserved => match evidence.worktree { AttemptWorktreePresent { path: _ } => WorkflowSegment { kind: kind, state: WorkflowSegmentComplete, detail: "attempt branch and worktree preceded state publication, and the worktree is present" } @@ -1326,7 +1355,7 @@ fn workflow_attempt_progress( evidence: WorkflowAttemptEvidence, ) -> WorkflowAttemptProgress { let provider = workflow_provider_from_evidence(evidence: evidence) - let admitted = evidence.modeled_state_present + let admitted = evidence.modeled_state == WorkflowModeledStateObserved && evidence.admission_receipt_readable && receipt_records_admission(receipt: evidence.admission_receipt) let segments = map( diff --git a/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag b/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag index 5fe8c9ae55c..ba6238a5c59 100644 --- a/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag @@ -1219,7 +1219,7 @@ fn completed_attempt_progress(node_id: String) -> WorkflowAttemptProgress { node_id: node_id, attempt_key: "feedfacefeedface", branch: join(["dispatch/", node_id, "-afeedfacefeedface"], ""), - modeled_state_present: true, + modeled_state: WorkflowModeledStateObserved, worktree: AttemptWorktreePresent { path: "/wt/fixture" }, admission_receipt_readable: true, admission_receipt: "{\"schema\": \"roadmap-dispatch-environment/v1\", \"status\": \"admitted\"}", @@ -1311,7 +1311,7 @@ test fn agent_incomplete_attempt_defers_verification() -> Bool { node_id: "belt-verify-running-node", attempt_key: "feedfacefeedface", branch: "dispatch/belt-verify-running-node-afeedfacefeedface", - modeled_state_present: true, + modeled_state: WorkflowModeledStateObserved, worktree: AttemptWorktreePresent { path: "/wt/fixture" }, admission_receipt_readable: true, admission_receipt: "{\"schema\": \"roadmap-dispatch-environment/v1\", \"status\": \"admitted\"}", diff --git a/dag/test/claim/roadmap/roadmap_event_carrier_witness_test.dag b/dag/test/claim/roadmap/roadmap_event_carrier_witness_test.dag index 2cd62382493..0df0cf4a755 100644 --- a/dag/test/claim/roadmap/roadmap_event_carrier_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_event_carrier_witness_test.dag @@ -1,6 +1,7 @@ module test.claim.roadmap.roadmap_event_carrier_witness_test import std.types { Bool, String, FilePath, NonEmptyStr } +import extdeps.filesystem.filesystem_io { filesystem_file_observation, filesystem_listing_observation, FilesystemFileIndeterminate, FilesystemReadRefused, FilesystemFileObservation } import gunbc.roadmap_model { RoadmapNodeId } import gunbc.principal_projection { PrincipalRef } import gunbc.roadmap.roadmap_event_log { RoadmapEvent, Claimed, roadmap_event_envelope, RoadmapEventId, EventPrincipalAuthenticated } @@ -68,3 +69,21 @@ test fn the_all_events_read_concatenates_and_the_first_refusal_refuses_the_whole && (match refused { AllEventsReadRefused { step, reason } => step == "node node-two decode" && reason == "corrupt" AllEventsRead { envelopes: _ } => false }) && (match empty { AllEventsRead { envelopes } => count(envelopes) == 0 AllEventsReadRefused { step: _, reason: _ } => false }) } + +// RED control at the receipt-flow site: the flow's unreadable arm consumes this observation, and +// the listing-refused world must arrive with the owner's "could not be listed" cause -- existence +// unknown -- never a claim that the receipt is listed. (The append flow itself is wet -- it runs +// the carrier program -- so the dry control pins the observation boundary it consumes.) +test fn a_refused_listing_at_the_receipt_site_arrives_with_existence_unknown_never_listed() -> Bool { + match filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/ops", success: false, entries: "", error: "Permission denied (os error 13)"), + name: "receipt.json", + path: "/srv/gunbc/ops/receipt.json", + read: FilesystemReadRefused { error: "not reached" }, + ) { + FilesystemFileIndeterminate { cause } => + string_contains(s: cause, pattern: "could not be listed") + && !string_contains(s: cause, pattern: "is listed") + _ => false + } +} diff --git a/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag b/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag index 106ac70c100..c969773388b 100644 --- a/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_served_observation_witness_test.dag @@ -254,6 +254,29 @@ test fn red_control_a_listed_body_that_cannot_be_read_is_unreadable_not_missing( && !string_contains(s: r.body, pattern: "ServedObservationBodyMissing") } +// RED control: the listing itself refused, so existence was never established either way. The +// refusal carries the owner's listing cause ("could not be listed") and never claims the body +// exists. +test fn a_refused_listing_leaves_existence_unknown_and_never_claims_the_body_exists() -> Bool { + let daily = fixture_entry(kind: DailyPage) + let r = served_observation_body_response( + entry: daily, + body: served_observation_body_read_from_observation( + entry: daily, + observation: filesystem_file_observation( + listing: filesystem_listing_observation(directory: "/srv/gunbc/served", success: false, entries: "", error: "Permission denied (os error 13)"), + name: "daily.html", + path: "/srv/gunbc/served/daily.html", + read: FilesystemReadRefused { error: "not reached" }, + ), + ), + ) + r.status == 503 + && string_contains(s: r.body, pattern: "could not be observed") + && string_contains(s: r.body, pattern: "could not be listed") + && !string_contains(s: r.body, pattern: "exists") +} + test fn a_refused_daily_render_is_served_as_plain_text_not_html() -> Bool { let entry = ServedObservationEntry { kind: DailyPage, status: 500, digest: served_observation_body_digest(body: "page render refused") } let r = served_observation_body_response( diff --git a/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag b/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag index 7d6d3e95e40..e60685fe5fd 100644 --- a/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag @@ -846,7 +846,7 @@ fn verify_lamp_evidence(receipt: String) -> WorkflowAttemptEvidence { node_id: "v1-emitter-fixed-point", attempt_key: "feedfacefeedface", branch: "dispatch/v1-emitter-fixed-point-afeedfacefeedface", - modeled_state_present: true, + modeled_state: WorkflowModeledStateObserved, worktree: AttemptWorktreePresent { path: "/wt/fixture" }, admission_receipt_readable: true, admission_receipt: "{\"schema\": \"roadmap-dispatch-environment/v1\", \"status\": \"admitted\"}", diff --git a/dag/test/claim/roadmap/roadmap_workflow_command_witness_test.dag b/dag/test/claim/roadmap/roadmap_workflow_command_witness_test.dag index 3e9a39a9f97..e8f4c391831 100644 --- a/dag/test/claim/roadmap/roadmap_workflow_command_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_workflow_command_witness_test.dag @@ -85,8 +85,9 @@ test fn retry_mechanism_cannot_mint_attempt() -> Bool { } test fn witness_legacy_attempt_cannot_satisfy_obligations() -> Bool { - legacy_attempt_cannot_satisfy_obligations(modeled_state_present: false) - && !legacy_attempt_cannot_satisfy_obligations(modeled_state_present: true) + legacy_attempt_cannot_satisfy_obligations(modeled_state: WorkflowModeledStateAbsent) + && !legacy_attempt_cannot_satisfy_obligations(modeled_state: WorkflowModeledStateObserved) + && !legacy_attempt_cannot_satisfy_obligations(modeled_state: WorkflowModeledStateUnreadable { cause: "Permission denied (os error 13)" }) } test fn legacy_attempt_projects_as_supersedable_not_fault() -> Bool { @@ -94,7 +95,7 @@ test fn legacy_attempt_projects_as_supersedable_not_fault() -> Bool { node_id: "legacy-node", attempt_key: "", branch: "dispatch/legacy-node", - modeled_state_present: false, + modeled_state: WorkflowModeledStateAbsent, worktree: AttemptWorktreeUnobserved { path: "", cause: "no modeled state pointer" }, admission_receipt_readable: false, admission_receipt: "", diff --git a/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag b/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag index 244c6d77c75..e1ac457b84b 100644 --- a/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag @@ -16,6 +16,7 @@ import gunbc.roadmap_provider_events { ProviderEventsRefused, } import gunbc.roadmap_workflow_progress { + WorkflowModeledStateStanding, WorkflowModeledStateObserved, WorkflowModeledStateAbsent, WorkflowModeledStateUnreadable, AttemptWorktreeObservation, AttemptWorktreePresent, AttemptWorktreeAbsent, AttemptWorktreeUnobserved, review_segment, WorkflowAttemptEvidence, @@ -119,7 +120,7 @@ fn fixture_evidence( node_id: "node-1", attempt_key: "feedfacefeedface", branch: "dispatch/node-1-afeedfacefeedface", - modeled_state_present: true, + modeled_state: WorkflowModeledStateObserved, worktree: AttemptWorktreePresent { path: "/wt/node-1-afeedfacefeedface" }, admission_receipt_readable: true, admission_receipt: "{\"schema\": \"roadmap-dispatch-environment/v1\", \"status\": \"admitted\"}", @@ -160,6 +161,40 @@ fn fixture_evidence( } } +fn fixture_evidence_with_standing( + standing: WorkflowModeledStateStanding, + events: String, + process: WorkerProcessEvidence, +) -> WorkflowAttemptEvidence { + let base = fixture_evidence(events: events, process: process) + WorkflowAttemptEvidence { + node_id: base.node_id, + attempt_key: base.attempt_key, + branch: base.branch, + modeled_state: standing, + worktree: base.worktree, + admission_receipt_readable: base.admission_receipt_readable, + admission_receipt: base.admission_receipt, + spawn_failure_present: base.spawn_failure_present, + spawn_failure: base.spawn_failure, + provider_events_readable: base.provider_events_readable, + provider_events: base.provider_events, + provider_events_error: base.provider_events_error, + process: base.process, + validation_summary: base.validation_summary, + verification_subject: base.verification_subject, + verification_selection: base.verification_selection, + verification_source: base.verification_source, + publication_subject: base.publication_subject, + publication_source: base.publication_source, + review: base.review, + submission_capture: base.submission_capture, + integration: base.integration, + goal_audit: base.goal_audit, + session_placement: base.session_placement, + } +} + fn segment_state( segments: List, key: String, @@ -252,6 +287,37 @@ test fn completed_event_and_nonzero_exit_refuse_contradiction() -> Bool { } } +// COMPOSED control (side chat on this PR): the evidence construction carries the typed pointer +// standing, so this route goes evidence -> provider, not helper text. An attempt whose +// current-attempt-key pointer could not be read has UNKNOWN standing: the provider must refuse +// with the standing-unknown arm and must NOT mint the legacy, supersedable standing that an +// established absence carries. +test fn an_unreadable_pointer_refuses_with_unknown_standing_and_never_legacy_standing() -> Bool { + let unreadable = workflow_provider_from_evidence(evidence: fixture_evidence_with_standing( + standing: WorkflowModeledStateUnreadable { cause: "Permission denied (os error 13)" }, + events: "{\"type\":\"turn.completed\",\"usage\":{}}\n", + process: WorkerProcessExited { exit_code: 0, last_command: "node" }, + )) + let absent = workflow_provider_from_evidence(evidence: fixture_evidence_with_standing( + standing: WorkflowModeledStateAbsent, + events: "{\"type\":\"turn.completed\",\"usage\":{}}\n", + process: WorkerProcessExited { exit_code: 0, last_command: "node" }, + )) + match unreadable { + ProviderEventsRefused { activity, detail } => + activity == "attempt standing unknown" + && string_contains(s: detail, pattern: "could not be read") + && !string_contains(s: detail, pattern: "legacy") + && match absent { + ProviderEventsRefused { activity: absent_activity, detail: absent_detail } => + absent_activity != "attempt standing unknown" + && string_contains(s: absent_detail, pattern: "legacy") + _ => false + } + _ => false + } +} + // review 44032 REDs. The Agent obligation's design contract requires a compatible process exit, so // turn.completed alone must never paint agent complete. These three witnesses pin the arms that // previously preserved ProviderCompleted from incomplete evidence: still-running stays active, @@ -393,7 +459,7 @@ test fn forged_admission_receipt_cannot_complete_environment() -> Bool { node_id: base.node_id, attempt_key: base.attempt_key, branch: base.branch, - modeled_state_present: base.modeled_state_present, + modeled_state: base.modeled_state, worktree: base.worktree, admission_receipt_readable: base.admission_receipt_readable, admission_receipt: nested_admitted_forgery(), @@ -429,7 +495,7 @@ test fn persisted_staging_failure_precedes_empty_event_stream() -> Bool { node_id: base.node_id, attempt_key: base.attempt_key, branch: base.branch, - modeled_state_present: base.modeled_state_present, + modeled_state: base.modeled_state, worktree: base.worktree, admission_receipt_readable: base.admission_receipt_readable, admission_receipt: base.admission_receipt, @@ -463,7 +529,7 @@ test fn legacy_attempt_refuses_unobserved_early_stages() -> Bool { node_id: "legacy", attempt_key: "", branch: "dispatch/legacy", - modeled_state_present: false, + modeled_state: WorkflowModeledStateAbsent, worktree: AttemptWorktreeUnobserved { path: "", cause: "no modeled state pointer" }, admission_receipt_readable: false, admission_receipt: "", @@ -522,7 +588,7 @@ test fn readable_but_invalid_admission_receipt_refuses_progress() -> Bool { node_id: base.node_id, attempt_key: base.attempt_key, branch: base.branch, - modeled_state_present: base.modeled_state_present, + modeled_state: base.modeled_state, worktree: base.worktree, admission_receipt_readable: true, admission_receipt: "{\"schema\": \"roadmap-dispatch-environment/v1\", \"status\": \"refused\"}", @@ -666,7 +732,7 @@ fn publication_segment_of( node_id: base.node_id, attempt_key: base.attempt_key, branch: base.branch, - modeled_state_present: base.modeled_state_present, + modeled_state: base.modeled_state, worktree: base.worktree, admission_receipt_readable: base.admission_receipt_readable, admission_receipt: base.admission_receipt, @@ -829,7 +895,7 @@ fn evidence_with_worktree(base: WorkflowAttemptEvidence, worktree: AttemptWorktr node_id: base.node_id, attempt_key: base.attempt_key, branch: base.branch, - modeled_state_present: base.modeled_state_present, + modeled_state: base.modeled_state, worktree: worktree, admission_receipt_readable: base.admission_receipt_readable, admission_receipt: base.admission_receipt, diff --git a/dag/test/claim/verification_evidence_addressing_witness_test.dag b/dag/test/claim/verification_evidence_addressing_witness_test.dag index e3a8ec19a92..c73e2c64199 100644 --- a/dag/test/claim/verification_evidence_addressing_witness_test.dag +++ b/dag/test/claim/verification_evidence_addressing_witness_test.dag @@ -39,6 +39,7 @@ import gunbc.roadmap_status { DerivableLine, Unsigned } import gunbc.roadmap_execution_contract { ExecutionContractUnspecified } import gunbc.roadmap_provider_events { ProviderCompleted } import gunbc.roadmap_workflow_progress { + WorkflowModeledStateObserved, WorkflowAttemptEvidence, AttemptWorktreePresent, workflow_attempt_progress, @@ -344,7 +345,7 @@ fn fixture_evidence( node_id: "recut-a", attempt_key: "feedfacefeedface", branch: "dispatch/recut-a-afeedfacefeedface", - modeled_state_present: true, + modeled_state: WorkflowModeledStateObserved, worktree: AttemptWorktreePresent { path: "/wt" }, admission_receipt_readable: true, admission_receipt: "{\"schema\": \"roadmap-dispatch-environment/v1\", \"status\": \"admitted\"}", From 59ada3e9f6079a60fb4606cae6245cb731dce914 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Sun, 4 Oct 2026 23:38:24 +0000 Subject: [PATCH 09/12] roadmap: move WorkflowModeledStateStanding into workflow_command The rework added workflow_command -> workflow_progress (for the standing type) while workflow_progress already imports workflow_command -- a dependency cycle every importer closure refused. The type moves to the lower module; workflow_progress and the witnesses import it from there, along the existing edge. No other change. --- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 4 +++- dag/gunbc/roadmap/roadmap_workflow_command.dag | 11 +++++++++-- dag/gunbc/roadmap/roadmap_workflow_progress.dag | 6 +----- .../roadmap_validation_oracle_witness_test.dag | 1 + .../roadmap_workflow_progress_witness_test.dag | 4 +++- .../verification_evidence_addressing_witness_test.dag | 2 +- 6 files changed, 18 insertions(+), 10 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index a0a0561d944..dcc8ea42551 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -522,10 +522,12 @@ import gunbc.roadmap_session_placement { session_placement_observe_for_attempt, SessionPlacementUnreadable, session_placement_settle_pass, SessionSettlePass, SessionSettlePassRan, SessionSettlePassRefused, session_settle_pass_wire, } +import gunbc.roadmap_workflow_command { + WorkflowModeledStateStanding, WorkflowModeledStateObserved, WorkflowModeledStateAbsent, WorkflowModeledStateUnreadable, +} import gunbc.roadmap_workflow_progress { AttemptWorktreeObservation, AttemptWorktreePresent, AttemptWorktreeAbsent, AttemptWorktreeUnobserved, attempt_worktree_observation_of, WorkflowAttemptEvidence, - WorkflowModeledStateStanding, WorkflowModeledStateObserved, WorkflowModeledStateAbsent, WorkflowModeledStateUnreadable, WorkflowAttemptProgress, workflow_attempt_progress, workflow_attempt_progress_json_members, diff --git a/dag/gunbc/roadmap/roadmap_workflow_command.dag b/dag/gunbc/roadmap/roadmap_workflow_command.dag index db1aeb16540..804da3183ea 100644 --- a/dag/gunbc/roadmap/roadmap_workflow_command.dag +++ b/dag/gunbc/roadmap/roadmap_workflow_command.dag @@ -1,12 +1,19 @@ module gunbc.roadmap_workflow_command -import std.types { String, Bool, Int, List } +import std.types { String, Bool, Int, List, NonEmptyStr } import std.content_hash { ContentHash } import gunbc.roadmap_verification_receipt { VerificationSubject } -import gunbc.roadmap_workflow_progress { WorkflowModeledStateStanding, WorkflowModeledStateAbsent } type AttemptKey = String +// The attempt's modeled-state pointer standing, as an established fact of the read that observed it: +// Observed carries the evidence; Absent is established absence; Unreadable is a pointer that could +// not be read, with the owner's located cause -- never treated as absence. +type WorkflowModeledStateStanding + = WorkflowModeledStateObserved + | WorkflowModeledStateAbsent + | WorkflowModeledStateUnreadable { cause: NonEmptyStr } + // THE THREE RETRY SHAPES ARE FIRST-CLASS COMMANDS, NOT INCIDENTAL TMUX CLEANUP. RetryMechanism // retries a failed mechanism at the same input identity without a new attempt or a consumed review // round. ResumeAttempt continues the same branch and worktree with a new worker turn and new head: diff --git a/dag/gunbc/roadmap/roadmap_workflow_progress.dag b/dag/gunbc/roadmap/roadmap_workflow_progress.dag index ef185863389..83debfa9d21 100644 --- a/dag/gunbc/roadmap/roadmap_workflow_progress.dag +++ b/dag/gunbc/roadmap/roadmap_workflow_progress.dag @@ -185,6 +185,7 @@ import gunbc.roadmap.roadmap_fanin { import gunbc.roadmap_workflow_command { legacy_attempt_supersedable_label, legacy_attempt_supersedable_detail, + WorkflowModeledStateStanding, WorkflowModeledStateObserved, WorkflowModeledStateAbsent, WorkflowModeledStateUnreadable, } // THE ATTEMPT'S WORKTREE, OBSERVED RATHER THAN INFERRED. The Workspace segment read complete from the @@ -210,11 +211,6 @@ fn attempt_worktree_observation_of(path: String, marker: FilesystemFileObservati // The modeled-state pointer's standing, carried as a typed fact: an attempt whose pointer is // established absent is a legacy attempt; an attempt whose pointer could not be read has // UNKNOWN standing, and no downstream route may mint legacy standing from that unknown. -type WorkflowModeledStateStanding - = WorkflowModeledStateObserved - | WorkflowModeledStateAbsent - | WorkflowModeledStateUnreadable { cause: NonEmptyStr } - type WorkflowAttemptEvidence { node_id: String attempt_key: String diff --git a/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag b/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag index e60685fe5fd..a9fde58ee90 100644 --- a/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_validation_oracle_witness_test.dag @@ -19,6 +19,7 @@ import std.content_hash { } import extdeps.pin { CrossFamilyIdentityIncomparable } import gunbc.git_diff_change_window { git_diff_window_absent_identity } +import gunbc.roadmap_workflow_command { WorkflowModeledStateObserved } import gunbc.roadmap_workflow_progress { AttemptWorktreePresent, WorkflowAttemptEvidence, diff --git a/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag b/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag index e1ac457b84b..42cd455d7ae 100644 --- a/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag @@ -15,8 +15,10 @@ import gunbc.roadmap_provider_events { ProviderFailed, ProviderEventsRefused, } -import gunbc.roadmap_workflow_progress { +import gunbc.roadmap_workflow_command { WorkflowModeledStateStanding, WorkflowModeledStateObserved, WorkflowModeledStateAbsent, WorkflowModeledStateUnreadable, +} +import gunbc.roadmap_workflow_progress { AttemptWorktreeObservation, AttemptWorktreePresent, AttemptWorktreeAbsent, AttemptWorktreeUnobserved, review_segment, WorkflowAttemptEvidence, diff --git a/dag/test/claim/verification_evidence_addressing_witness_test.dag b/dag/test/claim/verification_evidence_addressing_witness_test.dag index c73e2c64199..2ee1480fcdc 100644 --- a/dag/test/claim/verification_evidence_addressing_witness_test.dag +++ b/dag/test/claim/verification_evidence_addressing_witness_test.dag @@ -38,8 +38,8 @@ import gunbc.roadmap_model { import gunbc.roadmap_status { DerivableLine, Unsigned } import gunbc.roadmap_execution_contract { ExecutionContractUnspecified } import gunbc.roadmap_provider_events { ProviderCompleted } +import gunbc.roadmap_workflow_command { WorkflowModeledStateObserved } import gunbc.roadmap_workflow_progress { - WorkflowModeledStateObserved, WorkflowAttemptEvidence, AttemptWorktreePresent, workflow_attempt_progress, From 8818ca1f88ad1147c0ba135258477f492d771921 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 5 Oct 2026 01:55:24 +0000 Subject: [PATCH 10/12] roadmap: is_legacy_attempt matches the standing constructors explicitly The wildcard arm over the closed coproduct refused the floor (NonFoldResidueRosterDiverged, unrostered live site). Observed and Unreadable are matched by name; Unreadable's cause is carried but the arm's answer is the same as before: not legacy. --- dag/gunbc/roadmap/roadmap_workflow_command.dag | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dag/gunbc/roadmap/roadmap_workflow_command.dag b/dag/gunbc/roadmap/roadmap_workflow_command.dag index 804da3183ea..b57174ff775 100644 --- a/dag/gunbc/roadmap/roadmap_workflow_command.dag +++ b/dag/gunbc/roadmap/roadmap_workflow_command.dag @@ -67,7 +67,8 @@ type WorkflowCommandAdmission fn is_legacy_attempt(modeled_state: WorkflowModeledStateStanding) -> Bool { match modeled_state { WorkflowModeledStateAbsent => true - _ => false + WorkflowModeledStateObserved => false + WorkflowModeledStateUnreadable { cause: _ } => false } } From aab817c127cf0439b426da485b49d50597899cf2 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 5 Oct 2026 02:52:23 +0000 Subject: [PATCH 11/12] roadmap: repair the observed-chain extraction; absent-arm assertion names its real detail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The composed control went red in CI: my extraction of the observed chain had mangled its first arm — the legacy label leaked in and the real refusal ('attempt state refused' for an unreadable admission receipt) became a dead duplicate of the same condition. The observed chain now matches main's: unreadable receipt refuses with 'attempt state refused', then receipt-record, spawn failure, event capture, reconcile. The witness's absent-arm assertion checked the detail for the word 'legacy'; the detail says 'predates modeled admission' — assert that instead. --- dag/gunbc/roadmap/roadmap_workflow_progress.dag | 5 ----- .../claim/roadmap/roadmap_workflow_progress_witness_test.dag | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_workflow_progress.dag b/dag/gunbc/roadmap/roadmap_workflow_progress.dag index 83debfa9d21..4ccaf62f0a2 100644 --- a/dag/gunbc/roadmap/roadmap_workflow_progress.dag +++ b/dag/gunbc/roadmap/roadmap_workflow_progress.dag @@ -415,11 +415,6 @@ fn workflow_provider_from_observed( evidence: WorkflowAttemptEvidence, ) -> ProviderExecutionState { if !evidence.admission_receipt_readable { - ProviderEventsRefused { - activity: legacy_attempt_supersedable_label, - detail: legacy_attempt_supersedable_detail, - } - } else if !evidence.admission_receipt_readable { ProviderEventsRefused { activity: "attempt state refused", detail: "current attempt pointer exists but its admission receipt is unreadable", diff --git a/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag b/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag index 42cd455d7ae..8876cd0ef53 100644 --- a/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag @@ -313,7 +313,7 @@ test fn an_unreadable_pointer_refuses_with_unknown_standing_and_never_legacy_sta && match absent { ProviderEventsRefused { activity: absent_activity, detail: absent_detail } => absent_activity != "attempt standing unknown" - && string_contains(s: absent_detail, pattern: "legacy") + && string_contains(s: absent_detail, pattern: "predates modeled admission") _ => false } _ => false From 4d998bf7ee0f380714eb136d240b8ffe8c0396a8 Mon Sep 17 00:00:00 2001 From: gunbc-ci-auto-heal Date: Mon, 5 Oct 2026 05:53:31 +0000 Subject: [PATCH 12/12] roadmap: an unreadable pointer carries non-absence subordinate facts Side chat review 5409834403, blocker 1 (rest): both pointer branches of belt_workflow_attempt_evidence_without_modeled_state minted the same absence-shaped subordinate facts, and verification_segment reads SelectionAbsent as pending, with review and goal audit inheriting it -- an unreadable pointer still rendered as pending downstream. Fix (a), closer to the ruling: the pointer standing now decides the subordinate facts' standing. Established ABSENCE keeps the absence-shaped facts the legacy arm means; an UNREADABLE pointer carries SelectionUnaddressable, VerificationReceiptUnreadable, ReceiptSourceUnreadable and review_report_refused -- the wrong state is unwritable. Fix (b) for the fold: workflow_segment_from_evidence gates on the modeled standing before reading subordinate fields -- an unreadable pointer refuses every segment (verification, review, goal audit) with the located cause, while Observed and Absent take the existing fold. Controls: through workflow_attempt_progress (unreadable refuses verify/review/audit, absent still renders pending) and over the pure standing helpers (each non-absence variant carries the pointer's cause; absent still yields the absence variants). --- dag/gunbc/roadmap/roadmap_belt_actuate.dag | 50 +++++++++++++++++-- .../roadmap/roadmap_workflow_progress.dag | 20 ++++++++ .../roadmap_belt_actuate_witness_test.dag | 37 ++++++++++++++ ...roadmap_workflow_progress_witness_test.dag | 35 +++++++++++++ 4 files changed, 138 insertions(+), 4 deletions(-) diff --git a/dag/gunbc/roadmap/roadmap_belt_actuate.dag b/dag/gunbc/roadmap/roadmap_belt_actuate.dag index dcc8ea42551..c79bd07c0c4 100644 --- a/dag/gunbc/roadmap/roadmap_belt_actuate.dag +++ b/dag/gunbc/roadmap/roadmap_belt_actuate.dag @@ -5536,6 +5536,48 @@ type WorkflowModeledStatePointerState = WorkflowModeledStatePointerAbsent | WorkflowModeledStatePointerUnreadable { cause: NonEmptyStr } +// The pointer standing decides the subordinate facts' standing: established ABSENCE (no pointer) +// carries absence-shaped facts -- the legacy arm's own meaning -- while an UNREADABLE pointer +// carries non-absence variants: the wrong state is unwritable, and nothing downstream may read an +// unreadable pointer as "not yet". +fn selection_standing_for_pointer(pointer_state: WorkflowModeledStatePointerState) -> CurrentVerificationSelectionObservation { + match pointer_state { + WorkflowModeledStatePointerAbsent => SelectionAbsent + WorkflowModeledStatePointerUnreadable { cause } => + SelectionUnaddressable { + cause: join(["this attempt's current-attempt-key pointer could not be read, so whether a verification window was selected is unknown: ", cause], ""), + } + } +} + +fn verification_source_standing_for_pointer(pointer_state: WorkflowModeledStatePointerState) -> VerificationReceiptFileSource { + match pointer_state { + WorkflowModeledStatePointerAbsent => VerificationReceiptAbsent + WorkflowModeledStatePointerUnreadable { cause } => + VerificationReceiptUnreadable { + detail: join(["this attempt's current-attempt-key pointer could not be read, so its verification receipt could not be located: ", cause], ""), + } + } +} + +fn publication_source_standing_for_pointer(pointer_state: WorkflowModeledStatePointerState) -> PublicationReceiptSource { + match pointer_state { + WorkflowModeledStatePointerAbsent => ReceiptSourceAbsent + WorkflowModeledStatePointerUnreadable { cause } => + ReceiptSourceUnreadable { + detail: join(["this attempt's current-attempt-key pointer could not be read, so its publication receipt could not be located: ", cause], ""), + } + } +} + +fn review_report_standing_for_pointer(pointer_state: WorkflowModeledStatePointerState) -> ReviewReport { + match pointer_state { + WorkflowModeledStatePointerAbsent => review_report_absent(reason: "this attempt has no modeled state pointer, so no review report exists for it") + WorkflowModeledStatePointerUnreadable { cause } => + review_report_refused(reason: join(["this attempt's current-attempt-key pointer could not be read, so its review report could not be located: ", cause], "")) + } +} + fn belt_workflow_attempt_evidence_without_modeled_state( instance: HostDashboardInstance, panes: BeltAttemptPanes, @@ -5593,19 +5635,19 @@ fn belt_workflow_attempt_evidence_without_modeled_state( verification_subject: VerificationSubjectUnobserved { reason: pointer_reason, }, - verification_selection: SelectionAbsent, - verification_source: VerificationReceiptAbsent, + verification_selection: selection_standing_for_pointer(pointer_state: pointer_state), + verification_source: verification_source_standing_for_pointer(pointer_state: pointer_state), publication_subject: PublicationSubjectUnobserved { reason: pointer_reason, }, - publication_source: ReceiptSourceAbsent, + publication_source: publication_source_standing_for_pointer(pointer_state: pointer_state), submission_capture: CaptureUnreadable { reason: pointer_reason, }, integration: IntegrationReceiptUnreadable { reason: pointer_reason, }, - review: review_report_absent(reason: pointer_reason), + review: review_report_standing_for_pointer(pointer_state: pointer_state), goal_audit: [], session_placement: SessionPlacementUnreadable { unit: "", detail: placement_detail }, } diff --git a/dag/gunbc/roadmap/roadmap_workflow_progress.dag b/dag/gunbc/roadmap/roadmap_workflow_progress.dag index 4ccaf62f0a2..666df0f50ce 100644 --- a/dag/gunbc/roadmap/roadmap_workflow_progress.dag +++ b/dag/gunbc/roadmap/roadmap_workflow_progress.dag @@ -1085,6 +1085,26 @@ fn workflow_segment_from_evidence( evidence: WorkflowAttemptEvidence, provider: ProviderExecutionState, admitted: Bool, +) -> WorkflowSegment { + match evidence.modeled_state { + WorkflowModeledStateUnreadable { cause } => + WorkflowSegment { + kind: kind, + state: WorkflowSegmentRefused, + detail: join(["this attempt's standing is unknown because its current-attempt-key pointer could not be read: ", cause], ""), + } + WorkflowModeledStateObserved => + workflow_segment_from_standing_evidence(kind: kind, evidence: evidence, provider: provider, admitted: admitted) + WorkflowModeledStateAbsent => + workflow_segment_from_standing_evidence(kind: kind, evidence: evidence, provider: provider, admitted: admitted) + } +} + +fn workflow_segment_from_standing_evidence( + kind: WorkflowSegmentKind, + evidence: WorkflowAttemptEvidence, + provider: ProviderExecutionState, + admitted: Bool, ) -> WorkflowSegment { match kind { EnvironmentWorkflowSegment => diff --git a/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag b/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag index ba6238a5c59..5924cd22ed3 100644 --- a/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_belt_actuate_witness_test.dag @@ -51,6 +51,9 @@ import gunbc.roadmap_belt_actuate { belt_candidate_subject_reading, CandidateSubjectSettled, CandidateSubjectBound, VerificationPassedConfirmed, VerificationNotPassed, VerificationUnreadable, belt_publish_pass_outcome_from, + WorkflowModeledStatePointerState, WorkflowModeledStatePointerAbsent, WorkflowModeledStatePointerUnreadable, + selection_standing_for_pointer, verification_source_standing_for_pointer, + publication_source_standing_for_pointer, review_report_standing_for_pointer, } import gunbc.roadmap.roadmap_submission { SubmissionReadiness, SubmissionReady, SubmissionNeedsContext, @@ -198,6 +201,7 @@ import gunbc.roadmap.roadmap_submission { } import gunbc.roadmap_workflow_stage { WorkflowSegment, VerificationWorkflowSegment, WorkflowSegmentFailed, + WorkflowSegmentRefused, } import gunbc.roadmap_authority { declared_roadmap_nodes, declared_roadmap_edges } import gunbc.worker_lifecycle { WorkerProcessUnobserved } @@ -3319,3 +3323,36 @@ test fn a_failed_release_after_placement_is_carried_on_every_non_spawned_arm() - SpawnNotAdmitted { node_id: _, refusal: _ } => false } } + +// The pointer standing decides the subordinate facts' standing (side chat review 5409834403, fix +// (a)): an UNREADABLE pointer carries the non-absence variants -- the wrong state is unwritable -- +// while established ABSENCE keeps the absence-shaped facts the legacy arm means. +test fn an_unreadable_pointer_carries_non_absence_subordinate_facts() -> Bool { + let denied = WorkflowModeledStatePointerUnreadable { cause: "Permission denied (os error 13)" } + let absent = WorkflowModeledStatePointerAbsent + let sel_bad = selection_standing_for_pointer(pointer_state: denied) + let src_bad = verification_source_standing_for_pointer(pointer_state: denied) + let pub_bad = publication_source_standing_for_pointer(pointer_state: denied) + let rev_bad = review_report_standing_for_pointer(pointer_state: denied) + match sel_bad { + SelectionUnaddressable { cause } => + string_contains(s: cause, pattern: "pointer could not be read") + && match verification_source_standing_for_pointer(pointer_state: denied) { + VerificationReceiptUnreadable { detail } => + string_contains(s: detail, pattern: "pointer could not be read") + && match publication_source_standing_for_pointer(pointer_state: denied) { + ReceiptSourceUnreadable { detail: pd } => + string_contains(s: pd, pattern: "pointer could not be read") + && rev_bad.reconciliation.state == WorkflowSegmentRefused + && string_contains(s: rev_bad.reconciliation.detail, pattern: "pointer could not be read") + && match selection_standing_for_pointer(pointer_state: absent) { + SelectionAbsent => true + _ => false + } + _ => false + } + _ => false + } + _ => false + } +} diff --git a/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag b/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag index 8876cd0ef53..39a797cea7b 100644 --- a/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag +++ b/dag/test/claim/roadmap/roadmap_workflow_progress_witness_test.dag @@ -197,6 +197,19 @@ fn fixture_evidence_with_standing( } } +fn segment_detail( + segments: List, + key: String, +) -> String { + fold(segments, init: "", f: fn(acc, segment) { + if acc == "" && workflow_segment_kind_key(kind: segment.kind) == key { + segment.detail + } else { + acc + } + }) +} + fn segment_state( segments: List, key: String, @@ -320,6 +333,28 @@ test fn an_unreadable_pointer_refuses_with_unknown_standing_and_never_legacy_sta } } +// Composed control THROUGH the progress fold (side chat review 5409834403): an unreadable pointer +// refuses verification, review AND goal audit -- never pending, never absence-shaped -- while +// established absence still renders the legacy arm (pending). +test fn an_unreadable_pointer_refuses_verification_review_and_audit_through_workflow_attempt_progress() -> Bool { + let unreadable = workflow_attempt_progress(evidence: fixture_evidence_with_standing( + standing: WorkflowModeledStateUnreadable { cause: "Permission denied (os error 13)" }, + events: "{\"type\":\"turn.completed\",\"usage\":{}}\n", + process: WorkerProcessExited { exit_code: 0, last_command: "node" }, + )) + let absent = workflow_attempt_progress(evidence: fixture_evidence_with_standing( + standing: WorkflowModeledStateAbsent, + events: "{\"type\":\"turn.completed\",\"usage\":{}}\n", + process: WorkerProcessExited { exit_code: 0, last_command: "node" }, + )) + segment_state(unreadable.segments, "verify") == "refused" + && segment_state(unreadable.segments, "review") == "refused" + && segment_state(unreadable.segments, "audit") == "refused" + && string_contains(s: segment_detail(unreadable.segments, "verify"), pattern: "standing is unknown") + && segment_state(absent.segments, "verify") == "pending" + && segment_state(absent.segments, "review") == "pending" +} + // review 44032 REDs. The Agent obligation's design contract requires a compatible process exit, so // turn.completed alone must never paint agent complete. These three witnesses pin the arms that // previously preserved ProviderCompleted from incomplete evidence: still-running stays active,