Repository navigation
docs: define the resource-independent adoption contract (#141) - #147
Merged
Merged
Conversation
`ct adopt` followed technical API boundaries rather than the domain object users see, and the grouping was re-decided per resource type: hierarchy is not captured at all, dynamic rulesets need --with-dynamic, member fields are proposed as --with-member-fields in #135. Records the one contract instead: five categories (root, owned structural children, relationships, shared referenced resources, person-related data) with a default per category, the switch style, recursion and cycle rules, failure classification, output vocabulary, and the policy for promoting an opt-in to a default. Worked through the group and event examples, with the implied follow-up work and how #135 applies it. Closes #141. Claude-Session: https://claude.ai/code/session_018JShVZYNLaRb4hF5KbHCXG
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.
Adds
docs/adoption-contract.md— one resource-independent contract for what comes along whenct adoptadopts something, replacing the per-resource-type argument. Decision only; no adoption code changes.The five categories
--no-<child>to opt out--no-<relationship>refif already managed, else numeric id +TODOThe both-ends rule in row 3 is forced by the engine, not chosen:
applyHierarchydiffsparentsonly over managed parents, so an edge to an unmanaged group would be a phantom change everyplanre-proposes. Row 4 has no opt-in because a state entry is a claim of lifecycle ownership — the exact silent claim #143 exists to prevent. The remedies are an explicit secondct adopt, or (once #143 lands) an external/read-only declaration.Other decisions
--no-*for default-on,--with-*for default-off, plus--minimaldefined as "root only" (not "today's defaults"), which is what makes a future default flip non-breaking for scripts.--children-of) stays unbounded and cycle-guarded; composition recursion is depth 1 and never transitive, so cycles are structurally impossible there.captureDynamicworks); an unsupported endpoint warns once and continues; a denied/transient read (401/403/429/5xx) fails the run — a composite written from a partial read is a wrong blueprint that looks complete. Re-running with--no-<category>/--minimalsucceeds, so the incompleteness is a recorded decision. Mirrors plan degrades into a WRONG plan under HTTP 429 instead of aborting #126'sunreadableseam on the plan side.adopted/referenced/unmanaged/skipped/excluded), identical in--dry-run, followingadopt grants --all-declarable's "never a silent cap" discipline.excludedis a constant line, not a count — counting person records would mean reading them.--portable-rulesetsprecedent, bug(dynamic): unportablized ruleset ids are written silently — wrong-host rulesets fail quietly, not loudly #101); the negation ships in the same release as the flip; a flip is afeat:, neverBREAKING CHANGE:. Justified structurally: a default flip changes only whatadoptemits, never whatplan/applydo to an existing config — the config file stays the sole source of truth for what is managed.Worked examples
Group and event tables walk every item #141 listed. The group table shows the concrete gap:
--children-oftoday gets the roots, the fields and the exclusions right, and loses the shape (no hierarchy captured at all) and the member fields.Events are explicitly not committed to as an adoptable type —
runbook-manual-surface.mdputs them outside the mandate. The example is there to show the contract answers the question before the code exists, which is the point of #141: booking = category 3, the booked room = category 4, and that falls out of the categories with no new judgement.Follow-ups (listed in the doc, not done here)
Shared adoption-scope module; a composite descriptor on
AdoptableResourceso adoption is table-driven likeSYNTHETIC_FIELDS; hierarchy capture in adopt; promoting--with-dynamicto default-on; the five-verb summary; #143.#135 is unblocked and unchanged — it should ship exactly as specified, opt-in
--with-member-fields, treating member fields as a category-2 owned child (scoped identity, no host ids, member rows never read) and stating in the help text that the opt-in is transitional. The flip to default-on is a separate follow-up gated on the shared flag surface existing, so--no-member-fieldscan ship in the same release.Closes #141.
https://claude.ai/code/session_018JShVZYNLaRb4hF5KbHCXG