mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-09 09:11:00 +08:00
Merge pull request #2094 from DeusData/fix/1924-yaml-plain-scalar-indicators
fix(yaml): scope > | [ ] indicators to the value start so Hermes configs install (#1924)
This commit is contained in:
@@ -2624,8 +2624,18 @@ static int yaml_sequence_line_has_unsupported(const yaml_doc_t *doc, const yaml_
|
||||
if (value == '#' && (i == start || doc->data[i - YAML_UNIT] == ' ')) {
|
||||
break;
|
||||
}
|
||||
/* Block-scalar (`|`, `>`) and flow-sequence (`[`, `]`) indicators
|
||||
* count only where a NODE begins, the same positional rule
|
||||
* yaml_range_has_unsupported_ex applies to `&`/`*`. Interior ones are
|
||||
* text: `probe: kaomoji face >w< here` is a plain scalar, and a real
|
||||
* Hermes config carried two such personas (#1924). `probe: >`,
|
||||
* `probe: |` and `probe: [a, b]` still begin a value and stay
|
||||
* unsupported here. */
|
||||
if (value == '[' || value == ']' || value == '|' || value == '>') {
|
||||
return YAML_MATCH;
|
||||
bool begins_node = previous == '\0' || previous == ':' || previous == '-';
|
||||
if (begins_node) {
|
||||
return YAML_MATCH;
|
||||
}
|
||||
}
|
||||
if (value != ' ' && value != '\t') {
|
||||
previous = value;
|
||||
|
||||
@@ -1096,6 +1096,109 @@ TEST(config_yaml_edit_bom_before_our_own_section_stays_single_issue1656) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ── #1924: block/flow indicators are indicators only where a value begins ──
|
||||
*
|
||||
* The sequence-document scan (the pre_llm_hook_install path) refused `>`,
|
||||
* `|`, `[` and `]` at ANY position of a plain scalar, so a Hermes persona
|
||||
* such as `probe: kaomoji face >w< here` made the hook op fail while the
|
||||
* mcp_servers op on the same file succeeded (bisected to two persona strings
|
||||
* by @ukind). YAML reads them as indicators only where a value begins — the
|
||||
* rule the `*`/`&` fix and the quote fix (#1631) already apply. Both real
|
||||
* Hermes ops run here; our blocks append, so the original document must
|
||||
* survive as an untouched prefix. */
|
||||
|
||||
TEST(config_yaml_edit_hermes_accepts_interior_gt_in_plain_scalar_issue1924) {
|
||||
const char *initial = "agent:\n"
|
||||
" personalities:\n"
|
||||
" probe: kaomoji face >w< here\n";
|
||||
yaml_fixture_t fixture;
|
||||
ASSERT_EQ(yaml_fixture_init(&fixture, initial), 0);
|
||||
ASSERT_EQ(yaml_hermes_upsert(&fixture), CBM_YAML_IDENTITY_EDIT_OK);
|
||||
ASSERT_EQ(yaml_hermes_hook_upsert(&fixture), CBM_YAML_IDENTITY_EDIT_OK);
|
||||
char *after = yaml_read_alloc(fixture.path);
|
||||
ASSERT_NOT_NULL(after);
|
||||
ASSERT_EQ(strncmp(after, initial, strlen(initial)), 0);
|
||||
ASSERT_NOT_NULL(strstr(after, "cbm-context"));
|
||||
free(after);
|
||||
th_cleanup(fixture.dir);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(config_yaml_edit_hermes_accepts_interior_pipe_in_plain_scalar_issue1924) {
|
||||
const char *initial = "agent:\n"
|
||||
" personalities:\n"
|
||||
" probe: pipe char | mid value\n";
|
||||
yaml_fixture_t fixture;
|
||||
ASSERT_EQ(yaml_fixture_init(&fixture, initial), 0);
|
||||
ASSERT_EQ(yaml_hermes_upsert(&fixture), CBM_YAML_IDENTITY_EDIT_OK);
|
||||
ASSERT_EQ(yaml_hermes_hook_upsert(&fixture), CBM_YAML_IDENTITY_EDIT_OK);
|
||||
char *after = yaml_read_alloc(fixture.path);
|
||||
ASSERT_NOT_NULL(after);
|
||||
ASSERT_EQ(strncmp(after, initial, strlen(initial)), 0);
|
||||
ASSERT_NOT_NULL(strstr(after, "cbm-context"));
|
||||
free(after);
|
||||
th_cleanup(fixture.dir);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(config_yaml_edit_hermes_accepts_interior_brackets_in_plain_scalar_issue1924) {
|
||||
/* In block context `[`/`]` inside a plain scalar are ordinary text; only
|
||||
* a value-start `[` opens a flow sequence. */
|
||||
const char *initial = "agent:\n"
|
||||
" personalities:\n"
|
||||
" probe: see docs[1] and args[] here\n";
|
||||
yaml_fixture_t fixture;
|
||||
ASSERT_EQ(yaml_fixture_init(&fixture, initial), 0);
|
||||
ASSERT_EQ(yaml_hermes_upsert(&fixture), CBM_YAML_IDENTITY_EDIT_OK);
|
||||
ASSERT_EQ(yaml_hermes_hook_upsert(&fixture), CBM_YAML_IDENTITY_EDIT_OK);
|
||||
char *after = yaml_read_alloc(fixture.path);
|
||||
ASSERT_NOT_NULL(after);
|
||||
ASSERT_EQ(strncmp(after, initial, strlen(initial)), 0);
|
||||
ASSERT_NOT_NULL(strstr(after, "cbm-context"));
|
||||
free(after);
|
||||
th_cleanup(fixture.dir);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(config_yaml_edit_hermes_hook_still_refuses_block_scalar_value_issue1924) {
|
||||
/* A `>` or `|` that BEGINS a value is a real block scalar whose
|
||||
* indentation contract this editor does not model: the fail-closed
|
||||
* refusal stays, and the file stays byte-identical. */
|
||||
static const char *const initials[] = {
|
||||
"agent:\n personalities:\n probe: >\n folded face\n",
|
||||
"agent:\n personalities:\n probe: |\n literal face\n",
|
||||
"agent:\n personalities:\n probe: >-\n chomped face\n",
|
||||
};
|
||||
for (size_t i = 0U; i < sizeof(initials) / sizeof(initials[0]); i++) {
|
||||
yaml_fixture_t fixture;
|
||||
ASSERT_EQ(yaml_fixture_init(&fixture, initials[i]), 0);
|
||||
ASSERT_EQ(yaml_hermes_hook_upsert(&fixture), CBM_YAML_IDENTITY_EDIT_ERROR);
|
||||
char *after = yaml_read_alloc(fixture.path);
|
||||
ASSERT_NOT_NULL(after);
|
||||
ASSERT_STR_EQ(after, initials[i]);
|
||||
free(after);
|
||||
th_cleanup(fixture.dir);
|
||||
}
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(config_yaml_edit_hermes_hook_still_refuses_nonempty_flow_sequence_value_issue1924) {
|
||||
/* Pins the #1631 line: exact `[]` is accepted, a non-empty flow sequence
|
||||
* value is still unsupported in the sequence-document scan. */
|
||||
const char *initial = "agent:\n"
|
||||
" personalities:\n"
|
||||
" probe: [a, b]\n";
|
||||
yaml_fixture_t fixture;
|
||||
ASSERT_EQ(yaml_fixture_init(&fixture, initial), 0);
|
||||
ASSERT_EQ(yaml_hermes_hook_upsert(&fixture), CBM_YAML_IDENTITY_EDIT_ERROR);
|
||||
char *after = yaml_read_alloc(fixture.path);
|
||||
ASSERT_NOT_NULL(after);
|
||||
ASSERT_STR_EQ(after, initial);
|
||||
free(after);
|
||||
th_cleanup(fixture.dir);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ── Owned-entry repair (#1631 galaxy + goose upgrades) ──────────────────────
|
||||
*
|
||||
* Byte-identity alone freezes users on any OLD canonical our past writers
|
||||
@@ -1989,6 +2092,11 @@ SUITE(config_yaml_edit) {
|
||||
RUN_TEST(config_yaml_edit_hermes_accepts_interior_apostrophe_and_plain_wrap_issue1631);
|
||||
RUN_TEST(config_yaml_edit_accepts_utf8_bom_issue1656);
|
||||
RUN_TEST(config_yaml_edit_bom_before_our_own_section_stays_single_issue1656);
|
||||
RUN_TEST(config_yaml_edit_hermes_accepts_interior_gt_in_plain_scalar_issue1924);
|
||||
RUN_TEST(config_yaml_edit_hermes_accepts_interior_pipe_in_plain_scalar_issue1924);
|
||||
RUN_TEST(config_yaml_edit_hermes_accepts_interior_brackets_in_plain_scalar_issue1924);
|
||||
RUN_TEST(config_yaml_edit_hermes_hook_still_refuses_block_scalar_value_issue1924);
|
||||
RUN_TEST(config_yaml_edit_hermes_hook_still_refuses_nonempty_flow_sequence_value_issue1924);
|
||||
RUN_TEST(config_yaml_edit_repairs_prior_unquoted_command_entry_issue1631);
|
||||
RUN_TEST(config_yaml_edit_repairs_prior_goose_block_without_name);
|
||||
RUN_TEST(config_yaml_edit_still_refuses_truly_foreign_entry_under_our_key);
|
||||
|
||||
Reference in New Issue
Block a user