Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c78322b
Give the SCM object store two kinds of object, so a file has somewher…
Sep 1, 2026
8b57788
Apply the authored-source tagging rule to both arms, and make the inv…
Sep 1, 2026
921a90c
Execute the authored-source checkout refusal instead of arguing it fr…
Sep 1, 2026
22dfe35
Refuse for the right owner: split MemberFailed, and stop reporting a …
Sep 1, 2026
7f92f9d
Put every annotation above a subject, and ask the store for a NODE wh…
Sep 1, 2026
ceb3c9f
Brand a node reference so a file cannot inhabit a program position, a…
Sep 1, 2026
488b09d
Compare records by what the identity is derived from, and count the d…
Sep 1, 2026
fc10106
Name the identity rule for its subject, and freeze a predecessor docu…
Sep 1, 2026
dce2a40
The module names the codec and the tag names the version: retire a su…
Sep 1, 2026
71a5c49
Merge remote-tracking branch 'origin/main' into scm-object-model
Sep 1, 2026
9fefe55
Restore the rung-drop row an auto-merge deleted, and revert a module …
Sep 1, 2026
5ba7ade
Execute the authored-source wire round trip in both arms
Sep 1, 2026
40927ea
Integrate main, regenerating the projections rather than picking a side
Sep 1, 2026
8907b18
Make a node requirement a proposition, and let only the store answer it
Sep 1, 2026
8cf125c
Canonicalize on the other route too, and stop fabricating a collision
Sep 1, 2026
b8682d7
Derive dependency order instead of trusting insertion order
Sep 1, 2026
e710a35
Delete the two node-presence predicates the census replaced
Sep 1, 2026
651c991
Name the repository codec for the tag it implements, and freeze a rea…
Sep 1, 2026
837115a
Integrate main
Sep 1, 2026
54cd2e4
Read both census populations everywhere, not just the absent one
Sep 1, 2026
9bdd976
Seal the raw closure encoder behind a carrier the bad state cannot in…
Sep 1, 2026
103f2d5
Separate a wrong-kind commit root from an absent one, and execute the…
Sep 1, 2026
bec5a32
Make the commit-root fold exhaustive instead of rostering its wildcard
Sep 2, 2026
689a2b5
Carry the standing to its destination, scope the image claim, sweep t…
Sep 2, 2026
66f72d5
State the populations that exist, not the ones these notes were writt…
Sep 2, 2026
f9bbb36
Merge remote-tracking branch 'origin/main' into scm-object-model
Sep 2, 2026
e141a76
Exclude witness helpers from the census denominator, by name
Sep 2, 2026
5bdb538
Regenerate the Rust source-type bindings from their authority
Sep 2, 2026
f25132c
Two claims quantified over a population they did not check
Sep 2, 2026
aec3deb
Merge remote-tracking branch 'origin/main' into scm-object-model
Sep 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 51 additions & 10 deletions dag/gunbc/scm/checkout.dag
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,16 @@ import gunbc.scm.ancestry { RepositoryCommitRef }
import gunbc.scm.object_store {
ObjectId,
ObjectRecord,
SemanticNodeObjectRef,
SemanticNodeTarget,
ObjectStore,
StoredEdge,
find_object,
NodeRecordFound,
NodeRecordAbsent,
NodeRecordIsAuthoredSource,
find_node_record,
resolve_node_target,
node_target_of,
}

