Skip to content

fix(config): Record a change to compaction rules and labels - #1133

Merged
JeanMertz merged 5 commits into
mainfrom
config-compaction-labels
Sep 24, 2026
Merged

JeanMertz merged 5 commits into
mainfrom
config-compaction-labels

Conversation

@JeanMertz

Copy link
Copy Markdown
Collaborator

Reordering a compaction rule, or dropping one, reaches the
conversation. Both were computed and discarded, so the conversation kept
compacting by the rules it was created with and recomputed the same
non-delta on every turn.

A label rule the workspace config gained after a conversation was
created reaches that conversation, matching how every other map now
behaves.

The list naming fields whose clear could not be recorded is gone. No
field is exempt from the law that a clear survives a fold.

@JeanMertz
JeanMertz force-pushed the config-compaction-labels branch from 50c82b0 to 52498cb Compare September 8, 2026 08:09
Base automatically changed from config-parameter-flatten to main September 23, 2026 05:45
@JeanMertz
JeanMertz force-pushed the config-compaction-labels branch from 52498cb to 3da9af2 Compare September 23, 2026 07:34
Editing `conversation.compaction.rules` now reaches the conversation.
Before, the delta compared rules element by element, so reordering them
or dropping one produced nothing and the conversation kept compacting
the way it always had.

The rules carry their own merge strategy, so the delta says `replace`
and the fold reaches the list the user wrote, order included. An empty
list is left alone: it says nothing about rules rather than asking for
none, since resolution substitutes the built-in defaults for it, and
every sparse partial reaching the delta carries one. Replacing with it
would record zero rules nobody asked for and make a later
`jp conversation compact` do nothing.

With this, no field is exempt from the law that a cleared field
survives a fold, and the list of exceptions the sweep consulted is
gone. Every field the schema names can be set, cleared, and have either
recorded.

Signed-off-by: Jean Mertz <git@jeanmertz.com>
A label rule added to the workspace config now applies to conversations
created before it existed, matching how a server, tool, plugin or model
alias already behaves.

The conversation layer is a resolved snapshot merged over the layer
built from the config files, and this map stated `replace` on that
snapshot, so it dropped every rule the files declared and the
conversation did not. It merges per key instead, which leaves the
conversation's own rules winning field by field while a rule only the
files declare survives.

That was the last map stating `replace` from a snapshot, so the helper
that did it has no callers left and is gone. `attachments` still states
it and still should: appending is its default, and a list that merged
with itself would double.

Signed-off-by: Jean Mertz <git@jeanmertz.com>
The conversation layer states its label rules per key rather than as a
`replace`, and an empty map of them is skipped on the wire where the
wrapper was not. Every stored conversation config is three lines
shorter for it.

Belongs with the commit before this one; squash the two when splitting
the branch.

Signed-off-by: Jean Mertz <git@jeanmertz.com>
A label rule the workspace config gained after a conversation was
created now reaches that conversation, which is what the preceding
commit set out to deliver and did not.

The conversation layer fills from the layer built from the config files,
and `MergeableMap::fill_from` keeps only its own entries. Emitting the
conversation's snapshot key by key rather than as a replacement is what
lets a rule be filled in, but nothing was filling: a rule the
conversation did not hold was dropped rather than taken from the files,
exactly as it was when the snapshot stated `replace`.

Filling is now per key, as it already is for `providers.mcp`,
`plugins.command`, `providers.llm.aliases` and `conversation.tools`. A
map that states a strategy is still left alone, since its owner said how
it combines.

The regression runs through `ConfigPipeline::partial_with_conversation`,
where the fill happens. The preceding commit's test merges the two
layers instead, and a deep merge keeps both keys whatever `fill_from`
does, so it cannot fail for this.

Signed-off-by: Jean Mertz <git@jeanmertz.com>
@JeanMertz
JeanMertz force-pushed the config-compaction-labels branch from e2c9c9e to 53e8842 Compare September 24, 2026 21:53
Signed-off-by: Jean Mertz <git@jeanmertz.com>
@JeanMertz
JeanMertz merged commit 2d730b6 into main Sep 24, 2026
21 checks passed
@JeanMertz
JeanMertz deleted the config-compaction-labels branch September 24, 2026 22:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant