You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Filed as a follow-on to issue #1 (foundational v1 design) and issue #2 (update regulation). Surfaces a design question that wasn't visible until a real-world user started consolidating rules in their local ~/.claude/.
The triggering observation
During an audit of one maintainer's local ~/.claude/instructions/ we identified that several related rules — all four github-* PR-mechanics files (github-pr-title-issue-link, github-pr-assignment, github-content-via-file, github-claude-attribution) — fire at the same moment (every gh pr create invocation) and are mentally retrieved as a single concept ("PR discipline"). Twenty separately-named autoloaded rules dilute attention at conversation depth; collapsing co-firing rules into one named hook plausibly improves retention.
The same logic applies to greek-register + greek-terminology (one hook: "Greek narration") and to narrate-plan + paragraph-shape + bilingual-narration + bash-descriptions (one hook: "narration discipline"). At least three natural clusters exist today; more will surface.
The design problem this creates
The v1 schema's atomic cherry-pick unit is one rule per file. Every byte of the contract — name == filename validator rule, /install-memory-file <slug>, the manifest's 1-slug-↔-1-installed-file invariant — assumes that.
Merging upstream rules into one local file breaks the invariant:
the manifest can't track per-constituent upstream versions
--update can't say "rule X you merged has been amended upstream"
uninstalling 1 of 4 constituents means hand-surgery on a merged file
ad-hoc merges are private; colleagues can't share them
We could ignore this and tell users "if you merge, you lose upstream tracking on those files". That preserves the schema's simplicity but sacrifices exactly the bidirectional traceability that motivated curating the repo in the first place.
The three options considered
Option 1 — Upstream stays granular; user-side merges are unsupported. Cleanest from upstream's perspective. Users who want clusters do it in their local copy and accept loss of upstream tracking. Downside: sacrifices the trace back to upstream for updates, which is the design's whole premise.
Option 2 — Upstream ships curated bundles as a new content type. Add type: bundle with includes: [slug1, slug2, ...] frontmatter. The bundle file's body is the merged operational text. The installer treats bundle as "atomic install of the merged text, but manifest records the bundle slug AND each constituent slug for upstream tracking". Constituents continue to exist as separate files upstream — installable standalone for users who only want one. --update against a bundle checks each constituent's upstream version and warns when any constituent has diverged from the curated bundle. Adds one new type, one new frontmatter field, validator rules for bundle integrity, installer logic for the bundle install/update path.
Option 3 — Ad-hoc user-side merge with manifest tracking. No new upstream type; the installer grows a --merge-into <local-name> <slug1> <slug2> ... mode that fetches each constituent's body, concatenates them under a user-named local file, and records composite-of: [slug1, slug2, ...] in the manifest. Downside: re-merging blows away local hand-edits; ad-hoc merges are private (no sharing).
Recommendation
Option 2 (curated bundles) for v1.1.
Reasons:
Bundles match the actual use case: a curated cluster with a named hook, not an arbitrary user-side concatenation. The retention benefit comes from the named hook, which only makes sense if the bundle is a stable named artefact — which is what curation gives you.
It preserves cherry-pick atomicity for users who don't want the cluster. Other devs may want only github-pr-title-issue-link standalone; granular files keep that path open.
Implementation cost is contained: one frontmatter field, ~30 lines of installer logic, one validator rule. Cheaper than Option 3's ad-hoc merge engine.
Bidirectional upstream tracking is preserved by construction: the bundle's includes: list IS the trace from merged file to constituents.
Option 3 (ad-hoc user-side merge) planned for v1.2. Real use case, distinct from curated bundles: a user may want to merge rules in combinations the upstream maintainers haven't curated (or shouldn't have to curate), and still keep upstream-tracking discipline. Bundles cover the "named, shared, curated cluster" case; ad-hoc merges cover the "this one user's local convenience" case. Both are legitimate; shipping them in sequence (bundles first because they're easier to reason about, ad-hoc second once bundle semantics have settled) keeps the v1.1 surface contained without forfeiting Option 3.
Proposed schema additions for v1.1
type: bundle # new top-level value alongside instruction, memory-*, etc.includes: [github-pr-title-issue-link, # required for type: bundle; must be ≥ 2 entriesgithub-pr-assignment, # each must reference a slug that exists in the repogithub-content-via-file,github-claude-attribution]target: ~/.claude/instructions/<name>.md # bundles install to the instructions/ target by default# (since all the seed bundles are clusters of instructions)autoload: true # bundles inherit autoload semantics from instruction type
Validator rules (additive):
includes: mandatory for type: bundle, must have ≥ 2 entries.
Each entry in includes: must resolve to an existing slug in the repo.
A bundle and its constituents can coexist in the repo; a constituent can appear in multiple bundles (no exclusivity).
The bundle body should textually contain (or supersede) the operational content of each constituent — validator can't enforce this semantically, but a soft check warns if the bundle body is shorter than the sum of constituent bodies × 0.5 (a heuristic).
Manifest extensions (additive):
{
"slug": "github-pr-discipline",
"type": "bundle",
"includes": ["github-pr-title-issue-link", "github-pr-assignment",
"github-content-via-file", "github-claude-attribution"],
"constituents-installed-refs": { // upstream ref of each constituent at install time"github-pr-title-issue-link": "abc123",
"github-pr-assignment": "abc123",
"github-content-via-file": "abc123",
"github-claude-attribution": "abc123"
}
}
Installer behaviour:
install <bundle-slug>: refuses if any constituent is currently installed standalone (offer --replace-constituents to uninstall them first).
--update <bundle-slug>: fetches the bundle's own current ref; also fetches each constituent's current ref; reports "constituent X moved from 1.0.0 → 1.1.0 but the bundle hasn't been re-curated to incorporate the change yet". The user can wait for re-curation, or install the standalone constituent (which then requires uninstalling the bundle, since the standalone and bundle conflict).
--remove <bundle-slug>: removes the merged file, manifest entry gone.
Option 3 detail (v1.2)
The ad-hoc user-side merge mechanism is a separate, additive feature. Sketch of the design (full detail to be elaborated when v1.2 work opens):
New installer mode:tools/install.sh --merge-into <local-name> <slug1> <slug2> .... Fetches each constituent's body from upstream, concatenates them under a user-named local file at ~/.claude/instructions/<local-name>.md (or ~/.claude/memory/<local-name>.md for memory constituents), wires the @-include if applicable. The local file's frontmatter is synthesised: type: instruction (or memory-*), name: <local-name>, no scope: (because the constituents may differ in scope), plus the manifest-only composite-of: field.
Manifest extension:
{
"slug": "<local-name>",
"composite-of": ["slug1", "slug2", "slug3"],
"composite-installed-refs": {
"slug1": "abc123", "slug2": "abc123", "slug3": "abc123"
},
"composite-edited-locally": false// tracks whether the merged file has been hand-edited since merge
}
--update semantics for composites:
Fetch each constituent's current upstream ref; compare to recorded composite-installed-refs.
If no constituent has changed: no-op.
If any constituent has changed AND composite-edited-locally == false: prompt "re-merge from upstream?" — re-fetch all constituents, re-concatenate, overwrite. Updates the recorded refs.
If any constituent has changed AND composite-edited-locally == true: prompt "your local merged file has hand-edits since the last merge; re-merge will overwrite them. Options: re-merge / abort / diff first" — default abort.
Bundles vs composites — when to use which:
Curated bundle (v1.1)
Ad-hoc composite (v1.2)
Origin
Shipped upstream, named, curated
User invokes locally with constituent list
Sharing
Discoverable by all users via /list-memory-files
Private to the one user who created it
Body curation
Maintainer arranges constituents for narrative flow
Mechanical concatenation, no narrative curation
Versioning
Bundle has its own version:, evolves on re-curation
No bundle version; derives identity from constituent refs
Use case
"Three or more users will want this same merge"
"I personally want these two rules merged for my workflow"
A user can promote an ad-hoc composite to a curated bundle by submitting it through /submit-memory-file; the submission process scaffolds the bundle frontmatter from the composite's manifest entry. The reverse — installing a curated bundle as an ad-hoc composite — is also supported (--as-composite flag), for users who want to customise the merged body locally and accept the loss of bundle-level upstream tracking in exchange.
This is the cleanest bundle candidate: all four constituents are scope: universal, all fire at the same moment, and the merge is uncontroversial — any user opening a PR wants all four. Additional bundles can be curated later as patterns emerge in submissions.
Worked example of an ad-hoc composite (v1.2)
The audit surfaced two clusters that are not bundle candidates because they combine scope: universal (or near-universal) rules with scope: personal-share rules:
Mixes public and personal rules; not all users have Greek narration to discipline. Curating as a single upstream bundle would force-bundle a personal preference into a notionally-universal merge.
Both personal-share to one user. A "bundle of personal-share files" is conceptually odd — bundles are for content multiple users will want; personal-share content has one author by definition.
These are the canonical Option-3 use cases. Without composites, a user with Greek-language narration discipline would have to either accept living with the four files separately (the retention problem that motivated this whole ticket) or merge them locally and lose upstream tracking. Composites preserve upstream tracking on each constituent while letting the user collapse the named-hook surface to one entry per cluster.
The narration-discipline example also demonstrates a property bundles can't easily express: mixed-scope composition. The user's local file legitimately mixes scope: universal rules with scope: personal-share rules because the user's lived workflow doesn't respect that boundary. Bundles are constrained to single-scope curations (mixing scopes upstream invites confusion about who the bundle is for); composites are unconstrained because they're private.
Designated v1.2 reference test case.narration-discipline is the test case for the Option 3 implementation: any v1.2 PR that wires the composite mechanism must demonstrate it by composing this specific cluster (two universals + two personal-shares), exercising the mixed-scope path. Until v1.2 lands, the cluster stays uncollapsed in the maintainer's local ~/.claude/ — deliberately, as evidence of the retention cost composites are meant to relieve.
Scope clarification — what bundles are not
Bundles are not categories. The repo already has instructions/, memory/, rules/, etc. — those are content-type categories. A bundle is one specific merged file, not a category of files.
Bundles are not collections. "All universal-scope rules" or "all metanorma-team rules" is a filter on the catalogue, not a bundle. The picker can already filter on scope: and team:; that's a different mechanism.
Bundles are not user-defined. Curated bundles ship upstream; user-defined ad-hoc merges are the Option-3 mechanism, deferred separately.
Relationship to existing tickets
Populate repo #1 (foundational design) — bundles are an additive extension to the schema laid out there; v1 ships without them, v1.1 adds them. No v1 design changes required.
Update regulation policy: author authority, semver, deprecation, --update flow #2 (update regulation) — bundles inherit the existing semver/owners:/CODEOWNERS rules; the bundle file has its own version, separate from constituent versions. The bundle's version: bumps when the curation changes (text rearranged, constituent added/removed); the constituents' versions evolve independently.
Plan file — plans/v1-design.md will be updated to add a "Bundles (v1.1)" subsection pointing at this issue, and to add this issue to the "Issues generated to fulfil this plan" index. That update is a separate plan-only commit.
Open questions for the thread
Conflict policy. Should the installer refuse to install a bundle when standalone constituents are present (current proposal), or auto-uninstall the constituents with a single confirmation? Refusing is safer; auto-uninstall is friendlier.
Constituent-divergence handling. When a constituent's upstream version is ahead of what's in the bundle, the proposal is to warn but otherwise leave the bundle alone. Alternative: prompt the user to choose between "wait for bundle re-curation" and "install constituent standalone, uninstall bundle". The current proposal is the lower-friction default; the alternative gives the user explicit agency.
Bundle vs constituent exclusivity. The proposal allows a constituent to belong to multiple bundles. Is that complexity worth carrying, or should bundles partition the constituent space (each constituent in at most one bundle)? Partitioning is simpler but constrains future curation.
Body-format convention. Should the bundle body have a mandatory structure (e.g. one ## heading per constituent, in includes: order)? Or freeform, as long as the operational content is preserved? Mandatory structure aids validator checks; freeform aids retention (the curator can rearrange for narrative flow).
Bundles: curated multi-rule files as a new content type
Full plan: https://github.com/riboseinc/claude-memory-files/blob/main/plans/v1-design.md
Filed as a follow-on to issue #1 (foundational v1 design) and issue #2 (update regulation). Surfaces a design question that wasn't visible until a real-world user started consolidating rules in their local
~/.claude/.The triggering observation
During an audit of one maintainer's local
~/.claude/instructions/we identified that several related rules — all fourgithub-*PR-mechanics files (github-pr-title-issue-link,github-pr-assignment,github-content-via-file,github-claude-attribution) — fire at the same moment (everygh pr createinvocation) and are mentally retrieved as a single concept ("PR discipline"). Twenty separately-named autoloaded rules dilute attention at conversation depth; collapsing co-firing rules into one named hook plausibly improves retention.The same logic applies to
greek-register+greek-terminology(one hook: "Greek narration") and tonarrate-plan+paragraph-shape+bilingual-narration+bash-descriptions(one hook: "narration discipline"). At least three natural clusters exist today; more will surface.The design problem this creates
The v1 schema's atomic cherry-pick unit is one rule per file. Every byte of the contract —
name == filenamevalidator rule,/install-memory-file <slug>, the manifest's 1-slug-↔-1-installed-file invariant — assumes that.Merging upstream rules into one local file breaks the invariant:
--updatecan't say "rule X you merged has been amended upstream"We could ignore this and tell users "if you merge, you lose upstream tracking on those files". That preserves the schema's simplicity but sacrifices exactly the bidirectional traceability that motivated curating the repo in the first place.
The three options considered
Option 1 — Upstream stays granular; user-side merges are unsupported. Cleanest from upstream's perspective. Users who want clusters do it in their local copy and accept loss of upstream tracking. Downside: sacrifices the trace back to upstream for updates, which is the design's whole premise.
Option 2 — Upstream ships curated bundles as a new content type. Add
type: bundlewithincludes: [slug1, slug2, ...]frontmatter. The bundle file's body is the merged operational text. The installer treatsbundleas "atomic install of the merged text, but manifest records the bundle slug AND each constituent slug for upstream tracking". Constituents continue to exist as separate files upstream — installable standalone for users who only want one.--updateagainst a bundle checks each constituent's upstream version and warns when any constituent has diverged from the curated bundle. Adds one new type, one new frontmatter field, validator rules for bundle integrity, installer logic for the bundle install/update path.Option 3 — Ad-hoc user-side merge with manifest tracking. No new upstream type; the installer grows a
--merge-into <local-name> <slug1> <slug2> ...mode that fetches each constituent's body, concatenates them under a user-named local file, and recordscomposite-of: [slug1, slug2, ...]in the manifest. Downside: re-merging blows away local hand-edits; ad-hoc merges are private (no sharing).Recommendation
Option 2 (curated bundles) for v1.1.
Reasons:
github-pr-title-issue-linkstandalone; granular files keep that path open.includes:list IS the trace from merged file to constituents.Option 3 (ad-hoc user-side merge) planned for v1.2. Real use case, distinct from curated bundles: a user may want to merge rules in combinations the upstream maintainers haven't curated (or shouldn't have to curate), and still keep upstream-tracking discipline. Bundles cover the "named, shared, curated cluster" case; ad-hoc merges cover the "this one user's local convenience" case. Both are legitimate; shipping them in sequence (bundles first because they're easier to reason about, ad-hoc second once bundle semantics have settled) keeps the v1.1 surface contained without forfeiting Option 3.
Proposed schema additions for v1.1
Validator rules (additive):
includes:mandatory fortype: bundle, must have ≥ 2 entries.includes:must resolve to an existing slug in the repo.Manifest extensions (additive):
{ "slug": "github-pr-discipline", "type": "bundle", "includes": ["github-pr-title-issue-link", "github-pr-assignment", "github-content-via-file", "github-claude-attribution"], "constituents-installed-refs": { // upstream ref of each constituent at install time "github-pr-title-issue-link": "abc123", "github-pr-assignment": "abc123", "github-content-via-file": "abc123", "github-claude-attribution": "abc123" } }Installer behaviour:
install <bundle-slug>: refuses if any constituent is currently installed standalone (offer--replace-constituentsto uninstall them first).--update <bundle-slug>: fetches the bundle's own current ref; also fetches each constituent's current ref; reports "constituent X moved from 1.0.0 → 1.1.0 but the bundle hasn't been re-curated to incorporate the change yet". The user can wait for re-curation, or install the standalone constituent (which then requires uninstalling the bundle, since the standalone and bundle conflict).--remove <bundle-slug>: removes the merged file, manifest entry gone.Option 3 detail (v1.2)
The ad-hoc user-side merge mechanism is a separate, additive feature. Sketch of the design (full detail to be elaborated when v1.2 work opens):
New installer mode:
tools/install.sh --merge-into <local-name> <slug1> <slug2> .... Fetches each constituent's body from upstream, concatenates them under a user-named local file at~/.claude/instructions/<local-name>.md(or~/.claude/memory/<local-name>.mdfor memory constituents), wires the@-include if applicable. The local file's frontmatter is synthesised:type: instruction(ormemory-*),name: <local-name>, noscope:(because the constituents may differ in scope), plus the manifest-onlycomposite-of:field.Manifest extension:
{ "slug": "<local-name>", "composite-of": ["slug1", "slug2", "slug3"], "composite-installed-refs": { "slug1": "abc123", "slug2": "abc123", "slug3": "abc123" }, "composite-edited-locally": false // tracks whether the merged file has been hand-edited since merge }--updatesemantics for composites:composite-installed-refs.composite-edited-locally == false: prompt "re-merge from upstream?" — re-fetch all constituents, re-concatenate, overwrite. Updates the recorded refs.composite-edited-locally == true: prompt "your local merged file has hand-edits since the last merge; re-merge will overwrite them. Options: re-merge / abort / diff first" — default abort.Bundles vs composites — when to use which:
/list-memory-filesversion:, evolves on re-curationA user can promote an ad-hoc composite to a curated bundle by submitting it through
/submit-memory-file; the submission process scaffolds the bundle frontmatter from the composite's manifest entry. The reverse — installing a curated bundle as an ad-hoc composite — is also supported (--as-compositeflag), for users who want to customise the merged body locally and accept the loss of bundle-level upstream tracking in exchange.Seed bundle to ship in v1.1 (proposal)
One curated bundle, drawn from the audit:
github-pr-disciplinegithub-pr-title-issue-link,github-pr-assignment,github-content-via-file,github-claude-attributiongh pr createThis is the cleanest bundle candidate: all four constituents are
scope: universal, all fire at the same moment, and the merge is uncontroversial — any user opening a PR wants all four. Additional bundles can be curated later as patterns emerge in submissions.Worked example of an ad-hoc composite (v1.2)
The audit surfaced two clusters that are not bundle candidates because they combine
scope: universal(or near-universal) rules withscope: personal-sharerules:narration-disciplinenarrate-plan(universal),paragraph-shape(universal),bilingual-narration(personal-share — Greek-speaking authors only),bash-descriptions(personal-share — bilingualdescription:field)greek-narrationgreek-register(personal-share),greek-terminology(personal-share)These are the canonical Option-3 use cases. Without composites, a user with Greek-language narration discipline would have to either accept living with the four files separately (the retention problem that motivated this whole ticket) or merge them locally and lose upstream tracking. Composites preserve upstream tracking on each constituent while letting the user collapse the named-hook surface to one entry per cluster.
The
narration-disciplineexample also demonstrates a property bundles can't easily express: mixed-scope composition. The user's local file legitimately mixesscope: universalrules withscope: personal-sharerules because the user's lived workflow doesn't respect that boundary. Bundles are constrained to single-scope curations (mixing scopes upstream invites confusion about who the bundle is for); composites are unconstrained because they're private.Designated v1.2 reference test case.
narration-disciplineis the test case for the Option 3 implementation: any v1.2 PR that wires the composite mechanism must demonstrate it by composing this specific cluster (two universals + two personal-shares), exercising the mixed-scope path. Until v1.2 lands, the cluster stays uncollapsed in the maintainer's local~/.claude/— deliberately, as evidence of the retention cost composites are meant to relieve.Scope clarification — what bundles are not
instructions/,memory/,rules/, etc. — those are content-type categories. A bundle is one specific merged file, not a category of files.scope:andteam:; that's a different mechanism.Relationship to existing tickets
owners:/CODEOWNERS rules; the bundle file has its own version, separate from constituent versions. The bundle'sversion:bumps when the curation changes (text rearranged, constituent added/removed); the constituents' versions evolve independently.plans/v1-design.mdwill be updated to add a "Bundles (v1.1)" subsection pointing at this issue, and to add this issue to the "Issues generated to fulfil this plan" index. That update is a separate plan-only commit.Open questions for the thread
##heading per constituent, inincludes:order)? Or freeform, as long as the operational content is preserved? Mandatory structure aids validator checks; freeform aids retention (the curator can rearrange for narrative flow).🤖 Generated with Claude Code