feat(runtime): state the id contract once on every uuid parameter - #2007
Open
ohdearquant wants to merge 13 commits into
Open
feat(runtime): state the id contract once on every uuid parameter#2007ohdearquant wants to merge 13 commits into
ohdearquant wants to merge 13 commits into
Conversation
describe_verb appends a single shared ID_PARAM_CONTRACT to every parameter declared param_type=uuid, instead of the contract being restated per parameter across each pack HandlerDef or left implicit. A full UUID is namespace-agnostic: the caller already knows the specific record, so it resolves the same way everywhere. A short hex prefix is a namespace-scoped resolution -- a search -- so it can match nothing or match ambiguously across a namespace the caller does not share. Stating this once explains why some verbs accept a prefix while others require a full UUID: the asymmetry tracks whether the operation can assume the caller namespace, rather than being an inconsistency between verbs. Entity creation additionally echoes entity_type_normalized when an alias substitution was applied, so a caller can see what it requested and what was actually stored. Case-only folding is not reported as a substitution. Tests cover the shared contract on uuid params, the single and bulk create alias echo, and the negative case-folding arm.
ohdearquant
marked this pull request as ready for review
August 16, 2026 23:59
…ra-doc link `describe_verb` is public and `ID_PARAM_CONTRACT` is private, so the bracketed form trips rustdoc::private_intra_doc_links under -D warnings and fails the doc build. A plain code span conveys the same thing to a reader of the public docs without an allow attribute or widening the constant's visibility.
The shared ID_PARAM_CONTRACT is published through describe_verb to every caller of help, and it said short hex prefix resolution is scoped to the caller's namespace. The by-ID verbs (get/update/delete/merge) resolve prefixes through resolve_prefix_unfiltered, which applies no namespace predicate at all, matching their already-unfiltered full-UUID path: by-ID access is namespace-agnostic and the Gate, not storage-layer filtering, is the authorization seam (ADR-007 Rev 6). The code was correct and the published description was its negation, which is the worse direction of the two, since a caller trusting the text would expect a prefix to be confined to its own namespace. resolve_prefix_unfiltered's own doc comment in this crate already states the true rule, so the wire-facing string contradicted the code it describes. Correct the constant and the surrounding prose that encoded the same wrong claim. Make the help test discriminate. It previously asserted only that the description contains "namespace", which passes identically whether the contract states the rule or its negation, so it stayed green while the contract was wrong. It now pairs a must-match on the actual rule with a must-not-match on the wrong one; either arm alone still admits a contract that merely mentions namespaces without committing to anything.
ohdearquant
commented
Aug 17, 2026
ohdearquant
left a comment
Owner
Author
There was a problem hiding this comment.
Automated review. Posted by this repository's automated pull-request review pipeline; this is not a human read and does not gate the merge by itself.
Verdict on head 8c24c5d: REQUEST-CHANGES, 2 blocking findings. Finding details are delivered to the review's recipients rather than posted here. Do not merge this head while blocking findings are outstanding; a pipeline comment on a newer head supersedes this one.
Two doc sites still described by-ID prefix resolution as namespace-scoped after ADR-007 Rev 6 made it fully unfiltered: - atomic_prepare.rs's require_uuid doc named resolve_uuid_unfiltered while calling it a "namespace-scoped search" in the same sentence. - pack.rs's ID_PARAM_CONTRACT enumerated get/update/delete/merge but omitted link, whose source_id/target_id resolve through the identical unfiltered path (khive-pack-kg's handle_link calls resolve_uuid_unfiltered for both endpoints). Also trims the now-redundant "namespace-unfiltered under ADR-007" clause from each by-ID ParamDef description in khive-pack-kg, since describe_verb already appends that fact once via the shared ID_PARAM_CONTRACT constant. No behavior change — by-ID prefix resolution was already unfiltered; this corrects the text to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ingest validates the optional thread_id syntactically only — no existence or namespace lookup — so its declared resolution mode now states exactly that; the parameter description says the same in words.
The parameter-contract documentation linked VerbRegistry::describe_verb from khive-types, which cannot resolve a link into a downstream crate, and describe_verb's own documentation linked the private contract helper. Both references become plain code spans; sentences unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
describe_verbappends a single sharedID_PARAM_CONTRACTto every parameter declaredparam_type=uuid, instead of the contract being restated per parameter across each pack'sHandlerDefor left implicit.The contract it states: a full UUID is namespace-agnostic (the caller already knows the specific record), while a short hex prefix is a namespace-scoped resolution — a search — that can match nothing or match ambiguously across a namespace the caller does not share. Saying this once explains why some verbs accept a prefix and others require a full UUID: the asymmetry tracks whether the operation can assume the caller's namespace, rather than being an inconsistency between verbs.
Entity creation additionally echoes
entity_type_normalizedwhen an alias substitution was applied, so a caller can see both what it requested and what was actually stored. Case-only folding (case/hyphen/space folding to the same word) is deliberately not reported as a substitution.Coverage: the shared contract on uuid params, single-entity and bulk create alias echo, and the negative case-folding arm.
Local gates at this head:
cargo fmt --checkclean,cargo check -p khive-pack-kg -p khive-runtime --all-targetsclean,cargo clippysame scope clean,cargo test -p khive-pack-kg -p khive-runtime1766 passed / 0 failed.