Skip to content

fix(desktop): stop the instance form offering AI-config edits it discards - #4206

Open
fitz2882 wants to merge 1 commit into
block:mainfrom
fitz2882:fix/linked-instance-inherited-fields
Open

fix(desktop): stop the instance form offering AI-config edits it discards#4206
fitz2882 wants to merge 1 commit into
block:mainfrom
fitz2882:fix/linked-instance-inherited-fields

Conversation

@fitz2882

@fitz2882 fitz2882 commented Aug 2, 2026

Copy link
Copy Markdown

Summary

#1968 (8c0e8cb16) made the linked definition authoritative for model, LLM provider, and system prompt: resolve_effective_config reads all three from the definition and never consults the instance record. The write path moved; the controls did not.

AgentInstanceEditDialog still renders Model and LLM provider as live dropdowns, prefilled from the definition, while handleSubmit omits them for a linked instance and update_managed_agent drops them again server-side (apply_model_provider_prompt_update returns early on record.persona_id.is_some()). Editing either one reports a successful save and changes nothing, so the value has to be set a second time in the definition dialog.

Reported as "there are two places to edit an agent and you have to edit both."

  • personaRuntimeModel.ts: add definitionOwnsAiConfig, one predicate for "the definition, not this instance, owns these fields". The submit omissions and the control state now both derive from it instead of two independent linkedPersona != null checks — which is exactly how they drifted apart.
  • AgentInstanceEditDialog.tsx: Model and LLM provider, and the custom-value inputs behind both, go read-only when a definition owns them. They stay visible — the value answers "what does this agent run on" — but cannot be edited where the edit would be discarded.
  • AgentAiDefaults.tsx: AgentAiDefaultsNotice takes an optional onEditDefinition. When set it names the definition as the source and adds an Edit agent definition link — the same hand-off "Edit avatar" already makes. A disabled control with no route to the real one is the same dead end in a different costume.
  • A definition-owned provider no longer gates Save. Switching harness re-derives the provider draft and can blank it, which would otherwise leave Save disabled on a read-only field the user cannot repair, over a value this form never sends.
  • The Model field moves to EditAgentModelField, mirroring PersonaModelField on the definition form. AgentInstanceEditDialog is already 228 lines over the 1000-line cap and the file-size ratchet forbids growth; 1228 → 1211.

Definition-level respond-to and parallelism are a separate gap — they reach an instance at mint time only — and are not touched here.

Validation

Hermit toolchain, at a0a9982a:

  • just desktop-check — clean. The 2 useTemplate infos are pre-existing in personaCatalogRelay.test.mjs, untouched here and present at base ac4fa13b8.
  • just desktop-typecheck — clean
  • just desktop-test3914 passed, 0 failed (8 new)
  • just desktop-build — succeeds; the new copy and the edit-agent-definition testid are present in desktop/dist
  • desktop/scripts/check-file-sizes.mjs passes

New contract tests (definitionOwnedAiConfig.test.mjs) pin the predicate and both seams, so a future change that re-derives either one independently fails rather than silently reopening the same hole.

Not verified: the rendered dialog. No screenshot — the desktop E2E mock bridge cannot seed a linked managed agent for this surface, so verification stops at the built bundle.

Test plan

  1. Agents → Edit on an agent created from a definition.
  2. Model and LLM provider show the definition's values and are not editable; the summary below reads "Set on the agent definition and shared by every agent created from it."
  3. Edit agent definition closes the dialog and opens the definition form, where the change takes.
  4. On an agent with no linked definition, both fields remain editable and save as before.

…ards

## Summary

block#1968 (`8c0e8cb16`) made the linked definition authoritative for model,
LLM provider, and system prompt: `resolve_effective_config` reads all
three from the definition and never consults the instance record. The
write path moved; the controls did not. `AgentInstanceEditDialog` kept
rendering Model and LLM provider as live dropdowns, prefilled from the
definition, while `handleSubmit` omitted them and `update_managed_agent`
dropped them again server-side (`record.persona_id.is_some()` early
return). Editing either reported a successful save and changed nothing,
so the value had to be set a second time in the definition dialog — the
"you have to edit both places" report.

- `personaRuntimeModel.ts`: add `definitionOwnsAiConfig`, the single
predicate for "the definition, not this instance, owns these fields".
The submit omissions and the control state now derive from it instead of
two independent `linkedPersona != null` checks, which is exactly how they
drifted apart.
- `AgentInstanceEditDialog.tsx`: Model and LLM provider (and the custom-
value inputs behind both) go read-only when a definition owns them. They
stay visible — the value answers "what does this agent run on" — but
cannot be edited where the edit would be dropped.
- `AgentAiDefaults.tsx`: `AgentAiDefaultsNotice` takes an optional
`onEditDefinition`. When set it names the definition as the source and
adds an "Edit agent definition" link, the same hand-off "Edit avatar"
already makes. A disabled control with no route to the real one is the
same dead end in a different costume.
- A definition-owned provider no longer gates Save. Switching harness
re-derives the provider draft and can blank it, which would otherwise
leave Save disabled on a read-only field the user cannot repair, over a
value this form never sends.
- The Model field moves to `EditAgentModelField`, mirroring
`PersonaModelField` on the definition form. `AgentInstanceEditDialog` is
already 228 lines over the repo's 1000-line cap, and the file-size
ratchet forbids growth.

Definition-level respond-to and parallelism are a separate gap (they
reach an instance at mint time only) and are not touched here.

## Validation

At this commit, hermit toolchain:

- `just desktop-check` — clean; the 2 `useTemplate` infos are
pre-existing in `personaCatalogRelay.test.mjs`, untouched here, and
present at base `ac4fa13b8`
- `just desktop-typecheck` — clean
- `just desktop-test` — 3914 passed, 0 failed (8 new)
- `just desktop-build` — succeeds; new copy and the
`edit-agent-definition` testid confirmed present in `desktop/dist`
- `desktop/scripts/check-file-sizes.mjs` passes: 1228 -> 1211 lines

New contract tests (`definitionOwnedAiConfig.test.mjs`) pin the predicate
and both seams, so a future change that re-derives either one on its own
fails rather than silently re-opening the same hole.

Not verified: the rendered dialog. No screenshot — the desktop E2E mock
bridge cannot seed a linked managed agent for this surface.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: David Fitzsimmons <dave_fitz@icloud.com>
@fitz2882
fitz2882 marked this pull request as ready for review August 2, 2026 00:12
@fitz2882
fitz2882 requested a review from a team as a code owner August 2, 2026 00:12

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0a9982aa1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

: null,
[agent.personaId, personasQuery.data],
);
const aiConfigIsDefinitionOwned = definitionOwnsAiConfig(linkedPersona);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Key ownership off the persisted definition link

When the persona query has no data—because it is still loading, failed, or the instance is orphaned—linkedPersona is null even though agent.personaId remains set. The backend write guard keys solely on record.persona_id.is_some(), so this predicate enables model/provider controls and includes their changes in a save that silently discards them, preserving the exact dead-control behavior this change intends to remove. Derive ownership from agent.personaId and use the resolved persona separately for display/navigation.

Useful? React with 👍 / 👎.

disabled={aiFieldsDisabled}
discoveryLoading={modelDiscoveryLoading}
isRequired={modelRequired}
model={model}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Display the linked definition's effective AI values

When a definition's model or provider changes after an instance was created, the instance record intentionally retains stale create-time snapshots, but these newly read-only controls still receive model/provider state initialized from agent.model and agent.provider. Consequently the dialog claims to show what the agent runs on while displaying the old snapshot—and the summary below uses the same stale submission values—rather than linkedPersona.model and linkedPersona.provider that the backend actually resolves.

Useful? React with 👍 / 👎.

Comment on lines +308 to +311
export function definitionOwnsAiConfig(
linkedPersona: unknown | null | undefined,
): boolean {
return linkedPersona != null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document the definition-owned AI-config invariant

This introduces a new ownership predicate and changes how model/provider configuration is rendered and persisted, but the scoped contributor guide is unchanged. It explicitly requires configuration behavior changes to update that guide in the same PR and directs reviewers to treat a config-behavior diff without the update (or an explicit no-rules-changed note) as incomplete; document the new definition-ownership invariant and its query-state behavior.

AGENTS.md reference: desktop/src/features/agents/AGENTS.md:L177-L184

Useful? React with 👍 / 👎.

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