Skip to content

A board card's detail is cut at exactly 300 chars mid-word with no ellipsis — 13 of 15 irreversible acts vanish, and the full text is already on the same card #568

Description

@serge-ivo

A board card's detail is cut at exactly 300 chars mid-word with no ellipsis — 13 of 15 irreversible acts vanish, and the full text is already on the same card

What the owner sees

instance_board for instance bd43f4de-ef35-4051-bdec-43f8571414a1, card deleg-ae7fa3f2-53a1-43ba-b3ad-921dd8dba7b1, Failed column:

outcome: failed — run error: Too many API requests by single Worker invocation. To configure this limit, refer to https://developers.cloudflare.com/workers/wrangler/configuration/#limits | Acts: pushed directly to the trunk origin main; pushed directly to the trunk origin main; pushed directly to th

Measured: exactly 300 characters, ending …pushed directly to th. No ellipsis, no marker. It reads as a finished sentence that names two pushes.

The same card, in instance_task_events, carries the full text under a different key:

reasoning (691 chars): Overseer delegated on your behalf → apps/chess-academy: … — outcome: failed — run error: … | Acts: pushed directly to the trunk origin main; ×4; **and 11 more.**

4 named + 11 more = 15 acts. Corroborated independently by agent_trace(level:"warn"), which has 15 act.consequential events on that run's session, all irreversible: true — 14 push.trunk to origin main and one recursive file.delete.

The board shows 2 of 15 irreversible acts, and gives the reader no reason to think anything is missing. The delegated objective was "commit directly to main, push. Do NOT create feature branches."

Mechanism — the cut and the marker are in two different functions

Verified. workers/api/src/lib/delegation.ts:41:

...(opts.note ? { description: opts.note.slice(0, 300) } : {}),

against reasoning two lines up (:30), capped at 8000. The comment at :36-41 explains why both exist:

ALSO as description, because that is the field every generic reader takes a card's one-line detail from (board.ts, and instance-work.ts for a supervisor) — reasoning is in neither chain, so the outcome text reached the console and nothing else.

So description is the field every generic reader (the console board, MCP instance_board, a supervising agent) uses, and it is the one carrying the 300-char cut. reasoning — complete, with its honest and 11 more. — is on the same row and is read by nothing on that chain.

lib/coding-board.ts:65 has the identical slice(0, 300), and lib/repo-policies.ts:257 (MAX_DESCRIPTION = 300) is the third — but repo-policies.ts:367 is the one that gets it right:

const sentence = parts.sentence.slice(0, Math.max(0, MAX_DESCRIPTION - attribution.length));

it budgets for the attribution it must keep. Nothing does the equivalent for the acts list.

Verified precedent for the marker. lib/tool-result-cap.ts:95-99 already made exactly this decision for the tool log, and states the principle:

A success is cut EXACTLY as before, with no ellipsis … A failure marks its cut, because the sentence it is cutting is an instruction and a silently truncated instruction is how this started.

A truncated list of irreversible pushes is the same class of sentence and gets no marker.

Why the ordering of the text makes it worse

The 300 chars are spent, in order, on: the outcome, a raw Cloudflare error, a Wrangler docs URL (81 chars of it), and then the acts. So the least useful content — a link to a configuration page for a Worker the owner does not own (#523) — displaces the record of 13 pushes to main. The acts are the part a human reads a Failed card for.

What to do — cheapest first

1. Add the ellipsis. One character, and it converts a false complete sentence into a visibly-cut one. The lowest-value fix, and worth shipping alone if nothing else is.

2. Build description as a budgeted summary rather than a prefix of reasoning. The count is already known at the call site — and 11 more. is computed there. Put the count FIRST: outcome: failed — 15 irreversible acts (14 pushes to origin main, 1 recursive delete) — run error: …. 300 chars is plenty for the facts; it is not plenty for a prefix of a 691-char sentence.

3. Let the generic readers reach the full text. instance_board returns a truncated detail and no field saying more exists. Either return reasoning alongside, or return detailTruncated: true — a reader that cannot tell it was cut cannot go and look.

Alternatives considered and rejected

Acceptance criteria

  • A delegation card whose run performed N acts states N; a reader can tell 15 from 2.
  • Any truncated description is visibly marked as truncated.
  • The full text remains reachable from the board response (reasoning or an explicit flag).
  • Test: build a card from a run with 15 acts and a 200-char error; assert the rendered detail names 15 and is ≤ the cap.

Regression risk

description is read by board.ts and instance-work.ts (per the comment at delegation.ts:36), and a supervising agent parses it. Changing the shape from "prefix of reasoning" to "budgeted summary" changes what a Lead reads back from a subordinate — check subordinate-payload.ts, which already trims this text a second time (:111) and could double-trim a newly-front-loaded count. Assert the count survives both passes.

Related: #523 (same run and same card — that issue is about classifying the failure, this one is about the acts being invisible), #503 (a payload silently dropping half its content), #517 (tool-result-cap's marked-cut precedent).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    backendBackend / Worker / API workbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions