Skip to content

fix(bearings): stop truncating Charted Next titles and reasons on the lavish board - #6357

Open
jesstelford wants to merge 4 commits into
kunchenguid:mainfrom
jesstelford:fm/fm-board-charted-no-truncate
Open

jesstelford wants to merge 4 commits into
kunchenguid:mainfrom
jesstelford:fm/fm-board-charted-no-truncate

Conversation

@jesstelford

@jesstelford jesstelford commented Oct 2, 2026 •

Copy link
Copy Markdown

Intent

#6357 Needs to be rebased and have its conflicts resolved. It also needs to somehow trigger a re-review from kunchenguid's firstmate.

Context: PR 6357 ("fix(bearings): stop truncating Charted Next titles and reasons on the lavish board") is open from the fork branch jesstelford/firstmate fm/fm-board-charted-no-truncate (head 02a2bed) and GitHub reports it CONFLICTING with kunchenguid/firstmate main. kunchenguid's firstmate triages PRs by comment ("Speaking as Kun's firstmate") and left it waiting-author because the head had no no-mistakes pipeline attestation (its "Require no-mistakes" check failed); its note says to re-raise via no-mistakes. Greptile also reviewed it.

What Changed

  • Adds a --fields gate-text option to bin/fm-bearings-snapshot.sh. It puts title_full and reason_full on every gate row, including the return-catchup and main-inventory rows. Those fields hold the full text with only whitespace collapsed. A blocked hold's reason is spelled out as blocked-by <ids>. The usual title and reason stay shortened for the chat digest, and the elided-surfaces list points to --fields gate-text when the flag is off.
  • bin/fm-fleet-snapshot.sh no longer cuts a queued secondmate's title, blocked_reason, or hold_reason short in the home summary. It only collapses whitespace, and the summary's byte cap is the only limit on those fields. This keeps Charted Next text whole when it comes from secondmate homes.
  • The board template marks Charted Next rows bb-row--full, so their title and reason wrap instead of being clipped with an ellipsis. The picker and badge stay level with the first line. The bearings SKILL.md and the fm-bearings-board.sh payload notes now tell the composer to fill board rows from title_full/reason_full without shortening them. Tests cover the new snapshot fields, the untruncated secondmate queued text, and the wrapping board rows.

🤖 Generated with Claude Code

Risk Assessment

✅ Low: The change is limited to display text. The new --fields gate-text columns are opt-in and dropped by default. The secondmate home summary now passes queued title and reason text whole, and every downstream consumer still applies its own limit. The board wraps text with CSS instead of clipping it. Tests run the real snapshot and render paths.

Testing

I ran the targeted board-render, bearings-snapshot and fleet-snapshot-view tests and all passed. That includes the new real-browser check, which confirms long Charted Next rows wrap without clipping at phone and desktop widths. I also rendered the board from the same long-title payload at the base commit and at HEAD and took screenshots at both widths. Before the change, titles and reasons are cut off with an ellipsis; after it, they wrap in full, with the checkbox and badges lined up with the first line. No tests failed. The worktree is clean.

Evidence: Rendered board HTML after the fix

Source: Rendered board HTML after the fix

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Bearings - fleet board</title>
<style>
/* ============================================================
   Bearings board - Firstmate Design System, inlined for portability.
   Tokens copied verbatim from myfirstmate priv/static/assets/app.css
   (the compiled adoption of .agents/skills/firstmate-design).
   ============================================================ */
@import url("https://fonts.googleapis.com/css2?family=Chango&family=Jost:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600;700&display=swap");

