Skip to content

feat(mapper): add simplicio.mechanical-edit/v1 anchor producer (closes #110)#116

Merged
wesleysimplicio merged 1 commit into
mainfrom
claude/issue-110-mechanical-edit
Jun 2, 2026
Merged

feat(mapper): add simplicio.mechanical-edit/v1 anchor producer (closes #110)#116
wesleysimplicio merged 1 commit into
mainfrom
claude/issue-110-mechanical-edit

Conversation

@wesleysimplicio
Copy link
Copy Markdown
Owner

Resumo

Fecha #110. Implementa o lado do mapper como produtor do contrato simplicio.mechanical-edit/v1 (canônico em simplicio-runtime#69).

Mudanças

  • simplicio_mapper/mechanical_edit.py (novo):
    • snapshot_hash(text), range_hash(text, s, e), is_binary(path), extract_file_entry(root, path, ranges), build_context(root, selections).
    • Schemas: MECHANICAL_EDIT_SCHEMA, MECHANICAL_EDIT_RESULT_SCHEMA, MAPPER_INDEX_SCHEMA.
    • Refusa missing file (FileNotFoundError) e binary file (ValueError) — nunca emite âncora ambígua.
    • COMPACT_LINE_THRESHOLD=2000 — arquivos grandes mantêm before_hash mas omitem must_contain.
  • tests/python/test_mechanical_edit.py — 12 testes cobrindo: stable range hashing, snapshot drift, anchor drift, missing/binary refusal, large-file compact mode, multi-language (TS/Py/JSON/MD), deterministic context_hash.
  • tests/fixtures/mech-edit-host/sample.ts, sample.py, sample.json, sample.md, binary.bin.
  • SIMPLICIO_INTEGRATION.md — nova seção "Mechanical Edit Contract" documentando producer + exemplo + garantias.

Safety rule

Esquema canônico, sem variação repo-local — campos novos em v1 são aditivos; rename/remove exige v2 + ADR.

Validação

Item Resultado
unittest tests.python.test_mechanical_edit ✅ 12 pass
Suite Python completa ✅ 60 pass (1 falha pré-existente do container: git signing fixture, idêntica ao baseline)
ruff check ✅ 0 erros
npm run lint ✅ 0 erros

Refs #110, simplicio-runtime#69.

https://claude.ai/code/session_01JdmemqddwFnvbceWyuDE8m


Generated by Claude Code

Closes #110.

Adopts the canonical cross-Simplicio mechanical edit contract pinned in
simplicio-runtime#69. The mapper is now the deterministic producer of the
edit-anchor context an LLM planner needs to plan compact JSON edits without
rewriting whole files.

Adds `simplicio_mapper.mechanical_edit` exposing:

- `snapshot_hash(text)` — sha256 of the full file content.
- `range_hash(text, start, end)` — sha256 of the 1-indexed inclusive line
  slice. Raises ValueError on out-of-bounds.
- `is_binary(path)` — heuristic NUL-byte / UTF-8 decode probe over the
  first 8 KiB.
- `extract_file_entry(root, path, ranges)` — builds the per-file dict with
  language detection (reusing `mapper._language_for`), full snapshot hash,
  and a `selected_ranges[]` list with `start_line`, `end_line`,
  `before_hash`, and `must_contain` (first + last line of the range,
  truncated to 120 chars). Files longer than `COMPACT_LINE_THRESHOLD`
  (2000 lines) omit `must_contain` so the envelope stays compact while
  still anchoring via the hash.
- `build_context(root, selections)` — groups selections by path, builds
  per-file entries, and emits the full envelope with
  `schema="simplicio.mechanical-edit/v1"`, an overall `context_hash` over
  all snapshot + range hashes, and the canonical `mapper_schema` pointer
  to `simplicio.mapper-index/v1`.

Missing files raise `FileNotFoundError` and binary files raise
`ValueError` — the mapper never silently emits an ambiguous anchor.

Tests (`tests/python/test_mechanical_edit.py`, 12 cases) cover stable
range hashing, snapshot drift, anchor drift detection, missing-file
refusal, binary-file refusal, large-file compact mode, multi-language
fixture parity (TS / Python / JSON / Markdown), and deterministic
context-hash agreement on repeated runs.

Fixtures live under `tests/fixtures/mech-edit-host/` and ship with the
test suite so downstream consumers can copy them.

Documents the contract in `SIMPLICIO_INTEGRATION.md` next to the existing
runtime contract section.

https://claude.ai/code/session_01JdmemqddwFnvbceWyuDE8m
@wesleysimplicio wesleysimplicio marked this pull request as ready for review June 2, 2026 13:51
Copilot AI review requested due to automatic review settings June 2, 2026 13:51
@wesleysimplicio wesleysimplicio merged commit 049ab23 into main Jun 2, 2026
36 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a new mapper-side producer module for the canonical simplicio.mechanical-edit/v1 contract, including deterministic snapshot/range hashing, file selection context building, and supporting fixtures/docs so downstream tooling can plan anchored edits without rewriting whole files.

Changes:

  • Introduces simplicio_mapper/mechanical_edit.py with hashing helpers, binary refusal, and context envelope builder.
  • Adds a dedicated Python test suite plus multi-language fixtures to validate determinism, drift detection, and compact-mode behavior.
  • Documents the mechanical-edit producer contract and usage in SIMPLICIO_INTEGRATION.md.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
simplicio_mapper/mechanical_edit.py New producer helpers for simplicio.mechanical-edit/v1 context envelopes and anchor hashing.
tests/python/test_mechanical_edit.py New unit tests covering hashing stability, drift detection, refusal modes, compact mode, and determinism.
tests/fixtures/mech-edit-host/sample.ts TypeScript fixture for multi-language hashing/anchor tests.
tests/fixtures/mech-edit-host/sample.py Python fixture for multi-language hashing/anchor tests.
tests/fixtures/mech-edit-host/sample.json JSON fixture for multi-language hashing/anchor tests.
tests/fixtures/mech-edit-host/sample.md Markdown fixture for multi-language hashing/anchor tests.
tests/fixtures/mech-edit-host/binary.bin Binary fixture to validate binary refusal behavior.
SIMPLICIO_INTEGRATION.md Documentation describing the mechanical-edit producer contract, usage, and guarantees.

Comment on lines +99 to +100
def _absolute(root: str, path: str) -> str:
return path if os.path.isabs(path) else os.path.join(root, path)
Comment on lines +105 to +125
abs_path = _absolute(root, path)
if not os.path.exists(abs_path):
raise FileNotFoundError(f"file not found: {path}")
if is_binary(abs_path):
raise ValueError(f"binary file refused: {path}")
text = _read_safe(abs_path)
compact = len(text.splitlines()) > COMPACT_LINE_THRESHOLD
selected = []
for start, end in ranges:
selected.append({
"start_line": start,
"end_line": end,
"before_hash": range_hash(text, start, end),
"must_contain": _must_contain(text, start, end, compact),
})
return {
"path": path.replace(os.sep, "/"),
"language": _language_for(abs_path),
"snapshot_hash": snapshot_hash(text),
"selected_ranges": selected,
}
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
big = root / "huge.py"
big.write_text("\n".join(f"line_{i}" for i in range(COMPACT_LINE_THRESHOLD + 50)))
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.

3 participants