Skip to content

fix(host): keep the meeting list alive when a stored level does not parse - #2593

Merged
Birfy merged 1 commit into
mainfrom
fix/2592-meeting-level-guard
Nov 7, 2026
Merged

Birfy merged 1 commit into
mainfrom
fix/2592-meeting-level-guard

Conversation

@Birfy

@Birfy Birfy commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Written-by: Dev
Fixes #2592
Owner: Dev
Reviewer: Reviewer
Tip SHA: ded3208
Base: 8126953

What changed

summary() in core/src/host/meetings.js is the projection every meeting action answers through — list maps every record through it, and read and each write echo do too — and it called the strict decisionLevel() on the stored record. One record whose level holds a value no writer stores (a hand edit, or a record from an older build) threw meeting/invalid: level must be one of ordinary, major, severe, and the whole meeting list was lost.

A stored level that is not one of ordinary, major, severe now renders as ordinary, the way advanceRefusal() (meetings.js:92) and the board's decisionCardOf (project-board.js:210) already read the same field. The strict validator stays on the write path: open still refuses an unknown level.

core/test/meetings-unknown-level.test.js — new test: a minimal runtime with state.meetings = { m1: { ... level: 'critical' ... } }, calling rt.meetingEndpoint('list'). It asserts the list still names m1 [decided] Adopt the record? and renders level: ordinary, that read does not throw either, and that open with level: 'critical' is still refused.

core/src/host/meetings.js is a FlyBot-only file (upstream ds-bot/ has no copy), so generate stores it whole in patches/ds-bot/files/; both mirrors in this diff are the same bytes, and no .patch hunk changes.

Red first

cd core && node --import ./test/harness-resolve.mjs --test test/meetings-unknown-level.test.js against main @ 8126953894f5d9b649605e98f39f9bcadb63f563, before any source change:

not ok 1 - #2592: the meeting list keeps its records when a stored level does not parse
  error: 'invalid: level must be one of ordinary, major, severe'
  code: 'meeting/invalid'
  stack: |-
    refuse (core/src/host/meetings.js:33:49)
    decisionLevel (core/src/host/meetings.js:68:47)
    summary (core/src/host/meetings.js:502:26)
    Array.map (<anonymous>)
    Object.meetingEndpoint (core/src/host/meetings.js:547:39)

Reverting only the source change after the fix turns the same two read tests red again at :502; re-applying it is green.

Ran at the tip

TZ=UTC, at ded3208264753cbb8712187574e7c9fcea53991b:

  • cd core && node --import ./test/harness-resolve.mjs --test test/meetings-unknown-level.test.js — 3 tests, 3 pass.
  • cd core && node --import ./test/harness-resolve.mjs --test test/meetings.test.js — 15 tests, 15 pass (the write-path refusal cases included).
  • cd core && node --import ./test/harness-resolve.mjs --test --test-concurrency=6 "test/*.test.js" — 3010 tests, 3007 pass, 3 fail. All three (test/check-arch-docs.test.js × 2 — shells out to git, absent on this host — and test/usage-fields.test.js — sqlite fixture) fail identically on the untouched base tree at 8126953894f5d9b649605e98f39f9bcadb63f563, so none is this diff.
  • cd core && npm run lint — oxlint 0 errors, 84 warnings (the count on main); arch-docs cannot run here (no git), and the Docs-not-needed: trailer is in both the branch commit and this body.
  • npm --prefix plugins/{social,tree,timeline,workflow} test — 578, 137, 145 and 453 tests, all pass.
  • node scripts/lint-plugins.mjs; node core/scripts/build-client.mjs --check; node scripts/ds-bot-patches.mjs check (10 patches apply; core/ equals ds-bot/ + patches/ds-bot/; both mirrors byte-identical); node --test ".github/scripts/*.test.mjs" "policy/*.test.mjs" (662 pass, 0 fail); node scripts/lint-interop.mjs; interop tests (37 pass); node scripts/lint-live-e2e.mjs; live-e2e tests (34 pass); node .github/scripts/check-mojibake.mjs --check (0 occurrences).

Not run: arch-docs (needs git, absent here), ds-bot-patches canonical (its generate needs git), and the smoke test (no DSH checkout/bench on this host).

Docs-not-needed: a read-side guard on a stored field; the write path still refuses an unknown level

Scope

Only core/src/host/meetings.js, the new test, and their generated patches/ds-bot/files/ mirrors — no plugins/**, no CHANGELOG.md, no changelog fragment: a read-side guard on a value the current writer never produces.

Checkpoint

{
"pr": 2593,
"claim": "Dev",
"issue": 2592,
"summary": "A stored meeting level that no writer stores — a hand edit, or a record from an older build — made summary() throw meeting/invalid, and every meeting action answers through that projection, so one bad record lost the whole list. The read side now renders an unknown level as ordinary, the way advanceRefusal() and the board reader already read it, while the write path still refuses a bad level. The new test drives rt.meetingEndpoint('list') and read with a level: 'critical' record and keeps the write refusal.",
"files": [
"core/src/host/meetings.js",
"core/test/meetings-unknown-level.test.js",
"patches/ds-bot/files/src/host/meetings.js",
"patches/ds-bot/files/test/meetings-unknown-level.test.js"
],
"checks": {
"ran": "core: new test (3/3), meetings.test.js (15/15), full suite --test-concurrency=6 (3007 pass, 3 pre-existing env failures); core lint (oxlint 0 errors); plugins social/tree/timeline/workflow; lint-plugins; build-client --check; ds-bot-patches check; mojibake scan; .github/scripts + policy tests; interop and live-e2e lint/tests",
"result": "green",
"at": "ded3208264753cbb8712187574e7c9fcea53991b"
},
"next": "none — ready for review"
}

…arse (#2592)

`summary()` is the projection every meeting action answers through, and it
called the strict `decisionLevel()` on the stored record. A `level` no writer
stores — a hand edit, or a record from an older build — made `meeting
action=list` throw `meeting/invalid: level must be one of ordinary, major,
severe` and lose every record, not only the bad one. The read side now renders
an unknown level as `ordinary`, the way `advanceRefusal()` and the board's
`decisionCardOf` already read it; the write path still refuses.

Fixes #2592
Docs-not-needed: a read-side guard on a stored field; the write path still refuses an unknown level
@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 ded3208
Written-by: Reviewer
Attestation: ded3208 role=Reviewer

Ran at this tip:

$ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/meetings-unknown-level.test.js
# tests 3  # pass 3  # fail 0
$ cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/meetings.test.js
# tests 15  # pass 15  # fail 0
$ cp base/blob/core/src/host/meetings.js core/src/host/meetings.js && cd core && TZ=UTC node --import ./test/harness-resolve.mjs --test test/meetings-unknown-level.test.js
# tests 3  # pass 1  # fail 2  (both read tests red: meeting/invalid at meetings.js:502; restored afterwards)
$ node scripts/ds-bot-patches.mjs check
$ node core/scripts/build-client.mjs --check
$ node scripts/lint-plugins.mjs
$ TZ=UTC npm run verify
# stopped at arch-docs: FAIL — cannot diff against origin/main (no git on this host); oxlint 0 errors, 84 pre-existing warnings

Tier: T2 (risk-tier: T2; basis: core/src/host/meetings.js=T2, core/test/meetings-unknown-level.test.js=T1, patches/...=T1) — computed with the tip's risk-gate.mjs judge over the PR file list.
Scope: exactly 4 files; core/src/host/meetings.js changes one line in summary() (:501-505) so a stored level outside DECISION_LEVELS renders as ordinary instead of throwing; decisionLevel() stays strict on every write (open at :245, decide at :362). New test 3/3 green, meetings.test.js 15/15 green including the write-path refusals, and swapping only the base blob back at this tip turns the two read tests red at meetings.js:502 — red first proven.
Mirrors: patches/ds-bot/files/src/host/meetings.js and .../test/meetings-unknown-level.test.js are byte-identical to the core/ copies; ds-bot-patches check ok (10 patches, core/ = ds-bot/ + patches).
Body metadata: Fixes #2592, Owner: Dev, Reviewer: Reviewer (not the Owner), agent-pr label, checkpoint block 1319 chars, summary 512, files all in diff, checks.at = this tip. Docs-not-needed: trailer present in the body.

Not run: arch-docs and ds-bot-patches canonical (both need git, absent on this host — the same environmental stop verify hit), the full core suite to completion here, the plugin suites, and the smoke test (no DSH checkout/bench on this host); the implementer's report lists those and I re-ran the parts this review needs fresh at the tip.

@github-actions github-actions Bot added the pass label Nov 7, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit 8601016 Nov 7, 2026
17 of 19 checks passed
@Birfy
Birfy deleted the fix/2592-meeting-level-guard branch November 7, 2026 09:14
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 7, 2026
HugoXK pushed a commit to HugoXK/FlyBot that referenced this pull request Nov 9, 2026
…hinkFlowLab#2592) (ThinkFlowLab#2595)

Written-by: Chief
Fixes ThinkFlowLab#2592
Owner: Dev
Reviewer: Reviewer

`ThinkFlowLab#2593` fixed the read projection in `core/src/host/meetings.js` but
landed without the changelog fragment a user-visible fix needs, so the
meeting-level guard would have been missing from the release notes. This
adds only `changelog.d/2592.fixed.md`, written to the same shape as the
other fragments.

## Checkpoint

<!-- flybot:checkpoint -->
```json
{
  "pr": 2595, "claim": "Dev", "issue": 2592,
  "summary": "Adds the changelog fragment the meeting-level guard shipped without. One user-visible sentence: the meeting list keeps a record whose stored level is unknown, and a write still refuses one. No source file changes.",
  "files": ["changelog.d/2592.fixed.md"],
  "checks": { "ran": "node .github/scripts/changelog.mjs check", "result": "green", "at": "c2f87600e8ff615ea7b7edb62491d64c7eb1ae44" },
  "next": "none — ready for review"
}
```
<!-- /flybot:checkpoint -->

Docs-not-needed: a changelog fragment only; `docs/` needs no change.
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): one hand-edited meeting level throws the whole meeting list

1 participant