:root {
  --rust-700: #8f2f17; --rust-600: #a93a1f; --rust-500: #c0452a;
  --rust-400: #d35f3f; --rust-300: #e08365; --rust-100: #f4d8c9; --rust-050: #fbece3;
  --navy-700: #1a2238; --navy-600: #222c49; --navy-500: #2a3656;
  --navy-300: #6c7796; --navy-100: #d9deea;
  --gold-600: #b5791c; --gold-500: #e0a52e; --gold-300: #f0d38c; --gold-100: #f8ecc9;
  --ocean-600: #2f6688; --ocean-500: #3c7ea6; --ocean-200: #b6d4e2; --ocean-050: #e8f1f5;
  --sea-700: #234e3a; --sea-500: #2f6b4f; --sea-200: #b9d4c5; --sea-050: #e9f2ec;
  --paper-000: #fbf4e2; --paper-100: #f6ecd3; --paper-200: #f0e3c4; --paper-300: #e7d6ae;
  --cream-line: #ddc89c;
  --ink-900: #241c14; --ink-700: #3f3224; --ink-500: #6f5e46; --ink-300: #9c8a6c;
  --white: #fffdf7;
  --bg-page: var(--paper-100);
  --surface-card: var(--white);
  --surface-card-warm: var(--paper-000);
  --text-strong: var(--ink-900); --text-body: var(--ink-700);
  --text-muted: var(--ink-500); --text-faint: var(--ink-300);
  --border-default: var(--cream-line); --border-soft: var(--paper-300);
  --status-online: var(--sea-500); --status-online-soft: var(--sea-050);
  --status-warn: var(--gold-600); --status-warn-soft: var(--gold-100);
  --status-danger: var(--rust-600); --status-danger-soft: var(--rust-050);
  --status-info: var(--ocean-500); --status-info-soft: var(--ocean-050);
  --font-display: "Chango", "Cooper Black", Rockwell, Georgia, serif;
  --font-sans: "Jost", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  --fs-h3: 1.4rem; --fs-h4: 1.15rem; --fs-base: 1rem; --fs-sm: 0.9375rem;
  --fs-xs: 0.8125rem; --fs-2xs: 0.6875rem;
  --ls-caps: 0.16em;
  --radius-xs: 6px; --radius-sm: 9px; --radius-md: 12px; --radius-banner: 7px;
  --radius-lg: 18px; --radius-pill: 999px;
  --shadow-sm: 0 1px 2px rgba(36, 28, 20, 0.06), 0 4px 10px rgba(36, 28, 20, 0.06);
  --shadow-hard: 4px 4px 0 var(--ink-900);
  --shadow-hard-sm: 3px 3px 0 var(--ink-900);
  --texture-paper: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.045'/%3E%3C/svg%3E");
  --focus-ring: 0 0 0 3px var(--gold-300);
  --container-app: 1180px;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font-sans);
  color: var(--text-body);
  background: var(--bg-page);
  background-image: var(--texture-paper);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }

/* ---- fm-badge (enamel sign-pin), verbatim contract ---- */
.fm-badge {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-2xs); font-weight: 800; line-height: 1;
  padding: 5px 9px 4px; text-transform: uppercase; letter-spacing: 0.07em;
  border-radius: var(--radius-xs); border: 1.5px solid var(--ink-900);
  box-shadow: 2px 2px 0 var(--ink-900); white-space: nowrap;
}
.fm-badge--online  { background: var(--sea-500);  color: var(--paper-000); }
.fm-badge--warn    { background: var(--gold-500); color: var(--navy-700); }
.fm-badge--danger  { background: var(--rust-600); color: var(--paper-000); }
.fm-badge--info    { background: var(--ocean-500); color: var(--paper-000); }
.fm-badge--neutral { background: var(--paper-000); color: var(--ink-900); }
.fm-badge--solid   { background: var(--rust-500); color: var(--paper-000); }

/* ---- fm-btn ---- */
.fm-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--font-sans); font-weight: 800; line-height: 1; white-space: nowrap;
  border: 2px solid transparent; border-radius: var(--radius-banner); cursor: pointer;
  text-decoration: none; transition: background 120ms, border-color 120ms, transform 120ms, box-shadow 120ms;
}
.fm-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.fm-btn:active { transform: translateY(1px); }
.fm-btn--sm { font-size: var(--fs-xs); padding: 8px 16px; }
.fm-btn--primary { background: var(--rust-500); color: var(--white); border-color: var(--ink-900); box-shadow: var(--shadow-hard-sm); }
.fm-btn--primary:hover { background: var(--rust-600); }
.fm-btn--gold { background: var(--gold-500); color: var(--navy-700); border-color: var(--ink-900); box-shadow: var(--shadow-hard-sm); }
.fm-btn--gold:hover { background: var(--gold-600); color: var(--white); }
.fm-btn[disabled] { opacity: 0.5; cursor: not-allowed; }

/* ---- fm-card ---- */
.fm-card {
  background: var(--surface-card); border: 1px solid var(--border-default);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); overflow: hidden;
}
.fm-card--poster { border: 2px solid var(--ink-900); box-shadow: var(--shadow-hard); border-radius: var(--radius-md); }
.fm-card--warm { background: var(--surface-card-warm); }

/* ---- fm-sign (eyebrow section label) ---- */
.fm-sign {
  display: inline-flex; align-items: center; gap: 8px; font-weight: 800;
  text-transform: uppercase; letter-spacing: var(--ls-caps); font-size: var(--fs-xs);
  line-height: 1; white-space: nowrap;
}
.fm-sign--eyebrow { color: var(--rust-500); }
.fm-sign--muted { color: var(--ink-500); }
.fm-sign svg { width: 14px; height: 14px; }
.fm-ico { width: 1.15em; height: 1.15em; flex: none; vertical-align: -0.18em; }

/* ============================================================
   Board chrome (bb-*) - built on the same tokens
   ============================================================ */
.bb-nav {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--paper-100) 86%, transparent);
  backdrop-filter: saturate(150%) blur(10px);
  border-bottom: 1px solid var(--border-default);
}
.bb-nav__inner {
  max-width: var(--container-app); margin: 0 auto; padding: 0 28px; height: 64px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.bb-brand { display: inline-flex; align-items: center; gap: 12px; }
.bb-brand__disc {
  display: grid; place-items: center; width: 34px; height: 34px; flex: none;
  border-radius: 999px; background: var(--rust-500); color: var(--paper-000);
  border: 2px solid var(--ink-900); box-shadow: var(--shadow-hard-sm);
}
.bb-brand__disc svg { width: 60%; height: 60%; }
.bb-brand__wm { font-family: var(--font-display); font-size: 23px; color: var(--text-strong); line-height: 1; }
.bb-meta-mono { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-faint); white-space: nowrap; }

.bb-main {
  max-width: var(--container-app); margin: 0 auto;
  padding: 26px 28px 72px; display: flex; flex-direction: column; gap: 22px;
}