// A COMMIT IS A ROOT IDENTITY AND NOTHING ELSE. No parent, no author, no timestamp, no message, no
Expand All @@ -25,11 +32,19 @@ import gunbc.scm.object_store {
// and a single timeline. Keeping the commit to its root is what lets two commits be compared,
// deduplicated and reconstructed without consulting any history at all.
type Commit {
root: ObjectId
root: SemanticNodeTarget
}

// AN AUTHORED SOURCE AT A PROGRAM POSITION IS ITS OWN REFUSAL, NOT A MISSING OBJECT.
//
// Once the store holds two kinds of object, a checkout can reach an identity that IS present and is
// a file rather than a program. Reporting that as MissingObject would send an operator looking for
// bytes that are right there, which is the not-applicable-rendered-as-malformed failure -- the two
// have opposite remedies. It is also not silently skippable: reconstructing the node without that
// child would hand back a program the commit does not name.
type CheckoutRefusal
= MissingObject { identity: ObjectId }
| ObjectIsAuthoredSource { identity: ObjectId }

type CheckoutOutcome
= CheckedOut { program: Node }
Expand Down Expand Up @@ -59,7 +74,7 @@ fn checkout_edge_step(store: ObjectStore, acc: CheckoutEdgeAcc, stored: StoredEd
match acc.refusal {
Present { value: _ } => acc
Absent =>
match checkout_object(store: store, identity: stored.target) {
match checkout_node_target(store: store, target: stored.target) {
CheckoutRefused { cause: cause } =>
CheckoutEdgeAcc { edges: acc.edges, refusal: Present { value: cause } }
CheckedOut { program: child } =>
Expand All @@ -71,6 +86,24 @@ fn checkout_edge_step(store: ObjectStore, acc: CheckoutEdgeAcc, stored: StoredEd
}
}

// A TARGET IS RESOLVED HERE, NEVER PREJUDGED. The edge states that a semantic node is REQUIRED at a
// locator; it says nothing about whether this store satisfies that, and a graft may have supplied it
// since the edge was written. So the target goes through the store's own three-way resolve and the
// two negatives stay apart -- absent, or occupied by a file -- because they are different facts with
// different remedies and only one of them can be repaired by fetching.
//
// This used to match on a two-armed target and route the resolved arm straight to checkout_object.
// That arm is gone: it carried store-relative evidence inside a canonical record, and this function
// re-resolved it anyway, which was one of the four proofs that it never belonged there.
fn checkout_node_target(store: ObjectStore, target: SemanticNodeTarget) -> CheckoutOutcome {
match resolve_node_target(store: store, target: target) {
NodeRecordAbsent { identity: i } => CheckoutRefused { cause: MissingObject { identity: i } }
NodeRecordIsAuthoredSource { identity: i } =>
CheckoutRefused { cause: ObjectIsAuthoredSource { identity: i } }
NodeRecordFound(record) => checkout_record(store: store, record: record)
}
}

fn checkout_record(store: ObjectStore, record: ObjectRecord) -> CheckoutOutcome {
match fold(
record.children,
Expand All @@ -86,10 +119,18 @@ fn checkout_record(store: ObjectStore, record: ObjectRecord) -> CheckoutOutcome
}
}

fn checkout_object(store: ObjectStore, identity: ObjectId) -> CheckoutOutcome {
match find_object(store: store, identity: identity) {
Absent => CheckoutRefused { cause: MissingObject { identity: identity } }
Present { value: record } => checkout_record(store: store, record: record)
// THE REFUSALS SURVIVE THE BRAND, AND SAYING WHY IS THE HONEST PART. A SemanticNodeObjectRef was
// minted against an object of node kind IN SOME STORE; it does not carry WHICH store. A reference
// minted against one store and resolved against another can therefore still find nothing, or find a
// file -- so these two arms are a live boundary between stores rather than a wall the brand already
// built. What the brand removes is the AUTHORING mistake: no caller can hand this function a
// locator it never resolved to a node at all.
fn checkout_object(store: ObjectStore, identity: SemanticNodeObjectRef) -> CheckoutOutcome {
match find_node_record(store: store, identity: identity.locator) {
NodeRecordAbsent { identity: i } => CheckoutRefused { cause: MissingObject { identity: i } }
NodeRecordIsAuthoredSource { identity: i } =>
CheckoutRefused { cause: ObjectIsAuthoredSource { identity: i } }
NodeRecordFound(record) => checkout_record(store: store, record: record)
}
}

Expand All @@ -100,7 +141,7 @@ fn checkout_object(store: ObjectStore, identity: ObjectId) -> CheckoutOutcome {
// agreement, which is precisely the property that makes a monotonic store unable to name a program
// on its own.
fn checkout(store: ObjectStore, commit: Commit) -> CheckoutOutcome {
checkout_object(store: store, identity: commit.root)
checkout_node_target(store: store, target: commit.root)
}

// The rebuilt program carries OccurrenceSynthetic throughout, because occurrence identity is
Expand All @@ -109,7 +150,7 @@ fn checkout(store: ObjectStore, commit: Commit) -> CheckoutOutcome {
// and reconstruct identically. Round-trip equality is therefore asserted on content identity, never
// on occurrence provenance -- and asserting it on provenance would be asserting the one property the
// model says must not matter.
fn commit_of_root(root: ObjectId) -> Commit {
fn commit_of_root(root: SemanticNodeTarget) -> Commit {
Commit { root: root }
}

Expand Down Expand Up @@ -177,7 +218,7 @@ fn checkout_repository_at(repo: RepositoryEnvelope, reference: RepositoryCommitR
match commit_at_reference(commits: repo.commits, reference: reference) {
Absent => CheckoutReferenceUnresolved { reference: reference }
Present { value: selected } =>
match checkout(store: repo.store, commit: commit_of_root(root: selected.root)) {
match checkout(store: repo.store, commit: commit_of_root(root: node_target_of(reference: selected.root))) {
CheckoutRefused { cause: cause } =>
CheckoutProgramUnreconstructable { reference: reference, cause: cause }
CheckedOut { program: program } =>
Expand Down
Loading
Loading