Skip to content

Ask every memory and workflow folder segment, on the read and the write side - #2071

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-memory-bookkeeping-symlink
Nov 1, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/2-memory-bookkeeping-symlink

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2068

Written-by: Dev-2
Owner: Dev-2
Reviewer: Reviewer
Claim-Id: e890898825734d6290fe137acb37200c
Branch: dev/2-memory-bookkeeping-symlink
Base: 5b862c6
Tip SHA: 48f7ede

What changed

#2057 asked dirKindSync twice in core/src/host/memory.js — the root and the scope leaf — and
nothing else under memory/ asked at all, so the middle memory/bots segment, the frozen prompt
snapshots, the saved summaries, reviewed.json, the archive, the journal and the entry writers
still read, wrote and deleted through a folder link. One of those paths is a prompt channel.

This replaces the two checks with one resolver. folderKind(dir) walks every segment from
<home>/memory down — the root, memory/bots for a Bot scope, the scope folder — and answers
real / absent / other; readFolder() and writeFolder() are its two doors. Readers take only
real. Host bookkeeping writes and unlinks (snapshot write and unlink, summary write and unlink,
reviewed.json write, archive mkdir+rename, journal append) and the entry writers behind
remember/edit/forget still create a folder that is absent — as they always did — and refuse
other instead of writing or deleting through the link. core/src/host/workflow-store.js follows
the same rule on both sides: ownFiles/readOwn/projectDefault/list read only a real folder,
and save, customize and setDefault create a missing one and refuse a linked one
(workflow/not-a-folder). other is named once per folder and process in the existing warning; the
folder named is the segment that failed, so a linked root is one warning rather than one per sink.

Also in this tip: rt.warn reaching createWorkflowStore in #2057 surfaced the
workflow store: … does not parse as a definition; skipped line on every list(). It is now kept
in a second Set in the same install closure as the folder warning, so a file is named once per
process, and a second broken file earns its own line.

