Skip to content

fix: UltiEssentials follow-up (#66, #67, #68, #69) - #70

Merged
wisdommen merged 26 commits into
masterfrom
fix/p17-fu-followup
Oct 5, 2026
Merged

wisdommen merged 26 commits into
masterfrom
fix/p17-fu-followup

Conversation

@wisdommen

@wisdommen wisdommen commented Oct 4, 2026 •

Copy link
Copy Markdown
Member

Issue closure

Closes #66
Closes #67
Closes #68
Closes #69
Closes #72
Closes #73

The language-guard widening is tracked by UltiKits/UltiRemoteBag#44 (decision recorded there); this pull request applies it here and closes nothing for it.

Decisions applied

Checklist rows amended

Write-gate follow-up (plan 17-72, gate-1 top-up): FEATURES.md ultiessentials.config.comments (amended: a hand-written comment stays; #73), ultiessentials.spawn.setspawn-writes-location-only and ultiessentials.spawn.setlobby-writes-location-only (rollback wording); UAT-CHECKLIST.md ultiessentials.spawn.setspawn-writes-location-only.neg-refused-write (rollback wording).

Write-gate follow-up (plan 17-72): FEATURES.md ultiessentials.spawn.set, ultiessentials.lobby.set (amended), ultiessentials.spawn.setspawn-writes-location-only, ultiessentials.spawn.setlobby-writes-location-only (new); UAT-CHECKLIST.md ultiessentials.spawn.setspawn-writes-location-only, ultiessentials.spawn.setspawn-writes-location-only.neg-refused-write, ultiessentials.spawn.setlobby-writes-location-only (new), ultiessentials.spawn.set, ultiessentials.lobby.set (stale line citations), ultiessentials.i18n.language and the language convention (precondition note corrected).

FEATURES.md: ultiessentials.config.comments (new), ultiessentials.lifecycle.reload-partial (new), ultiessentials.lifecycle.removed-key-warning (amended), ultiessentials.storedkey.repair (amended).

UAT-CHECKLIST.md (all new, run by the real-server session): ultiessentials.config.comments-fresh, ultiessentials.config.comments-upgrade, ultiessentials.lifecycle.reload-partial, ultiessentials.lifecycle.removed-key-warning.reload-restored, ultiessentials.storedkey.repair.json-store.

Red-when-reverted evidence

Behaviour changes

- `/ul reload UltiEssentials` no longer replies that the module reloaded when one of its three background
  services (scheduled commands, scoreboard, name prefixes) did not restart: the reply now says the reload was
  partial and names the service and why, and the framework logs its partial-reload warning instead of its
  success line. The other services still restart (UltiKits/UltiEssentials#66).
- `/ul reload UltiEssentials` 在三个后台服务(定时命令、计分板、头顶称号)中有服务未能重启时,不再回复模块已重载:回复会说明
  这是一次部分重载,并指出是哪个服务、原因是什么;框架记录的也是部分重载警告而不是成功日志。其他服务仍会重启
  (UltiKits/UltiEssentials#66)。
- The comments above the 78 keys of this module's five configuration files (`config/essentials.yml`,
  `config/lobby.yml`, `config/motd.yml`, `config/spawn.yml`, `config/tabbar.yml`) now come from the module's
  language files: a server set to `language: en` writes English comments on a fresh install (they were
  Chinese in every language). An existing file's comments on these keys switch to the server's language at
  the next start; values are untouched, and a comment you wrote by hand above one of these keys is replaced
  (UltiKits/UltiEssentials#67).
- 本模块五个配置文件(`config/essentials.yml`、`config/lobby.yml`、`config/motd.yml`、`config/spawn.yml`、`config/tabbar.yml`)
  中 78 个配置项上方的注释现在取自模块的语言文件:`language: en` 的服务器全新安装时写入英文注释(此前所有语言下都是中文)。
  已有文件中这些配置项的注释会在下次启动时切换为服务器语言;配置值不变,你手写在这些配置项上方的注释会被替换
  (UltiKits/UltiEssentials#67)。
- On UltiTools-API 6.3.0 with JSON storage (`datasource.type: json`), the start-up repair of records written
  before UltiKits/UltiEssentials#34 gives them their primary key again. 6.3.0 hands out copies of stored
  records, so the repair wrote nothing there, logged an error at every start, and `/delhome`, `/delwarp`,
  `/unban` and an owner's lock removal kept failing on those records. If writing a record's key fails, every
  record of that type is left exactly as it was and the next start tries again (UltiKits/UltiEssentials#69).
- 在 UltiTools-API 6.3.0 上使用 JSON 存储(`datasource.type: json`)时,启动修复重新能为 #34 修复之前写入的记录补上主键。
  6.3.0 读取记录时返回副本,修复因此什么都没写入、每次启动都记录一条错误,`/delhome`、`/delwarp`、`/unban` 以及主人拆除上锁
  容器对这些记录仍然无效。若某条记录的主键写入失败,该类型的所有记录保持原样,下次启动重试(UltiKits/UltiEssentials#69)。
- The module loads on UltiTools-API 6.3.0. 6.3.0 removes the configuration object the removed-settings
  warning read, so this module was refused at load; the warning now asks the framework whether a removed key
  is in `config/essentials.yml`, and warns exactly as before at start-up and on every `/ul reload` for a file
  holding one (a key left with an empty value included) and not at all otherwise. A file the framework could
  not read or parse produces the framework's own `Cannot load` line and no removed-setting warning
  (UltiKits/UltiEssentials#68).
- 本模块可在 UltiTools-API 6.3.0 上加载。6.3.0 删除了「已删除配置项」警告所读取的配置对象,本模块因此无法加载;现在改为
  向框架询问 `config/essentials.yml` 中是否仍有某个已删除的键,启动时和每次 `/ul reload` 时的警告与之前完全一致(值为空的
  键同样会报告),文件中没有时不报。框架无法读取或解析的文件只会出现框架自己的 `Cannot load` 日志,不再报已删除配置项
  (UltiKits/UltiEssentials#68)。

Threat model

This pull request defends against: server crashes and restarts (the #69 repair runs in one transaction and rolls back whole; a key that did not reach the store fails the transaction), the operator's normal commands (/ul reload UltiEssentials, editing the five config files, switching language:), and an upgrade from the current master build with existing config and JSON data. It does not defend against hand-edited framework-internal files, a Java security manager, or a third party changing the data folder while the server runs.

Known limitation

This module's plugin.yml api-version and any README compatibility line still advertise the pre-6.3.0 API level, and the framework's 6.3.0 lifecycle hooks (onReload(ReloadReport), onUnregister()) run only under a 6.3.0 framework. The api-version: 630 pin lands together with the UltiTools 6.3.0 release package, by an earlier maintainer decision, before any module release. No module version is bumped and nothing is released from this pull request. Tracking instance: UltiKits/UltiRecipe#19.

Gates

This repository is not onboarded to Codacy — gate 2 for module repositories is the Codex review only, per the phase's carried-forward decision.

Gate Status
1 — own deep review self-review: 0 blockers, 0 warnings, 4 notes; click-handler check 0 hits; row-count sweep 2 sites, both verified by re-query (no change); config-layer check clean
2 — Codex review (local) 1 run (gpt-6.1-sol, effort high) on head dd29137: 0 findings — "未发现本次变更引入的可确认缺陷。" (no confirmable defect introduced by this change). No fix commit followed, so no confirmation run. Summary comment posted on this pull request.
3 — real-server acceptance runs in the combined real-server session for these pull requests, before merge
4 — maven-ci success on dd29137 — https://github.com/UltiKits/UltiEssentials/actions/runs/37179872226 (pull_request) and https://github.com/UltiKits/UltiEssentials/actions/runs/37152783021 (push)

🤖 Generated with Claude Code

Write-gate follow-up (plan 17-72)

Built against the merged UltiTools-API 6.3.0-SNAPSHOT (framework alpha de8d9c0c, the write gate of UltiTools-Reborn#611).

  • Sweep-owned test fixes (0713318). The 17-70 cross-module sweep found 20 module tests failing against the merged framework, no production defect: the comment-upgrade test's mocked module could not read its own shipped catalogues (the framework now replaces only comments it identifies as its own), and the reload tests' mocked UltiTools had no logger for the framework's not-written warning. Both mocks now behave like the running server; 1337 tests, 0 failures.
  • /setspawn and /setlobby save the whole entity: a hand-edited location is not written but reported as set, and a refused write is reported as success #72. Maintainer decision of 2026-10-04: an operator's command writes exactly the item it names. Both commands call saveOperatorChange with their six location paths instead of the whole-entity save(), which on the merged framework left a hand-edited location setting unwritten while chat said the location was set, and answered a refused write as a success. A refused write (ConfigWriteRefusedException) is answered Spawn point not saved: <reason>. ... and any failed write puts the six settings in memory back to what the file holds (maintainer decision of 2026-10-05). Commits 9c596cf (test, red by behaviour), fd76c3a (fix).
  • Write-gate sweep. 46b111b adds a real-file guard that a language switch rewrites only the shipped-text settings of essentials.yml (a typo value and a hand-written comment stay byte for byte; a mutation control goes red); 13e1929 replaces the advice to edit official language files with copy-rename-select.
  • Proofs: UltiEssentials-72-RED.log / UltiEssentials-72-GREEN.log; UltiEssentials-S5-CONTROL-overwrite-RED.log.

Gate-1 top-up follow-ups (plan 17-72)

wisdommen and others added 15 commits October 4, 2026 06:47
…rk (#68)

UltiTools-API 6.3.0 removes the configuration accessor RemovedConfigKeys read, so on the merged
framework this module does not compile (and a jar built against 6.2.x is refused at load with
NoSuchMethodError). New cases pin what the migration must keep: a removed key holding an explicit
null is reported, an unparseable or unreadable file reports no removed key, and a removed key put
back between two reloads is reported on the second and not once removed again. The existing
start-up, reload and clean-file controls stay.

RED: main does not compile against the merged framework (RemovedConfigKeys.java:83, getConfig()).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rver thread

From UltiTools-API 6.3.0 ConfigManager refuses to register, load or reload a configuration off the
server thread (ConfigManager#permitsConfigThread, UltiKits/UltiTools-Reborn#538). The shared mock
server answered isPrimaryThread() with Mockito's default false, so on the merged framework every
config load in 70-odd tests was refused and they failed for a reason no real server has.
WildCooldownBindingTest already stubbed it locally; the stub now lives in the shared helper.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…he module loads on 6.3.0 (#68)

UltiTools-API 6.3.0 removes the configuration entity's parsed-file accessor that
RemovedConfigKeys#warningsFor read (RemovedConfigKeys.java:83), so the module did not compile on
the merged framework and a jar built against 6.2.x is refused at load with NoSuchMethodError.
The warning now asks AbstractConfigEntity#isPresentInFile for each removed key: the same ordered,
localized lines at start-up and on every /ul reload for a file holding a removed key (an explicit
null included), none for a clean file. A file the framework could not read or parse reports no key
(the framework logs its own SEVERE line); a missing configuration bean still yields the
"not checked" line. Javadoc no longer describes the removed accessor.

CHANGELOG, FEATURES row ultiessentials.lifecycle.removed-key-warning and new checklist row
ultiessentials.lifecycle.removed-key-warning.reload-restored in the same commit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… on 6.3.0 (#69)

On the merged framework a JSON read is a detached copy and insert caches a copy with putIfAbsent
(UltiKits/UltiTools-Reborn#522), so the repair's in-place key write no longer reaches the store and
11 existing EntityIdBackfillServiceTest cases go red (repaired 0). The cache-backed case is
restated: the store is asked to delete the legacy entry by its identity (one cache lookup) and never
by condition (the full-cache pass the repair avoids for speed); SilentlyFailingStore counts
condition deletes separately. A new case pins that a legacy entry stored under a name other than
its identity is not counted repaired while an un-keyed copy remains.

RED: 12 failures in EntityIdBackfillServiceTest on the merged framework.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lete destroys nothing (#69)

The repair now has to remove the legacy entry before inserting the keyed record on a JSON store, so
an insert that silently reaches nothing would lose the record. The detached-read fixture (which
modelled the open question of UltiKits/UltiTools-Reborn#522 before 6.3.0, now the real JSON
operator's behaviour) is restated for that hazard and delegates its transaction to the real
operator, whose rollback restores the entries: four legacy records stay, unkeyed, reported as
untouched.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…again (#69)

UltiTools-API 6.3.0 makes JSON reads detached copies and makes insert cache a copy with
putIfAbsent (UltiKits/UltiTools-Reborn#522). The repair skipped the delete on a cache-backed store
and relied on insert writing the key onto the cached legacy record in place, so on the merged
framework it repaired nothing on JSON and logged its partial-write error at every start.

On a cache-backed store the legacy entry is now removed by its identity (delById, one cache
lookup, not the full-cache condition delete the repair avoids for speed) and the keyed record
inserted, inside the same single transaction; each key is confirmed there, and a key that did not
reach the store fails the transaction, whose rollback restores every entry. A record is counted
repaired only when no unkeyed copy of its identity remains. The relational path is unchanged.

CHANGELOG, FEATURES row ultiessentials.storedkey.repair and new checklist row
ultiessentials.storedkey.repair.json-store in the same commit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…mework's CJK gate

Guard 2 gains one assertion per range the framework's check-cjk-scope.sh detects beyond
U+4E00 to U+9FFF (Han Extension A, a supplementary-plane ideograph, a compatibility ideograph,
CJK Symbols and Punctuation, Halfwidth and Fullwidth Forms), a kana and range-edge control that
must stay undetected, and a literal holding only a widened-range character. RED on the old scanner
(6 failures).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ct (UltiRemoteBag#44)

I18nSourceScanner#containsCjk iterates code points and detects the Han script (extensions,
supplementary planes, compatibility ideographs, radicals), U+3000 to U+303F and U+FF00 to U+FFEF;
kana stays out. Character for character the contract of the framework's check-cjk-scope.sh.
Nothing in this module's sources or catalogues is newly caught (both guards green, 88 tests).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… key sites (#67)

A comment written as one {key} token resolves through the module catalogue, so guard 1 must treat
it like an i18n call: the key has to exist in every language and every catalogue key has to be
reachable. A literal comment, a token with more text, two tokens and an empty token are not sites.
RED on the old scanner, which has no such site (3 failures).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…key sites (#67)

I18nSourceScanner adds a CONFIG_COMMENT key site for a comment that is one string literal holding a
single trimmed {key} token, read exactly as the framework reads it, and marks that literal as a key.
Both language guards now check such a key in every catalogue and keep its catalogue entries reachable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…guard 2 no longer skips them (#67)

Guard 2's skip for the comment element of @ConfigEntry ends with the adoption of translatable config
comments, so a new Chinese-only comment fails the build; the guard file's header names this one
deliberate difference from the modules that keep the skip. EssentialsConfigCommentsTest runs the
framework's real load of all five files (essentials, lobby, motd, spawn, tabbar): every one of the
78 comments is one {key} token, the zh catalogue holds the text master 0875d16 wrote verbatim and the
en catalogue its translation (fixture src/test/resources/config/config-comments.tsv), a fresh
install under en writes English comments and under zh the Chinese ones, and an upgrade from a file
with the Chinese comments switches only the comment lines, keeps an edited value, and is byte
identical on the second start.

RED: guard 2 reports the 78 Chinese comments; four of the five new cases fail (the zh fresh install
already holds, as it must).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… files (#67)

The 78 Chinese @ConfigEntry comments in EssentialsConfig (59), SpawnConfig (8), LobbyConfig (6),
MotdConfig (3) and TabBarConfig (2) become {essentials.config.<file stem>.<key path>} tokens;
lang/zh.json carries the Chinese text master wrote verbatim and lang/en.json its English
translation. The framework (UltiTools-Reborn#542) writes them in the server's language on a fresh
install and rewrites existing token comments on every write of the file; values are untouched.
0 Chinese comment attributes remain (78 of 78 converted; control: git grep counts 78 comment
attributes).

CHANGELOG, FEATURES row ultiessentials.config.comments and checklist rows
ultiessentials.config.comments-fresh and ultiessentials.config.comments-upgrade in the same commit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…al, naming it (#66)

The reload tests now call the hook the framework calls, onReload(ReloadReport), with a fresh
report (UltiEssentialsServiceReloadTest, UltiEssentialsTest, UltiEssentialsRemovedConfigKeyTest,
EssentialsConfigTextTest); on master the framework's default forwards it to onReload(), so every
existing case still holds there. New assertions: a service whose reload throws, and one the
container cannot resolve, each reach the report as one partial reason naming the service; a clean
reload reports nothing partial; through the framework's own reloadWithReport() a failing service
makes the report partial and the framework logs its partial line, never the plain "reloaded." line;
UltiEssentials declares onReload(ReloadReport).

RED: 4 failures in UltiEssentialsServiceReloadTest (the report stays empty) and
UltiEssentialsLifecycleTest#declaresTheReportingReloadHook (no such method).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…eloadReport) (#66)

WildCooldownBindingTest verified onReload() directly, which compiled only while UltiEssentials
declared that overload in its own package. The framework calls onReload(ReloadReport); the
verification now reaches that protected hook reflectively, so it holds before and after the module
moves to it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ad (#66)

UltiEssentials now overrides onReload(ReloadReport). reloadService still restarts each of
ScheduledCommandService, ScoreboardService and NamePrefixService on its own and logs a failure as
before, and also records it in the framework's report naming the service: "<Service> could not be
reached and was not restarted; ..." or "<Service> did not restart: <cause>" (new catalogue keys
essentials.reload.partial_unreachable / partial_failed in en and zh). /ul reload UltiEssentials
then replies that the reload was partial instead of an unconditional success, and the framework
logs its partial line (UltiKits/UltiTools-Reborn#529). The start-up restart in registerSelf passes
no report and is unchanged. Javadoc that named onReload() or said the reply is unconditional is
updated.

CHANGELOG, FEATURES Lifecycle prose and row ultiessentials.lifecycle.reload-partial, and checklist
row ultiessentials.lifecycle.reload-partial in the same commit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@wisdommen

Copy link
Copy Markdown
Member Author

Local Codex review, run 1 of at most 2 (gate 2).

  • Model: gpt-6.1-sol, reasoning effort high (codex review --base master, Codex CLI 0.159.2, read-only sandbox)
  • Reviewed head: dd29137 against master 0875d16
  • Duration: 2026-10-04 05:26:20Z to 05:29:52Z
  • Verdict (quoted, translated): "未发现本次变更引入的可确认缺陷。" — no confirmable defect introduced by this change; diff check, bilingual catalogue consistency and the framework API contract checked.
  • Findings: 0 (P1 0, P2 0, P3 0). No fix commit follows, so no confirmation run is needed.

This repository is not onboarded to Codacy; gate 2 for module repositories is the Codex review only.

@wisdommen

Copy link
Copy Markdown
Member Author

Top-up review P3 / out-of-threat-model findings are collected in the follow-up issue: #71

…de and reload tests

Owned by the 17-70 cross-module sweep (17-FU2-MODULE-SWEEP.md, UltiEssentials):
- class B (EssentialsConfigCommentsTest:194): since the maintainer decision of
  2026-10-04 only framework-written comments are rewritten, and the framework
  recognises its own comment by comparing it with the module's shipped
  catalogues (UltiToolsPlugin#shippedCatalogueTexts). The test's mocked module
  answered that seam with an empty list, so the old Chinese comment counted as
  the operator's. The mock is now the module's own class and that one method
  runs for real, reading this module's catalogues as a real server does.
- class D (UltiEssentialsServiceReloadTest, 17 tests; UltiEssentialsRemoved-
  ConfigKeyTest, 2 tests): a module save that leaves a change unwritten now
  logs a WARNING through UltiTools#getLogger(), which the test's mocked
  UltiTools answered with null. The mock now returns a real logger, as the
  running plugin always has.
No production code changes; 1337 tests, 0 failures against the merged framework.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
wisdommen and others added 9 commits October 5, 2026 19:00
…on settings (#72)

Maintainer decision 2026-10-04 (what code may write, by file type): an
operator's command writes exactly the item it names - /setspawn only the
location. Maintainer decision 2026-10-05: a write the gate refuses is
answered 'not saved' and why, and the in-memory change is rolled back.
New real-file tests through the framework's write gate (RED by behaviour on
this head, whose whole-entity save() leaves a hand-edited location setting
unwritten and answers a refused write with 'Spawn point set'):
- a location setting edited by hand since the load is replaced too, and
  only the six location lines change (spawn and lobby);
- an anchored file is not written, the reply says not saved and why, and
  the running location is the one the file holds (spawn and lobby);
- guard: a hand edit of spawn.teleport-on-respawn stays.
The mock-based command tests now expect saveOperatorChange with the six
paths instead of save().

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…tings, and roll back a refused write (#72)

Both commands now call saveOperatorChange with their six location paths
(SpawnConfig/LobbyConfig#locationPaths) instead of the whole-entity save():
the operator's command is consent for those six settings, so a location
setting edited by hand is replaced, and the framework's write gate keeps
every other line byte for byte (maintainer decision 2026-10-04).
A ConfigWriteRefusedException is answered with the new 'Spawn point not
saved: <reason>. ...' / 'Lobby not saved: ...' line, and any IOException
puts the six settings in memory back to what the file holds (maintainer
decision 2026-10-05); other IOExceptions keep the existing save-failed line.
CHANGELOG, FEATURES (two persistence rows; spawn.set and lobby.set point at
them) and UAT rows ultiessentials.spawn.setspawn-writes-location-only (+ a
refused-write negative row) and ultiessentials.spawn.setlobby-writes-
location-only in the same commit; the two existing rows' stale line
citations now name the method.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…editing an official language file

Maintainer decision 2026-10-04 (official language files are framework-owned):
UltiTools-API 6.3.0 restores an edited official language file at every start.
The changelog no longer tells operators to re-apply edits to lang/en.json or
lang/zh.json, the README names the copy-rename-select customisation, and the
checklist's note that an upgrade never refreshes an extracted language file
(convention and ultiessentials.i18n.language) is replaced by what the
framework now does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ngs of essentials.yml (write-gate sweep S5)

Maintainer decision 2026-10-04 (what code may write, by file type): code may
re-render a value that still equals shipped text after a language switch and
nothing else. Real-file guard over the module's materializer save: after an
en -> zh switch and reload, a typo value (default-max-homes: 3O) and an
operator's comment written above a framework comment stay byte for byte, and
every value line outside the four shipped-text settings is unchanged. Green on
the merged framework; a mutation control shows the instrument sees an
overwrite.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…st.java

Gate-1 top-up IN-03 of plan 17-72: the file was CRLF at dd29137 and 9c596cf
rewrote it with LF. Whitespace only: `git diff -w --ignore-cr-at-eol` against
the previous commit is empty; `file` reports CRLF line terminators again.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ues held before the command and logs why (#72 review)

Gate-1 top-up IN-01/IN-02 of plan 17-72: the rollback after a write failure
that is not a refusal had no test; it now asserts the six setters are called
with the values held before the command. Both failure branches must log one
WARNING with the reason, so the reply's "fix the file the server log names"
holds on every path (RED on this head: nothing is logged). CRLF kept.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e rollback restores the values held before the command (#72)

Gate-1 top-up IN-01/IN-02 of plan 17-72:
- both catch blocks (refusal and any other write failure) log one WARNING
  with the write's own message - file and reason, never a value - through
  the new essentials.log.location_not_saved, so the reply's "fix the file the
  server log names" holds on every path;
- the javadoc, the code comment, CHANGELOG (EN/ZH), FEATURES and the refused-
  write UAT row now say the rollback restores the values held before the
  command, not "what the file holds" (the two differ after a hand edit).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… release v1.0.0 follows the language (#73)

Gate-1 top-up F-02 (found by the UltiRemoteBag review, routed to plan
17-72): release v1.0.0 wrote 随机传送冷却时间(秒) above features.wild.cooldown;
it equals no shipped catalogue text and is not registered, so under en it
stays Chinese. The upgrade test (en and zh) is RED on that text; a
one-character variant is the operator's and must stay (control). The
language-guard exemption for the literal the fix registers is added here
(stale, and so RED, until the fix lands).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…t as one the module shipped (#73)

@ConfigEntry(previousComments) on features.wild.cooldown holds the text
release v1.0.0 wrote there, so the framework counts an upgraded file's copy
as its own and rewrites it in the server's language; any other text above
the key stays the operator's (maintainer decision 2026-10-04, register old
shipped comment texts as framework-owned).
The #67 entry in CHANGELOG (EN/ZH) and the ultiessentials.config.comments
FEATURES row no longer say a hand-written comment is replaced: under the
2026-10-04 decision it stays byte for byte, and only the module's own
comment (catalogue text, or a registered earlier text) is switched.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ck in full (class-L sweep)

Gate-1 top-up class-L sweep of plan 17-72: the copy-rename-select advice in
the changelog and the README now carries the framework's whole rule (alpha
de8d9c0c, Localized#officialLanguageOf and isSafeLanguageCode): the new name
starts with the language code and a hyphen and holds only ASCII letters,
digits, `_` and `-`; one `language` setting serves the framework and every
module; messages the copy lacks come from the official file its name starts
with, and a module without that file uses its official one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@wisdommen

Copy link
Copy Markdown
Member Author

Local Codex review, run 2 of 2 (plan 17-72 write-gate follow-up)

  • Command: codex review --base master -c model='"gpt-6.1-sol"' -c model_reasoning_effort='"high"' (Codex CLI 0.159.2, read-only sandbox), run in the worktree at head c0088f8 (base 0875d16), 2026-10-05 08:58–09:02 UTC.
  • Verdict, quoted: 「未发现由本补丁引入、需要修复的明确缺陷;相关调用路径与本地依赖 API 契约一致。本次为只读审查,未重新运行测试、构建或真实服务器 UAT。」 (No clear defect introduced by this patch that needs fixing; the call paths match the local dependency's API contracts. Read-only review; tests, build and real-server UAT were not re-run.)
  • Findings: P1 0, P2 0, P3 0. No fix commit followed, so c0088f8 is the reviewed head. The Codex budget for this pull request is now spent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment