RFD D04: Conversation Config Inheritance for --cfg
- Status: Draft
- Category: Design
- Authors: Jean Mertz git@jeanmertz.com
- Date: 2026-04-20
- Requires: RFD 020, RFD 038, RFD 070
Summary
This RFD extends --cfg to accept conversation IDs (jp-c...) as values. When a conversation ID is passed, the named conversation's fully-resolved config is loaded and layered into the directive pipeline like any other --cfg value. --fork uses the source conversation's ID as the implicit starting config. Together these enable forked conversations, explicit cross-conversation config sharing, and reproducible config sources tied to an auditable conversation.
Motivation
Today, every new conversation starts from the workspace's default configuration. There is no way to:
- Fork a conversation and carry its resolved config (including accumulated
ConfigDeltas) into the new conversation. - Inherit another conversation's config explicitly when starting or continuing a conversation — useful for team-shared "template" conversations, debugging, or running one conversation's config against another's chat history.
- Reference a specific conversation as a config source for reproducibility. Pointing
--cfgat a conversation ID makes the source explicit and auditable ("this query ran with the same config as conversation X").
RFD 020 introduces --fork but leaves the config-inheritance question open. This RFD answers it: --fork defaults to the source conversation's config, which is equivalent to --cfg=<source-id>.
Design
Conversation IDs as --cfg values
--cfg accepts a conversation ID (jp-c prefix + digits) as an alternative to file paths, key-value assignments, JSON objects, and keywords (RFD 038). When passed, the named conversation's full resolved config — base + init + all event-stream ConfigDeltas — is converted to a fully-populated partial and merged left-to-right in the same pipeline:
# Start from another conversation's config, then layer overrides
jp q --cfg=jp-c17528832001 --cfg=overrides.toml
# Fork but adopt a different conversation's config instead of the source's
jp q --fork --cfg=jp-c17528832001
# Continue a conversation, but switch config to another conversation's
jp q --cfg=jp-c17528832001Conversation IDs have a distinctive format (jp-c prefix + digits), so they are unambiguous with both keywords (UPPERCASE) and file paths. A file named literally jp-c17528832001 would collide, but this is unlikely in practice — and RFD 038's disambiguation rules resolve exact matches before falling back to file path resolution.
The resolved-config expansion includes everything the source conversation accumulated: its base workspace snapshot, its init creation-time directives, and every post-creation ConfigDelta in its event stream. The result is a fully-populated partial that overwrites most fields at its position in the --cfg pipeline, just like --cfg=WORKSPACE does.
--fork default
When --fork is used without an explicit --cfg keyword or conversation ID, the implicit starting config is the source conversation's resolved config. This is equivalent to --cfg=<source-conversation-id>:
jp q --fork # starts from source conversation
jp q --fork --cfg=WORKSPACE # starts from workspace config
jp q --fork --cfg=NONE --cfg=custom.toml # starts from defaults, then custom
jp q --fork --cfg=jp-c17528832001 # starts from a different conversationA keyword at any position overrides the implicit default.
Disambiguation
RFD 038 owns the full disambiguation table. The conversation-ID row:
jp-c17528832001→ conversation ID (matches^jp-c\d+$).jp-c-not-an-id.toml→ file path../jp-c17528832001→ file path (leading./disambiguates).
Exact jp-c-prefix matches take precedence over file path resolution.
Errors
If --cfg=<conversation-id> references a conversation that does not exist in workspace storage, the command errors with:
- The conversation ID that wasn't found.
- A suggestion to check
jp conversation ls.
No fallback to file path resolution — a missing conversation ID is always an error, to avoid silently running against a different config than the user asked for.
Interaction with RFD 038 keywords
Keywords and conversation IDs compose cleanly in the --cfg pipeline — each position either sets or resets accumulated state:
# Start from conversation A, reset to defaults (NONE pre-scan), apply custom
jp q --cfg=jp-c17528832001 --cfg=NONE --cfg=custom.toml
# Result: defaults + custom (NONE discarded the conversation load)
# Start from conversation A, overlay conversation B, then local override
jp q --cfg=jp-c17528832001 --cfg=jp-c99999999999 --cfg=overrides.toml
# Result: A's resolved + B's resolved + overrides
# Fork from a conversation, reset to workspace defaults, apply a new skill
jp q --fork=jp-c17528832001 --cfg=WORKSPACE --cfg=debug.toml
# Result: workspace defaults + debugThe ordering semantics are inherited from RFD 008 and RFD 038 — this RFD adds no new processing rules beyond resolving the conversation ID to a partial.
Interaction with RFD 070 claims
When --cfg=jp-c<id> expands a source conversation's resolved config, all the fields it sets are claimed under a single source identity: hash(conversation_id). The inner provenance from the source conversation — which specific -c dev or -c architect originally claimed each field — is not preserved in the target conversation's claims state.
This is acceptable: inheritance is a wholesale "adopt this state" operation. -C jp-c<id> in the target conversation undoes it wholesale. Users who need finer-grained control over inherited influence should layer sources explicitly:
# Instead of relying on conversation-A's inner provenance:
jp q --cfg=jp-c17528832001 --cfg=overrides.toml
# Layer the same sources explicitly for full provenance:
jp q --cfg=dev --cfg=architect --cfg=overrides.tomlSee RFD 070 for the claims-history mechanism.
Drawbacks
Hidden dependency on another conversation. A conversation created with --cfg=jp-c<id> has an implicit dependency on the source conversation existing in storage. If the source is deleted or on a different machine, the target conversation works correctly (the expanded partial is baked into its own base_config.json's init at creation), but reproducing the source relationship requires the source to be present. This mirrors how file-based --cfg=dev.toml depends on the file existing at invocation time, but conversation storage is less visible to users than a file tree.
Large inherited partials. A conversation's resolved config can be thousands of lines of merged workspace + persona + override state. When expanded via --cfg=jp-c<id>, the full partial is loaded and merged. For most practical cases this is fine, but there's no size limit — a heavily customized source conversation produces a correspondingly large partial at the merge step.
Circular references are disallowed but need a check. --cfg=jp-c<own-id> (a conversation referencing itself) would create an infinite-recursion loop at load time. The resolver must detect and reject self-references with a clear error.
Alternatives
Separate --inherit flag
A dedicated flag (--inherit=conversation, --inherit=workspace, etc.) that controls the base config independently from --cfg.
Rejected because it creates two flags that interact in non-obvious ways. Unifying under --cfg with conversation-ID values (and RFD 038's keywords) is simpler: one flag, one processing model, left-to-right.
Reserved PARENT keyword
A PARENT keyword that expands to the parent conversation's resolved config.
Deferred. For --fork, the source conversation's config is already the implicit default. For explicit use, --cfg=<conversation-id> achieves the same result. A PARENT keyword becomes more useful if conversation trees (RFD 039) introduce scenarios where the parent ID is not readily known; it can be added then without design changes.
Store the full inherited partial in the target's init
When --cfg=jp-c<id> creates a new conversation, the source's resolved config could be materialized verbatim into the new conversation's init list, making the new conversation fully self-contained.
Rejected because it massively inflates base_config.json: the target would duplicate the source's full state on disk. The current design stores only a ConfigDelta::Apply representing the inheritance (the resolved partial at inheritance time), which is already the fully-populated partial but persisted as a single event rather than a snapshot. Future invocations fold it normally.
Non-Goals
- Keyword reset points.
NONEandWORKSPACEare RFD 038's concern. - Provenance preservation across inheritance. As noted, inner provenance from the source conversation is not retained. Users who want fine-grained provenance must layer sources explicitly.
- Cross-workspace conversation inheritance.
--cfg=jp-c<id>resolves within the current workspace's storage. Inheriting from a conversation in another workspace is out of scope; it would require workspace traversal logic that doesn't exist today.
Risks and Open Questions
Invalid conversation IDs
When --cfg=<conversation-id> references a non-existent conversation, the error must include:
- The conversation ID that wasn't found.
- A suggestion to check
jp conversation ls. - Any close matches (e.g. Levenshtein-distance-1 from an existing ID) if typo assistance is cheap to implement.
Typo assistance is a nice-to-have, not a blocker.
Conversation ID resolution cost
Loading a source conversation requires reading its base_config.json and folding all its ConfigDelta events. For long conversations this may involve parsing hundreds of events. In practice this is fast (well under 100ms for typical streams), but worth validating for conversations with thousands of events.
Forward-compatibility with conversation trees
RFD 039 introduces conversation trees. A tree's root is a conversation, and its children inherit from the parent. Under this RFD, that inheritance is --cfg=<parent-id> applied implicitly. If trees introduce a notion of "parent" that's distinct from "source of fork," a PARENT keyword may become useful (see Alternatives).
Implementation Plan
Phase 1: --cfg=<conversation-id> resolution
Add conversation-ID recognition to --cfg processing:
- Disambiguate conversation-ID values by pattern (
jp-cprefix + digits). - Resolve the ID against workspace storage to load the conversation stream.
- Fold the stream's
base + init + event-stream ConfigDeltasinto a fully-resolved partial. - Insert that partial into the
--cfgdirective pipeline as anApplydirective.
Error handling:
- Missing conversation: clear error including the ID and
jp conversation lssuggestion. - Self-reference (
--cfg=<own-id>): detected via storage-level cycle check, rejected with an error.
Can be merged independently.
Phase 2: --fork config inheritance
Wire --fork to use the source conversation's resolved config as the implicit starting config:
- When
--forkis present without an explicit--cfgkeyword or conversation ID, prepend a synthetic--cfg=<source-id>to the directive list. - Subsequent explicit
--cfgvalues layer on top.
Depends on Phase 1. Can be merged alongside or after RFD 020's --fork implementation.
References
- RFD 008: Ordered Tool Directives — establishes left-to-right processing for interleaved CLI flags.
- RFD 020: Parallel Conversations — defines
--forkflag that interacts with conversation inheritance. - RFD 038: Config Reset Keywords — defines
NONEandWORKSPACEkeywords and theConfigDeltaenum withResetvariant. Conversation IDs compose with keywords in the same--cfgpipeline; RFD 038's disambiguation table covers both. - RFD 039: Conversation Trees — may motivate a
PARENTkeyword for tree-aware inheritance. - RFD 070: Negative Config Deltas — defines claim-history-driven revert. Conversation-ID inheritance collapses inner provenance under a single source identity.