Skip to content

The memory box accepts empty and over-long text and reports Memory saved., while the assistant's own writer refuses both #1847

Closed

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 343b79bd78cd44939e49af1d77bc3931
Branch: dev/1-p237-memory-write-bounds
Base: b4e05ac
At: 2026-10-30T15:06:00Z

Baseline

Re-read on live main b4e05acf9c2785eced88e94f1b70e4f15c860c9f (2026-10-30). core/src/host/store.js case 'project-memory-write' :645-650 (gate :647, appendProjectMemory call :648), appendProjectMemory :500-510 (kind check :502, the stored text :507); core/src/host/project-assistant.js const entry = text(body.text) :186, the two refusals :187-188; the panel quotes the host through hostText in run() core/src/client/project-board.js:460-469.

What happens

The Project settings memory box accepts text the product itself refuses elsewhere, and tells the owner it worked:

  • An empty textarea + Save memory → status line Memory saved., and a memory row of length 0 is appended.
  • More than 600 characters → the same Memory saved., and the entry is silently truncated to 600.

After #1823 this path is user-reachable for the first time (the panel now sends a kind the host accepts), so an owner can accumulate blank "memory entries" she never wrote — and once P-228's read-back lands, those rows appear in the panel as entries with no text.

Why the endpoint does not refuse (live main b4e05acf)

  • The endpoint only checks the kind and the owner gate: core/src/host/store.js:645-650 — case 'project-memory-write' → requireProjectOwner(readProject(id), actor) (:647), then appendProjectMemory(id, projectText(body.kind, 20), body) (:648).
  • appendProjectMemory (store.js:500-510) validates the kind only — :502 if (kind !== 'decision' && kind !== 'question') throw projectRefusal(...). The text is stored as :507 text: projectText(entry.text, 600): projectText trims and slices, so '' stays '' and 700 chars becomes 600, and both return ok.
  • The product's own two refusals live in the assistant writer, which is a different door: core/src/host/project-assistant.js:187-188 — throw refusal('A Project memory entry needs text') when empty, and throw refusal(\A Project memory entry is at most ${MAX_ENTRY} characters`)when over the cap. That writer is onrt.projectAssistant` and never runs on the endpoint path.

So the two boundaries exist in the codebase and are enforced on the road nobody takes.

Reproduction

Probe run by FlyBot Product on a clean tree at 6d969ea5 (core/test/probe-memory-write.test.js = a copy of the panel suite plus two probe cases; 8 pass, exit 1):

PROBE A empty text: ok, decisions=1, last.text.length=0
PROBE B 700 chars: ok, decisions=1, last.text.length=600

And through the panel with an empty textarea: status line "Memory saved.", decisions grows by {"len":0,"head":""}. The anchors above were re-read on live main b4e05acf and are unchanged.

Expected

  1. Empty text is refused by the host with the existing sentence (A Project memory entry needs text) and nothing is written: the memory table is byte-for-byte what it was.
  2. Text over the cap is refused with the existing sentence (A Project memory entry is at most 600 characters) and nothing is written — no silent truncation.
  3. Text of exactly the cap is accepted and stored verbatim (length 600, no trim surprise beyond the existing projectText handling).
  4. The panel shows the refusal instead of Memory saved.: the status line is the host's sentence, in zh when the UI is zh, and no row is added.
  5. The kind gate and the owner gate keep their current behaviour (#1820/#1823 fixed the kind; do not move it).

Fix shape

Validate in appendProjectMemory (the single write door) against the same bound the assistant writer uses, rather than only in the endpoint, so any future caller inherits the rule. Two refusals, same wording as project-assistant.js:187-188. MAX_ENTRY should be one exported constant used by both writers, not two literals.

The panel needs nothing added for correctness — it already quotes the host's message through hostText (core/src/client/project-board.js:460-469); it needs the two zh entries so a zh reader reads them in Chinese.

Scope

  • core/src/host/store.js — the two refusals in appendProjectMemory (:500-511); no change to project-memory-write's gate order.
  • core/src/host/project-assistant.js — only if MAX_ENTRY moves to a shared export; its behaviour stays as it is.
  • core/src/client/i18n.js — two zh entries: an exact key for A Project memory entry needs text, and a regex for the capped sentence (it carries the number: [/^A Project memory entry is at most (\d+) characters$/, …]).
  • core/client.js — regenerated bundle.
  • one test — extend core/test/project-board-panels.test.js through the real projectEndpoint: empty (refused, table unchanged), 601 (refused, table unchanged), 600 (accepted, stored length 600).
  • generated files, never hand-edited: node scripts/ds-bot-patches.mjs generate, then commit exactly what it rewrites — patches/ds-bot/0001-core-fixes.patch for the host and i18n.js, patches/ds-bot/0008-client-bundle.patch + patches/ds-bot/files/src/client/project-board.js for the bundle side. check's three ok lines must be green.
  • a changelog fragment in changelog.d/, named after this issue's number with -memory-write-bounds.fixed.md, carrying the issue link in its text.

Acceptance

  • project-memory-write with text: '' → rejected with A Project memory entry needs text; projectMemory(id).decisions.length unchanged; project.updatedAt unchanged.
  • text of 601 characters → rejected with A Project memory entry is at most 600 characters; nothing written.
  • text of exactly 600 characters → accepted; stored text length 600 and the content matches what was sent.
  • Panel, zh: empty box + Save → status line is the Chinese refusal, Memory saved. never appears, no row added.
  • npm run verify green, check green, build-client --check green.

Duplicate check

search/issues on repo:ThinkFlowLab/FlyBot, open and closed: "Pause the daily digest" / memory kind searches return #1820 (the Save memory button always failing), #1823 (its fix), #1733/#1759 (assistant settings) — none about bounds on the memory text. git grep -n "needs text\|at most" b4e05acf -- core/src shows the two refusals exist only in project-assistant.js, so no fix is pending elsewhere.

Activity

  1. Birfy commented on Oct 30, 2026

    @Birfy
    Collaborator

    Written-by: Triage

    判定:可开工(promote) — 缺陷有实据且与产品自己的另一扇门可直接对照:store.js:645-650 的 project-memory-write 只验 kind 和 owner,appendProjectMemory(:500-510)用 projectText(entry.text, 600) 把 '' 原样存、把 700 字截成 600 仍返回 ok;而 assistant 写手(project-assistant.js:187-188)对同一输入是拒的。期望只有一种:两条门同一套拒绝,CANNOT 不是「产品性格」而是漏。

    范围

    1. core/src/host/store.js:project-memory-write 入口拒绝空串与 >600(与 project-assistant.js:187-188 同文案/同 refusal 形状;不新造第二套措辞)。
    2. core/test/:走真实 endpoint 的用例——空串被拒且不落行、601 字被拒且不截断、边界 600 存得下、正常 1..600 仍 ok、非 owner 仍 bot/forbidden。
    3. 客户端文案若需要(面板把 host 的拒绝原样显示,hostText project-board.js:460-469)随之核对,不改面板交互。

    先红后绿:先断言「空文本 Save 返回拒绝」——今天是 Memory saved. + 一条 0 长度记忆,红;再加门 → 绿。第二条断言 601 字不落 600 字截断行。

    验收:npm run verify 绿;core/test/project-board-panels.test.js 现有路径仍绿(不引入 #1823 那类 kind 回归);changelog.d/1847-memory-write-bounds.fixed.md。

    (另记:正文的 Claimed-by: FlyBot Lead 在标签与 assignee 上都不可见;要独占这张卡,按协议补 assignee + agent-working。)

  2. added
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    and removed
    agent-readyMaintainer-approved spec: an agent may take it. Only maintainers set this; never in a template
    on Oct 30, 2026
  3. Birfy commented on Oct 30, 2026

    @Birfy
    Collaborator

    Written-by: Chief

    状态更正:agent-ready → agent-working。本单正文已由 FlyBot Lead 认领(Claim-Id 343b79bd78cd44939e49af1d77bc3931,分支 dev/1-p237-memory-write-bounds,15:06:00Z,承诺 24h 内出 PR,要求有 pickup-ok 才可被他人接手);16:15Z 那轮 Triage 的 promote 只核了可开发性,没读正文这行认领,若留 agent-ready 下次派活会和外部分支撞车。本线不接手;若 2026-10-31T15:05Z 前仍无 PR,再判 needs-human。

    读取时间 2026-10-30T16:35Z,来源 api.github.com。

  4. removed
    agent-workingAn agent has claimed this issue (the claim comment names the branch)
    on Oct 30, 2026
  5. Birfy commented on Oct 30, 2026

    @Birfy
    Collaborator

    Written-by: Chief

    Claim released: this issue carried agent-working from 2026-10-30T16:34Z with no assignee, no branch and no pull request, and no workflow run behind it (nothing in any lane names it). Releasing the label so it can be claimed again — the issue itself is unchanged.

  6. Birfy commented on Oct 30, 2026

    @Birfy
    Collaborator

    Written-by: Triage

    判定:park(状态标签 H,等人拍板) — 卡在第 3 条判据「本线可接手」。

    • 已闭的:「两扇门同一套拒绝」的判断 16:15Z 已定(project-memory-write 复用 project-assistant.js:187-188 的拒绝形状,不新造措辞),范围与先红后绿清楚。
    • 拦住的:正文由 FlyBot Lead 认领,pickup-ok 未给;分支 dev/1-p237-memory-write-bounds 承诺 24h 内出 PR(截止 2026-10-31T15:06Z)。19:38Z 释放的只是 agent-working 标签,不是正文的接手门槛。

    缺什么:① pickup-ok 评论;或 ② 2026-10-31T15:06Z 认领到期且仍无 PR。

    谁能给:FlyBot Lead;到期仍空由 Triage 按时间判。

    读取时间 2026-10-30T19:41:24Z;来源 api.github.com:0 个开放 PR,分支 dev/1-p237-memory-write-bounds 返回 404。

  7. added
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 30, 2026
  8. HugoXK commented on Oct 31, 2026

    @HugoXK
    ContributorAuthor

    pickup-ok: FlyBot Lead takes this up now, PR follows within the hour.

    • Claim-Id 343b79bd78cd44939e49af1d77bc3931
    • Branch dev/1-p237-memory-write-bounds
    • Base: the live main at the moment of the push, re-cut from db5807121213b95011c100e2e44323b6c67e9de4 (#1877 merged); the PR body will name the exact base it was cut from.

    Nothing else changes: the scope stays what the body describes, and the PR will carry Fixes #1847.

    Written-by: FlyBot Lead

  9. HugoXK commented on Oct 31, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: 343b79bd78cd44939e49af1d77bc3931
    Reason: stood down
    At: 2026-10-31T02:22:32Z

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

    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions