Skip to content

fix(setup): configure a comment-only Hermes config.yaml - #2096

Merged
morluto merged 11 commits into
morluto:mainfrom
JayOfTheKeyboard:fix/hermes-comment-only-config
Oct 13, 2026
Merged

morluto merged 11 commits into
morluto:mainfrom
JayOfTheKeyboard:fix/hermes-comment-only-config

Conversation

@JayOfTheKeyboard

@JayOfTheKeyboard JayOfTheKeyboard commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

rea setup --client hermes still fails when ~/.hermes/config.yaml holds only comments. #1983 fixed the empty file, and its PR body left the comment-only file out because the serializer would need separate handling. This finishes that case. No issue is linked, because the gap is the one #1983 already describes.

Problem and expected behavior

A file such as # Hermes configuration\n is not whitespace, so it skips the empty-file rule #1983 added. YAML then parses it to null, the same value an empty file gives, and the Hermes branch of parseDocument rejects it:

$ printf '# Hermes configuration\n' > "$HOME/.hermes/config.yaml"
$ rea setup --client hermes --yes --format json
"status": "needs_human"
"remediation": "Hermes: Configuration …/.hermes/config.yaml is malformed: … \"Invalid input: expected record, received null\""

A file holding only a --- marker fails the same way, and fixing the parse is not enough for it. yaml 2.9.0 gives a bare --- document a null scalar as its root, where a comment-only file has empty contents. serializeHermesConfiguration cannot set a key path on a scalar ("Expected a YAML collection as document contents").

Expected: both files are configured like an empty one, and their comments stay. DeepSeek Harness already does this for a comment-only cordis.patch.yml (appends a registration to a comment-only patch file in dshClientConfiguration.test.ts).

Change and scope

  • parseDocument reads a Hermes document with no settings as {}: comments only, or a bare ---. An explicit null or ~ root is written out, so it is still rejected like any other root that is not a map (changed after review).
  • serializeHermesConfiguration replaces a null-scalar root with an empty map before it adds the entry. The comments the scalar carried move to the new map, so ---\n# Hermes configuration\n keeps its comment.
  • A comment-only file needed no serializer change. Its root is empty and setIn already builds the map, keeping the comment above it.

Not changed, and yours to decide:

  • DeepSeek Harness accepts a comment-only cordis.patch.yml but rejects a bare --- one with "Cordis patches must be a YAML operation list". It is the same null-scalar root, in a separate reader (DshCordisYaml). I left it alone to keep this to Hermes.
  • JSON and JSONC clients reject a file that holds only a comment, for example // cursor\n in ~/.cursor/mcp.json, with "Invalid JSON/JSONC at offset 10: ValueExpected". That comes from the JSON parser, so this fix does not reach it, and whether each client should accept such a file is a separate question.
  • Uninstall leaves mcp_servers: {} behind, after the kept comments. An empty file does the same today after fix(setup): configure an empty Hermes config.yaml #1983.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Hermes document parsing in parseDocument and its serializer, serializeHermesConfiguration (src/application/ClientConfigurationDocument.ts).
  • CLI and MCP/tool-catalog contract: none.
  • Provider, bridge, target-format, or platform compatibility: Hermes client setup on every platform.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Process execution, authorization, cleanup, or containment impact: none. Backups and readback are unchanged.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Evidence and regression coverage

  • Tests: fix(setup): configure an empty Hermes config.yaml #1983's it.each in tests/boundary/filesystem/hermesClientConfiguration.test.ts gets 3 more inputs: "# Hermes configuration\n", "---\n" and "---\n# Hermes configuration\n". Each case now also checks that the written file starts with the original text. The shape-diagnostics it.each gets null, ~, a comment then null, and --- ~, which must still be rejected before any change. All 4 fail on the first commit and pass on the second.
  • Base reproduction: on the parent commit all 3 new cases fail with expected 'failed' to be 'configured'. The 2 existing cases still pass.
  • Each part of the fix is needed. Without the empty-document check in parseDocument, all 3 fail. Without the root replacement, the 2 --- cases fail. Without carrying the comments over, "---\n# Hermes configuration\n" fails on the kept text.
  • User-visible CLI output: the built CLI, rea setup --client hermes --yes against a scratch HOME, before and after.
config.yaml before Before After
empty ready, configured ready, configured
# Hermes configuration needs_human, "expected record, received null" ready, comment kept
--- needs_human, "expected record, received null" ready, marker kept
--- then # Hermes configuration needs_human, "expected record, received null" ready, marker and comment kept

With the fix, a second rea setup plans no actions and leaves the file byte-identical, and doctor reads the registration as aligned. rea uninstall then reports removed and keeps the comments and the marker.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run test:focused over the 8 files that cover client configuration setup, including the Hermes and DeepSeek Harness acceptance and boundary tests: 227 tests passed.
  • npm run check:fast: passed.
  • npm run verify:test-discovery: "Discovered all 915 test files exactly once."

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: Linux x64 (Fedora 44) only. No real Hermes client was run.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: none. Only a YAML document with no root, or a bare ---, is newly accepted, as a document with no settings. An explicit null root is rejected as before.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-13T18:43:36.270307Z 7114ccd New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@dandgabr dandgabr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review suggestion

  • P2 — Preserve explicit YAML null roots. parseYaml(text) ?? {} treats every YAML null scalar (for example null or ~) like a comment-only or bare---- document. Setup then replaces that explicit value with the REA server map, silently discarding existing configuration. Restrict the empty-document exception to syntactically empty/comment-only input and a bare document marker; continue rejecting explicit null scalar roots.

@morluto

morluto commented Oct 13, 2026

Copy link
Copy Markdown
Owner

@greptileai review

@greptile-apps

greptile-apps Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no blocking issue was found.

Summary

Allows Hermes setup to configure comment-only and bare-marker YAML files while keeping their comments.

  • Hermes setup adds registrations to comment-only and bare-marker YAML files.

Reviews (10) · Last reviewed commit: "Merge branch 'main' into fix/hermes-comm..." · Reviewed by Greptile

comments, or only a `---` marker, also parses to a null YAML document.
Setup still rejected it as malformed ("expected record, received null")
and reported Hermes as invalid.

Treat a null Hermes document as holding no settings, as morluto#1983 does for
an empty file. The serializer already edits a comment-only document in
place. A bare `---` document holds a null scalar that takes no keys, so
replace it with a map and keep the comments written after the marker.
Only a document with no root, or a bare --- marker whose null scalar has
no source text, holds no settings. An explicit null or ~ is a value, so
setup and uninstall reject it as before, the same as any other non-map
root.
@JayOfTheKeyboard
JayOfTheKeyboard force-pushed the fix/hermes-comment-only-config branch from 4e86418 to 41c6de4 Compare October 13, 2026 07:18
@JayOfTheKeyboard

Copy link
Copy Markdown
Contributor Author

@dandgabr Thanks, you're right. 41c6de4 narrows it. A document is treated as empty only when it has no root (comments only) or its root is the null scalar of a bare ---, which has no source text. An explicit null, ~ or --- ~ is written out, so setup and uninstall reject it again before changing anything, as main does.

Four cases cover it in the shape-diagnostics it.each: null, ~, a comment then null, and --- ~. All four fail on the previous commit and pass now. The branch is also rebased onto current main, and I've updated the body to match.

@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: 41c6de4b78

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

morluto and others added 3 commits October 13, 2026 20:13
…ction length limit

The Hermes YAML registration block reached 159 lines, past oxlint's
max-lines-per-function limit of 150. Move the empty-document cases and
the multiple-document rejection into their own block.
@JayOfTheKeyboard

JayOfTheKeyboard commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks for taking this further. I ran the checks you skipped against ede7dfe. The Hermes configuration tests passed (28), but typecheck and lint each failed once. Both are fixed on the branch now:

  • 5c9ffd8: editHermesConfigurationYaml assigned a new map to the contents of a Document.Parsed, which only accepts parsed nodes (HermesConfigurationYaml.ts(95,5): error TS2322). The edited document is annotated as a plain Document again, as it was before the move.
  • 67ec447: the Hermes YAML registration block in hermesClientConfiguration.test.ts had reached 159 lines, past max-lines-per-function (150). The empty-document cases and your multiple-document rejection now have their own Hermes YAML empty configurations block, with no other change.

check:fast (typecheck and lint) and the file's 28 tests pass on 5c9ffd8.

parseDocument with options returns a Document.Parsed, whose contents
accept only parsed nodes, so assigning the new map for a bare --- root
failed typecheck (TS2322). Annotate the edited document as Document, as
the code did before it moved into HermesConfigurationYaml.ts.
@morluto morluto added bug Something isn't working area: setup Setup, packaging, upgrade, install, Doctor, and client registration area: domain Domain logic, errors, and pure functions P2: medium Medium priority issue labels Oct 13, 2026
@morluto
morluto merged commit aa6f74a into morluto:main Oct 13, 2026
31 checks passed
@JayOfTheKeyboard

Copy link
Copy Markdown
Contributor Author

Thanks for merging, and for tightening the empty-document rule.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: domain Domain logic, errors, and pure functions area: setup Setup, packaging, upgrade, install, Doctor, and client registration bug Something isn't working P2: medium Medium priority issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants