Skip to content

Write verify.json atomically, refuse corrupt files and fix database storage - #5

Merged
valzargaming merged 1 commit into
mainfrom
claude/storage-integrity
Nov 6, 2026
Merged

valzargaming merged 1 commit into
mainfrom
claude/storage-integrity

Conversation

@valzargaming

Copy link
Copy Markdown
Member

Requested by Val · project thread

Problem

  • writeJson did a plain file_put_contents. A crash or full disk mid-write leaves a truncated verify.json.
  • loadJsonFile turned any undecodable file into [] (reproduced with a truncated file). The next registration then persisted that empty list, silently wiping every verified account. The bot would keep its cached copy, but the server would start over from zero.
  • json_encode failure (invalid UTF-8 in a key) wrote an empty file.
  • JSON_PATH was always prefixed with getcwd(), so an absolute path such as D:/Civilizationbot/json/verified.json was unusable.
  • Database storage mode broke on the second insert (reproduced with SQLite: HY000 column index out of range): rows read back carry an id column that was fed into the named-parameter insert. It also still wrote the JSON file on every change.
  • SS14PersistentState::getVerifyList() returned an expression by reference (notice).

Change

  • writeJson encodes first (with JSON_INVALID_UTF8_SUBSTITUTE | JSON_THROW_ON_ERROR), writes a temporary file beside the target and renames it into place.
  • loadJsonFile throws on invalid JSON; empty or missing files still load as [].
  • Absolute Unix, drive-letter and UNC JSON_PATH values are used as-is.
  • New PersistentState::saveVerifyList() is the single write path for both endpoints: filesystem writes JSON, database rewrites the table in one transaction using only the record columns. The connection comes from an overridable connect(), so database mode is now covered by an in-memory SQLite test. writeJson stays static, so the existing offline test subclasses keep working.

Compatibility with Civilizationbot

The bot only calls getState()->getVerifyList(), handleEndpoint, and the SS14 endpoint's add/remove/fetch/getIndex; none of those signatures change. Its integration call pattern was replayed against this branch and passed.

Verification

php scripts/lint.php                                   # Parsed 29 PHP files with PHP 8.3.6
./vendor/bin/phpunit --bootstrap vendor/autoload.php tests/isolated   # OK (13 tests, 63 assertions)

New tests/isolated/StorageIntegrityTest.php.


Generated by Claude Code

…torage

writeJson now encodes first, writes to a temporary file in the same
directory and renames it into place, so a crash or full disk mid-write
cannot leave a truncated list. Invalid UTF-8 in a record is substituted
instead of making json_encode fail and writing an empty file.

loadJsonFile used to turn any unreadable JSON into an empty list, which
the next registration then persisted, silently wiping every verified
account. It now throws; the request fails with a clear error instead.

JSON_PATH may be absolute (Unix, drive-letter or UNC); it was always
prefixed with the working directory.

Database storage was unusable past the first record: rows read back
carry an uid=0(root) gid=0(root) groups=0(root) column, and feeding them to the named-parameter insert
failed with HY000/HY093. The table is now rewritten in a transaction
with only the record columns, database mode no longer also writes the
JSON file, and the connection is opened through an overridable connect()
so the behaviour is covered by an in-memory SQLite test. Also fixes a
by-reference return notice in the SS14 state's database read.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WTP2S4qKEFT8a6a6yFEL4
Copilot AI balanced review requested due to automatic review settings November 6, 2026 11:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@valzargaming valzargaming self-assigned this Nov 6, 2026

Copy link
Copy Markdown
Member Author

2026-11-06: head 09d03ab. Local: php scripts/lint.php (29 files) and ./vendor/bin/phpunit --bootstrap vendor/autoload.php tests/isolated OK (13 tests, 63 assertions). CI: https://github.com/Valgorithms/Verifier-server/actions/runs/42212434450 passed on PHP 8.2 to 8.5. Note: the commit message contains a shell artefact (uid=0(root)...) where the word id should be; the PR description is correct. A squash merge with the PR title avoids carrying it. Not verified: Civilizationbot's integration test against a bumped pin, live run.


Generated by Claude Code

@valzargaming
valzargaming merged commit 3fce0a8 into main Nov 6, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants