ReMe/reme/steps
Ziyang Guo b0991db725
fix(frontmatter-read): put the frontmatter in the answer, not just a key count (#564)
* fix(frontmatter-read): put the frontmatter in the answer, not just a key count

`frontmatter_read` answered `Read frontmatter from <path> (N key(s))` and left
the frontmatter itself in `metadata["frontmatter"]`. MCP returns only
`response.answer`, so the LLM that asked for a file's frontmatter learned how
many keys exist and nothing else — the same shape of gap #379 describes for
`list`, where the caller cannot act on the result.

Render each key on its own line in the answer. Values are flattened to one
line each so a multi-line description cannot break the line-per-key layout:
lists join with commas and nested mappings render as compact JSON. An empty
frontmatter says `(no keys)` rather than trailing off after the colon.

The answer also names the probed path instead of the raw input, so a
suffix-less request reports the `.md` file that was actually read and the
caller can follow up with `read` without consulting metadata. Structured
`metadata["frontmatter"]` is unchanged, so programmatic consumers see no
difference.

Towards #379.

* docs(config): match the frontmatter_read description to the new answer

The job description still advertised "Read a file's frontmatter as a dict",
but the answer is now rendered `key: value` lines — the dict only survives in
metadata. An LLM choosing between `frontmatter_read` and a whole-file `read`
would expect a dict it can index into and get prose instead.

This is the wording-alignment point raised on #397, applied here before
review rather than after.

* fix(frontmatter-read): never fail a valid YAML read on rendering

Review on #564 found two rendering regressions plus boundary losses:

- json.dumps(..., sort_keys=True) raised on datetime.date values (YAML
  timestamps) and on mixed-type mapping keys, turning a parseable
  frontmatter into a failed job with empty metadata - the base revision
  read those files fine.
- Comma-joining lists lost item boundaries (["a, b", "c"] read back as
  one ambiguous string), [] was indistinguishable from an empty string,
  and the whitespace collapse also mangled spaces inside serialized
  values.

_render_value is now a single tolerant renderer: bare strings keep their
text but have line breaks escaped; everything else goes through
json.dumps(ensure_ascii=False, default=str) (no key sort) so collections
stay structured and YAML-only types serialize; anything JSON still
cannot represent falls back to repr. Keys are rendered through the same
path so a multiline key cannot break the one-key-per-line layout either.
The structured metadata contract is untouched.

Also aligns the benchmark.yaml job description with the textual answer
(default.yaml already says 'one key: value per line') and the module
docstring now distinguishes the textual answer from the structured
payload in metadata.

Regression coverage: nested dates, mixed mapping keys, empty lists,
comma-containing items, significant whitespace, and a literal block
scalar with a real newline.

* fix(frontmatter-read): use json.dumps for all scalars to preserve string and mapping distinctions

Follow-up to the @jinliyl review on #564 (10-15 round 2):

- _render_value now sends every scalar through json.dumps rather than
  the str-only branch. The str-only branch collapsed a literal '\n'
  two-character sequence and an actual newline into the same rendered
  form ('\n') and lost the distinction between the string 'null'
  and YAML null. The JSON form preserves both.
- Mixed mapping key types (int 1 vs string '1') now round-trip with
  their JSON-form distinction in the answer, matching the metadata.
  Callers that re-parse the answer body as JSON still discard one
  value (this is a YAML-side limitation, not a renderer one); pinned
  by a regression test.
- Updated the three existing answer assertions to carry the new
  quoted scalar form, and added two regression tests:
  test_read_answer_distinguishes_newline_from_literal_backslash_n and
  test_read_answer_mixed_mapping_key_types_render_via_json.

Note: the str-only branch's '\n' escape was the only reason the
description: |- literal-block test in test_read_answer_tolerates_rich_yaml_types
rendered as 'line one\\nline two'. With the change, the same
description renders as '"line one\nline two"' (real newline, JSON-
escaped), which is exactly the fix the review asked for.

* fix(frontmatter-read): split key/value rendering so keys are not JSON-quoted

The round-2 change sent every scalar (including string keys) through
json.dumps, which added quotes around string keys. The rendered answer
came out as '- "name": "Cobalt"' instead of '- name: "Cobalt"' on
the round-2 head; the 5 new and updated tests in test_frontmatter_steps
all failed for that reason on Unit Tests - py3.11 through py3.14.

Split the renderer:
- _render_key: pass-through str(key) so the answer reads as '- key:'.
- _render_value: still json.dumps (with default=str and ensure_ascii=False)
  for the structural pinning the round-2 review asked for (literal
  '\n' vs actual newline, the string 'null' vs YAML null, mixed mapping
  key types). Caller formats the line as '- {key}: {value}'.

Updated the three existing answer assertions to match the key-unchanged,
value-quoted shape (e.g. '- name: "Cobalt"'). The two new tests added
in the round-2 commit (newline, mixed keys) were already pinned on the
right shape; the round-1 key-quote was the only bug.

* fix(frontmatter-read): JSON-encode keys too so every answer entry stays on one line

A multiline YAML key broke the one-key-per-line layout because keys were
interpolated raw. Route keys through the same json.dumps renderer as
values, update the expected answers to the quoted-key form, correct the
renderer docstring and mixed-key test comments (JSON collapses int 1 and
string "1" keys alike — the metadata keeps the distinction), and pin the
rich-type answer to the quoted "created": "2026-10-12" rendering.

* test(frontmatter-read): cover multiline keys, reseed YAML blocks as literal strings

Add the multiline-key regression the review asked for, restate the
mixed-mapping-key case as a documented presentation limitation (JSON
stringifies both keys, so a re-parser keeps only the last value), and
write the YAML seeds as triple-quoted literals so black stops joining
the implicit concatenations and pylint's implicit-str-concat goes quiet.

* refactor(frontmatter-read): simplify answer rendering

---------

Co-authored-by: jinli.yl <jinli.yl@alibaba-inc.com>
2026-10-28 16:19:15 +08:00
..
common feat(sandbox): Add Home AutoBash plugin and path-scoped execution sandboxes (#603) 2026-10-27 18:08:26 +08:00
compaction fix(memory): preserve file consistency and decision client lifecycle (#672) 2026-10-26 13:08:02 +08:00
evolve fix(memory): preserve file consistency and decision client lifecycle (#672) 2026-10-26 13:08:02 +08:00
file_io fix(frontmatter-read): put the frontmatter in the answer, not just a key count (#564) 2026-10-28 16:19:15 +08:00
index fix(memory): preserve file consistency and decision client lifecycle (#672) 2026-10-26 13:08:02 +08:00
transfer feat(benchmark): add lme benchmark steps (#326) 2026-07-07 18:53:39 +09:00
__init__.py refactor: generalize decision models and add recoverable context compaction (#626) 2026-10-19 17:56:30 +08:00
base_step.py feat: auto-tag plugin-generated reports (#534) 2026-09-11 11:52:52 +08:00