Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ All milestones use the routine route and one accountable owner. Technical/model/
Scope: minimal additive typed facts/references for pre-dispatch task snapshots, manifest/recipe identity, policy/window assignment and deviations; derive whole-issue analysis through the existing bounded private read path. Retain current receipt limits, SQLite writer/replay semantics, privacy and public allowlist. Do not copy execution intent into a new journal.
Acceptance: repeatable input snapshots reproduce totals, coverage and assigned-treatment reports; unique costs reconcile across root/children/retry/review/repair; source fixtures cover reordering, duplicates, correction, reopened roots, incomplete CI, provider mismatch, unsupported usage and late arrival. Negative controls for double-counted cache/reasoning, missing child, redraw on retry and cross-workspace join stay visibly invalid/incomplete. Demonstrate bounded output and old-store/schema compatibility, plus source-owned checks appropriate to the change.
Closure: source delivered and compatibility evidence retained. Publication, installed adoption and actual operating collection are separately reported pending; a green synthetic report is not a measured benefit.
C1 source window: [causal declaration receiver](learn-01-causal-receiver.md) records the immutable admission/1 byte contract, bounded snapshot/analyzer behavior and synthetic fixture controls. Local focused source qualification passed; source delivery, C3 exact composition, publication and installed receiver-first adoption remain pending. It does not close .2 or imply installed emission.
Evidence: #3884 delivered the typed observations, bounded private snapshot analysis and retained native inventory source binding. Follow-on source adds schema 11 durable learning-fact order plus immutable `learn-shared-cost-allocation/1` and `learn-shared-cost-authority/1`: a sorted roster and fixed equal-largest-remainder rule must be persisted before every assignment, and later evidence must join the exact retained native source and observed provider total. Historical schema-10 facts retain unknown order. #3916 added protected host-config/2 credential roles and grants, receiver-owned receipt admissions, schema-12 first-fact provenance, a restricted native-collector fact scope, and private snapshot v4. Recovery retains the original authenticated role, replay cannot upgrade provenance, schema-11 history stays unknown, and generic full-corpus submissions remain unqualified. Follow-on hardening binds exact receipt retries to the original enrolled authority, rejects incompatible credentials for one scope, and refuses contradictory protected collector grants in analysis. A protected host command now accepts only dispatch/native selectors, resolves lineage and profile from durable admissions, loads executable/source/principal from private installation configuration, clears inherited environment, recomputes the roster/counters/binding, atomically retains the canonical envelope, and submits it through the restricted collector principal. The roadmap adapter selects an installed private Host configuration separately, sends only its durable dispatch/parent/native selectors, and preserves generic/local fallback as UNKNOWN for missing configuration, old Host and refusal; a protected success cannot copy authority into the generic producer. The resolver admits the adapter's exact `collaboration-spawn-agent` lineage and refuses other durable runtimes. Profile mismatch refuses before invocation and exact retry reuses retained bytes. A protected collector corpus passes the credential-provenance check but remains explicitly `native-source-verification-unavailable` and `snapshot-origin-unverified`; therefore every shared-cost comparison also remains `independent-shared-cost-authority-unavailable`. .2 stays open pending published installed configuration/credential, qualified capture custody, and trusted snapshot acquisition. Enrollment, live collection and any efficiency result remain pending.

- [x] **LEARN-01.3 — Integrate fixed profiles and context assembly into the existing orchestrator — route: routine.**
Expand Down
15 changes: 15 additions & 0 deletions docs/roadmaps/learn-01-causal-receiver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# LEARN-01.2 causal declaration receiver

This is the receiver-owned C1 source window of LEARN-01.2. The [LEARN owning plan](2026-09-12-095059-stable-policy-orchestration-and-statistical-learning.md) remains the completion ledger. Coordination owns committed admission/1 and producer publication; this repository owns the versioned `execution-causal-admission/1` telemetry event, immutable retention and bounded analysis.

The event binds its canonical identity, item and dispatch to the embedded admission/1 DTO. It carries canonical base64 of the exact decoded committed admission bytes, their SHA-256, and the verified route-binding and launch-intent SHA-256 references. The receiver parses the closed admission shape, bounded purpose/dependency/retry declaration and canonical invocation lineage; reserializing the DTO cannot replace the retained bytes. Declaration edges preserve their recorded order and source references. A source digest is evidence about committed bytes, not execution authority or access to another workspace.

Schema-14 `ingest_facts` retains the event with first-receipt provenance. Exact replay is idempotent; changed content, revision or kind substitution involving a causal declaration refuses atomically. The existing `learningObservations` relation exposes the opaque canonical fact under its existing row, byte and selected-item bounds. Older batches and snapshots remain decodable; an older receiver or analyzer may refuse the new kind. No database migration, backfill or existing fact rewrite occurs.

The private analyzer reports declarations returned, selected admitted invocations without declarations, missing or contradictory own joins, declared dependency coverage counts and unresolved references, with bounded diagnostic samples. `dependencyCoverage=complete` describes only a source declaration for that admission. Missing historical facts, parent/root/dependency targets and external-original references remain unresolved. The analyzer keeps `waitCoverage=unknown` and `criticalPath=not-evaluated`; declarations cannot complete the invocation census, usage, capture, allocation, enrollment or token comparison.

The fixed fixtures in `tests/learn-01-analysis/fixtures/causal-admission/` are synthetic. Their manifest pins case count and each admission/event byte length and digest. The root admission intentionally reorders JSON properties and includes whitespace to catch raw-byte reserialization. Manifest route/intent digests are inert contract references, not hashes of supplied committed route/intent files. Coordination's real PostgreSQL controls must derive those digests from genuine retained route/intent bytes and pass emitted batches through an exact qualified receiver artifact.

Local source qualification on the isolated C1 candidate passed 46 Python analyzer tests, three focused Core decoder tests, one CLI schema-14 temporary-store ingress/reopen/replay/refusal test and one Dashboard private-row projection test. The synthetic fixture manifest SHA-256 is `58de080758af83f2e243faf6995a44a529d00f810d43b2ac64753d14f59ce880`. The source-built CLI DLL SHA-256 is `e3a0f5949cb54c7a00d2883083fd05d0efcd239fc18eb07a4e4c850a1492ef87`; its runtime closure and C3 producer/receiver composition still require separate exact-artifact qualification. Earlier local restore/compiler/test-helper failures remain retained in the private qualification record. These checks used disposable source fixtures and temporary stores; they did not ingest into an installed observer or establish native provenance.

Source order is merged A1–A3, C1 receiver, C2 producer, then C3 exact composition. Publication and installed adoption remain separate: adopt a compatible receiver before enabling new producer emission. This source window does not select a dependency wait or change fresh-workspace defaults. LEARN-01.2 and LEARN-01.4 remain open pending installed custody, independent population and shared-cost evidence, and the later qualified wait/lifecycle reduction.
17 changes: 13 additions & 4 deletions src/FS.GG.Coord.Cli/TelemetryStoreApplication.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1630,6 +1630,7 @@ WHERE f.identity=$resource AND f.revision=$sourceRevision AND f.content_digest=$
| TelemetryStore.RuntimeResponseNativeInventory _
| TelemetryStore.RuntimeProviderObservation _
| TelemetryStore.RuntimeResponseUsage _
| TelemetryStore.ExecutionCausalAdmission _
| TelemetryStore.RuntimeNativeInventorySource _
| TelemetryStore.LearnSharedCost _
| TelemetryStore.LearnSharedCostAuthority _
Expand Down Expand Up @@ -3456,6 +3457,13 @@ UNION ALL SELECT count(*) FROM runtime_turn_usage WHERE invocation_id=$invocatio
| _ -> ()

match state with
| Some(oldKind, digest, revision)
when oldKind = "execution-causal-admission/1"
|| fact.Kind = "execution-causal-admission/1" ->
if oldKind = fact.Kind && digest = fact.ContentDigest && revision = fact.Revision then
replayed <- replayed + 1L
else
invalidOp "execution-causal-admission/1 is immutable after persistence"
| Some(_, digest, _) when digest = fact.ContentDigest -> replayed <- replayed + 1L
| Some(_, _, revision) when fact.Revision <= revision ->
invalidOp $"native fact identity conflict: %s{fact.Kind}/%s{fact.Identity}"
Expand Down Expand Up @@ -3517,6 +3525,7 @@ UNION ALL SELECT count(*) FROM runtime_turn_usage WHERE invocation_id=$invocatio

insert.ExecuteNonQuery() |> ignore
if fact.Kind.StartsWith("learn-", StringComparison.Ordinal)
|| fact.Kind = "execution-causal-admission/1"
|| fact.Kind = "runtime-native-inventory/1"
|| fact.Kind = "runtime-native-inventory-source/1" then
use order = connection.CreateCommand()
Expand Down Expand Up @@ -6360,7 +6369,7 @@ WHERE n.source_ref=$source;
ItemId = itemId
FactCount =
runtimeScalar
"SELECT count(*) FROM current_ingest_facts WHERE item_id=$item AND kind NOT IN ('learn-task-snapshot','learn-context-manifest','learn-experiment-assignment','learn-accounting-inventory/1','runtime-native-inventory/1','runtime-native-inventory-source/1','learn-shared-cost/1','learn-shared-cost-allocation/1','learn-shared-cost-authority/1','learn-native-delivery-source/1','learn-installed-origin/1');"
"SELECT count(*) FROM current_ingest_facts WHERE item_id=$item AND kind NOT IN ('learn-task-snapshot','learn-context-manifest','learn-experiment-assignment','learn-accounting-inventory/1','runtime-native-inventory/1','runtime-native-inventory-source/1','learn-shared-cost/1','learn-shared-cost-allocation/1','learn-shared-cost-authority/1','learn-native-delivery-source/1','learn-installed-origin/1','execution-causal-admission/1');"
UnknownUsageCounters = unknownUsageCounters
UsageObservations = usageCount
DeliveryObservations = count "delivery_observations"
Expand Down Expand Up @@ -7375,7 +7384,7 @@ WHERE n.source_ref=$source;
use learningCount = connection.CreateCommand()
learningCount.Transaction <- transaction
learningCount.CommandText <-
$"SELECT count(*) FROM ingest_facts WHERE kind IN ('learn-task-snapshot','learn-context-manifest','learn-experiment-assignment','learn-accounting-inventory/1','runtime-native-inventory/1','runtime-native-inventory-source/1','learn-shared-cost/1','learn-shared-cost-allocation/1','learn-shared-cost-authority/1','learn-native-delivery-source/1','learn-installed-origin/1')%s{learningItemFilter};"
$"SELECT count(*) FROM ingest_facts WHERE kind IN ('learn-task-snapshot','learn-context-manifest','learn-experiment-assignment','learn-accounting-inventory/1','runtime-native-inventory/1','runtime-native-inventory-source/1','learn-shared-cost/1','learn-shared-cost-allocation/1','learn-shared-cost-authority/1','learn-native-delivery-source/1','learn-installed-origin/1','execution-causal-admission/1')%s{learningItemFilter};"
itemId |> Option.iter (parameter learningCount "$selected")

