Skip to content

CourseForge 4.10.0

Choose a tag to compare

@ZcarecroW ZcarecroW released this 01 Sep 21:26
· 11 commits to main since this release
v4.10.0
2d4a2af

Eleven readers went over the whole application with one instruction — find what is actually wrong, not what could be tidier — and this release is what they found. Twenty-odd fixes and no features. The ones worth knowing about, in the order of the damage they did:

Stopping a batch threw away the pages it had already written

"Stop this run" on a batch asked the provider to cancel and then closed the run on the spot. But a cancel is asynchronous everywhere, and a batch keeps whatever it answered before the request landed — pages that were paid for. Nothing could download them while the batch was winding down, and nothing ever tried afterwards, because a closed run is never polled. On OpenRouter, which has no cancel route at all, the run was closed while the batch ran on, billing, with nobody left to collect it.

The run now stays open until the provider has said its last word. The next check collects the pages that were finished, releases only the ones that never ran, and closes the run then. A provider that cannot be stopped says so, and its run stays open for the same reason.

A book created twice, once per retry

A course's first publish created its book, attached it to the shelf, and then wrote the id down. A deleted shelf or a token not allowed to edit shelves failed the middle step, the id went with the exception, and the next push created another book — one orphan per attempt. The id is now written down the moment the book exists.

A setting saved, and lost to a page load

Reading data/config.json rewrote it, on every request, whenever the file held a prompts key at its top level — any installation that has overridden a single prompt — and took no lock doing it. A read just before an administrator's save wrote its stale copy back just after. Reading no longer writes anything; the one-time reduction of a 3.x document happens under the same lock as every other write.

Deleting a profile stranded the batch running under it

The Profiles screen deleted without looking; the next poll of a run under that profile found no credentials and wrote the run off, while the batch ran on at the provider. A profile with an open run under it is now refused, on both surfaces.

The course editor was the one screen that never asked

Every other screen asks "Leave without saving?". The course editor did not — one click on the sidebar or the back arrow and an unsaved page, outline or destinations list was gone. All five tabs now ask. Rewrite everything asks before it starts from the two primary buttons as it always did from the third, and an outline edit is no longer discarded when a title is renamed on the Content tab.

Smaller, and still real

  • create_course over MCP let an administrator bind a course to somebody else's profile; it is refused, as the browser refuses it.
  • update_profile ignored preset_key, the per-kind extras and bookstack_name when adding a profile's first account or instance — every profile made in the browser.
  • gpt-5-chat-latest was taken for a reasoning model and had its temperature stripped.
  • The Claude subscription account passed the model id to the command line unchecked; anything that is not a model id is refused first.
  • Chinese and Japanese word counts counted a sentence as one word. Each character counts.
  • A four-backtick fence quoting a three-backtick example was closed at the example.
  • A placeholder inside a value — a page quoting its own {{tags}} — had the real tag list written into it before the model saw it.
  • A course update refused for its shelf had already renamed the course.
  • Publishing one page or chapter from the Content tab reported success when one of several wikis had failed.
  • Creating a second account with a generated password, or a second connection, replaced the card holding the first password or token. Both wait now until the card is acknowledged.
  • Mermaid, MathJax and the editor's language modes stayed failed for the session after one non-network failure.
  • A batch the provider accepted but the database could not record is withdrawn from the provider rather than left running unknown.
  • tools/deploy.php --full --prune pruned nothing; the development router could be walked past with /x/../; two workers migrating the same column no longer answer one with a 500.

Upgrading

Nothing to do, and no schema change.


379 tests pass, 14 of them new in tests/review-fixes.test.php, each checked to fail against the previous code. Verified in a browser against a scratch database with a fake model and a fake BookStack behind it: outline, pages and a published book on the old code, then the new dialogs and guards on the fixed one.