feat(desktop): edit the bound Hermes profile from Crew (model + SOUL.md write-through, optional description) - #149
Conversation
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
…t ownership Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
…123 plan Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Runtime UI verification (and what it caught)Driven by hand through the real UI — headed Chromium against the E2E mock bridge ( One real bug found and fixed: the create-in-place persona step rendered only red text ("Hermes profile 'recruit' has no SOUL.md") with no textarea and no save — Three copy problems fixed, all of them versions of the confusion this issue exists to end:
One consistency fix: a failed profile-model write used to discard the user's typed edit; it now keeps the typed values alongside the error, matching the SOUL editor. Verified passing: profile model shows the profile's real values and re-reads after save; a classified write failure shows the message, keeps values, and persists nothing; SOUL editor opens populated, gates save on dirty, persists, and keeps unsaved text on a failed save; empty Agent instructions saves fine; Not covered: real write-through against a live Devin session: https://app.devin.ai/sessions/1c69da96697d48b68f30f159b1310f9c |
20a5382 to
cc974d5
Compare
|
Rebased onto current Post-rebase local gates: The
The required |
cc974d5 to
44e8879
Compare
…t ownership Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
…123 plan Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
44e8879 to
79f5229
Compare
79f5229 to
7bf12a5
Compare
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com> Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
The force-pushed sync introduced a duplicate D-039 and dropped the blank line before D-031. Move D-037 ahead of D-038/D-039 and keep each decision once. No other content changed. Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
cc7c394 to
c9dee9b
Compare
…t ownership Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
…123 plan Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
…t ownership Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
…123 plan Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
…t ownership Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
…123 plan Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com>
…lose the epic, keep a three-item remainder (#145) * docs(plans): scope audit and completion plan for Hermes first-class operations (#104) Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com> * docs(plans): use repository relay verification commands Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com> * docs(plans): use in-repo citations and non-colliding contract ids Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com> * docs(plans): record founder decisions on #104 model-config supersession and disposition Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com> * docs(plans): fix section separators around the decided-questions table Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com> * docs(plans): add #104 closeout artifacts and record the #149 amendment ownership Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com> * docs(plans): cite #149's shipped capability descriptor instead of the #123 plan Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-Authored-By: Oscar Le <oscar.lehuu@gmail.com> --------- Signed-off-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Oscar Le <oscar.lehuu@gmail.com>








Summary
Crew becomes the editing surface for the bound Hermes profile, without becoming a second source of truth. Three deliverables plus docs.
1. Model write-through. The bound profile's real model is read from
~/.hermes/profiles/<name>/config.yaml(model.provider/model.default) and edited throughhermes -p <name> config set model.provider|model.default. Crew persists nothing: the write-through model field descriptor carriespersistence: { kind: "none" },buildRuntimeModelProviderPayloaddrops model/provider from the save payload for such runtimes (test-pinned), andBUZZ_ACP_MODELstripping at spawn is untouched. The control always shows the shared-everywhere note and the next-fresh-session timing.2. SOUL.md write-through.
HermesSoulEditoropens populated with the real current file; a read failure is a named error, never a blank textarea presented as the persona. Writes are atomic (temp file in the same directory + rename), byte-exact, and touch no other profile file. Create-in-place gets a skippable persona step so Crew-born profiles aren't generic.3. Description is optional. Empty "Agent instructions" already produced
None→ noBUZZ_ACP_SYSTEM_PROMPTenv and nosession/newsystemPromptfield; that's now pinned by tests and the UI labels it optional with a three-layer helper (L1 profileSOUL.md, L2 harness office rules, L3 this box).Capability contract, not runtime branches (D-025). Each catalog entry now carries
{ modelSource, personaDoc, layer3 }, derived once at thefromRawAcpRuntimeCatalogprojection boundary — zero upstream Rust catalog changes.modelSourcecomes from the existing fact triple (profileArg&&providerLocked&& nomodelEnvVar);runtimeOwnsModelViaProfileis re-expressed through it rather than kept as a rival truth.personaDoccannot be derived from those facts (a profile-locked runtime does not imply a persona document), so it is a table in the capability module — the only place the stringhermesappears. Render code never branches onruntime.id, and runtimes without a persona document render nothing at all: no disabled editor, no implication of profile memory.Two settled sub-decisions, both recorded in D-038:
SOUL.mdat profile creation and there is no trustworthy source to reset from; shipping a Crew copy would drift from Hermes. Recorded as a known gap inHERMES.md.config setinvocations, so a failure on the second can leave the profile partially updated. No rollback is invented without a verified Hermes unset operation.Related issue
Fixes #118. Decision D-038 (allocated by the orchestrator; D-028..D-037 and D-039 are taken by PRs #120/#124/#127/#128/#129/#134/#122/#135).
Branched off
agents/profile-lifecycle-hardening(#134 / issue #119) per the sequencing plan, and targetsmain— until #134 merges this PR's diff contains #134's readiness/lifecycle commits as well. Doc edits are composed on top of the #124 (docs/state-truth-and-gate-audit) and #134 text, notmain's stale version. The model read path reuses #134'sread_profile_yamlrather than duplicating a parser.Testing
Not verifiable here, stated plainly: this VM has no
hermesbinary, no~/.hermes, and the repo has no install path, so the issue's live probe (real profile write, live-turn model pickup) could not be run. Evidence below is Rust tests against a tempHERMES_HOMEwith a fakehermes, contract tests, and Playwright against the mock bridge.BUZZ_ACP_SYSTEM_PROMPTand omitssession/newsystemPrompt; non-empty still usesSystemPromptTransport::Fieldforhermes-agentat protocol v2+.pnpm --filter buzz check,pnpm --filter buzz typecheck,check:px-text,check:file-sizes,cargo fmt --all -- --check: pass.pnpm --filter buzz test: 5053 passed / 0 failed / 1 skipped.just test-unit: pass.channel-activity-popover.spec.ts:274/368/459/753reproduce identically onorigin/agents/profile-lifecycle-hardeningwith no diff to that spec. CI is the arbiter — known-broken advisory lanes (Desktop Smoke E2E1/3/4, baseline run 31362178966) and themissionInboxflake (Flaky merge-gating test: missionInbox 'reference-stable snapshot' fails intermittently on main (fails Desktop Fast + NuncioCrew Gate) #135, fix in flight as fix(desktop): memoize mission inbox snapshots on inputs, not the wall clock #138) are excluded from the required Gate by design (D-032).Screenshots are posted as follow-up comments via
scripts/post-screenshots.shper the repo's PR-screenshot rule.Hand-driven UI run (headed Chromium against the mock bridge) caught and fixed a real bug plus copy problems, detailed in a follow-up comment: the create-in-place persona step rendered an error instead of an editor because
HermesSoulEditortreated amissingSOUL.md as fatal (a brand-new profile has none) —missingnow opens an empty editor that creates the file on save, whiledoes_not_existstays a named error. Also suppressed a contradictory "Not configured" global-defaults summary under the profile model, dropped the L1/L2/L3 numbering (it started at "L2" for non-Hermes runtimes), reworded the helper to lead with meaning over filenames, and made a failed model write keep the user's typed edit like the persona editor does.CI: required
NuncioCrew Gateand all required checks green; the only red lanes areDesktop Smoke E2Eshards 1/3, the known-broken advisory lane excluded from the Gate by design (D-032).Docs: D-038 in
DECISIONS.md;HERMES.mdrule 2 + hiring flow + reset gap; Slice 2 C-03..C-12 reconciled against what actually shipped infeatures/0001-hermes-first-class-runtime.md;STATE.mdper the anti-drift rule;desktop/src/features/agents/AGENTS.mdrules 3 and 8 rewritten (model resolution is a capability;ownedByProfilesurvives only for runtimes that genuinely cannot expose a model).Link to Devin session: https://app.devin.ai/sessions/1c69da96697d48b68f30f159b1310f9c
Requested by: @oscarlehuu