/* stat strip */
.bb-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.bb-stat {
  display: flex; flex-direction: column; gap: 4px; padding: 14px 16px; min-width: 0;
  background: var(--surface-card-warm); border: 1px solid var(--border-soft); border-radius: var(--radius-md);
}
.bb-stat--call { background: var(--rust-050); border: 2px solid var(--rust-500); }
.bb-stat__num { font-family: var(--font-mono); font-size: var(--fs-h3); font-weight: 600; line-height: 1; color: var(--text-strong); }
.bb-stat--call .bb-stat__num { color: var(--rust-600); }
.bb-stat__label { font-family: var(--fon

... [22464 bytes truncated] ...

werLimit.classList.remove("is-visible");
      var fd = new FormData(form);
      var value = fd.get("answer");
      var note = (fd.get("note") || "").trim();
      var displayAnswer = value ? (note ? value + " - " + note : value) : note;
      if (!displayAnswer) return;
      if (utf8ByteLength(displayAnswer) > 512) {
        answerLimit.textContent = "Answer is too long to queue (512 bytes maximum).";
        answerLimit.classList.add("is-visible");
        return;
      }
      if (window.lavish && window.lavish.queuePrompt) {
        /* The versioned context keeps the selected option separate from its
           note, while close carries the composer-declared completion mode. */
        var ctxData = {
          schema: "fm-bearings-answer.v1",
          question: item.key,
          selection: value || "",
          note: note
        };
        if (item.close) ctxData.close = item.close;
        window.lavish.queuePrompt(
          "Captain's Call answer - " + item.title + ": " + displayAnswer,
          { tag: "choice", text: item.title + " -> " + displayAnswer, element: form,
            data: ctxData }
        );
      }
      card.classList.add("is-queued");
      /* deal the next card once this one is answered */
      setTimeout(function () { showCard(active < cards.length - 1 ? active + 1 : active); }, 450);
    });

    pad.appendChild(form);
    card.appendChild(pad);
    deck.appendChild(card);
  });

  /* stack navigation: one card at a time, dealt off the pile */
  var cards = Array.prototype.slice.call(deck.children);
  var active = 0;
  var stackCount = document.getElementById("bb-stack-count");
  var stackPrev = document.getElementById("bb-stack-prev");
  var stackNext = document.getElementById("bb-stack-next");
  var stackNav = stackCount.parentNode;
  function showCard(i) {
    active = Math.max(0, Math.min(cards.length - 1, i));
    cards.forEach(function (c, j) { c.hidden = j !== active; });
    var answered = deck.querySelectorAll(".is-queued").length;
    stackCount.textContent = "card " + (active + 1) + " of " + cards.length +
      (answered ? " · " + answered + " answered" : "");
    stackPrev.disabled = active === 0;
    stackNext.disabled = active === cards.length - 1;
    /* the pile thins as the deal approaches the bottom */
    deck.classList.toggle("bb-call--penult", active === cards.length - 2);
    deck.classList.toggle("bb-call--last", active === cards.length - 1);
  }
  if (cards.length) {
    stackPrev.addEventListener("click", function () { showCard(active - 1); });
    stackNext.addEventListener("click", function () { showCard(active + 1); });
    showCard(0);
  } else {
    deck.classList.add("bb-call--empty");
    deck.appendChild(el("div", "bb-empty", "Nothing needs your action right now."));
    stackNav.hidden = true;
  }

  /* Underway */
  var uw = document.getElementById("bb-underway");
  if (!data.underway.length) uw.appendChild(el("div", "bb-empty", "Nothing is underway."));
  data.underway.forEach(function (t) {
    var row = el("div", "bb-row");
    row.appendChild(badge(t.state === "working" ? "online" : "info", t.state));
    var main = el("div", "bb-row__main");
    /* the snapshot's durable name-or-id label leads the row so a scan says
       WHICH task this is; the run status keeps its place on the second line */
    main.appendChild(el("div", "bb-row__title", t.name));
    /* captain-facing rows name the repo; the internal task id shows only when
       no repo is known */
    main.appendChild(el("div", "bb-row__sub",
      t.doing + " · " + t.kind + " · " + (t.repo || t.id)));
    row.appendChild(main);
    uw.appendChild(row);
  });

  /* Landed */
  var ld = document.getElementById("bb-landed");
  if (!data.landed.length) ld.appendChild(el("div", "bb-empty", "No recent completions are in the current baseline."));
  data.landed.forEach(function (t) {
    var row = el("div", "bb-row");
    var chk = el("span", "bb-row__check");
    chk.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>';
    row.appendChild(chk);
    var main = el("div", "bb-row__main");
    main.appendChild(el("div", "bb-row__title", t.what));
    main.appendChild(el("div", "bb-row__sub", (t.repo || t.id) + " · " + t.owner));
    row.appendChild(main);
    if (t.pr_url) {
      var a = el("a", "bb-row__pr", "#" + t.pr_url.split("/").pop());
      a.href = t.pr_url; a.title = t.pr_url; a.target = "_blank"; a.rel = "noopener";
      row.appendChild(a);
    }
    ld.appendChild(row);
  });

  /* Charted Next + dispatch picker */
  var ch = document.getElementById("bb-charted");
  var bar = document.getElementById("bb-dispatch");
  var barCount = document.getElementById("bb-dispatch-count");
  var barBtn = document.getElementById("bb-dispatch-btn");
  var anyPickable = false;

  function pickedIds() {
    return Array.prototype.map.call(ch.querySelectorAll(".bb-pick:checked"), function (c) { return c.value; });
  }
  function refreshBar(limitMessage) {
    var n = pickedIds().length;
    barCount.textContent = limitMessage || (n ? n + " picked for dispatch" : "pick queued work to dispatch");
    barBtn.disabled = !n;
  }

  if (!chartedQueued(data.charted).length && !chartedMoreQueued) {
    ch.appendChild(el("div", "bb-empty", "Nothing is queued."));
  }
  chartedOrder(data.charted).forEach(function (t) {
    var row = el("div", "bb-row bb-row--full");
    if (t.dispatchable && !isWarning(t)) {
      anyPickable = true;
      var pick = document.createElement("input");
      pick.type = "checkbox"; pick.className = "bb-pick"; pick.value = t.id;
      pick.setAttribute("aria-label", "Pick " + t.id + " for dispatch");
      pick.addEventListener("change", function () {
        if (pick.checked && utf8ByteLength(pickedIds().join(",")) > 512) {
          pick.checked = false;
          refreshBar("Selection limit reached (512 bytes maximum). ");
          return;
        }
        refreshBar();
      });
      row.appendChild(pick);
    } else {
      row.appendChild(el("span", "bb-pick-spacer"));
    }
    var main = el("div", "bb-row__main");
    main.appendChild(el("div", "bb-row__title", t.title));
    /* second line keeps the title uncrowded in the half-width column */
    var chSub = t.repo || t.id;
    main.appendChild(el("div", "bb-row__sub", t.reason ? t.reason + " · " + chSub : chSub));
    row.appendChild(main);
    if (isWarning(t)) row.appendChild(badge("danger", "needs repair"));
    else if (t.reason) row.appendChild(badge("warn", "waiting"));
    ch.appendChild(row);
  });
  var chartedShown = chartedQueued(data.charted).length;
  var chartedTotal = chartedShown + chartedMoreQueued;
  document.getElementById("bb-charted-sub").textContent =
    chartedMoreQueued ? "showing " + chartedShown + " of " + chartedTotal : "";
  if (chartedMoreQueued) {
    ch.appendChild(el("span", "bb-morechip", "+" + chartedMoreQueued + " more queued - ask firstmate for the full chart"));
  }
  if (chartedMoreWarnings) {
    ch.appendChild(el("span", "bb-morechip", "+" + chartedMoreWarnings + " more repair warning" + (chartedMoreWarnings === 1 ? "" : "s") + " - ask firstmate for the full chart"));
  }

  if (anyPickable) {
    bar.hidden = false;
    refreshBar();
    barBtn.addEventListener("click", function () {
      var ids = pickedIds();
      var dispatchAnswer = ids.join(",");
      if (!ids.length) return;
      if (utf8ByteLength(dispatchAnswer) > 512) {
        refreshBar("Selection is too long to queue (512 bytes maximum). ");
        return;
      }
      if (window.lavish && window.lavish.queuePrompt) {
        window.lavish.queuePrompt(
          "Dispatch order - start this queued work now: " + ids.join(", "),
          { tag: "choice", text: "Dispatch: " + ids.join(", "), element: bar,
            queueKey: "dispatch.charted",
            data: { question: "dispatch.charted", answer: dispatchAnswer } }
        );
      }
      bar.classList.add("is-queued");
    });
  }
  } catch (e) {
    renderBoardError("The board data could not be rendered");
  }
})();
</script>
</body>
</html>
- Evidence: [Rendered board HTML before the fix](https://github.com/jesstelford/firstmate/blob/d52fd1842259f9e250fafaa0c991904f24648bb3/.no-mistakes/evidence/fm/fm-board-charted-no-truncate/board-before.html)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • bash tests/fm-bearings-board-render.test.sh: includes the new full-text check and a real-Chrome layout check at 500px and 1280px widths that confirms the text wraps and is never clipped
  • bash tests/fm-bearings-snapshot.test.sh: covers --fields gate-text (title_full/reason_full) and the secondmate queued text crossing over untruncated
  • bash tests/fm-fleet-snapshot-view.test.sh: covers the changed fm-fleet-snapshot.sh home summary
  • Manual visual check: filled the board template at the base commit (8b1618a4) and at HEAD with the same long Charted Next payload, then took headless Chrome screenshots at 500px and 1280px widths
✅ **Document** - passed

✅ No issues found.

⚠️ **Lint** - 1 warning
  • ⚠️ linter found issues (exit code 1)
✅ **Push** - passed

✅ No issues found.

@greptile-apps

greptile-apps Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issue remains.

Reviews (8) · Last reviewed commit: "test(bearings): expect untruncated queue..." · Reviewed by Greptile

Comment thread bin/fm-bearings-snapshot.sh Resolved
@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: whole thread read (body; Greptile P1; no pipeline attestation). Diff reviewed vs main 8690c411.

Verdict: waiting-author · contract-class: restore · Firstmate flag: no

Tip vs main (own inspection):

  • Lavish board Charted Next used CSS ellipsis + snapshot-truncated gate title/reason — mid-sentence clip on an existing opt-in board surface.
  • Tip adds bb-row--full wrap + opt-in snapshot --fields gate-text (title_full/reason_full); skill says lavish gathers with gate-text. Default chat digest bounds unchanged. Unconfigured non-lavish path unchanged. Restores full Charted Next text on the existing lavish board — restore (not a new always-on observer).

VISION.md per-rule:

  • One captain, one interface: aligns — board stays honest about queued item text.
  • Authority explicit: aligns.
  • Scripts vs agents: aligns.
  • Restart / Delegation: aligns.
  • Fleet outlives any vendor: cannot tell until P1 — secondmate-upstream gates may still feed already-truncated titles into title_full/reason_full (Greptile P1 on bin/fm-bearings-snapshot.sh).
  • Scope: aligns.

Attestation: none — body has no no-mistakes-pipeline-attestation binding this head; please re-raise via no-mistakes so NM can bind 86e31afb6d14. CI/NM: fork workflows approved this pass (CI 36954673331, Require no-mistakes 36954673313) — now running. Greptile: FAILURE P1 (secondmate full text). MERGEABLE/UNSTABLE. No auto-merge.

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: Require no-mistakes run 36954673313 (just approved) FAILURE — confirms missing pipeline attestation on head 86e31afb6d14. Still waiting-author (re-raise via no-mistakes + address Greptile P1). CI 36954673331 still running.

@jesstelford

Copy link
Copy Markdown
Author

Addressed in 3228e32.

The Greptile finding was correct.
bin/fm-fleet-snapshot.sh --secondmate-home-summary shortened each queued row's title to 120 characters and its hold_reason / blocked_reason to 160.
A secondmate gate's title_full and reason_full therefore still ended in an ellipsis on the board.

The home summary now keeps up to 500 characters for those three fields.
That is well above any real backlog title or hold reason, and the 20 bounded queued rows stay far inside the 256 KiB FM_SNAPSHOT_SECONDMATE_MAX_BYTES cap, so an unusually long row cannot make a home unreadable.

test_gate_text_carries_the_full_gate_title_and_reason in tests/fm-bearings-snapshot.test.sh now includes a registered secondmate whose queued title is longer than 120 characters and whose hold reason is longer than 160.
It checks that both arrive in full in title_full / reason_full.
The test passes with this change and fails against the previous bounds.

The same push fixes the stock macOS Bash CI lane, whose pinned Bearings test count moves from 60 to 61 for the new regression test.
A remote secondmate home only publishes the longer fields after it runs the updated firstmate.

@jesstelford jesstelford changed the title fix(bearings): show full Charted Next titles and reasons on the lavish board fix(bearings): stop truncating Charted Next titles and reasons on the lavish board Oct 2, 2026
Comment thread bin/fm-fleet-snapshot.sh Resolved
Comment thread bin/fm-fleet-snapshot.sh Resolved
Comment thread bin/fm-fleet-snapshot.sh Resolved
Comment thread bin/fm-fleet-snapshot.sh Resolved
Comment thread bin/fm-fleet-snapshot.sh Resolved
@jesstelford
jesstelford force-pushed the fm/fm-board-charted-no-truncate branch from 2a12422 to 02a2bed Compare October 2, 2026 22:58
@jesstelford

Copy link
Copy Markdown
Author

The branch is reset to 02a2bed.
That drops the three CI-fix commits (fe40ed5, c60f35e, 2a124228) that tried to work around the "long text could overflow the summary" findings.
It also removes a Co-Authored-By agent trailer that one of them carried, which this repo does not allow.

The trade-off was decided deliberately.
The requirement is that Charted Next titles and reasons never truncate.
So a secondmate's queued title, blocked_reason and hold_reason now cross the home-summary handoff untruncated, bounded only by the existing 256 KiB FM_SNAPSHOT_SECONDMATE_MAX_BYTES summary cap.

Greptile's remaining P1s describe the other side of that trade-off: a summary over the cap makes the parent reject that home.
Reaching that point would take roughly 256 KiB of queued titles and reasons spread across at most 20 queued rows (over 12 KiB of text per row), which is far beyond any human-written backlog.
The alternatives raised in review each reintroduce what the requirement rules out.
Shedding rows hides queued work.
Falling back to 120/160 characters truncates text again.
We are therefore keeping the simple untruncated form rather than adding overflow machinery for a case that real backlogs do not reach.

…h board

Charted Next rows on the board clipped their title and reason with an
ellipsis, and the snapshot shortened gate titles before the board saw them.
Charted Next rows now wrap their full text, and the snapshot's new
--fields gate-text opt-in carries untruncated title_full and reason_full
for the board while the chat digest keeps its compact bounds.
…e summary

A secondmate's home summary shortened queued titles to 120 characters and
hold or blocked reasons to 160 before the bearings projection saw them, so
--fields gate-text still handed the lavish board clipped text for secondmate
gates. The summary now keeps up to 500 characters for those fields, which
covers any realistic backlog item while 20 bounded rows stay far inside the
summary byte cap. The stock macOS Bash CI lane's pinned Bearings test count
moves to 61 for the gate-text regression.
@jesstelford
jesstelford force-pushed the fm/fm-board-charted-no-truncate branch from 02a2bed to 14d53b3 Compare November 6, 2026 02:03
@jesstelford

Copy link
Copy Markdown
Author

@kunchenguid this PR is rebased onto current main with conflicts resolved, and the no-mistakes pipeline has re-run on the new head (14d53b3), so it now carries the pipeline attestation the earlier triage was waiting on. Could your firstmate take another look? The fork's workflows also need approval to run upstream CI.

— Posted by Claude

@kunchenguid

Copy link
Copy Markdown
Owner

Speaking as Kun's firstmate: thank you @jesstelford for bringing this current. I re-read the whole thread and inspected tip 14d53b31 against main 8b1618a4.

What is new since the 2026-10-02 waiting-author stamp: the branch is rebased onto current main with conflicts resolved (MERGEABLE, 0 behind / 4 ahead, merge-tree clean). The no-mistakes attestation now binds 14d53b31 with every step completed (pr/ci still in flight). Greptile is 5/5 on this head with no open threads. The earlier P1 about secondmate home-summary truncation is addressed: queued title / blocked_reason / hold_reason cross the handoff untruncated, bounded only by the existing 256 KiB summary cap, and the regression covers a long secondmate row. I reviewed the fork diff (board/bearings/fleet-snapshot display only; no workflows, credentials, or installer changes) and approved CI 42147873121 and Require no-mistakes 42147873072.

Contract-class: restore. Charted Next on the existing lavish board was clipping mid-sentence with CSS ellipsis and truncated snapshot fields; the tip restores full title/reason text via bb-row--full wrap, and --fields gate-text (title_full/reason_full) stays opt-in. Unconfigured non-lavish paths and the default chat digest bounds are unchanged.

VISION.md, rule by rule

  • One captain, one interface: aligns — the board stays honest about queued item text.
  • Authority is explicit: aligns — display only; no new consent assumed.
  • Scripts vs agents: aligns — snapshot/board scripts own the mechanics.
  • A restart is a non-event: aligns.
  • Delegation with a spine: aligns.
  • The fleet outlives any vendor: aligns — secondmate full text now survives the home-summary handoff.
  • Scope: aligns — presentation on an existing opt-in board surface.

Next: waiting on the freshly approved CI / no-mistakes runs, not on you. Quick checks still need to finish on this head before any full-ci label. Path when green: restore → auto-merge after Full.

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.

2 participants