From bb237ecfa83e5c401359ab66efada1d67f067be3 Mon Sep 17 00:00:00 2001 From: agessaman Date: Fri, 10 Jul 2026 07:58:39 -0700 Subject: [PATCH] feat(startup): validate config on every start; polish ini_writer appends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - meshcore_bot.py: run config validation on normal startup and print errors/warnings (capped at 15) — misspelled sections/keys previously failed silently, the top source of configuration confusion - ini_writer: append new keys directly under a section's last entry instead of after its trailing blank line - add .claude/launch.json for browser-preview of the standalone viewer Verified end-to-end in the browser: /plugins renders 43 command cards, opt-in commands show off, dynamic-row add/save writes config.ini with a timestamped backup and preserved comments. --- .claude/launch.json | 18 ++++++++++++++++++ meshcore_bot.py | 25 +++++++++++++++++++++++++ modules/ini_writer.py | 7 ++++++- 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 .claude/launch.json diff --git a/.claude/launch.json b/.claude/launch.json new file mode 100644 index 0000000..1c1194c --- /dev/null +++ b/.claude/launch.json @@ -0,0 +1,18 @@ +{ + "version": "0.0.1", + "configurations": [ + { + "name": "web-viewer", + "runtimeExecutable": ".venv/bin/python", + "runtimeArgs": [ + "-m", + "modules.web_viewer.app", + "--config", + "/private/tmp/claude-501/-Users-adam-meshcore-bot/31648a90-50b9-4cae-b1f6-0e7815c5a855/scratchpad/viewer/config.ini", + "--port", + "8757" + ], + "port": 8757 + } + ] +} diff --git a/meshcore_bot.py b/meshcore_bot.py index 469721d..b122f66 100644 --- a/meshcore_bot.py +++ b/meshcore_bot.py @@ -113,6 +113,31 @@ def main(): print(f"Info: {message}", file=sys.stderr) sys.exit(1 if has_error else 0) + # Always sanity-check the config on normal startup too — misspelled + # sections/keys otherwise fail silently and are the most common source of + # "why doesn't my setting work" confusion. Non-fatal: warn and continue. + try: + from modules.config_validation import ( + SEVERITY_ERROR as _SEV_ERR, + SEVERITY_WARNING as _SEV_WARN, + validate_config as _startup_validate, + ) + _issues = [ + (sev, msg) for sev, msg in _startup_validate(args.config) + if sev in (_SEV_ERR, _SEV_WARN) + ] + _MAX_SHOWN = 15 + for sev, msg in _issues[:_MAX_SHOWN]: + print(f"Config {sev}: {msg}", file=sys.stderr) + if len(_issues) > _MAX_SHOWN: + print( + f"Config: ... and {len(_issues) - _MAX_SHOWN} more issue(s); " + "run with --validate-config for the full report", + file=sys.stderr, + ) + except Exception as exc: # noqa: BLE001 - never block startup on the linter itself + print(f"Config validation skipped: {exc}", file=sys.stderr) + from modules.core import MeshCoreBot bot = MeshCoreBot(config_file=args.config) diff --git a/modules/ini_writer.py b/modules/ini_writer.py index a74a745..d9eb775 100644 --- a/modules/ini_writer.py +++ b/modules/ini_writer.py @@ -148,7 +148,12 @@ def update_ini_values( def _record_section_boundary() -> None: if current_norm is not None and current_norm in remaining: - section_body_end[current_norm] = len(out) + # Walk back over trailing blank lines so appended keys sit directly + # under the section's last entry instead of after the gap. + end = len(out) + while end > 0 and out[end - 1].strip() == "": + end -= 1 + section_body_end[current_norm] = end for raw in lines: section_match = _SECTION_RE.match(raw)