Skip to content

fix(host): refuse a workflow folder that cannot be listed - #2615

Merged
Birfy merged 1 commit into
mainfrom
dev/2614-workflows-unreadable
Nov 7, 2026
Merged

Birfy merged 1 commit into
mainfrom
dev/2614-workflows-unreadable

Conversation

@Birfy

@Birfy Birfy commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #2614
Owner: Dev
Reviewer: Reviewer

What changed

A workflows/ folder that is a real directory but cannot be listed (a restore with the owner bits wrong, or a chmod -R accident) answered {"ok":true,"own":[]} while open('mine') still read the definition by name — listing needs +r, reading one name needs only +x. ownFiles() no longer swallows the readdirSync failure; list() answers workflow/unreadable naming the folder; save() and setDefault() report wrote/changed beside that refusal instead of ok:true with an empty own; open() still answers by name, and definitions() / rt.workflowDefinitions() keep their preset fallback.

Red first: the two new tests fail on main at the list() / save() assertions (true !== false), and pass with the change.

Ran at this tip:

$ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/workflow-store.test.js
# tests 25 / pass 25 / fail 0
$ node scripts/ds-bot-patches.mjs check
ok   10 patches apply cleanly in order
ok   core/ equals ds-bot/ + patches/ds-bot/
$ node /data/dsh/bot/workspace/d2607/repo/core/dev/harness/node_modules/oxlint/bin/oxlint -c .oxlintrc.json src test
Found 81 warnings and 0 errors.
$ node .github/scripts/changelog.mjs check
changelog: 17 fragment(s) in changelog.d/ are well formed

Not run: the full npm run verify (the shared host is running other work) and the smoke test (no index.js / boot change).

Written-by: Dev

Checkpoint

{
"pr": 2615,
"claim": "Dev",
"issue": 2614,
"summary": "A workflows folder that is a real directory but cannot be listed answered ok:true with an empty own, while open still read a definition by name. The listing half now refuses with workflow/unreadable naming the folder, and save/setDefault report wrote and changed beside that refusal instead of ok:true with no workflows. open still answers by name and the preset fallback is unchanged. The two new tests fail on main and pass with this change.",
"files": [
"core/src/host/workflow-store.js",
"core/test/workflow-store.test.js",
"patches/ds-bot/files/src/host/workflow-store.js",
"patches/ds-bot/files/test/workflow-store.test.js",
"docs/subsystems/core-host.md",
"changelog.d/2614.fixed.md"
],
"checks": {
"ran": "cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/workflow-store.test.js; node scripts/ds-bot-patches.mjs check; oxlint src test; node .github/scripts/changelog.mjs check",
"result": "green",
"at": "a4f3735c7dd9fb787ee84fc9150a1f663dc03232"
},
"next": "none — ready for review"
}

A `workflows/` folder that is a real directory but cannot be read listed
nothing, while `open` still read a definition by name, so a save answered
`ok: true` with an empty `own`. The listing half now refuses with
`workflow/unreadable`, a landed write keeps `wrote: true`, and opening by
name still works.

Refs #2614
@Birfy Birfy added the agent-pr Pull request opened by an agent label Nov 7, 2026
@Birfy Birfy self-assigned this Nov 7, 2026
@Birfy

Birfy commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at a4f3735
Written-by: Reviewer
Attestation: a4f3735 role=Reviewer

Ran at this tip:

$ cd core && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/harness-resolve.mjs --test test/workflow-store.test.js
# tests 25  # pass 25  # fail 0
$ cp /data/dsh/bot/workspace/w2615/base-store.js /data/dsh/bot/workspace/w2615/tip/core/src/host/workflow-store.js
$ cd core && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/harness-resolve.mjs --test test/workflow-store.test.js
# tests 25  # pass 23  # fail 2
$ cp /data/dsh/bot/workspace/w2615/tip-store.js.keep /data/dsh/bot/workspace/w2615/tip/core/src/host/workflow-store.js
$ cd core && TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/harness-resolve.mjs --test test/workflow-store.test.js
# tests 25  # pass 25  # fail 0
$ TZ=UTC DSH_HARNESS=/data/dsh/bot/workspace/d2607/repo/core/dev/harness node --import ./test/harness-resolve.mjs /tmp/repro2615.mjs
# reproduce: customize ok -> chmod 0o311 -> list workflow/unreadable (no own) / save ok:false wrote:true changed:true / open ok:true / chmod 0o755 -> list own ['mine']
$ node scripts/ds-bot-patches.mjs check
# skipped pin (no git)  ok 10 patches apply cleanly in order  ok core/ equals ds-bot/ + patches/ds-bot/
$ cd core && npx --no-install oxlint src test
# Found 81 warnings and 0 errors.
$ node .github/scripts/changelog.mjs check
# changelog: 17 fragment(s) in changelog.d/ are well formed

Review points:

  • Scope: exactly the 6 files the brief names, each blob matches the tip (core store dca74aa0, test a6596ac9, docs e032144b, changelog f3f5775f). No core/client.js, ruleset, CI or other subsystem touched; base a754cc4e compare returns the same 6.
  • (a) Source only changed what it should: ownFiles() keeps if (!folderReal()) return [] and no longer catches readdirSync; list() converts the failure to refuseUnreadable() (workflow/unreadable, no own key); save() computes wrote and, when the list refuses, returns that refusal plus id/wrote/changed instead of spreading an empty own into ok:true; setDefault() same; open() spreads the listing only when listed.ok. readOwn()'s JSON.parse catch, definitions() / rt.workflowDefinitions() preset fallback, folderReal/folderWritable/fileWritable are untouched (diff shows zero hunks there).
  • (b) Mirrors: cmp says patches/ds-bot/files/src/host/workflow-store.js and .../test/workflow-store.test.js are byte-identical to their core/ files, and both pairs share one blob sha (dca74aa0 / a6596ac9).
  • (c) #2068 probes: WORKFLOW_PROBES test is green (test 15), sinks and gates still match one-to-one, and this change adds no new join(dir, …) sink — the new refusal only names dir in its message.
  • Red first: with the base blob the same suite is 23/25, failing only not ok 24 and not ok 25 — the new list() assertion at core/test/workflow-store.test.js:554 (listed.ok true !== false) and the new save() assertion at :579 (saved.ok); restoring the tip file returns 25/25.
  • Independent reproduction on a real folder (not just the tests): customize writes mine; chmod 0o311 makes list() ok:false/workflow/unreadable with no own, save() ok:false with wrote:true/changed:true and the bytes really land (mine.json name Mine 2 on disk), open('mine') stays ok:true; a no-op save reports wrote:false; chmod 0o755 lists mine again.
  • CI at the tip: tests-ok success, merge-gate success, every other run success; only enqueue-guard is red because no PASS existed yet — the expected pre-review state.
  • Checkpoint block is valid: 1180 chars (<1500), summary 443 chars (<600), next 23, pr 2615 / claim Dev / issue 2614, 6 files all in this diff, checks.at the full tip sha.

Not run: the full npm run verify (the shared host is running other lines) and the smoke test (no index.js / host boot change).

@Birfy
Birfy added this pull request to the merge queue Nov 7, 2026
@github-actions github-actions Bot added the pass label Nov 7, 2026
Merged via the queue into main with commit bfc6295 Nov 7, 2026
17 of 19 checks passed
@Birfy
Birfy deleted the dev/2614-workflows-unreadable branch November 7, 2026 14:50
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 7, 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.

bug(host): a workflows folder that cannot be listed answers ok:true with an empty list

1 participant