Fix char/byte offset mix in Handoff::Document#complete #1423

Open
opened 2026-10-27 16:37:50 +00:00 by jared · 0 comments
Owner

Problem

complete computes offset = @frontmatter.begin(1) + @completed_line.begin(0) (character offsets) and passes it to byteslice (byte offsets). With a multi-byte character in the frontmatter before completed_at:, the stamped file loses a newline and keeps a stray :; it is then unparseable. No test fails because tests use ASCII text.

Reproduce

From plugins/os:

# ruby -Ilib -rhandoff -e '...'
text = "---\nproject: café\ncompleted_at:\n---\nbody\n"
p Handoff::Document.new(path: "x.md", text: text).complete(Time.utc(2026, 10, 27))
# => "---\nproject: cafécompleted_at: 2026-10-27 00:00:00:\n---\nbody\n"

With project: cafe (ASCII) the output is correct.

Approach

Required work:

  1. Make offset and slice use one unit: character indexing ([], length) for both, or byte offsets (MatchData#byteoffset) for both.
  2. Add a Document test from a string with a non-ASCII character in the frontmatter before completed_at:.

Acceptance criteria

  1. Given frontmatter project: café and an empty completed_at:, when complete(now) runs, then the result equals the input with only that line replaced by completed_at: <stamp>.
  2. Given that stamped text, when Document.new parses it, then completed_at returns the stamp.

Owner sketch

Handoff::Document keeps the fix. Optional: a small frontmatter-line object owns the line's span, so readers stop doing [1]/begin(1) arithmetic.

Origin

  • Trigger: false-positive process on Sdlc/Structural/InstanceVariableAssumption (#1364), 2026-10-27
  • Improvised this session: none
  • Chain: stamped handoff unparseable ← complete passes a character offset to byteslice (plugins/os/lib/handoff/document.rb:35) ← DESIGN
  • Root candidate: none found; searched Handoff::Document, byteslice, byte offset, complete_at
  • Where: plugins/os/lib/handoff/document.rb
  • Session: 0b4d7950-df1c-4eba-a561-ccc19b386a9a
  • Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/0b4d7950-df1c-4eba-a561-ccc19b386a9a.jsonl

VERDICT: CREATE

## Problem `complete` computes `offset = @frontmatter.begin(1) + @completed_line.begin(0)` (character offsets) and passes it to `byteslice` (byte offsets). With a multi-byte character in the frontmatter before `completed_at:`, the stamped file loses a newline and keeps a stray `:`; it is then unparseable. No test fails because tests use ASCII text. ## Reproduce From `plugins/os`: ```ruby # ruby -Ilib -rhandoff -e '...' text = "---\nproject: café\ncompleted_at:\n---\nbody\n" p Handoff::Document.new(path: "x.md", text: text).complete(Time.utc(2026, 10, 27)) # => "---\nproject: cafécompleted_at: 2026-10-27 00:00:00:\n---\nbody\n" ``` With `project: cafe` (ASCII) the output is correct. ## Approach Required work: 1. Make offset and slice use one unit: character indexing (`[]`, `length`) for both, or byte offsets (`MatchData#byteoffset`) for both. 2. Add a `Document` test from a string with a non-ASCII character in the frontmatter before `completed_at:`. ## Acceptance criteria 1. Given frontmatter `project: café` and an empty `completed_at:`, when `complete(now)` runs, then the result equals the input with only that line replaced by `completed_at: <stamp>`. 2. Given that stamped text, when `Document.new` parses it, then `completed_at` returns the stamp. ## Owner sketch `Handoff::Document` keeps the fix. Optional: a small frontmatter-line object owns the line's span, so readers stop doing `[1]`/`begin(1)` arithmetic. ## Origin - Trigger: false-positive process on Sdlc/Structural/InstanceVariableAssumption (#1364), 2026-10-27 - Improvised this session: none - Chain: stamped handoff unparseable ← `complete` passes a character offset to `byteslice` (`plugins/os/lib/handoff/document.rb:35`) ← DESIGN - Root candidate: none found; searched Handoff::Document, byteslice, byte offset, complete_at - Where: plugins/os/lib/handoff/document.rb - Session: 0b4d7950-df1c-4eba-a561-ccc19b386a9a - Transcript: /home/jared/.claude/projects/-home-jared-dev-cc-os/0b4d7950-df1c-4eba-a561-ccc19b386a9a.jsonl VERDICT: CREATE
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#1423
No description provided.