diff --git a/packages/hashline/CHANGELOG.md b/packages/hashline/CHANGELOG.md
index 4f10d94fe..ca481d360 100644
--- a/packages/hashline/CHANGELOG.md
+++ b/packages/hashline/CHANGELOG.md
@@ -2,10 +2,6 @@
## [Unreleased]
-### Fixed
-
-- Fixed repeated edit-tool rejections by recovering comma-separated ranges and malformed local-model trailers, while steering agents to canonical string input and `.=` syntax ([#5805](https://github.com/can1357/oh-my-pi/issues/5805)).
-
## [17.0.0] - 2026-07-15
### Added
diff --git a/packages/hashline/src/input.ts b/packages/hashline/src/input.ts
index fcba57dfb..e2b3f9455 100644
--- a/packages/hashline/src/input.ts
+++ b/packages/hashline/src/input.ts
@@ -116,11 +116,6 @@ function parseHashlineHeaderLine(line: string, cwd?: string): RawSection | null
// the half-dozen variants models actually emit.
const recovered = tryParseRecoveryHeader(trimmed, cwd);
if (recovered !== null) return recovered;
- if (trimmed === "[" || trimmed.startsWith('["') || trimmed.startsWith("[{")) {
- throw new Error(
- "Edit input must be one patch string, not a JSON array. Join patch lines with newlines inside the `input` string.",
- );
- }
throw new Error(
`Input header must be ${HL_FILE_PREFIX}PATH${HL_FILE_SUFFIX} or ${HL_FILE_PREFIX}PATH${HL_FILE_HASH_SEP}TAG${HL_FILE_SUFFIX} with a ${HL_FILE_HASH_LENGTH}-hex content-hash tag; got ${JSON.stringify(trimmed)}.`,
);
diff --git a/packages/hashline/src/prompt.md b/packages/hashline/src/prompt.md
index 2d57b09b8..655401689 100644
--- a/packages/hashline/src/prompt.md
+++ b/packages/hashline/src/prompt.md
@@ -1,10 +1,5 @@
Your patch language names lines to replace, delete, or insert at, then lists the new content. Rule of thumb: a header ending in `:` is followed by `+` body rows; `DEL` has no body.
-
-- Input is ONE patch string. NEVER pass an array.
-- Ranges use `N.=M` exactly. NEVER commas or `:=:`.
-
-
Every file section starts with `[PATH#TAG]`. `TAG` = 4-hex snapshot tag from your latest `read`/`search`, REQUIRED on every section — no hashless form. Create new files with `write`; hashline only edits existing files.
@@ -135,13 +130,6 @@ SWAP.BLK 1:
-# WRONG — comma range and `:=:` trailer. RIGHT: `SWAP 1.=17:`
-SWAP 1,17:=:
-+replacement
-# RIGHT
-SWAP 1.=17:
-+replacement
-
# WRONG — empty `SWAP` to delete. RIGHT: DEL 4
SWAP 4.=4:
@@ -178,9 +166,7 @@ INS.POST 3:
If you remember nothing else:
-1. INPUT IS ONE STRING. NEVER pass patch lines as an array.
-2. RE-GROUND AFTER EVERY EDIT. Every apply mints a fresh `#TAG` and renumbers — take the next edit's numbers from the edit response or a fresh `read`. Stale tag or surprise? STOP, re-`read`.
-3. RANGES ARE EXACT. Use `N.=M`; NEVER commas or `:=:`.
-4. RANGES ARE TIGHT. Cover only lines that change; a stale wide range shreds everything it spans. Whole construct → `SWAP.BLK N`.
-5. THE BODY IS THE FINAL CONTENT. Every body row starts with `+`; Markdown bullets use `+- item`, not `- item`.
+1. RE-GROUND AFTER EVERY EDIT. Every apply mints a fresh `#TAG` and renumbers — take the next edit's numbers from the edit response or a fresh `read`. Stale tag or surprise? STOP, re-`read`.
+2. RANGES ARE TIGHT. Cover only lines that change; a stale wide range shreds everything it spans. Whole construct → `SWAP.BLK N`.
+3. THE BODY IS THE FINAL CONTENT. Every body row starts with `+`; Markdown bullets use `+- item`, not `- item`.
diff --git a/packages/hashline/test/leniency.test.ts b/packages/hashline/test/leniency.test.ts
index 46144cc51..70af46634 100644
--- a/packages/hashline/test/leniency.test.ts
+++ b/packages/hashline/test/leniency.test.ts
@@ -57,12 +57,6 @@ describe("hashline section headers", () => {
expect(message).not.toContain("#0A3");
}
});
-
- it("explains that array-shaped tool input must be one patch string", () => {
- expect(() => Patch.parse('["[a.ts#1A2B]", "SWAP 1.=1:", "+after"]')).toThrow(
- /one patch string, not a JSON array/,
- );
- });
});
describe("hashline core — verb header forms", () => {
@@ -93,8 +87,6 @@ describe("hashline core — verb header forms", () => {
expect(applyPatch(FILE, "SWAP 2\u20263:\n+X")).toBe("a\nX\nd\ne");
expect(applyPatch(FILE, "SWAP 2 3:\n+X")).toBe("a\nX\nd\ne");
expect(applyPatch(FILE, "SWAP 2..3:\n+X")).toBe("a\nX\nd\ne"); // legacy `..` still accepted
- expect(applyPatch(FILE, "SWAP 2,3:\n+X")).toBe("a\nX\nd\ne");
- expect(applyPatch(FILE, "SWAP 2,3:=:\n+X")).toBe("a\nX\nd\ne");
expect(applyPatch(FILE, "SWAP 2.=3\n+X")).toBe("a\nX\nd\ne"); // missing colon
});
@@ -195,12 +187,8 @@ describe("hashline body contracts", () => {
expect(() => parsePatch("DEL 2\n+X")).toThrow(/does not take body rows/);
});
- it("accepts a trailing colon on bodyless delete headers", () => {
- expect(applyPatch(FILE, "DEL 2,3:")).toBe("a\nd\ne");
- });
-
- it("still rejects delete body rows after a trailing colon", () => {
- expect(() => parsePatch("DEL 2:\n+X")).toThrow(/does not take body rows/);
+ it("rejects delete with a colon", () => {
+ expect(() => parsePatch("DEL 2:\n+X")).toThrow(/has no colon/);
});
});