import Base import ./Reading.bend as Reading import ./LogicalKey.bend as LogicalKey import ./ContentHash.bend as ContentHash import ./StateTree.bend as StateTree import ./Staging.bend as Staging import ./ContentObject.bend as ContentObject import ./JsonAdapter.bend as JsonAdapter import ./Value.bend as Value import ./Store.bend as Store import bend-kit-json@0.3.0.0/json.bend as Json # Linear history: staging -> merged tree -> commit -> per-key version index. # Commits serialize as canonical JSON without timestamp/certificate (both # re-materialize on load: 0 / None). Loads re-serialize the parsed fields and # compare against the stored text: a mismatch yields None, never corruption. # Kit is named for the Val TYPE only; operations go through JsonAdapter. # Effects are Store Op actions composed in do-blocks. Reads resolve to Values. # One metadata key/value field. type CommitMeta is Data: Field{field_key: String, field_value: String} # Identifier, parents, tree hash, timestamp, metadata and certificate. type Commit is Data: Make{commit_identifier: String, parent_commit_identifiers: List<&2, String>, tree_hash: String, timestamp: Nat, metadata: List<&2, CommitMeta>, certificate_hash: Maybe<&2, String>} # Projects the identifier out of a commit. def commit_identifier_of(commit_value: Commit) -> String: match commit_value: case Make{commit_identifier, parent_commit_identifiers, tree_hash, timestamp, metadata, certificate_hash}: commit_identifier # Projects the tree hash out of a commit. def commit_tree_of(commit_value: Commit) -> String: match commit_value: case Make{commit_identifier, parent_commit_identifiers, tree_hash, timestamp, metadata, certificate_hash}: tree_hash # Encodes one metadata field as a two-item array. def meta_pair_to_json(meta_field: CommitMeta) -> Json.Val: match meta_field: case Field{field_key, field_value}: JsonAdapter.make_arr(Con{JsonAdapter.make_str(field_key), Con{JsonAdapter.make_str(field_value), Nil{}}}) # Encodes the metadata list. def meta_list_to_json(metadata: List<&2, CommitMeta>) -> List<&2, Json.Val>: match metadata: case Nil{}: Nil{} case Con{head_field, remaining_fields}: Con{meta_pair_to_json(head_field), meta_list_to_json(remaining_fields)} # Encodes parent identifiers as strings. def encode_parent_list(parent_commit_identifiers: List<&2, String>) -> List<&2, Json.Val>: match parent_commit_identifiers: case Nil{}: Nil{} case Con{head_parent, remaining_parents}: Con{JsonAdapter.make_str(head_parent), encode_parent_list(remaining_parents)} # Encodes the parents/tree/meta document canonically. def finalize_commit_document(parents_field: Sigma<&2, &2, String, _ => Json.Val>, tree_field: Sigma<&2, &2, String, _ => Json.Val>, meta_field: Sigma<&2, &2, String, _ => Json.Val>) -> String: JsonAdapter.encode_canonical(JsonAdapter.make_obj(Con{parents_field, Con{tree_field, Con{meta_field, Nil{}}}})) # Serializes parents, tree and metadata; the hashed bytes. def serialize_commit(parent_commit_identifiers: List<&2, String>, metadata: List<&2, CommitMeta>, tree_hash_value: String) -> String: finalize_commit_document(JsonAdapter.make_kv("parents", JsonAdapter.make_arr(encode_parent_list(parent_commit_identifiers))), JsonAdapter.make_kv("tree", JsonAdapter.make_str(tree_hash_value)), JsonAdapter.make_kv("meta", JsonAdapter.make_arr(meta_list_to_json(metadata)))) # Conses a decoded meta field onto the tail. def prepend_meta_field(field_key: String, field_value: String, parsed_tail: Maybe<&2, List<&2, CommitMeta>>) -> Maybe<&2, List<&2, CommitMeta>>: match parsed_tail: case None{}: None{} case Some{tail_fields}: Some{Con{Field{field_key, field_value}, tail_fields}} # Unwraps the field value, then prepends. def prepend_meta_value(field_key: String, value_maybe: Maybe<&2, String>, parsed_tail: Maybe<&2, List<&2, CommitMeta>>) -> Maybe<&2, List<&2, CommitMeta>>: match value_maybe: case None{}: None{} case Some{field_value}: prepend_meta_field(field_key, field_value, parsed_tail) # Unwraps the key text, then continues. def prepend_meta_texts(key_maybe: Maybe<&2, String>, value_maybe: Maybe<&2, String>, parsed_tail: Maybe<&2, List<&2, CommitMeta>>) -> Maybe<&2, List<&2, CommitMeta>>: match key_maybe: case None{}: None{} case Some{field_key}: prepend_meta_value(field_key, value_maybe, parsed_tail) # Decodes one [key, value] array; None on wrong shape. def prepend_meta_items(pair_items: List<&2, Json.Val>, parsed_tail: Maybe<&2, List<&2, CommitMeta>>) -> Maybe<&2, List<&2, CommitMeta>>: match pair_items: case Con{key_json, Con{value_json, Nil{}}}: prepend_meta_texts(JsonAdapter.as_str(key_json), JsonAdapter.as_str(value_json), parsed_tail) case _: None{} # Unwraps the pair array, then decodes. def prepend_parsed_meta(pair_maybe: Maybe<&2, List<&2, Json.Val>>, parsed_tail: Maybe<&2, List<&2, CommitMeta>>) -> Maybe<&2, List<&2, CommitMeta>>: match pair_maybe: case None{}: None{} case Some{pair_items}: prepend_meta_items(pair_items, parsed_tail) # Decodes one metadata item. def prepend_meta_entry(head_item: Json.Val, parsed_tail: Maybe<&2, List<&2, CommitMeta>>) -> Maybe<&2, List<&2, CommitMeta>>: prepend_parsed_meta(JsonAdapter.as_arr(head_item), parsed_tail) # Decodes the metadata array; None on any bad item. def parse_meta_list(meta_items: List<&2, Json.Val>) -> Maybe<&2, List<&2, CommitMeta>>: match meta_items: case Nil{}: Some{Nil{}} case Con{head_item, remaining_items}: prepend_meta_entry(head_item, parse_meta_list(remaining_items)) # Accepts only when re-serialization matches stored bytes. def verify_commit_text(text_matches: Bool, +requested_identifier: String, +parent_list: List<&2, String>, +tree_text: String, +parsed_meta: List<&2, CommitMeta>) -> Maybe<&2, Commit>: match text_matches: case True{}: Some{Make{requested_identifier, parent_list, tree_text, 0n, parsed_meta, None{}}} case False{}: None{} # Continues commit extraction once metadata decodes. def finish_commit_meta(+requested_identifier: String, +stored_text: String, +parent_list: List<&2, String>, +tree_text: String, meta_maybe: Maybe<&2, List<&2, CommitMeta>>) -> Maybe<&2, Commit>: match meta_maybe: case None{}: None{} case Some{+parsed_meta}: verify_commit_text(String.eq(serialize_commit(parent_list, parsed_meta, tree_text), stored_text), requested_identifier, parent_list, tree_text, parsed_meta) # Continues commit extraction once the meta items decode. def finish_commit(+requested_identifier: String, +stored_text: String, +parent_list: List<&2, String>, +tree_text: String, meta_items_maybe: Maybe<&2, List<&2, Json.Val>>) -> Maybe<&2, Commit>: match meta_items_maybe: case None{}: None{} case Some{meta_items}: finish_commit_meta(requested_identifier, stored_text, parent_list, tree_text, parse_meta_list(meta_items)) # Unwraps the meta array, then continues. def finish_meta_array(+requested_identifier: String, +stored_text: String, +parent_list: List<&2, String>, +tree_text: String, meta_maybe: Maybe<&2, Json.Val>) -> Maybe<&2, Commit>: match meta_maybe: case None{}: None{} case Some{meta_json}: finish_commit(requested_identifier, stored_text, parent_list, tree_text, JsonAdapter.as_arr(meta_json)) # Unwraps the tree field as text. def tree_maybe_as_text(tree_maybe: Maybe<&2, Json.Val>) -> Maybe<&2, String>: match tree_maybe: case None{}: None{} case Some{tree_json}: JsonAdapter.as_str(tree_json) # Continues commit extraction once the tree text decodes. def extract_commit_meta(+requested_identifier: String, +stored_text: String, +parent_list: List<&2, String>, tree_text_maybe: Maybe<&2, String>, +document: Json.Val) -> Maybe<&2, Commit>: match tree_text_maybe: case None{}: None{} case Some{tree_text}: finish_meta_array(requested_identifier, stored_text, parent_list, tree_text, JsonAdapter.get_field(document, "meta")) # Continues commit extraction once parents decode. def extract_commit_tree(+requested_identifier: String, +stored_text: String, parent_strings_maybe: Maybe<&2, List<&2, String>>, tree_maybe: Maybe<&2, Json.Val>, +document: Json.Val) -> Maybe<&2, Commit>: match parent_strings_maybe: case None{}: None{} case Some{parent_list}: extract_commit_meta(requested_identifier, stored_text, parent_list, tree_maybe_as_text(tree_maybe), document) # Unwraps the parent items, then continues. def extract_parents_items(+requested_identifier: String, +stored_text: String, items_maybe: Maybe<&2, List<&2, Json.Val>>, +document: Json.Val) -> Maybe<&2, Commit>: match items_maybe: case None{}: None{} case Some{parent_items}: extract_commit_tree(requested_identifier, stored_text, ContentObject.extract_each_string(parent_items), JsonAdapter.get_field(document, "tree"), document) # Unwraps the parents field, then continues. def extract_parents_array(+requested_identifier: String, +stored_text: String, parents_maybe: Maybe<&2, Json.Val>, +document: Json.Val) -> Maybe<&2, Commit>: match parents_maybe: case None{}: None{} case Some{parents_json}: extract_parents_items(requested_identifier, stored_text, JsonAdapter.as_arr(parents_json), document) # Reads the parents field of the document. def extract_commit_parents(+requested_identifier: String, +stored_text: String, +document: Json.Val) -> Maybe<&2, Commit>: extract_parents_array(requested_identifier, stored_text, JsonAdapter.get_field(document, "parents"), document) # Builds the commit from parsed JSON; None on malformed input. def commit_from_parse(requested_identifier: String, stored_text: String, parse_result: Maybe<&2, Json.Val>) -> Maybe<&2, Commit>: match parse_result: case None{}: None{} case Some{+document}: extract_commit_parents(requested_identifier, stored_text, document) # Parses stored commit text. def parse_commit_text(requested_identifier: String, +stored_text: String) -> Maybe<&2, Commit>: commit_from_parse(requested_identifier, stored_text, JsonAdapter.parse_text(stored_text)) # Parses loaded text; missing storage reads as None. def parse_commit(requested_identifier: String, stored_maybe: Maybe<&2, String>) -> Maybe<&2, Commit>: match stored_maybe: case None{}: None{} case Some{stored_text}: parse_commit_text(requested_identifier, stored_text) # Loads and verifies a commit by identifier. def load_commit(+requested_identifier: String) -> Store.Op<&2, Maybe<&2, Commit>>: do Store.Op<&2, Maybe<&2, Commit>>: stored_maybe : Maybe<&2, String> <- Store.oget(LogicalKey.build_commit_storage_key(requested_identifier)) return parse_commit(requested_identifier, stored_maybe) # Empty Sess result of unit type. def empty_unit() -> Store.Op<&2, Unit>: do Store.Op<&2, Unit>: return Unit{} # Empty Sess result of entry-list type. def empty_pair_list() -> Store.Op<&2, List<&2, Reading.ChainEntry>>: do Store.Op<&2, List<&2, Reading.ChainEntry>>: return Nil{} # Empty Sess result of tree type. def empty_tree_maybe() -> Store.Op<&2, Maybe<&2, StateTree.StateTreeNode>>: do Store.Op<&2, Maybe<&2, StateTree.StateTreeNode>>: return None{} # Serializes, hashes and stores a commit. def persist_commit(+parent_commit_identifiers: List<&2, String>, +metadata: List<&2, CommitMeta>, +tree_hash_value: String) -> Store.Op<&2, Commit>: do Store.Op<&2, Commit>: +commit_text : String = serialize_commit(parent_commit_identifiers, metadata, tree_hash_value) +commit_identifier : String = ContentHash.compute_sha256_hex(commit_text) stored_unit : Unit <- Store.oput(LogicalKey.build_commit_storage_key(commit_identifier), commit_text) return Make{commit_identifier, parent_commit_identifiers, tree_hash_value, 0n, metadata, None{}} # Hashes and stores a merged entry list as a tree. def persist_tree(+merged_entries: List<&2, Reading.ChainEntry>) -> Store.Op<&2, String>: do Store.Op<&2, String>: +tree_hash_value : String = StateTree.compute_tree_hash(merged_entries) stored_unit : Unit <- Store.oput(LogicalKey.build_tree_storage_key(tree_hash_value), StateTree.serialize_tree(merged_entries, tree_hash_value)) return tree_hash_value # Writes one version-index entry per merged entry. def write_each_index(merged_entries: List<&2, Reading.ChainEntry>, +commit_value: Commit) -> Store.Op<&2, Unit>: match merged_entries: case Nil{}: empty_unit() case Con{+head_entry, remaining_entries}: do Store.Op<&2, Unit>: head_unit : Unit <- Store.oput(LogicalKey.build_version_index_key(Reading.entry_key_of(head_entry), commit_identifier_of(commit_value)), Reading.entry_value_of(head_entry)) tail_unit : Unit <- write_each_index(remaining_entries, commit_value) return tail_unit # Removes staged keys consumed by the commit. def clear_stage_keys(+session_identifier: String, merged_entries: List<&2, Reading.ChainEntry>) -> Store.Op<&2, Unit>: match merged_entries: case Nil{}: empty_unit() case Con{head_entry, remaining_entries}: do Store.Op<&2, Unit>: cleared_unit : Unit <- Store.odel(LogicalKey.build_stage_storage_key_combined(session_identifier, Reading.entry_key_of(head_entry))) cleared_tail : Unit <- clear_stage_keys(session_identifier, remaining_entries) return cleared_tail # Writes the index and clears the stage in sequence. def persist_index_and_clear(+session_identifier: String, +merged_entries: List<&2, Reading.ChainEntry>, +commit_value: Commit) -> Store.Op<&2, Unit>: do Store.Op<&2, Unit>: indexed_unit : Unit <- write_each_index(merged_entries, commit_value) cleared_unit : Unit <- clear_stage_keys(session_identifier, merged_entries) index_cleared_unit : Unit <- Store.odel(Staging.stage_index_key(session_identifier)) return index_cleared_unit # Persists tree, commit and index, then clears the stage. def materialize_commit(+session_identifier: String, +parent_commit_identifiers: List<&2, String>, metadata: List<&2, CommitMeta>, +merged_entries: List<&2, Reading.ChainEntry>) -> Store.Op<&2, Commit>: do Store.Op<&2, Commit>: +tree_hash_value : String <- persist_tree(merged_entries) +commit_value : Commit <- persist_commit(parent_commit_identifiers, metadata, tree_hash_value) cleared_unit : Unit <- persist_index_and_clear(session_identifier, merged_entries, commit_value) return commit_value # Parses loaded tree text. def parse_tree_text(tree_hash_value: String, tree_maybe: Maybe<&2, String>) -> Maybe<&2, StateTree.StateTreeNode>: match tree_maybe: case None{}: None{} case Some{tree_text}: StateTree.parse_tree_document(tree_text) # Loads a tree by hash. def load_tree_text(+tree_hash_value: String) -> Store.Op<&2, Maybe<&2, StateTree.StateTreeNode>>: do Store.Op<&2, Maybe<&2, StateTree.StateTreeNode>>: tree_maybe : Maybe<&2, String> <- Store.oget(LogicalKey.build_tree_storage_key(tree_hash_value)) return parse_tree_text(tree_hash_value, tree_maybe) # Loads the tree a commit points at. def load_tree_by_commit(commit_maybe: Maybe<&2, Commit>) -> Store.Op<&2, Maybe<&2, StateTree.StateTreeNode>>: match commit_maybe: case None{}: empty_tree_maybe() case Some{found_commit}: load_tree_text(commit_tree_of(found_commit)) # Reads the tree for a commit identifier. def read_tree_at(+commit_identifier: String) -> Store.Op<&2, Maybe<&2, StateTree.StateTreeNode>>: do Store.Op<&2, Maybe<&2, StateTree.StateTreeNode>>: commit_maybe : Maybe<&2, Commit> <- load_commit(commit_identifier) loaded_tree : Maybe<&2, StateTree.StateTreeNode> <- load_tree_by_commit(commit_maybe) return loaded_tree # Missing base trees read as empty. def base_or_empty(tree_maybe: Maybe<&2, StateTree.StateTreeNode>) -> List<&2, Reading.ChainEntry>: match tree_maybe: case None{}: Nil{} case Some{parent_tree}: StateTree.tree_entries(parent_tree) # Loads the first parent entries. def load_parent_tree(head_parent: String) -> Store.Op<&2, List<&2, Reading.ChainEntry>>: do Store.Op<&2, List<&2, Reading.ChainEntry>>: tree_maybe : Maybe<&2, StateTree.StateTreeNode> <- read_tree_at(head_parent) return base_or_empty(tree_maybe) # Loads base entries; root commits start empty. def load_base_entries(parent_commit_identifiers: List<&2, String>) -> Store.Op<&2, List<&2, Reading.ChainEntry>>: match parent_commit_identifiers: case Nil{}: empty_pair_list() case Con{head_parent, remaining_parents}: load_parent_tree(head_parent) # Skips missing staged values, keeps present ones. def prepend_pair_maybe(+head_key: String, head_value_maybe: Maybe<&2, String>, tail_pairs: List<&2, Reading.ChainEntry>) -> List<&2, Reading.ChainEntry>: match head_value_maybe: case None{}: tail_pairs case Some{head_value}: Con{Reading.make_entry(head_key, head_value), tail_pairs} # Loads one staged value and prepends it. def prepend_loaded_pair(+session_identifier: String, +head_key: String, tail_action: Store.Op<&2, List<&2, Reading.ChainEntry>>) -> Store.Op<&2, List<&2, Reading.ChainEntry>>: do Store.Op<&2, List<&2, Reading.ChainEntry>>: head_value_maybe : Maybe<&2, String> <- Store.oget(LogicalKey.build_stage_storage_key_combined(session_identifier, head_key)) tail_pairs : List<&2, Reading.ChainEntry> <- tail_action return prepend_pair_maybe(head_key, head_value_maybe, tail_pairs) # Loads every staged key in the manifest. def load_each_pair(+session_identifier: String, staged_keys: List<&2, String>) -> Store.Op<&2, List<&2, Reading.ChainEntry>>: match staged_keys: case Nil{}: empty_pair_list() case Con{head_key, remaining_keys}: prepend_loaded_pair(session_identifier, head_key, load_each_pair(session_identifier, remaining_keys)) # Collects the session staged pairs. def collect_staged_pairs(+session_identifier: String) -> Store.Op<&2, List<&2, Reading.ChainEntry>>: do Store.Op<&2, List<&2, Reading.ChainEntry>>: staged_keys : List<&2, String> <- Staging.load_stage_index(session_identifier) collected_pairs : List<&2, Reading.ChainEntry> <- load_each_pair(session_identifier, staged_keys) return collected_pairs # Materializes stage plus base into a new commit. def create_commit(+session_identifier: String, +parent_commit_identifiers: List<&2, String>, metadata: List<&2, CommitMeta>) -> Store.Op<&2, Commit>: do Store.Op<&2, Commit>: staged_pairs : List<&2, Reading.ChainEntry> <- collect_staged_pairs(session_identifier) base_entries : List<&2, Reading.ChainEntry> <- load_base_entries(parent_commit_identifiers) finished_commit : Commit <- materialize_commit(session_identifier, parent_commit_identifiers, metadata, StateTree.merge_sorted_entries(staged_pairs, base_entries)) return finished_commit # Versioned reads walk the first-parent chain with Nat fuel; unknown # commits contribute "" links that never match a real index key. def commit_parents_of(commit_value: Commit) -> List<&2, String>: match commit_value: case Make{commit_identifier, parent_commit_identifiers, tree_hash, timestamp, metadata, certificate_hash}: parent_commit_identifiers # Empty Sess result of string-list type. def empty_string_list() -> Store.Op<&2, List<&2, String>>: do Store.Op<&2, List<&2, String>>: return Nil{} # Empty Sess result of entry type. def empty_entry_maybe() -> Store.Op<&2, Maybe<&2, Reading.ChainEntry>>: do Store.Op<&2, Maybe<&2, Reading.ChainEntry>>: return None{} # Empty Op result of value type. def empty_value_maybe() -> Store.Op<&2, Maybe<&2, Value.Value>>: do Store.Op<&2, Maybe<&2, Value.Value>>: return None{} # Empty Op result of text type (commit identifiers, not values). def empty_text_maybe() -> Store.Op<&2, Maybe<&2, String>>: do Store.Op<&2, Maybe<&2, String>>: return None{} # Heads the parent list; root commits yield empty. def first_parent_name(parent_identifiers: List<&2, String>) -> String: match parent_identifiers: case Nil{}: "" case Con{head_parent, remaining_parents}: head_parent # Heads the parent list; unknown commits yield empty. def first_parent_text(commit_maybe: Maybe<&2, Commit>) -> String: match commit_maybe: case None{}: "" case Some{found_commit}: first_parent_name(commit_parents_of(found_commit)) # Unknown links end the walk: their index probes can never hit, so search # results are unchanged while misses stop descending (empty parent text is # terminal: missing commits and parentless roots both resolve to ""). def fuel_for_empty(is_empty: Bool, gated_left: Nat) -> Nat: match is_empty: case True{}: 0n case False{}: gated_left # Gates the remaining fuel on parent emptiness. def fuel_for_parent(+parent_text: String, gated_left: Nat) -> Nat: fuel_for_empty(String.eq(parent_text, ""), gated_left) # Walks first-parents with fuel; newest first. checker_fuel decreases every # level for the termination checker; gated_fuel carries the early stop. # Callers pass the same fuel twice. def walk_commit_chain(checker_fuel: Nat, gated_fuel: Nat, +commit_identifier: String) -> Store.Op<&2, List<&2, String>>: match checker_fuel gated_fuel: case 0n _: empty_string_list() case 1n+checker_left 0n: empty_string_list() case 1n+checker_left 1n+gated_left: do Store.Op<&2, List<&2, String>>: commit_maybe : Maybe<&2, Commit> <- load_commit(commit_identifier) +parent_text : String = first_parent_text(commit_maybe) tail_chain : List<&2, String> <- walk_commit_chain(checker_left, fuel_for_parent(parent_text, gated_left), parent_text) return Con{commit_identifier, tail_chain} # A stored value wins; otherwise the tail result. def decide_search_hit(stored_maybe: Maybe<&2, String>, +lookup_key: String, tail_result: Maybe<&2, Reading.ChainEntry>) -> Maybe<&2, Reading.ChainEntry>: match stored_maybe: case None{}: tail_result case Some{stored_value}: Some{Reading.make_entry(lookup_key, stored_value)} # Searches the ancestor chain newest-first. def search_chain_entries(ancestor_chain: List<&2, String>, +namespace_name: String, +record_identifier: String) -> Store.Op<&2, Maybe<&2, Reading.ChainEntry>>: match ancestor_chain: case Nil{}: empty_entry_maybe() case Con{head_commit, remaining_commits}: do Store.Op<&2, Maybe<&2, Reading.ChainEntry>>: +lookup_key : String = Reading.encode_lookup_key(namespace_name, record_identifier) stored_maybe : Maybe<&2, String> <- Store.oget(LogicalKey.build_version_index_key(lookup_key, head_commit)) tail_result : Maybe<&2, Reading.ChainEntry> <- search_chain_entries(remaining_commits, namespace_name, record_identifier) return decide_search_hit(stored_maybe, lookup_key, tail_result) # Projects the payload out of a loaded object. def payload_or_empty(loaded_maybe: Maybe<&2, ContentObject.ContentObject>) -> Maybe<&2, Value.Value>: match loaded_maybe: case None{}: None{} case Some{loaded_object}: Some{ContentObject.object_payload_of(loaded_object)} # Loads an object payload by content hash. def load_object_payload(+value_hash: String) -> Store.Op<&2, Maybe<&2, Value.Value>>: do Store.Op<&2, Maybe<&2, Value.Value>>: loaded_maybe : Maybe<&2, ContentObject.ContentObject> <- ContentObject.load_content_object(value_hash) return payload_or_empty(loaded_maybe) # Tombstones read as absent; hashes load payloads. def decide_resolve_value(is_tombstone: Bool, +stored_value: String) -> Store.Op<&2, Maybe<&2, Value.Value>>: match is_tombstone: case True{}: empty_value_maybe() case False{}: load_object_payload(stored_value) # Resolves one stored index value. def resolve_entry_value(+stored_value: String) -> Store.Op<&2, Maybe<&2, Value.Value>>: decide_resolve_value(String.eq(stored_value, "TOMBSTONE"), stored_value) # Resolves a found chain entry to its payload. def resolve_found_entry(found_maybe: Maybe<&2, Reading.ChainEntry>) -> Store.Op<&2, Maybe<&2, Value.Value>>: match found_maybe: case None{}: empty_value_maybe() case Some{found_entry}: resolve_entry_value(Reading.entry_value_of(found_entry)) # Reads a namespaced value at a commit. def read_value_at(+namespace_name: String, +record_identifier: String, +commit_identifier: String) -> Store.Op<&2, Maybe<&2, Value.Value>>: do Store.Op<&2, Maybe<&2, Value.Value>>: ancestor_chain : List<&2, String> <- walk_commit_chain(10000n, 10000n, commit_identifier) found_entry : Maybe<&2, Reading.ChainEntry> <- search_chain_entries(ancestor_chain, namespace_name, record_identifier) resolved_value : Maybe<&2, Value.Value> <- resolve_found_entry(found_entry) return resolved_value