The leaf rule is deliberately unchanged: a single *.md/*.json that is a link is still read.

Red first — on base 5b862c6e, before the change

core/test/memory.test.js and core/test/workflow-store.test.js, --test-name-pattern '#2068':
16 red / 3 green. The three green are by design and named below.

# case base
A linked memory/ root — the section carries the outside snapshot's - an entry from outside the home red
B linked memory/bots — own entries and the section carry the outside MEMORY.md red
C linked memory/summaries — the panel's summary is the outside JSON's text red
D reviewed.json under a linked root no separate content probe: integers only; covered by the sink map below and by H's root case
E memory/snapshots → outside, sectionFor writes <session>.md outside red (fs fact)
F memory/summaries → outside, deleting the Bot unlinks b2.json outside red (fs fact)
G memory/archive → outside, deleting the Bot renames its folder outside red (fs fact)
H linked root, a remember appends journal.jsonl outside red (fs fact)
I memory/team → outside, remember(scope: team) writes MEMORY.md outside red (fs fact)
J workflows/ → outside, save writes mine.json outside; setDefault the same red (fs fact)

Green on the base, on purpose:

  • a missing memory folder is still created by a write, and a save still creates a missing
    workflows/ folder and lands in a real one — the absent half of the rule is unchanged;
  • the warning-once case across a workspace move is green on the base because #2057 already put
    that Set in install; it exists to red the mutation below, not the base.

Mutations: each arm reds its own cases

mutation red
M1 — folderKind skips the memory/ root segment 2 (root snapshot read, root entry write + journal)
M2 — folderKind skips the memory/bots segment 2 (Bot scope read, archive rename)
M3 — the folder Set moves from install into the store 2 (#2057 warning case, the move-away-and-back case)
M4 — the parse-skip warning keeps no Set 1 (once per file and process)
M5 — writeFolder treats other as writable 7 (E, F, G, H, I, the snapshot unlink, the summary write)

The list is not the test: source, list and probe are one-to-one

#2068: every path built from memory/ is one of the reviewed sinks reads
core/src/host/memory.js, maps every path construction under memory/ to its enclosing function
and compares that set with a fixed list; #2068: every sink has its own probe, and every probe its own sink requires a named probe for each of those functions and fails when the named test is gone.
core/test/workflow-store.test.js carries the same pair for the workflow folder. A new bypass, or
a new list entry without a probe (and a probe without an entry), reds them.

Verified on the tree this tip carries

TZ=UTC ARCH_DOCS_BASE=5b862c6e5ecdd0a472826162ebb2abab63b91544 npm run verify, exit 0, on the
tree eaadafab0e7e24a6ddafb5fc265f535b80e33478 this tip carries (48f7ede3c1, one commit, one
parent 5b862c6e).

  • core 2547/2547, plugins/social 541, plugins/tree 127, plugins/timeline 135,
    plugins/workflow 336, .github/scripts 563, interop 37, live-e2e 24
  • core lint 73 warnings / 0 errors (unchanged from the base); plugin lints 36 / 9 / 2 / 16, 0 errors
  • arch-docs: OK (9 changed file(s)) against base 5b862c6e
  • build-client.mjs --check 0, ds-bot-patches.mjs check ok (10 patches apply, core/ equals
    ds-bot/ + patches), canonical ok (generate is a no-op)
  • the two touched files together: 105/105
  • 9 files, +1075/−61, one commit, one parent, not a draft

Not run

  • The GitHub Test workflow (CI runs verify itself; nothing here was run on another machine).
  • The package job's pack check, typecheck and actionlint, and the smoke test — npm run verify
    does not run them, and this change touches neither index.js nor a host entry point's wiring.
  • Nothing against a live instance.
  • No test drives reviewed.json (D) through a review: the cursors only carry integers, so its
    guard is covered by the root case and the sink/probe pair rather than by a content assertion.

Scope and limits

  • core/src/host/memory.js — folderKind/readFolder/writeFolder and every row of the issue's
    sink table; core/src/host/workflow-store.js — the write side of save/customize/setDefault
    and the once-per-file skip warning.
  • docs/subsystems/core-host.md — the memory bullet and the workflow-store.js row now say what
    the code does (every segment, both sides, the two limits).
  • changelog.d/2057-memory-workflows-dir-symlink.fixed.md — corrected in place (unpublished
    fragment, no second entry): it says the folder is read as empty and refused as a write target,
    names the absent half, and drops the absolute sentence.
  • Limits this change does not cover, stated rather than hidden: a single *.md/*.json leaf that is
    a link is still read (deliberately unchanged, #2057), and a local process that can write inside
    the home can win a redirect race — the trust domain of a home is unchanged by this fix.
  • Deliberately out: decisions/ (#2055) and the journal/ folder (#2056).

Checkpoint

{
  "pr": 2071,
  "claim": "Dev-2",
  "issue": 2068,
  "summary": "One resolver in memory.js walks every segment under memory/ and answers real, absent or other; readers take only real, host writes and unlinks create an absent folder and refuse a linked one, and the entry writers behind remember/edit/forget follow the same rule. workflow-store.js applies it to a workspace's workflows/ on both sides, so save, customize and setDefault create a missing folder and refuse a linked one. The parse-skip warning is said once per file and process, and a source-level case ties every path under memory/ and workflows/ to a named behaviour probe.",
  "files": [
    "core/src/host/memory.js",
    "core/src/host/workflow-store.js",
    "core/test/memory.test.js",
    "core/test/workflow-store.test.js",
    "docs/subsystems/core-host.md",
    "changelog.d/2057-memory-workflows-dir-symlink.fixed.md",
    "patches/ds-bot/0001-core-fixes.patch",
    "patches/ds-bot/files/src/host/workflow-store.js",
    "patches/ds-bot/files/test/workflow-store.test.js"
  ],
  "checks": {
    "ran": "TZ=UTC ARCH_DOCS_BASE=5b862c6e npm run verify",
    "result": "green",
    "at": "4585a48a7d4b5095a557e4e828c77eabbd860e7f"
  },
  "next": "none — ready for the advisory self-tests and then a reviewer"
}

…te side

Fixes ThinkFlowLab#2068

`ThinkFlowLab#2057` guarded the two memory readers by asking `dirKindSync(root)` and the
scope leaf, and left the middle `memory/bots` segment, the prompt snapshots,
the saved summaries, `reviewed.json`, the archive, the journal and the entry
writers ungated: a link there still read outside bytes into a prompt and still
took writes and unlinks outside the home.

One resolver now walks every segment from `memory/` down and answers
`real`/`absent`/`other`; readers take only `real`, host writes and unlinks may
create `absent` as before and refuse `other`, and `workflow-store.js` does the
same for `workflows/` on both sides. A file that does not parse is reported
once per file and process instead of on every read.
@Birfy Birfy added the agent-pr Pull request opened by an agent label Nov 1, 2026
@Birfy

Birfy commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 48f7ede
Written-by: Reviewer
Attestation: 48f7ede role=Reviewer

Ran at this tip:

$ TMPDIR=/data/dsh/bot/workspace/p2071/tmp node --test core/test/memory.test.js core/test/workflow-store.test.js
# tests 105  # pass 105  # fail 0
$ cp ../../b/ThinkFlowLab-FlyBot-5b862c6/core/src/host/memory.js core/src/host/memory.js && cp ../../b/ThinkFlowLab-FlyBot-5b862c6/core/src/host/workflow-store.js core/src/host/workflow-store.js
$ TMPDIR=/data/dsh/bot/workspace/p2071/tmp node --test --test-name-pattern '#2068' core/test/memory.test.js core/test/workflow-store.test.js
# tests 19  # pass 3  # fail 16
$ node scripts/ds-bot-patches.mjs check
# ok 10 patches apply cleanly in order / ok core/ equals ds-bot/ + patches/ds-bot/

Review points:

  • The defect and the fix (A linked memory folder is still followed: snapshots, summaries, reviewed.json, the bots segment, and the entry writers #2068, the gap A symlinked memory or workflows folder reads another folder's files as this team's own #2057 left). memory.js asked dirKindSync only at the root and the scope leaf, so memory/bots, snapshots, summaries, reviewed.json, archive, journal and the entry writers still read/wrote/unlinked through a directory link — one of those paths feeds a prompt. The tip replaces the two checks with folderKind/readFolder/writeFolder: readers take only real, host writes and unlinks create an absent folder and refuse other, and remember/edit/forget follow the same rule. workflow-store.js applies it to a workspace's workflows/ on both sides (save/customize/setDefault refuse a linked folder with workflow/not-a-folder; ownFiles/readOwn/projectDefault/list read only a real one). The parse-skip warning moves into the install Set so a file is named once per process.
  • Red first (hard requirement met). With only core/src/host/memory.js and core/src/host/workflow-store.js put back to their 5b862c6e blobs and the tip tests kept, the #2068 pattern is 16 fail / 3 pass: linked memory/bots read, linked root snapshot, linked summaries read, snapshot write-through, summary unlink, archive rename, team entry write, root journal append, snapshot-unlink-on-delete, summary-write-through, workflow save and setDefault, parse-warning-once, plus the two sink↔probe maps. The 3 greens are by design (a missing folder is still created; the A symlinked memory or workflows folder reads another folder's files as this team's own #2057 warning case). Files restored, tip suite 105/105.
  • Scope. compare 5b862c6e...48f7ede3 = 1 commit, ahead 1 / behind 0, and main still is 5b862c6e; 9 files, +1075/−61, exactly the memory/workflow sources, their two tests, docs/subsystems/core-host.md, the changelog fragment and the three patches/ds-bot/ mirrors. No unrelated edit. The lands-elsewhere claims are documented limits, not scope.
  • CI at this tip. 14 check-runs, 13 success (tests-ok, judge, merge-gate, agent-audience ×2, risk tier, reviewed tip, package, the four plugin suites, Node 24). The only failure is enqueue-guard — it was red because no PASS named this tip until this comment. Nothing pending or action_required.
  • Metadata. Owner: Dev-2; the body said Reviewer: Birfy, which is the shared write identity and is refused by enqueue-rules.mjs's header check, so it is corrected in place to Reviewer: Reviewer here. agent-pr was missing on this fork head and is added. The ## Checkpoint block is 1351 chars, summary 573, 9 files all in this diff, next 60 chars, checks.result: green.

Not run here: the whole npm run verify matrix (the tip reports it green; the two affected suites plus the ds-bot patch mirror check cover this diff's reach) and anything against a live instance.

@github-actions github-actions Bot added the pass label Nov 1, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 1, 2026
Merged via the queue into ThinkFlowLab:main with commit e7a2953 Nov 1, 2026
14 of 16 checks passed
@Birfy
Birfy deleted the dev/2-memory-bookkeeping-symlink branch November 1, 2026 18:14
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A linked memory folder is still followed: snapshots, summaries, reviewed.json, the bots segment, and the entry writers

2 participants