if not compactCi && Convert.ToInt64(learningCount.ExecuteScalar()) > 10000L then
Expand Down Expand Up @@ -7436,7 +7445,7 @@ WHERE n.source_ref=$source;
JsonArray()
else
rows
($"SELECT o.sequence AS ingest_order,a.producer AS receipt_producer,a.stream AS receipt_stream,a.authority_role AS receipt_role,a.grant_id AS receipt_grant_id,a.grant_generation AS receipt_grant_generation,a.receipt_key,a.envelope_digest AS receipt_envelope_digest,f.identity,f.kind,f.item_id,f.revision,f.content_digest,f.canonical FROM ingest_facts f LEFT JOIN learning_fact_order o ON o.identity=f.identity LEFT JOIN fact_admissions a ON a.identity=f.identity WHERE f.kind IN ('learn-task-snapshot','learn-context-manifest','learn-experiment-assignment','learn-accounting-inventory/1','runtime-native-inventory/1','runtime-native-inventory-source/1','learn-shared-cost/1','learn-shared-cost-allocation/1','learn-shared-cost-authority/1','learn-native-delivery-source/1','learn-installed-origin/1')%s{learningFactItemFilter} ORDER BY f.item_id,f.kind,f.identity LIMIT 10001;")
($"SELECT o.sequence AS ingest_order,a.producer AS receipt_producer,a.stream AS receipt_stream,a.authority_role AS receipt_role,a.grant_id AS receipt_grant_id,a.grant_generation AS receipt_grant_generation,a.receipt_key,a.envelope_digest AS receipt_envelope_digest,f.identity,f.kind,f.item_id,f.revision,f.content_digest,f.canonical FROM ingest_facts f LEFT JOIN learning_fact_order o ON o.identity=f.identity LEFT JOIN fact_admissions a ON a.identity=f.identity WHERE f.kind IN ('learn-task-snapshot','learn-context-manifest','learn-experiment-assignment','learn-accounting-inventory/1','runtime-native-inventory/1','runtime-native-inventory-source/1','learn-shared-cost/1','learn-shared-cost-allocation/1','learn-shared-cost-authority/1','learn-native-delivery-source/1','learn-installed-origin/1','execution-causal-admission/1')%s{learningFactItemFilter} ORDER BY f.item_id,f.kind,f.identity LIMIT 10001;")

[
"populations",
Expand Down Expand Up @@ -8492,7 +8501,7 @@ WHERE n.source_ref=$source;
use command = connection.CreateCommand()

command.CommandText <-
"SELECT DISTINCT item_id FROM current_ingest_facts WHERE item_id IS NOT NULL AND kind NOT IN ('learn-task-snapshot','learn-context-manifest','learn-experiment-assignment','learn-accounting-inventory/1','runtime-native-inventory/1','runtime-native-inventory-source/1','learn-shared-cost/1','learn-shared-cost-allocation/1','learn-shared-cost-authority/1','learn-native-delivery-source/1','learn-installed-origin/1') ORDER BY item_id;"
"SELECT DISTINCT item_id FROM current_ingest_facts WHERE item_id IS NOT NULL AND kind NOT IN ('learn-task-snapshot','learn-context-manifest','learn-experiment-assignment','learn-accounting-inventory/1','runtime-native-inventory/1','runtime-native-inventory-source/1','learn-shared-cost/1','learn-shared-cost-allocation/1','learn-shared-cost-authority/1','learn-native-delivery-source/1','learn-installed-origin/1','execution-causal-admission/1') ORDER BY item_id;"

use reader = command.ExecuteReader()
let items = ResizeArray<string>()
Expand Down
Loading
Loading